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