📄 ip.c
字号:
/* * 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 * Alan Cox : Fragmentation bogosity removed * (Thanks to Mark.Bush@prg.ox.ac.uk) * Dmitry Gorodchanin : Send of a raw packet crash fix. * Alan Cox : Silly ip bug when an overlength * fragment turns up. Now frees the * queue. * * 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_DEFRAGextern int last_retran;extern void sort_send(struct sock *sk);#define min(a,b) ((a)<(b)?(a):(b))voidip_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");}intip_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 voidstrict_route(struct iphdr *iph, struct options *opt){}static voidloose_route(struct iphdr *iph, struct options *opt){}static voidprint_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. */voidip_route_check(unsigned long daddr){}#if 0/* this routine puts the options at the end of an ip header. */static intbuild_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 intip_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. */intip_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 intdo_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 shortip_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 shortip_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. */intip_csum(struct iphdr *iph){ return ip_fast_csum((unsigned char *)iph, iph->ihl);}/* Generate a checksym for an outgoing IP datagram. */static voidip_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));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -