URL
https://opencores.org/ocsvn/or1k_old/or1k_old/trunk
Subversion Repositories or1k_old
[/] [or1k_old/] [trunk/] [uclinux/] [uClinux-2.0.x/] [net/] [ipv4/] [ip_output.c] - Rev 1765
Go to most recent revision | Compare with Previous | Blame | View Log
/* * 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) output 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> * Alan Cox, <Alan.Cox@linux.org> * Richard Underwood * Stefan Becker, <stefanb@yello.ping.de> * Jorge Cwik, <jorge@laser.satlink.net> * Arnt Gulbrandsen, <agulbra@nvg.unit.no> * * See ip_input.c for original log * * Fixes: * Alan Cox : Missing nonblock feature in ip_build_xmit. * Mike Kilburn : htons() missing in ip_build_xmit. * Bradford Johnson: Fix faulty handling of some frames when * no route is found. * Alexander Demenshin: Missing sk/skb free in ip_queue_xmit * (in case if packet not accepted by * output firewall rules) * Elliot Poger : Added support for SO_BINDTODEVICE. * Juan Jose Ciarlante: sk/skb source address rewriting * Elena Apolinario Fdez de Sousa,:ipmr_forward never received multicast * Juan-Mariano de Goyeneche traffic generated locally. */ #include <asm/segment.h> #include <asm/system.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/config.h> #include <linux/socket.h> #include <linux/sockios.h> #include <linux/in.h> #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <net/snmp.h> #include <net/ip.h> #include <net/protocol.h> #include <net/route.h> #include <net/tcp.h> #include <net/udp.h> #include <linux/skbuff.h> #include <net/sock.h> #include <net/arp.h> #include <net/icmp.h> #include <net/raw.h> #include <net/checksum.h> #include <linux/igmp.h> #include <linux/ip_fw.h> #include <linux/firewall.h> #include <linux/mroute.h> #include <net/netlink.h> /* * Allows dynamic re-writing of packet's addresses. * value & 3 do rewrite * value & 2 be verbose * value & 4 rewrite connected sockets too * Currently implemented: * tcp_output.c if sk->state!=TCP_SYN_SENT * ip_masq.c if no packet has been received by tunnel */ int sysctl_ip_dynaddr = 0; /* * Very Promisc source address re-assignation. * ONLY acceptable if socket is NOT connected yet. * Caller already checked sysctl_ip_dynaddr & 3 and EITHER * sysctl_ip_dynaddr & 4 OR consistent sk->state * (TCP_SYN_SENT for tcp, udp-connect sockets are set TCP_ESTABLISHED) */ int ip_rewrite_addrs (struct sock *sk, struct sk_buff *skb, struct device *dev) { u32 new_saddr = dev->pa_addr; struct iphdr *iph; /* * Be carefull: new_saddr must be !0 */ if (!new_saddr) { printk(KERN_WARNING "ip_rewrite_addrs(): NULL device \"%s\" addr\n", dev->name); return 0; } /* * Ouch!, this should not happen. */ if (!sk->saddr || !sk->rcv_saddr) { printk(KERN_WARNING "ip_rewrite_addrs(): not valid sock addrs: saddr=%08lX rcv_saddr=%08lX", ntohl(sk->saddr), ntohl(sk->rcv_saddr)); return 0; } /* * Be verbose if sysctl value & 2 */ if (sysctl_ip_dynaddr & 2) { printk(KERN_INFO "ip_rewrite_addrs(): shifting saddr from %s", in_ntoa(skb->saddr)); printk(" to %s (state %d)\n", in_ntoa(new_saddr), sk->state); } iph = skb->ip_hdr; if (new_saddr != iph->saddr) { iph->saddr = new_saddr; skb->saddr = new_saddr; ip_send_check(iph); } else if (sysctl_ip_dynaddr & 2) { printk(KERN_WARNING "ip_rewrite_addrs(): skb already changed (???).\n"); return 0; } /* * Maybe whe are in a skb chain loop and socket address has * yet been 'damaged'. */ if (new_saddr != sk->saddr) { sk->saddr = new_saddr; sk->rcv_saddr = new_saddr; sk->prot->rehash(sk); } else if (sysctl_ip_dynaddr & 2) printk(KERN_NOTICE "ip_rewrite_addrs(): no change needed for sock\n"); return 1; } /* * Loop a packet back to the sender. */ static void ip_loopback(struct device *old_dev, struct sk_buff *skb) { struct device *dev=&loopback_dev; int len=ntohs(skb->ip_hdr->tot_len); struct sk_buff *newskb=dev_alloc_skb(len+dev->hard_header_len+15); if(newskb==NULL) return; newskb->link3=NULL; newskb->sk=NULL; newskb->dev=dev; newskb->saddr=skb->saddr; newskb->daddr=skb->daddr; newskb->raddr=skb->raddr; newskb->free=1; newskb->lock=0; newskb->users=0; newskb->pkt_type=skb->pkt_type; /* * Put a MAC header on the packet */ ip_send(NULL,newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr); /* * Add the rest of the data space. */ newskb->ip_hdr=(struct iphdr *)skb_put(newskb, len); memcpy(newskb->proto_priv, skb->proto_priv, sizeof(skb->proto_priv)); /* * Copy the data */ memcpy(newskb->ip_hdr,skb->ip_hdr,len); /* Recurse. The device check against IFF_LOOPBACK will stop infinite recursion */ /*printk("Loopback output queued [%lX to %lX].\n", newskb->ip_hdr->saddr,newskb->ip_hdr->daddr);*/ ip_queue_xmit(NULL, dev, newskb, 2); } /* * Take an skb, and fill in the MAC header. */ int ip_send(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr) { int mac = 0; skb->dev = dev; skb->arp = 1; skb->protocol = htons(ETH_P_IP); skb_reserve(skb,(dev->hard_header_len+15)&~15); /* 16 byte aligned IP headers are always good */ if (dev->hard_header) { /* * Build a hardware header. Source address is our mac, destination unknown * (rebuild header will sort this out) */ if (rt && dev == rt->rt_dev && rt->rt_hh) { memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len); if (rt->rt_hh->hh_uptodate) return dev->hard_header_len; #if RT_CACHE_DEBUG >= 2 printk("ip_send: hh miss %08x via %08x\n", daddr, rt->rt_gateway); #endif skb->arp = 0; skb->raddr = daddr; return dev->hard_header_len; } mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len); if (mac < 0) { mac = -mac; skb->arp = 0; skb->raddr = daddr; /* next routing address */ } } return mac; } static int ip_send_room(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr) { int mac = 0; skb->dev = dev; skb->arp = 1; skb->protocol = htons(ETH_P_IP); skb_reserve(skb,MAX_HEADER); if (dev->hard_header) { if (rt && dev == rt->rt_dev && rt->rt_hh) { memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len); if (rt->rt_hh->hh_uptodate) return dev->hard_header_len; #if RT_CACHE_DEBUG >= 2 printk("ip_send_room: hh miss %08x via %08x\n", daddr, rt->rt_gateway); #endif skb->arp = 0; skb->raddr = daddr; return dev->hard_header_len; } mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len); if (mac < 0) { mac = -mac; skb->arp = 0; skb->raddr = daddr; /* next routing address */ } } return mac; } int ip_id_count = 0; /* * 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, __u32 saddr, __u32 daddr, struct device **dev, int type, struct options *opt, int len, int tos, int ttl, struct rtable ** rp) { struct rtable *rt; __u32 raddr; int tmp; struct iphdr *iph; __u32 final_daddr = daddr; if (opt && opt->srr) daddr = opt->faddr; /* * See if we need to look up the device. */ #ifdef CONFIG_IP_MULTICAST if(MULTICAST(daddr) && *dev==NULL && skb->sk && *skb->sk->ip_mc_name) *dev=dev_get(skb->sk->ip_mc_name); #endif if (rp) { rt = ip_check_route(rp, daddr, skb->localroute, *dev); /* * If rp != NULL rt_put following below should not * release route, so that... */ if (rt) atomic_inc(&rt->rt_refcnt); } else rt = ip_rt_route(daddr, skb->localroute, *dev); if (*dev == NULL) { if (rt == NULL) { ip_statistics.IpOutNoRoutes++; return(-ENETUNREACH); } *dev = rt->rt_dev; } if ((LOOPBACK(saddr) && !LOOPBACK(daddr)) || !saddr) saddr = rt ? rt->rt_src : (*dev)->pa_addr; raddr = rt ? rt->rt_gateway : daddr; if (opt && opt->is_strictroute && rt && (rt->rt_flags & RTF_GATEWAY)) { ip_rt_put(rt); ip_statistics.IpOutNoRoutes++; return -ENETUNREACH; } /* * Now build the MAC header. */ if (type==IPPROTO_TCP) tmp = ip_send_room(rt, skb, raddr, len, *dev, saddr); else tmp = ip_send(rt, skb, raddr, len, *dev, saddr); ip_rt_put(rt); /* * Book keeping */ skb->dev = *dev; skb->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); /* * Build the IP addresses */ if (opt) iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr) + opt->optlen); else iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr)); iph->version = 4; iph->ihl = 5; iph->tos = tos; iph->frag_off = 0; iph->ttl = ttl; iph->daddr = daddr; iph->saddr = saddr; iph->protocol = type; skb->ip_hdr = iph; if (!opt || !opt->optlen) return sizeof(struct iphdr) + tmp; iph->ihl += opt->optlen>>2; ip_options_build(skb, opt, final_daddr, (*dev)->pa_addr, 0); return iph->ihl*4 + tmp; } /* * Generate a checksum for an outgoing IP datagram. */ void ip_send_check(struct iphdr *iph) { iph->check = 0; iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); } /* * If a sender wishes the packet to remain unfreed * we add it to his send queue. This arguably belongs * in the TCP level since nobody else uses it. BUT * remember IPng might change all the rules. */ static inline void add_to_send_queue(struct sock * sk, struct sk_buff * skb) { unsigned long flags; /* The socket now has more outstanding blocks */ sk->packets_out++; /* Protect the list for a moment */ save_flags(flags); cli(); if (skb->link3 != NULL) { NETDEBUG(printk("ip.c: link3 != NULL\n")); skb->link3 = NULL; } if (sk->send_head == NULL) { sk->send_tail = skb; sk->send_head = skb; sk->send_next = skb; } else { sk->send_tail->link3 = skb; sk->send_tail = skb; } restore_flags(flags); } /* * 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. If free==2 we not only * free the block but also don't assign a new ip seq number. * 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) { unsigned int tot_len; struct iphdr *iph; IS_SKB(skb); /* * Do some book-keeping in the packet for later */ skb->sk = sk; skb->dev = dev; skb->when = jiffies; /* * Find the IP header and set the length. This is bad * but once we get the skb data handling code in the * hardware will push its header sensibly and we will * set skb->ip_hdr to avoid this mess and the fixed * header length problem */ iph = skb->ip_hdr; tot_len = skb->len - (((unsigned char *)iph) - skb->data); iph->tot_len = htons(tot_len); switch (free) { /* No reassigning numbers to fragments... */ default: free = 1; break; case 0: add_to_send_queue(sk, skb); /* fall through */ case 1: iph->id = htons(ip_id_count++); } skb->free = free; /* Sanity check */ if (dev == NULL) goto no_device; #ifdef CONFIG_FIREWALL if (call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT) goto out; #endif /* * Do we need to fragment. Again this is inefficient. * We need to somehow lock the original buffer and use * bits of it. */ if (tot_len > dev->mtu) goto fragment; /* * Add an IP checksum */ ip_send_check(iph); /* * More debugging. You cannot queue a packet already on a list * Spot this and moan loudly. */ if (skb->next != NULL) { NETDEBUG(printk("ip_queue_xmit: next != NULL\n")); skb_unlink(skb); } /* * If the indicated interface is up and running, send the packet. */ ip_statistics.IpOutRequests++; #ifdef CONFIG_IP_ACCT ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT); #endif #ifdef CONFIG_IP_MULTICAST /* * Multicasts are looped back for other local users */ if (MULTICAST(iph->daddr) && !(dev->flags&IFF_LOOPBACK)) { if(sk==NULL || sk->ip_mc_loop) { if(iph->daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI)) { ip_loopback(dev,skb); } else { struct ip_mc_list *imc=dev->ip_mc_list; while(imc!=NULL) { if(imc->multiaddr==iph->daddr) { ip_loopback(dev,skb); break; } imc=imc->next; } } } /* Multicasts with ttl 0 must not go beyond the host */ if (iph->ttl==0) goto out; } #endif if ((dev->flags & IFF_BROADCAST) && !(dev->flags & IFF_LOOPBACK) && (iph->daddr==dev->pa_brdaddr || iph->daddr==0xFFFFFFFF)) ip_loopback(dev,skb); if (dev->flags & IFF_UP) { /* * If we have an owner use its priority setting, * otherwise use NORMAL */ int priority = SOPRI_NORMAL; if (sk) priority = sk->priority; dev_queue_xmit(skb, dev, priority); return; } if(sk) sk->err = ENETDOWN; ip_statistics.IpOutDiscards++; out: if (free) kfree_skb(skb, FREE_WRITE); return; no_device: NETDEBUG(printk("IP: ip_queue_xmit dev = NULL\n")); goto out; fragment: ip_fragment(sk,skb,dev,0); goto out; } /* * Build and send a packet, with as little as one copy * * Doesn't care much about ip options... option length can be * different for fragment at 0 and other fragments. * * Note that the fragment at the highest offset is sent first, * so the getfrag routine can fill in the TCP/UDP checksum header * field in the last fragment it sends... actually it also helps * the reassemblers, they can put most packets in at the head of * the fragment queue, and they know the total size in advance. This * last feature will measurable improve the Linux fragment handler. * * The callback has five args, an arbitrary pointer (copy of frag), * the source IP address (may depend on the routing table), the * destination address (char *), the offset to copy from, and the * length to be copied. * */ int ip_build_xmit(struct sock *sk, void getfrag (const void *, __u32, char *, unsigned int, unsigned int), const void *frag, unsigned short int length, __u32 daddr, __u32 user_saddr, struct options * opt, int flags, int type, int noblock) { struct rtable *rt; unsigned int fraglen, maxfraglen, fragheaderlen; int offset, mf; __u32 saddr; unsigned short id; struct iphdr *iph; __u32 raddr; struct device *dev = NULL; struct hh_cache * hh=NULL; int nfrags=0; __u32 true_daddr = daddr; if (opt && opt->srr && !sk->ip_hdrincl) daddr = opt->faddr; ip_statistics.IpOutRequests++; #ifdef CONFIG_IP_MULTICAST if(MULTICAST(daddr) && *sk->ip_mc_name) { dev=dev_get(sk->ip_mc_name); if(!dev) return -ENODEV; rt=NULL; if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr))) saddr = sk->saddr; else saddr = dev->pa_addr; } else { #endif rt = ip_check_route(&sk->ip_route_cache, daddr, sk->localroute || (flags&MSG_DONTROUTE) || (opt && opt->is_strictroute), sk->bound_device); if (rt == NULL) { ip_statistics.IpOutNoRoutes++; return(-ENETUNREACH); } saddr = rt->rt_src; hh = rt->rt_hh; if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr))) saddr = sk->saddr; dev=rt->rt_dev; #ifdef CONFIG_IP_MULTICAST } if (rt && !dev) dev = rt->rt_dev; #endif if (user_saddr) saddr = user_saddr; raddr = rt ? rt->rt_gateway : daddr; /* * Now compute the buffer space we require */ /* * Try the simple case first. This leaves broadcast, multicast, fragmented frames, and by * choice RAW frames within 20 bytes of maximum size(rare) to the long path */ if (!sk->ip_hdrincl) { length += sizeof(struct iphdr); if(opt) length += opt->optlen; } if(length <= dev->mtu && !MULTICAST(daddr) && daddr!=0xFFFFFFFF && daddr!=dev->pa_brdaddr) { int error; struct sk_buff *skb=sock_alloc_send_skb(sk, length+15+dev->hard_header_len,0, noblock, &error); if(skb==NULL) { ip_statistics.IpOutDiscards++; return error; } skb->dev=dev; skb->protocol = htons(ETH_P_IP); skb->free=1; skb->when=jiffies; skb->sk=sk; skb->arp=0; skb->saddr=saddr; skb->raddr = raddr; skb_reserve(skb,(dev->hard_header_len+15)&~15); if (hh) { skb->arp=1; memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len); if (!hh->hh_uptodate) { skb->arp = 0; #if RT_CACHE_DEBUG >= 2 printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway); #endif } } else if(dev->hard_header) { if(dev->hard_header(skb,dev,ETH_P_IP,NULL,NULL,0)>0) skb->arp=1; } else skb->arp=1; skb->ip_hdr=iph=(struct iphdr *)skb_put(skb,length); dev_lock_list(); if(!sk->ip_hdrincl) { iph->version=4; iph->ihl=5; iph->tos=sk->ip_tos; iph->tot_len = htons(length); iph->id=htons(ip_id_count++); iph->frag_off = 0; iph->ttl=sk->ip_ttl; iph->protocol=type; iph->saddr=saddr; iph->daddr=daddr; if (opt) { iph->ihl += opt->optlen>>2; ip_options_build(skb, opt, true_daddr, dev->pa_addr, 0); } iph->check=0; iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); getfrag(frag,saddr,((char *)iph)+iph->ihl*4,0, length-iph->ihl*4); } else getfrag(frag,saddr,(void *)iph,0,length); dev_unlock_list(); #ifdef CONFIG_FIREWALL if(call_out_firewall(PF_INET, skb->dev, iph, NULL)< FW_ACCEPT) { kfree_skb(skb, FREE_WRITE); return -EPERM; } #endif #ifdef CONFIG_IP_ACCT ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT); #endif if(dev->flags&IFF_UP) dev_queue_xmit(skb,dev,sk->priority); else { ip_statistics.IpOutDiscards++; kfree_skb(skb, FREE_WRITE); } return 0; } if (!sk->ip_hdrincl) length -= sizeof(struct iphdr); if(opt) { length -= opt->optlen; fragheaderlen = dev->hard_header_len + sizeof(struct iphdr) + opt->optlen; maxfraglen = ((dev->mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen; } else { fragheaderlen = dev->hard_header_len; if(!sk->ip_hdrincl) fragheaderlen += 20; /* * Fragheaderlen is the size of 'overhead' on each buffer. Now work * out the size of the frames to send. */ maxfraglen = ((dev->mtu-20) & ~7) + fragheaderlen; } /* * Start at the end of the frame by handling the remainder. */ offset = length - (length % (maxfraglen - fragheaderlen)); /* * Amount of memory to allocate for final fragment. */ fraglen = length - offset + fragheaderlen; if(length-offset==0) { fraglen = maxfraglen; offset -= maxfraglen-fragheaderlen; } /* * The last fragment will not have MF (more fragments) set. */ mf = 0; /* * Can't fragment raw packets */ if (sk->ip_hdrincl && offset > 0) return(-EMSGSIZE); /* * Lock the device lists. */ dev_lock_list(); /* * Get an identifier */ id = htons(ip_id_count++); /* * Being outputting the bytes. */ do { struct sk_buff * skb; int error; char *data; /* * Get the memory we require with some space left for alignment. */ skb = sock_alloc_send_skb(sk, fraglen+15, 0, noblock, &error); if (skb == NULL) { ip_statistics.IpOutDiscards++; if(nfrags>1) ip_statistics.IpFragCreates++; dev_unlock_list(); return(error); } /* * Fill in the control structures */ skb->dev = dev; skb->protocol = htons(ETH_P_IP); skb->when = jiffies; skb->free = 1; /* dubious, this one */ skb->sk = sk; skb->arp = 0; skb->saddr = saddr; skb->daddr = daddr; skb->raddr = raddr; skb_reserve(skb,(dev->hard_header_len+15)&~15); data = skb_put(skb, fraglen-dev->hard_header_len); /* * Save us ARP and stuff. In the optimal case we do no route lookup (route cache ok) * no ARP lookup (arp cache ok) and output. The cache checks are still too slow but * this can be fixed later. For gateway routes we ought to have a rt->.. header cache * pointer to speed header cache builds for identical targets. */ if (hh) { skb->arp=1; memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len); if (!hh->hh_uptodate) { skb->arp = 0; #if RT_CACHE_DEBUG >= 2 printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway); #endif } } else if (dev->hard_header) { if(dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, 0)>0) skb->arp=1; } else skb->arp = 1; /* * Find where to start putting bytes. */ skb->ip_hdr = iph = (struct iphdr *)data; /* * Only write IP header onto non-raw packets */ if(!sk->ip_hdrincl) { iph->version = 4; iph->ihl = 5; /* ugh */ if (opt) { iph->ihl += opt->optlen>>2; ip_options_build(skb, opt, true_daddr, dev->pa_addr, offset); } iph->tos = sk->ip_tos; iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4); iph->id = id; iph->frag_off = htons(offset>>3); iph->frag_off |= mf; #ifdef CONFIG_IP_MULTICAST if (MULTICAST(daddr)) iph->ttl = sk->ip_mc_ttl; else #endif iph->ttl = sk->ip_ttl; iph->protocol = type; iph->check = 0; iph->saddr = saddr; iph->daddr = daddr; iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); data += iph->ihl*4; /* * Any further fragments will have MF set. */ mf = htons(IP_MF); } /* * User data callback */ getfrag(frag, saddr, data, offset, fraglen-fragheaderlen); /* * Account for the fragment. */ #ifdef CONFIG_FIREWALL if(!offset && call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT) { kfree_skb(skb, FREE_WRITE); dev_unlock_list(); return -EPERM; } #endif #ifdef CONFIG_IP_ACCT ip_fw_chk(iph, dev, NULL, ip_acct_chain, 0, IP_FW_MODE_ACCT_OUT); #endif offset -= (maxfraglen-fragheaderlen); fraglen = maxfraglen; #ifdef CONFIG_IP_MULTICAST /* * Multicasts are looped back for other local users */ if (MULTICAST(daddr) && !(dev->flags&IFF_LOOPBACK)) { #ifdef CONFIG_IP_MROUTE /* We need this so that mrouted "hears" packets sent from the same host it is running on... (jmel) */ if (mroute_socket&&(iph->protocol!=IPPROTO_IGMP)) { if((skb->ip_hdr->daddr&htonl(0xFFFFFF00))!=htonl(0xE0000000)) { struct sk_buff* skb2=skb_clone(skb, GFP_ATOMIC); if(skb2) { skb2->free=1; ipmr_forward(skb2, 0); } } } #endif /* * Loop back any frames. The check for IGMP_ALL_HOSTS is because * you are always magically a member of this group. * * Always loop back all host messages when running as a multicast router. */ if(sk==NULL || sk->ip_mc_loop) { if(daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI)) ip_loopback(dev,skb); else { struct ip_mc_list *imc=dev->ip_mc_list; while(imc!=NULL) { if(imc->multiaddr==daddr) { ip_loopback(dev,skb); break; } imc=imc->next; } } } /* * Multicasts with ttl 0 must not go beyond the host. Fixme: avoid the * extra clone. */ if(skb->ip_hdr->ttl==0) { kfree_skb(skb, FREE_WRITE); nfrags++; continue; } } #endif nfrags++; /* * BSD loops broadcasts */ if((dev->flags&IFF_BROADCAST) && (daddr==0xFFFFFFFF || daddr==dev->pa_brdaddr) && !(dev->flags&IFF_LOOPBACK)) ip_loopback(dev,skb); /* * Now queue the bytes into the device. */ if (dev->flags & IFF_UP) { dev_queue_xmit(skb, dev, sk->priority); } else { /* * Whoops... */ ip_statistics.IpOutDiscards++; if(nfrags>1) ip_statistics.IpFragCreates+=nfrags; kfree_skb(skb, FREE_WRITE); dev_unlock_list(); /* * BSD behaviour. */ if(sk!=NULL) sk->err=ENETDOWN; return(0); /* lose rest of fragments */ } } while (offset >= 0); if(nfrags>1) ip_statistics.IpFragCreates+=nfrags; dev_unlock_list(); return(0); } /* * IP protocol layer initialiser */ static struct packet_type ip_packet_type = { 0, /* MUTTER ntohs(ETH_P_IP),*/ NULL, /* All devices */ ip_rcv, NULL, NULL, }; #ifdef CONFIG_RTNETLINK /* * Netlink hooks for IP */ void ip_netlink_msg(unsigned long msg, __u32 daddr, __u32 gw, __u32 mask, short flags, short metric, char *name) { struct sk_buff *skb=alloc_skb(sizeof(struct netlink_rtinfo), GFP_ATOMIC); struct netlink_rtinfo *nrt; struct sockaddr_in *s; if(skb==NULL) return; skb->free=1; nrt=(struct netlink_rtinfo *)skb_put(skb, sizeof(struct netlink_rtinfo)); nrt->rtmsg_type=msg; s=(struct sockaddr_in *)&nrt->rtmsg_dst; s->sin_family=AF_INET; s->sin_addr.s_addr=daddr; s=(struct sockaddr_in *)&nrt->rtmsg_gateway; s->sin_family=AF_INET; s->sin_addr.s_addr=gw; s=(struct sockaddr_in *)&nrt->rtmsg_genmask; s->sin_family=AF_INET; s->sin_addr.s_addr=mask; nrt->rtmsg_flags=flags; nrt->rtmsg_metric=metric; strcpy(nrt->rtmsg_device,name); if (netlink_post(NETLINK_ROUTE, skb)) kfree_skb(skb, FREE_WRITE); } #endif /* * Device notifier */ static int ip_rt_event(struct notifier_block *this, unsigned long event, void *ptr) { struct device *dev=ptr; if(event==NETDEV_DOWN) { ip_netlink_msg(RTMSG_DELDEVICE, 0,0,0,0,0,dev->name); ip_rt_flush(dev); } /* * Join the initial group if multicast. */ if(event==NETDEV_UP) { #ifdef CONFIG_IP_MULTICAST ip_mc_allhost(dev); #endif ip_netlink_msg(RTMSG_NEWDEVICE, 0,0,0,0,0,dev->name); ip_rt_update(NETDEV_UP, dev); } return NOTIFY_DONE; } struct notifier_block ip_rt_notifier={ ip_rt_event, NULL, 0 }; /* * IP registers the packet type and then calls the subprotocol initialisers */ #ifdef CONFIG_IP_MULTICAST #ifdef CONFIG_PROC_FS static struct proc_dir_entry pde1 = { PROC_NET_IGMP, 4, "igmp", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, ip_mc_procinfo }; #endif #endif void ip_init(void) { ip_packet_type.type=htons(ETH_P_IP); dev_add_pack(&ip_packet_type); /* So we flush routes when a device is downed */ register_netdevice_notifier(&ip_rt_notifier); /* ip_raw_init(); ip_packet_init(); ip_tcp_init(); ip_udp_init();*/ #ifdef CONFIG_IP_MULTICAST #ifdef CONFIG_PROC_FS proc_net_register(&pde1); #endif #endif }
Go to most recent revision | Compare with Previous | Blame | View Log