| /* |
| * 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. |
| * Fred Van Kempen : IP fragment support (borrowed from NET2E) |
| * Gerhard Koerting: Forward fragmented frames correctly. |
| * Gerhard Koerting: Fixes to my fix of the above 8-). |
| * Gerhard Koerting: IP interface addressing fix. |
| * Linus Torvalds : More robustness checks |
| * Alan Cox : Even more checks: Still not as robust as it ought to be |
| * Alan Cox : Save IP header pointer for later |
| * Alan Cox : ip option setting |
| * Alan Cox : Use ip_tos/ip_ttl settings |
| * |
| * 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" |
| |
| #define CONFIG_IP_FORWARD |
| #define CONFIG_IP_DEFRAG |
| |
| 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 = skb->data; |
| 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, int tos, int ttl) |
| { |
| 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 = skb->data; |
| |
| /* 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 (saddr == 0x0100007FL && 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 = tos; |
| iph->frag_off = 0; |
| iph->ttl = ttl; |
| 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; |
| |
| if (wlen) { |
| unsigned long bogus; |
| __asm__("clc\n" |
| "1:\t" |
| "lodsl\n\t" |
| "adcl %3, %0\n\t" |
| "decl %2\n\t" |
| "jne 1b\n\t" |
| "adcl $0, %0\n\t" |
| "movl %0, %3\n\t" |
| "shrl $16, %3\n\t" |
| "addw %w3, %w0\n\t" |
| "adcw $0, %w0" |
| : "=r" (sum), "=S" (buff), "=r" (wlen), "=a" (bogus) |
| : "0" (sum), "1" (buff), "2" (wlen)); |
| } |
| 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__("clc\n" |
| "1:\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" |
| : "=b" (sum) , "=S" (buff) |
| : "0" (sum), "c" (len >> 2) ,"1" (buff) |
| : "ax", "cx", "si", "bx" ); |
| } |
| if (len & 2) { |
| __asm__("lodsw\n\t" |
| "addw %%ax, %%bx\n\t" |
| "adcw $0, %%bx" |
| : "=b" (sum), "=S" (buff) |
| : "0" (sum), "1" (buff) |
| : "bx", "ax", "si"); |
| } |
| if (len & 1) { |
| __asm__("lodsb\n\t" |
| "movb $0, %%ah\n\t" |
| "addw %%ax, %%bx\n\t" |
| "adcw $0, %%bx" |
| : "=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); |
| } |
| |
| /* 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); |
| } |
| |
| /************************ Fragment Handlers From NET2E not yet with tweaks to beat 4K **********************************/ |
| |
| static struct ipq *ipqueue = NULL; /* IP fragment queue */ |
| /* Create a new fragment entry. */ |
| static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr) |
| { |
| struct ipfrag *fp; |
| |
| fp = (struct ipfrag *) kmalloc(sizeof(struct ipfrag), GFP_ATOMIC); |
| if (fp == NULL) |
| { |
| printk("IP: frag_create: no memory left !\n"); |
| return(NULL); |
| } |
| memset(fp, 0, sizeof(struct ipfrag)); |
| |
| /* Fill in the structure. */ |
| fp->offset = offset; |
| fp->end = end; |
| fp->len = end - offset; |
| fp->skb = skb; |
| fp->ptr = ptr; |
| |
| return(fp); |
| } |
| |
| |
| /* |
| * Find the correct entry in the "incomplete datagrams" queue for |
| * this IP datagram, and return the queue entry address if found. |
| */ |
| static struct ipq *ip_find(struct iphdr *iph) |
| { |
| struct ipq *qp; |
| struct ipq *qplast; |
| |
| cli(); |
| qplast = NULL; |
| for(qp = ipqueue; qp != NULL; qplast = qp, qp = qp->next) |
| { |
| if (iph->id== qp->iph->id && iph->saddr == qp->iph->saddr && |
| iph->daddr == qp->iph->daddr && iph->protocol == qp->iph->protocol) |
| { |
| del_timer(&qp->timer); /* So it doesnt vanish on us. The timer will be reset anyway */ |
| sti(); |
| return(qp); |
| } |
| } |
| sti(); |
| return(NULL); |
| } |
| |
| |
| /* |
| * Remove an entry from the "incomplete datagrams" queue, either |
| * because we completed, reassembled and processed it, or because |
| * it timed out. |
| */ |
| |
| static void ip_free(struct ipq *qp) |
| { |
| struct ipfrag *fp; |
| struct ipfrag *xp; |
| |
| /* Stop the timer for this entry. */ |
| /* printk("ip_free\n");*/ |
| del_timer(&qp->timer); |
| |
| /* Remove this entry from the "incomplete datagrams" queue. */ |
| cli(); |
| if (qp->prev == NULL) |
| { |
| ipqueue = qp->next; |
| if (ipqueue != NULL) |
| ipqueue->prev = NULL; |
| } |
| else |
| { |
| qp->prev->next = qp->next; |
| if (qp->next != NULL) |
| qp->next->prev = qp->prev; |
| } |
| |
| /* Release all fragment data. */ |
| /* printk("ip_free: kill frag data\n");*/ |
| fp = qp->fragments; |
| while (fp != NULL) |
| { |
| xp = fp->next; |
| IS_SKB(fp->skb); |
| kfree_skb(fp->skb,FREE_READ); |
| kfree_s(fp, sizeof(struct ipfrag)); |
| fp = xp; |
| } |
| |
| /* printk("ip_free: cleanup\n");*/ |
| |
| /* Release the MAC header. */ |
| kfree_s(qp->mac, qp->maclen); |
| |
| /* Release the IP header. */ |
| kfree_s(qp->iph, qp->ihlen + 8); |
| |
| /* Finally, release the queue descriptor itself. */ |
| kfree_s(qp, sizeof(struct ipq)); |
| /* printk("ip_free:done\n");*/ |
| sti(); |
| } |
| |
| |
| /* Oops- a fragment queue timed out. Kill it and send an ICMP reply. */ |
| |
| static void ip_expire(unsigned long arg) |
| { |
| struct ipq *qp; |
| |
| qp = (struct ipq *)arg; |
| DPRINTF((DBG_IP, "IP: queue_expire: fragment queue 0x%X timed out!\n", qp)); |
| |
| /* Send an ICMP "Fragment Reassembly Timeout" message. */ |
| #if 0 |
| icmp_send(qp->iph->ip_src.s_addr, ICMP_TIME_EXCEEDED, |
| ICMP_EXC_FRAGTIME, qp->iph); |
| #endif |
| if(qp->fragments!=NULL) |
| icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED, |
| ICMP_EXC_FRAGTIME, qp->dev); |
| |
| /* Nuke the fragment queue. */ |
| ip_free(qp); |
| } |
| |
| |
| /* |
| * Add an entry to the 'ipq' queue for a newly received IP datagram. |
| * We will (hopefully :-) receive all other fragments of this datagram |
| * in time, so we just create a queue for this datagram, in which we |
| * will insert the received fragments at their respective positions. |
| */ |
| |
| static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev) |
| { |
| struct ipq *qp; |
| int maclen; |
| int ihlen; |
| |
| qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC); |
| if (qp == NULL) |
| { |
| printk("IP: create: no memory left !\n"); |
| return(NULL); |
| } |
| memset(qp, 0, sizeof(struct ipq)); |
| |
| /* Allocate memory for the MAC header. */ |
| maclen = ((unsigned long) iph) - ((unsigned long) skb->data); |
| qp->mac = (unsigned char *) kmalloc(maclen, GFP_ATOMIC); |
| if (qp->mac == NULL) |
| { |
| printk("IP: create: no memory left !\n"); |
| kfree_s(qp, sizeof(struct ipq)); |
| return(NULL); |
| } |
| |
| /* Allocate memory for the IP header (plus 8 octects for ICMP). */ |
| ihlen = (iph->ihl * sizeof(unsigned long)); |
| qp->iph = (struct iphdr *) kmalloc(ihlen + 8, GFP_ATOMIC); |
| if (qp->iph == NULL) |
| { |
| printk("IP: create: no memory left !\n"); |
| kfree_s(qp->mac, maclen); |
| kfree_s(qp, sizeof(struct ipq)); |
| return(NULL); |
| } |
| |
| /* Fill in the structure. */ |
| memcpy(qp->mac, skb->data, maclen); |
| memcpy(qp->iph, iph, ihlen + 8); |
| qp->len = 0; |
| qp->ihlen = ihlen; |
| qp->maclen = maclen; |
| qp->fragments = NULL; |
| qp->dev = dev; |
| /* printk("Protocol = %d\n",qp->iph->protocol);*/ |
| |
| /* Start a timer for this entry. */ |
| qp->timer.expires = IP_FRAG_TIME; /* about 30 seconds */ |
| qp->timer.data = (unsigned long) qp; /* pointer to queue */ |
| qp->timer.function = ip_expire; /* expire function */ |
| add_timer(&qp->timer); |
| |
| /* Add this entry to the queue. */ |
| qp->prev = NULL; |
| cli(); |
| qp->next = ipqueue; |
| if (qp->next != NULL) |
| qp->next->prev = qp; |
| ipqueue = qp; |
| sti(); |
| return(qp); |
| } |
| |
| |
| /* See if a fragment queue is complete. */ |
| static int ip_done(struct ipq *qp) |
| { |
| struct ipfrag *fp; |
| int offset; |
| |
| /* Only possible if we received the final fragment. */ |
| if (qp->len == 0) |
| return(0); |
| |
| /* Check all fragment offsets to see if they connect. */ |
| fp = qp->fragments; |
| offset = 0; |
| while (fp != NULL) |
| { |
| if (fp->offset > offset) |
| return(0); /* fragment(s) missing */ |
| offset = fp->end; |
| fp = fp->next; |
| } |
| |
| /* All fragments are present. */ |
| return(1); |
| } |
| |
| |
| /* Build a new IP datagram from all its fragments. */ |
| static struct sk_buff *ip_glue(struct ipq *qp) |
| { |
| struct sk_buff *skb; |
| struct iphdr *iph; |
| struct ipfrag *fp; |
| unsigned char *ptr; |
| int count, len; |
| |
| /* Allocate a new buffer for the datagram. */ |
| len = sizeof(struct sk_buff)+qp->maclen + qp->ihlen + qp->len; |
| if ((skb = alloc_skb(len,GFP_ATOMIC)) == NULL) |
| { |
| printk("IP: queue_glue: no memory for glueing queue 0x%X\n", (int) qp); |
| ip_free(qp); |
| return(NULL); |
| } |
| |
| /* Fill in the basic details. */ |
| skb->len = (len - qp->maclen); |
| skb->h.raw = skb->data; |
| skb->free = 1; |
| skb->lock = 1; |
| |
| /* Copy the original MAC and IP headers into the new buffer. */ |
| ptr = (unsigned char *) skb->h.raw; |
| memcpy(ptr, ((unsigned char *) qp->mac), qp->maclen); |
| /* printk("Copied %d bytes of mac header.\n",qp->maclen);*/ |
| ptr += qp->maclen; |
| memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen); |
| /* printk("Copied %d byte of ip header.\n",qp->ihlen);*/ |
| ptr += qp->ihlen; |
| skb->h.raw += qp->maclen; |
| |
| /* printk("Protocol = %d\n",skb->h.iph->protocol);*/ |
| count = 0; |
| |
| /* Copy the data portions of all fragments into the new buffer. */ |
| fp = qp->fragments; |
| while(fp != NULL) |
| { |
| if(count+fp->len>skb->len) |
| { |
| printk("Invalid fragment list: Fragment over size.\n"); |
| kfree_skb(skb,FREE_WRITE); |
| return NULL; |
| } |
| /* printk("Fragment %d size %d\n",fp->offset,fp->len);*/ |
| memcpy((ptr + fp->offset), fp->ptr, fp->len); |
| count += fp->len; |
| fp = fp->next; |
| } |
| |
| /* We glued together all fragments, so remove the queue entry. */ |
| ip_free(qp); |
| |
| /* Done with all fragments. Fixup the new IP header. */ |
| iph = skb->h.iph; |
| iph->frag_off = 0; |
| iph->tot_len = htons((iph->ihl * sizeof(unsigned long)) + count); |
| skb->ip_hdr = iph; |
| return(skb); |
| } |
| |
| |
| /* Process an incoming IP datagram fragment. */ |
| static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev) |
| { |
| struct ipfrag *prev, *next; |
| struct ipfrag *tfp; |
| struct ipq *qp; |
| struct sk_buff *skb2; |
| unsigned char *ptr; |
| int flags, offset; |
| int i, ihl, end; |
| |
| /* Find the entry of this IP datagram in the "incomplete datagrams" queue. */ |
| qp = ip_find(iph); |
| |
| /* Is this a non-fragmented datagram? */ |
| offset = ntohs(iph->frag_off); |
| flags = offset & ~IP_OFFSET; |
| offset &= IP_OFFSET; |
| if (((flags & IP_MF) == 0) && (offset == 0)) |
| { |
| if (qp != NULL) |
| ip_free(qp); /* Huh? How could this exist?? */ |
| return(skb); |
| } |
| offset <<= 3; /* offset is in 8-byte chunks */ |
| |
| /* |
| * If the queue already existed, keep restarting its timer as long |
| * as we still are receiving fragments. Otherwise, create a fresh |
| * queue entry. |
| */ |
| if (qp != NULL) |
| { |
| del_timer(&qp->timer); |
| qp->timer.expires = IP_FRAG_TIME; /* about 30 seconds */ |
| qp->timer.data = (unsigned long) qp; /* pointer to queue */ |
| qp->timer.function = ip_expire; /* expire function */ |
| add_timer(&qp->timer); |
| } |
| else |
| { |
| if ((qp = ip_create(skb, iph, dev)) == NULL) |
| return(NULL); |
| } |
| |
| /* Determine the position of this fragment. */ |
| ihl = (iph->ihl * sizeof(unsigned long)); |
| end = offset + ntohs(iph->tot_len) - ihl; |
| |
| /* Point into the IP datagram 'data' part. */ |
| ptr = skb->data + dev->hard_header_len + ihl; |
| |
| /* Is this the final fragment? */ |
| if ((flags & IP_MF) == 0) |
| qp->len = end; |
| |
| /* |
| * Find out which fragments are in front and at the back of us |
| * in the chain of fragments so far. We must know where to put |
| * this fragment, right? |
| */ |
| prev = NULL; |
| for(next = qp->fragments; next != NULL; next = next->next) |
| { |
| if (next->offset > offset) |
| break; /* bingo! */ |
| prev = next; |
| } |
| |
| /* |
| * We found where to put this one. |
| * Check for overlap with preceeding fragment, and, if needed, |
| * align things so that any overlaps are eliminated. |
| */ |
| if (prev != NULL && offset < prev->end) |
| { |
| i = prev->end - offset; |
| offset += i; /* ptr into datagram */ |
| ptr += i; /* ptr into fragment data */ |
| DPRINTF((DBG_IP, "IP: defrag: fixed low overlap %d bytes\n", i)); |
| } |
| |
| /* |
| * Look for overlap with succeeding segments. |
| * If we can merge fragments, do it. |
| */ |
| |
| for(; next != NULL; next = tfp) |
| { |
| tfp = next->next; |
| if (next->offset >= end) |
| break; /* no overlaps at all */ |
| |
| i = end - next->offset; /* overlap is 'i' bytes */ |
| next->len -= i; /* so reduce size of */ |
| next->offset += i; /* next fragment */ |
| next->ptr += i; |
| |
| /* If we get a frag size of <= 0, remove it. */ |
| if (next->len <= 0) |
| { |
| DPRINTF((DBG_IP, "IP: defrag: removing frag 0x%X (len %d)\n", |
| next, next->len)); |
| if (next->prev != NULL) |
| next->prev->next = next->next; |
| else |
| qp->fragments = next->next; |
| |
| if (tfp->next != NULL) |
| next->next->prev = next->prev; |
| |
| kfree_s(next, sizeof(struct ipfrag)); |
| } |
| DPRINTF((DBG_IP, "IP: defrag: fixed high overlap %d bytes\n", i)); |
| } |
| |
| /* Insert this fragment in the chain of fragments. */ |
| tfp = NULL; |
| tfp = ip_frag_create(offset, end, skb, ptr); |
| tfp->prev = prev; |
| tfp->next = next; |
| if (prev != NULL) |
| prev->next = tfp; |
| else |
| qp->fragments = tfp; |
| |
| if (next != NULL) |
| next->prev = tfp; |
| |
| /* |
| * OK, so we inserted this new fragment into the chain. |
| * Check if we now have a full IP datagram which we can |
| * bump up to the IP layer... |
| */ |
| |
| if (ip_done(qp)) |
| { |
| skb2 = ip_glue(qp); /* glue together the fragments */ |
| return(skb2); |
| } |
| return(NULL); |
| } |
| |
| |
| /* |
| * This IP datagram is too large to be sent in one piece. Break it up into |
| * smaller pieces (each of size equal to the MAC header plus IP header plus |
| * a block of the data of the original IP data part) that will yet fit in a |
| * single device frame, and queue such a frame for sending by calling the |
| * ip_queue_xmit(). Note that this is recursion, and bad things will happen |
| * if this function causes a loop... |
| */ |
| void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag) |
| { |
| struct iphdr *iph; |
| unsigned char *raw; |
| unsigned char *ptr; |
| struct sk_buff *skb2; |
| int left, mtu, hlen, len; |
| int offset; |
| |
| /* Point into the IP datagram header. */ |
| raw = skb->data; |
| iph = (struct iphdr *) (raw + dev->hard_header_len); |
| |
| /* Setup starting values. */ |
| hlen = (iph->ihl * sizeof(unsigned long)); |
| left = ntohs(iph->tot_len) - hlen; |
| hlen += dev->hard_header_len; |
| mtu = (dev->mtu - hlen); |
| ptr = (raw + hlen); |
| |
| DPRINTF((DBG_IP, "IP: Fragmentation Desired\n")); |
| DPRINTF((DBG_IP, " DEV=%s, MTU=%d, LEN=%d SRC=%s", |
| dev->name, dev->mtu, left, in_ntoa(iph->saddr))); |
| DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr))); |
| |
| /* Check for any "DF" flag. */ |
| if (ntohs(iph->frag_off) & IP_DF) |
| { |
| DPRINTF((DBG_IP, "IP: Fragmentation Desired, but DF set !\n")); |
| DPRINTF((DBG_IP, " DEV=%s, MTU=%d, LEN=%d SRC=%s", |
| dev->name, dev->mtu, left, in_ntoa(iph->saddr))); |
| DPRINTF((DBG_IP, " DST=%s\n", in_ntoa(iph->daddr))); |
| |
| /* |
| * FIXME: |
| * We should send an ICMP warning message here! |
| */ |
| |
| icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev); |
| return; |
| } |
| |
| /* Fragment the datagram. */ |
| if (is_frag & 2) |
| offset = (ntohs(iph->frag_off) & 0x1fff) << 3; |
| else |
| offset = 0; |
| while(left > 0) |
| { |
| len = left; |
| if (len+8 > mtu) |
| len = (dev->mtu - hlen - 8); |
| if ((left - len) >= 8) |
| { |
| len /= 8; |
| len *= 8; |
| } |
| DPRINTF((DBG_IP,"IP: frag: creating fragment of %d bytes (%d total)\n", |
| len, len + hlen)); |
| |
| /* Allocate buffer. */ |
| if ((skb2 = alloc_skb(sizeof(struct sk_buff) + len + hlen,GFP_KERNEL)) == NULL) |
| { |
| printk("IP: frag: no memory for new fragment!\n"); |
| return; |
| } |
| skb2->arp = skb->arp; |
| skb2->free = skb->free; |
| skb2->len = len + hlen; |
| skb2->h.raw=(char *) skb2->data; |
| |
| if (sk) |
| sk->wmem_alloc += skb2->mem_len; |
| |
| /* Copy the packet header into the new buffer. */ |
| memcpy(skb2->h.raw, raw, hlen); |
| |
| /* Copy a block of the IP datagram. */ |
| memcpy(skb2->h.raw + hlen, ptr, len); |
| left -= len; |
| |
| skb2->h.raw+=dev->hard_header_len; |
| /* Fill in the new header fields. */ |
| iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/); |
| iph->frag_off = htons((offset >> 3)); |
| /* Added AC : If we are fragmenting a fragment thats not the |
| last fragment then keep MF on each bit */ |
| if (left > 0 || (is_frag & 1)) |
| iph->frag_off |= htons(IP_MF); |
| ptr += len; |
| offset += len; |
| /* printk("Queue frag\n");*/ |
| |
| /* Put this fragment into the sending queue. */ |
| ip_queue_xmit(sk, dev, skb2, 1); |
| /* printk("Queued\n");*/ |
| } |
| } |
| |
| |
| |
| #ifdef CONFIG_IP_FORWARD |
| |
| /* Forward an IP datagram to its next destination. */ |
| static void |
| ip_forward(struct sk_buff *skb, struct device *dev, int is_frag) |
| { |
| struct device *dev2; |
| struct iphdr *iph; |
| struct sk_buff *skb2; |
| struct rtable *rt; |
| unsigned char *ptr; |
| unsigned long raddr; |
| |
| /* |
| * Only forward packets that were fired at us when we are in promiscuous |
| * mode. In standard mode we rely on the driver to filter for us. |
| */ |
| |
| if(dev->flags&IFF_PROMISC) |
| { |
| if(memcmp((char *)&skb[1],dev->dev_addr,dev->addr_len)) |
| return; |
| } |
| |
| /* |
| * 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; |
| |
| |
| if (dev == dev2) |
| return; |
| /* |
| * 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 = skb2->data; |
| 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. */ |
| 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); |
| |
| if(skb2->len > dev2->mtu) |
| { |
| ip_fragment(NULL,skb2,dev2, is_frag); |
| kfree_skb(skb2,FREE_WRITE); |
| } |
| else |
| 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; |
| int is_frag=0; |
| |
| DPRINTF((DBG_IP, "<<\n")); |
| |
| skb->ip_hdr = iph; /* Fragments can cause ICMP errors too! */ |
| /* Is the datagram acceptable? */ |
| if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || 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; |
| } |
| |
| if (iph->frag_off & 0x0020) |
| is_frag|=1; |
| if (ntohs(iph->frag_off) & 0x1fff) |
| is_frag|=2; |
| |
| /* 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, is_frag); |
| #else |
| printk("Machine %x tried to use us as a forwarder to %x but we have forwarding disabled!\n", |
| iph->saddr,iph->daddr); |
| #endif |
| skb->sk = NULL; |
| kfree_skb(skb, FREE_WRITE); |
| return(0); |
| } |
| |
| /* |
| * Reassemble IP fragments. |
| */ |
| |
| if(is_frag) |
| { |
| #ifdef CONFIG_IP_DEFRAG |
| skb=ip_defrag(iph,skb,dev); |
| if(skb==NULL) |
| { |
| return 0; |
| } |
| iph=skb->h.iph; |
| #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 |
| } |
| |
| |
| |
| 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); |
| } |
| |
| /* Point into the IP datagram, just past the header. */ |
| |
| skb->ip_hdr = iph; |
| 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->ip_hdr = (struct iphdr *)( |
| (unsigned long)skb2 + |
| (unsigned long) skb->ip_hdr - |
| (unsigned long)skb); |
| 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 = skb->data; |
| ptr += dev->hard_header_len; |
| iph = (struct iphdr *)ptr; |
| iph->tot_len = ntohs(skb->len-dev->hard_header_len); |
| |
| if(skb->len > dev->mtu) |
| { |
| /* printk("Fragment!\n");*/ |
| ip_fragment(sk,skb,dev,0); |
| IS_SKB(skb); |
| kfree_skb(skb,FREE_WRITE); |
| return; |
| } |
| |
| 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(skb->data, 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 && !skb_device_locked(skb)) |
| 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; |
| } |
| } |
| } |
| |
| /* |
| * Socket option code for IP. This is the end of the line after any TCP,UDP etc options on |
| * an IP socket. |
| */ |
| |
| int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) |
| { |
| int val,err; |
| |
| if (optval == NULL) |
| return(-EINVAL); |
| |
| err=verify_area(VERIFY_READ, optval, sizeof(int)); |
| if(err) |
| return err; |
| |
| val = get_fs_long((unsigned long *)optval); |
| |
| if(level!=SOL_IP) |
| return -EOPNOTSUPP; |
| |
| switch(optname) |
| { |
| case IP_TOS: |
| if(val<0||val>255) |
| return -EINVAL; |
| sk->ip_tos=val; |
| return 0; |
| case IP_TTL: |
| if(val<1||val>255) |
| return -EINVAL; |
| sk->ip_ttl=val; |
| return 0; |
| /* IP_OPTIONS and friends go here eventually */ |
| default: |
| return(-ENOPROTOOPT); |
| } |
| } |
| |
| int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) |
| { |
| int val,err; |
| |
| if(level!=SOL_IP) |
| return -EOPNOTSUPP; |
| |
| switch(optname) |
| { |
| case IP_TOS: |
| val=sk->ip_tos; |
| break; |
| case IP_TTL: |
| val=sk->ip_ttl; |
| break; |
| default: |
| return(-ENOPROTOOPT); |
| } |
| err=verify_area(VERIFY_WRITE, optlen, sizeof(int)); |
| if(err) |
| return err; |
| put_fs_long(sizeof(int),(unsigned long *) optlen); |
| |
| err=verify_area(VERIFY_WRITE, optval, sizeof(int)); |
| if(err) |
| return err; |
| put_fs_long(val,(unsigned long *)optval); |
| |
| return(0); |
| } |