Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/ipv4/ip_options.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* INET An implementation of the TCP/IP protocol suite for the LINUX
4
* operating system. INET is implemented using the BSD Socket
5
* interface as the means of communication with the user level.
6
*
7
* The options processing module for ip.c
8
*
9
* Authors: A.N.Kuznetsov
10
*
11
*/
12
13
#define pr_fmt(fmt) "IPv4: " fmt
14
15
#include <linux/capability.h>
16
#include <linux/module.h>
17
#include <linux/slab.h>
18
#include <linux/types.h>
19
#include <linux/uaccess.h>
20
#include <linux/unaligned.h>
21
#include <linux/skbuff.h>
22
#include <linux/ip.h>
23
#include <linux/icmp.h>
24
#include <linux/netdevice.h>
25
#include <linux/rtnetlink.h>
26
#include <net/sock.h>
27
#include <net/ip.h>
28
#include <net/icmp.h>
29
#include <net/route.h>
30
#include <net/cipso_ipv4.h>
31
#include <net/ip_fib.h>
32
33
/*
34
* Write options to IP header, record destination address to
35
* source route option, address of outgoing interface
36
* (we should already know it, so that this function is allowed be
37
* called only after routing decision) and timestamp,
38
* if we originate this datagram.
39
*
40
* daddr is real destination address, next hop is recorded in IP header.
41
* saddr is address of outgoing interface.
42
*/
43
44
void ip_options_build(struct sk_buff *skb, struct ip_options *opt,
45
__be32 daddr, struct rtable *rt)
46
{
47
unsigned char *iph = skb_network_header(skb);
48
49
memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
50
memcpy(iph + sizeof(struct iphdr), opt->__data, opt->optlen);
51
opt = &(IPCB(skb)->opt);
52
53
if (opt->srr)
54
memcpy(iph + opt->srr + iph[opt->srr + 1] - 4, &daddr, 4);
55
56
if (opt->rr_needaddr)
57
ip_rt_get_source(iph + opt->rr + iph[opt->rr + 2] - 5, skb, rt);
58
if (opt->ts_needaddr)
59
ip_rt_get_source(iph + opt->ts + iph[opt->ts + 2] - 9, skb, rt);
60
if (opt->ts_needtime) {
61
__be32 midtime;
62
63
midtime = inet_current_timestamp();
64
memcpy(iph + opt->ts + iph[opt->ts + 2] - 5, &midtime, 4);
65
}
66
}
67
68
/*
69
* Provided (sopt, skb) points to received options,
70
* build in dopt compiled option set appropriate for answering.
71
* i.e. invert SRR option, copy anothers,
72
* and grab room in RR/TS options.
73
*
74
* NOTE: dopt cannot point to skb.
75
*/
76
77
int __ip_options_echo(struct net *net, struct ip_options *dopt,
78
struct sk_buff *skb, const struct ip_options *sopt)
79
{
80
unsigned char *sptr, *dptr;
81
int soffset, doffset;
82
int optlen;
83
84
memset(dopt, 0, sizeof(struct ip_options));
85
86
if (sopt->optlen == 0)
87
return 0;
88
89
sptr = skb_network_header(skb);
90
dptr = dopt->__data;
91
92
if (sopt->rr) {
93
optlen = sptr[sopt->rr+1];
94
soffset = sptr[sopt->rr+2];
95
dopt->rr = dopt->optlen + sizeof(struct iphdr);
96
memcpy(dptr, sptr+sopt->rr, optlen);
97
if (sopt->rr_needaddr && soffset <= optlen) {
98
if (soffset + 3 > optlen)
99
return -EINVAL;
100
dptr[2] = soffset + 4;
101
dopt->rr_needaddr = 1;
102
}
103
dptr += optlen;
104
dopt->optlen += optlen;
105
}
106
if (sopt->ts) {
107
optlen = sptr[sopt->ts+1];
108
soffset = sptr[sopt->ts+2];
109
dopt->ts = dopt->optlen + sizeof(struct iphdr);
110
memcpy(dptr, sptr+sopt->ts, optlen);
111
if (soffset <= optlen) {
112
if (sopt->ts_needaddr) {
113
if (soffset + 3 > optlen)
114
return -EINVAL;
115
dopt->ts_needaddr = 1;
116
soffset += 4;
117
}
118
if (sopt->ts_needtime) {
119
if (soffset + 3 > optlen)
120
return -EINVAL;
121
if ((dptr[3]&0xF) != IPOPT_TS_PRESPEC) {
122
dopt->ts_needtime = 1;
123
soffset += 4;
124
} else {
125
dopt->ts_needtime = 0;
126
127
if (soffset + 7 <= optlen) {
128
__be32 addr;
129
130
memcpy(&addr, dptr+soffset-1, 4);
131
if (inet_addr_type(net, addr) != RTN_UNICAST) {
132
dopt->ts_needtime = 1;
133
soffset += 8;
134
}
135
}
136
}
137
}
138
dptr[2] = soffset;
139
}
140
dptr += optlen;
141
dopt->optlen += optlen;
142
}
143
if (sopt->srr) {
144
unsigned char *start = sptr+sopt->srr;
145
__be32 faddr;
146
147
optlen = start[1];
148
soffset = start[2];
149
doffset = 0;
150
if (soffset > optlen)
151
soffset = optlen + 1;
152
soffset -= 4;
153
if (soffset > 3) {
154
memcpy(&faddr, &start[soffset-1], 4);
155
for (soffset -= 4, doffset = 4; soffset > 3; soffset -= 4, doffset += 4)
156
memcpy(&dptr[doffset-1], &start[soffset-1], 4);
157
/*
158
* RFC1812 requires to fix illegal source routes.
159
*/
160
if (memcmp(&ip_hdr(skb)->saddr,
161
&start[soffset + 3], 4) == 0)
162
doffset -= 4;
163
}
164
if (doffset > 3) {
165
dopt->faddr = faddr;
166
dptr[0] = start[0];
167
dptr[1] = doffset+3;
168
dptr[2] = 4;
169
dptr += doffset+3;
170
dopt->srr = dopt->optlen + sizeof(struct iphdr);
171
dopt->optlen += doffset+3;
172
dopt->is_strictroute = sopt->is_strictroute;
173
}
174
}
175
if (sopt->cipso) {
176
optlen = sptr[sopt->cipso+1];
177
dopt->cipso = dopt->optlen+sizeof(struct iphdr);
178
memcpy(dptr, sptr+sopt->cipso, optlen);
179
dptr += optlen;
180
dopt->optlen += optlen;
181
}
182
while (dopt->optlen & 3) {
183
*dptr++ = IPOPT_END;
184
dopt->optlen++;
185
}
186
return 0;
187
}
188
189
/*
190
* Options "fragmenting", just fill options not
191
* allowed in fragments with NOOPs.
192
* Simple and stupid 8), but the most efficient way.
193
*/
194
195
void ip_options_fragment(struct sk_buff *skb)
196
{
197
unsigned char *optptr = skb_network_header(skb) + sizeof(struct iphdr);
198
struct ip_options *opt = &(IPCB(skb)->opt);
199
int l = opt->optlen;
200
int optlen;
201
202
while (l > 0) {
203
switch (*optptr) {
204
case IPOPT_END:
205
return;
206
case IPOPT_NOOP:
207
l--;
208
optptr++;
209
continue;
210
}
211
optlen = optptr[1];
212
if (optlen < 2 || optlen > l)
213
return;
214
if (!IPOPT_COPIED(*optptr))
215
memset(optptr, IPOPT_NOOP, optlen);
216
l -= optlen;
217
optptr += optlen;
218
}
219
opt->ts = 0;
220
opt->rr = 0;
221
opt->rr_needaddr = 0;
222
opt->ts_needaddr = 0;
223
opt->ts_needtime = 0;
224
}
225
226
/* helper used by ip_options_compile() to call fib_compute_spec_dst()
227
* at most one time.
228
*/
229
static void spec_dst_fill(__be32 *spec_dst, struct sk_buff *skb)
230
{
231
if (*spec_dst == htonl(INADDR_ANY))
232
*spec_dst = fib_compute_spec_dst(skb);
233
}
234
235
/*
236
* Verify options and fill pointers in struct options.
237
* Caller should clear *opt, and set opt->data.
238
* If opt == NULL, then skb->data should point to IP header.
239
*/
240
241
int __ip_options_compile(struct net *net,
242
struct ip_options *opt, struct sk_buff *skb,
243
__be32 *info)
244
{
245
__be32 spec_dst = htonl(INADDR_ANY);
246
unsigned char *pp_ptr = NULL;
247
struct rtable *rt = NULL;
248
unsigned char *optptr;
249
unsigned char *iph;
250
int optlen, l;
251
252
if (skb) {
253
rt = skb_rtable(skb);
254
optptr = (unsigned char *)&(ip_hdr(skb)[1]);
255
} else
256
optptr = opt->__data;
257
iph = optptr - sizeof(struct iphdr);
258
259
for (l = opt->optlen; l > 0; ) {
260
switch (*optptr) {
261
case IPOPT_END:
262
for (optptr++, l--; l > 0; optptr++, l--) {
263
if (*optptr != IPOPT_END) {
264
*optptr = IPOPT_END;
265
opt->is_changed = 1;
266
}
267
}
268
goto eol;
269
case IPOPT_NOOP:
270
l--;
271
optptr++;
272
continue;
273
}
274
if (unlikely(l < 2)) {
275
pp_ptr = optptr;
276
goto error;
277
}
278
optlen = optptr[1];
279
if (optlen < 2 || optlen > l) {
280
pp_ptr = optptr;
281
goto error;
282
}
283
switch (*optptr) {
284
case IPOPT_SSRR:
285
case IPOPT_LSRR:
286
if (optlen < 3) {
287
pp_ptr = optptr + 1;
288
goto error;
289
}
290
if (optptr[2] < 4) {
291
pp_ptr = optptr + 2;
292
goto error;
293
}
294
/* NB: cf RFC-1812 5.2.4.1 */
295
if (opt->srr) {
296
pp_ptr = optptr;
297
goto error;
298
}
299
if (!skb) {
300
if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {
301
pp_ptr = optptr + 1;
302
goto error;
303
}
304
memcpy(&opt->faddr, &optptr[3], 4);
305
if (optlen > 7)
306
memmove(&optptr[3], &optptr[7], optlen-7);
307
}
308
opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
309
opt->srr = optptr - iph;
310
break;
311
case IPOPT_RR:
312
if (opt->rr) {
313
pp_ptr = optptr;
314
goto error;
315
}
316
if (optlen < 3) {
317
pp_ptr = optptr + 1;
318
goto error;
319
}
320
if (optptr[2] < 4) {
321
pp_ptr = optptr + 2;
322
goto error;
323
}
324
if (optptr[2] <= optlen) {
325
if (optptr[2]+3 > optlen) {
326
pp_ptr = optptr + 2;
327
goto error;
328
}
329
if (rt) {
330
spec_dst_fill(&spec_dst, skb);
331
memcpy(&optptr[optptr[2]-1], &spec_dst, 4);
332
opt->is_changed = 1;
333
}
334
optptr[2] += 4;
335
opt->rr_needaddr = 1;
336
}
337
opt->rr = optptr - iph;
338
break;
339
case IPOPT_TIMESTAMP:
340
if (opt->ts) {
341
pp_ptr = optptr;
342
goto error;
343
}
344
if (optlen < 4) {
345
pp_ptr = optptr + 1;
346
goto error;
347
}
348
if (optptr[2] < 5) {
349
pp_ptr = optptr + 2;
350
goto error;
351
}
352
if (optptr[2] <= optlen) {
353
unsigned char *timeptr = NULL;
354
if (optptr[2]+3 > optlen) {
355
pp_ptr = optptr + 2;
356
goto error;
357
}
358
switch (optptr[3]&0xF) {
359
case IPOPT_TS_TSONLY:
360
if (skb)
361
timeptr = &optptr[optptr[2]-1];
362
opt->ts_needtime = 1;
363
optptr[2] += 4;
364
break;
365
case IPOPT_TS_TSANDADDR:
366
if (optptr[2]+7 > optlen) {
367
pp_ptr = optptr + 2;
368
goto error;
369
}
370
if (rt) {
371
spec_dst_fill(&spec_dst, skb);
372
memcpy(&optptr[optptr[2]-1], &spec_dst, 4);
373
timeptr = &optptr[optptr[2]+3];
374
}
375
opt->ts_needaddr = 1;
376
opt->ts_needtime = 1;
377
optptr[2] += 8;
378
break;
379
case IPOPT_TS_PRESPEC:
380
if (optptr[2]+7 > optlen) {
381
pp_ptr = optptr + 2;
382
goto error;
383
}
384
{
385
__be32 addr;
386
memcpy(&addr, &optptr[optptr[2]-1], 4);
387
if (inet_addr_type(net, addr) == RTN_UNICAST)
388
break;
389
if (skb)
390
timeptr = &optptr[optptr[2]+3];
391
}
392
opt->ts_needtime = 1;
393
optptr[2] += 8;
394
break;
395
default:
396
if (!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) {
397
pp_ptr = optptr + 3;
398
goto error;
399
}
400
break;
401
}
402
if (timeptr) {
403
__be32 midtime;
404
405
midtime = inet_current_timestamp();
406
memcpy(timeptr, &midtime, 4);
407
opt->is_changed = 1;
408
}
409
} else if ((optptr[3]&0xF) != IPOPT_TS_PRESPEC) {
410
unsigned int overflow = optptr[3]>>4;
411
if (overflow == 15) {
412
pp_ptr = optptr + 3;
413
goto error;
414
}
415
if (skb) {
416
optptr[3] = (optptr[3]&0xF)|((overflow+1)<<4);
417
opt->is_changed = 1;
418
}
419
}
420
opt->ts = optptr - iph;
421
break;
422
case IPOPT_RA:
423
if (optlen < 4) {
424
pp_ptr = optptr + 1;
425
goto error;
426
}
427
if (optptr[2] == 0 && optptr[3] == 0)
428
opt->router_alert = optptr - iph;
429
break;
430
case IPOPT_CIPSO:
431
if ((!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) || opt->cipso) {
432
pp_ptr = optptr;
433
goto error;
434
}
435
opt->cipso = optptr - iph;
436
if (cipso_v4_validate(skb, &optptr)) {
437
pp_ptr = optptr;
438
goto error;
439
}
440
break;
441
case IPOPT_SEC:
442
case IPOPT_SID:
443
default:
444
if (!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) {
445
pp_ptr = optptr;
446
goto error;
447
}
448
break;
449
}
450
l -= optlen;
451
optptr += optlen;
452
}
453
454
eol:
455
if (!pp_ptr)
456
return 0;
457
458
error:
459
if (info)
460
*info = htonl((pp_ptr-iph)<<24);
461
return -EINVAL;
462
}
463
EXPORT_SYMBOL(__ip_options_compile);
464
465
int ip_options_compile(struct net *net,
466
struct ip_options *opt, struct sk_buff *skb)
467
{
468
int ret;
469
__be32 info;
470
471
ret = __ip_options_compile(net, opt, skb, &info);
472
if (ret != 0 && skb)
473
icmp_send(skb, ICMP_PARAMETERPROB, 0, info);
474
return ret;
475
}
476
EXPORT_SYMBOL(ip_options_compile);
477
478
/*
479
* Undo all the changes done by ip_options_compile().
480
*/
481
482
void ip_options_undo(struct ip_options *opt)
483
{
484
if (opt->srr) {
485
unsigned char *optptr = opt->__data + opt->srr - sizeof(struct iphdr);
486
487
memmove(optptr + 7, optptr + 3, optptr[1] - 7);
488
memcpy(optptr + 3, &opt->faddr, 4);
489
}
490
if (opt->rr_needaddr) {
491
unsigned char *optptr = opt->__data + opt->rr - sizeof(struct iphdr);
492
493
optptr[2] -= 4;
494
memset(&optptr[optptr[2] - 1], 0, 4);
495
}
496
if (opt->ts) {
497
unsigned char *optptr = opt->__data + opt->ts - sizeof(struct iphdr);
498
499
if (opt->ts_needtime) {
500
optptr[2] -= 4;
501
memset(&optptr[optptr[2] - 1], 0, 4);
502
if ((optptr[3] & 0xF) == IPOPT_TS_PRESPEC)
503
optptr[2] -= 4;
504
}
505
if (opt->ts_needaddr) {
506
optptr[2] -= 4;
507
memset(&optptr[optptr[2] - 1], 0, 4);
508
}
509
}
510
}
511
512
int ip_options_get(struct net *net, struct ip_options_rcu **optp,
513
sockptr_t data, int optlen)
514
{
515
struct ip_options_rcu *opt;
516
517
opt = kzalloc(sizeof(struct ip_options_rcu) + ((optlen + 3) & ~3),
518
GFP_KERNEL);
519
if (!opt)
520
return -ENOMEM;
521
if (optlen && copy_from_sockptr(opt->opt.__data, data, optlen)) {
522
kfree(opt);
523
return -EFAULT;
524
}
525
526
while (optlen & 3)
527
opt->opt.__data[optlen++] = IPOPT_END;
528
opt->opt.optlen = optlen;
529
if (optlen && ip_options_compile(net, &opt->opt, NULL)) {
530
kfree(opt);
531
return -EINVAL;
532
}
533
kfree(*optp);
534
*optp = opt;
535
return 0;
536
}
537
538
void ip_forward_options(struct sk_buff *skb)
539
{
540
struct ip_options *opt = &(IPCB(skb)->opt);
541
unsigned char *optptr;
542
struct rtable *rt = skb_rtable(skb);
543
unsigned char *raw = skb_network_header(skb);
544
545
if (opt->rr_needaddr) {
546
optptr = (unsigned char *)raw + opt->rr;
547
ip_rt_get_source(&optptr[optptr[2]-5], skb, rt);
548
opt->is_changed = 1;
549
}
550
if (opt->srr_is_hit) {
551
int srrptr, srrspace;
552
553
optptr = raw + opt->srr;
554
555
for ( srrptr = optptr[2], srrspace = optptr[1];
556
srrptr <= srrspace;
557
srrptr += 4
558
) {
559
if (srrptr + 3 > srrspace)
560
break;
561
if (memcmp(&opt->nexthop, &optptr[srrptr-1], 4) == 0)
562
break;
563
}
564
if (srrptr + 3 <= srrspace) {
565
opt->is_changed = 1;
566
ip_hdr(skb)->daddr = opt->nexthop;
567
ip_rt_get_source(&optptr[srrptr-1], skb, rt);
568
optptr[2] = srrptr+4;
569
} else {
570
net_crit_ratelimited("%s(): Argh! Destination lost!\n",
571
__func__);
572
}
573
if (opt->ts_needaddr) {
574
optptr = raw + opt->ts;
575
ip_rt_get_source(&optptr[optptr[2]-9], skb, rt);
576
opt->is_changed = 1;
577
}
578
}
579
if (opt->is_changed) {
580
opt->is_changed = 0;
581
ip_send_check(ip_hdr(skb));
582
}
583
}
584
585
int ip_options_rcv_srr(struct sk_buff *skb, struct net_device *dev)
586
{
587
struct ip_options *opt = &(IPCB(skb)->opt);
588
int srrspace, srrptr;
589
__be32 nexthop;
590
struct iphdr *iph = ip_hdr(skb);
591
unsigned char *optptr = skb_network_header(skb) + opt->srr;
592
struct rtable *rt = skb_rtable(skb);
593
struct rtable *rt2;
594
unsigned long orefdst;
595
int err;
596
597
if (!rt)
598
return 0;
599
600
if (skb->pkt_type != PACKET_HOST)
601
return -EINVAL;
602
if (rt->rt_type == RTN_UNICAST) {
603
if (!opt->is_strictroute)
604
return 0;
605
icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl(16<<24));
606
return -EINVAL;
607
}
608
if (rt->rt_type != RTN_LOCAL)
609
return -EINVAL;
610
611
for (srrptr = optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {
612
if (srrptr + 3 > srrspace) {
613
icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));
614
return -EINVAL;
615
}
616
memcpy(&nexthop, &optptr[srrptr-1], 4);
617
618
orefdst = skb->_skb_refdst;
619
skb_dst_set(skb, NULL);
620
err = ip_route_input(skb, nexthop, iph->saddr, ip4h_dscp(iph),
621
dev) ? -EINVAL : 0;
622
rt2 = skb_rtable(skb);
623
if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {
624
skb_dst_drop(skb);
625
skb->_skb_refdst = orefdst;
626
return -EINVAL;
627
}
628
refdst_drop(orefdst);
629
if (rt2->rt_type != RTN_LOCAL)
630
break;
631
/* Superfast 8) loopback forward */
632
iph->daddr = nexthop;
633
opt->is_changed = 1;
634
}
635
if (srrptr <= srrspace) {
636
opt->srr_is_hit = 1;
637
opt->nexthop = nexthop;
638
opt->is_changed = 1;
639
}
640
return 0;
641
}
642
EXPORT_SYMBOL(ip_options_rcv_srr);
643
644