aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2015-11-28 15:53:04 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2015-12-09 07:18:37 -0500
commit33d5a7b14bfd02e60af9d223db8dfff0cbcabe6b (patch)
treec0f2892d80f78d959f3ae38c625fd03a6e5a1bb8 /net
parent7ec3f7b47b8d9ad7ba425726f2c58f9ddce040df (diff)
netfilter: nf_tables: extend tracing infrastructure
nft monitor mode can then decode and display this trace data. Parts of LL/Network/Transport headers are provided as separate attributes. Otherwise, printing IP address data becomes virtually impossible for userspace since in the case of the netdev family we really don't want userspace to have to know all the possible link layer types and/or sizes just to display/print an ip address. We also don't want userspace to have to follow ipv6 header chains to get the s/dport info, the kernel already did this work for us. To avoid bloating nft_do_chain all data required for tracing is encapsulated in nft_traceinfo. The structure is initialized unconditionally(!) for each nft_do_chain invocation. This unconditionall call will be moved under a static key in a followup patch. With lots of help from Patrick McHardy and Pablo Neira. Signed-off-by: Florian Westphal <fw@strlen.de> Acked-by: Patrick McHardy <kaber@trash.net> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/Makefile2
-rw-r--r--net/netfilter/nf_tables_api.c12
-rw-r--r--net/netfilter/nf_tables_core.c45
-rw-r--r--net/netfilter/nf_tables_trace.c271
-rw-r--r--net/netfilter/nfnetlink.c1
5 files changed, 312 insertions, 19 deletions
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 7638c36b498c..22934846b5d1 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -67,7 +67,7 @@ obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o
67obj-$(CONFIG_NETFILTER_SYNPROXY) += nf_synproxy_core.o 67obj-$(CONFIG_NETFILTER_SYNPROXY) += nf_synproxy_core.o
68 68
69# nf_tables 69# nf_tables
70nf_tables-objs += nf_tables_core.o nf_tables_api.o 70nf_tables-objs += nf_tables_core.o nf_tables_api.o nf_tables_trace.o
71nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o nft_dynset.o 71nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o nft_dynset.o
72nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o 72nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o
73 73
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 93cc4737018f..c4969a0d54ba 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -4446,22 +4446,22 @@ static void nft_verdict_uninit(const struct nft_data *data)
4446 } 4446 }
4447} 4447}
4448 4448
4449static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data) 4449int nft_verdict_dump(struct sk_buff *skb, int type, const struct nft_verdict *v)
4450{ 4450{
4451 struct nlattr *nest; 4451 struct nlattr *nest;
4452 4452
4453 nest = nla_nest_start(skb, NFTA_DATA_VERDICT); 4453 nest = nla_nest_start(skb, type);
4454 if (!nest) 4454 if (!nest)
4455 goto nla_put_failure; 4455 goto nla_put_failure;
4456 4456
4457 if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict.code))) 4457 if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(v->code)))
4458 goto nla_put_failure; 4458 goto nla_put_failure;
4459 4459
4460 switch (data->verdict.code) { 4460 switch (v->code) {
4461 case NFT_JUMP: 4461 case NFT_JUMP:
4462 case NFT_GOTO: 4462 case NFT_GOTO:
4463 if (nla_put_string(skb, NFTA_VERDICT_CHAIN, 4463 if (nla_put_string(skb, NFTA_VERDICT_CHAIN,
4464 data->verdict.chain->name)) 4464 v->chain->name))
4465 goto nla_put_failure; 4465 goto nla_put_failure;
4466 } 4466 }
4467 nla_nest_end(skb, nest); 4467 nla_nest_end(skb, nest);
@@ -4572,7 +4572,7 @@ int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
4572 err = nft_value_dump(skb, data, len); 4572 err = nft_value_dump(skb, data, len);
4573 break; 4573 break;
4574 case NFT_DATA_VERDICT: 4574 case NFT_DATA_VERDICT:
4575 err = nft_verdict_dump(skb, data); 4575 err = nft_verdict_dump(skb, NFTA_DATA_VERDICT, &data->verdict);
4576 break; 4576 break;
4577 default: 4577 default:
4578 err = -EINVAL; 4578 err = -EINVAL;
diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c
index f3695a497408..2395de7c8ab2 100644
--- a/net/netfilter/nf_tables_core.c
+++ b/net/netfilter/nf_tables_core.c
@@ -44,22 +44,36 @@ static struct nf_loginfo trace_loginfo = {
44 }, 44 },
45}; 45};
46 46
47static void __nft_trace_packet(const struct nft_pktinfo *pkt, 47static noinline void __nft_trace_packet(struct nft_traceinfo *info,
48 const struct nft_chain *chain, 48 const struct nft_chain *chain,
49 int rulenum, enum nft_trace type) 49 int rulenum, enum nft_trace type)
50{ 50{
51 const struct nft_pktinfo *pkt = info->pkt;
52
53 if (!pkt->skb->nf_trace)
54 return;
55
56 info->chain = chain;
57 info->type = type;
58
59 nft_trace_notify(info);
60
51 nf_log_trace(pkt->net, pkt->pf, pkt->hook, pkt->skb, pkt->in, 61 nf_log_trace(pkt->net, pkt->pf, pkt->hook, pkt->skb, pkt->in,
52 pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ", 62 pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ",
53 chain->table->name, chain->name, comments[type], 63 chain->table->name, chain->name, comments[type],
54 rulenum); 64 rulenum);
55} 65}
56 66
57static inline void nft_trace_packet(const struct nft_pktinfo *pkt, 67static inline void nft_trace_packet(struct nft_traceinfo *info,
58 const struct nft_chain *chain, 68 const struct nft_chain *chain,
59 int rulenum, enum nft_trace type) 69 const struct nft_rule *rule,
70 int rulenum,
71 enum nft_trace_types type)
60{ 72{
61 if (unlikely(pkt->skb->nf_trace)) 73 if (unlikely(info->trace)) {
62 __nft_trace_packet(pkt, chain, rulenum, type); 74 info->rule = rule;
75 __nft_trace_packet(info, chain, rulenum, type);
76 }
63} 77}
64 78
65static void nft_cmp_fast_eval(const struct nft_expr *expr, 79static void nft_cmp_fast_eval(const struct nft_expr *expr,
@@ -121,7 +135,9 @@ nft_do_chain(struct nft_pktinfo *pkt, void *priv)
121 struct nft_stats *stats; 135 struct nft_stats *stats;
122 int rulenum; 136 int rulenum;
123 unsigned int gencursor = nft_genmask_cur(net); 137 unsigned int gencursor = nft_genmask_cur(net);
138 struct nft_traceinfo info;
124 139
140 nft_trace_init(&info, pkt, &regs.verdict, basechain);
125do_chain: 141do_chain:
126 rulenum = 0; 142 rulenum = 0;
127 rule = list_entry(&chain->rules, struct nft_rule, list); 143 rule = list_entry(&chain->rules, struct nft_rule, list);
@@ -151,7 +167,8 @@ next_rule:
151 regs.verdict.code = NFT_CONTINUE; 167 regs.verdict.code = NFT_CONTINUE;
152 continue; 168 continue;
153 case NFT_CONTINUE: 169 case NFT_CONTINUE:
154 nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); 170 nft_trace_packet(&info, chain, rule,
171 rulenum, NFT_TRACETYPE_RULE);
155 continue; 172 continue;
156 } 173 }
157 break; 174 break;
@@ -161,7 +178,8 @@ next_rule:
161 case NF_ACCEPT: 178 case NF_ACCEPT:
162 case NF_DROP: 179 case NF_DROP:
163 case NF_QUEUE: 180 case NF_QUEUE:
164 nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); 181 nft_trace_packet(&info, chain, rule,
182 rulenum, NFT_TRACETYPE_RULE);
165 return regs.verdict.code; 183 return regs.verdict.code;
166 } 184 }
167 185
@@ -174,7 +192,8 @@ next_rule:
174 stackptr++; 192 stackptr++;
175 /* fall through */ 193 /* fall through */
176 case NFT_GOTO: 194 case NFT_GOTO:
177 nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); 195 nft_trace_packet(&info, chain, rule,
196 rulenum, NFT_TRACETYPE_RULE);
178 197
179 chain = regs.verdict.chain; 198 chain = regs.verdict.chain;
180 goto do_chain; 199 goto do_chain;
@@ -182,7 +201,8 @@ next_rule:
182 rulenum++; 201 rulenum++;
183 /* fall through */ 202 /* fall through */
184 case NFT_RETURN: 203 case NFT_RETURN:
185 nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN); 204 nft_trace_packet(&info, chain, rule,
205 rulenum, NFT_TRACETYPE_RETURN);
186 break; 206 break;
187 default: 207 default:
188 WARN_ON(1); 208 WARN_ON(1);
@@ -196,7 +216,8 @@ next_rule:
196 goto next_rule; 216 goto next_rule;
197 } 217 }
198 218
199 nft_trace_packet(pkt, basechain, -1, NFT_TRACE_POLICY); 219 nft_trace_packet(&info, basechain, NULL, -1,
220 NFT_TRACETYPE_POLICY);
200 221
201 rcu_read_lock_bh(); 222 rcu_read_lock_bh();
202 stats = this_cpu_ptr(rcu_dereference(nft_base_chain(basechain)->stats)); 223 stats = this_cpu_ptr(rcu_dereference(nft_base_chain(basechain)->stats));
diff --git a/net/netfilter/nf_tables_trace.c b/net/netfilter/nf_tables_trace.c
new file mode 100644
index 000000000000..36fd7ad6729a
--- /dev/null
+++ b/net/netfilter/nf_tables_trace.c
@@ -0,0 +1,271 @@
1/*
2 * (C) 2015 Red Hat GmbH
3 * Author: Florian Westphal <fw@strlen.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9
10#include <linux/module.h>
11#include <linux/hash.h>
12#include <linux/jhash.h>
13#include <linux/if_vlan.h>
14#include <linux/init.h>
15#include <linux/skbuff.h>
16#include <linux/netlink.h>
17#include <linux/netfilter.h>
18#include <linux/netfilter/nfnetlink.h>
19#include <linux/netfilter/nf_tables.h>
20#include <net/netfilter/nf_tables_core.h>
21#include <net/netfilter/nf_tables.h>
22
23#define NFT_TRACETYPE_LL_HSIZE 20
24#define NFT_TRACETYPE_NETWORK_HSIZE 40
25#define NFT_TRACETYPE_TRANSPORT_HSIZE 20
26
27static int trace_fill_id(struct sk_buff *nlskb, struct sk_buff *skb)
28{
29 __be32 id;
30
31 /* using skb address as ID results in a limited number of
32 * values (and quick reuse).
33 *
34 * So we attempt to use as many skb members that will not
35 * change while skb is with netfilter.
36 */
37 id = (__be32)jhash_2words(hash32_ptr(skb), skb_get_hash(skb),
38 skb->skb_iif);
39
40 return nla_put_be32(nlskb, NFTA_TRACE_ID, id);
41}
42
43static int trace_fill_header(struct sk_buff *nlskb, u16 type,
44 const struct sk_buff *skb,
45 int off, unsigned int len)
46{
47 struct nlattr *nla;
48
49 if (len == 0)
50 return 0;
51
52 nla = nla_reserve(nlskb, type, len);
53 if (!nla || skb_copy_bits(skb, off, nla_data(nla), len))
54 return -1;
55
56 return 0;
57}
58
59static int nf_trace_fill_ll_header(struct sk_buff *nlskb,
60 const struct sk_buff *skb)
61{
62 struct vlan_ethhdr veth;
63 int off;
64
65 BUILD_BUG_ON(sizeof(veth) > NFT_TRACETYPE_LL_HSIZE);
66
67 off = skb_mac_header(skb) - skb->data;
68 if (off != -ETH_HLEN)
69 return -1;
70
71 if (skb_copy_bits(skb, off, &veth, ETH_HLEN))
72 return -1;
73
74 veth.h_vlan_proto = skb->vlan_proto;
75 veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb));
76 veth.h_vlan_encapsulated_proto = skb->protocol;
77
78 return nla_put(nlskb, NFTA_TRACE_LL_HEADER, sizeof(veth), &veth);
79}
80
81static int nf_trace_fill_dev_info(struct sk_buff *nlskb,
82 const struct net_device *indev,
83 const struct net_device *outdev)
84{
85 if (indev) {
86 if (nla_put_be32(nlskb, NFTA_TRACE_IIF,
87 htonl(indev->ifindex)))
88 return -1;
89
90 if (nla_put_be16(nlskb, NFTA_TRACE_IIFTYPE,
91 htons(indev->type)))
92 return -1;
93 }
94
95 if (outdev) {
96 if (nla_put_be32(nlskb, NFTA_TRACE_OIF,
97 htonl(outdev->ifindex)))
98 return -1;
99
100 if (nla_put_be16(nlskb, NFTA_TRACE_OIFTYPE,
101 htons(outdev->type)))
102 return -1;
103 }
104
105 return 0;
106}
107
108static int nf_trace_fill_pkt_info(struct sk_buff *nlskb,
109 const struct nft_pktinfo *pkt)
110{
111 const struct sk_buff *skb = pkt->skb;
112 unsigned int len = min_t(unsigned int,
113 pkt->xt.thoff - skb_network_offset(skb),
114 NFT_TRACETYPE_NETWORK_HSIZE);
115 int off = skb_network_offset(skb);
116
117 if (trace_fill_header(nlskb, NFTA_TRACE_NETWORK_HEADER, skb, off, len))
118 return -1;
119
120 len = min_t(unsigned int, skb->len - pkt->xt.thoff,
121 NFT_TRACETYPE_TRANSPORT_HSIZE);
122
123 if (trace_fill_header(nlskb, NFTA_TRACE_TRANSPORT_HEADER, skb,
124 pkt->xt.thoff, len))
125 return -1;
126
127 if (!skb_mac_header_was_set(skb))
128 return 0;
129
130 if (skb_vlan_tag_get(skb))
131 return nf_trace_fill_ll_header(nlskb, skb);
132
133 off = skb_mac_header(skb) - skb->data;
134 len = min_t(unsigned int, -off, NFT_TRACETYPE_LL_HSIZE);
135 return trace_fill_header(nlskb, NFTA_TRACE_LL_HEADER,
136 skb, off, len);
137}
138
139static int nf_trace_fill_rule_info(struct sk_buff *nlskb,
140 const struct nft_traceinfo *info)
141{
142 if (!info->rule)
143 return 0;
144
145 /* a continue verdict with ->type == RETURN means that this is
146 * an implicit return (end of chain reached).
147 *
148 * Since no rule matched, the ->rule pointer is invalid.
149 */
150 if (info->type == NFT_TRACETYPE_RETURN &&
151 info->verdict->code == NFT_CONTINUE)
152 return 0;
153
154 return nla_put_be64(nlskb, NFTA_TRACE_RULE_HANDLE,
155 cpu_to_be64(info->rule->handle));
156}
157
158void nft_trace_notify(struct nft_traceinfo *info)
159{
160 const struct nft_pktinfo *pkt = info->pkt;
161 struct nfgenmsg *nfmsg;
162 struct nlmsghdr *nlh;
163 struct sk_buff *skb;
164 unsigned int size;
165 int event = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_TRACE;
166
167 if (!nfnetlink_has_listeners(pkt->net, NFNLGRP_NFTRACE))
168 return;
169
170 size = nlmsg_total_size(sizeof(struct nfgenmsg)) +
171 nla_total_size(NFT_TABLE_MAXNAMELEN) +
172 nla_total_size(NFT_CHAIN_MAXNAMELEN) +
173 nla_total_size(sizeof(__be64)) + /* rule handle */
174 nla_total_size(sizeof(__be32)) + /* trace type */
175 nla_total_size(0) + /* VERDICT, nested */
176 nla_total_size(sizeof(u32)) + /* verdict code */
177 nla_total_size(NFT_CHAIN_MAXNAMELEN) + /* jump target */
178 nla_total_size(sizeof(u32)) + /* id */
179 nla_total_size(NFT_TRACETYPE_LL_HSIZE) +
180 nla_total_size(NFT_TRACETYPE_NETWORK_HSIZE) +
181 nla_total_size(NFT_TRACETYPE_TRANSPORT_HSIZE) +
182 nla_total_size(sizeof(u32)) + /* iif */
183 nla_total_size(sizeof(__be16)) + /* iiftype */
184 nla_total_size(sizeof(u32)) + /* oif */
185 nla_total_size(sizeof(__be16)) + /* oiftype */
186 nla_total_size(sizeof(u32)) + /* mark */
187 nla_total_size(sizeof(u32)) + /* nfproto */
188 nla_total_size(sizeof(u32)); /* policy */
189
190 skb = nlmsg_new(size, GFP_ATOMIC);
191 if (!skb)
192 return;
193
194 nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct nfgenmsg), 0);
195 if (!nlh)
196 goto nla_put_failure;
197
198 nfmsg = nlmsg_data(nlh);
199 nfmsg->nfgen_family = info->basechain->type->family;
200 nfmsg->version = NFNETLINK_V0;
201 nfmsg->res_id = 0;
202
203 if (nla_put_be32(skb, NFTA_TRACE_NFPROTO, htonl(pkt->pf)))
204 goto nla_put_failure;
205
206 if (nla_put_be32(skb, NFTA_TRACE_TYPE, htonl(info->type)))
207 goto nla_put_failure;
208
209 if (trace_fill_id(skb, pkt->skb))
210 goto nla_put_failure;
211
212 if (info->chain) {
213 if (nla_put_string(skb, NFTA_TRACE_CHAIN,
214 info->chain->name))
215 goto nla_put_failure;
216 if (nla_put_string(skb, NFTA_TRACE_TABLE,
217 info->chain->table->name))
218 goto nla_put_failure;
219 }
220
221 if (nf_trace_fill_rule_info(skb, info))
222 goto nla_put_failure;
223
224 switch (info->type) {
225 case NFT_TRACETYPE_UNSPEC:
226 case __NFT_TRACETYPE_MAX:
227 break;
228 case NFT_TRACETYPE_RETURN:
229 case NFT_TRACETYPE_RULE:
230 if (nft_verdict_dump(skb, NFTA_TRACE_VERDICT, info->verdict))
231 goto nla_put_failure;
232 break;
233 case NFT_TRACETYPE_POLICY:
234 if (nla_put_be32(skb, NFTA_TRACE_POLICY,
235 info->basechain->policy))
236 goto nla_put_failure;
237 break;
238 }
239
240 if (pkt->skb->mark &&
241 nla_put_be32(skb, NFTA_TRACE_MARK, htonl(pkt->skb->mark)))
242 goto nla_put_failure;
243
244 if (!info->packet_dumped) {
245 if (nf_trace_fill_dev_info(skb, pkt->in, pkt->out))
246 goto nla_put_failure;
247
248 if (nf_trace_fill_pkt_info(skb, pkt))
249 goto nla_put_failure;
250 info->packet_dumped = true;
251 }
252
253 nlmsg_end(skb, nlh);
254 nfnetlink_send(skb, pkt->net, 0, NFNLGRP_NFTRACE, 0, GFP_ATOMIC);
255 return;
256
257 nla_put_failure:
258 WARN_ON_ONCE(1);
259 kfree_skb(skb);
260}
261
262void nft_trace_init(struct nft_traceinfo *info, const struct nft_pktinfo *pkt,
263 const struct nft_verdict *verdict,
264 const struct nft_chain *chain)
265{
266 info->basechain = nft_base_chain(chain);
267 info->trace = true;
268 info->packet_dumped = false;
269 info->pkt = pkt;
270 info->verdict = verdict;
271}
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index 46453ab318db..28591fa94ba5 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -49,6 +49,7 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = {
49 [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP, 49 [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
50 [NFNLGRP_NFTABLES] = NFNL_SUBSYS_NFTABLES, 50 [NFNLGRP_NFTABLES] = NFNL_SUBSYS_NFTABLES,
51 [NFNLGRP_ACCT_QUOTA] = NFNL_SUBSYS_ACCT, 51 [NFNLGRP_ACCT_QUOTA] = NFNL_SUBSYS_ACCT,
52 [NFNLGRP_NFTRACE] = NFNL_SUBSYS_NFTABLES,
52}; 53};
53 54
54void nfnl_lock(__u8 subsys_id) 55void nfnl_lock(__u8 subsys_id)