📄 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 voidip_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. */intip_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. */voidip_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); }}voidip_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. */voidip_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 + -