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
 291        switch (event) {
 292        case NETDEV_UNREGISTER:
 293                if (strcmp(basechain->dev_name, dev->name) != 0)
 294                        return;
 295
 296                __nft_release_basechain(ctx);
 297                break;
 298        case NETDEV_CHANGENAME:
 299                if (dev->ifindex != basechain->ops.dev->ifindex)
 300                        return;
 301
 302                strncpy(basechain->dev_name, dev->name, IFNAMSIZ);
 303                break;
 304        }
 305}
 306
 307static int nf_tables_netdev_event(struct notifier_block *this,
 308                                  unsigned long event, void *ptr)
 309{
 310        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 311        struct nft_table *table;
 312        struct nft_chain *chain, *nr;
 313        struct nft_ctx ctx = {
 314                .net    = dev_net(dev),
 315        };
 316
 317        if (event != NETDEV_UNREGISTER &&
 318            event != NETDEV_CHANGENAME)
 319                return NOTIFY_DONE;
 320
 321        nfnl_lock(NFNL_SUBSYS_NFTABLES);
 322        list_for_each_entry(table, &ctx.net->nft.tables, list) {
 323                if (table->family != NFPROTO_NETDEV)
 324                        continue;
 325
 326                ctx.family = table->family;
 327                ctx.table = table;
 328                list_for_each_entry_safe(chain, nr, &table->chains, list) {
 329                        if (!nft_is_base_chain(chain))
 330                                continue;
 331
 332                        ctx.chain = chain;
 333                        nft_netdev_event(event, dev, &ctx);
 334                }
 335        }
 336        nfnl_unlock(NFNL_SUBSYS_NFTABLES);
 337
 338        return NOTIFY_DONE;
 339}
 340
 341static struct notifier_block nf_tables_netdev_notifier = {
 342        .notifier_call  = nf_tables_netdev_event,
 343};
 344
 345static int nft_chain_filter_netdev_init(void)
 346{
 347        int err;
 348
 349        nft_register_chain_type(&nft_chain_filter_netdev);
 350
 351        err = register_netdevice_notifier(&nf_tables_netdev_notifier);
 352        if (err)
 353                goto err_register_netdevice_notifier;
 354
 355        return 0;
 356
 357err_register_netdevice_notifier:
 358        nft_unregister_chain_type(&nft_chain_filter_netdev);
 359
 360        return err;
 361}
 362
 363static void nft_chain_filter_netdev_fini(void)
 364{
 365        nft_unregister_chain_type(&nft_chain_filter_netdev);
 366        unregister_netdevice_notifier(&nf_tables_netdev_notifier);
 367}
 368#else
 369static inline int nft_chain_filter_netdev_init(void) { return 0; }
 370static inline void nft_chain_filter_netdev_fini(void) {}
 371#endif /* CONFIG_NF_TABLES_NETDEV */
 372
 373int __init nft_chain_filter_init(void)
 374{
 375        int err;
 376
 377        err = nft_chain_filter_netdev_init();
 378        if (err < 0)
 379                return err;
 380
 381        nft_chain_filter_ipv4_init();
 382        nft_chain_filter_ipv6_init();
 383        nft_chain_filter_arp_init();
 384        nft_chain_filter_inet_init();
 385        nft_chain_filter_bridge_init();
 386
 387        return 0;
 388}
 389
 390void __exit nft_chain_filter_fini(void)
 391{
 392        nft_chain_filter_bridge_fini();
 393        nft_chain_filter_inet_fini();
 394        nft_chain_filter_arp_fini();
 395        nft_chain_filter_ipv6_fini();
 396        nft_chain_filter_ipv4_fini();
 397        nft_chain_filter_netdev_fini();
 398}
 399