cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nft_chain_filter.c (10843B)


      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			  (1 << NF_NETDEV_EGRESS),
    315	.hooks		= {
    316		[NF_NETDEV_INGRESS]	= nft_do_chain_netdev,
    317		[NF_NETDEV_EGRESS]	= nft_do_chain_netdev,
    318	},
    319};
    320
    321static void nft_netdev_event(unsigned long event, struct net_device *dev,
    322			     struct nft_ctx *ctx)
    323{
    324	struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
    325	struct nft_hook *hook, *found = NULL;
    326	int n = 0;
    327
    328	if (event != NETDEV_UNREGISTER)
    329		return;
    330
    331	list_for_each_entry(hook, &basechain->hook_list, list) {
    332		if (hook->ops.dev == dev)
    333			found = hook;
    334
    335		n++;
    336	}
    337	if (!found)
    338		return;
    339
    340	if (n > 1) {
    341		nf_unregister_net_hook(ctx->net, &found->ops);
    342		list_del_rcu(&found->list);
    343		kfree_rcu(found, rcu);
    344		return;
    345	}
    346
    347	__nft_release_basechain(ctx);
    348}
    349
    350static int nf_tables_netdev_event(struct notifier_block *this,
    351				  unsigned long event, void *ptr)
    352{
    353	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
    354	struct nftables_pernet *nft_net;
    355	struct nft_table *table;
    356	struct nft_chain *chain, *nr;
    357	struct nft_ctx ctx = {
    358		.net	= dev_net(dev),
    359	};
    360
    361	if (event != NETDEV_UNREGISTER &&
    362	    event != NETDEV_CHANGENAME)
    363		return NOTIFY_DONE;
    364
    365	if (!check_net(ctx.net))
    366		return NOTIFY_DONE;
    367
    368	nft_net = nft_pernet(ctx.net);
    369	mutex_lock(&nft_net->commit_mutex);
    370	list_for_each_entry(table, &nft_net->tables, list) {
    371		if (table->family != NFPROTO_NETDEV)
    372			continue;
    373
    374		ctx.family = table->family;
    375		ctx.table = table;
    376		list_for_each_entry_safe(chain, nr, &table->chains, list) {
    377			if (!nft_is_base_chain(chain))
    378				continue;
    379
    380			ctx.chain = chain;
    381			nft_netdev_event(event, dev, &ctx);
    382		}
    383	}
    384	mutex_unlock(&nft_net->commit_mutex);
    385
    386	return NOTIFY_DONE;
    387}
    388
    389static struct notifier_block nf_tables_netdev_notifier = {
    390	.notifier_call	= nf_tables_netdev_event,
    391};
    392
    393static int nft_chain_filter_netdev_init(void)
    394{
    395	int err;
    396
    397	nft_register_chain_type(&nft_chain_filter_netdev);
    398
    399	err = register_netdevice_notifier(&nf_tables_netdev_notifier);
    400	if (err)
    401		goto err_register_netdevice_notifier;
    402
    403	return 0;
    404
    405err_register_netdevice_notifier:
    406	nft_unregister_chain_type(&nft_chain_filter_netdev);
    407
    408	return err;
    409}
    410
    411static void nft_chain_filter_netdev_fini(void)
    412{
    413	nft_unregister_chain_type(&nft_chain_filter_netdev);
    414	unregister_netdevice_notifier(&nf_tables_netdev_notifier);
    415}
    416#else
    417static inline int nft_chain_filter_netdev_init(void) { return 0; }
    418static inline void nft_chain_filter_netdev_fini(void) {}
    419#endif /* CONFIG_NF_TABLES_NETDEV */
    420
    421int __init nft_chain_filter_init(void)
    422{
    423	int err;
    424
    425	err = nft_chain_filter_netdev_init();
    426	if (err < 0)
    427		return err;
    428
    429	nft_chain_filter_ipv4_init();
    430	nft_chain_filter_ipv6_init();
    431	nft_chain_filter_arp_init();
    432	nft_chain_filter_inet_init();
    433	nft_chain_filter_bridge_init();
    434
    435	return 0;
    436}
    437
    438void nft_chain_filter_fini(void)
    439{
    440	nft_chain_filter_bridge_fini();
    441	nft_chain_filter_inet_fini();
    442	nft_chain_filter_arp_fini();
    443	nft_chain_filter_ipv6_fini();
    444	nft_chain_filter_ipv4_fini();
    445	nft_chain_filter_netdev_fini();
    446}