linux/net/netfilter/nf_tables_netdev.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8
   9#include <linux/init.h>
  10#include <linux/module.h>
  11#include <linux/netdevice.h>
  12#include <net/netfilter/nf_tables.h>
  13#include <linux/ip.h>
  14#include <linux/ipv6.h>
  15#include <net/netfilter/nf_tables_ipv4.h>
  16#include <net/netfilter/nf_tables_ipv6.h>
  17
  18static unsigned int
  19nft_do_chain_netdev(void *priv, struct sk_buff *skb,
  20                    const struct nf_hook_state *state)
  21{
  22        struct nft_pktinfo pkt;
  23
  24        switch (skb->protocol) {
  25        case htons(ETH_P_IP):
  26                nft_set_pktinfo_ipv4_validate(&pkt, skb, state);
  27                break;
  28        case htons(ETH_P_IPV6):
  29                nft_set_pktinfo_ipv6_validate(&pkt, skb, state);
  30                break;
  31        default:
  32                nft_set_pktinfo_unspec(&pkt, skb, state);
  33                break;
  34        }
  35
  36        return nft_do_chain(&pkt, priv);
  37}
  38
  39static struct nft_af_info nft_af_netdev __read_mostly = {
  40        .family         = NFPROTO_NETDEV,
  41        .nhooks         = NF_NETDEV_NUMHOOKS,
  42        .owner          = THIS_MODULE,
  43        .flags          = NFT_AF_NEEDS_DEV,
  44        .nops           = 1,
  45        .hooks          = {
  46                [NF_NETDEV_INGRESS]     = nft_do_chain_netdev,
  47        },
  48};
  49
  50static int nf_tables_netdev_init_net(struct net *net)
  51{
  52        net->nft.netdev = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL);
  53        if (net->nft.netdev == NULL)
  54                return -ENOMEM;
  55
  56        memcpy(net->nft.netdev, &nft_af_netdev, sizeof(nft_af_netdev));
  57
  58        if (nft_register_afinfo(net, net->nft.netdev) < 0)
  59                goto err;
  60
  61        return 0;
  62err:
  63        kfree(net->nft.netdev);
  64        return -ENOMEM;
  65}
  66
  67static void nf_tables_netdev_exit_net(struct net *net)
  68{
  69        nft_unregister_afinfo(net, net->nft.netdev);
  70        kfree(net->nft.netdev);
  71}
  72
  73static struct pernet_operations nf_tables_netdev_net_ops = {
  74        .init   = nf_tables_netdev_init_net,
  75        .exit   = nf_tables_netdev_exit_net,
  76};
  77
  78static const struct nf_chain_type nft_filter_chain_netdev = {
  79        .name           = "filter",
  80        .type           = NFT_CHAIN_T_DEFAULT,
  81        .family         = NFPROTO_NETDEV,
  82        .owner          = THIS_MODULE,
  83        .hook_mask      = (1 << NF_NETDEV_INGRESS),
  84};
  85
  86static void nft_netdev_event(unsigned long event, struct net_device *dev,
  87                             struct nft_ctx *ctx)
  88{
  89        struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
  90
  91        switch (event) {
  92        case NETDEV_UNREGISTER:
  93                if (strcmp(basechain->dev_name, dev->name) != 0)
  94                        return;
  95
  96                __nft_release_basechain(ctx);
  97                break;
  98        case NETDEV_CHANGENAME:
  99                if (dev->ifindex != basechain->ops[0].dev->ifindex)
 100                        return;
 101
 102                strncpy(basechain->dev_name, dev->name, IFNAMSIZ);
 103                break;
 104        }
 105}
 106
 107static int nf_tables_netdev_event(struct notifier_block *this,
 108                                  unsigned long event, void *ptr)
 109{
 110        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 111        struct nft_af_info *afi;
 112        struct nft_table *table;
 113        struct nft_chain *chain, *nr;
 114        struct nft_ctx ctx = {
 115                .net    = dev_net(dev),
 116        };
 117
 118        if (event != NETDEV_UNREGISTER &&
 119            event != NETDEV_CHANGENAME)
 120                return NOTIFY_DONE;
 121
 122        nfnl_lock(NFNL_SUBSYS_NFTABLES);
 123        list_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) {
 124                ctx.afi = afi;
 125                if (afi->family != NFPROTO_NETDEV)
 126                        continue;
 127
 128                list_for_each_entry(table, &afi->tables, list) {
 129                        ctx.table = table;
 130                        list_for_each_entry_safe(chain, nr, &table->chains, list) {
 131                                if (!nft_is_base_chain(chain))
 132                                        continue;
 133
 134                                ctx.chain = chain;
 135                                nft_netdev_event(event, dev, &ctx);
 136                        }
 137                }
 138        }
 139        nfnl_unlock(NFNL_SUBSYS_NFTABLES);
 140
 141        return NOTIFY_DONE;
 142}
 143
 144static struct notifier_block nf_tables_netdev_notifier = {
 145        .notifier_call  = nf_tables_netdev_event,
 146};
 147
 148static int __init nf_tables_netdev_init(void)
 149{
 150        int ret;
 151
 152        ret = nft_register_chain_type(&nft_filter_chain_netdev);
 153        if (ret)
 154                return ret;
 155
 156        ret = register_pernet_subsys(&nf_tables_netdev_net_ops);
 157        if (ret)
 158                goto err1;
 159
 160        ret = register_netdevice_notifier(&nf_tables_netdev_notifier);
 161        if (ret)
 162                goto err2;
 163
 164        return 0;
 165
 166err2:
 167        unregister_pernet_subsys(&nf_tables_netdev_net_ops);
 168err1:
 169        nft_unregister_chain_type(&nft_filter_chain_netdev);
 170        return ret;
 171}
 172
 173static void __exit nf_tables_netdev_exit(void)
 174{
 175        unregister_netdevice_notifier(&nf_tables_netdev_notifier);
 176        unregister_pernet_subsys(&nf_tables_netdev_net_ops);
 177        nft_unregister_chain_type(&nft_filter_chain_netdev);
 178}
 179
 180module_init(nf_tables_netdev_init);
 181module_exit(nf_tables_netdev_exit);
 182
 183MODULE_LICENSE("GPL");
 184MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
 185MODULE_ALIAS_NFT_FAMILY(5); /* NFPROTO_NETDEV */
 186