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