]> sourceware.org Git - systemtap.git/blob - tapset-netfilter.cxx
buildrun.cxx: adapt to kernel 5.4+
[systemtap.git] / tapset-netfilter.cxx
1 // tapset for netfilter hooks
2 // Copyright (C) 2012-2017 Red Hat Inc.
3 //
4 // This file is part of systemtap, and is free software. You can
5 // redistribute it and/or modify it under the terms of the GNU General
6 // Public License (GPL); either version 2, or (at your option) any
7 // later version.
8
9
10 #include "session.h"
11 #include "tapsets.h"
12 #include "translate.h"
13 #include "util.h"
14 #include <cstring>
15 #include <string>
16 #include <limits.h>
17
18 using namespace std;
19 using namespace __gnu_cxx;
20
21 static const string TOK_NETFILTER("netfilter");
22 static const string TOK_HOOK("hook");
23 static const string TOK_PF("pf");
24 static const string TOK_PRIORITY("priority");
25
26 // ------------------------------------------------------------------------
27 // netfilter derived probes
28 // ------------------------------------------------------------------------
29
30
31 struct netfilter_derived_probe: public derived_probe
32 {
33 string hook;
34 string pf;
35 string priority;
36 unsigned nf_index;
37
38 set<string> context_vars;
39
40 netfilter_derived_probe (systemtap_session &, probe* p,
41 probe_point* l, string h,
42 string protof, string pri);
43 virtual void join_group (systemtap_session& s);
44 };
45
46
47 struct netfilter_derived_probe_group: public generic_dpg<netfilter_derived_probe>
48 {
49 public:
50 void emit_module_decls (systemtap_session& s);
51 void emit_module_init (systemtap_session& s);
52 void emit_module_exit (systemtap_session& s);
53 };
54
55 struct netfilter_var_expanding_visitor: public var_expanding_visitor
56 {
57 netfilter_var_expanding_visitor(systemtap_session& s);
58
59 set<string> context_vars;
60
61 void visit_target_symbol (target_symbol* e);
62 };
63
64 netfilter_derived_probe::netfilter_derived_probe (systemtap_session &s, probe* p,
65 probe_point* l, string h,
66 string protof, string pri):
67 derived_probe (p, l), hook (h), pf (protof), priority (pri)
68 {
69 static unsigned nf_index_ctr = 0;
70 this->nf_index = nf_index_ctr++; // PR14137: need to generate unique
71 // identifier, since p->name may be
72 // shared in c_unparser::emit_probe()
73
74 bool hook_error = false;
75 bool pf_error = false;
76
77 // Map the strings passed in to the actual values defined in netfilter_*.h
78 // NOTE: We need to hard code all the following
79 // constants rather than just include the
80 // appropriate kernel headers because in different
81 // versions of the kernel, certain constants are not
82 // defined. This was the best method at the time to
83 // get them to compile properly.
84
85 // Validate hook, pf, priority
86 if(pf == "NFPROTO_IPV4")
87 {
88 // Protocol Family
89 pf = "2";
90
91 // Hook
92 if (hook == "NF_INET_PRE_ROUTING") hook = "0";
93 else if (hook == "NF_INET_LOCAL_IN") hook = "1";
94 else if (hook == "NF_INET_FORWARD") hook = "2";
95 else if (hook == "NF_INET_LOCAL_OUT") hook = "3";
96 else if (hook == "NF_INET_POST_ROUTING") hook = "4";
97 else if (hook == "NF_IP_PRE_ROUTING") hook = "0";
98 else if (hook == "NF_IP_LOCAL_IN") hook = "1";
99 else if (hook == "NF_IP_FORWARD") hook = "2";
100 else if (hook == "NF_IP_LOCAL_OUT") hook = "3";
101 else if (hook == "NF_IP_POST_ROUTING") hook = "4";
102 else hook_error = true;
103
104 // Priority
105 if (priority == "NF_IP_PRI_FIRST") priority = lex_cast(INT_MIN);
106 else if (priority == "NF_IP_PRI_CONNTRACK_DEFRAG") priority = "-400";
107 else if (priority == "NF_IP_PRI_RAW") priority = "-300";
108 else if (priority == "NF_IP_PRI_SELINUX_FIRST") priority = "-225";
109 else if (priority == "NF_IP_PRI_CONNTRACK") priority = "-200";
110 else if (priority == "NF_IP_PRI_MANGLE") priority = "-150";
111 else if (priority == "NF_IP_PRI_NAT_DST") priority = "-100";
112 else if (priority == "NF_IP_PRI_FILTER") priority = "0";
113 else if (priority == "NF_IP_PRI_SECURITY") priority = "50";
114 else if (priority == "NF_IP_PRI_NAT_SRC") priority = "100";
115 else if (priority == "NF_IP_PRI_SELINUX_LAST") priority = "225";
116 else if (priority == "NF_IP_PRI_CONNTRACK_CONFIRM") priority = lex_cast(INT_MAX);
117 else if (priority == "NF_IP_PRI_LAST") priority = lex_cast(INT_MAX);
118 }
119 else if(pf=="NFPROTO_IPV6")
120 {
121 // Protocol Family
122 pf = "10";
123
124 // Hook
125 if (hook == "NF_IP6_PRE_ROUTING") hook = "0";
126 else if (hook == "NF_IP6_LOCAL_IN") hook = "1";
127 else if (hook == "NF_IP6_FORWARD") hook = "2";
128 else if (hook == "NF_IP6_LOCAL_OUT") hook = "3";
129 else if (hook == "NF_IP6_POST_ROUTING") hook = "4";
130 else hook_error = true;
131
132 // Priority
133 if (priority == "NF_IP6_PRI_FIRST") priority = lex_cast(INT_MIN);
134 else if (priority == "NF_IP6_PRI_CONNTRACK_DEFRAG") priority = "-400";
135 else if (priority == "NF_IP6_PRI_RAW") priority = "-300";
136 else if (priority == "NF_IP6_PRI_SELINUX_FIRST") priority = "-225";
137 else if (priority == "NF_IP6_PRI_CONNTRACK") priority = "-200";
138 else if (priority == "NF_IP6_PRI_MANGLE") priority = "-150";
139 else if (priority == "NF_IP6_PRI_NAT_DST") priority = "-100";
140 else if (priority == "NF_IP6_PRI_FILTER") priority = "0";
141 else if (priority == "NF_IP6_PRI_SECURITY") priority = "50";
142 else if (priority == "NF_IP6_PRI_NAT_SRC") priority = "100";
143 else if (priority == "NF_IP6_PRI_SELINUX_LAST") priority = "225";
144 else if (priority == "NF_IP6_PRI_LAST") priority = lex_cast(INT_MAX);
145 }
146 else if (pf == "NFPROTO_ARP")
147 {
148 // Protocol Family
149 pf = "3";
150
151 // Hook
152 if (hook == "NF_ARP_IN") hook = "0";
153 else if (hook == "NF_ARP_OUT") hook = "1";
154 else if (hook == "NF_ARP_FORWARD") hook = "2";
155 else hook_error = true;
156 }
157 else if (pf == "NFPROTO_BRIDGE")
158 {
159 // Protocol Family
160 pf = "7";
161
162 // Hook
163 if (hook == "NF_BR_PRE_ROUTING") hook = "0";
164 else if (hook == "NF_BR_LOCAL_IN") hook = "1";
165 else if (hook == "NF_BR_FORWARD") hook = "2";
166 else if (hook == "NF_BR_LOCAL_OUT") hook = "3";
167 else if (hook == "NF_BR_POST_ROUTING") hook = "4";
168 else hook_error = true;
169 }
170 else
171 pf_error = true;
172
173 // If not running in guru mode, we need more strict checks on hook name,
174 // protocol family and priority to avoid people putting in wacky embedded c
175 // nastiness. Otherwise, and if it didn't match any of the above lists,
176 // pass the string in as is.
177 if(!s.guru_mode)
178 {
179 // At this point the priority should be a 32 bit integer encoded as a string.
180 // Ensure that this is the case.
181 try
182 {
183 int prio = lex_cast<int32_t>(priority);
184 (void) prio;
185 }
186 catch (const runtime_error&)
187 {
188 throw SEMANTIC_ERROR
189 (_F("unsupported netfilter priority \"%s\" for protocol family \"%s\"; need stap -g",
190 priority.c_str(), pf.c_str()));
191 }
192
193 // Complain and abort if there were any hook name errors
194 if (hook_error)
195 throw SEMANTIC_ERROR
196 (_F("unsupported netfilter hook \"%s\" for protocol family \"%s\"; need stap -g",
197 hook.c_str(), pf.c_str()));
198
199 // Complain and abort if there were any pf errors
200 if (pf_error)
201 throw SEMANTIC_ERROR
202 (_F("unsupported netfilter protocol family \"%s\"; need stap -g", pf.c_str()));
203 }
204
205 // Expand local variables in the probe body
206 netfilter_var_expanding_visitor v (s);
207 var_expand_const_fold_loop (s, this->body, v);
208
209 // Create probe-local vardecls, before symbol resolution might make
210 // one for us, so that we can set the all-important synthetic flag.
211 for (set<string>::iterator it = v.context_vars.begin();
212 it != v.context_vars.end();
213 it++)
214 {
215 string name = *it;
216 this->context_vars.insert(name);
217 vardecl *v = new vardecl;
218 v->name = name;
219 v->tok = this->tok; /* XXX: but really the $context var. */
220 v->set_arity (0, this->tok);
221 v->type = pe_long;
222 v->synthetic = true; // suppress rvalue or lvalue optimizations
223 this->locals.push_back (v);
224 }
225 }
226
227 void
228 netfilter_derived_probe::join_group (systemtap_session& s)
229 {
230 if (! s.netfilter_derived_probes)
231 s.netfilter_derived_probes = new netfilter_derived_probe_group ();
232 s.netfilter_derived_probes->enroll (this);
233 this->group = s.netfilter_derived_probes;
234 }
235
236
237 void
238 netfilter_derived_probe_group::emit_module_decls (systemtap_session& s)
239 {
240 if (probes.empty()) return;
241
242 // Here we emit any global data structures and functions, including callback functions
243 // to be invoked by netfilter.
244 //
245 // For other kernel callbacks, a token is passed back to help identify a particular
246 // probe-point registration. For netfilter, nope, so once we're in a notification callback,
247 // we can't find out exactly on whose (which probe point's) behalf we were called.
248 //
249 // So, we just emit one netfilter callback function per systemtap probe, each with its
250 // own nf_hook_ops structure. Note that the translator already emits a stp_probes[] array,
251 // pre-filled with probe names and handler functions and that sort of stuff.
252
253 s.op->newline() << "/* ---- netfilter probes ---- */";
254
255 s.op->newline() << "#include <linux/netfilter.h>";
256 s.op->newline() << "#include <linux/skbuff.h>";
257 s.op->newline() << "#include <linux/udp.h>";
258 s.op->newline() << "#include <linux/tcp.h>";
259 s.op->newline() << "#include <linux/ip.h>";
260
261 s.op->newline() << "#include \"linux/netfilter.c\"";
262
263 for (unsigned i=0; i < probes.size(); i++)
264 {
265 netfilter_derived_probe *np = probes[i];
266 s.op->newline() << "static unsigned int enter_netfilter_probe_" << np->nf_index;
267
268 // Previous to kernel 2.6.22, the hookfunction definition takes
269 // a struct sk_buff **skb, whereas currently it uses a *skb. We
270 // need emit the right version so this will compile on RHEL5,
271 // for example.
272 s.op->newline() << "#if defined(STAPCONF_NETFILTER_V44)";
273 s.op->newline() << "(void *priv, struct sk_buff *nf_skb, const struct nf_hook_state *nf_state)";
274 s.op->newline() << "{";
275 s.op->newline() << "#elif defined(STAPCONF_NETFILTER_V41)";
276 s.op->newline() << "(const struct nf_hook_ops *nf_ops, struct sk_buff *nf_skb, const struct nf_hook_state *nf_state)";
277 s.op->newline() << "{";
278 s.op->newline() << "#elif defined(STAPCONF_NETFILTER_V313)";
279
280 s.op->newline() << "(const struct nf_hook_ops *nf_ops, struct sk_buff *nf_skb, const struct net_device *nf_in, const struct net_device *nf_out, int (*nf_okfn)(struct sk_buff *))";
281 s.op->newline() << "{";
282
283 s.op->newline() << "#elif defined(STAPCONF_NETFILTER_V313B)";
284
285 s.op->newline() << "(const struct nf_hook_ops *nf_ops, struct sk_buff *nf_skb, const struct net_device *nf_in, const struct net_device *nf_out, const struct nf_hook_state *nf_state)";
286 s.op->newline() << "{";
287
288 s.op->newline() << "#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22)";
289
290 s.op->newline() << "(unsigned int nf_hooknum, struct sk_buff *nf_skb, const struct net_device *nf_in, const struct net_device *nf_out, int (*nf_okfn)(struct sk_buff *))";
291 s.op->newline() << "{";
292
293 s.op->newline() << "#else";
294
295 s.op->newline() << "(unsigned int nf_hooknum, struct sk_buff **nf_pskb, const struct net_device *nf_in, const struct net_device *nf_out, int (*nf_okfn)(struct sk_buff *))";
296 s.op->newline() << "{";
297 s.op->newline(1) << "struct sk_buff *nf_skb = nf_pskb ? *nf_pskb : NULL;";
298
299 s.op->newline(-1) << "#endif";
300 s.op->newline(1) << "const struct stap_probe * const stp = & stap_probes[" << np->session_index << "];";
301 s.op->newline() << "int nf_verdict = NF_ACCEPT;"; // default NF_ACCEPT, to be used by $verdict context var
302 s.op->newline() << "#if defined(STAPCONF_NETFILTER_V44)";
303 s.op->newline() << "unsigned int nf_hooknum = nf_state->hook;";
304 s.op->newline() << "#elif defined(STAPCONF_NETFILTER_V313) || defined(STAPCONF_NETFILTER_V313B) || defined(STAPCONF_NETFILTER_V41)";
305 s.op->newline() << "unsigned int nf_hooknum = nf_ops->hooknum;";
306 s.op->newline() << "#endif";
307 s.op->newline() << "#if defined(STAPCONF_NETFILTER_V41) || defined(STAPCONF_NETFILTER_V44)";
308 s.op->newline() << "struct net_device *nf_in = nf_state->in;";
309 s.op->newline() << "struct net_device *nf_out = nf_state->out;";
310 s.op->newline() << "#endif";
311 s.op->newline() << "#if defined(STAPCONF_NETFILTER_V44)";
312 s.op->newline() << "int (*nf_okfn)(struct net *, struct sock *, struct sk_buff *) = nf_state->okfn;";
313 s.op->newline() << "#elif defined(STAPCONF_NETFILTER_V41)";
314 s.op->newline() << "int (*nf_okfn)(struct sock *, struct sk_buff *) = nf_state->okfn;";
315 s.op->newline() << "#endif";
316 common_probe_entryfn_prologue (s, "STAP_SESSION_RUNNING", "stp",
317 "stp_probe_type_netfilter",
318 false);
319
320 // Copy or pretend-to-touch each incoming parameter.
321
322 string c_p = "c->probe_locals." + lex_cast(np->name()); // this is where the $context vars show up
323 // NB: PR14137: this should be the potentially shared name,
324 // since the generated probe handler body refers to that name.
325
326 if (np->context_vars.find("__nf_hooknum") != np->context_vars.end())
327 s.op->newline() << c_p + "." + s.up->c_localname("__nf_hooknum") + " = (int64_t)(uintptr_t) nf_hooknum;";
328 else
329 s.op->newline() << "(void) nf_hooknum;";
330 if (np->context_vars.find("__nf_skb") != np->context_vars.end())
331 s.op->newline() << c_p + "." + s.up->c_localname("__nf_skb") + " = (int64_t)(uintptr_t) nf_skb;";
332 else
333 s.op->newline() << "(void) nf_skb;";
334 if (np->context_vars.find("__nf_in") != np->context_vars.end())
335 s.op->newline() << c_p + "." + s.up->c_localname("__nf_in") + " = (int64_t)(uintptr_t) nf_in;";
336 else
337 s.op->newline() << "(void) nf_in;";
338 if (np->context_vars.find("__nf_out") != np->context_vars.end())
339 s.op->newline() << c_p + "." + s.up->c_localname("__nf_out") + " = (int64_t)(uintptr_t) nf_out;";
340 else
341 s.op->newline() << "(void) nf_out;";
342 if (np->context_vars.find("__nf_verdict") != np->context_vars.end())
343 s.op->newline() << c_p + "." + s.up->c_localname("__nf_verdict") + " = (int64_t) nf_verdict;";
344 else
345 s.op->newline() << "(void) nf_out;";
346
347 // Invoke the probe handler
348 s.op->newline() << "(*stp->ph) (c);";
349
350 common_probe_entryfn_epilogue (s, false, otf_safe_context(s));
351
352 if (np->context_vars.find("__nf_verdict") != np->context_vars.end())
353 s.op->newline() << "if (c != NULL) nf_verdict = (int) "+c_p+"." + s.up->c_localname("__nf_verdict") + ";";
354
355 s.op->newline() << "return nf_verdict;";
356 s.op->newline(-1) << "}";
357
358 // now emit the nf_hook_ops struct for this probe.
359 s.op->newline() << "static struct nf_hook_ops netfilter_opts_" << np->nf_index << " = {";
360 s.op->newline() << ".hook = enter_netfilter_probe_" << np->nf_index << ",";
361 s.op->newline() << "#ifndef STAPCONF_NETFILTER_V44";
362 s.op->newline() << ".owner = THIS_MODULE,";
363 s.op->newline() << "#endif";
364
365 // XXX: if these strings/numbers are not range-limited / validated before we get here,
366 // ie during the netfilter_derived_probe ctor, then we will emit potential trash here,
367 // leading to all kinds of horror. Like zombie women eating roach-filled walnuts. Dogs
368 // and cats living together. Foreign foods taking over the refrigerator. Don't let this
369 // happen to you!
370 s.op->newline() << ".hooknum = " << np->hook << ",";
371 s.op->newline() << ".pf = " << np->pf << ",";
372 s.op->newline() << ".priority = " << np->priority << ",";
373 s.op->newline() << "};";
374 }
375 s.op->newline();
376 }
377
378
379 void
380 netfilter_derived_probe_group::emit_module_init (systemtap_session& s)
381 {
382 if (probes.empty()) return;
383
384 // We register (do not execute) the probes here.
385 // NB: since we anticipate only a few netfilter/hook type probes, there is no need to
386 // emit an initialization loop into the generated C code. We can simply unroll it.
387 for (unsigned i=0; i < probes.size(); i++)
388 {
389 netfilter_derived_probe *np = probes[i];
390 s.op->newline() << "rc = nf_register_hook (& netfilter_opts_" << np->nf_index << ");";
391 if (i > 0) // unregister others upon failure
392 {
393 s.op->newline() << "if (rc < 0) {";
394 s.op->indent(1);
395 for (int j=i-1; j>=0; j--) // XXX: j must be signed for loop to work
396 {
397 netfilter_derived_probe *np2 = probes[j];
398 s.op->newline() << "nf_unregister_hook (& netfilter_opts_" << np2->nf_index << ");";
399 }
400 s.op->newline(-1) << "}";
401 }
402 }
403 }
404
405
406 void
407 netfilter_derived_probe_group::emit_module_exit (systemtap_session& s)
408 {
409 if (probes.empty()) return;
410
411 // We register (do not execute) the probes here.
412 for (unsigned i=0; i < probes.size(); i++)
413 {
414 netfilter_derived_probe *np = probes[i];
415 s.op->newline() << "nf_unregister_hook (& netfilter_opts_" << np->nf_index << ");";
416 }
417 }
418
419
420 netfilter_var_expanding_visitor::netfilter_var_expanding_visitor (systemtap_session& s):
421 var_expanding_visitor (s)
422 {
423 }
424
425
426 void
427 netfilter_var_expanding_visitor::visit_target_symbol (target_symbol* e)
428 {
429 try
430 {
431 assert(e->name.size() > 0 && e->name[0] == '$');
432
433 if (e->addressof)
434 throw SEMANTIC_ERROR(_("cannot take address of netfilter hook context variable"), e->tok);
435
436 // We map all $context variables to similarly named probe locals.
437 // See emit_module_decls for how the parameters & result are handled.
438 string c_var;
439 bool lvalue_ok = false;
440 bool need_guru = false;
441 if (e->name == "$hooknum") { c_var = "__nf_hooknum"; }
442 else if (e->name == "$skb") { c_var = "__nf_skb"; }
443 else if (e->name == "$in") { c_var = "__nf_in"; }
444 else if (e->name == "$out") { c_var = "__nf_out"; }
445 else if (e->name == "$okfn") { c_var = "__nf_okfn"; }
446 else if (e->name == "$verdict") { c_var = "__nf_verdict"; lvalue_ok = true; need_guru = true; }
447 // XXX: also support $$vars / $$parms
448 else
449 throw SEMANTIC_ERROR(_("unsupported context variable"), e->tok);
450
451 if (! lvalue_ok && is_active_lvalue (e))
452 throw SEMANTIC_ERROR(_("write to netfilter parameter not permitted"), e->tok);
453
454 // Writing to variables like $verdict requires guru mode, for obvious reasons
455 if(need_guru && !sess.guru_mode)
456 throw SEMANTIC_ERROR(_("write to netfilter verdict requires guru mode; need stap -g"), e->tok);
457
458 context_vars.insert (c_var);
459
460 // Synthesize a symbol to reference those variables
461 symbol* sym = new symbol;
462 sym->type = pe_long;
463 sym->tok = e->tok;
464 sym->name = c_var;
465 provide (sym);
466 }
467 catch (const semantic_error &er)
468 {
469 e->chain (er);
470 provide (e);
471 }
472 }
473 // ------------------------------------------------------------------------
474 // unified probe builder for netfilter probes
475 // ------------------------------------------------------------------------
476
477
478 struct netfilter_builder: public derived_probe_builder
479 {
480 virtual void build(systemtap_session & sess,
481 probe * base, probe_point * location,
482 literal_map_t const & parameters,
483 vector<derived_probe *> & finished_results);
484
485 static void register_patterns(systemtap_session& s);
486
487 virtual string name() { return "netfilter builder"; }
488 };
489
490
491 void
492 netfilter_builder::build(systemtap_session & sess,
493 probe * base,
494 probe_point * location,
495 literal_map_t const & parameters,
496 vector<derived_probe *> & finished_results)
497 {
498 interned_string hook; // no default
499 interned_string pf; // no default
500 interned_string priority = "0"; // Default: somewhere in the middle
501
502 if(!get_param(parameters, TOK_HOOK, hook))
503 throw SEMANTIC_ERROR (_("missing hooknum"));
504
505 if(!get_param(parameters, TOK_PF, pf))
506 throw SEMANTIC_ERROR (_("missing protocol family"));
507
508 get_param(parameters, TOK_PRIORITY, priority);
509
510 finished_results.push_back(new netfilter_derived_probe(sess, base, location, hook, pf, priority));
511 }
512
513 void
514 register_tapset_netfilter(systemtap_session& s)
515 {
516 match_node* root = s.pattern_root;
517 derived_probe_builder *builder = new netfilter_builder();
518
519
520 //netfilter.hook().pf()
521 root->bind(TOK_NETFILTER)->bind_str(TOK_HOOK)->bind_str(TOK_PF)->bind(builder);
522
523 //netfilter.pf().hook()
524 root->bind(TOK_NETFILTER)->bind_str(TOK_PF)->bind_str(TOK_HOOK)->bind(builder);
525
526 //netfilter.hook().pf().priority()
527 root->bind(TOK_NETFILTER)->bind_str(TOK_HOOK)->bind_str(TOK_PF)->bind_str(TOK_PRIORITY)->bind(builder);
528
529 //netfilter.pf().hook().priority()
530 root->bind(TOK_NETFILTER)->bind_str(TOK_PF)->bind_str(TOK_HOOK)->bind_str(TOK_PRIORITY)->bind(builder);
531 }
532
533 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.068729 seconds and 5 git commands to generate.