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, skb);
  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, skb);
  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, skb);
 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, skb);
 153                break;
 154        case NFPROTO_IPV6:
 155                nft_set_pktinfo_ipv6(&pkt, skb);
 156                break;
 157        default:
 158                break;
 159        }
 160
 161        return nft_do_chain(&pkt, priv);
 162}
 163
 164static const struct nft_chain_type nft_chain_filter_inet = {
 165        .name           = "filter",
 166        .type           = NFT_CHAIN_T_DEFAULT,
 167        .family         = NFPROTO_INET,
 168        .hook_mask      = (1 << NF_INET_LOCAL_IN) |
 169                          (1 << NF_INET_LOCAL_OUT) |
 170                          (1 << NF_INET_FORWARD) |
 171                          (1 << NF_INET_PRE_ROUTING) |
 172                          (1 << NF_INET_POST_ROUTING),
 173        .hooks          = {
 174                [NF_INET_LOCAL_IN]      = nft_do_chain_inet,
 175                [NF_INET_LOCAL_OUT]     = nft_do_chain_inet,
 176                [NF_INET_FORWARD]       = nft_do_chain_inet,
 177                [NF_INET_PRE_ROUTING]   = nft_do_chain_inet,
 178                [NF_INET_POST_ROUTING]  = nft_do_chain_inet,
 179        },
 180};
 181
 182static void nft_chain_filter_inet_init(void)
 183{
 184        nft_register_chain_type(&nft_chain_filter_inet);
 185}
 186
 187static void nft_chain_filter_inet_fini(void)
 188{
 189        nft_unregister_chain_type(&nft_chain_filter_inet);
 190}
 191#else
 192static inline void nft_chain_filter_inet_init(void) {}
 193static inline void nft_chain_filter_inet_fini(void) {}
 194#endif /* CONFIG_NF_TABLES_IPV6 */
 195
 196#ifdef CONFIG_NF_TABLES_BRIDGE
 197static unsigned int
 198nft_do_chain_bridge(void *priv,
 199                    struct sk_buff *skb,
 200                    const struct nf_hook_state *state)
 201{
 202        struct nft_pktinfo pkt;
 203
 204        nft_set_pktinfo(&pkt, skb, state);
 205
 206        switch (eth_hdr(skb)->h_proto) {
 207        case htons(ETH_P_IP):
 208                nft_set_pktinfo_ipv4_validate(&pkt, skb);
 209                break;
 210        case htons(ETH_P_IPV6):
 211                nft_set_pktinfo_ipv6_validate(&pkt, skb);
 212                break;
 213        default:
 214                nft_set_pktinfo_unspec(&pkt, skb);
 215                break;
 216        }
 217
 218        return nft_do_chain(&pkt, priv);
 219}
 220
 221static const struct nft_chain_type nft_chain_filter_bridge = {
 222        .name           = "filter",
 223        .type           = NFT_CHAIN_T_DEFAULT,
 224        .family         = NFPROTO_BRIDGE,
 225        .hook_mask      = (1 << NF_BR_PRE_ROUTING) |
 226                          (1 << NF_BR_LOCAL_IN) |
 227                          (1 << NF_BR_FORWARD) |
 228                          (1 << NF_BR_LOCAL_OUT) |
 229                          (1 << NF_BR_POST_ROUTING),
 230        .hooks          = {
 231                [NF_BR_PRE_ROUTING]     = nft_do_chain_bridge,
 232                [NF_BR_LOCAL_IN]        = nft_do_chain_bridge,
 233                [NF_BR_FORWARD]         = nft_do_chain_bridge,
 234                [NF_BR_LOCAL_OUT]       = nft_do_chain_bridge,
 235                [NF_BR_POST_ROUTING]    = nft_do_chain_bridge,
 236        },
 237};
 238
 239static void nft_chain_filter_bridge_init(void)
 240{
 241        nft_register_chain_type(&nft_chain_filter_bridge);
 242}
 243
 244static void nft_chain_filter_bridge_fini(void)
 245{
 246        nft_unregister_chain_type(&nft_chain_filter_bridge);
 247}
 248#else
 249static inline void nft_chain_filter_bridge_init(void) {}
 250static inline void nft_chain_filter_bridge_fini(void) {}
 251#endif /* CONFIG_NF_TABLES_BRIDGE */
 252
 253#ifdef CONFIG_NF_TABLES_NETDEV
 254static unsigned int nft_do_chain_netdev(void *priv, struct sk_buff *skb,
 255                                        const struct nf_hook_state *state)
 256{
 257        struct nft_pktinfo pkt;
 258
 259        nft_set_pktinfo(&pkt, skb, state);
 260
 261        switch (skb->protocol) {
 262        case htons(ETH_P_IP):
 263                nft_set_pktinfo_ipv4_validate(&pkt, skb);
 264                break;
 265        case htons(ETH_P_IPV6):
 266                nft_set_pktinfo_ipv6_validate(&pkt, skb);
 267                break;
 268        default:
 269                nft_set_pktinfo_unspec(&pkt, skb);
 270                break;
 271        }
 272
 273        return nft_do_chain(&pkt, priv);
 274}
 275
 276static const struct nft_chain_type nft_chain_filter_netdev = {
 277        .name           = "filter",
 278        .type           = NFT_CHAIN_T_DEFAULT,
 279        .family         = NFPROTO_NETDEV,
 280        .hook_mask      = (1 << NF_NETDEV_INGRESS),
 281        .hooks          = {
 282                [NF_NETDEV_INGRESS]     = nft_do_chain_netdev,
 283        },
 284};
 285
 286static void nft_netdev_event(unsigned long event, struct net_device *dev,
 287                             struct nft_ctx *ctx)
 288{
 289        struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
 290        struct nft_hook *hook, *found = NULL;
 291        int n = 0;
 292
 293        if (event != NETDEV_UNREGISTER)
 294                return;
 295
 296        list_for_each_entry(hook, &basechain->hook_list, list) {
 297                if (hook->ops.dev == dev)
 298                        found = hook;
 299
 300                n++;
 301        }
 302        if (!found)
 303                return;
 304
 305        if (n > 1) {
 306                nf_unregister_net_hook(ctx->net, &found->ops);
 307                list_del_rcu(&found->list);
 308                kfree_rcu(found, rcu);
 309                return;
 310        }
 311
 312        /* UNREGISTER events are also happening on netns exit.
 313         *
 314         * Although nf_tables core releases all tables/chains, only this event
 315         * handler provides guarantee that hook->ops.dev is still accessible,
 316         * so we cannot skip exiting net namespaces.
 317         */
 318        __nft_release_basechain(ctx);
 319}
 320
 321static int nf_tables_netdev_event(struct notifier_block *this,
 322                                  unsigned long event, void *ptr)
 323{
 324        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 325        struct nft_table *table;
 326        struct nft_chain *chain, *nr;
 327        struct nft_ctx ctx = {
 328                .net    = dev_net(dev),
 329        };
 330
 331        if (event != NETDEV_UNREGISTER &&
 332            event != NETDEV_CHANGENAME)
 333                return NOTIFY_DONE;
 334
 335        mutex_lock(&ctx.net->nft_commit_mutex);
 336        list_for_each_entry(table, &ctx.net->nft.tables, list) {
 337                if (table->family != NFPROTO_NETDEV)
 338                        continue;
 339
 340                ctx.family = table->family;
 341                ctx.table = table;
 342                list_for_each_entry_safe(chain, nr, &table->chains, list) {
 343                        if (!nft_is_base_chain(chain))
 344                                continue;
 345
 346                        ctx.chain = chain;
 347                        nft_netdev_event(event, dev, &ctx);
 348                }
 349        }
 350        mutex_unlock(&ctx.net->nft_commit_mutex);
 351
 352        return NOTIFY_DONE;
 353}
 354
 355static struct notifier_block nf_tables_netdev_notifier = {
 356        .notifier_call  = nf_tables_netdev_event,
 357};
 358
 359static int nft_chain_filter_netdev_init(void)
 360{
 361        int err;
 362
 363        nft_register_chain_type(&nft_chain_filter_netdev);
 364
 365        err = register_netdevice_notifier(&nf_tables_netdev_notifier);
 366        if (err)
 367                goto err_register_netdevice_notifier;
 368
 369        return 0;
 370
 371err_register_netdevice_notifier:
 372        nft_unregister_chain_type(&nft_chain_filter_netdev);
 373
 374        return err;
 375}
 376
 377static void nft_chain_filter_netdev_fini(void)
 378{
 379        nft_unregister_chain_type(&nft_chain_filter_netdev);
 380        unregister_netdevice_notifier(&nf_tables_netdev_notifier);
 381}
 382#else
 383static inline int nft_chain_filter_netdev_init(void) { return 0; }
 384static inline void nft_chain_filter_netdev_fini(void) {}
 385#endif /* CONFIG_NF_TABLES_NETDEV */
 386
 387int __init nft_chain_filter_init(void)
 388{
 389        int err;
 390
 391        err = nft_chain_filter_netdev_init();
 392        if (err < 0)
 393                return err;
 394
 395        nft_chain_filter_ipv4_init();
 396        nft_chain_filter_ipv6_init();
 397        nft_chain_filter_arp_init();
 398        nft_chain_filter_inet_init();
 399        nft_chain_filter_bridge_init();
 400
 401        return 0;
 402}
 403
 404void nft_chain_filter_fini(void)
 405{
 406        nft_chain_filter_bridge_fini();
 407        nft_chain_filter_inet_fini();
 408        nft_chain_filter_arp_fini();
 409        nft_chain_filter_ipv6_fini();
 410        nft_chain_filter_ipv4_fini();
 411        nft_chain_filter_netdev_fini();
 412}
 413