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