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