udp: restrict offloads to one namespace
authorHannes Frederic Sowa <hannes@stressinduktion.org>
Thu, 7 Jan 2016 13:28:39 +0000 (14:28 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sun, 10 Jan 2016 22:28:24 +0000 (17:28 -0500)
udp tunnel offloads tend to aggregate datagrams based on inner
headers. gro engine gets notified by tunnel implementations about
possible offloads. The match is solely based on the port number.

Imagine a tunnel bound to port 53, the offloading will look into all
DNS packets and tries to aggregate them based on the inner data found
within. This could lead to data corruption and malformed DNS packets.

While this patch minimizes the problem and helps an administrator to find
the issue by querying ip tunnel/fou, a better way would be to match on
the specific destination ip address so if a user space socket is bound
to the same address it will conflict.

Cc: Tom Herbert <tom@herbertland.com>
Cc: Eric Dumazet <edumazet@google.com>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/geneve.c
drivers/net/vxlan.c
include/net/protocol.h
net/ipv4/fou.c
net/ipv4/udp_offload.c

index 58efdec12f300dec69f6790c19f7cd56b10a4b22..db96b0cbb8ba62868aa701ff6419b1dbba4cf706 100644 (file)
@@ -376,7 +376,7 @@ static void geneve_notify_add_rx_port(struct geneve_sock *gs)
        int err;
 
        if (sa_family == AF_INET) {
-               err = udp_add_offload(&gs->udp_offloads);
+               err = udp_add_offload(sock_net(sk), &gs->udp_offloads);
                if (err)
                        pr_warn("geneve: udp_add_offload failed with status %d\n",
                                err);
index 405a7b6cca2560219d490d9e1597138c0c85259e..e1e147f2d6ceb5cef4f7195e5a6a8cbd9620b5d6 100644 (file)
@@ -621,7 +621,7 @@ static void vxlan_notify_add_rx_port(struct vxlan_sock *vs)
        int err;
 
        if (sa_family == AF_INET) {
-               err = udp_add_offload(&vs->udp_offloads);
+               err = udp_add_offload(net, &vs->udp_offloads);
                if (err)
                        pr_warn("vxlan: udp_add_offload failed with status %d\n", err);
        }
index d6fcc1fcdb5b0928a0bd89279e11819a9cc3169d..da689f5432dee2c9e84718abb0cc842305b22a68 100644 (file)
@@ -107,7 +107,7 @@ int inet_del_offload(const struct net_offload *prot, unsigned char num);
 void inet_register_protosw(struct inet_protosw *p);
 void inet_unregister_protosw(struct inet_protosw *p);
 
-int  udp_add_offload(struct udp_offload *prot);
+int  udp_add_offload(struct net *net, struct udp_offload *prot);
 void udp_del_offload(struct udp_offload *prot);
 
 #if IS_ENABLED(CONFIG_IPV6)
index bd903fe0f7508d9d7a94c4ce1e0a825f3e35c398..976f0dcf699197a6e96b0b047ff4ac053125e690 100644 (file)
@@ -498,7 +498,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
        sk->sk_allocation = GFP_ATOMIC;
 
        if (cfg->udp_config.family == AF_INET) {
-               err = udp_add_offload(&fou->udp_offloads);
+               err = udp_add_offload(net, &fou->udp_offloads);
                if (err)
                        goto error;
        }
index f9386160cbee0288e294ea2cd8ba3b5be65cdbf6..5d396b96ae8bb9766543de8e5b756d8aa3e29bdf 100644 (file)
@@ -21,6 +21,7 @@ static struct udp_offload_priv __rcu *udp_offload_base __read_mostly;
 
 struct udp_offload_priv {
        struct udp_offload      *offload;
+       possible_net_t  net;
        struct rcu_head         rcu;
        struct udp_offload_priv __rcu *next;
 };
@@ -241,13 +242,14 @@ out:
        return segs;
 }
 
-int udp_add_offload(struct udp_offload *uo)
+int udp_add_offload(struct net *net, struct udp_offload *uo)
 {
        struct udp_offload_priv *new_offload = kzalloc(sizeof(*new_offload), GFP_ATOMIC);
 
        if (!new_offload)
                return -ENOMEM;
 
+       write_pnet(&new_offload->net, net);
        new_offload->offload = uo;
 
        spin_lock(&udp_offload_lock);
@@ -311,7 +313,8 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
        rcu_read_lock();
        uo_priv = rcu_dereference(udp_offload_base);
        for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) {
-               if (uo_priv->offload->port == uh->dest &&
+               if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) &&
+                   uo_priv->offload->port == uh->dest &&
                    uo_priv->offload->callbacks.gro_receive)
                        goto unflush;
        }
@@ -389,7 +392,8 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff)
 
        uo_priv = rcu_dereference(udp_offload_base);
        for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) {
-               if (uo_priv->offload->port == uh->dest &&
+               if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) &&
+                   uo_priv->offload->port == uh->dest &&
                    uo_priv->offload->callbacks.gro_complete)
                        break;
        }