xfrm: fix policy lookup for ipv6 gre packets
authorGhalem Boudour <ghalem.boudour@6wind.com>
Fri, 19 Nov 2021 17:20:16 +0000 (18:20 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 27 Jan 2022 09:54:26 +0000 (10:54 +0100)
commit bcf141b2eb551b3477b24997ebc09c65f117a803 upstream.

On egress side, xfrm lookup is called from __gre6_xmit() with the
fl6_gre_key field not initialized leading to policies selectors check
failure. Consequently, gre packets are sent without encryption.

On ingress side, INET6_PROTO_NOPOLICY was set, thus packets were not
checked against xfrm policies. Like for egress side, fl6_gre_key should be
correctly set, this is now done in decode_session6().

Fixes: c12b395a4664 ("gre: Support GRE over IPv6")
Cc: stable@vger.kernel.org
Signed-off-by: Ghalem Boudour <ghalem.boudour@6wind.com>
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/ipv6/ip6_gre.c
net/xfrm/xfrm_policy.c

index 09fa49b..9a0263f 100644 (file)
@@ -755,6 +755,7 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
                fl6->daddr = key->u.ipv6.dst;
                fl6->flowlabel = key->label;
                fl6->flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+               fl6->fl6_gre_key = tunnel_id_to_key32(key->tun_id);
 
                dsfield = key->tos;
                flags = key->tun_flags &
@@ -990,6 +991,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
                fl6.daddr = key->u.ipv6.dst;
                fl6.flowlabel = key->label;
                fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+               fl6.fl6_gre_key = tunnel_id_to_key32(key->tun_id);
 
                dsfield = key->tos;
                if (!(tun_info->key.tun_flags & TUNNEL_ERSPAN_OPT))
@@ -1098,6 +1100,7 @@ static void ip6gre_tnl_link_config_common(struct ip6_tnl *t)
        fl6->flowi6_oif = p->link;
        fl6->flowlabel = 0;
        fl6->flowi6_proto = IPPROTO_GRE;
+       fl6->fl6_gre_key = t->parms.o_key;
 
        if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS))
                fl6->flowlabel |= IPV6_TCLASS_MASK & p->flowinfo;
@@ -1543,7 +1546,7 @@ static void ip6gre_fb_tunnel_init(struct net_device *dev)
 static struct inet6_protocol ip6gre_protocol __read_mostly = {
        .handler     = gre_rcv,
        .err_handler = ip6gre_err,
-       .flags       = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
+       .flags       = INET6_PROTO_FINAL,
 };
 
 static void ip6gre_destroy_tunnels(struct net *net, struct list_head *head)
index 3a9831c..9f02d3f 100644 (file)
@@ -33,6 +33,7 @@
 #include <net/flow.h>
 #include <net/xfrm.h>
 #include <net/ip.h>
+#include <net/gre.h>
 #if IS_ENABLED(CONFIG_IPV6_MIP6)
 #include <net/mip6.h>
 #endif
@@ -3455,6 +3456,26 @@ decode_session6(struct sk_buff *skb, struct flowi *fl, bool reverse)
                        }
                        fl6->flowi6_proto = nexthdr;
                        return;
+               case IPPROTO_GRE:
+                       if (!onlyproto &&
+                           (nh + offset + 12 < skb->data ||
+                            pskb_may_pull(skb, nh + offset + 12 - skb->data))) {
+                               struct gre_base_hdr *gre_hdr;
+                               __be32 *gre_key;
+
+                               nh = skb_network_header(skb);
+                               gre_hdr = (struct gre_base_hdr *)(nh + offset);
+                               gre_key = (__be32 *)(gre_hdr + 1);
+
+                               if (gre_hdr->flags & GRE_KEY) {
+                                       if (gre_hdr->flags & GRE_CSUM)
+                                               gre_key++;
+                                       fl6->fl6_gre_key = *gre_key;
+                               }
+                       }
+                       fl6->flowi6_proto = nexthdr;
+                       return;
+
 #if IS_ENABLED(CONFIG_IPV6_MIP6)
                case IPPROTO_MH:
                        offset += ipv6_optlen(exthdr);