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

nf_tables_trace.c (7564B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * (C) 2015 Red Hat GmbH
      4 * Author: Florian Westphal <fw@strlen.de>
      5 */
      6
      7#include <linux/module.h>
      8#include <linux/static_key.h>
      9#include <linux/hash.h>
     10#include <linux/siphash.h>
     11#include <linux/if_vlan.h>
     12#include <linux/init.h>
     13#include <linux/skbuff.h>
     14#include <linux/netlink.h>
     15#include <linux/netfilter.h>
     16#include <linux/netfilter/nfnetlink.h>
     17#include <linux/netfilter/nf_tables.h>
     18#include <net/netfilter/nf_tables_core.h>
     19#include <net/netfilter/nf_tables.h>
     20
     21#define NFT_TRACETYPE_LL_HSIZE		20
     22#define NFT_TRACETYPE_NETWORK_HSIZE	40
     23#define NFT_TRACETYPE_TRANSPORT_HSIZE	20
     24
     25DEFINE_STATIC_KEY_FALSE(nft_trace_enabled);
     26EXPORT_SYMBOL_GPL(nft_trace_enabled);
     27
     28static int trace_fill_header(struct sk_buff *nlskb, u16 type,
     29			     const struct sk_buff *skb,
     30			     int off, unsigned int len)
     31{
     32	struct nlattr *nla;
     33
     34	if (len == 0)
     35		return 0;
     36
     37	nla = nla_reserve(nlskb, type, len);
     38	if (!nla || skb_copy_bits(skb, off, nla_data(nla), len))
     39		return -1;
     40
     41	return 0;
     42}
     43
     44static int nf_trace_fill_ll_header(struct sk_buff *nlskb,
     45				   const struct sk_buff *skb)
     46{
     47	struct vlan_ethhdr veth;
     48	int off;
     49
     50	BUILD_BUG_ON(sizeof(veth) > NFT_TRACETYPE_LL_HSIZE);
     51
     52	off = skb_mac_header(skb) - skb->data;
     53	if (off != -ETH_HLEN)
     54		return -1;
     55
     56	if (skb_copy_bits(skb, off, &veth, ETH_HLEN))
     57		return -1;
     58
     59	veth.h_vlan_proto = skb->vlan_proto;
     60	veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb));
     61	veth.h_vlan_encapsulated_proto = skb->protocol;
     62
     63	return nla_put(nlskb, NFTA_TRACE_LL_HEADER, sizeof(veth), &veth);
     64}
     65
     66static int nf_trace_fill_dev_info(struct sk_buff *nlskb,
     67				  const struct net_device *indev,
     68				  const struct net_device *outdev)
     69{
     70	if (indev) {
     71		if (nla_put_be32(nlskb, NFTA_TRACE_IIF,
     72				 htonl(indev->ifindex)))
     73			return -1;
     74
     75		if (nla_put_be16(nlskb, NFTA_TRACE_IIFTYPE,
     76				 htons(indev->type)))
     77			return -1;
     78	}
     79
     80	if (outdev) {
     81		if (nla_put_be32(nlskb, NFTA_TRACE_OIF,
     82				 htonl(outdev->ifindex)))
     83			return -1;
     84
     85		if (nla_put_be16(nlskb, NFTA_TRACE_OIFTYPE,
     86				 htons(outdev->type)))
     87			return -1;
     88	}
     89
     90	return 0;
     91}
     92
     93static int nf_trace_fill_pkt_info(struct sk_buff *nlskb,
     94				  const struct nft_pktinfo *pkt)
     95{
     96	const struct sk_buff *skb = pkt->skb;
     97	int off = skb_network_offset(skb);
     98	unsigned int len, nh_end;
     99
    100	nh_end = pkt->flags & NFT_PKTINFO_L4PROTO ? nft_thoff(pkt) : skb->len;
    101	len = min_t(unsigned int, nh_end - skb_network_offset(skb),
    102		    NFT_TRACETYPE_NETWORK_HSIZE);
    103	if (trace_fill_header(nlskb, NFTA_TRACE_NETWORK_HEADER, skb, off, len))
    104		return -1;
    105
    106	if (pkt->flags & NFT_PKTINFO_L4PROTO) {
    107		len = min_t(unsigned int, skb->len - nft_thoff(pkt),
    108			    NFT_TRACETYPE_TRANSPORT_HSIZE);
    109		if (trace_fill_header(nlskb, NFTA_TRACE_TRANSPORT_HEADER, skb,
    110				      nft_thoff(pkt), len))
    111			return -1;
    112	}
    113
    114	if (!skb_mac_header_was_set(skb))
    115		return 0;
    116
    117	if (skb_vlan_tag_get(skb))
    118		return nf_trace_fill_ll_header(nlskb, skb);
    119
    120	off = skb_mac_header(skb) - skb->data;
    121	len = min_t(unsigned int, -off, NFT_TRACETYPE_LL_HSIZE);
    122	return trace_fill_header(nlskb, NFTA_TRACE_LL_HEADER,
    123				 skb, off, len);
    124}
    125
    126static int nf_trace_fill_rule_info(struct sk_buff *nlskb,
    127				   const struct nft_traceinfo *info)
    128{
    129	if (!info->rule || info->rule->is_last)
    130		return 0;
    131
    132	/* a continue verdict with ->type == RETURN means that this is
    133	 * an implicit return (end of chain reached).
    134	 *
    135	 * Since no rule matched, the ->rule pointer is invalid.
    136	 */
    137	if (info->type == NFT_TRACETYPE_RETURN &&
    138	    info->verdict->code == NFT_CONTINUE)
    139		return 0;
    140
    141	return nla_put_be64(nlskb, NFTA_TRACE_RULE_HANDLE,
    142			    cpu_to_be64(info->rule->handle),
    143			    NFTA_TRACE_PAD);
    144}
    145
    146static bool nft_trace_have_verdict_chain(struct nft_traceinfo *info)
    147{
    148	switch (info->type) {
    149	case NFT_TRACETYPE_RETURN:
    150	case NFT_TRACETYPE_RULE:
    151		break;
    152	default:
    153		return false;
    154	}
    155
    156	switch (info->verdict->code) {
    157	case NFT_JUMP:
    158	case NFT_GOTO:
    159		break;
    160	default:
    161		return false;
    162	}
    163
    164	return true;
    165}
    166
    167void nft_trace_notify(struct nft_traceinfo *info)
    168{
    169	const struct nft_pktinfo *pkt = info->pkt;
    170	struct nlmsghdr *nlh;
    171	struct sk_buff *skb;
    172	unsigned int size;
    173	u32 mark = 0;
    174	u16 event;
    175
    176	if (!nfnetlink_has_listeners(nft_net(pkt), NFNLGRP_NFTRACE))
    177		return;
    178
    179	size = nlmsg_total_size(sizeof(struct nfgenmsg)) +
    180		nla_total_size(strlen(info->chain->table->name)) +
    181		nla_total_size(strlen(info->chain->name)) +
    182		nla_total_size_64bit(sizeof(__be64)) +	/* rule handle */
    183		nla_total_size(sizeof(__be32)) +	/* trace type */
    184		nla_total_size(0) +			/* VERDICT, nested */
    185			nla_total_size(sizeof(u32)) +	/* verdict code */
    186		nla_total_size(sizeof(u32)) +		/* id */
    187		nla_total_size(NFT_TRACETYPE_LL_HSIZE) +
    188		nla_total_size(NFT_TRACETYPE_NETWORK_HSIZE) +
    189		nla_total_size(NFT_TRACETYPE_TRANSPORT_HSIZE) +
    190		nla_total_size(sizeof(u32)) +		/* iif */
    191		nla_total_size(sizeof(__be16)) +	/* iiftype */
    192		nla_total_size(sizeof(u32)) +		/* oif */
    193		nla_total_size(sizeof(__be16)) +	/* oiftype */
    194		nla_total_size(sizeof(u32)) +		/* mark */
    195		nla_total_size(sizeof(u32)) +		/* nfproto */
    196		nla_total_size(sizeof(u32));		/* policy */
    197
    198	if (nft_trace_have_verdict_chain(info))
    199		size += nla_total_size(strlen(info->verdict->chain->name)); /* jump target */
    200
    201	skb = nlmsg_new(size, GFP_ATOMIC);
    202	if (!skb)
    203		return;
    204
    205	event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_TRACE);
    206	nlh = nfnl_msg_put(skb, 0, 0, event, 0, info->basechain->type->family,
    207			   NFNETLINK_V0, 0);
    208	if (!nlh)
    209		goto nla_put_failure;
    210
    211	if (nla_put_be32(skb, NFTA_TRACE_NFPROTO, htonl(nft_pf(pkt))))
    212		goto nla_put_failure;
    213
    214	if (nla_put_be32(skb, NFTA_TRACE_TYPE, htonl(info->type)))
    215		goto nla_put_failure;
    216
    217	if (nla_put_u32(skb, NFTA_TRACE_ID, info->skbid))
    218		goto nla_put_failure;
    219
    220	if (nla_put_string(skb, NFTA_TRACE_CHAIN, info->chain->name))
    221		goto nla_put_failure;
    222
    223	if (nla_put_string(skb, NFTA_TRACE_TABLE, info->chain->table->name))
    224		goto nla_put_failure;
    225
    226	if (nf_trace_fill_rule_info(skb, info))
    227		goto nla_put_failure;
    228
    229	switch (info->type) {
    230	case NFT_TRACETYPE_UNSPEC:
    231	case __NFT_TRACETYPE_MAX:
    232		break;
    233	case NFT_TRACETYPE_RETURN:
    234	case NFT_TRACETYPE_RULE:
    235		if (nft_verdict_dump(skb, NFTA_TRACE_VERDICT, info->verdict))
    236			goto nla_put_failure;
    237
    238		/* pkt->skb undefined iff NF_STOLEN, disable dump */
    239		if (info->verdict->code == NF_STOLEN)
    240			info->packet_dumped = true;
    241		else
    242			mark = pkt->skb->mark;
    243
    244		break;
    245	case NFT_TRACETYPE_POLICY:
    246		mark = pkt->skb->mark;
    247
    248		if (nla_put_be32(skb, NFTA_TRACE_POLICY,
    249				 htonl(info->basechain->policy)))
    250			goto nla_put_failure;
    251		break;
    252	}
    253
    254	if (mark && nla_put_be32(skb, NFTA_TRACE_MARK, htonl(mark)))
    255		goto nla_put_failure;
    256
    257	if (!info->packet_dumped) {
    258		if (nf_trace_fill_dev_info(skb, nft_in(pkt), nft_out(pkt)))
    259			goto nla_put_failure;
    260
    261		if (nf_trace_fill_pkt_info(skb, pkt))
    262			goto nla_put_failure;
    263		info->packet_dumped = true;
    264	}
    265
    266	nlmsg_end(skb, nlh);
    267	nfnetlink_send(skb, nft_net(pkt), 0, NFNLGRP_NFTRACE, 0, GFP_ATOMIC);
    268	return;
    269
    270 nla_put_failure:
    271	WARN_ON_ONCE(1);
    272	kfree_skb(skb);
    273}
    274
    275void nft_trace_init(struct nft_traceinfo *info, const struct nft_pktinfo *pkt,
    276		    const struct nft_verdict *verdict,
    277		    const struct nft_chain *chain)
    278{
    279	static siphash_key_t trace_key __read_mostly;
    280	struct sk_buff *skb = pkt->skb;
    281
    282	info->basechain = nft_base_chain(chain);
    283	info->trace = true;
    284	info->nf_trace = pkt->skb->nf_trace;
    285	info->packet_dumped = false;
    286	info->pkt = pkt;
    287	info->verdict = verdict;
    288
    289	net_get_random_once(&trace_key, sizeof(trace_key));
    290
    291	info->skbid = (u32)siphash_3u32(hash32_ptr(skb),
    292					skb_get_hash(skb),
    293					skb->skb_iif,
    294					&trace_key);
    295}