linux/net/netfilter/nft_socket.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2#include <linux/module.h>
   3#include <linux/netfilter/nf_tables.h>
   4#include <net/netfilter/nf_tables.h>
   5#include <net/netfilter/nf_tables_core.h>
   6#include <net/netfilter/nf_socket.h>
   7#include <net/inet_sock.h>
   8#include <net/tcp.h>
   9
  10struct nft_socket {
  11        enum nft_socket_keys            key:8;
  12        u8                              level;
  13        union {
  14                u8                      dreg;
  15        };
  16};
  17
  18static void nft_socket_wildcard(const struct nft_pktinfo *pkt,
  19                                struct nft_regs *regs, struct sock *sk,
  20                                u32 *dest)
  21{
  22        switch (nft_pf(pkt)) {
  23        case NFPROTO_IPV4:
  24                nft_reg_store8(dest, inet_sk(sk)->inet_rcv_saddr == 0);
  25                break;
  26#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
  27        case NFPROTO_IPV6:
  28                nft_reg_store8(dest, ipv6_addr_any(&sk->sk_v6_rcv_saddr));
  29                break;
  30#endif
  31        default:
  32                regs->verdict.code = NFT_BREAK;
  33                return;
  34        }
  35}
  36
  37#ifdef CONFIG_SOCK_CGROUP_DATA
  38static noinline bool
  39nft_sock_get_eval_cgroupv2(u32 *dest, const struct nft_pktinfo *pkt, u32 level)
  40{
  41        struct sock *sk = skb_to_full_sk(pkt->skb);
  42        struct cgroup *cgrp;
  43
  44        if (!sk || !sk_fullsock(sk) || !net_eq(nft_net(pkt), sock_net(sk)))
  45                return false;
  46
  47        cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
  48        if (level > cgrp->level)
  49                return false;
  50
  51        memcpy(dest, &cgrp->ancestor_ids[level], sizeof(u64));
  52
  53        return true;
  54}
  55#endif
  56
  57static void nft_socket_eval(const struct nft_expr *expr,
  58                            struct nft_regs *regs,
  59                            const struct nft_pktinfo *pkt)
  60{
  61        const struct nft_socket *priv = nft_expr_priv(expr);
  62        struct sk_buff *skb = pkt->skb;
  63        struct sock *sk = skb->sk;
  64        u32 *dest = &regs->data[priv->dreg];
  65
  66        if (sk && !net_eq(nft_net(pkt), sock_net(sk)))
  67                sk = NULL;
  68
  69        if (!sk)
  70                switch(nft_pf(pkt)) {
  71                case NFPROTO_IPV4:
  72                        sk = nf_sk_lookup_slow_v4(nft_net(pkt), skb, nft_in(pkt));
  73                        break;
  74#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
  75                case NFPROTO_IPV6:
  76                        sk = nf_sk_lookup_slow_v6(nft_net(pkt), skb, nft_in(pkt));
  77                        break;
  78#endif
  79                default:
  80                        WARN_ON_ONCE(1);
  81                        regs->verdict.code = NFT_BREAK;
  82                        return;
  83                }
  84
  85        if (!sk) {
  86                regs->verdict.code = NFT_BREAK;
  87                return;
  88        }
  89
  90        switch(priv->key) {
  91        case NFT_SOCKET_TRANSPARENT:
  92                nft_reg_store8(dest, inet_sk_transparent(sk));
  93                break;
  94        case NFT_SOCKET_MARK:
  95                if (sk_fullsock(sk)) {
  96                        *dest = sk->sk_mark;
  97                } else {
  98                        regs->verdict.code = NFT_BREAK;
  99                        return;
 100                }
 101                break;
 102        case NFT_SOCKET_WILDCARD:
 103                if (!sk_fullsock(sk)) {
 104                        regs->verdict.code = NFT_BREAK;
 105                        return;
 106                }
 107                nft_socket_wildcard(pkt, regs, sk, dest);
 108                break;
 109#ifdef CONFIG_SOCK_CGROUP_DATA
 110        case NFT_SOCKET_CGROUPV2:
 111                if (!nft_sock_get_eval_cgroupv2(dest, pkt, priv->level)) {
 112                        regs->verdict.code = NFT_BREAK;
 113                        return;
 114                }
 115                break;
 116#endif
 117        default:
 118                WARN_ON(1);
 119                regs->verdict.code = NFT_BREAK;
 120        }
 121
 122        if (sk != skb->sk)
 123                sock_gen_put(sk);
 124}
 125
 126static const struct nla_policy nft_socket_policy[NFTA_SOCKET_MAX + 1] = {
 127        [NFTA_SOCKET_KEY]               = { .type = NLA_U32 },
 128        [NFTA_SOCKET_DREG]              = { .type = NLA_U32 },
 129        [NFTA_SOCKET_LEVEL]             = { .type = NLA_U32 },
 130};
 131
 132static int nft_socket_init(const struct nft_ctx *ctx,
 133                           const struct nft_expr *expr,
 134                           const struct nlattr * const tb[])
 135{
 136        struct nft_socket *priv = nft_expr_priv(expr);
 137        unsigned int len;
 138
 139        if (!tb[NFTA_SOCKET_DREG] || !tb[NFTA_SOCKET_KEY])
 140                return -EINVAL;
 141
 142        switch(ctx->family) {
 143        case NFPROTO_IPV4:
 144#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
 145        case NFPROTO_IPV6:
 146#endif
 147        case NFPROTO_INET:
 148                break;
 149        default:
 150                return -EOPNOTSUPP;
 151        }
 152
 153        priv->key = ntohl(nla_get_u32(tb[NFTA_SOCKET_KEY]));
 154        switch(priv->key) {
 155        case NFT_SOCKET_TRANSPARENT:
 156        case NFT_SOCKET_WILDCARD:
 157                len = sizeof(u8);
 158                break;
 159        case NFT_SOCKET_MARK:
 160                len = sizeof(u32);
 161                break;
 162#ifdef CONFIG_CGROUPS
 163        case NFT_SOCKET_CGROUPV2: {
 164                unsigned int level;
 165
 166                if (!tb[NFTA_SOCKET_LEVEL])
 167                        return -EINVAL;
 168
 169                level = ntohl(nla_get_u32(tb[NFTA_SOCKET_LEVEL]));
 170                if (level > 255)
 171                        return -EOPNOTSUPP;
 172
 173                priv->level = level;
 174                len = sizeof(u64);
 175                break;
 176        }
 177#endif
 178        default:
 179                return -EOPNOTSUPP;
 180        }
 181
 182        return nft_parse_register_store(ctx, tb[NFTA_SOCKET_DREG], &priv->dreg,
 183                                        NULL, NFT_DATA_VALUE, len);
 184}
 185
 186static int nft_socket_dump(struct sk_buff *skb,
 187                           const struct nft_expr *expr)
 188{
 189        const struct nft_socket *priv = nft_expr_priv(expr);
 190
 191        if (nla_put_u32(skb, NFTA_SOCKET_KEY, htonl(priv->key)))
 192                return -1;
 193        if (nft_dump_register(skb, NFTA_SOCKET_DREG, priv->dreg))
 194                return -1;
 195        if (priv->key == NFT_SOCKET_CGROUPV2 &&
 196            nla_put_u32(skb, NFTA_SOCKET_LEVEL, htonl(priv->level)))
 197                return -1;
 198        return 0;
 199}
 200
 201static struct nft_expr_type nft_socket_type;
 202static const struct nft_expr_ops nft_socket_ops = {
 203        .type           = &nft_socket_type,
 204        .size           = NFT_EXPR_SIZE(sizeof(struct nft_socket)),
 205        .eval           = nft_socket_eval,
 206        .init           = nft_socket_init,
 207        .dump           = nft_socket_dump,
 208};
 209
 210static struct nft_expr_type nft_socket_type __read_mostly = {
 211        .name           = "socket",
 212        .ops            = &nft_socket_ops,
 213        .policy         = nft_socket_policy,
 214        .maxattr        = NFTA_SOCKET_MAX,
 215        .owner          = THIS_MODULE,
 216};
 217
 218static int __init nft_socket_module_init(void)
 219{
 220        return nft_register_expr(&nft_socket_type);
 221}
 222
 223static void __exit nft_socket_module_exit(void)
 224{
 225        nft_unregister_expr(&nft_socket_type);
 226}
 227
 228module_init(nft_socket_module_init);
 229module_exit(nft_socket_module_exit);
 230
 231MODULE_LICENSE("GPL");
 232MODULE_AUTHOR("Máté Eckl");
 233MODULE_DESCRIPTION("nf_tables socket match module");
 234MODULE_ALIAS_NFT_EXPR("socket");
 235