linux/net/netfilter/nft_chain_filter.c
<<
>>
Prefs
   1#include <linux/init.h>
   2#include <linux/kernel.h>
   3#include <linux/netdevice.h>
   4#include <net/net_namespace.h>
   5#include <net/netfilter/nf_tables.h>
   6#include <linux/netfilter_ipv4.h>
   7#include <linux/netfilter_ipv6.h>
   8#include <linux/netfilter_bridge.h>
   9#include <linux/netfilter_arp.h>
  10#include <net/netfilter/nf_tables_ipv4.h>
  11#include <net/netfilter/nf_tables_ipv6.h>
  12
  13#ifdef CONFIG_NF_TABLES_IPV4
  14static unsigned int nft_do_chain_ipv4(void *priv,
  15                                      struct sk_buff *skb,
  16                                      const struct nf_hook_state *state)
  17{
  18        struct nft_pktinfo pkt;
  19
  20        nft_set_pktinfo(&pkt, skb, state);
  21        nft_set_pktinfo_ipv4(&pkt);
  22
  23        return nft_do_chain(&pkt, priv);
  24}
  25
  26static const struct nft_chain_type nft_chain_filter_ipv4 = {
  27        .name           = "filter",
  28        .type           = NFT_CHAIN_T_DEFAULT,
  29        .family         = NFPROTO_IPV4,
  30        .hook_mask      = (1 << NF_INET_LOCAL_IN) |
  31                          (1 << NF_INET_LOCAL_OUT) |
  32                          (1 << NF_INET_FORWARD) |
  33                          (1 << NF_INET_PRE_ROUTING) |
  34                          (1 << NF_INET_POST_ROUTING),
  35        .hooks          = {
  36                [NF_INET_LOCAL_IN]      = nft_do_chain_ipv4,
  37                [NF_INET_LOCAL_OUT]     = nft_do_chain_ipv4,
  38                [NF_INET_FORWARD]       = nft_do_chain_ipv4,
  39                [NF_INET_PRE_ROUTING]   = nft_do_chain_ipv4,
  40                [NF_INET_POST_ROUTING]  = nft_do_chain_ipv4,
  41        },
  42};
  43
  44static void nft_chain_filter_ipv4_init(void)
  45{
  46        nft_register_chain_type(&nft_chain_filter_ipv4);
  47}
  48static void nft_chain_filter_ipv4_fini(void)
  49{
  50        nft_unregister_chain_type(&nft_chain_filter_ipv4);
  51}
  52
  53#else
  54static inline void nft_chain_filter_ipv4_init(void) {}
  55static inline void nft_chain_filter_ipv4_fini(void) {}
  56#endif /* CONFIG_NF_TABLES_IPV4 */
  57
  58#ifdef CONFIG_NF_TABLES_ARP
  59static unsigned int nft_do_chain_arp(void *priv, struct sk_buff *skb,
  60                                     const struct nf_hook_state *state)
  61{
  62        struct nft_pktinfo pkt;
  63
  64        nft_set_pktinfo(&pkt, skb, state);
  65        nft_set_pktinfo_unspec(&pkt);
  66
  67        return nft_do_chain(&pkt, priv);
  68}
  69
  70static const struct nft_chain_type nft_chain_filter_arp = {
  71        .name           = "filter",
  72        .type           = NFT_CHAIN_T_DEFAULT,
  73        .family         = NFPROTO_ARP,
  74        .owner          = THIS_MODULE,
  75        .hook_mask      = (1 << NF_ARP_IN) |
  76                          (1 << NF_ARP_OUT),
  77        .hooks          = {
  78                [NF_ARP_IN]             = nft_do_chain_arp,
  79                [NF_ARP_OUT]            = nft_do_chain_arp,
  80        },
  81};
  82
  83static void nft_chain_filter_arp_init(void)
  84{
  85        nft_register_chain_type(&nft_chain_filter_arp);
  86}
  87
  88static void nft_chain_filter_arp_fini(void)
  89{
  90        nft_unregister_chain_type(&nft_chain_filter_arp);
  91}
  92#else
  93static inline void nft_chain_filter_arp_init(void) {}
  94static inline void nft_chain_filter_arp_fini(void) {}
  95#endif /* CONFIG_NF_TABLES_ARP */
  96
  97#ifdef CONFIG_NF_TABLES_IPV6
  98static unsigned int nft_do_chain_ipv6(void *priv,
  99                                      struct sk_buff *skb,
 100                                      const struct nf_hook_state *state)
 101{
 102        struct nft_pktinfo pkt;
 103
 104        nft_set_pktinfo(&pkt, skb, state);
 105        nft_set_pktinfo_ipv6(&pkt);
 106
 107        return nft_do_chain(&pkt, priv);
 108}
 109
 110static const struct nft_chain_type nft_chain_filter_ipv6 = {
 111        .name           = "filter",
 112        .type           = NFT_CHAIN_T_DEFAULT,
 113        .family         = NFPROTO_IPV6,
 114        .hook_mask      = (1 << NF_INET_LOCAL_IN) |
 115                          (1 << NF_INET_LOCAL_OUT) |
 116                          (1 << NF_INET_FORWARD) |
 117                          (1 << NF_INET_PRE_ROUTING) |
 118                          (1 << NF_INET_POST_ROUTING),
 119        .hooks          = {
 120                [NF_INET_LOCAL_IN]      = nft_do_chain_ipv6,
 121                [NF_INET_LOCAL_OUT]     = nft_do_chain_ipv6,
 122                [NF_INET_FORWARD]       = nft_do_chain_ipv6,
 123                [NF_INET_PRE_ROUTING]   = nft_do_chain_ipv6,
 124                [NF_INET_POST_ROUTING]  = nft_do_chain_ipv6,
 125        },
 126};
 127
 128static void nft_chain_filter_ipv6_init(void)
 129{
 130        nft_register_chain_type(&nft_chain_filter_ipv6);
 131}
 132
 133static void nft_chain_filter_ipv6_fini(void)
 134{
 135        nft_unregister_chain_type(&nft_chain_filter_ipv6);
 136}
 137#else
 138static inline void nft_chain_filter_ipv6_init(void) {}
 139static inline void nft_chain_filter_ipv6_fini(void) {}
 140#endif /* CONFIG_NF_TABLES_IPV6 */
 141
 142#ifdef CONFIG_NF_TABLES_INET
 143static unsigned int nft_do_chain_inet(void *priv, struct sk_buff *skb,
 144                                      const struct nf_hook_state *state)
 145{
 146        struct nft_pktinfo pkt;
 147
 148        nft_set_pktinfo(&pkt, skb, state);
 149
 150        switch (state->pf) {
 151        case NFPROTO_IPV4:
 152                nft_set_pktinfo_ipv4(&pkt);
 153                break;
 154        case NFPROTO_IPV6:
 155                nft_set_pktinfo_ipv6(&pkt);
 156                break;
 157        default:
 158                break;
 159        }
 160
 161        return nft_do_chain(&pkt, priv);
 162}
 163
 164static unsigned int nft_do_chain_inet_ingress(void *priv, struct sk_buff *skb,
 165                                              const struct nf_hook_state *state)
 166{
 167        struct nf_hook_state ingress_state = *state;
 168        struct nft_pktinfo pkt;
 169
 170        switch (skb->protocol) {
 171        case htons(ETH_P_IP):
 172                /* Original hook is NFPROTO_NETDEV and NF_NETDEV_INGRESS. */
 173                ingress_state.pf = NFPROTO_IPV4;
 174                ingress_state.hook = NF_INET_INGRESS;
 175                nft_set_pktinfo(&pkt, skb, &ingress_state);
 176
 177                if (nft_set_pktinfo_ipv4_ingress(&pkt) < 0)
 178                        return NF_DROP;
 179                break;
 180        case htons(ETH_P_IPV6):
 181                ingress_state.pf = NFPROTO_IPV6;
 182                ingress_state.hook = NF_INET_INGRESS;
 183                nft_set_pktinfo(&pkt, skb, &ingress_state);
 184
 185                if (nft_set_pktinfo_ipv6_ingress(&pkt) < 0)
 186                        return NF_DROP;
 187                break;
 188        default:
 189                return NF_ACCEPT;
 190        }
 191
 192        return nft_do_chain(&pkt, priv);
 193}
 194
 195static const struct nft_chain_type nft_chain_filter_inet = {
 196        .name           = "filter",
 197        .type           = NFT_CHAIN_T_DEFAULT,
 198        .family         = NFPROTO_INET,
 199        .hook_mask      = (1 << NF_INET_INGRESS) |
 200                          (1 << NF_INET_LOCAL_IN) |
 201                          (1 << NF_INET_LOCAL_OUT) |
 202                          (1 << NF_INET_FORWARD) |
 203                          (1 << NF_INET_PRE_ROUTING) |
 204                          (1 << NF_INET_POST_ROUTING),
 205        .hooks          = {
 206                [NF_INET_INGRESS]       = nft_do_chain_inet_ingress,
 207                [NF_INET_LOCAL_IN]      = nft_do_chain_inet,
 208                [NF_INET_LOCAL_OUT]     = nft_do_chain_inet,
 209                [NF_INET_FORWARD]       = nft_do_chain_inet,
 210                [NF_INET_PRE_ROUTING]   = nft_do_chain_inet,
 211                [NF_INET_POST_ROUTING]  = nft_do_chain_inet,
 212        },
 213};
 214
 215static void nft_chain_filter_inet_init(void)
 216{
 217        nft_register_chain_type(&nft_chain_filter_inet);
 218}
 219
 220static void nft_chain_filter_inet_fini(void)
 221{
 222        nft_unregister_chain_type(&nft_chain_filter_inet);
 223}
 224#else
 225static inline void nft_chain_filter_inet_init(void) {}
 226static inline void nft_chain_filter_inet_fini(void) {}
 227#endif /* CONFIG_NF_TABLES_IPV6 */
 228
 229#if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE)
 230static unsigned int
 231nft_do_chain_bridge(void *priv,
 232                    struct sk_buff *skb,
 233                    const struct nf_hook_state *state)
 234{
 235        struct nft_pktinfo pkt;
 236
 237        nft_set_pktinfo(&pkt, skb, state);
 238
 239        switch (eth_hdr(skb)->h_proto) {
 240        case htons(ETH_P_IP):
 241                nft_set_pktinfo_ipv4_validate(&pkt);
 242                break;
 243        case htons(ETH_P_IPV6):
 244                nft_set_pktinfo_ipv6_validate(&pkt);
 245                break;
 246        default:
 247                nft_set_pktinfo_unspec(&pkt);
 248                break;
 249        }
 250
 251        return nft_do_chain(&pkt, priv);
 252}
 253
 254static const struct nft_chain_type nft_chain_filter_bridge = {
 255        .name           = "filter",
 256        .type           = NFT_CHAIN_T_DEFAULT,
 257        .family         = NFPROTO_BRIDGE,
 258        .hook_mask      = (1 << NF_BR_PRE_ROUTING) |
 259                          (1 << NF_BR_LOCAL_IN) |
 260                          (1 << NF_BR_FORWARD) |
 261                          (1 << NF_BR_LOCAL_OUT) |
 262                          (1 << NF_BR_POST_ROUTING),
 263        .hooks          = {
 264                [NF_BR_PRE_ROUTING]     = nft_do_chain_bridge,
 265                [NF_BR_LOCAL_IN]        = nft_do_chain_bridge,
 266                [NF_BR_FORWARD]         = nft_do_chain_bridge,
 267                [NF_BR_LOCAL_OUT]       = nft_do_chain_bridge,
 268                [NF_BR_POST_ROUTING]    = nft_do_chain_bridge,
 269        },
 270};
 271
 272static void nft_chain_filter_bridge_init(void)
 273{
 274        nft_register_chain_type(&nft_chain_filter_bridge);
 275}
 276
 277static void nft_chain_filter_bridge_fini(void)
 278{
 279        nft_unregister_chain_type(&nft_chain_filter_bridge);
 280}
 281#else
 282static inline void nft_chain_filter_bridge_init(void) {}
 283static inline void nft_chain_filter_bridge_fini(void) {}
 284#endif /* CONFIG_NF_TABLES_BRIDGE */
 285
 286#ifdef CONFIG_NF_TABLES_NETDEV
 287static unsigned int nft_do_chain_netdev(void *priv, struct sk_buff *skb,
 288                                        const struct nf_hook_state *state)
 289{
 290        struct nft_pktinfo pkt;
 291
 292        nft_set_pktinfo(&pkt, skb, state);
 293
 294        switch (skb->protocol) {
 295        case htons(ETH_P_IP):
 296                nft_set_pktinfo_ipv4_validate(&pkt);
 297                break;
 298        case htons(ETH_P_IPV6):
 299                nft_set_pktinfo_ipv6_validate(&pkt);
 300                break;
 301        default:
 302                nft_set_pktinfo_unspec(&pkt);
 303                break;
 304        }
 305
 306        return nft_do_chain(&pkt, priv);
 307}
 308
 309static const struct nft_chain_type nft_chain_filter_netdev = {
 310        .name           = "filter",
 311        .type           = NFT_CHAIN_T_DEFAULT,
 312        .family         = NFPROTO_NETDEV,
 313        .hook_mask      = (1 << NF_NETDEV_INGRESS),
 314        .hooks          = {
 315                [NF_NETDEV_INGRESS]     = nft_do_chain_netdev,
 316        },
 317};
 318
 319static void nft_netdev_event(unsigned long event, struct net_device *dev,
 320                             struct nft_ctx *ctx)
 321{
 322        struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
 323        struct nft_hook *hook, *found = NULL;
 324        int n = 0;
 325
 326        if (event != NETDEV_UNREGISTER)
 327                return;
 328
 329        list_for_each_entry(hook, &basechain->hook_list, list) {
 330                if (hook->ops.dev == dev)
 331                        found = hook;
 332
 333                n++;
 334        }
 335        if (!found)
 336                return;
 337
 338        if (n > 1) {
 339                nf_unregister_net_hook(ctx->net, &found->ops);
 340                list_del_rcu(&found->list);
 341                kfree_rcu(found, rcu);
 342                return;
 343        }
 344
 345        /* UNREGISTER events are also happening on netns exit.
 346         *
 347         * Although nf_tables core releases all tables/chains, only this event
 348         * handler provides guarantee that hook->ops.dev is still accessible,
 349         * so we cannot skip exiting net namespaces.
 350         */
 351        __nft_release_basechain(ctx);
 352}
 353
 354static int nf_tables_netdev_event(struct notifier_block *this,
 355                                  unsigned long event, void *ptr)
 356{
 357        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 358        struct nftables_pernet *nft_net;
 359        struct nft_table *table;
 360        struct nft_chain *chain, *nr;
 361        struct nft_ctx ctx = {
 362                .net    = dev_net(dev),
 363        };
 364
 365        if (event != NETDEV_UNREGISTER &&
 366            event != NETDEV_CHANGENAME)
 367                return NOTIFY_DONE;
 368
 369        nft_net = nft_pernet(ctx.net);
 370        mutex_lock(&nft_net->commit_mutex);
 371        list_for_each_entry(table, &nft_net->tables, list) {
 372                if (table->family != NFPROTO_NETDEV)
 373                        continue;
 374
 375                ctx.family = table->family;
 376                ctx.table = table;
 377                list_for_each_entry_safe(chain, nr, &table->chains, list) {
 378                        if (!nft_is_base_chain(chain))
 379                                continue;
 380
 381                        ctx.chain = chain;
 382                        nft_netdev_event(event, dev, &ctx);
 383                }
 384        }
 385        mutex_unlock(&nft_net->commit_mutex);
 386
 387        return NOTIFY_DONE;
 388}
 389
 390static struct notifier_block nf_tables_netdev_notifier = {
 391        .notifier_call  = nf_tables_netdev_event,
 392};
 393
 394static int nft_chain_filter_netdev_init(void)
 395{
 396        int err;
 397
 398        nft_register_chain_type(&nft_chain_filter_netdev);
 399
 400        err = register_netdevice_notifier(&nf_tables_netdev_notifier);
 401        if (err)
 402                goto err_register_netdevice_notifier;
 403
 404        return 0;
 405
 406err_register_netdevice_notifier:
 407        nft_unregister_chain_type(&nft_chain_filter_netdev);
 408
 409        return err;
 410}
 411
 412static void nft_chain_filter_netdev_fini(void)
 413{
 414        nft_unregister_chain_type(&nft_chain_filter_netdev);
 415        unregister_netdevice_notifier(&nf_tables_netdev_notifier);
 416}
 417#else
 418static inline int nft_chain_filter_netdev_init(void) { return 0; }
 419static inline void nft_chain_filter_netdev_fini(void) {}
 420#endif /* CONFIG_NF_TABLES_NETDEV */
 421
 422int __init nft_chain_filter_init(void)
 423{
 424        int err;
 425
 426        err = nft_chain_filter_netdev_init();
 427        if (err < 0)
 428                return err;
 429
 430        nft_chain_filter_ipv4_init();
 431        nft_chain_filter_ipv6_init();
 432        nft_chain_filter_arp_init();
 433        nft_chain_filter_inet_init();
 434        nft_chain_filter_bridge_init();
 435
 436        return 0;
 437}
 438
 439void nft_chain_filter_fini(void)
 440{
 441        nft_chain_filter_bridge_fini();
 442        nft_chain_filter_inet_fini();
 443        nft_chain_filter_arp_fini();
 444        nft_chain_filter_ipv6_fini();
 445        nft_chain_filter_ipv4_fini();
 446        nft_chain_filter_netdev_fini();
 447}
 448