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

nfnetlink_hook.c (9425B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (c) 2021 Red Hat GmbH
      4 *
      5 * Author: Florian Westphal <fw@strlen.de>
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/kallsyms.h>
     10#include <linux/kernel.h>
     11#include <linux/types.h>
     12#include <linux/skbuff.h>
     13#include <linux/errno.h>
     14#include <linux/netlink.h>
     15#include <linux/slab.h>
     16
     17#include <linux/netfilter.h>
     18
     19#include <linux/netfilter/nfnetlink.h>
     20#include <linux/netfilter/nfnetlink_hook.h>
     21
     22#include <net/netfilter/nf_tables.h>
     23#include <net/sock.h>
     24
     25static const struct nla_policy nfnl_hook_nla_policy[NFNLA_HOOK_MAX + 1] = {
     26	[NFNLA_HOOK_HOOKNUM]	= { .type = NLA_U32 },
     27	[NFNLA_HOOK_PRIORITY]	= { .type = NLA_U32 },
     28	[NFNLA_HOOK_DEV]	= { .type = NLA_STRING,
     29				    .len = IFNAMSIZ - 1 },
     30	[NFNLA_HOOK_FUNCTION_NAME] = { .type = NLA_NUL_STRING,
     31				       .len = KSYM_NAME_LEN, },
     32	[NFNLA_HOOK_MODULE_NAME] = { .type = NLA_NUL_STRING,
     33				     .len = MODULE_NAME_LEN, },
     34	[NFNLA_HOOK_CHAIN_INFO] = { .type = NLA_NESTED, },
     35};
     36
     37static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
     38				     const struct nlmsghdr *nlh,
     39				     struct netlink_dump_control *c)
     40{
     41	int err;
     42
     43	if (!try_module_get(THIS_MODULE))
     44		return -EINVAL;
     45
     46	rcu_read_unlock();
     47	err = netlink_dump_start(nlsk, skb, nlh, c);
     48	rcu_read_lock();
     49	module_put(THIS_MODULE);
     50
     51	return err;
     52}
     53
     54struct nfnl_dump_hook_data {
     55	char devname[IFNAMSIZ];
     56	unsigned long headv;
     57	u8 hook;
     58};
     59
     60static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
     61					const struct nfnl_dump_hook_data *ctx,
     62					unsigned int seq,
     63					const struct nf_hook_ops *ops)
     64{
     65	struct net *net = sock_net(nlskb->sk);
     66	struct nlattr *nest, *nest2;
     67	struct nft_chain *chain;
     68	int ret = 0;
     69
     70	if (ops->hook_ops_type != NF_HOOK_OP_NF_TABLES)
     71		return 0;
     72
     73	chain = ops->priv;
     74	if (WARN_ON_ONCE(!chain))
     75		return 0;
     76
     77	if (!nft_is_active(net, chain))
     78		return 0;
     79
     80	nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
     81	if (!nest)
     82		return -EMSGSIZE;
     83
     84	ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE,
     85			   htonl(NFNL_HOOK_TYPE_NFTABLES));
     86	if (ret)
     87		goto cancel_nest;
     88
     89	nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
     90	if (!nest2)
     91		goto cancel_nest;
     92
     93	ret = nla_put_string(nlskb, NFNLA_CHAIN_TABLE, chain->table->name);
     94	if (ret)
     95		goto cancel_nest;
     96
     97	ret = nla_put_string(nlskb, NFNLA_CHAIN_NAME, chain->name);
     98	if (ret)
     99		goto cancel_nest;
    100
    101	ret = nla_put_u8(nlskb, NFNLA_CHAIN_FAMILY, chain->table->family);
    102	if (ret)
    103		goto cancel_nest;
    104
    105	nla_nest_end(nlskb, nest2);
    106	nla_nest_end(nlskb, nest);
    107	return ret;
    108
    109cancel_nest:
    110	nla_nest_cancel(nlskb, nest);
    111	return -EMSGSIZE;
    112}
    113
    114static int nfnl_hook_dump_one(struct sk_buff *nlskb,
    115			      const struct nfnl_dump_hook_data *ctx,
    116			      const struct nf_hook_ops *ops,
    117			      int family, unsigned int seq)
    118{
    119	u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
    120	unsigned int portid = NETLINK_CB(nlskb).portid;
    121	struct nlmsghdr *nlh;
    122	int ret = -EMSGSIZE;
    123	u32 hooknum;
    124#ifdef CONFIG_KALLSYMS
    125	char sym[KSYM_SYMBOL_LEN];
    126	char *module_name;
    127#endif
    128	nlh = nfnl_msg_put(nlskb, portid, seq, event,
    129			   NLM_F_MULTI, family, NFNETLINK_V0, 0);
    130	if (!nlh)
    131		goto nla_put_failure;
    132
    133#ifdef CONFIG_KALLSYMS
    134	ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
    135	if (ret >= sizeof(sym)) {
    136		ret = -EINVAL;
    137		goto nla_put_failure;
    138	}
    139
    140	module_name = strstr(sym, " [");
    141	if (module_name) {
    142		char *end;
    143
    144		*module_name = '\0';
    145		module_name += 2;
    146		end = strchr(module_name, ']');
    147		if (end) {
    148			*end = 0;
    149
    150			ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name);
    151			if (ret)
    152				goto nla_put_failure;
    153		}
    154	}
    155
    156	ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym);
    157	if (ret)
    158		goto nla_put_failure;
    159#endif
    160
    161	if (ops->pf == NFPROTO_INET && ops->hooknum == NF_INET_INGRESS)
    162		hooknum = NF_NETDEV_INGRESS;
    163	else
    164		hooknum = ops->hooknum;
    165
    166	ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(hooknum));
    167	if (ret)
    168		goto nla_put_failure;
    169
    170	ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
    171	if (ret)
    172		goto nla_put_failure;
    173
    174	ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops);
    175	if (ret)
    176		goto nla_put_failure;
    177
    178	nlmsg_end(nlskb, nlh);
    179	return 0;
    180nla_put_failure:
    181	nlmsg_trim(nlskb, nlh);
    182	return ret;
    183}
    184
    185static const struct nf_hook_entries *
    186nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
    187{
    188	const struct nf_hook_entries *hook_head = NULL;
    189#if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
    190	struct net_device *netdev;
    191#endif
    192
    193	switch (pf) {
    194	case NFPROTO_IPV4:
    195		if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
    196			return ERR_PTR(-EINVAL);
    197		hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
    198		break;
    199	case NFPROTO_IPV6:
    200		if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
    201			return ERR_PTR(-EINVAL);
    202		hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
    203		break;
    204	case NFPROTO_ARP:
    205#ifdef CONFIG_NETFILTER_FAMILY_ARP
    206		if (hook >= ARRAY_SIZE(net->nf.hooks_arp))
    207			return ERR_PTR(-EINVAL);
    208		hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
    209#endif
    210		break;
    211	case NFPROTO_BRIDGE:
    212#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
    213		if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
    214			return ERR_PTR(-EINVAL);
    215		hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
    216#endif
    217		break;
    218#if IS_ENABLED(CONFIG_DECNET)
    219	case NFPROTO_DECNET:
    220		if (hook >= ARRAY_SIZE(net->nf.hooks_decnet))
    221			return ERR_PTR(-EINVAL);
    222		hook_head = rcu_dereference(net->nf.hooks_decnet[hook]);
    223		break;
    224#endif
    225#if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
    226	case NFPROTO_NETDEV:
    227		if (hook >= NF_NETDEV_NUMHOOKS)
    228			return ERR_PTR(-EOPNOTSUPP);
    229
    230		if (!dev)
    231			return ERR_PTR(-ENODEV);
    232
    233		netdev = dev_get_by_name_rcu(net, dev);
    234		if (!netdev)
    235			return ERR_PTR(-ENODEV);
    236
    237#ifdef CONFIG_NETFILTER_INGRESS
    238		if (hook == NF_NETDEV_INGRESS)
    239			return rcu_dereference(netdev->nf_hooks_ingress);
    240#endif
    241#ifdef CONFIG_NETFILTER_EGRESS
    242		if (hook == NF_NETDEV_EGRESS)
    243			return rcu_dereference(netdev->nf_hooks_egress);
    244#endif
    245		fallthrough;
    246#endif
    247	default:
    248		return ERR_PTR(-EPROTONOSUPPORT);
    249	}
    250
    251	return hook_head;
    252}
    253
    254static int nfnl_hook_dump(struct sk_buff *nlskb,
    255			  struct netlink_callback *cb)
    256{
    257	struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
    258	struct nfnl_dump_hook_data *ctx = cb->data;
    259	int err, family = nfmsg->nfgen_family;
    260	struct net *net = sock_net(nlskb->sk);
    261	struct nf_hook_ops * const *ops;
    262	const struct nf_hook_entries *e;
    263	unsigned int i = cb->args[0];
    264
    265	rcu_read_lock();
    266
    267	e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
    268	if (!e)
    269		goto done;
    270
    271	if (IS_ERR(e)) {
    272		cb->seq++;
    273		goto done;
    274	}
    275
    276	if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
    277		cb->seq++;
    278
    279	ops = nf_hook_entries_get_hook_ops(e);
    280
    281	for (; i < e->num_hook_entries; i++) {
    282		err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
    283					 cb->nlh->nlmsg_seq);
    284		if (err)
    285			break;
    286	}
    287
    288done:
    289	nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
    290	rcu_read_unlock();
    291	cb->args[0] = i;
    292	return nlskb->len;
    293}
    294
    295static int nfnl_hook_dump_start(struct netlink_callback *cb)
    296{
    297	const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
    298	const struct nlattr * const *nla = cb->data;
    299	struct nfnl_dump_hook_data *ctx = NULL;
    300	struct net *net = sock_net(cb->skb->sk);
    301	u8 family = nfmsg->nfgen_family;
    302	char name[IFNAMSIZ] = "";
    303	const void *head;
    304	u32 hooknum;
    305
    306	hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
    307	if (hooknum > 255)
    308		return -EINVAL;
    309
    310	if (family == NFPROTO_NETDEV) {
    311		if (!nla[NFNLA_HOOK_DEV])
    312			return -EINVAL;
    313
    314		nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
    315	}
    316
    317	rcu_read_lock();
    318	/* Not dereferenced; for consistency check only */
    319	head = nfnl_hook_entries_head(family, hooknum, net, name);
    320	rcu_read_unlock();
    321
    322	if (head && IS_ERR(head))
    323		return PTR_ERR(head);
    324
    325	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
    326	if (!ctx)
    327		return -ENOMEM;
    328
    329	strscpy(ctx->devname, name, sizeof(ctx->devname));
    330	ctx->headv = (unsigned long)head;
    331	ctx->hook = hooknum;
    332
    333	cb->seq = 1;
    334	cb->data = ctx;
    335
    336	return 0;
    337}
    338
    339static int nfnl_hook_dump_stop(struct netlink_callback *cb)
    340{
    341	kfree(cb->data);
    342	return 0;
    343}
    344
    345static int nfnl_hook_get(struct sk_buff *skb,
    346			 const struct nfnl_info *info,
    347			 const struct nlattr * const nla[])
    348{
    349	if (!nla[NFNLA_HOOK_HOOKNUM])
    350		return -EINVAL;
    351
    352	if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
    353		struct netlink_dump_control c = {
    354			.start = nfnl_hook_dump_start,
    355			.done = nfnl_hook_dump_stop,
    356			.dump = nfnl_hook_dump,
    357			.module = THIS_MODULE,
    358			.data = (void *)nla,
    359		};
    360
    361		return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
    362	}
    363
    364	return -EOPNOTSUPP;
    365}
    366
    367static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
    368	[NFNL_MSG_HOOK_GET] = {
    369		.call		= nfnl_hook_get,
    370		.type		= NFNL_CB_RCU,
    371		.attr_count	= NFNLA_HOOK_MAX,
    372		.policy		= nfnl_hook_nla_policy
    373	},
    374};
    375
    376static const struct nfnetlink_subsystem nfhook_subsys = {
    377	.name				= "nfhook",
    378	.subsys_id			= NFNL_SUBSYS_HOOK,
    379	.cb_count			= NFNL_MSG_HOOK_MAX,
    380	.cb				= nfnl_hook_cb,
    381};
    382
    383MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
    384
    385static int __init nfnetlink_hook_init(void)
    386{
    387	return nfnetlink_subsys_register(&nfhook_subsys);
    388}
    389
    390static void __exit nfnetlink_hook_exit(void)
    391{
    392	nfnetlink_subsys_unregister(&nfhook_subsys);
    393}
    394
    395module_init(nfnetlink_hook_init);
    396module_exit(nfnetlink_hook_exit);
    397
    398MODULE_LICENSE("GPL");
    399MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
    400MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");