]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
authorDavid S. Miller <davem@davemloft.net>
Mon, 30 Dec 2019 22:22:11 +0000 (14:22 -0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 30 Dec 2019 22:22:11 +0000 (14:22 -0800)
Pablo Neira Ayuso says:

====================
Netfilter updates for net-next

The following patchset contains Netfilter updates for net-next:

1) Remove #ifdef pollution around nf_ingress(), from Lukas Wunner.

2) Document ingress hook in netdevice, also from Lukas.

3) Remove htons() in tunnel metadata port netlink attributes,
   from Xin Long.

4) Missing erspan netlink attribute validation also from Xin Long.

5) Missing erspan version in tunnel, from Xin Long.

6) Missing attribute nest in NFTA_TUNNEL_KEY_OPTS_{VXLAN,ERSPAN}
   Patch from Xin Long.

7) Missing nla_nest_cancel() in tunnel netlink dump path,
   from Xin Long.

8) Remove two exported conntrack symbols with no clients,
   from Florian Westphal.

9) Add nft_meta_get_eval_time() helper to nft_meta, from Florian.

10) Add nft_meta_pkttype helper for loopback, also from Florian.

11) Add nft_meta_socket uid helper, from Florian Westphal.

12) Add nft_meta_cgroup helper, from Florian.

13) Add nft_meta_ifkind helper, from Florian.

14) Group all interface related meta selector, from Florian.

15) Add nft_prandom_u32() helper, from Florian.

16) Add nft_meta_rtclassid helper, from Florian.

17) Add support for matching on the slave device index,
    from Florian.

This batch, among other things, contains updates for the netfilter
tunnel netlink interface: This extension is still incomplete and lacking
proper userspace support which is actually my fault, I did not find the
time to go back and finish this. This update is breaking tunnel UAPI in
some aspects to fix it but do it better sooner than never.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netdevice.h
include/uapi/linux/netfilter/nf_tables.h
net/core/dev.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_extend.c
net/netfilter/nft_meta.c
net/netfilter/nft_tunnel.c

index f007155ae8f4825f4d8a8b63f746bd2a4bff6b00..2fd19fb8826dd8f95d01e8afa7fa2f27337f7fc0 100644 (file)
@@ -1709,6 +1709,7 @@ enum netdev_priv_flags {
  *     @miniq_ingress:         ingress/clsact qdisc specific data for
  *                             ingress processing
  *     @ingress_queue:         XXX: need comments on this one
+ *     @nf_hooks_ingress:      netfilter hooks executed for ingress packets
  *     @broadcast:             hw bcast address
  *
  *     @rx_cpu_rmap:   CPU reverse-mapping for RX completion interrupts,
index bb9b049310dffbebbaf84113aaee5737bbf94190..e237ecbdcd8ac3740cfaa714fcb6e3a1f340a317 100644 (file)
@@ -805,6 +805,8 @@ enum nft_exthdr_attributes {
  * @NFT_META_TIME_NS: time since epoch (in nanoseconds)
  * @NFT_META_TIME_DAY: day of week (from 0 = Sunday to 6 = Saturday)
  * @NFT_META_TIME_HOUR: hour of day (in seconds)
+ * @NFT_META_SDIF: slave device interface index
+ * @NFT_META_SDIFNAME: slave device interface name
  */
 enum nft_meta_keys {
        NFT_META_LEN,
@@ -840,6 +842,8 @@ enum nft_meta_keys {
        NFT_META_TIME_NS,
        NFT_META_TIME_DAY,
        NFT_META_TIME_HOUR,
+       NFT_META_SDIF,
+       NFT_META_SDIFNAME,
 };
 
 /**
index f1b8afcfbc0f521266ecca7a10c2c3cdc34a3e9c..d99f88c5863674840f8271d134af37e7a9a6463e 100644 (file)
@@ -4932,7 +4932,6 @@ static bool skb_pfmemalloc_protocol(struct sk_buff *skb)
 static inline int nf_ingress(struct sk_buff *skb, struct packet_type **pt_prev,
                             int *ret, struct net_device *orig_dev)
 {
-#ifdef CONFIG_NETFILTER_INGRESS
        if (nf_hook_ingress_active(skb)) {
                int ingress_retval;
 
@@ -4946,7 +4945,6 @@ static inline int nf_ingress(struct sk_buff *skb, struct packet_type **pt_prev,
                rcu_read_unlock();
                return ingress_retval;
        }
-#endif /* CONFIG_NETFILTER_INGRESS */
        return 0;
 }
 
index f475fec84536a3e7bf61b771fa5a6a7a9f06288e..f4c4b467c87e9abf55b84cf0fae3948836b18afe 100644 (file)
@@ -2334,7 +2334,6 @@ int nf_conntrack_set_hashsize(const char *val, const struct kernel_param *kp)
 
        return nf_conntrack_hash_resize(hashsize);
 }
-EXPORT_SYMBOL_GPL(nf_conntrack_set_hashsize);
 
 static __always_inline unsigned int total_extension_size(void)
 {
index c24e5b64b00cbd8d125dc1dd7e3158169eb22bf8..3dbe2329c3f173a871bad9b6a4154683b02dc572 100644 (file)
@@ -37,7 +37,6 @@ void nf_ct_ext_destroy(struct nf_conn *ct)
 
        kfree(ct->ext);
 }
-EXPORT_SYMBOL(nf_ct_ext_destroy);
 
 void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
 {
index 9740b554fdb311216f2337ccab508cedd3d8c8d1..951b6e87ed5d9fd4de57aff772a2a9f2d58b0d2a 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/smp.h>
 #include <linux/static_key.h>
 #include <net/dst.h>
+#include <net/ip.h>
 #include <net/sock.h>
 #include <net/tcp_states.h> /* for TCP_TIME_WAIT */
 #include <net/netfilter/nf_tables.h>
@@ -33,8 +34,9 @@
 
 static DEFINE_PER_CPU(struct rnd_state, nft_prandom_state);
 
-static u8 nft_meta_weekday(time64_t secs)
+static u8 nft_meta_weekday(void)
 {
+       time64_t secs = ktime_get_real_seconds();
        unsigned int dse;
        u8 wday;
 
@@ -56,14 +58,264 @@ static u32 nft_meta_hour(time64_t secs)
                + tm.tm_sec;
 }
 
+static noinline_for_stack void
+nft_meta_get_eval_time(enum nft_meta_keys key,
+                      u32 *dest)
+{
+       switch (key) {
+       case NFT_META_TIME_NS:
+               nft_reg_store64(dest, ktime_get_real_ns());
+               break;
+       case NFT_META_TIME_DAY:
+               nft_reg_store8(dest, nft_meta_weekday());
+               break;
+       case NFT_META_TIME_HOUR:
+               *dest = nft_meta_hour(ktime_get_real_seconds());
+               break;
+       default:
+               break;
+       }
+}
+
+static noinline bool
+nft_meta_get_eval_pkttype_lo(const struct nft_pktinfo *pkt,
+                            u32 *dest)
+{
+       const struct sk_buff *skb = pkt->skb;
+
+       switch (nft_pf(pkt)) {
+       case NFPROTO_IPV4:
+               if (ipv4_is_multicast(ip_hdr(skb)->daddr))
+                       nft_reg_store8(dest, PACKET_MULTICAST);
+               else
+                       nft_reg_store8(dest, PACKET_BROADCAST);
+               break;
+       case NFPROTO_IPV6:
+               nft_reg_store8(dest, PACKET_MULTICAST);
+               break;
+       case NFPROTO_NETDEV:
+               switch (skb->protocol) {
+               case htons(ETH_P_IP): {
+                       int noff = skb_network_offset(skb);
+                       struct iphdr *iph, _iph;
+
+                       iph = skb_header_pointer(skb, noff,
+                                                sizeof(_iph), &_iph);
+                       if (!iph)
+                               return false;
+
+                       if (ipv4_is_multicast(iph->daddr))
+                               nft_reg_store8(dest, PACKET_MULTICAST);
+                       else
+                               nft_reg_store8(dest, PACKET_BROADCAST);
+
+                       break;
+               }
+               case htons(ETH_P_IPV6):
+                       nft_reg_store8(dest, PACKET_MULTICAST);
+                       break;
+               default:
+                       WARN_ON_ONCE(1);
+                       return false;
+               }
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return false;
+       }
+
+       return true;
+}
+
+static noinline bool
+nft_meta_get_eval_skugid(enum nft_meta_keys key,
+                        u32 *dest,
+                        const struct nft_pktinfo *pkt)
+{
+       struct sock *sk = skb_to_full_sk(pkt->skb);
+       struct socket *sock;
+
+       if (!sk || !sk_fullsock(sk) || !net_eq(nft_net(pkt), sock_net(sk)))
+               return false;
+
+       read_lock_bh(&sk->sk_callback_lock);
+       sock = sk->sk_socket;
+       if (!sock || !sock->file) {
+               read_unlock_bh(&sk->sk_callback_lock);
+               return false;
+       }
+
+       switch (key) {
+       case NFT_META_SKUID:
+               *dest = from_kuid_munged(&init_user_ns,
+                                        sock->file->f_cred->fsuid);
+               break;
+       case NFT_META_SKGID:
+               *dest = from_kgid_munged(&init_user_ns,
+                                        sock->file->f_cred->fsgid);
+               break;
+       default:
+               break;
+       }
+
+       read_unlock_bh(&sk->sk_callback_lock);
+       return true;
+}
+
+#ifdef CONFIG_CGROUP_NET_CLASSID
+static noinline bool
+nft_meta_get_eval_cgroup(u32 *dest, const struct nft_pktinfo *pkt)
+{
+       struct sock *sk = skb_to_full_sk(pkt->skb);
+
+       if (!sk || !sk_fullsock(sk) || !net_eq(nft_net(pkt), sock_net(sk)))
+               return false;
+
+       *dest = sock_cgroup_classid(&sk->sk_cgrp_data);
+       return true;
+}
+#endif
+
+static noinline bool nft_meta_get_eval_kind(enum nft_meta_keys key,
+                                           u32 *dest,
+                                           const struct nft_pktinfo *pkt)
+{
+       const struct net_device *in = nft_in(pkt), *out = nft_out(pkt);
+
+       switch (key) {
+       case NFT_META_IIFKIND:
+               if (!in || !in->rtnl_link_ops)
+                       return false;
+               strncpy((char *)dest, in->rtnl_link_ops->kind, IFNAMSIZ);
+               break;
+       case NFT_META_OIFKIND:
+               if (!out || !out->rtnl_link_ops)
+                       return false;
+               strncpy((char *)dest, out->rtnl_link_ops->kind, IFNAMSIZ);
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+static void nft_meta_store_ifindex(u32 *dest, const struct net_device *dev)
+{
+       *dest = dev ? dev->ifindex : 0;
+}
+
+static void nft_meta_store_ifname(u32 *dest, const struct net_device *dev)
+{
+       strncpy((char *)dest, dev ? dev->name : "", IFNAMSIZ);
+}
+
+static bool nft_meta_store_iftype(u32 *dest, const struct net_device *dev)
+{
+       if (!dev)
+               return false;
+
+       nft_reg_store16(dest, dev->type);
+       return true;
+}
+
+static bool nft_meta_store_ifgroup(u32 *dest, const struct net_device *dev)
+{
+       if (!dev)
+               return false;
+
+       *dest = dev->group;
+       return true;
+}
+
+static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest,
+                                    const struct nft_pktinfo *pkt)
+{
+       switch (key) {
+       case NFT_META_IIFNAME:
+               nft_meta_store_ifname(dest, nft_in(pkt));
+               break;
+       case NFT_META_OIFNAME:
+               nft_meta_store_ifname(dest, nft_out(pkt));
+               break;
+       case NFT_META_IIF:
+               nft_meta_store_ifindex(dest, nft_in(pkt));
+               break;
+       case NFT_META_OIF:
+               nft_meta_store_ifindex(dest, nft_out(pkt));
+               break;
+       case NFT_META_IIFTYPE:
+               if (!nft_meta_store_iftype(dest, nft_in(pkt)))
+                       return false;
+               break;
+       case NFT_META_OIFTYPE:
+               if (!nft_meta_store_iftype(dest, nft_out(pkt)))
+                       return false;
+               break;
+       case NFT_META_IIFGROUP:
+               if (!nft_meta_store_ifgroup(dest, nft_out(pkt)))
+                       return false;
+               break;
+       case NFT_META_OIFGROUP:
+               if (!nft_meta_store_ifgroup(dest, nft_out(pkt)))
+                       return false;
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+static noinline u32 nft_prandom_u32(void)
+{
+       struct rnd_state *state = this_cpu_ptr(&nft_prandom_state);
+
+       return prandom_u32_state(state);
+}
+
+#ifdef CONFIG_IP_ROUTE_CLASSID
+static noinline bool
+nft_meta_get_eval_rtclassid(const struct sk_buff *skb, u32 *dest)
+{
+       const struct dst_entry *dst = skb_dst(skb);
+
+       if (!dst)
+               return false;
+
+       *dest = dst->tclassid;
+       return true;
+}
+#endif
+
+static noinline u32 nft_meta_get_eval_sdif(const struct nft_pktinfo *pkt)
+{
+       switch (nft_pf(pkt)) {
+       case NFPROTO_IPV4:
+               return inet_sdif(pkt->skb);
+       case NFPROTO_IPV6:
+               return inet6_sdif(pkt->skb);
+       }
+
+       return 0;
+}
+
+static noinline void
+nft_meta_get_eval_sdifname(u32 *dest, const struct nft_pktinfo *pkt)
+{
+       u32 sdif = nft_meta_get_eval_sdif(pkt);
+       const struct net_device *dev;
+
+       dev = sdif ? dev_get_by_index_rcu(nft_net(pkt), sdif) : NULL;
+       nft_meta_store_ifname(dest, dev);
+}
+
 void nft_meta_get_eval(const struct nft_expr *expr,
                       struct nft_regs *regs,
                       const struct nft_pktinfo *pkt)
 {
        const struct nft_meta *priv = nft_expr_priv(expr);
        const struct sk_buff *skb = pkt->skb;
-       const struct net_device *in = nft_in(pkt), *out = nft_out(pkt);
-       struct sock *sk;
        u32 *dest = &regs->data[priv->dreg];
 
        switch (priv->key) {
@@ -88,69 +340,26 @@ void nft_meta_get_eval(const struct nft_expr *expr,
                *dest = skb->mark;
                break;
        case NFT_META_IIF:
-               *dest = in ? in->ifindex : 0;
-               break;
        case NFT_META_OIF:
-               *dest = out ? out->ifindex : 0;
-               break;
        case NFT_META_IIFNAME:
-               strncpy((char *)dest, in ? in->name : "", IFNAMSIZ);
-               break;
        case NFT_META_OIFNAME:
-               strncpy((char *)dest, out ? out->name : "", IFNAMSIZ);
-               break;
        case NFT_META_IIFTYPE:
-               if (in == NULL)
-                       goto err;
-               nft_reg_store16(dest, in->type);
-               break;
        case NFT_META_OIFTYPE:
-               if (out == NULL)
+       case NFT_META_IIFGROUP:
+       case NFT_META_OIFGROUP:
+               if (!nft_meta_get_eval_ifname(priv->key, dest, pkt))
                        goto err;
-               nft_reg_store16(dest, out->type);
                break;
        case NFT_META_SKUID:
-               sk = skb_to_full_sk(skb);
-               if (!sk || !sk_fullsock(sk) ||
-                   !net_eq(nft_net(pkt), sock_net(sk)))
-                       goto err;
-
-               read_lock_bh(&sk->sk_callback_lock);
-               if (sk->sk_socket == NULL ||
-                   sk->sk_socket->file == NULL) {
-                       read_unlock_bh(&sk->sk_callback_lock);
-                       goto err;
-               }
-
-               *dest = from_kuid_munged(&init_user_ns,
-                               sk->sk_socket->file->f_cred->fsuid);
-               read_unlock_bh(&sk->sk_callback_lock);
-               break;
        case NFT_META_SKGID:
-               sk = skb_to_full_sk(skb);
-               if (!sk || !sk_fullsock(sk) ||
-                   !net_eq(nft_net(pkt), sock_net(sk)))
+               if (!nft_meta_get_eval_skugid(priv->key, dest, pkt))
                        goto err;
-
-               read_lock_bh(&sk->sk_callback_lock);
-               if (sk->sk_socket == NULL ||
-                   sk->sk_socket->file == NULL) {
-                       read_unlock_bh(&sk->sk_callback_lock);
-                       goto err;
-               }
-               *dest = from_kgid_munged(&init_user_ns,
-                                sk->sk_socket->file->f_cred->fsgid);
-               read_unlock_bh(&sk->sk_callback_lock);
                break;
 #ifdef CONFIG_IP_ROUTE_CLASSID
-       case NFT_META_RTCLASSID: {
-               const struct dst_entry *dst = skb_dst(skb);
-
-               if (dst == NULL)
+       case NFT_META_RTCLASSID:
+               if (!nft_meta_get_eval_rtclassid(skb, dest))
                        goto err;
-               *dest = dst->tclassid;
                break;
-       }
 #endif
 #ifdef CONFIG_NETWORK_SECMARK
        case NFT_META_SECMARK:
@@ -163,97 +372,41 @@ void nft_meta_get_eval(const struct nft_expr *expr,
                        break;
                }
 
-               switch (nft_pf(pkt)) {
-               case NFPROTO_IPV4:
-                       if (ipv4_is_multicast(ip_hdr(skb)->daddr))
-                               nft_reg_store8(dest, PACKET_MULTICAST);
-                       else
-                               nft_reg_store8(dest, PACKET_BROADCAST);
-                       break;
-               case NFPROTO_IPV6:
-                       nft_reg_store8(dest, PACKET_MULTICAST);
-                       break;
-               case NFPROTO_NETDEV:
-                       switch (skb->protocol) {
-                       case htons(ETH_P_IP): {
-                               int noff = skb_network_offset(skb);
-                               struct iphdr *iph, _iph;
-
-                               iph = skb_header_pointer(skb, noff,
-                                                        sizeof(_iph), &_iph);
-                               if (!iph)
-                                       goto err;
-
-                               if (ipv4_is_multicast(iph->daddr))
-                                       nft_reg_store8(dest, PACKET_MULTICAST);
-                               else
-                                       nft_reg_store8(dest, PACKET_BROADCAST);
-
-                               break;
-                       }
-                       case htons(ETH_P_IPV6):
-                               nft_reg_store8(dest, PACKET_MULTICAST);
-                               break;
-                       default:
-                               WARN_ON_ONCE(1);
-                               goto err;
-                       }
-                       break;
-               default:
-                       WARN_ON_ONCE(1);
+               if (!nft_meta_get_eval_pkttype_lo(pkt, dest))
                        goto err;
-               }
                break;
        case NFT_META_CPU:
                *dest = raw_smp_processor_id();
                break;
-       case NFT_META_IIFGROUP:
-               if (in == NULL)
-                       goto err;
-               *dest = in->group;
-               break;
-       case NFT_META_OIFGROUP:
-               if (out == NULL)
-                       goto err;
-               *dest = out->group;
-               break;
 #ifdef CONFIG_CGROUP_NET_CLASSID
        case NFT_META_CGROUP:
-               sk = skb_to_full_sk(skb);
-               if (!sk || !sk_fullsock(sk) ||
-                   !net_eq(nft_net(pkt), sock_net(sk)))
+               if (!nft_meta_get_eval_cgroup(dest, pkt))
                        goto err;
-               *dest = sock_cgroup_classid(&sk->sk_cgrp_data);
                break;
 #endif
-       case NFT_META_PRANDOM: {
-               struct rnd_state *state = this_cpu_ptr(&nft_prandom_state);
-               *dest = prandom_u32_state(state);
+       case NFT_META_PRANDOM:
+               *dest = nft_prandom_u32();
                break;
-       }
 #ifdef CONFIG_XFRM
        case NFT_META_SECPATH:
                nft_reg_store8(dest, secpath_exists(skb));
                break;
 #endif
        case NFT_META_IIFKIND:
-               if (in == NULL || in->rtnl_link_ops == NULL)
-                       goto err;
-               strncpy((char *)dest, in->rtnl_link_ops->kind, IFNAMSIZ);
-               break;
        case NFT_META_OIFKIND:
-               if (out == NULL || out->rtnl_link_ops == NULL)
+               if (!nft_meta_get_eval_kind(priv->key, dest, pkt))
                        goto err;
-               strncpy((char *)dest, out->rtnl_link_ops->kind, IFNAMSIZ);
                break;
        case NFT_META_TIME_NS:
-               nft_reg_store64(dest, ktime_get_real_ns());
-               break;
        case NFT_META_TIME_DAY:
-               nft_reg_store8(dest, nft_meta_weekday(ktime_get_real_seconds()));
-               break;
        case NFT_META_TIME_HOUR:
-               *dest = nft_meta_hour(ktime_get_real_seconds());
+               nft_meta_get_eval_time(priv->key, dest);
+               break;
+       case NFT_META_SDIF:
+               *dest = nft_meta_get_eval_sdif(pkt);
+               break;
+       case NFT_META_SDIFNAME:
+               nft_meta_get_eval_sdifname(dest, pkt);
                break;
        default:
                WARN_ON(1);
@@ -335,6 +488,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
        case NFT_META_MARK:
        case NFT_META_IIF:
        case NFT_META_OIF:
+       case NFT_META_SDIF:
        case NFT_META_SKUID:
        case NFT_META_SKGID:
 #ifdef CONFIG_IP_ROUTE_CLASSID
@@ -356,6 +510,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
        case NFT_META_OIFNAME:
        case NFT_META_IIFKIND:
        case NFT_META_OIFKIND:
+       case NFT_META_SDIFNAME:
                len = IFNAMSIZ;
                break;
        case NFT_META_PRANDOM:
@@ -386,16 +541,28 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
 }
 EXPORT_SYMBOL_GPL(nft_meta_get_init);
 
-static int nft_meta_get_validate(const struct nft_ctx *ctx,
-                                const struct nft_expr *expr,
-                                const struct nft_data **data)
+static int nft_meta_get_validate_sdif(const struct nft_ctx *ctx)
 {
-#ifdef CONFIG_XFRM
-       const struct nft_meta *priv = nft_expr_priv(expr);
        unsigned int hooks;
 
-       if (priv->key != NFT_META_SECPATH)
-               return 0;
+       switch (ctx->family) {
+       case NFPROTO_IPV4:
+       case NFPROTO_IPV6:
+       case NFPROTO_INET:
+               hooks = (1 << NF_INET_LOCAL_IN) |
+                       (1 << NF_INET_FORWARD);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return nft_chain_validate_hooks(ctx->chain, hooks);
+}
+
+static int nft_meta_get_validate_xfrm(const struct nft_ctx *ctx)
+{
+#ifdef CONFIG_XFRM
+       unsigned int hooks;
 
        switch (ctx->family) {
        case NFPROTO_NETDEV:
@@ -418,6 +585,25 @@ static int nft_meta_get_validate(const struct nft_ctx *ctx,
 #endif
 }
 
+static int nft_meta_get_validate(const struct nft_ctx *ctx,
+                                const struct nft_expr *expr,
+                                const struct nft_data **data)
+{
+       const struct nft_meta *priv = nft_expr_priv(expr);
+
+       switch (priv->key) {
+       case NFT_META_SECPATH:
+               return nft_meta_get_validate_xfrm(ctx);
+       case NFT_META_SDIF:
+       case NFT_META_SDIFNAME:
+               return nft_meta_get_validate_sdif(ctx);
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 int nft_meta_set_validate(const struct nft_ctx *ctx,
                          const struct nft_expr *expr,
                          const struct nft_data **data)
index 3d4c2ae605a8e2c3ceee669570775cd5ebbb3e1e..23cd163689d5a36b98abd4858f21f63a680174ab 100644 (file)
@@ -248,8 +248,9 @@ static int nft_tunnel_obj_vxlan_init(const struct nlattr *attr,
 }
 
 static const struct nla_policy nft_tunnel_opts_erspan_policy[NFTA_TUNNEL_KEY_ERSPAN_MAX + 1] = {
+       [NFTA_TUNNEL_KEY_ERSPAN_VERSION]        = { .type = NLA_U32 },
        [NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX]       = { .type = NLA_U32 },
-       [NFTA_TUNNEL_KEY_ERSPAN_V2_DIR] = { .type = NLA_U8 },
+       [NFTA_TUNNEL_KEY_ERSPAN_V2_DIR]         = { .type = NLA_U8 },
        [NFTA_TUNNEL_KEY_ERSPAN_V2_HWID]        = { .type = NLA_U8 },
 };
 
@@ -442,10 +443,15 @@ static int nft_tunnel_ip_dump(struct sk_buff *skb, struct ip_tunnel_info *info)
                if (!nest)
                        return -1;
 
-               if (nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_SRC, &info->key.u.ipv6.src) < 0 ||
-                   nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_DST, &info->key.u.ipv6.dst) < 0 ||
-                   nla_put_be32(skb, NFTA_TUNNEL_KEY_IP6_FLOWLABEL, info->key.label))
+               if (nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_SRC,
+                                    &info->key.u.ipv6.src) < 0 ||
+                   nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_DST,
+                                    &info->key.u.ipv6.dst) < 0 ||
+                   nla_put_be32(skb, NFTA_TUNNEL_KEY_IP6_FLOWLABEL,
+                                info->key.label)) {
+                       nla_nest_cancel(skb, nest);
                        return -1;
+               }
 
                nla_nest_end(skb, nest);
        } else {
@@ -453,9 +459,13 @@ static int nft_tunnel_ip_dump(struct sk_buff *skb, struct ip_tunnel_info *info)
                if (!nest)
                        return -1;
 
-               if (nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_SRC, info->key.u.ipv4.src) < 0 ||
-                   nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_DST, info->key.u.ipv4.dst) < 0)
+               if (nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_SRC,
+                                   info->key.u.ipv4.src) < 0 ||
+                   nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_DST,
+                                   info->key.u.ipv4.dst) < 0) {
+                       nla_nest_cancel(skb, nest);
                        return -1;
+               }
 
                nla_nest_end(skb, nest);
        }
@@ -467,42 +477,58 @@ static int nft_tunnel_opts_dump(struct sk_buff *skb,
                                struct nft_tunnel_obj *priv)
 {
        struct nft_tunnel_opts *opts = &priv->opts;
-       struct nlattr *nest;
+       struct nlattr *nest, *inner;
 
        nest = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS);
        if (!nest)
                return -1;
 
        if (opts->flags & TUNNEL_VXLAN_OPT) {
+               inner = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS_VXLAN);
+               if (!inner)
+                       goto failure;
                if (nla_put_be32(skb, NFTA_TUNNEL_KEY_VXLAN_GBP,
                                 htonl(opts->u.vxlan.gbp)))
-                       return -1;
+                       goto inner_failure;
+               nla_nest_end(skb, inner);
        } else if (opts->flags & TUNNEL_ERSPAN_OPT) {
+               inner = nla_nest_start_noflag(skb, NFTA_TUNNEL_KEY_OPTS_ERSPAN);
+               if (!inner)
+                       goto failure;
+               if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ERSPAN_VERSION,
+                                htonl(opts->u.erspan.version)))
+                       goto inner_failure;
                switch (opts->u.erspan.version) {
                case ERSPAN_VERSION:
                        if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX,
                                         opts->u.erspan.u.index))
-                               return -1;
+                               goto inner_failure;
                        break;
                case ERSPAN_VERSION2:
                        if (nla_put_u8(skb, NFTA_TUNNEL_KEY_ERSPAN_V2_HWID,
                                       get_hwid(&opts->u.erspan.u.md2)) ||
                            nla_put_u8(skb, NFTA_TUNNEL_KEY_ERSPAN_V2_DIR,
                                       opts->u.erspan.u.md2.dir))
-                               return -1;
+                               goto inner_failure;
                        break;
                }
+               nla_nest_end(skb, inner);
        }
        nla_nest_end(skb, nest);
-
        return 0;
+
+inner_failure:
+       nla_nest_cancel(skb, inner);
+failure:
+       nla_nest_cancel(skb, nest);
+       return -1;
 }
 
 static int nft_tunnel_ports_dump(struct sk_buff *skb,
                                 struct ip_tunnel_info *info)
 {
-       if (nla_put_be16(skb, NFTA_TUNNEL_KEY_SPORT, htons(info->key.tp_src)) < 0 ||
-           nla_put_be16(skb, NFTA_TUNNEL_KEY_DPORT, htons(info->key.tp_dst)) < 0)
+       if (nla_put_be16(skb, NFTA_TUNNEL_KEY_SPORT, info->key.tp_src) < 0 ||
+           nla_put_be16(skb, NFTA_TUNNEL_KEY_DPORT, info->key.tp_dst) < 0)
                return -1;
 
        return 0;