]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/netfilter/nft_meta.c
Merge tag 'jfs-5.6' of git://github.com/kleikamp/linux-shaggy
[linux.git] / net / netfilter / nft_meta.c
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)