Main Page | Class List | File List | Class Members | File Members

ip_options.c

Go to the documentation of this file.
00001 /* 00002 * INET An implementation of the TCP/IP protocol suite for the LINUX 00003 * operating system. INET is implemented using the BSD Socket 00004 * interface as the means of communication with the user level. 00005 * 00006 * The options processing module for ip.c 00007 * 00008 * Version: $Id: ip_options.c,v 1.21 2001/09/01 00:31:50 davem Exp $ 00009 * 00010 * Authors: A.N.Kuznetsov 00011 * 00012 */ 00013 00014 #include <linux/types.h> 00015 #include <asm/uaccess.h> 00016 #include <linux/skbuff.h> 00017 #include <linux/ip.h> 00018 #include <linux/icmp.h> 00019 #include <linux/netdevice.h> 00020 #include <linux/rtnetlink.h> 00021 #include <net/sock.h> 00022 #include <net/ip.h> 00023 #include <net/icmp.h> 00024 00025 /* 00026 * Write options to IP header, record destination address to 00027 * source route option, address of outgoing interface 00028 * (we should already know it, so that this function is allowed be 00029 * called only after routing decision) and timestamp, 00030 * if we originate this datagram. 00031 * 00032 * daddr is real destination address, next hop is recorded in IP header. 00033 * saddr is address of outgoing interface. 00034 */ 00035 00036 void ip_options_build(struct sk_buff * skb, struct ip_options * opt, 00037 u32 daddr, struct rtable *rt, int is_frag) 00038 { 00039 unsigned char * iph = skb->nh.raw; 00040 00041 memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options)); 00042 memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen); 00043 opt = &(IPCB(skb)->opt); 00044 opt->is_data = 0; 00045 00046 if (opt->srr) 00047 memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4); 00048 00049 if (!is_frag) { 00050 if (opt->rr_needaddr) 00051 ip_rt_get_source(iph+opt->rr+iph[opt->rr+2]-5, rt); 00052 if (opt->ts_needaddr) 00053 ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, rt); 00054 if (opt->ts_needtime) { 00055 struct timeval tv; 00056 __u32 midtime; 00057 do_gettimeofday(&tv); 00058 midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000); 00059 memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4); 00060 } 00061 return; 00062 } 00063 if (opt->rr) { 00064 memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]); 00065 opt->rr = 0; 00066 opt->rr_needaddr = 0; 00067 } 00068 if (opt->ts) { 00069 memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]); 00070 opt->ts = 0; 00071 opt->ts_needaddr = opt->ts_needtime = 0; 00072 } 00073 } 00074 00075 /* 00076 * Provided (sopt, skb) points to received options, 00077 * build in dopt compiled option set appropriate for answering. 00078 * i.e. invert SRR option, copy anothers, 00079 * and grab room in RR/TS options. 00080 * 00081 * NOTE: dopt cannot point to skb. 00082 */ 00083 00084 int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) 00085 { 00086 struct ip_options *sopt; 00087 unsigned char *sptr, *dptr; 00088 int soffset, doffset; 00089 int optlen; 00090 u32 daddr; 00091 00092 memset(dopt, 0, sizeof(struct ip_options)); 00093 00094 dopt->is_data = 1; 00095 00096 sopt = &(IPCB(skb)->opt); 00097 00098 if (sopt->optlen == 0) { 00099 dopt->optlen = 0; 00100 return 0; 00101 } 00102 00103 sptr = skb->nh.raw; 00104 dptr = dopt->__data; 00105 00106 if (skb->dst) 00107 daddr = ((struct rtable*)skb->dst)->rt_spec_dst; 00108 else 00109 daddr = skb->nh.iph->daddr; 00110 00111 if (sopt->rr) { 00112 optlen = sptr[sopt->rr+1]; 00113 soffset = sptr[sopt->rr+2]; 00114 dopt->rr = dopt->optlen + sizeof(struct iphdr); 00115 memcpy(dptr, sptr+sopt->rr, optlen); 00116 if (sopt->rr_needaddr && soffset <= optlen) { 00117 if (soffset + 3 > optlen) 00118 return -EINVAL; 00119 dptr[2] = soffset + 4; 00120 dopt->rr_needaddr = 1; 00121 } 00122 dptr += optlen; 00123 dopt->optlen += optlen; 00124 } 00125 if (sopt->ts) { 00126 optlen = sptr[sopt->ts+1]; 00127 soffset = sptr[sopt->ts+2]; 00128 dopt->ts = dopt->optlen + sizeof(struct iphdr); 00129 memcpy(dptr, sptr+sopt->ts, optlen); 00130 if (soffset <= optlen) { 00131 if (sopt->ts_needaddr) { 00132 if (soffset + 3 > optlen) 00133 return -EINVAL; 00134 dopt->ts_needaddr = 1; 00135 soffset += 4; 00136 } 00137 if (sopt->ts_needtime) { 00138 if (soffset + 3 > optlen) 00139 return -EINVAL; 00140 if ((dptr[3]&0xF) != IPOPT_TS_PRESPEC) { 00141 dopt->ts_needtime = 1; 00142 soffset += 4; 00143 } else { 00144 dopt->ts_needtime = 0; 00145 00146 if (soffset + 8 <= optlen) { 00147 __u32 addr; 00148 00149 memcpy(&addr, sptr+soffset-1, 4); 00150 if (inet_addr_type(addr) != RTN_LOCAL) { 00151 dopt->ts_needtime = 1; 00152 soffset += 8; 00153 } 00154 } 00155 } 00156 } 00157 dptr[2] = soffset; 00158 } 00159 dptr += optlen; 00160 dopt->optlen += optlen; 00161 } 00162 if (sopt->srr) { 00163 unsigned char * start = sptr+sopt->srr; 00164 u32 faddr; 00165 00166 optlen = start[1]; 00167 soffset = start[2]; 00168 doffset = 0; 00169 if (soffset > optlen) 00170 soffset = optlen + 1; 00171 soffset -= 4; 00172 if (soffset > 3) { 00173 memcpy(&faddr, &start[soffset-1], 4); 00174 for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4) 00175 memcpy(&dptr[doffset-1], &start[soffset-1], 4); 00176 /* 00177 * RFC1812 requires to fix illegal source routes. 00178 */ 00179 if (memcmp(&skb->nh.iph->saddr, &start[soffset+3], 4) == 0) 00180 doffset -= 4; 00181 } 00182 if (doffset > 3) { 00183 memcpy(&start[doffset-1], &daddr, 4); 00184 dopt->faddr = faddr; 00185 dptr[0] = start[0]; 00186 dptr[1] = doffset+3; 00187 dptr[2] = 4; 00188 dptr += doffset+3; 00189 dopt->srr = dopt->optlen + sizeof(struct iphdr); 00190 dopt->optlen += doffset+3; 00191 dopt->is_strictroute = sopt->is_strictroute; 00192 } 00193 } 00194 while (dopt->optlen & 3) { 00195 *dptr++ = IPOPT_END; 00196 dopt->optlen++; 00197 } 00198 return 0; 00199 } 00200 00201 /* 00202 * Options "fragmenting", just fill options not 00203 * allowed in fragments with NOOPs. 00204 * Simple and stupid 8), but the most efficient way. 00205 */ 00206 00207 void ip_options_fragment(struct sk_buff * skb) 00208 { 00209 unsigned char * optptr = skb->nh.raw; 00210 struct ip_options * opt = &(IPCB(skb)->opt); 00211 int l = opt->optlen; 00212 int optlen; 00213 00214 while (l > 0) { 00215 switch (*optptr) { 00216 case IPOPT_END: 00217 return; 00218 case IPOPT_NOOP: 00219 l--; 00220 optptr++; 00221 continue; 00222 } 00223 optlen = optptr[1]; 00224 if (optlen<2 || optlen>l) 00225 return; 00226 if (!IPOPT_COPIED(*optptr)) 00227 memset(optptr, IPOPT_NOOP, optlen); 00228 l -= optlen; 00229 optptr += optlen; 00230 } 00231 opt->ts = 0; 00232 opt->rr = 0; 00233 opt->rr_needaddr = 0; 00234 opt->ts_needaddr = 0; 00235 opt->ts_needtime = 0; 00236 return; 00237 } 00238 00239 /* 00240 * Verify options and fill pointers in struct options. 00241 * Caller should clear *opt, and set opt->data. 00242 * If opt == NULL, then skb->data should point to IP header. 00243 */ 00244 00245 int ip_options_compile(struct ip_options * opt, struct sk_buff * skb) 00246 { 00247 int l; 00248 unsigned char * iph; 00249 unsigned char * optptr; 00250 int optlen; 00251 unsigned char * pp_ptr = NULL; 00252 struct rtable *rt = skb ? (struct rtable*)skb->dst : NULL; 00253 00254 if (!opt) { 00255 opt = &(IPCB(skb)->opt); 00256 memset(opt, 0, sizeof(struct ip_options)); 00257 iph = skb->nh.raw; 00258 opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr); 00259 optptr = iph + sizeof(struct iphdr); 00260 opt->is_data = 0; 00261 } else { 00262 optptr = opt->is_data ? opt->__data : (unsigned char*)&(skb->nh.iph[1]); 00263 iph = optptr - sizeof(struct iphdr); 00264 } 00265 00266 for (l = opt->optlen; l > 0; ) { 00267 switch (*optptr) { 00268 case IPOPT_END: 00269 for (optptr++, l--; l>0; optptr++, l--) { 00270 if (*optptr != IPOPT_END) { 00271 *optptr = IPOPT_END; 00272 opt->is_changed = 1; 00273 } 00274 } 00275 goto eol; 00276 case IPOPT_NOOP: 00277 l--; 00278 optptr++; 00279 continue; 00280 } 00281 optlen = optptr[1]; 00282 if (optlen<2 || optlen>l) { 00283 pp_ptr = optptr; 00284 goto error; 00285 } 00286 switch (*optptr) { 00287 case IPOPT_SSRR: 00288 case IPOPT_LSRR: 00289 if (optlen < 3) { 00290 pp_ptr = optptr + 1; 00291 goto error; 00292 } 00293 if (optptr[2] < 4) { 00294 pp_ptr = optptr + 2; 00295 goto error; 00296 } 00297 /* NB: cf RFC-1812 5.2.4.1 */ 00298 if (opt->srr) { 00299 pp_ptr = optptr; 00300 goto error; 00301 } 00302 if (!skb) { 00303 if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) { 00304 pp_ptr = optptr + 1; 00305 goto error; 00306 } 00307 memcpy(&opt->faddr, &optptr[3], 4); 00308 if (optlen > 7) 00309 memmove(&optptr[3], &optptr[7], optlen-7); 00310 } 00311 opt->is_strictroute = (optptr[0] == IPOPT_SSRR); 00312 opt->srr = optptr - iph; 00313 break; 00314 case IPOPT_RR: 00315 if (opt->rr) { 00316 pp_ptr = optptr; 00317 goto error; 00318 } 00319 if (optlen < 3) { 00320 pp_ptr = optptr + 1; 00321 goto error; 00322 } 00323 if (optptr[2] < 4) { 00324 pp_ptr = optptr + 2; 00325 goto error; 00326 } 00327 if (optptr[2] <= optlen) { 00328 if (optptr[2]+3 > optlen) { 00329 pp_ptr = optptr + 2; 00330 goto error; 00331 } 00332 if (skb) { 00333 memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); 00334 opt->is_changed = 1; 00335 } 00336 optptr[2] += 4; 00337 opt->rr_needaddr = 1; 00338 } 00339 opt->rr = optptr - iph; 00340 break; 00341 case IPOPT_TIMESTAMP: 00342 if (opt->ts) { 00343 pp_ptr = optptr; 00344 goto error; 00345 } 00346 if (optlen < 4) { 00347 pp_ptr = optptr + 1; 00348 goto error; 00349 } 00350 if (optptr[2] < 5) { 00351 pp_ptr = optptr + 2; 00352 goto error; 00353 } 00354 if (optptr[2] <= optlen) { 00355 __u32 * timeptr = NULL; 00356 if (optptr[2]+3 > optptr[1]) { 00357 pp_ptr = optptr + 2; 00358 goto error; 00359 } 00360 switch (optptr[3]&0xF) { 00361 case IPOPT_TS_TSONLY: 00362 opt->ts = optptr - iph; 00363 if (skb) 00364 timeptr = (__u32*)&optptr[optptr[2]-1]; 00365 opt->ts_needtime = 1; 00366 optptr[2] += 4; 00367 break; 00368 case IPOPT_TS_TSANDADDR: 00369 if (optptr[2]+7 > optptr[1]) { 00370 pp_ptr = optptr + 2; 00371 goto error; 00372 } 00373 opt->ts = optptr - iph; 00374 if (skb) { 00375 memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); 00376 timeptr = (__u32*)&optptr[optptr[2]+3]; 00377 } 00378 opt->ts_needaddr = 1; 00379 opt->ts_needtime = 1; 00380 optptr[2] += 8; 00381 break; 00382 case IPOPT_TS_PRESPEC: 00383 if (optptr[2]+7 > optptr[1]) { 00384 pp_ptr = optptr + 2; 00385 goto error; 00386 } 00387 opt->ts = optptr - iph; 00388 { 00389 u32 addr; 00390 memcpy(&addr, &optptr[optptr[2]-1], 4); 00391 if (inet_addr_type(addr) == RTN_UNICAST) 00392 break; 00393 if (skb) 00394 timeptr = (__u32*)&optptr[optptr[2]+3]; 00395 } 00396 opt->ts_needtime = 1; 00397 optptr[2] += 8; 00398 break; 00399 default: 00400 if (!skb && !capable(CAP_NET_RAW)) { 00401 pp_ptr = optptr + 3; 00402 goto error; 00403 } 00404 break; 00405 } 00406 if (timeptr) { 00407 struct timeval tv; 00408 __u32 midtime; 00409 do_gettimeofday(&tv); 00410 midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000); 00411 memcpy(timeptr, &midtime, sizeof(__u32)); 00412 opt->is_changed = 1; 00413 } 00414 } else { 00415 unsigned overflow = optptr[3]>>4; 00416 if (overflow == 15) { 00417 pp_ptr = optptr + 3; 00418 goto error; 00419 } 00420 opt->ts = optptr - iph; 00421 if (skb) { 00422 optptr[3] = (optptr[3]&0xF)|((overflow+1)<<4); 00423 opt->is_changed = 1; 00424 } 00425 } 00426 break; 00427 case IPOPT_RA: 00428 if (optlen < 4) { 00429 pp_ptr = optptr + 1; 00430 goto error; 00431 } 00432 if (optptr[2] == 0 && optptr[3] == 0) 00433 opt->router_alert = optptr - iph; 00434 break; 00435 case IPOPT_SEC: 00436 case IPOPT_SID: 00437 default: 00438 if (!skb && !capable(CAP_NET_RAW)) { 00439 pp_ptr = optptr; 00440 goto error; 00441 } 00442 break; 00443 } 00444 l -= optlen; 00445 optptr += optlen; 00446 } 00447 00448 eol: 00449 if (!pp_ptr) 00450 return 0; 00451 00452 error: 00453 if (skb) { 00454 icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((pp_ptr-iph)<<24)); 00455 } 00456 return -EINVAL; 00457 } 00458 00459 00460 /* 00461 * Undo all the changes done by ip_options_compile(). 00462 */ 00463 00464 void ip_options_undo(struct ip_options * opt) 00465 { 00466 if (opt->srr) { 00467 unsigned char * optptr = opt->__data+opt->srr-sizeof(struct iphdr); 00468 memmove(optptr+7, optptr+3, optptr[1]-7); 00469 memcpy(optptr+3, &opt->faddr, 4); 00470 } 00471 if (opt->rr_needaddr) { 00472 unsigned char * optptr = opt->__data+opt->rr-sizeof(struct iphdr); 00473 optptr[2] -= 4; 00474 memset(&optptr[optptr[2]-1], 0, 4); 00475 } 00476 if (opt->ts) { 00477 unsigned char * optptr = opt->__data+opt->ts-sizeof(struct iphdr); 00478 if (opt->ts_needtime) { 00479 optptr[2] -= 4; 00480 memset(&optptr[optptr[2]-1], 0, 4); 00481 if ((optptr[3]&0xF) == IPOPT_TS_PRESPEC) 00482 optptr[2] -= 4; 00483 } 00484 if (opt->ts_needaddr) { 00485 optptr[2] -= 4; 00486 memset(&optptr[optptr[2]-1], 0, 4); 00487 } 00488 } 00489 } 00490 00491 int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen, int user) 00492 { 00493 struct ip_options *opt; 00494 00495 opt = kmalloc(sizeof(struct ip_options)+((optlen+3)&~3), GFP_KERNEL); 00496 if (!opt) 00497 return -ENOMEM; 00498 memset(opt, 0, sizeof(struct ip_options)); 00499 if (optlen) { 00500 if (user) { 00501 if (copy_from_user(opt->__data, data, optlen)) { 00502 kfree(opt); 00503 return -EFAULT; 00504 } 00505 } else 00506 memcpy(opt->__data, data, optlen); 00507 } 00508 while (optlen & 3) 00509 opt->__data[optlen++] = IPOPT_END; 00510 opt->optlen = optlen; 00511 opt->is_data = 1; 00512 opt->is_setbyuser = 1; 00513 if (optlen && ip_options_compile(opt, NULL)) { 00514 kfree(opt); 00515 return -EINVAL; 00516 } 00517 *optp = opt; 00518 return 0; 00519 } 00520 00521 void ip_forward_options(struct sk_buff *skb) 00522 { 00523 struct ip_options * opt = &(IPCB(skb)->opt); 00524 unsigned char * optptr; 00525 struct rtable *rt = (struct rtable*)skb->dst; 00526 unsigned char *raw = skb->nh.raw; 00527 00528 if (opt->rr_needaddr) { 00529 optptr = (unsigned char *)raw + opt->rr; 00530 ip_rt_get_source(&optptr[optptr[2]-5], rt); 00531 opt->is_changed = 1; 00532 } 00533 if (opt->srr_is_hit) { 00534 int srrptr, srrspace; 00535 00536 optptr = raw + opt->srr; 00537 00538 for ( srrptr=optptr[2], srrspace = optptr[1]; 00539 srrptr <= srrspace; 00540 srrptr += 4 00541 ) { 00542 if (srrptr + 3 > srrspace) 00543 break; 00544 if (memcmp(&rt->rt_dst, &optptr[srrptr-1], 4) == 0) 00545 break; 00546 } 00547 if (srrptr + 3 <= srrspace) { 00548 opt->is_changed = 1; 00549 ip_rt_get_source(&optptr[srrptr-1], rt); 00550 skb->nh.iph->daddr = rt->rt_dst; 00551 optptr[2] = srrptr+4; 00552 } else if (net_ratelimit()) 00553 printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n"); 00554 if (opt->ts_needaddr) { 00555 optptr = raw + opt->ts; 00556 ip_rt_get_source(&optptr[optptr[2]-9], rt); 00557 opt->is_changed = 1; 00558 } 00559 } 00560 if (opt->is_changed) { 00561 opt->is_changed = 0; 00562 ip_send_check(skb->nh.iph); 00563 } 00564 } 00565 00566 int ip_options_rcv_srr(struct sk_buff *skb) 00567 { 00568 struct ip_options *opt = &(IPCB(skb)->opt); 00569 int srrspace, srrptr; 00570 u32 nexthop; 00571 struct iphdr *iph = skb->nh.iph; 00572 unsigned char * optptr = skb->nh.raw + opt->srr; 00573 struct rtable *rt = (struct rtable*)skb->dst; 00574 struct rtable *rt2; 00575 int err; 00576 00577 if (!opt->srr) 00578 return 0; 00579 00580 if (skb->pkt_type != PACKET_HOST) 00581 return -EINVAL; 00582 if (rt->rt_type == RTN_UNICAST) { 00583 if (!opt->is_strictroute) 00584 return 0; 00585 icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl(16<<24)); 00586 return -EINVAL; 00587 } 00588 if (rt->rt_type != RTN_LOCAL) 00589 return -EINVAL; 00590 00591 for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) { 00592 if (srrptr + 3 > srrspace) { 00593 icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24)); 00594 return -EINVAL; 00595 } 00596 memcpy(&nexthop, &optptr[srrptr-1], 4); 00597 00598 rt = (struct rtable*)skb->dst; 00599 skb->dst = NULL; 00600 err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->dev); 00601 rt2 = (struct rtable*)skb->dst; 00602 if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) { 00603 ip_rt_put(rt2); 00604 skb->dst = &rt->u.dst; 00605 return -EINVAL; 00606 } 00607 ip_rt_put(rt); 00608 if (rt2->rt_type != RTN_LOCAL) 00609 break; 00610 /* Superfast 8) loopback forward */ 00611 memcpy(&iph->daddr, &optptr[srrptr-1], 4); 00612 opt->is_changed = 1; 00613 } 00614 if (srrptr <= srrspace) { 00615 opt->srr_is_hit = 1; 00616 opt->is_changed = 1; 00617 } 00618 return 0; 00619 }

Generated on Wed Dec 1 21:25:31 2004 for Linux 2.4.23 Networking by doxygen 1.3.8