Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: nf_tables: add netdev table to filter from ingress

This allows us to create netdev tables that contain ingress chains. Use
skb_header_pointer() as we may see shared sk_buffs at this stage.

This change provides access to the existing nf_tables features from the ingress
hook.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+190
+1
include/net/netns/nftables.h
··· 13 13 struct nft_af_info *inet; 14 14 struct nft_af_info *arp; 15 15 struct nft_af_info *bridge; 16 + struct nft_af_info *netdev; 16 17 unsigned int base_seq; 17 18 u8 gencursor; 18 19 };
+5
net/netfilter/Kconfig
··· 456 456 help 457 457 This option enables support for a mixed IPv4/IPv6 "inet" table. 458 458 459 + config NF_TABLES_NETDEV 460 + tristate "Netfilter nf_tables netdev tables support" 461 + help 462 + This option enables support for the "netdev" table. 463 + 459 464 config NFT_EXTHDR 460 465 tristate "Netfilter nf_tables IPv6 exthdr module" 461 466 help
+1
net/netfilter/Makefile
··· 75 75 76 76 obj-$(CONFIG_NF_TABLES) += nf_tables.o 77 77 obj-$(CONFIG_NF_TABLES_INET) += nf_tables_inet.o 78 + obj-$(CONFIG_NF_TABLES_NETDEV) += nf_tables_netdev.o 78 79 obj-$(CONFIG_NFT_COMPAT) += nft_compat.o 79 80 obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o 80 81 obj-$(CONFIG_NFT_META) += nft_meta.o
+183
net/netfilter/nf_tables_netdev.c
··· 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 <net/netfilter/nf_tables.h> 12 + #include <linux/ip.h> 13 + #include <linux/ipv6.h> 14 + #include <net/netfilter/nf_tables_ipv4.h> 15 + #include <net/netfilter/nf_tables_ipv6.h> 16 + 17 + static inline void 18 + nft_netdev_set_pktinfo_ipv4(struct nft_pktinfo *pkt, 19 + const struct nf_hook_ops *ops, struct sk_buff *skb, 20 + const struct nf_hook_state *state) 21 + { 22 + struct iphdr *iph, _iph; 23 + u32 len, thoff; 24 + 25 + nft_set_pktinfo(pkt, ops, skb, state); 26 + 27 + iph = skb_header_pointer(skb, skb_network_offset(skb), sizeof(*iph), 28 + &_iph); 29 + if (!iph) 30 + return; 31 + 32 + iph = ip_hdr(skb); 33 + if (iph->ihl < 5 || iph->version != 4) 34 + return; 35 + 36 + len = ntohs(iph->tot_len); 37 + thoff = iph->ihl * 4; 38 + if (skb->len < len) 39 + return; 40 + else if (len < thoff) 41 + return; 42 + 43 + pkt->tprot = iph->protocol; 44 + pkt->xt.thoff = thoff; 45 + pkt->xt.fragoff = ntohs(iph->frag_off) & IP_OFFSET; 46 + } 47 + 48 + static inline void 49 + __nft_netdev_set_pktinfo_ipv6(struct nft_pktinfo *pkt, 50 + const struct nf_hook_ops *ops, 51 + struct sk_buff *skb, 52 + const struct nf_hook_state *state) 53 + { 54 + #if IS_ENABLED(CONFIG_IPV6) 55 + struct ipv6hdr *ip6h, _ip6h; 56 + unsigned int thoff = 0; 57 + unsigned short frag_off; 58 + int protohdr; 59 + u32 pkt_len; 60 + 61 + ip6h = skb_header_pointer(skb, skb_network_offset(skb), sizeof(*ip6h), 62 + &_ip6h); 63 + if (!ip6h) 64 + return; 65 + 66 + if (ip6h->version != 6) 67 + return; 68 + 69 + pkt_len = ntohs(ip6h->payload_len); 70 + if (pkt_len + sizeof(*ip6h) > skb->len) 71 + return; 72 + 73 + protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL); 74 + if (protohdr < 0) 75 + return; 76 + 77 + pkt->tprot = protohdr; 78 + pkt->xt.thoff = thoff; 79 + pkt->xt.fragoff = frag_off; 80 + #endif 81 + } 82 + 83 + static inline void nft_netdev_set_pktinfo_ipv6(struct nft_pktinfo *pkt, 84 + const struct nf_hook_ops *ops, 85 + struct sk_buff *skb, 86 + const struct nf_hook_state *state) 87 + { 88 + nft_set_pktinfo(pkt, ops, skb, state); 89 + __nft_netdev_set_pktinfo_ipv6(pkt, ops, skb, state); 90 + } 91 + 92 + static unsigned int 93 + nft_do_chain_netdev(const struct nf_hook_ops *ops, struct sk_buff *skb, 94 + const struct nf_hook_state *state) 95 + { 96 + struct nft_pktinfo pkt; 97 + 98 + switch (eth_hdr(skb)->h_proto) { 99 + case htons(ETH_P_IP): 100 + nft_netdev_set_pktinfo_ipv4(&pkt, ops, skb, state); 101 + break; 102 + case htons(ETH_P_IPV6): 103 + nft_netdev_set_pktinfo_ipv6(&pkt, ops, skb, state); 104 + break; 105 + default: 106 + nft_set_pktinfo(&pkt, ops, skb, state); 107 + break; 108 + } 109 + 110 + return nft_do_chain(&pkt, ops); 111 + } 112 + 113 + static struct nft_af_info nft_af_netdev __read_mostly = { 114 + .family = NFPROTO_NETDEV, 115 + .nhooks = NF_NETDEV_NUMHOOKS, 116 + .owner = THIS_MODULE, 117 + .flags = NFT_AF_NEEDS_DEV, 118 + .nops = 1, 119 + .hooks = { 120 + [NF_NETDEV_INGRESS] = nft_do_chain_netdev, 121 + }, 122 + }; 123 + 124 + static int nf_tables_netdev_init_net(struct net *net) 125 + { 126 + net->nft.netdev = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL); 127 + if (net->nft.netdev == NULL) 128 + return -ENOMEM; 129 + 130 + memcpy(net->nft.netdev, &nft_af_netdev, sizeof(nft_af_netdev)); 131 + 132 + if (nft_register_afinfo(net, net->nft.netdev) < 0) 133 + goto err; 134 + 135 + return 0; 136 + err: 137 + kfree(net->nft.netdev); 138 + return -ENOMEM; 139 + } 140 + 141 + static void nf_tables_netdev_exit_net(struct net *net) 142 + { 143 + nft_unregister_afinfo(net->nft.netdev); 144 + kfree(net->nft.netdev); 145 + } 146 + 147 + static struct pernet_operations nf_tables_netdev_net_ops = { 148 + .init = nf_tables_netdev_init_net, 149 + .exit = nf_tables_netdev_exit_net, 150 + }; 151 + 152 + static const struct nf_chain_type nft_filter_chain_netdev = { 153 + .name = "filter", 154 + .type = NFT_CHAIN_T_DEFAULT, 155 + .family = NFPROTO_NETDEV, 156 + .owner = THIS_MODULE, 157 + .hook_mask = (1 << NF_NETDEV_INGRESS), 158 + }; 159 + 160 + static int __init nf_tables_netdev_init(void) 161 + { 162 + int ret; 163 + 164 + nft_register_chain_type(&nft_filter_chain_netdev); 165 + ret = register_pernet_subsys(&nf_tables_netdev_net_ops); 166 + if (ret < 0) 167 + nft_unregister_chain_type(&nft_filter_chain_netdev); 168 + 169 + return ret; 170 + } 171 + 172 + static void __exit nf_tables_netdev_exit(void) 173 + { 174 + unregister_pernet_subsys(&nf_tables_netdev_net_ops); 175 + nft_unregister_chain_type(&nft_filter_chain_netdev); 176 + } 177 + 178 + module_init(nf_tables_netdev_init); 179 + module_exit(nf_tables_netdev_exit); 180 + 181 + MODULE_LICENSE("GPL"); 182 + MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); 183 + MODULE_ALIAS_NFT_FAMILY(5); /* NFPROTO_NETDEV */