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