lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Message-Id: <E1IhnTz-0003HT-00@gondolin.me.apana.org.au> Date: Tue, 16 Oct 2007 22:33:19 +0800 From: Herbert Xu <herbert@...dor.apana.org.au> To: "David S. Miller" <davem@...emloft.net>, netdev@...r.kernel.org, Patrick McHardy <kaber@...sh.net>, Herbert Xu <herbert@...dor.apana.org.au> Subject: [PATCH 11/12] [IPSEC]: Reinject packet instead of calling netfilter directly on input [IPSEC]: Reinject packet instead of calling netfilter directly on input Currently we call netfilter directly on input after a series of transport mode transforms (and BEET but that's a separate bug). This is inconsistent because other parts of the stack such AF_PACKET cannot see the decapsulated packet. In fact this is a common complaint about the Linux IPsec stack. Another problem is that there is a potential for stack overflow if we encounter a DNAT rule which turns a foreign packet into a local one that contains another transport mode SA. This patch introduces a major behavioural change by reinjecting the packet instead of calling netfilter directly. This solves both of the aformentioned problems. It is still inconsistent with how we do things on output since we don't pass things through AF_PACKET there either but the same inconsistency exists for tunnel mode too so it's not a new problem. To make things easier I've added a new function called netif_rerx which resets netfilter and the dst before reinjecting the packet using netif_rx. This can be used by other tunnel code as well. I haven't added a reinject function for RO mode since it can never be called on that path and if it does we want to know about it through an OOPS. Signed-off-by: Herbert Xu <herbert@...dor.apana.org.au> --- include/linux/netdevice.h | 1 + include/net/xfrm.h | 8 ++++++++ net/core/dev.c | 12 ++++++++++++ net/ipv4/xfrm4_input.c | 24 ++---------------------- net/ipv4/xfrm4_mode_beet.c | 7 +++++++ net/ipv4/xfrm4_mode_transport.c | 11 +++++++++++ net/ipv4/xfrm4_mode_tunnel.c | 7 +++++++ net/ipv6/xfrm6_input.c | 23 ++--------------------- net/ipv6/xfrm6_mode_beet.c | 7 +++++++ net/ipv6/xfrm6_mode_transport.c | 10 ++++++++++ net/ipv6/xfrm6_mode_tunnel.c | 7 +++++++ 11 files changed, 74 insertions(+), 43 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 39dd83b..097f911 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1039,6 +1039,7 @@ extern void dev_kfree_skb_any(struct sk_buff *skb); #define HAVE_NETIF_RX 1 extern int netif_rx(struct sk_buff *skb); extern int netif_rx_ni(struct sk_buff *skb); +extern int netif_rerx(struct sk_buff *skb); #define HAVE_NETIF_RECEIVE_SKB 1 extern int netif_receive_skb(struct sk_buff *skb); extern int dev_valid_name(const char *name); diff --git a/include/net/xfrm.h b/include/net/xfrm.h index a9e8247..e5ae5fa 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -311,6 +311,14 @@ struct xfrm_mode { */ int (*output)(struct xfrm_state *x,struct sk_buff *skb); + /* + * Reinject packet into stack. + * + * On entry, the packet is in the state as on exit from the + * input function above. + */ + int (*reinject)(struct xfrm_state *x,struct sk_buff *skb); + struct xfrm_state_afinfo *afinfo; struct module *owner; unsigned int encap; diff --git a/net/core/dev.c b/net/core/dev.c index 38b03da..b753ec8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1808,6 +1808,18 @@ int netif_rx_ni(struct sk_buff *skb) EXPORT_SYMBOL(netif_rx_ni); +/* Reinject a packet that has previously been processed, e.g., by tunneling. */ +int netif_rerx(struct sk_buff *skb) +{ + nf_reset(skb); + + dst_release(skb->dst); + skb->dst = NULL; + + return netif_rx(skb); +} +EXPORT_SYMBOL(netif_rerx); + static inline struct net_device *skb_bond(struct sk_buff *skb) { struct net_device *dev = skb->dev; diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index 5cb0b59..f5576d5 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -41,7 +41,6 @@ int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; struct xfrm_state *x; int xfrm_nr = 0; - int decaps = 0; unsigned int nhoff = offsetof(struct iphdr, protocol); seq = 0; @@ -95,7 +94,6 @@ int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, goto drop; if (x->props.mode == XFRM_MODE_TUNNEL) { - decaps = 1; break; } @@ -122,26 +120,8 @@ int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, xfrm_nr * sizeof(xfrm_vec[0])); skb->sp->len += xfrm_nr; - nf_reset(skb); - - if (decaps) { - dst_release(skb->dst); - skb->dst = NULL; - netif_rx(skb); - return 0; - } else { -#ifdef CONFIG_NETFILTER - __skb_push(skb, skb->data - skb_network_header(skb)); - ip_hdr(skb)->tot_len = htons(skb->len); - ip_send_check(ip_hdr(skb)); - - NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL, - xfrm4_rcv_encap_finish); - return 0; -#else - return -ip_hdr(skb)->protocol; -#endif - } + x->mode->reinject(x, skb); + return 0; drop_unlock: spin_unlock(&x->lock); diff --git a/net/ipv4/xfrm4_mode_beet.c b/net/ipv4/xfrm4_mode_beet.c index 73d2338..012ae98 100644 --- a/net/ipv4/xfrm4_mode_beet.c +++ b/net/ipv4/xfrm4_mode_beet.c @@ -11,6 +11,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/netdevice.h> #include <linux/skbuff.h> #include <linux/stringify.h> #include <net/dst.h> @@ -109,9 +110,15 @@ out: return err; } +static int xfrm4_beet_reinject(struct xfrm_state *x, struct sk_buff *skb) +{ + return netif_rerx(skb); +} + static struct xfrm_mode xfrm4_beet_mode = { .input = xfrm4_beet_input, .output = xfrm4_beet_output, + .reinject = xfrm4_beet_reinject, .owner = THIS_MODULE, .encap = XFRM_MODE_BEET, }; diff --git a/net/ipv4/xfrm4_mode_transport.c b/net/ipv4/xfrm4_mode_transport.c index fd840c7..602418b 100644 --- a/net/ipv4/xfrm4_mode_transport.c +++ b/net/ipv4/xfrm4_mode_transport.c @@ -7,6 +7,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/netdevice.h> #include <linux/skbuff.h> #include <linux/stringify.h> #include <net/dst.h> @@ -54,9 +55,19 @@ static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb) return 0; } +static int xfrm4_transport_reinject(struct xfrm_state *x, struct sk_buff *skb) +{ + __skb_push(skb, skb->data - skb_network_header(skb)); + ip_hdr(skb)->tot_len = htons(skb->len); + ip_send_check(ip_hdr(skb)); + + return netif_rerx(skb); +} + static struct xfrm_mode xfrm4_transport_mode = { .input = xfrm4_transport_input, .output = xfrm4_transport_output, + .reinject = xfrm4_transport_reinject, .owner = THIS_MODULE, .encap = XFRM_MODE_TRANSPORT, }; diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index 1ae9d32..780908a 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -7,6 +7,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/netdevice.h> #include <linux/skbuff.h> #include <linux/stringify.h> #include <net/dst.h> @@ -134,9 +135,15 @@ out: return err; } +static int xfrm4_tunnel_reinject(struct xfrm_state *x, struct sk_buff *skb) +{ + return netif_rerx(skb); +} + static struct xfrm_mode xfrm4_tunnel_mode = { .input = xfrm4_tunnel_input, .output = xfrm4_tunnel_output, + .reinject = xfrm4_tunnel_reinject, .owner = THIS_MODULE, .encap = XFRM_MODE_TUNNEL, }; diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index b1201c3..1347e0a 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -23,7 +23,6 @@ int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi) struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH]; struct xfrm_state *x; int xfrm_nr = 0; - int decaps = 0; unsigned int nhoff; nhoff = IP6CB(skb)->nhoff; @@ -72,7 +71,6 @@ int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi) goto drop; if (x->props.mode == XFRM_MODE_TUNNEL) { /* XXX */ - decaps = 1; break; } @@ -98,25 +96,8 @@ int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi) xfrm_nr * sizeof(xfrm_vec[0])); skb->sp->len += xfrm_nr; - nf_reset(skb); - - if (decaps) { - dst_release(skb->dst); - skb->dst = NULL; - netif_rx(skb); - return -1; - } else { -#ifdef CONFIG_NETFILTER - ipv6_hdr(skb)->payload_len = htons(skb->len); - __skb_push(skb, skb->data - skb_network_header(skb)); - - NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL, - ip6_rcv_finish); - return -1; -#else - return 1; -#endif - } + x->mode->reinject(x, skb); + return -1; drop_unlock: spin_unlock(&x->lock); diff --git a/net/ipv6/xfrm6_mode_beet.c b/net/ipv6/xfrm6_mode_beet.c index 13bb1e8..17622cf 100644 --- a/net/ipv6/xfrm6_mode_beet.c +++ b/net/ipv6/xfrm6_mode_beet.c @@ -11,6 +11,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/netdevice.h> #include <linux/skbuff.h> #include <linux/stringify.h> #include <net/dsfield.h> @@ -74,9 +75,15 @@ out: return err; } +static int xfrm6_beet_reinject(struct xfrm_state *x, struct sk_buff *skb) +{ + return netif_rerx(skb); +} + static struct xfrm_mode xfrm6_beet_mode = { .input = xfrm6_beet_input, .output = xfrm6_beet_output, + .reinject = xfrm6_beet_reinject, .owner = THIS_MODULE, .encap = XFRM_MODE_BEET, }; diff --git a/net/ipv6/xfrm6_mode_transport.c b/net/ipv6/xfrm6_mode_transport.c index 4e34410..e165442 100644 --- a/net/ipv6/xfrm6_mode_transport.c +++ b/net/ipv6/xfrm6_mode_transport.c @@ -8,6 +8,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/netdevice.h> #include <linux/skbuff.h> #include <linux/stringify.h> #include <net/dst.h> @@ -59,9 +60,18 @@ static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb) return 0; } +static int xfrm6_transport_reinject(struct xfrm_state *x, struct sk_buff *skb) +{ + ipv6_hdr(skb)->payload_len = htons(skb->len); + __skb_push(skb, skb->data - skb_network_header(skb)); + + return netif_rerx(skb); +} + static struct xfrm_mode xfrm6_transport_mode = { .input = xfrm6_transport_input, .output = xfrm6_transport_output, + .reinject = xfrm6_transport_reinject, .owner = THIS_MODULE, .encap = XFRM_MODE_TRANSPORT, }; diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c index ea22838..1329d6a 100644 --- a/net/ipv6/xfrm6_mode_tunnel.c +++ b/net/ipv6/xfrm6_mode_tunnel.c @@ -8,6 +8,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/netdevice.h> #include <linux/skbuff.h> #include <linux/stringify.h> #include <net/dsfield.h> @@ -113,9 +114,15 @@ out: return err; } +static int xfrm6_tunnel_reinject(struct xfrm_state *x, struct sk_buff *skb) +{ + return netif_rerx(skb); +} + static struct xfrm_mode xfrm6_tunnel_mode = { .input = xfrm6_tunnel_input, .output = xfrm6_tunnel_output, + .reinject = xfrm6_tunnel_reinject, .owner = THIS_MODULE, .encap = XFRM_MODE_TUNNEL, }; - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@...r.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists