| /* |
| * INET An implementation of the TCP/IP protocol suite for the LINUX |
| * operating system. INET is implemented using the BSD Socket |
| * interface as the means of communication with the user level. |
| * |
| * The Internet Protocol (IP) module. |
| * |
| * Version: @(#)ip.c 1.0.16b 9/1/93 |
| * |
| * Authors: Ross Biro, <bir7@leland.Stanford.Edu> |
| * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> |
| * Donald Becker, <becker@super.org> |
| * |
| * Fixes: |
| * Alan Cox : Commented a couple of minor bits of surplus code |
| * Alan Cox : Undefining IP_FORWARD doesn't include the code |
| * (just stops a compiler warning). |
| * Alan Cox : Frames with >=MAX_ROUTE record routes, strict routes or loose routes |
| * are junked rather than corrupting things. |
| * Alan Cox : Frames to bad broadcast subnets are dumped |
| * We used to process them non broadcast and |
| * boy could that cause havoc. |
| * Alan Cox : ip_forward sets the free flag on the |
| * new frame it queues. Still crap because |
| * it copies the frame but at least it |
| * doesn't eat memory too. |
| * Alan Cox : Generic queue code and memory fixes. |
| * |
| * To Fix: |
| * IP option processing is mostly not needed. ip_forward needs to know about routing rules |
| * and time stamp but that's about all. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version |
| * 2 of the License, or (at your option) any later version. |
| */ |
| #include <asm/segment.h> |
| #include <asm/system.h> |
| #include <linux/types.h> |
| #include <linux/kernel.h> |
| #include <linux/sched.h> |
| #include <linux/string.h> |
| #include <linux/errno.h> |
| #include <linux/socket.h> |
| #include <linux/sockios.h> |
| #include <linux/in.h> |
| #include "inet.h" |
| #include "dev.h" |
| #include "eth.h" |
| #include "ip.h" |
| #include "protocol.h" |
| #include "route.h" |
| #include "tcp.h" |
| #include "skbuff.h" |
| #include "sock.h" |
| #include "arp.h" |
| #include "icmp.h" |
| |
| #undef CONFIG_IP_FORWARD |
| |
| extern int last_retran; |
| extern void sort_send(struct sock *sk); |
| |
| void |
| ip_print(struct iphdr *ip) |
| { |
| unsigned char buff[32]; |
| unsigned char *ptr; |
| int addr, len, i; |
| |
| if (inet_debug != DBG_IP) return; |
| |
| /* Dump the IP header. */ |
| printk("IP: ihl=%d, version=%d, tos=%d, tot_len=%d\n", |
| ip->ihl, ip->version, ip->tos, ntohs(ip->tot_len)); |
| printk(" id=%X, ttl=%d, prot=%d, check=%X\n", |
| ip->id, ip->ttl, ip->protocol, ip->check); |
| printk(" frag_off=%d\n", ip->frag_off); |
| printk(" soucre=%s ", in_ntoa(ip->saddr)); |
| printk("dest=%s\n", in_ntoa(ip->daddr)); |
| printk(" ----\n"); |
| |
| /* Dump the data. */ |
| ptr = (unsigned char *)(ip + 1); |
| addr = 0; |
| len = ntohs(ip->tot_len) - (4 * ip->ihl); |
| while (len > 0) { |
| printk(" %04X: ", addr); |
| for(i = 0; i < 16; i++) { |
| if (len > 0) { |
| printk("%02X ", (*ptr & 0xFF)); |
| buff[i] = *ptr++; |
| if (buff[i] < 32 || buff[i] > 126) buff[i] = '.'; |
| } else { |
| printk(" "); |
| buff[i] = ' '; |
| } |
| addr++; |
| len--; |
| }; |
| buff[i] = '\0'; |
| printk(" \"%s\"\n", buff); |
| } |
| printk(" ----\n\n"); |
| } |
| |
| |
| int |
| ip_ioctl(struct sock *sk, int cmd, unsigned long arg) |
| { |
| switch(cmd) { |
| case DDIOCSDBG: |
| return(dbg_ioctl((void *) arg, DBG_IP)); |
| default: |
| return(-EINVAL); |
| } |
| } |
| |
| |
| /* these two routines will do routining. */ |
| static void |
| strict_route(struct iphdr *iph, struct options *opt) |
| { |
| } |
| |
| |
| static void |
| loose_route(struct iphdr *iph, struct options *opt) |
| { |
| } |
| |
| |
| static void |
| print_ipprot(struct inet_protocol *ipprot) |
| { |
| DPRINTF((DBG_IP, "handler = %X, protocol = %d, copy=%d \n", |
| ipprot->handler, ipprot->protocol, ipprot->copy)); |
| } |
| |
| |
| /* This routine will check to see if we have lost a gateway. */ |
| void |
| ip_route_check(unsigned long daddr) |
| { |
| } |
| |
| |
| #if 0 |
| /* this routine puts the options at the end of an ip header. */ |
| static int |
| build_options(struct iphdr *iph, struct options *opt) |
| { |
| unsigned char *ptr; |
| /* currently we don't support any options. */ |
| ptr = (unsigned char *)(iph+1); |
| *ptr = 0; |
| return (4); |
| } |
| #endif |
| |
| |
| /* Take an skb, and fill in the MAC header. */ |
| static int |
| ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev, |
| unsigned long saddr) |
| { |
| unsigned char *ptr; |
| int mac; |
| |
| ptr = (unsigned char *)(skb + 1); |
| mac = 0; |
| skb->arp = 1; |
| if (dev->hard_header) { |
| mac = dev->hard_header(ptr, dev, ETH_P_IP, daddr, saddr, len); |
| } |
| if (mac < 0) { |
| mac = -mac; |
| skb->arp = 0; |
| } |
| skb->dev = dev; |
| return(mac); |
| } |
| |
| |
| /* |
| * This routine builds the appropriate hardware/IP headers for |
| * the routine. It assumes that if *dev != NULL then the |
| * protocol knows what it's doing, otherwise it uses the |
| * routing/ARP tables to select a device struct. |
| */ |
| int |
| ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr, |
| struct device **dev, int type, struct options *opt, int len) |
| { |
| static struct options optmem; |
| struct iphdr *iph; |
| struct rtable *rt; |
| unsigned char *buff; |
| unsigned long raddr; |
| static int count = 0; |
| int tmp; |
| |
| if (saddr == 0) |
| saddr = my_addr(); |
| |
| DPRINTF((DBG_IP, "ip_build_header (skb=%X, saddr=%X, daddr=%X, *dev=%X,\n" |
| " type=%d, opt=%X, len = %d)\n", |
| skb, saddr, daddr, *dev, type, opt, len)); |
| |
| buff = (unsigned char *)(skb + 1); |
| |
| /* See if we need to look up the device. */ |
| if (*dev == NULL) { |
| rt = rt_route(daddr, &optmem); |
| if (rt == NULL) |
| return(-ENETUNREACH); |
| |
| *dev = rt->rt_dev; |
| if (daddr != 0x0100007FL) |
| saddr = rt->rt_dev->pa_addr; |
| raddr = rt->rt_gateway; |
| |
| DPRINTF((DBG_IP, "ip_build_header: saddr set to %s\n", in_ntoa(saddr))); |
| opt = &optmem; |
| } else { |
| /* We still need the address of the first hop. */ |
| rt = rt_route(daddr, &optmem); |
| raddr = (rt == NULL) ? 0 : rt->rt_gateway; |
| } |
| if (raddr == 0) |
| raddr = daddr; |
| |
| /* Now build the MAC header. */ |
| tmp = ip_send(skb, raddr, len, *dev, saddr); |
| buff += tmp; |
| len -= tmp; |
| |
| skb->dev = *dev; |
| skb->saddr = saddr; |
| if (skb->sk) skb->sk->saddr = saddr; |
| |
| /* Now build the IP header. */ |
| |
| /* If we are using IPPROTO_RAW, then we don't need an IP header, since |
| one is being supplied to us by the user */ |
| |
| if(type == IPPROTO_RAW) return (tmp); |
| |
| iph = (struct iphdr *)buff; |
| iph->version = 4; |
| iph->tos = 0; |
| iph->frag_off = 0; |
| iph->ttl = 32; |
| iph->daddr = daddr; |
| iph->saddr = saddr; |
| iph->protocol = type; |
| iph->ihl = 5; |
| iph->id = htons(count++); |
| |
| /* Setup the IP options. */ |
| #ifdef Not_Yet_Avail |
| build_options(iph, opt); |
| #endif |
| |
| return(20 + tmp); /* IP header plus MAC header size */ |
| } |
| |
| |
| static int |
| do_options(struct iphdr *iph, struct options *opt) |
| { |
| unsigned char *buff; |
| int done = 0; |
| int i, len = sizeof(struct iphdr); |
| |
| /* Zero out the options. */ |
| opt->record_route.route_size = 0; |
| opt->loose_route.route_size = 0; |
| opt->strict_route.route_size = 0; |
| opt->tstamp.ptr = 0; |
| opt->security = 0; |
| opt->compartment = 0; |
| opt->handling = 0; |
| opt->stream = 0; |
| opt->tcc = 0; |
| return(0); |
| |
| /* Advance the pointer to start at the options. */ |
| buff = (unsigned char *)(iph + 1); |
| |
| /* Now start the processing. */ |
| while (!done && len < iph->ihl*4) switch(*buff) { |
| case IPOPT_END: |
| done = 1; |
| break; |
| case IPOPT_NOOP: |
| buff++; |
| len++; |
| break; |
| case IPOPT_SEC: |
| buff++; |
| if (*buff != 11) return(1); |
| buff++; |
| opt->security = ntohs(*(unsigned short *)buff); |
| buff += 2; |
| opt->compartment = ntohs(*(unsigned short *)buff); |
| buff += 2; |
| opt->handling = ntohs(*(unsigned short *)buff); |
| buff += 2; |
| opt->tcc = ((*buff) << 16) + ntohs(*(unsigned short *)(buff+1)); |
| buff += 3; |
| len += 11; |
| break; |
| case IPOPT_LSRR: |
| buff++; |
| if ((*buff - 3)% 4 != 0) return(1); |
| len += *buff; |
| opt->loose_route.route_size = (*buff -3)/4; |
| buff++; |
| if (*buff % 4 != 0) return(1); |
| opt->loose_route.pointer = *buff/4 - 1; |
| buff++; |
| buff++; |
| for (i = 0; i < opt->loose_route.route_size; i++) { |
| if(i>=MAX_ROUTE) |
| return(1); |
| opt->loose_route.route[i] = *(unsigned long *)buff; |
| buff += 4; |
| } |
| break; |
| case IPOPT_SSRR: |
| buff++; |
| if ((*buff - 3)% 4 != 0) return(1); |
| len += *buff; |
| opt->strict_route.route_size = (*buff -3)/4; |
| buff++; |
| if (*buff % 4 != 0) return(1); |
| opt->strict_route.pointer = *buff/4 - 1; |
| buff++; |
| buff++; |
| for (i = 0; i < opt->strict_route.route_size; i++) { |
| if(i>=MAX_ROUTE) |
| return(1); |
| opt->strict_route.route[i] = *(unsigned long *)buff; |
| buff += 4; |
| } |
| break; |
| case IPOPT_RR: |
| buff++; |
| if ((*buff - 3)% 4 != 0) return(1); |
| len += *buff; |
| opt->record_route.route_size = (*buff -3)/4; |
| buff++; |
| if (*buff % 4 != 0) return(1); |
| opt->record_route.pointer = *buff/4 - 1; |
| buff++; |
| buff++; |
| for (i = 0; i < opt->record_route.route_size; i++) { |
| if(i>=MAX_ROUTE) |
| return 1; |
| opt->record_route.route[i] = *(unsigned long *)buff; |
| buff += 4; |
| } |
| break; |
| case IPOPT_SID: |
| len += 4; |
| buff +=2; |
| opt->stream = *(unsigned short *)buff; |
| buff += 2; |
| break; |
| case IPOPT_TIMESTAMP: |
| buff++; |
| len += *buff; |
| if (*buff % 4 != 0) return(1); |
| opt->tstamp.len = *buff / 4 - 1; |
| buff++; |
| if ((*buff - 1) % 4 != 0) return(1); |
| opt->tstamp.ptr = (*buff-1)/4; |
| buff++; |
| opt->tstamp.x.full_char = *buff; |
| buff++; |
| for (i = 0; i < opt->tstamp.len; i++) { |
| opt->tstamp.data[i] = *(unsigned long *)buff; |
| buff += 4; |
| } |
| break; |
| default: |
| return(1); |
| } |
| |
| if (opt->record_route.route_size == 0) { |
| if (opt->strict_route.route_size != 0) { |
| memcpy(&(opt->record_route), &(opt->strict_route), |
| sizeof(opt->record_route)); |
| } else if (opt->loose_route.route_size != 0) { |
| memcpy(&(opt->record_route), &(opt->loose_route), |
| sizeof(opt->record_route)); |
| } |
| } |
| |
| if (opt->strict_route.route_size != 0 && |
| opt->strict_route.route_size != opt->strict_route.pointer) { |
| strict_route(iph, opt); |
| return(0); |
| } |
| |
| if (opt->loose_route.route_size != 0 && |
| opt->loose_route.route_size != opt->loose_route.pointer) { |
| loose_route(iph, opt); |
| return(0); |
| } |
| |
| return(0); |
| } |
| |
| /* This is a version of ip_compute_csum() optimized for IP headers, which |
| always checksum on 4 octet boundaries. */ |
| static inline unsigned short |
| ip_fast_csum(unsigned char * buff, int wlen) |
| { |
| unsigned long sum = 0; |
| __asm__("\t clc\n" |
| "1:\n" |
| "\t lodsl\n" |
| "\t adcl %%eax, %%ebx\n" |
| "\t loop 1b\n" |
| "\t adcl $0, %%ebx\n" |
| "\t movl %%ebx, %%eax\n" |
| "\t shrl $16, %%eax\n" |
| "\t addw %%ax, %%bx\n" |
| "\t adcw $0, %%bx\n" |
| : "=b" (sum) , "=S" (buff) |
| : "0" (sum), "c" (wlen) ,"1" (buff) |
| : "ax", "cx", "si", "bx" ); |
| return (~sum) & 0xffff; |
| } |
| |
| /* |
| * This routine does all the checksum computations that don't |
| * require anything special (like copying or special headers). |
| */ |
| unsigned short |
| ip_compute_csum(unsigned char * buff, int len) |
| { |
| unsigned long sum = 0; |
| |
| /* Do the first multiple of 4 bytes and convert to 16 bits. */ |
| if (len > 3) { |
| __asm__("\t clc\n" |
| "1:\n" |
| "\t lodsl\n" |
| "\t adcl %%eax, %%ebx\n" |
| "\t loop 1b\n" |
| "\t adcl $0, %%ebx\n" |
| "\t movl %%ebx, %%eax\n" |
| "\t shrl $16, %%eax\n" |
| "\t addw %%ax, %%bx\n" |
| "\t adcw $0, %%bx\n" |
| : "=b" (sum) , "=S" (buff) |
| : "0" (sum), "c" (len >> 2) ,"1" (buff) |
| : "ax", "cx", "si", "bx" ); |
| } |
| if (len & 2) { |
| __asm__("\t lodsw\n" |
| "\t addw %%ax, %%bx\n" |
| "\t adcw $0, %%bx\n" |
| : "=b" (sum), "=S" (buff) |
| : "0" (sum), "1" (buff) |
| : "bx", "ax", "si"); |
| } |
| if (len & 1) { |
| __asm__("\t lodsb\n" |
| "\t movb $0, %%ah\n" |
| "\t addw %%ax, %%bx\n" |
| "\t adcw $0, %%bx\n" |
| : "=b" (sum), "=S" (buff) |
| : "0" (sum), "1" (buff) |
| : "bx", "ax", "si"); |
| } |
| sum =~sum; |
| return(sum & 0xffff); |
| } |
| |
| /* Check the header of an incoming IP datagram. This version is still used in slhc.c. */ |
| int |
| ip_csum(struct iphdr *iph) |
| { |
| return (ip_fast_csum((unsigned char *)iph, iph->ihl) != 0); |
| } |
| |
| /* Generate a checksym for an outgoing IP datagram. */ |
| static void |
| ip_send_check(struct iphdr *iph) |
| { |
| iph->check = 0; |
| iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); |
| } |
| |
| #ifdef CONFIG_IP_FORWARD |
| |
| /* Forward an IP datagram to its next destination. */ |
| static void |
| ip_forward(struct sk_buff *skb, struct device *dev) |
| { |
| struct device *dev2; |
| struct iphdr *iph; |
| struct sk_buff *skb2; |
| struct rtable *rt; |
| unsigned char *ptr; |
| unsigned long raddr; |
| |
| /* |
| * According to the RFC, we must first decrease the TTL field. If |
| * that reaches zero, we must reply an ICMP control message telling |
| * that the packet's lifetime expired. |
| */ |
| iph = skb->h.iph; |
| iph->ttl--; |
| if (iph->ttl <= 0) { |
| DPRINTF((DBG_IP, "\nIP: *** datagram expired: TTL=0 (ignored) ***\n")); |
| DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr))); |
| DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr))); |
| |
| /* Tell the sender its packet died... */ |
| icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, dev); |
| return; |
| } |
| |
| /* Re-compute the IP header checksum. */ |
| ip_send_check(iph); |
| |
| /* |
| * OK, the packet is still valid. Fetch its destination address, |
| * and give it to the IP sender for further processing. |
| */ |
| rt = rt_route(iph->daddr, NULL); |
| if (rt == NULL) { |
| DPRINTF((DBG_IP, "\nIP: *** routing (phase I) failed ***\n")); |
| |
| /* Tell the sender its packet cannot be delivered... */ |
| icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, dev); |
| return; |
| } |
| |
| /* |
| * Gosh. Not only is the packet valid; we even know how to |
| * forward it onto its final destination. Can we say this |
| * is being plain lucky? |
| * If the router told us that there is no GW, use the dest. |
| * IP address itself- we seem to be connected directly... |
| */ |
| raddr = rt->rt_gateway; |
| if (raddr != 0) { |
| rt = rt_route(raddr, NULL); |
| if (rt == NULL) { |
| DPRINTF((DBG_IP, "\nIP: *** routing (phase II) failed ***\n")); |
| |
| /* Tell the sender its packet cannot be delivered... */ |
| icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, dev); |
| return; |
| } |
| if (rt->rt_gateway != 0) raddr = rt->rt_gateway; |
| } else raddr = iph->daddr; |
| dev2 = rt->rt_dev; |
| |
| /* |
| * We now allocate a new buffer, and copy the datagram into it. |
| * If the indicated interface is up and running, kick it. |
| */ |
| DPRINTF((DBG_IP, "\nIP: *** fwd %s -> ", in_ntoa(iph->saddr))); |
| DPRINTF((DBG_IP, "%s (via %s), LEN=%d\n", |
| in_ntoa(raddr), dev2->name, skb->len)); |
| |
| if (dev2->flags & IFF_UP) { |
| skb2 = (struct sk_buff *) alloc_skb(sizeof(struct sk_buff) + |
| dev2->hard_header_len + skb->len, GFP_ATOMIC); |
| if (skb2 == NULL) { |
| printk("\nIP: No memory available for IP forward\n"); |
| return; |
| } |
| ptr = (unsigned char *)(skb2 + 1); |
| skb2->sk = NULL; |
| skb2->free = 1; |
| skb2->len = skb->len + dev2->hard_header_len; |
| skb2->mem_addr = skb2; |
| skb2->mem_len = sizeof(struct sk_buff) + skb2->len; |
| skb2->next = NULL; |
| skb2->h.raw = ptr; |
| |
| /* Copy the packet data into the new buffer. */ |
| skb2->h.raw = ptr; |
| memcpy(ptr + dev2->hard_header_len, skb->h.raw, skb->len); |
| |
| /* Now build the MAC header. */ |
| (void) ip_send(skb2, raddr, skb->len, dev2, dev2->pa_addr); |
| |
| dev2->queue_xmit(skb2, dev2, SOPRI_NORMAL); |
| } |
| } |
| |
| |
| #endif |
| |
| /* This function receives all incoming IP datagrams. */ |
| int |
| ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) |
| { |
| struct iphdr *iph = skb->h.iph; |
| unsigned char hash; |
| unsigned char flag = 0; |
| unsigned char opts_p = 0; /* Set iff the packet has options. */ |
| struct inet_protocol *ipprot; |
| static struct options opt; /* since we don't use these yet, and they |
| take up stack space. */ |
| int brd; |
| |
| DPRINTF((DBG_IP, "<<\n")); |
| |
| /* Is the datagram acceptable? */ |
| if (iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0) { |
| DPRINTF((DBG_IP, "\nIP: *** datagram error ***\n")); |
| DPRINTF((DBG_IP, " SRC = %s ", in_ntoa(iph->saddr))); |
| DPRINTF((DBG_IP, " DST = %s (ignored)\n", in_ntoa(iph->daddr))); |
| skb->sk = NULL; |
| kfree_skb(skb, FREE_WRITE); |
| return(0); |
| } |
| |
| if (iph->ihl != 5) { /* Fast path for the typical optionless IP packet. */ |
| ip_print(iph); /* Bogus, only for debugging. */ |
| memset((char *) &opt, 0, sizeof(opt)); |
| if (do_options(iph, &opt) != 0) |
| return 0; |
| opts_p = 1; |
| } |
| |
| /* Do any IP forwarding required. chk_addr() is expensive -- avoid it someday. */ |
| if ((brd = chk_addr(iph->daddr)) == 0) { |
| #ifdef CONFIG_IP_FORWARD |
| ip_forward(skb, dev); |
| #endif |
| printk("Machine %x tried to use us as a forwarder to %x but we have forwarding disabled!\n", |
| iph->saddr,iph->daddr); |
| skb->sk = NULL; |
| kfree_skb(skb, FREE_WRITE); |
| return(0); |
| } |
| |
| if(brd==IS_INVBCAST) |
| { |
| /* printk("Invalid broadcast address from %x [target %x] (Probably they have a wrong netmask)\n", |
| iph->saddr,iph->daddr);*/ |
| skb->sk=NULL; |
| kfree_skb(skb,FREE_WRITE); |
| return(0); |
| } |
| |
| /* |
| * Reassemble IP fragments. |
| */ |
| |
| if ((iph->frag_off & 0x0020) || (ntohs(iph->frag_off) & 0x1fff)) { |
| #ifdef CONFIG_IP_DEFRAG |
| ip_defrag(skb); |
| #else |
| printk("\nIP: *** datagram fragmentation not yet implemented ***\n"); |
| printk(" SRC = %s ", in_ntoa(iph->saddr)); |
| printk(" DST = %s (ignored)\n", in_ntoa(iph->daddr)); |
| icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev); |
| skb->sk = NULL; |
| kfree_skb(skb, FREE_WRITE); |
| return(0); |
| #endif |
| } |
| |
| /* Point into the IP datagram, just past the header. */ |
| skb->h.raw += iph->ihl*4; |
| hash = iph->protocol & (MAX_INET_PROTOS -1); |
| for (ipprot = (struct inet_protocol *)inet_protos[hash]; |
| ipprot != NULL; |
| ipprot=(struct inet_protocol *)ipprot->next) |
| { |
| struct sk_buff *skb2; |
| |
| if (ipprot->protocol != iph->protocol) continue; |
| DPRINTF((DBG_IP, "Using protocol = %X:\n", ipprot)); |
| print_ipprot(ipprot); |
| |
| /* |
| * See if we need to make a copy of it. This will |
| * only be set if more than one protocol wants it. |
| * and then not for the last one. |
| */ |
| if (ipprot->copy) { |
| skb2 = alloc_skb(skb->mem_len, GFP_ATOMIC); |
| if (skb2 == NULL) |
| continue; |
| memcpy(skb2, skb, skb->mem_len); |
| skb2->mem_addr = skb2; |
| skb2->h.raw = (unsigned char *)( |
| (unsigned long)skb2 + |
| (unsigned long) skb->h.raw - |
| (unsigned long)skb); |
| skb2->free=1; |
| } else { |
| skb2 = skb; |
| } |
| flag = 1; |
| |
| /* |
| * Pass on the datagram to each protocol that wants it, |
| * based on the datagram protocol. We should really |
| * check the protocol handler's return values here... |
| */ |
| ipprot->handler(skb2, dev, opts_p ? &opt : 0, iph->daddr, |
| (ntohs(iph->tot_len) - (iph->ihl * 4)), |
| iph->saddr, 0, ipprot); |
| |
| } |
| |
| /* |
| * All protocols checked. |
| * If this packet was a broadcast, we may *not* reply to it, since that |
| * causes (proven, grin) ARP storms and a leakage of memory (i.e. all |
| * ICMP reply messages get queued up for transmission...) |
| */ |
| if (!flag) { |
| if (brd != IS_BROADCAST) |
| icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev); |
| skb->sk = NULL; |
| kfree_skb(skb, FREE_WRITE); |
| } |
| |
| return(0); |
| } |
| |
| |
| /* |
| * Queues a packet to be sent, and starts the transmitter |
| * if necessary. if free = 1 then we free the block after |
| * transmit, otherwise we don't. |
| * This routine also needs to put in the total length, and |
| * compute the checksum. |
| */ |
| void |
| ip_queue_xmit(struct sock *sk, struct device *dev, |
| struct sk_buff *skb, int free) |
| { |
| struct iphdr *iph; |
| unsigned char *ptr; |
| |
| if (sk == NULL) free = 1; |
| if (dev == NULL) { |
| printk("IP: ip_queue_xmit dev = NULL\n"); |
| return; |
| } |
| IS_SKB(skb); |
| skb->free = free; |
| skb->dev = dev; |
| skb->when = jiffies; |
| |
| DPRINTF((DBG_IP, ">>\n")); |
| ptr = (unsigned char *)(skb + 1); |
| ptr += dev->hard_header_len; |
| iph = (struct iphdr *)ptr; |
| iph->tot_len = ntohs(skb->len - dev->hard_header_len); |
| ip_send_check(iph); |
| ip_print(iph); |
| skb->next = NULL; |
| |
| /* See if this is the one trashing our queue. Ross? */ |
| skb->magic = 1; |
| if (!free) { |
| skb->link3 = NULL; |
| sk->packets_out++; |
| cli(); |
| if (sk->send_head == NULL) { |
| sk->send_tail = skb; |
| sk->send_head = skb; |
| } else { |
| /* See if we've got a problem. */ |
| if (sk->send_tail == NULL) { |
| printk("IP: ***bug sk->send_tail == NULL != sk->send_head\n"); |
| sort_send(sk); |
| } else { |
| sk->send_tail->link3 = skb; |
| sk->send_tail = skb; |
| } |
| } |
| sti(); |
| reset_timer(sk, TIME_WRITE, |
| backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); |
| } else { |
| skb->sk = sk; |
| } |
| |
| /* If the indicated interface is up and running, kick it. */ |
| if (dev->flags & IFF_UP) { |
| if (sk != NULL) { |
| dev->queue_xmit(skb, dev, sk->priority); |
| } |
| else { |
| dev->queue_xmit(skb, dev, SOPRI_NORMAL); |
| } |
| } else { |
| if (free) kfree_skb(skb, FREE_WRITE); |
| } |
| } |
| |
| |
| void |
| ip_retransmit(struct sock *sk, int all) |
| { |
| struct sk_buff * skb; |
| struct proto *prot; |
| struct device *dev; |
| |
| prot = sk->prot; |
| skb = sk->send_head; |
| while (skb != NULL) { |
| dev = skb->dev; |
| /* I know this can't happen but as it does.. */ |
| if(dev==NULL) |
| { |
| printk("ip_retransmit: NULL device bug!\n"); |
| goto oops; |
| } |
| |
| IS_SKB(skb); |
| |
| /* |
| * The rebuild_header function sees if the ARP is done. |
| * If not it sends a new ARP request, and if so it builds |
| * the header. |
| */ |
| cli(); /* We might get interrupted by an arp reply here and fill |
| the frame in twice. Because of the technique used this |
| would be a little sad */ |
| if (!skb->arp) { |
| if (dev->rebuild_header((struct enet_header *)(skb+1),dev)) { |
| sti(); /* Failed to rebuild - next */ |
| if (!all) break; |
| skb = (struct sk_buff *)skb->link3; |
| continue; |
| } |
| } |
| skb->arp = 1; |
| sti(); |
| skb->when = jiffies; |
| |
| /* If the interface is (still) up and running, kick it. */ |
| if (dev->flags & IFF_UP) { |
| if (sk) dev->queue_xmit(skb, dev, sk->priority); |
| /* else dev->queue_xmit(skb, dev, SOPRI_NORMAL ); CANNOT HAVE SK=NULL HERE */ |
| } |
| |
| oops: sk->retransmits++; |
| sk->prot->retransmits ++; |
| if (!all) break; |
| |
| /* This should cut it off before we send too many packets. */ |
| if (sk->retransmits > sk->cong_window) break; |
| skb = (struct sk_buff *)skb->link3; |
| } |
| |
| /* |
| * Increase the RTT time every time we retransmit. |
| * This will cause exponential back off on how hard we try to |
| * get through again. Once we get through, the rtt will settle |
| * back down reasonably quickly. |
| */ |
| sk->backoff++; |
| reset_timer(sk, TIME_WRITE, backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)); |
| } |
| |
| /* Backoff function - the subject of much research */ |
| int backoff(int n) |
| { |
| /* Use binary exponential up to retry #4, and quadratic after that |
| * This yields the sequence |
| * 1, 2, 4, 8, 16, 25, 36, 49, 64, 81, 100 ... |
| */ |
| |
| if(n<0) |
| { |
| printk("Backoff < 0!\n"); |
| return 16; /* Make up a value */ |
| } |
| |
| if(n <= 4) |
| return 1 << n; /* Binary exponential back off */ |
| else |
| { |
| if(n<255) |
| return n * n; /* Quadratic back off */ |
| else |
| { |
| printk("Overloaded backoff!\n"); |
| return 255*255; |
| } |
| } |
| } |