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        union {
  13                enum nft_registers      dreg:8;
  14        };
  15};
  16
  17static void nft_socket_eval(const struct nft_expr *expr,
  18                            struct nft_regs *regs,
  19                            const struct nft_pktinfo *pkt)
  20{
  21        const struct nft_socket *priv = nft_expr_priv(expr);
  22        struct sk_buff *skb = pkt->skb;
  23        struct sock *sk = skb->sk;
  24        u32 *dest = &regs->data[priv->dreg];
  25
  26        if (sk && !net_eq(nft_net(pkt), sock_net(sk)))
  27                sk = NULL;
  28
  29        if (!sk)
  30                switch(nft_pf(pkt)) {
  31                case NFPROTO_IPV4:
  32                        sk = nf_sk_lookup_slow_v4(nft_net(pkt), skb, nft_in(pkt));
  33                        break;
  34#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
  35                case NFPROTO_IPV6:
  36                        sk = nf_sk_lookup_slow_v6(nft_net(pkt), skb, nft_in(pkt));
  37                        break;
  38#endif
  39                default:
  40                        WARN_ON_ONCE(1);
  41                        regs->verdict.code = NFT_BREAK;
  42                        return;
  43                }
  44
  45        if (!sk) {
  46                regs->verdict.code = NFT_BREAK;
  47                return;
  48        }
  49
  50        /* So that subsequent socket matching not to require other lookups. */
  51        skb->sk = sk;
  52
  53        switch(priv->key) {
  54        case NFT_SOCKET_TRANSPARENT:
  55                nft_reg_store8(dest, inet_sk_transparent(sk));
  56                break;
  57        case NFT_SOCKET_MARK:
  58                if (sk_fullsock(sk)) {
  59                        *dest = sk->sk_mark;
  60                } else {
  61                        regs->verdict.code = NFT_BREAK;
  62                        return;
  63                }
  64                break;
  65        default:
  66                WARN_ON(1);
  67                regs->verdict.code = NFT_BREAK;
  68        }
  69}
  70
  71static const struct nla_policy nft_socket_policy[NFTA_SOCKET_MAX + 1] = {
  72        [NFTA_SOCKET_KEY]               = { .type = NLA_U32 },
  73        [NFTA_SOCKET_DREG]              = { .type = NLA_U32 },
  74};
  75
  76static int nft_socket_init(const struct nft_ctx *ctx,
  77                           const struct nft_expr *expr,
  78                           const struct nlattr * const tb[])
  79{
  80        struct nft_socket *priv = nft_expr_priv(expr);
  81        unsigned int len;
  82
  83        if (!tb[NFTA_SOCKET_DREG] || !tb[NFTA_SOCKET_KEY])
  84                return -EINVAL;
  85
  86        switch(ctx->family) {
  87        case NFPROTO_IPV4:
  88#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
  89        case NFPROTO_IPV6:
  90#endif
  91        case NFPROTO_INET:
  92                break;
  93        default:
  94                return -EOPNOTSUPP;
  95        }
  96
  97        priv->key = ntohl(nla_get_u32(tb[NFTA_SOCKET_KEY]));
  98        switch(priv->key) {
  99        case NFT_SOCKET_TRANSPARENT:
 100                len = sizeof(u8);
 101                break;
 102        case NFT_SOCKET_MARK:
 103                len = sizeof(u32);
 104                break;
 105        default:
 106                return -EOPNOTSUPP;
 107        }
 108
 109        priv->dreg = nft_parse_register(tb[NFTA_SOCKET_DREG]);
 110        return nft_validate_register_store(ctx, priv->dreg, NULL,
 111                                           NFT_DATA_VALUE, len);
 112}
 113
 114static int nft_socket_dump(struct sk_buff *skb,
 115                           const struct nft_expr *expr)
 116{
 117        const struct nft_socket *priv = nft_expr_priv(expr);
 118
 119        if (nla_put_u32(skb, NFTA_SOCKET_KEY, htonl(priv->key)))
 120                return -1;
 121        if (nft_dump_register(skb, NFTA_SOCKET_DREG, priv->dreg))
 122                return -1;
 123        return 0;
 124}
 125
 126static struct nft_expr_type nft_socket_type;
 127static const struct nft_expr_ops nft_socket_ops = {
 128        .type           = &nft_socket_type,
 129        .size           = NFT_EXPR_SIZE(sizeof(struct nft_socket)),
 130        .eval           = nft_socket_eval,
 131        .init           = nft_socket_init,
 132        .dump           = nft_socket_dump,
 133};
 134
 135static struct nft_expr_type nft_socket_type __read_mostly = {
 136        .name           = "socket",
 137        .ops            = &nft_socket_ops,
 138        .policy         = nft_socket_policy,
 139        .maxattr        = NFTA_SOCKET_MAX,
 140        .owner          = THIS_MODULE,
 141};
 142
 143static int __init nft_socket_module_init(void)
 144{
 145        return nft_register_expr(&nft_socket_type);
 146}
 147
 148static void __exit nft_socket_module_exit(void)
 149{
 150        nft_unregister_expr(&nft_socket_type);
 151}
 152
 153module_init(nft_socket_module_init);
 154module_exit(nft_socket_module_exit);
 155
 156MODULE_LICENSE("GPL");
 157MODULE_AUTHOR("Máté Eckl");
 158MODULE_DESCRIPTION("nf_tables socket match module");
 159MODULE_ALIAS_NFT_EXPR("socket");
 160