diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
commit | 19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch) | |
tree | 40b1cb534496a7f1ca0f5c314a523c69f1fee464 /net/ipv4/ip_input.c | |
parent | 7206675c40394c78a90e74812bbdbf8cf3cca1be (diff) |
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'net/ipv4/ip_input.c')
-rw-r--r-- | net/ipv4/ip_input.c | 706 |
1 files changed, 219 insertions, 487 deletions
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 3e286c4d7..2642832e3 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -109,8 +109,6 @@ * output should probably do its own fragmentation at the UDP/RAW layer. TCP shouldn't cause * fragmentation anyway. * - * FIXME: copy frag 0 iph to qp->iph - * * 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 @@ -156,24 +154,15 @@ #include <linux/firewall.h> #include <linux/mroute.h> #include <net/netlink.h> -#ifdef CONFIG_NET_ALIAS #include <linux/net_alias.h> -#endif - -extern int last_retran; -extern void sort_send(struct sock *sk); - -#define min(a,b) ((a)<(b)?(a):(b)) +#include <linux/ipsec.h> /* * SNMP management statistics */ -#ifdef CONFIG_IP_FORWARD -struct ip_mib ip_statistics={1,64,}; /* Forwarding=Yes, Default TTL=64 */ -#else -struct ip_mib ip_statistics={2,64,}; /* Forwarding=No, Default TTL=64 */ -#endif +struct ip_mib ip_statistics={2,IPDEFTTL,}; /* Forwarding=No, Default TTL=64 */ + /* * Handle the issuing of an ioctl() request @@ -190,548 +179,291 @@ int ip_ioctl(struct sock *sk, int cmd, unsigned long arg) } } -#ifdef CONFIG_IP_TRANSPARENT_PROXY -/* - * Check the packet against our socket administration to see - * if it is related to a connection on our system. - * Needed for transparent proxying. - */ -int ip_chksock(struct sk_buff *skb) -{ - switch (skb->h.iph->protocol) { - case IPPROTO_ICMP: - return icmp_chkaddr(skb); - case IPPROTO_TCP: - return tcp_chkaddr(skb); - case IPPROTO_UDP: - return udp_chkaddr(skb); - default: - return 0; - } -} +#if defined(CONFIG_IP_TRANSPARENT_PROXY) && !defined(CONFIG_IP_ALWAYS_DEFRAG) +#define CONFIG_IP_ALWAYS_DEFRAG 1 #endif -/* - * This function receives all incoming IP datagrams. - * - * On entry skb->data points to the start of the IP header and - * the MAC header has been removed. - */ - -int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +int ip_local_deliver(struct sk_buff *skb) { - struct iphdr *iph = skb->h.iph; + struct iphdr *iph = skb->nh.iph; +#ifdef CONFIG_IP_MASQUERADE + struct device *dev = skb->dev; +#endif + struct inet_protocol *ipprot; struct sock *raw_sk=NULL; unsigned char hash; - unsigned char flag = 0; - struct inet_protocol *ipprot; - int brd=IS_MYADDR; - struct options * opt = NULL; - int is_frag=0; - __u32 daddr; - -#ifdef CONFIG_FIREWALL - int fwres; - __u16 rport; -#endif -#ifdef CONFIG_IP_MROUTE - int mroute_pkt=0; -#endif - -#ifdef CONFIG_NET_IPV6 - /* - * Intercept IPv6 frames. We dump ST-II and invalid types just below.. - */ - - if(iph->version == 6) - return ipv6_rcv(skb,dev,pt); -#endif - - ip_statistics.IpInReceives++; + int flag = 0; +#ifndef CONFIG_IP_ALWAYS_DEFRAG /* - * Account for the packet (even if the packet is - * not accepted by the firewall!). + * Reassemble IP fragments. */ -#ifdef CONFIG_IP_ACCT - ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN); -#endif + if (iph->frag_off & htons(IP_MF|IP_OFFSET)) { + skb = ip_defrag(skb); + if (!skb) + return 0; + iph = skb->nh.iph; + } +#endif +#ifdef CONFIG_IP_MASQUERADE /* - * Tag the ip header of this packet so we can find it + * Do we need to de-masquerade this packet? */ + { + int ret = ip_fw_demasquerade(&skb, dev); + if (ret < 0) { + kfree_skb(skb, FREE_WRITE); + return 0; + } - skb->ip_hdr = iph; + if (ret) { + iph=skb->nh.iph; + IPCB(skb)->flags |= IPSKB_MASQUERADED; + dst_release(skb->dst); + skb->dst = NULL; + if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev)) { + kfree_skb(skb, FREE_WRITE); + return 0; + } + return skb->dst->input(skb); + } + } +#endif - /* - * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum. - * RFC1122: 3.1.2.3 MUST discard a frame with invalid source address [NEEDS FIXING]. - * - * Is the datagram acceptable? - * - * 1. Length at least the size of an ip header - * 2. Version of 4 - * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums] - * 4. Doesn't have a bogus length - * (5. We ought to check for IP multicast addresses and undefined types.. does this matter ?) + /* + * Point into the IP datagram, just past the header. */ - if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0 - || skb->len < ntohs(iph->tot_len)) - { - ip_statistics.IpInHdrErrors++; - kfree_skb(skb, FREE_WRITE); - return(0); - } + skb->h.raw = skb->nh.raw + iph->ihl*4; /* - * Our transport medium may have padded the buffer out. Now we know it - * is IP we can trim to the true length of the frame. - * Note this now means skb->len holds ntohs(iph->tot_len). + * Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies. + * + * RFC 1122: SHOULD pass TOS value up to the transport layer. */ + + /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */ + hash = iph->protocol & (MAX_INET_PROTOS - 1); - skb_trim(skb,ntohs(iph->tot_len)); - - /* - * Try to select closest <src,dst> alias device, if any. - * net_alias_dev_rcv_sel32 returns main device if it - * fails to found other. + /* + * If there maybe a raw socket we must check - if not we don't care less */ - -#ifdef CONFIG_NET_ALIAS - if (iph->daddr != skb->dev->pa_addr && net_alias_has(skb->dev)) - skb->dev = dev = net_alias_dev_rcv_sel32(skb->dev, AF_INET, iph->saddr, iph->daddr); -#endif - - if (iph->ihl > 5) - { - skb->ip_summed = 0; - if (ip_options_compile(NULL, skb)) - return(0); - opt = (struct options*)skb->proto_priv; -#ifdef CONFIG_IP_NOSR - if (opt->srr) - { - kfree_skb(skb, FREE_READ); - return -EINVAL; + + if((raw_sk = raw_v4_htable[hash]) != NULL) { + struct sock *sknext = NULL; + struct sk_buff *skb1; + raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr); + if(raw_sk) { /* Any raw sockets */ + do { + /* Find the next */ + sknext = raw_v4_lookup(raw_sk->next, iph->protocol, + iph->saddr, iph->daddr); + if(sknext) + skb1 = skb_clone(skb, GFP_ATOMIC); + else + break; /* One pending raw socket left */ + if(skb1) + { + if(ipsec_sk_policy(raw_sk,skb1)) + raw_rcv(raw_sk, skb1); + else + kfree_skb(skb1, FREE_WRITE); + } + raw_sk = sknext; + } while(raw_sk!=NULL); + + /* Here either raw_sk is the last raw socket, or NULL if + * none. We deliver to the last raw socket AFTER the + * protocol checks as it avoids a surplus copy. + */ } -#endif } -#if defined(CONFIG_IP_TRANSPARENT_PROXY) && !defined(CONFIG_IP_ALWAYS_DEFRAG) -#define CONFIG_IP_ALWAYS_DEFRAG 1 -#endif -#ifdef CONFIG_IP_ALWAYS_DEFRAG /* - * Defragment all incoming traffic before even looking at it. - * If you have forwarding enabled, this makes the system a - * defragmenting router. Not a common thing. - * You probably DON'T want to enable this unless you have to. - * You NEED to use this if you want to use transparent proxying, - * otherwise, we can't vouch for your sanity. + * skb->h.raw now points at the protocol beyond the IP header. */ - - /* - * See if the frame is fragmented. - */ - - if(iph->frag_off) + + for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next) { - if (iph->frag_off & htons(IP_MF)) - is_frag|=IPFWD_FRAGMENT; + struct sk_buff *skb2; + + if (ipprot->protocol != iph->protocol) + continue; /* - * Last fragment ? + * 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 there is a pending + * raw delivery wait for that */ - if (iph->frag_off & htons(IP_OFFSET)) - is_frag|=IPFWD_LASTFRAG; - + if (ipprot->copy || raw_sk) + { + skb2 = skb_clone(skb, GFP_ATOMIC); + if(skb2==NULL) + continue; + } + else + { + skb2 = skb; + } + flag = 1; + /* - * Reassemble IP fragments. + * 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... */ - if(is_frag) - { - /* Defragment. Obtain the complete packet if there is one */ - skb=ip_defrag(iph,skb,dev); - if(skb==NULL) - return 0; - skb->dev = dev; - iph=skb->h.iph; - is_frag = 0; - /* - * When the reassembled packet gets forwarded, the ip - * header checksum should be correct. - * For better performance, this should actually only - * be done in that particular case, i.e. set a flag - * here and calculate the checksum in ip_forward. - */ - ip_send_check(iph); - } + ipprot->handler(skb2, ntohs(iph->tot_len) - (iph->ihl * 4)); } -#endif /* - * See if the firewall wants to dispose of the packet. + * 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...) */ - -#ifdef CONFIG_FIREWALL - if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport))<FW_ACCEPT) + if(raw_sk!=NULL) /* Shift to last raw user */ { - if(fwres==FW_REJECT) - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev); + if(ipsec_sk_policy(raw_sk, skb)) + raw_rcv(raw_sk, skb); + else + kfree_skb(skb, FREE_WRITE); + } + else if (!flag) /* Free and report errors */ + { + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); kfree_skb(skb, FREE_WRITE); - return 0; } -#ifdef CONFIG_IP_TRANSPARENT_PROXY - if (fwres==FW_REDIRECT) - skb->redirport = rport; - else -#endif - skb->redirport = 0; -#endif - -#ifndef CONFIG_IP_ALWAYS_DEFRAG - /* - * Remember if the frame is fragmented. + return(0); +} + +int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +{ + struct iphdr *iph = skb->nh.iph; + struct ip_options * opt = NULL; + int err; + +#ifdef CONFIG_NET_IPV6 + /* + * Intercept IPv6 frames. We dump ST-II and invalid types just below.. */ - if(iph->frag_off) - { - if (iph->frag_off & htons(IP_MF)) - is_frag|=IPFWD_FRAGMENT; - /* - * Last fragment ? - */ - - if (iph->frag_off & htons(IP_OFFSET)) - is_frag|=IPFWD_LASTFRAG; - } - + if(iph->version == 6) + return ipv6_rcv(skb,dev,pt); #endif + /* - * Do any IP forwarding required. chk_addr() is expensive -- avoid it someday. - * - * This is inefficient. While finding out if it is for us we could also compute - * the routing table entry. This is where the great unified cache theory comes - * in as and when someone implements it - * - * For most hosts over 99% of packets match the first conditional - * and don't go via ip_chk_addr. Note: brd is set to IS_MYADDR at - * function entry. - */ - daddr = iph->daddr; -#ifdef CONFIG_IP_TRANSPARENT_PROXY - /* - * ip_chksock adds still more overhead for forwarded traffic... + * When interface is in promisc. mode, drop all the crap + * that it receives, do not truing to analyse it. */ - if ( iph->daddr == skb->dev->pa_addr || skb->redirport || (brd = ip_chk_addr(iph->daddr)) != 0 || ip_chksock(skb)) -#else - if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0) -#endif - { - if (opt && opt->srr) - { - int srrspace, srrptr; - __u32 nexthop; - unsigned char * optptr = ((unsigned char *)iph) + opt->srr; - - if (brd != IS_MYADDR || skb->pkt_type != PACKET_HOST) - { - kfree_skb(skb, FREE_WRITE); - return 0; - } - - for ( srrptr=optptr[2], srrspace = optptr[1]; - srrptr <= srrspace; - srrptr += 4 - ) - { - int brd2; - if (srrptr + 3 > srrspace) - { - icmp_send(skb, ICMP_PARAMETERPROB, 0, opt->srr+2, - skb->dev); - kfree_skb(skb, FREE_WRITE); - return 0; - } - memcpy(&nexthop, &optptr[srrptr-1], 4); - if ((brd2 = ip_chk_addr(nexthop)) == 0) - break; - if (brd2 != IS_MYADDR) - { + if (skb->pkt_type == PACKET_OTHERHOST) + goto drop; - /* - * ANK: should we implement weak tunneling of multicasts? - * Are they obsolete? DVMRP specs (RFC-1075) is old enough... - * [They are obsolete] - */ - kfree_skb(skb, FREE_WRITE); - return -EINVAL; - } - memcpy(&daddr, &optptr[srrptr-1], 4); - } - if (srrptr <= srrspace) - { - opt->srr_is_hit = 1; - opt->is_changed = 1; -#ifdef CONFIG_IP_FORWARD - if (ip_forward(skb, dev, is_frag, nexthop)) - kfree_skb(skb, FREE_WRITE); -#else - ip_statistics.IpInAddrErrors++; - kfree_skb(skb, FREE_WRITE); -#endif - return 0; - } - } - -#ifdef CONFIG_IP_MULTICAST - if(!(dev->flags&IFF_ALLMULTI) && brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK)) - { - /* - * Check it is for one of our groups - */ - struct ip_mc_list *ip_mc=dev->ip_mc_list; - do - { - if(ip_mc==NULL) - { - kfree_skb(skb, FREE_WRITE); - return 0; - } - if(ip_mc->multiaddr==iph->daddr) - break; - ip_mc=ip_mc->next; - } - while(1); - } -#endif - -#ifndef CONFIG_IP_ALWAYS_DEFRAG - /* - * Reassemble IP fragments. - */ + ip_statistics.IpInReceives++; - if(is_frag) - { - /* Defragment. Obtain the complete packet if there is one */ - skb=ip_defrag(iph,skb,dev); - if(skb==NULL) - return 0; - skb->dev = dev; - iph=skb->h.iph; - } + /* + * Account for the packet (even if the packet is + * not accepted by the firewall!). + */ -#endif +#ifdef CONFIG_IP_ACCT + ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN); +#endif -#ifdef CONFIG_IP_MASQUERADE - /* - * Do we need to de-masquerade this packet? - */ - { - int ret = ip_fw_demasquerade(&skb,dev); - if (ret < 0) { - kfree_skb(skb, FREE_WRITE); - return 0; - } + /* + * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum. + * + * Is the datagram acceptable? + * + * 1. Length at least the size of an ip header + * 2. Version of 4 + * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums] + * 4. Doesn't have a bogus length + */ - if (ret) - { - struct iphdr *iph=skb->h.iph; - if (ip_forward(skb, dev, IPFWD_MASQUERADED, iph->daddr)) - kfree_skb(skb, FREE_WRITE); - return 0; - } - } -#endif + if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 + || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0 + || skb->len < ntohs(iph->tot_len)) + goto inhdr_error; - /* - * Point into the IP datagram, just past the header. - */ + /* + * Our transport medium may have padded the buffer out. Now we know it + * is IP we can trim to the true length of the frame. + * Note this now means skb->len holds ntohs(iph->tot_len). + */ - skb->ip_hdr = iph; - skb->h.raw += iph->ihl*4; + skb_trim(skb, ntohs(iph->tot_len)); -#ifdef CONFIG_IP_MROUTE - /* - * Check the state on multicast routing (multicast and not 224.0.0.z) - */ - - if(brd==IS_MULTICAST && (iph->daddr&htonl(0xFFFFFF00))!=htonl(0xE0000000)) - mroute_pkt=1; + if (skb->dst == NULL) { + err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev); + if (err) + goto drop; + } +#ifdef CONFIG_IP_ALWAYS_DEFRAG + if (iph->frag_off & htons(IP_MF|IP_OFFSET)) { + skb = ip_defrag(skb); + if (!skb) + return 0; + iph = skb->nh.iph; + ip_send_check(iph); + } #endif - /* - * Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies. - * - * RFC 1122: SHOULD pass TOS value up to the transport layer. - */ - - hash = iph->protocol & (SOCK_ARRAY_SIZE-1); - - /* - * If there maybe a raw socket we must check - if not we don't care less - */ - - if((raw_sk=raw_prot.sock_array[hash])!=NULL) - { - struct sock *sknext=NULL; - struct sk_buff *skb1; - raw_sk=get_sock_raw(raw_sk, iph->protocol, iph->saddr, iph->daddr); - if(raw_sk) /* Any raw sockets */ - { - do - { - /* Find the next */ - sknext=get_sock_raw(raw_sk->next, iph->protocol, iph->saddr, iph->daddr); - if(sknext) - skb1=skb_clone(skb, GFP_ATOMIC); - else - break; /* One pending raw socket left */ - if(skb1) - raw_rcv(raw_sk, skb1, dev, iph->saddr,daddr); - raw_sk=sknext; - } - while(raw_sk!=NULL); - - /* - * Here either raw_sk is the last raw socket, or NULL if none - */ - - /* - * We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy - */ - } - } - - /* - * skb->h.raw now points at the protocol beyond the IP header. - */ - - 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; - /* - * 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 there is a pending - * raw delivery wait for that - */ - -#ifdef CONFIG_IP_MROUTE - if (ipprot->copy || raw_sk || mroute_pkt) -#else - if (ipprot->copy || raw_sk) -#endif - { - skb2 = skb_clone(skb, GFP_ATOMIC); - if(skb2==NULL) - continue; - } - 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, opt, 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...) - */ -#ifdef CONFIG_IP_MROUTE - /* - * Forward the last copy to the multicast router. If - * there is a pending raw delivery however make a copy - * and forward that. - */ - - if(mroute_pkt) - { - flag=1; - if(raw_sk==NULL) - ipmr_forward(skb, is_frag); - else - { - struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC); - if(skb2) - { - skb2->free=1; - ipmr_forward(skb2, is_frag); - } + if (iph->ihl > 5) { + skb->ip_summed = 0; + if (ip_options_compile(NULL, skb)) + goto inhdr_error; + + opt = &(IPCB(skb)->opt); + if (opt->srr) { + if (!ipv4_config.source_route) { + if (ipv4_config.log_martians) + printk(KERN_INFO "source route option %08lx -> %08lx\n", + ntohl(iph->saddr), ntohl(iph->daddr)); + goto drop; } + if (RT_LOCALADDR(((struct rtable*)skb->dst)->rt_flags) && + ip_options_rcv_srr(skb)) + goto drop; } -#endif - - if(raw_sk!=NULL) /* Shift to last raw user */ - raw_rcv(raw_sk, skb, dev, iph->saddr, daddr); - else if (!flag) /* Free and report errors */ - { - if (brd != IS_BROADCAST && brd!=IS_MULTICAST) - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev); - kfree_skb(skb, FREE_WRITE); - } - - return(0); } - - /* - * Do any unicast IP forwarding required. - */ /* - * Don't forward multicast or broadcast frames. + * See if the firewall wants to dispose of the packet. */ + +#ifdef CONFIG_FIREWALL + { + int fwres; + u16 rport; + + if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport, &skb))<FW_ACCEPT) { + if (fwres==FW_REJECT) + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); + goto drop; + } - if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST) - { - kfree_skb(skb,FREE_WRITE); - return 0; +#ifdef CONFIG_IP_TRANSPARENT_PROXY + if (fwres==FW_REDIRECT && (IPCB(skb)->redirport = rport) != 0) + return ip_local_deliver(skb); +#endif } +#endif - /* - * The packet is for another target. Forward the frame - */ + return skb->dst->input(skb); -#ifdef CONFIG_IP_FORWARD - if (opt && opt->is_strictroute) - { - icmp_send(skb, ICMP_PARAMETERPROB, 0, 16, skb->dev); - kfree_skb(skb, FREE_WRITE); - return -1; - } - if (ip_forward(skb, dev, is_frag, iph->daddr)) - kfree_skb(skb, FREE_WRITE); -#else -/* printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n", - iph->saddr,iph->daddr);*/ - ip_statistics.IpInAddrErrors++; - kfree_skb(skb, FREE_WRITE); -#endif - return(0); +inhdr_error: + ip_statistics.IpInHdrErrors++; +drop: + kfree_skb(skb, FREE_WRITE); + return(0); } - |