summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/netfilter_ipv4.h6
-rw-r--r--include/linux/netfilter_ipv4/ip_nat_core.h6
-rw-r--r--net/core/netfilter.c63
-rw-r--r--net/ipv4/netfilter/ip_nat_core.c59
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;