1 // tapset for netfilter hooks
2 // Copyright (C) 2012-2017 Red Hat Inc.
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
12 #include "translate.h"
19 using namespace __gnu_cxx
;
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");
26 // ------------------------------------------------------------------------
27 // netfilter derived probes
28 // ------------------------------------------------------------------------
31 struct netfilter_derived_probe
: public derived_probe
38 set
<string
> context_vars
;
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
);
47 struct netfilter_derived_probe_group
: public generic_dpg
<netfilter_derived_probe
>
50 void emit_module_decls (systemtap_session
& s
);
51 void emit_module_init (systemtap_session
& s
);
52 void emit_module_exit (systemtap_session
& s
);
55 struct netfilter_var_expanding_visitor
: public var_expanding_visitor
57 netfilter_var_expanding_visitor(systemtap_session
& s
);
59 set
<string
> context_vars
;
61 void visit_target_symbol (target_symbol
* e
);
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
)
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()
74 bool hook_error
= false;
75 bool pf_error
= false;
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.
85 // Validate hook, pf, priority
86 if(pf
== "NFPROTO_IPV4")
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;
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
);
119 else if(pf
=="NFPROTO_IPV6")
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;
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
);
146 else if (pf
== "NFPROTO_ARP")
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;
157 else if (pf
== "NFPROTO_BRIDGE")
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;
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.
179 // At this point the priority should be a 32 bit integer encoded as a string.
180 // Ensure that this is the case.
183 int prio
= lex_cast
<int32_t>(priority
);
186 catch (const runtime_error
&)
189 (_F("unsupported netfilter priority \"%s\" for protocol family \"%s\"; need stap -g",
190 priority
.c_str(), pf
.c_str()));
193 // Complain and abort if there were any hook name errors
196 (_F("unsupported netfilter hook \"%s\" for protocol family \"%s\"; need stap -g",
197 hook
.c_str(), pf
.c_str()));
199 // Complain and abort if there were any pf errors
202 (_F("unsupported netfilter protocol family \"%s\"; need stap -g", pf
.c_str()));
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
);
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();
216 this->context_vars
.insert(name
);
217 vardecl
*v
= new vardecl
;
219 v
->tok
= this->tok
; /* XXX: but really the $context var. */
220 v
->set_arity (0, this->tok
);
222 v
->synthetic
= true; // suppress rvalue or lvalue optimizations
223 this->locals
.push_back (v
);
228 netfilter_derived_probe::join_group (systemtap_session
& s
)
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
;
238 netfilter_derived_probe_group::emit_module_decls (systemtap_session
& s
)
240 if (probes
.empty()) return;
242 // Here we emit any global data structures and functions, including callback functions
243 // to be invoked by netfilter.
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.
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.
253 s
.op
->newline() << "/* ---- netfilter probes ---- */";
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>";
261 s
.op
->newline() << "#include \"linux/netfilter.c\"";
263 for (unsigned i
=0; i
< probes
.size(); i
++)
265 netfilter_derived_probe
*np
= probes
[i
];
266 s
.op
->newline() << "static unsigned int enter_netfilter_probe_" << np
->nf_index
;
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,
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)";
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() << "{";
283 s
.op
->newline() << "#elif defined(STAPCONF_NETFILTER_V313B)";
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() << "{";
288 s
.op
->newline() << "#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22)";
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() << "{";
293 s
.op
->newline() << "#else";
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;";
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",
320 // Copy or pretend-to-touch each incoming parameter.
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.
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;";
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;";
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;";
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;";
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;";
345 s
.op
->newline() << "(void) nf_out;";
347 // Invoke the probe handler
348 s
.op
->newline() << "(*stp->ph) (c);";
350 common_probe_entryfn_epilogue (s
, false, otf_safe_context(s
));
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") + ";";
355 s
.op
->newline() << "return nf_verdict;";
356 s
.op
->newline(-1) << "}";
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";
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
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() << "};";
380 netfilter_derived_probe_group::emit_module_init (systemtap_session
& s
)
382 if (probes
.empty()) return;
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
++)
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
393 s
.op
->newline() << "if (rc < 0) {";
395 for (int j
=i
-1; j
>=0; j
--) // XXX: j must be signed for loop to work
397 netfilter_derived_probe
*np2
= probes
[j
];
398 s
.op
->newline() << "nf_unregister_hook (& netfilter_opts_" << np2
->nf_index
<< ");";
400 s
.op
->newline(-1) << "}";
407 netfilter_derived_probe_group::emit_module_exit (systemtap_session
& s
)
409 if (probes
.empty()) return;
411 // We register (do not execute) the probes here.
412 for (unsigned i
=0; i
< probes
.size(); i
++)
414 netfilter_derived_probe
*np
= probes
[i
];
415 s
.op
->newline() << "nf_unregister_hook (& netfilter_opts_" << np
->nf_index
<< ");";
420 netfilter_var_expanding_visitor::netfilter_var_expanding_visitor (systemtap_session
& s
):
421 var_expanding_visitor (s
)
427 netfilter_var_expanding_visitor::visit_target_symbol (target_symbol
* e
)
431 assert(e
->name
.size() > 0 && e
->name
[0] == '$');
434 throw SEMANTIC_ERROR(_("cannot take address of netfilter hook context variable"), e
->tok
);
436 // We map all $context variables to similarly named probe locals.
437 // See emit_module_decls for how the parameters & result are handled.
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
449 throw SEMANTIC_ERROR(_("unsupported context variable"), e
->tok
);
451 if (! lvalue_ok
&& is_active_lvalue (e
))
452 throw SEMANTIC_ERROR(_("write to netfilter parameter not permitted"), e
->tok
);
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
);
458 context_vars
.insert (c_var
);
460 // Synthesize a symbol to reference those variables
461 symbol
* sym
= new symbol
;
467 catch (const semantic_error
&er
)
473 // ------------------------------------------------------------------------
474 // unified probe builder for netfilter probes
475 // ------------------------------------------------------------------------
478 struct netfilter_builder
: public derived_probe_builder
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
);
485 static void register_patterns(systemtap_session
& s
);
487 virtual string
name() { return "netfilter builder"; }
492 netfilter_builder::build(systemtap_session
& sess
,
494 probe_point
* location
,
495 literal_map_t
const & parameters
,
496 vector
<derived_probe
*> & finished_results
)
498 interned_string hook
; // no default
499 interned_string pf
; // no default
500 interned_string priority
= "0"; // Default: somewhere in the middle
502 if(!get_param(parameters
, TOK_HOOK
, hook
))
503 throw SEMANTIC_ERROR (_("missing hooknum"));
505 if(!get_param(parameters
, TOK_PF
, pf
))
506 throw SEMANTIC_ERROR (_("missing protocol family"));
508 get_param(parameters
, TOK_PRIORITY
, priority
);
510 finished_results
.push_back(new netfilter_derived_probe(sess
, base
, location
, hook
, pf
, priority
));
514 register_tapset_netfilter(systemtap_session
& s
)
516 match_node
* root
= s
.pattern_root
;
517 derived_probe_builder
*builder
= new netfilter_builder();
520 //netfilter.hook().pf()
521 root
->bind(TOK_NETFILTER
)->bind_str(TOK_HOOK
)->bind_str(TOK_PF
)->bind(builder
);
523 //netfilter.pf().hook()
524 root
->bind(TOK_NETFILTER
)->bind_str(TOK_PF
)->bind_str(TOK_HOOK
)->bind(builder
);
526 //netfilter.hook().pf().priority()
527 root
->bind(TOK_NETFILTER
)->bind_str(TOK_HOOK
)->bind_str(TOK_PF
)->bind_str(TOK_PRIORITY
)->bind(builder
);
529 //netfilter.pf().hook().priority()
530 root
->bind(TOK_NETFILTER
)->bind_str(TOK_PF
)->bind_str(TOK_HOOK
)->bind_str(TOK_PRIORITY
)->bind(builder
);
533 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */