📄 ip.c
字号:
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;
skb->ip_hdr = iph;
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, sk->rto);
} 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_do_retransmit(struct sock *sk, int all)
{
struct sk_buff * skb;
struct proto *prot;
struct device *dev;
int retransmits;
prot = sk->prot;
skb = sk->send_head;
retransmits = sk->retransmits;
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: 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;
}
}
/*
* This is the normal code called for timeouts. It does the retransmission
* and then does backoff. ip_do_retransmit is separated out because
* tcp_ack needs to send stuff from the retransmit queue without
* initiating a backoff.
*/
void
ip_retransmit(struct sock *sk, int all)
{
ip_do_retransmit(sk, all);
/*
* Increase the timeout each time we retransmit. Note that
* we do not increase the rtt estimate. rto is initialized
* from rtt, but increases here. Jacobson (SIGCOMM 88) suggests
* that doubling rto each time is the least we can get away with.
* In KA9Q, Karns uses this for the first few times, and then
* goes to quadratic. netBSD doubles, but only goes up to *64,
* and clamps at 1 to 64 sec afterwards. Note that 120 sec is
* defined in the protocol as the maximum possible RTT. I guess
* we'll have to use something other than TCP to talk to the
* University of Mars.
*/
sk->retransmits++;
sk->backoff++;
sk->rto = min(sk->rto << 1, 120*HZ);
reset_timer(sk, TIME_WRITE, sk->rto);
}
/*
* 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);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -