1 /*
2 * libxt_conntrack
3 * Shared library add-on to iptables for conntrack matching support.
4 *
5 * GPL (C) 2001 Marc Boucher (marc@mbsi.ca).
6 * Copyright © CC Computer Consultants GmbH, 2007 - 2008
7 * Jan Engelhardt <jengelh@computergmbh.de>
8 */
9 #include <stdbool.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <xtables.h>
15 #include <linux/netfilter/xt_conntrack.h>
16 #include <linux/netfilter/nf_conntrack_common.h>
17
18 struct ip_conntrack_old_tuple {
19 struct {
20 __be32 ip;
21 union {
22 __u16 all;
23 } u;
24 } src;
25
26 struct {
27 __be32 ip;
28 union {
29 __u16 all;
30 } u;
31
32 /* The protocol. */
33 __u16 protonum;
34 } dst;
35 };
36
37 struct xt_conntrack_info {
38 unsigned int statemask, statusmask;
39
40 struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX];
41 struct in_addr sipmsk[IP_CT_DIR_MAX], dipmsk[IP_CT_DIR_MAX];
42
43 unsigned long expires_min, expires_max;
44
45 /* Flags word */
46 uint8_t flags;
47 /* Inverse flags */
48 uint8_t invflags;
49 };
50
51 enum {
52 O_CTSTATE = 0,
53 O_CTPROTO,
54 O_CTORIGSRC,
55 O_CTORIGDST,
56 O_CTREPLSRC,
57 O_CTREPLDST,
58 O_CTORIGSRCPORT,
59 O_CTORIGDSTPORT,
60 O_CTREPLSRCPORT,
61 O_CTREPLDSTPORT,
62 O_CTSTATUS,
63 O_CTEXPIRE,
64 O_CTDIR,
65 };
66
conntrack_mt_help(void)67 static void conntrack_mt_help(void)
68 {
69 printf(
70 "conntrack match options:\n"
71 "[!] --ctstate {INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT}[,...]\n"
72 " State(s) to match\n"
73 "[!] --ctproto proto Protocol to match; by number or name, e.g. \"tcp\"\n"
74 "[!] --ctorigsrc address[/mask]\n"
75 "[!] --ctorigdst address[/mask]\n"
76 "[!] --ctreplsrc address[/mask]\n"
77 "[!] --ctrepldst address[/mask]\n"
78 " Original/Reply source/destination address\n"
79 "[!] --ctorigsrcport port\n"
80 "[!] --ctorigdstport port\n"
81 "[!] --ctreplsrcport port\n"
82 "[!] --ctrepldstport port\n"
83 " TCP/UDP/SCTP orig./reply source/destination port\n"
84 "[!] --ctstatus {NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED}[,...]\n"
85 " Status(es) to match\n"
86 "[!] --ctexpire time[:time] Match remaining lifetime in seconds against\n"
87 " value or range of values (inclusive)\n"
88 " --ctdir {ORIGINAL|REPLY} Flow direction of packet\n");
89 }
90
91 #define s struct xt_conntrack_info /* for v0 */
92 static const struct xt_option_entry conntrack_mt_opts_v0[] = {
93 {.name = "ctstate", .id = O_CTSTATE, .type = XTTYPE_STRING,
94 .flags = XTOPT_INVERT},
95 {.name = "ctproto", .id = O_CTPROTO, .type = XTTYPE_PROTOCOL,
96 .flags = XTOPT_INVERT},
97 {.name = "ctorigsrc", .id = O_CTORIGSRC, .type = XTTYPE_HOST,
98 .flags = XTOPT_INVERT},
99 {.name = "ctorigdst", .id = O_CTORIGDST, .type = XTTYPE_HOST,
100 .flags = XTOPT_INVERT},
101 {.name = "ctreplsrc", .id = O_CTREPLSRC, .type = XTTYPE_HOST,
102 .flags = XTOPT_INVERT},
103 {.name = "ctrepldst", .id = O_CTREPLDST, .type = XTTYPE_HOST,
104 .flags = XTOPT_INVERT},
105 {.name = "ctstatus", .id = O_CTSTATUS, .type = XTTYPE_STRING,
106 .flags = XTOPT_INVERT},
107 {.name = "ctexpire", .id = O_CTEXPIRE, .type = XTTYPE_UINT32RC,
108 .flags = XTOPT_INVERT},
109 XTOPT_TABLEEND,
110 };
111 #undef s
112
113 #define s struct xt_conntrack_mtinfo3 /* for v1-v3 */
114 /* We exploit the fact that v1-v3 share the same layout */
115 static const struct xt_option_entry conntrack_mt_opts[] = {
116 {.name = "ctstate", .id = O_CTSTATE, .type = XTTYPE_STRING,
117 .flags = XTOPT_INVERT},
118 {.name = "ctproto", .id = O_CTPROTO, .type = XTTYPE_PROTOCOL,
119 .flags = XTOPT_INVERT},
120 {.name = "ctorigsrc", .id = O_CTORIGSRC, .type = XTTYPE_HOSTMASK,
121 .flags = XTOPT_INVERT},
122 {.name = "ctorigdst", .id = O_CTORIGDST, .type = XTTYPE_HOSTMASK,
123 .flags = XTOPT_INVERT},
124 {.name = "ctreplsrc", .id = O_CTREPLSRC, .type = XTTYPE_HOSTMASK,
125 .flags = XTOPT_INVERT},
126 {.name = "ctrepldst", .id = O_CTREPLDST, .type = XTTYPE_HOSTMASK,
127 .flags = XTOPT_INVERT},
128 {.name = "ctstatus", .id = O_CTSTATUS, .type = XTTYPE_STRING,
129 .flags = XTOPT_INVERT},
130 {.name = "ctexpire", .id = O_CTEXPIRE, .type = XTTYPE_UINT32RC,
131 .flags = XTOPT_INVERT},
132 {.name = "ctorigsrcport", .id = O_CTORIGSRCPORT, .type = XTTYPE_PORTRC,
133 .flags = XTOPT_INVERT},
134 {.name = "ctorigdstport", .id = O_CTORIGDSTPORT, .type = XTTYPE_PORTRC,
135 .flags = XTOPT_INVERT},
136 {.name = "ctreplsrcport", .id = O_CTREPLSRCPORT, .type = XTTYPE_PORTRC,
137 .flags = XTOPT_INVERT},
138 {.name = "ctrepldstport", .id = O_CTREPLDSTPORT, .type = XTTYPE_PORTRC,
139 .flags = XTOPT_INVERT},
140 {.name = "ctdir", .id = O_CTDIR, .type = XTTYPE_STRING},
141 XTOPT_TABLEEND,
142 };
143 #undef s
144
145 static int
parse_state(const char * state,size_t len,struct xt_conntrack_info * sinfo)146 parse_state(const char *state, size_t len, struct xt_conntrack_info *sinfo)
147 {
148 if (strncasecmp(state, "INVALID", len) == 0)
149 sinfo->statemask |= XT_CONNTRACK_STATE_INVALID;
150 else if (strncasecmp(state, "NEW", len) == 0)
151 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
152 else if (strncasecmp(state, "ESTABLISHED", len) == 0)
153 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
154 else if (strncasecmp(state, "RELATED", len) == 0)
155 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
156 else if (strncasecmp(state, "UNTRACKED", len) == 0)
157 sinfo->statemask |= XT_CONNTRACK_STATE_UNTRACKED;
158 else if (strncasecmp(state, "SNAT", len) == 0)
159 sinfo->statemask |= XT_CONNTRACK_STATE_SNAT;
160 else if (strncasecmp(state, "DNAT", len) == 0)
161 sinfo->statemask |= XT_CONNTRACK_STATE_DNAT;
162 else
163 return 0;
164 return 1;
165 }
166
167 static void
parse_states(const char * arg,struct xt_conntrack_info * sinfo)168 parse_states(const char *arg, struct xt_conntrack_info *sinfo)
169 {
170 const char *comma;
171
172 while ((comma = strchr(arg, ',')) != NULL) {
173 if (comma == arg || !parse_state(arg, comma-arg, sinfo))
174 xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
175 arg = comma+1;
176 }
177 if (!*arg)
178 xtables_error(PARAMETER_PROBLEM, "\"--ctstate\" requires a list of "
179 "states with no spaces, e.g. "
180 "ESTABLISHED,RELATED");
181 if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
182 xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
183 }
184
185 static bool
conntrack_ps_state(struct xt_conntrack_mtinfo3 * info,const char * state,size_t z)186 conntrack_ps_state(struct xt_conntrack_mtinfo3 *info, const char *state,
187 size_t z)
188 {
189 if (strncasecmp(state, "INVALID", z) == 0)
190 info->state_mask |= XT_CONNTRACK_STATE_INVALID;
191 else if (strncasecmp(state, "NEW", z) == 0)
192 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
193 else if (strncasecmp(state, "ESTABLISHED", z) == 0)
194 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
195 else if (strncasecmp(state, "RELATED", z) == 0)
196 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
197 else if (strncasecmp(state, "UNTRACKED", z) == 0)
198 info->state_mask |= XT_CONNTRACK_STATE_UNTRACKED;
199 else if (strncasecmp(state, "SNAT", z) == 0)
200 info->state_mask |= XT_CONNTRACK_STATE_SNAT;
201 else if (strncasecmp(state, "DNAT", z) == 0)
202 info->state_mask |= XT_CONNTRACK_STATE_DNAT;
203 else
204 return false;
205 return true;
206 }
207
208 static void
conntrack_ps_states(struct xt_conntrack_mtinfo3 * info,const char * arg)209 conntrack_ps_states(struct xt_conntrack_mtinfo3 *info, const char *arg)
210 {
211 const char *comma;
212
213 while ((comma = strchr(arg, ',')) != NULL) {
214 if (comma == arg || !conntrack_ps_state(info, arg, comma - arg))
215 xtables_error(PARAMETER_PROBLEM,
216 "Bad ctstate \"%s\"", arg);
217 arg = comma + 1;
218 }
219
220 if (strlen(arg) == 0 || !conntrack_ps_state(info, arg, strlen(arg)))
221 xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
222 }
223
224 static int
parse_status(const char * status,size_t len,struct xt_conntrack_info * sinfo)225 parse_status(const char *status, size_t len, struct xt_conntrack_info *sinfo)
226 {
227 if (strncasecmp(status, "NONE", len) == 0)
228 sinfo->statusmask |= 0;
229 else if (strncasecmp(status, "EXPECTED", len) == 0)
230 sinfo->statusmask |= IPS_EXPECTED;
231 else if (strncasecmp(status, "SEEN_REPLY", len) == 0)
232 sinfo->statusmask |= IPS_SEEN_REPLY;
233 else if (strncasecmp(status, "ASSURED", len) == 0)
234 sinfo->statusmask |= IPS_ASSURED;
235 #ifdef IPS_CONFIRMED
236 else if (strncasecmp(status, "CONFIRMED", len) == 0)
237 sinfo->statusmask |= IPS_CONFIRMED;
238 #endif
239 else
240 return 0;
241 return 1;
242 }
243
244 static void
parse_statuses(const char * arg,struct xt_conntrack_info * sinfo)245 parse_statuses(const char *arg, struct xt_conntrack_info *sinfo)
246 {
247 const char *comma;
248
249 while ((comma = strchr(arg, ',')) != NULL) {
250 if (comma == arg || !parse_status(arg, comma-arg, sinfo))
251 xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
252 arg = comma+1;
253 }
254
255 if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
256 xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
257 }
258
259 static bool
conntrack_ps_status(struct xt_conntrack_mtinfo3 * info,const char * status,size_t z)260 conntrack_ps_status(struct xt_conntrack_mtinfo3 *info, const char *status,
261 size_t z)
262 {
263 if (strncasecmp(status, "NONE", z) == 0)
264 info->status_mask |= 0;
265 else if (strncasecmp(status, "EXPECTED", z) == 0)
266 info->status_mask |= IPS_EXPECTED;
267 else if (strncasecmp(status, "SEEN_REPLY", z) == 0)
268 info->status_mask |= IPS_SEEN_REPLY;
269 else if (strncasecmp(status, "ASSURED", z) == 0)
270 info->status_mask |= IPS_ASSURED;
271 else if (strncasecmp(status, "CONFIRMED", z) == 0)
272 info->status_mask |= IPS_CONFIRMED;
273 else
274 return false;
275 return true;
276 }
277
278 static void
conntrack_ps_statuses(struct xt_conntrack_mtinfo3 * info,const char * arg)279 conntrack_ps_statuses(struct xt_conntrack_mtinfo3 *info, const char *arg)
280 {
281 const char *comma;
282
283 while ((comma = strchr(arg, ',')) != NULL) {
284 if (comma == arg || !conntrack_ps_status(info, arg, comma - arg))
285 xtables_error(PARAMETER_PROBLEM,
286 "Bad ctstatus \"%s\"", arg);
287 arg = comma + 1;
288 }
289
290 if (strlen(arg) == 0 || !conntrack_ps_status(info, arg, strlen(arg)))
291 xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
292 }
293
conntrack_parse(struct xt_option_call * cb)294 static void conntrack_parse(struct xt_option_call *cb)
295 {
296 struct xt_conntrack_info *sinfo = cb->data;
297
298 xtables_option_parse(cb);
299 switch (cb->entry->id) {
300 case O_CTSTATE:
301 parse_states(cb->arg, sinfo);
302 if (cb->invert)
303 sinfo->invflags |= XT_CONNTRACK_STATE;
304 break;
305 case O_CTPROTO:
306 if (cb->invert)
307 sinfo->invflags |= XT_CONNTRACK_PROTO;
308 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = cb->val.protocol;
309
310 if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
311 && (sinfo->invflags & XT_INV_PROTO))
312 xtables_error(PARAMETER_PROBLEM,
313 "rule would never match protocol");
314
315 sinfo->flags |= XT_CONNTRACK_PROTO;
316 break;
317 case O_CTORIGSRC:
318 if (cb->invert)
319 sinfo->invflags |= XT_CONNTRACK_ORIGSRC;
320 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = cb->val.haddr.ip;
321 sinfo->flags |= XT_CONNTRACK_ORIGSRC;
322 break;
323 case O_CTORIGDST:
324 if (cb->invert)
325 sinfo->invflags |= XT_CONNTRACK_ORIGDST;
326 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = cb->val.haddr.ip;
327 sinfo->flags |= XT_CONNTRACK_ORIGDST;
328 break;
329 case O_CTREPLSRC:
330 if (cb->invert)
331 sinfo->invflags |= XT_CONNTRACK_REPLSRC;
332 sinfo->tuple[IP_CT_DIR_REPLY].src.ip = cb->val.haddr.ip;
333 sinfo->flags |= XT_CONNTRACK_REPLSRC;
334 break;
335 case O_CTREPLDST:
336 if (cb->invert)
337 sinfo->invflags |= XT_CONNTRACK_REPLDST;
338 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = cb->val.haddr.ip;
339 sinfo->flags |= XT_CONNTRACK_REPLDST;
340 break;
341 case O_CTSTATUS:
342 parse_statuses(cb->arg, sinfo);
343 if (cb->invert)
344 sinfo->invflags |= XT_CONNTRACK_STATUS;
345 sinfo->flags |= XT_CONNTRACK_STATUS;
346 break;
347 case O_CTEXPIRE:
348 sinfo->expires_min = cb->val.u32_range[0];
349 sinfo->expires_max = cb->val.u32_range[0];
350 if (cb->nvals >= 2)
351 sinfo->expires_max = cb->val.u32_range[1];
352 if (cb->invert)
353 sinfo->invflags |= XT_CONNTRACK_EXPIRES;
354 sinfo->flags |= XT_CONNTRACK_EXPIRES;
355 break;
356 }
357 }
358
conntrack_mt_parse(struct xt_option_call * cb,uint8_t rev)359 static void conntrack_mt_parse(struct xt_option_call *cb, uint8_t rev)
360 {
361 struct xt_conntrack_mtinfo3 *info = cb->data;
362
363 xtables_option_parse(cb);
364 switch (cb->entry->id) {
365 case O_CTSTATE:
366 conntrack_ps_states(info, cb->arg);
367 info->match_flags |= XT_CONNTRACK_STATE;
368 if (cb->invert)
369 info->invert_flags |= XT_CONNTRACK_STATE;
370 break;
371 case O_CTPROTO:
372 info->l4proto = cb->val.protocol;
373 if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
374 xtables_error(PARAMETER_PROBLEM, "conntrack: rule would "
375 "never match protocol");
376
377 info->match_flags |= XT_CONNTRACK_PROTO;
378 if (cb->invert)
379 info->invert_flags |= XT_CONNTRACK_PROTO;
380 break;
381 case O_CTORIGSRC:
382 info->origsrc_addr = cb->val.haddr;
383 info->origsrc_mask = cb->val.hmask;
384 info->match_flags |= XT_CONNTRACK_ORIGSRC;
385 if (cb->invert)
386 info->invert_flags |= XT_CONNTRACK_ORIGSRC;
387 break;
388 case O_CTORIGDST:
389 info->origdst_addr = cb->val.haddr;
390 info->origdst_mask = cb->val.hmask;
391 info->match_flags |= XT_CONNTRACK_ORIGDST;
392 if (cb->invert)
393 info->invert_flags |= XT_CONNTRACK_ORIGDST;
394 break;
395 case O_CTREPLSRC:
396 info->replsrc_addr = cb->val.haddr;
397 info->replsrc_mask = cb->val.hmask;
398 info->match_flags |= XT_CONNTRACK_REPLSRC;
399 if (cb->invert)
400 info->invert_flags |= XT_CONNTRACK_REPLSRC;
401 break;
402 case O_CTREPLDST:
403 info->repldst_addr = cb->val.haddr;
404 info->repldst_mask = cb->val.hmask;
405 info->match_flags |= XT_CONNTRACK_REPLDST;
406 if (cb->invert)
407 info->invert_flags |= XT_CONNTRACK_REPLDST;
408 break;
409 case O_CTSTATUS:
410 conntrack_ps_statuses(info, cb->arg);
411 info->match_flags |= XT_CONNTRACK_STATUS;
412 if (cb->invert)
413 info->invert_flags |= XT_CONNTRACK_STATUS;
414 break;
415 case O_CTEXPIRE:
416 info->expires_min = cb->val.u32_range[0];
417 info->expires_max = cb->val.u32_range[0];
418 if (cb->nvals >= 2)
419 info->expires_max = cb->val.u32_range[1];
420 info->match_flags |= XT_CONNTRACK_EXPIRES;
421 if (cb->invert)
422 info->invert_flags |= XT_CONNTRACK_EXPIRES;
423 break;
424 case O_CTORIGSRCPORT:
425 info->origsrc_port = cb->val.port_range[0];
426 info->origsrc_port_high = cb->val.port_range[cb->nvals >= 2];
427 info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
428 if (cb->invert)
429 info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
430 break;
431 case O_CTORIGDSTPORT:
432 info->origdst_port = cb->val.port_range[0];
433 info->origdst_port_high = cb->val.port_range[cb->nvals >= 2];
434 info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
435 if (cb->invert)
436 info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
437 break;
438 case O_CTREPLSRCPORT:
439 info->replsrc_port = cb->val.port_range[0];
440 info->replsrc_port_high = cb->val.port_range[cb->nvals >= 2];
441 info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
442 if (cb->invert)
443 info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
444 break;
445 case O_CTREPLDSTPORT:
446 info->repldst_port = cb->val.port_range[0];
447 info->repldst_port_high = cb->val.port_range[cb->nvals >= 2];
448 info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
449 if (cb->invert)
450 info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
451 break;
452 case O_CTDIR:
453 if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
454 info->match_flags |= XT_CONNTRACK_DIRECTION;
455 info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
456 } else if (strcasecmp(cb->arg, "REPLY") == 0) {
457 info->match_flags |= XT_CONNTRACK_DIRECTION;
458 info->invert_flags |= XT_CONNTRACK_DIRECTION;
459 } else {
460 xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", cb->arg);
461 }
462 break;
463 }
464 }
465
466 #define cinfo_transform(r, l) \
467 do { \
468 memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \
469 (r)->state_mask = (l)->state_mask; \
470 (r)->status_mask = (l)->status_mask; \
471 } while (false);
472
conntrack1_mt_parse(struct xt_option_call * cb)473 static void conntrack1_mt_parse(struct xt_option_call *cb)
474 {
475 struct xt_conntrack_mtinfo1 *info = cb->data;
476 struct xt_conntrack_mtinfo3 up;
477
478 memset(&up, 0, sizeof(up));
479 cinfo_transform(&up, info);
480 up.origsrc_port_high = up.origsrc_port;
481 up.origdst_port_high = up.origdst_port;
482 up.replsrc_port_high = up.replsrc_port;
483 up.repldst_port_high = up.repldst_port;
484 cb->data = &up;
485 conntrack_mt_parse(cb, 3);
486 if (up.origsrc_port != up.origsrc_port_high ||
487 up.origdst_port != up.origdst_port_high ||
488 up.replsrc_port != up.replsrc_port_high ||
489 up.repldst_port != up.repldst_port_high)
490 xtables_error(PARAMETER_PROBLEM,
491 "conntrack rev 1 does not support port ranges");
492 cinfo_transform(info, &up);
493 cb->data = info;
494 }
495
conntrack2_mt_parse(struct xt_option_call * cb)496 static void conntrack2_mt_parse(struct xt_option_call *cb)
497 {
498 #define cinfo2_transform(r, l) \
499 memcpy((r), (l), offsetof(typeof(*(l)), sizeof(*info));
500
501 struct xt_conntrack_mtinfo2 *info = cb->data;
502 struct xt_conntrack_mtinfo3 up;
503
504 memset(&up, 0, sizeof(up));
505 memcpy(&up, info, sizeof(*info));
506 up.origsrc_port_high = up.origsrc_port;
507 up.origdst_port_high = up.origdst_port;
508 up.replsrc_port_high = up.replsrc_port;
509 up.repldst_port_high = up.repldst_port;
510 cb->data = &up;
511 conntrack_mt_parse(cb, 3);
512 if (up.origsrc_port != up.origsrc_port_high ||
513 up.origdst_port != up.origdst_port_high ||
514 up.replsrc_port != up.replsrc_port_high ||
515 up.repldst_port != up.repldst_port_high)
516 xtables_error(PARAMETER_PROBLEM,
517 "conntrack rev 2 does not support port ranges");
518 memcpy(info, &up, sizeof(*info));
519 cb->data = info;
520 #undef cinfo2_transform
521 }
522
conntrack3_mt_parse(struct xt_option_call * cb)523 static void conntrack3_mt_parse(struct xt_option_call *cb)
524 {
525 conntrack_mt_parse(cb, 3);
526 }
527
conntrack_mt_check(struct xt_fcheck_call * cb)528 static void conntrack_mt_check(struct xt_fcheck_call *cb)
529 {
530 if (cb->xflags == 0)
531 xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option "
532 "is required");
533 }
534
535 static void
print_state(unsigned int statemask)536 print_state(unsigned int statemask)
537 {
538 const char *sep = " ";
539
540 if (statemask & XT_CONNTRACK_STATE_INVALID) {
541 printf("%sINVALID", sep);
542 sep = ",";
543 }
544 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
545 printf("%sNEW", sep);
546 sep = ",";
547 }
548 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
549 printf("%sRELATED", sep);
550 sep = ",";
551 }
552 if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
553 printf("%sESTABLISHED", sep);
554 sep = ",";
555 }
556 if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
557 printf("%sUNTRACKED", sep);
558 sep = ",";
559 }
560 if (statemask & XT_CONNTRACK_STATE_SNAT) {
561 printf("%sSNAT", sep);
562 sep = ",";
563 }
564 if (statemask & XT_CONNTRACK_STATE_DNAT) {
565 printf("%sDNAT", sep);
566 sep = ",";
567 }
568 }
569
570 static void
print_status(unsigned int statusmask)571 print_status(unsigned int statusmask)
572 {
573 const char *sep = " ";
574
575 if (statusmask & IPS_EXPECTED) {
576 printf("%sEXPECTED", sep);
577 sep = ",";
578 }
579 if (statusmask & IPS_SEEN_REPLY) {
580 printf("%sSEEN_REPLY", sep);
581 sep = ",";
582 }
583 if (statusmask & IPS_ASSURED) {
584 printf("%sASSURED", sep);
585 sep = ",";
586 }
587 if (statusmask & IPS_CONFIRMED) {
588 printf("%sCONFIRMED", sep);
589 sep = ",";
590 }
591 if (statusmask == 0)
592 printf("%sNONE", sep);
593 }
594
595 static void
conntrack_dump_addr(const union nf_inet_addr * addr,const union nf_inet_addr * mask,unsigned int family,bool numeric)596 conntrack_dump_addr(const union nf_inet_addr *addr,
597 const union nf_inet_addr *mask,
598 unsigned int family, bool numeric)
599 {
600 if (family == NFPROTO_IPV4) {
601 if (!numeric && addr->ip == 0) {
602 printf(" anywhere");
603 return;
604 }
605 if (numeric)
606 printf(" %s%s",
607 xtables_ipaddr_to_numeric(&addr->in),
608 xtables_ipmask_to_numeric(&mask->in));
609 else
610 printf(" %s%s",
611 xtables_ipaddr_to_anyname(&addr->in),
612 xtables_ipmask_to_numeric(&mask->in));
613 } else if (family == NFPROTO_IPV6) {
614 if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
615 addr->ip6[2] == 0 && addr->ip6[3] == 0) {
616 printf(" anywhere");
617 return;
618 }
619 if (numeric)
620 printf(" %s%s",
621 xtables_ip6addr_to_numeric(&addr->in6),
622 xtables_ip6mask_to_numeric(&mask->in6));
623 else
624 printf(" %s%s",
625 xtables_ip6addr_to_anyname(&addr->in6),
626 xtables_ip6mask_to_numeric(&mask->in6));
627 }
628 }
629
630 static void
print_addr(const struct in_addr * addr,const struct in_addr * mask,int inv,int numeric)631 print_addr(const struct in_addr *addr, const struct in_addr *mask,
632 int inv, int numeric)
633 {
634 char buf[BUFSIZ];
635
636 if (inv)
637 printf(" !");
638
639 if (mask->s_addr == 0L && !numeric)
640 printf(" %s", "anywhere");
641 else {
642 if (numeric)
643 strcpy(buf, xtables_ipaddr_to_numeric(addr));
644 else
645 strcpy(buf, xtables_ipaddr_to_anyname(addr));
646 strcat(buf, xtables_ipmask_to_numeric(mask));
647 printf(" %s", buf);
648 }
649 }
650
651 static void
matchinfo_print(const void * ip,const struct xt_entry_match * match,int numeric,const char * optpfx)652 matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
653 {
654 const struct xt_conntrack_info *sinfo = (const void *)match->data;
655
656 if(sinfo->flags & XT_CONNTRACK_STATE) {
657 if (sinfo->invflags & XT_CONNTRACK_STATE)
658 printf(" !");
659 printf(" %sctstate", optpfx);
660 print_state(sinfo->statemask);
661 }
662
663 if(sinfo->flags & XT_CONNTRACK_PROTO) {
664 if (sinfo->invflags & XT_CONNTRACK_PROTO)
665 printf(" !");
666 printf(" %sctproto", optpfx);
667 printf(" %u", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
668 }
669
670 if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
671 if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
672 printf(" !");
673 printf(" %sctorigsrc", optpfx);
674
675 print_addr(
676 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
677 &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
678 false,
679 numeric);
680 }
681
682 if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
683 if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
684 printf(" !");
685 printf(" %sctorigdst", optpfx);
686
687 print_addr(
688 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
689 &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
690 false,
691 numeric);
692 }
693
694 if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
695 if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
696 printf(" !");
697 printf(" %sctreplsrc", optpfx);
698
699 print_addr(
700 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
701 &sinfo->sipmsk[IP_CT_DIR_REPLY],
702 false,
703 numeric);
704 }
705
706 if(sinfo->flags & XT_CONNTRACK_REPLDST) {
707 if (sinfo->invflags & XT_CONNTRACK_REPLDST)
708 printf(" !");
709 printf(" %sctrepldst", optpfx);
710
711 print_addr(
712 (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
713 &sinfo->dipmsk[IP_CT_DIR_REPLY],
714 false,
715 numeric);
716 }
717
718 if(sinfo->flags & XT_CONNTRACK_STATUS) {
719 if (sinfo->invflags & XT_CONNTRACK_STATUS)
720 printf(" !");
721 printf(" %sctstatus", optpfx);
722 print_status(sinfo->statusmask);
723 }
724
725 if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
726 if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
727 printf(" !");
728 printf(" %sctexpire ", optpfx);
729
730 if (sinfo->expires_max == sinfo->expires_min)
731 printf("%lu", sinfo->expires_min);
732 else
733 printf("%lu:%lu", sinfo->expires_min, sinfo->expires_max);
734 }
735
736 if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
737 if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
738 printf(" %sctdir REPLY", optpfx);
739 else
740 printf(" %sctdir ORIGINAL", optpfx);
741 }
742
743 }
744
745 static void
conntrack_dump_ports(const char * prefix,const char * opt,u_int16_t port_low,u_int16_t port_high)746 conntrack_dump_ports(const char *prefix, const char *opt,
747 u_int16_t port_low, u_int16_t port_high)
748 {
749 if (port_high == 0 || port_low == port_high)
750 printf(" %s%s %u", prefix, opt, port_low);
751 else
752 printf(" %s%s %u:%u", prefix, opt, port_low, port_high);
753 }
754
755 static void
conntrack_dump(const struct xt_conntrack_mtinfo3 * info,const char * prefix,unsigned int family,bool numeric,bool v3)756 conntrack_dump(const struct xt_conntrack_mtinfo3 *info, const char *prefix,
757 unsigned int family, bool numeric, bool v3)
758 {
759 if (info->match_flags & XT_CONNTRACK_STATE) {
760 if (info->invert_flags & XT_CONNTRACK_STATE)
761 printf(" !");
762 printf(" %sctstate", prefix);
763 print_state(info->state_mask);
764 }
765
766 if (info->match_flags & XT_CONNTRACK_PROTO) {
767 if (info->invert_flags & XT_CONNTRACK_PROTO)
768 printf(" !");
769 printf(" %sctproto %u", prefix, info->l4proto);
770 }
771
772 if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
773 if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
774 printf(" !");
775 printf(" %sctorigsrc", prefix);
776 conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
777 family, numeric);
778 }
779
780 if (info->match_flags & XT_CONNTRACK_ORIGDST) {
781 if (info->invert_flags & XT_CONNTRACK_ORIGDST)
782 printf(" !");
783 printf(" %sctorigdst", prefix);
784 conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
785 family, numeric);
786 }
787
788 if (info->match_flags & XT_CONNTRACK_REPLSRC) {
789 if (info->invert_flags & XT_CONNTRACK_REPLSRC)
790 printf(" !");
791 printf(" %sctreplsrc", prefix);
792 conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
793 family, numeric);
794 }
795
796 if (info->match_flags & XT_CONNTRACK_REPLDST) {
797 if (info->invert_flags & XT_CONNTRACK_REPLDST)
798 printf(" !");
799 printf(" %sctrepldst", prefix);
800 conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
801 family, numeric);
802 }
803
804 if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
805 if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
806 printf(" !");
807 conntrack_dump_ports(prefix, "ctorigsrcport",
808 v3 ? info->origsrc_port : ntohs(info->origsrc_port),
809 v3 ? info->origsrc_port_high : 0);
810 }
811
812 if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
813 if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
814 printf(" !");
815 conntrack_dump_ports(prefix, "ctorigdstport",
816 v3 ? info->origdst_port : ntohs(info->origdst_port),
817 v3 ? info->origdst_port_high : 0);
818 }
819
820 if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
821 if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
822 printf(" !");
823 conntrack_dump_ports(prefix, "ctreplsrcport",
824 v3 ? info->replsrc_port : ntohs(info->replsrc_port),
825 v3 ? info->replsrc_port_high : 0);
826 }
827
828 if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
829 if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
830 printf(" !");
831 conntrack_dump_ports(prefix, "ctrepldstport",
832 v3 ? info->repldst_port : ntohs(info->repldst_port),
833 v3 ? info->repldst_port_high : 0);
834 }
835
836 if (info->match_flags & XT_CONNTRACK_STATUS) {
837 if (info->invert_flags & XT_CONNTRACK_STATUS)
838 printf(" !");
839 printf(" %sctstatus", prefix);
840 print_status(info->status_mask);
841 }
842
843 if (info->match_flags & XT_CONNTRACK_EXPIRES) {
844 if (info->invert_flags & XT_CONNTRACK_EXPIRES)
845 printf(" !");
846 printf(" %sctexpire ", prefix);
847
848 if (info->expires_max == info->expires_min)
849 printf("%u", (unsigned int)info->expires_min);
850 else
851 printf("%u:%u", (unsigned int)info->expires_min,
852 (unsigned int)info->expires_max);
853 }
854
855 if (info->match_flags & XT_CONNTRACK_DIRECTION) {
856 if (info->invert_flags & XT_CONNTRACK_DIRECTION)
857 printf(" %sctdir REPLY", prefix);
858 else
859 printf(" %sctdir ORIGINAL", prefix);
860 }
861 }
862
conntrack_print(const void * ip,const struct xt_entry_match * match,int numeric)863 static void conntrack_print(const void *ip, const struct xt_entry_match *match,
864 int numeric)
865 {
866 matchinfo_print(ip, match, numeric, "");
867 }
868
869 static void
conntrack1_mt4_print(const void * ip,const struct xt_entry_match * match,int numeric)870 conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match,
871 int numeric)
872 {
873 const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
874 struct xt_conntrack_mtinfo3 up;
875
876 cinfo_transform(&up, info);
877 conntrack_dump(&up, "", NFPROTO_IPV4, numeric, false);
878 }
879
880 static void
conntrack1_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)881 conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match,
882 int numeric)
883 {
884 const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
885 struct xt_conntrack_mtinfo3 up;
886
887 cinfo_transform(&up, info);
888 conntrack_dump(&up, "", NFPROTO_IPV6, numeric, false);
889 }
890
891 static void
conntrack2_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)892 conntrack2_mt_print(const void *ip, const struct xt_entry_match *match,
893 int numeric)
894 {
895 conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, false);
896 }
897
898 static void
conntrack2_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)899 conntrack2_mt6_print(const void *ip, const struct xt_entry_match *match,
900 int numeric)
901 {
902 conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, false);
903 }
904
905 static void
conntrack3_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)906 conntrack3_mt_print(const void *ip, const struct xt_entry_match *match,
907 int numeric)
908 {
909 conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, true);
910 }
911
912 static void
conntrack3_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)913 conntrack3_mt6_print(const void *ip, const struct xt_entry_match *match,
914 int numeric)
915 {
916 conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, true);
917 }
918
conntrack_save(const void * ip,const struct xt_entry_match * match)919 static void conntrack_save(const void *ip, const struct xt_entry_match *match)
920 {
921 matchinfo_print(ip, match, 1, "--");
922 }
923
conntrack3_mt_save(const void * ip,const struct xt_entry_match * match)924 static void conntrack3_mt_save(const void *ip,
925 const struct xt_entry_match *match)
926 {
927 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, true);
928 }
929
conntrack3_mt6_save(const void * ip,const struct xt_entry_match * match)930 static void conntrack3_mt6_save(const void *ip,
931 const struct xt_entry_match *match)
932 {
933 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, true);
934 }
935
conntrack2_mt_save(const void * ip,const struct xt_entry_match * match)936 static void conntrack2_mt_save(const void *ip,
937 const struct xt_entry_match *match)
938 {
939 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, false);
940 }
941
conntrack2_mt6_save(const void * ip,const struct xt_entry_match * match)942 static void conntrack2_mt6_save(const void *ip,
943 const struct xt_entry_match *match)
944 {
945 conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, false);
946 }
947
948 static void
conntrack1_mt4_save(const void * ip,const struct xt_entry_match * match)949 conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match)
950 {
951 const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
952 struct xt_conntrack_mtinfo3 up;
953
954 cinfo_transform(&up, info);
955 conntrack_dump(&up, "--", NFPROTO_IPV4, true, false);
956 }
957
958 static void
conntrack1_mt6_save(const void * ip,const struct xt_entry_match * match)959 conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match)
960 {
961 const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
962 struct xt_conntrack_mtinfo3 up;
963
964 cinfo_transform(&up, info);
965 conntrack_dump(&up, "--", NFPROTO_IPV6, true, false);
966 }
967
968 static struct xtables_match conntrack_mt_reg[] = {
969 {
970 .version = XTABLES_VERSION,
971 .name = "conntrack",
972 .revision = 0,
973 .family = NFPROTO_IPV4,
974 .size = XT_ALIGN(sizeof(struct xt_conntrack_info)),
975 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
976 .help = conntrack_mt_help,
977 .x6_parse = conntrack_parse,
978 .x6_fcheck = conntrack_mt_check,
979 .print = conntrack_print,
980 .save = conntrack_save,
981 .x6_options = conntrack_mt_opts_v0,
982 },
983 {
984 .version = XTABLES_VERSION,
985 .name = "conntrack",
986 .revision = 1,
987 .family = NFPROTO_IPV4,
988 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
989 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
990 .help = conntrack_mt_help,
991 .x6_parse = conntrack1_mt_parse,
992 .x6_fcheck = conntrack_mt_check,
993 .print = conntrack1_mt4_print,
994 .save = conntrack1_mt4_save,
995 .x6_options = conntrack_mt_opts,
996 },
997 {
998 .version = XTABLES_VERSION,
999 .name = "conntrack",
1000 .revision = 1,
1001 .family = NFPROTO_IPV6,
1002 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1003 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1004 .help = conntrack_mt_help,
1005 .x6_parse = conntrack1_mt_parse,
1006 .x6_fcheck = conntrack_mt_check,
1007 .print = conntrack1_mt6_print,
1008 .save = conntrack1_mt6_save,
1009 .x6_options = conntrack_mt_opts,
1010 },
1011 {
1012 .version = XTABLES_VERSION,
1013 .name = "conntrack",
1014 .revision = 2,
1015 .family = NFPROTO_IPV4,
1016 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1017 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1018 .help = conntrack_mt_help,
1019 .x6_parse = conntrack2_mt_parse,
1020 .x6_fcheck = conntrack_mt_check,
1021 .print = conntrack2_mt_print,
1022 .save = conntrack2_mt_save,
1023 .x6_options = conntrack_mt_opts,
1024 },
1025 {
1026 .version = XTABLES_VERSION,
1027 .name = "conntrack",
1028 .revision = 2,
1029 .family = NFPROTO_IPV6,
1030 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1031 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1032 .help = conntrack_mt_help,
1033 .x6_parse = conntrack2_mt_parse,
1034 .x6_fcheck = conntrack_mt_check,
1035 .print = conntrack2_mt6_print,
1036 .save = conntrack2_mt6_save,
1037 .x6_options = conntrack_mt_opts,
1038 },
1039 {
1040 .version = XTABLES_VERSION,
1041 .name = "conntrack",
1042 .revision = 3,
1043 .family = NFPROTO_IPV4,
1044 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1045 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1046 .help = conntrack_mt_help,
1047 .x6_parse = conntrack3_mt_parse,
1048 .x6_fcheck = conntrack_mt_check,
1049 .print = conntrack3_mt_print,
1050 .save = conntrack3_mt_save,
1051 .x6_options = conntrack_mt_opts,
1052 },
1053 {
1054 .version = XTABLES_VERSION,
1055 .name = "conntrack",
1056 .revision = 3,
1057 .family = NFPROTO_IPV6,
1058 .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1059 .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1060 .help = conntrack_mt_help,
1061 .x6_parse = conntrack3_mt_parse,
1062 .x6_fcheck = conntrack_mt_check,
1063 .print = conntrack3_mt6_print,
1064 .save = conntrack3_mt6_save,
1065 .x6_options = conntrack_mt_opts,
1066 },
1067 };
1068
_init(void)1069 void _init(void)
1070 {
1071 xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
1072 }
1073