• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/jhash.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 
25 DEFINE_STATIC_KEY_FALSE(nft_trace_enabled);
26 EXPORT_SYMBOL_GPL(nft_trace_enabled);
27 
trace_fill_id(struct sk_buff * nlskb,struct sk_buff * skb)28 static int trace_fill_id(struct sk_buff *nlskb, struct sk_buff *skb)
29 {
30 	__be32 id;
31 
32 	/* using skb address as ID results in a limited number of
33 	 * values (and quick reuse).
34 	 *
35 	 * So we attempt to use as many skb members that will not
36 	 * change while skb is with netfilter.
37 	 */
38 	id = (__be32)jhash_2words(hash32_ptr(skb), skb_get_hash(skb),
39 				  skb->skb_iif);
40 
41 	return nla_put_be32(nlskb, NFTA_TRACE_ID, id);
42 }
43 
trace_fill_header(struct sk_buff * nlskb,u16 type,const struct sk_buff * skb,int off,unsigned int len)44 static int trace_fill_header(struct sk_buff *nlskb, u16 type,
45 			     const struct sk_buff *skb,
46 			     int off, unsigned int len)
47 {
48 	struct nlattr *nla;
49 
50 	if (len == 0)
51 		return 0;
52 
53 	nla = nla_reserve(nlskb, type, len);
54 	if (!nla || skb_copy_bits(skb, off, nla_data(nla), len))
55 		return -1;
56 
57 	return 0;
58 }
59 
nf_trace_fill_ll_header(struct sk_buff * nlskb,const struct sk_buff * skb)60 static int nf_trace_fill_ll_header(struct sk_buff *nlskb,
61 				   const struct sk_buff *skb)
62 {
63 	struct vlan_ethhdr veth;
64 	int off;
65 
66 	BUILD_BUG_ON(sizeof(veth) > NFT_TRACETYPE_LL_HSIZE);
67 
68 	off = skb_mac_header(skb) - skb->data;
69 	if (off != -ETH_HLEN)
70 		return -1;
71 
72 	if (skb_copy_bits(skb, off, &veth, ETH_HLEN))
73 		return -1;
74 
75 	veth.h_vlan_proto = skb->vlan_proto;
76 	veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb));
77 	veth.h_vlan_encapsulated_proto = skb->protocol;
78 
79 	return nla_put(nlskb, NFTA_TRACE_LL_HEADER, sizeof(veth), &veth);
80 }
81 
nf_trace_fill_dev_info(struct sk_buff * nlskb,const struct net_device * indev,const struct net_device * outdev)82 static int nf_trace_fill_dev_info(struct sk_buff *nlskb,
83 				  const struct net_device *indev,
84 				  const struct net_device *outdev)
85 {
86 	if (indev) {
87 		if (nla_put_be32(nlskb, NFTA_TRACE_IIF,
88 				 htonl(indev->ifindex)))
89 			return -1;
90 
91 		if (nla_put_be16(nlskb, NFTA_TRACE_IIFTYPE,
92 				 htons(indev->type)))
93 			return -1;
94 	}
95 
96 	if (outdev) {
97 		if (nla_put_be32(nlskb, NFTA_TRACE_OIF,
98 				 htonl(outdev->ifindex)))
99 			return -1;
100 
101 		if (nla_put_be16(nlskb, NFTA_TRACE_OIFTYPE,
102 				 htons(outdev->type)))
103 			return -1;
104 	}
105 
106 	return 0;
107 }
108 
nf_trace_fill_pkt_info(struct sk_buff * nlskb,const struct nft_pktinfo * pkt)109 static int nf_trace_fill_pkt_info(struct sk_buff *nlskb,
110 				  const struct nft_pktinfo *pkt)
111 {
112 	const struct sk_buff *skb = pkt->skb;
113 	int off = skb_network_offset(skb);
114 	unsigned int len, nh_end;
115 
116 	nh_end = pkt->tprot_set ? pkt->xt.thoff : skb->len;
117 	len = min_t(unsigned int, nh_end - skb_network_offset(skb),
118 		    NFT_TRACETYPE_NETWORK_HSIZE);
119 	if (trace_fill_header(nlskb, NFTA_TRACE_NETWORK_HEADER, skb, off, len))
120 		return -1;
121 
122 	if (pkt->tprot_set) {
123 		len = min_t(unsigned int, skb->len - pkt->xt.thoff,
124 			    NFT_TRACETYPE_TRANSPORT_HSIZE);
125 		if (trace_fill_header(nlskb, NFTA_TRACE_TRANSPORT_HEADER, skb,
126 				      pkt->xt.thoff, len))
127 			return -1;
128 	}
129 
130 	if (!skb_mac_header_was_set(skb))
131 		return 0;
132 
133 	if (skb_vlan_tag_get(skb))
134 		return nf_trace_fill_ll_header(nlskb, skb);
135 
136 	off = skb_mac_header(skb) - skb->data;
137 	len = min_t(unsigned int, -off, NFT_TRACETYPE_LL_HSIZE);
138 	return trace_fill_header(nlskb, NFTA_TRACE_LL_HEADER,
139 				 skb, off, len);
140 }
141 
nf_trace_fill_rule_info(struct sk_buff * nlskb,const struct nft_traceinfo * info)142 static int nf_trace_fill_rule_info(struct sk_buff *nlskb,
143 				   const struct nft_traceinfo *info)
144 {
145 	if (!info->rule)
146 		return 0;
147 
148 	/* a continue verdict with ->type == RETURN means that this is
149 	 * an implicit return (end of chain reached).
150 	 *
151 	 * Since no rule matched, the ->rule pointer is invalid.
152 	 */
153 	if (info->type == NFT_TRACETYPE_RETURN &&
154 	    info->verdict->code == NFT_CONTINUE)
155 		return 0;
156 
157 	return nla_put_be64(nlskb, NFTA_TRACE_RULE_HANDLE,
158 			    cpu_to_be64(info->rule->handle),
159 			    NFTA_TRACE_PAD);
160 }
161 
nft_trace_have_verdict_chain(struct nft_traceinfo * info)162 static bool nft_trace_have_verdict_chain(struct nft_traceinfo *info)
163 {
164 	switch (info->type) {
165 	case NFT_TRACETYPE_RETURN:
166 	case NFT_TRACETYPE_RULE:
167 		break;
168 	default:
169 		return false;
170 	}
171 
172 	switch (info->verdict->code) {
173 	case NFT_JUMP:
174 	case NFT_GOTO:
175 		break;
176 	default:
177 		return false;
178 	}
179 
180 	return true;
181 }
182 
nft_trace_notify(struct nft_traceinfo * info)183 void nft_trace_notify(struct nft_traceinfo *info)
184 {
185 	const struct nft_pktinfo *pkt = info->pkt;
186 	struct nfgenmsg *nfmsg;
187 	struct nlmsghdr *nlh;
188 	struct sk_buff *skb;
189 	unsigned int size;
190 	u16 event;
191 
192 	if (!nfnetlink_has_listeners(nft_net(pkt), NFNLGRP_NFTRACE))
193 		return;
194 
195 	size = nlmsg_total_size(sizeof(struct nfgenmsg)) +
196 		nla_total_size(strlen(info->chain->table->name)) +
197 		nla_total_size(strlen(info->chain->name)) +
198 		nla_total_size_64bit(sizeof(__be64)) +	/* rule handle */
199 		nla_total_size(sizeof(__be32)) +	/* trace type */
200 		nla_total_size(0) +			/* VERDICT, nested */
201 			nla_total_size(sizeof(u32)) +	/* verdict code */
202 		nla_total_size(sizeof(u32)) +		/* id */
203 		nla_total_size(NFT_TRACETYPE_LL_HSIZE) +
204 		nla_total_size(NFT_TRACETYPE_NETWORK_HSIZE) +
205 		nla_total_size(NFT_TRACETYPE_TRANSPORT_HSIZE) +
206 		nla_total_size(sizeof(u32)) +		/* iif */
207 		nla_total_size(sizeof(__be16)) +	/* iiftype */
208 		nla_total_size(sizeof(u32)) +		/* oif */
209 		nla_total_size(sizeof(__be16)) +	/* oiftype */
210 		nla_total_size(sizeof(u32)) +		/* mark */
211 		nla_total_size(sizeof(u32)) +		/* nfproto */
212 		nla_total_size(sizeof(u32));		/* policy */
213 
214 	if (nft_trace_have_verdict_chain(info))
215 		size += nla_total_size(strlen(info->verdict->chain->name)); /* jump target */
216 
217 	skb = nlmsg_new(size, GFP_ATOMIC);
218 	if (!skb)
219 		return;
220 
221 	event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_TRACE);
222 	nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct nfgenmsg), 0);
223 	if (!nlh)
224 		goto nla_put_failure;
225 
226 	nfmsg = nlmsg_data(nlh);
227 	nfmsg->nfgen_family	= info->basechain->type->family;
228 	nfmsg->version		= NFNETLINK_V0;
229 	nfmsg->res_id		= 0;
230 
231 	if (nla_put_be32(skb, NFTA_TRACE_NFPROTO, htonl(nft_pf(pkt))))
232 		goto nla_put_failure;
233 
234 	if (nla_put_be32(skb, NFTA_TRACE_TYPE, htonl(info->type)))
235 		goto nla_put_failure;
236 
237 	if (trace_fill_id(skb, pkt->skb))
238 		goto nla_put_failure;
239 
240 	if (nla_put_string(skb, NFTA_TRACE_CHAIN, info->chain->name))
241 		goto nla_put_failure;
242 
243 	if (nla_put_string(skb, NFTA_TRACE_TABLE, info->chain->table->name))
244 		goto nla_put_failure;
245 
246 	if (nf_trace_fill_rule_info(skb, info))
247 		goto nla_put_failure;
248 
249 	switch (info->type) {
250 	case NFT_TRACETYPE_UNSPEC:
251 	case __NFT_TRACETYPE_MAX:
252 		break;
253 	case NFT_TRACETYPE_RETURN:
254 	case NFT_TRACETYPE_RULE:
255 		if (nft_verdict_dump(skb, NFTA_TRACE_VERDICT, info->verdict))
256 			goto nla_put_failure;
257 		break;
258 	case NFT_TRACETYPE_POLICY:
259 		if (nla_put_be32(skb, NFTA_TRACE_POLICY,
260 				 htonl(info->basechain->policy)))
261 			goto nla_put_failure;
262 		break;
263 	}
264 
265 	if (pkt->skb->mark &&
266 	    nla_put_be32(skb, NFTA_TRACE_MARK, htonl(pkt->skb->mark)))
267 		goto nla_put_failure;
268 
269 	if (!info->packet_dumped) {
270 		if (nf_trace_fill_dev_info(skb, nft_in(pkt), nft_out(pkt)))
271 			goto nla_put_failure;
272 
273 		if (nf_trace_fill_pkt_info(skb, pkt))
274 			goto nla_put_failure;
275 		info->packet_dumped = true;
276 	}
277 
278 	nlmsg_end(skb, nlh);
279 	nfnetlink_send(skb, nft_net(pkt), 0, NFNLGRP_NFTRACE, 0, GFP_ATOMIC);
280 	return;
281 
282  nla_put_failure:
283 	WARN_ON_ONCE(1);
284 	kfree_skb(skb);
285 }
286 
nft_trace_init(struct nft_traceinfo * info,const struct nft_pktinfo * pkt,const struct nft_verdict * verdict,const struct nft_chain * chain)287 void nft_trace_init(struct nft_traceinfo *info, const struct nft_pktinfo *pkt,
288 		    const struct nft_verdict *verdict,
289 		    const struct nft_chain *chain)
290 {
291 	info->basechain = nft_base_chain(chain);
292 	info->trace = true;
293 	info->packet_dumped = false;
294 	info->pkt = pkt;
295 	info->verdict = verdict;
296 }
297