diff options
| -rw-r--r-- | include/linux/netfilter_ipv4.h | 6 | ||||
| -rw-r--r-- | include/linux/netfilter_ipv4/ip_nat_core.h | 6 | ||||
| -rw-r--r-- | net/core/netfilter.c | 63 | ||||
| -rw-r--r-- | net/ipv4/netfilter/ip_nat_core.c | 59 |
4 files changed, 69 insertions, 65 deletions
diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h index 3744c61f424e..336c47be6837 100644 --- a/include/linux/netfilter_ipv4.h +++ b/include/linux/netfilter_ipv4.h @@ -75,6 +75,12 @@ void nf_debug_ip_finish_output2(struct sk_buff *skb); #endif /*CONFIG_NETFILTER_DEBUG*/ extern int ip_route_me_harder(struct sk_buff **pskb); + +/* Call this before modifying an existing IP packet: ensures it is + modifiable and linear to the point you care about (writable_len). + Returns true or false. */ +extern int skb_ip_make_writable(struct sk_buff **pskb, + unsigned int writable_len); #endif /*__KERNEL__*/ #endif /*__LINUX_IP_NETFILTER_H*/ diff --git a/include/linux/netfilter_ipv4/ip_nat_core.h b/include/linux/netfilter_ipv4/ip_nat_core.h index 5f1a0bea8073..a8bcdc9874a5 100644 --- a/include/linux/netfilter_ipv4/ip_nat_core.h +++ b/include/linux/netfilter_ipv4/ip_nat_core.h @@ -30,10 +30,4 @@ extern void place_in_hashes(struct ip_conntrack *conntrack, extern struct ip_nat_protocol ip_nat_protocol_tcp; extern struct ip_nat_protocol ip_nat_protocol_udp; extern struct ip_nat_protocol ip_nat_protocol_icmp; - -/* Call this before modifying an existing IP packet: ensures it is - modifiable and linear to the point you care about (writable_len). - Returns true or false. */ -extern int skb_ip_make_writable(struct sk_buff **pskb, - unsigned int writable_len); #endif /* _IP_NAT_CORE_H */ diff --git a/net/core/netfilter.c b/net/core/netfilter.c index 432157079ff4..3adb3f6ee0de 100644 --- a/net/core/netfilter.c +++ b/net/core/netfilter.c @@ -20,6 +20,9 @@ #include <linux/if.h> #include <linux/netdevice.h> #include <linux/inetdevice.h> +#include <linux/tcp.h> +#include <linux/udp.h> +#include <linux/icmp.h> #include <net/sock.h> #include <net/route.h> #include <linux/ip.h> @@ -683,8 +686,68 @@ out: return err; } + +int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len) +{ + struct sk_buff *nskb; + unsigned int iplen; + + if (writable_len > (*pskb)->len) + return 0; + + /* Not exclusive use of packet? Must copy. */ + if (skb_shared(*pskb) || skb_cloned(*pskb)) + goto copy_skb; + + /* Alexey says IP hdr is always modifiable and linear, so ok. */ + if (writable_len <= (*pskb)->nh.iph->ihl*4) + return 1; + + iplen = writable_len - (*pskb)->nh.iph->ihl*4; + + /* DaveM says protocol headers are also modifiable. */ + switch ((*pskb)->nh.iph->protocol) { + case IPPROTO_TCP: { + struct tcphdr hdr; + if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4, + &hdr, sizeof(hdr)) != 0) + goto copy_skb; + if (writable_len <= (*pskb)->nh.iph->ihl*4 + hdr.doff*4) + goto pull_skb; + goto copy_skb; + } + case IPPROTO_UDP: + if (writable_len<=(*pskb)->nh.iph->ihl*4+sizeof(struct udphdr)) + goto pull_skb; + goto copy_skb; + case IPPROTO_ICMP: + if (writable_len + <= (*pskb)->nh.iph->ihl*4 + sizeof(struct icmphdr)) + goto pull_skb; + goto copy_skb; + /* Insert other cases here as desired */ + } + +copy_skb: + nskb = skb_copy(*pskb, GFP_ATOMIC); + if (!nskb) + return 0; + BUG_ON(skb_is_nonlinear(nskb)); + + /* Rest of kernel will get very unhappy if we pass it a + suddenly-orphaned skbuff */ + if ((*pskb)->sk) + skb_set_owner_w(nskb, (*pskb)->sk); + kfree_skb(*pskb); + *pskb = nskb; + return 1; + +pull_skb: + return pskb_may_pull(*pskb, writable_len); +} #endif /*CONFIG_INET*/ + /* This does not belong here, but ipt_REJECT needs it if connection tracking in use: without this, connection may not be in hash table, and hence manufactured ICMP or RST packets will not be associated diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c index d2fb5b616b70..d8d99cc6493b 100644 --- a/net/ipv4/netfilter/ip_nat_core.c +++ b/net/ipv4/netfilter/ip_nat_core.c @@ -969,65 +969,6 @@ icmp_reply_translation(struct sk_buff **pskb, return 0; } -int skb_ip_make_writable(struct sk_buff **pskb, unsigned int writable_len) -{ - struct sk_buff *nskb; - unsigned int iplen; - - if (writable_len > (*pskb)->len) - return 0; - - /* Not exclusive use of packet? Must copy. */ - if (skb_shared(*pskb) || skb_cloned(*pskb)) - goto copy_skb; - - /* Alexey says IP hdr is always modifiable and linear, so ok. */ - if (writable_len <= (*pskb)->nh.iph->ihl*4) - return 1; - - iplen = writable_len - (*pskb)->nh.iph->ihl*4; - - /* DaveM says protocol headers are also modifiable. */ - switch ((*pskb)->nh.iph->protocol) { - case IPPROTO_TCP: { - struct tcphdr hdr; - if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4, - &hdr, sizeof(hdr)) != 0) - goto copy_skb; - if (writable_len <= (*pskb)->nh.iph->ihl*4 + hdr.doff*4) - goto pull_skb; - goto copy_skb; - } - case IPPROTO_UDP: - if (writable_len<=(*pskb)->nh.iph->ihl*4+sizeof(struct udphdr)) - goto pull_skb; - goto copy_skb; - case IPPROTO_ICMP: - if (writable_len - <= (*pskb)->nh.iph->ihl*4 + sizeof(struct icmphdr)) - goto pull_skb; - goto copy_skb; - /* Insert other cases here as desired */ - } - -copy_skb: - nskb = skb_copy(*pskb, GFP_ATOMIC); - if (!nskb) - return 0; - BUG_ON(skb_is_nonlinear(nskb)); - - /* Rest of kernel will get very unhappy if we pass it a - suddenly-orphaned skbuff */ - if ((*pskb)->sk) - skb_set_owner_w(nskb, (*pskb)->sk); - kfree_skb(*pskb); - *pskb = nskb; - return 1; - -pull_skb: - return pskb_may_pull(*pskb, writable_len); -} - int __init ip_nat_init(void) { size_t i; |
