📄 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_DEFRAG
extern int last_retran;
extern void sort_send(struct sock *sk);
#define min(a,b) ((a)<(b)?(a):(b))
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));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -