1 /*
2 * (C) 2000-2006 by the netfilter coreteam <coreteam@netfilter.org>:
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18 #include "config.h"
19 #include <ctype.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <inttypes.h>
23 #include <netdb.h>
24 #include <stdarg.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/statfs.h>
33 #include <sys/types.h>
34 #include <sys/utsname.h>
35 #include <sys/wait.h>
36 #include <arpa/inet.h>
37 #if defined(HAVE_LINUX_MAGIC_H)
38 # include <linux/magic.h> /* for PROC_SUPER_MAGIC */
39 #elif defined(HAVE_LINUX_PROC_FS_H)
40 # include <linux/proc_fs.h> /* Linux 2.4 */
41 #else
42 # define PROC_SUPER_MAGIC 0x9fa0
43 #endif
44
45 #include <xtables.h>
46 #include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */
47 #include <linux/netfilter_ipv4/ip_tables.h>
48 #include <linux/netfilter_ipv6/ip6_tables.h>
49 #include <libiptc/libxtc.h>
50
51 #ifndef NO_SHARED_LIBS
52 #include <dlfcn.h>
53 #endif
54 #ifndef IPT_SO_GET_REVISION_MATCH /* Old kernel source. */
55 # define IPT_SO_GET_REVISION_MATCH (IPT_BASE_CTL + 2)
56 # define IPT_SO_GET_REVISION_TARGET (IPT_BASE_CTL + 3)
57 #endif
58 #ifndef IP6T_SO_GET_REVISION_MATCH /* Old kernel source. */
59 # define IP6T_SO_GET_REVISION_MATCH 68
60 # define IP6T_SO_GET_REVISION_TARGET 69
61 #endif
62 #include <getopt.h>
63 #include "iptables/internal.h"
64 #include "xshared.h"
65
66 #define NPROTO 255
67
68 #ifndef PROC_SYS_MODPROBE
69 #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
70 #endif
71
72 /* we need this for ip6?tables-restore. ip6?tables-restore.c sets line to the
73 * current line of the input file, in order to give a more precise error
74 * message. ip6?tables itself doesn't need this, so it is initialized to the
75 * magic number of -1 */
76 int line = -1;
77
78 void basic_exit_err(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
79
80 struct xtables_globals *xt_params = NULL;
81
basic_exit_err(enum xtables_exittype status,const char * msg,...)82 void basic_exit_err(enum xtables_exittype status, const char *msg, ...)
83 {
84 va_list args;
85
86 va_start(args, msg);
87 fprintf(stderr, "%s v%s: ", xt_params->program_name, xt_params->program_version);
88 vfprintf(stderr, msg, args);
89 va_end(args);
90 fprintf(stderr, "\n");
91 exit(status);
92 }
93
xtables_free_opts(int unused)94 void xtables_free_opts(int unused)
95 {
96 if (xt_params->opts != xt_params->orig_opts) {
97 free(xt_params->opts);
98 xt_params->opts = NULL;
99 }
100 }
101
xtables_merge_options(struct option * orig_opts,struct option * oldopts,const struct option * newopts,unsigned int * option_offset)102 struct option *xtables_merge_options(struct option *orig_opts,
103 struct option *oldopts,
104 const struct option *newopts,
105 unsigned int *option_offset)
106 {
107 unsigned int num_oold = 0, num_old = 0, num_new = 0, i;
108 struct option *merge, *mp;
109
110 if (newopts == NULL)
111 return oldopts;
112
113 for (num_oold = 0; orig_opts[num_oold].name; num_oold++) ;
114 if (oldopts != NULL)
115 for (num_old = 0; oldopts[num_old].name; num_old++) ;
116 for (num_new = 0; newopts[num_new].name; num_new++) ;
117
118 /*
119 * Since @oldopts also has @orig_opts already (and does so at the
120 * start), skip these entries.
121 */
122 oldopts += num_oold;
123 num_old -= num_oold;
124
125 merge = malloc(sizeof(*mp) * (num_oold + num_old + num_new + 1));
126 if (merge == NULL)
127 return NULL;
128
129 /* Let the base options -[ADI...] have precedence over everything */
130 memcpy(merge, orig_opts, sizeof(*mp) * num_oold);
131 mp = merge + num_oold;
132
133 /* Second, the new options */
134 xt_params->option_offset += XT_OPTION_OFFSET_SCALE;
135 *option_offset = xt_params->option_offset;
136 memcpy(mp, newopts, sizeof(*mp) * num_new);
137
138 for (i = 0; i < num_new; ++i, ++mp)
139 mp->val += *option_offset;
140
141 /* Third, the old options */
142 memcpy(mp, oldopts, sizeof(*mp) * num_old);
143 mp += num_old;
144 xtables_free_opts(0);
145
146 /* Clear trailing entry */
147 memset(mp, 0, sizeof(*mp));
148 return merge;
149 }
150
151 static const struct xtables_afinfo afinfo_ipv4 = {
152 .kmod = "ip_tables",
153 .proc_exists = "/proc/net/ip_tables_names",
154 .libprefix = "libipt_",
155 .family = NFPROTO_IPV4,
156 .ipproto = IPPROTO_IP,
157 .so_rev_match = IPT_SO_GET_REVISION_MATCH,
158 .so_rev_target = IPT_SO_GET_REVISION_TARGET,
159 };
160
161 static const struct xtables_afinfo afinfo_ipv6 = {
162 .kmod = "ip6_tables",
163 .proc_exists = "/proc/net/ip6_tables_names",
164 .libprefix = "libip6t_",
165 .family = NFPROTO_IPV6,
166 .ipproto = IPPROTO_IPV6,
167 .so_rev_match = IP6T_SO_GET_REVISION_MATCH,
168 .so_rev_target = IP6T_SO_GET_REVISION_TARGET,
169 };
170
171 /* Dummy families for arptables-compat and ebtables-compat. Leave structure
172 * fields that we don't use unset.
173 */
174 static const struct xtables_afinfo afinfo_bridge = {
175 .libprefix = "libebt_",
176 .family = NFPROTO_BRIDGE,
177 };
178
179 static const struct xtables_afinfo afinfo_arp = {
180 .libprefix = "libarpt_",
181 .family = NFPROTO_ARP,
182 };
183
184 const struct xtables_afinfo *afinfo;
185
186 /* Search path for Xtables .so files */
187 static const char *xtables_libdir;
188
189 /* the path to command to load kernel module */
190 const char *xtables_modprobe_program;
191
192 /* Keep track of matches/targets pending full registration: linked lists. */
193 struct xtables_match *xtables_pending_matches;
194 struct xtables_target *xtables_pending_targets;
195
196 /* Keep track of fully registered external matches/targets: linked lists. */
197 struct xtables_match *xtables_matches;
198 struct xtables_target *xtables_targets;
199
200 /* Fully register a match/target which was previously partially registered. */
201 static void xtables_fully_register_pending_match(struct xtables_match *me);
202 static void xtables_fully_register_pending_target(struct xtables_target *me);
203
xtables_init(void)204 void xtables_init(void)
205 {
206 xtables_libdir = getenv("XTABLES_LIBDIR");
207 if (xtables_libdir != NULL)
208 return;
209 xtables_libdir = getenv("IPTABLES_LIB_DIR");
210 if (xtables_libdir != NULL) {
211 fprintf(stderr, "IPTABLES_LIB_DIR is deprecated, "
212 "use XTABLES_LIBDIR.\n");
213 return;
214 }
215 /*
216 * Well yes, IP6TABLES_LIB_DIR is of lower priority over
217 * IPTABLES_LIB_DIR since this moved to libxtables; I think that is ok
218 * for these env vars are deprecated anyhow, and in light of the
219 * (shared) libxt_*.so files, makes less sense to have
220 * IPTABLES_LIB_DIR != IP6TABLES_LIB_DIR.
221 */
222 xtables_libdir = getenv("IP6TABLES_LIB_DIR");
223 if (xtables_libdir != NULL) {
224 fprintf(stderr, "IP6TABLES_LIB_DIR is deprecated, "
225 "use XTABLES_LIBDIR.\n");
226 return;
227 }
228 xtables_libdir = XTABLES_LIBDIR;
229 }
230
xtables_set_nfproto(uint8_t nfproto)231 void xtables_set_nfproto(uint8_t nfproto)
232 {
233 switch (nfproto) {
234 case NFPROTO_IPV4:
235 afinfo = &afinfo_ipv4;
236 break;
237 case NFPROTO_IPV6:
238 afinfo = &afinfo_ipv6;
239 break;
240 case NFPROTO_BRIDGE:
241 afinfo = &afinfo_bridge;
242 break;
243 case NFPROTO_ARP:
244 afinfo = &afinfo_arp;
245 break;
246 default:
247 fprintf(stderr, "libxtables: unhandled NFPROTO in %s\n",
248 __func__);
249 }
250 }
251
252 /**
253 * xtables_set_params - set the global parameters used by xtables
254 * @xtp: input xtables_globals structure
255 *
256 * The app is expected to pass a valid xtables_globals data-filled
257 * with proper values
258 * @xtp cannot be NULL
259 *
260 * Returns -1 on failure to set and 0 on success
261 */
xtables_set_params(struct xtables_globals * xtp)262 int xtables_set_params(struct xtables_globals *xtp)
263 {
264 if (!xtp) {
265 fprintf(stderr, "%s: Illegal global params\n",__func__);
266 return -1;
267 }
268
269 xt_params = xtp;
270
271 if (!xt_params->exit_err)
272 xt_params->exit_err = basic_exit_err;
273
274 return 0;
275 }
276
xtables_init_all(struct xtables_globals * xtp,uint8_t nfproto)277 int xtables_init_all(struct xtables_globals *xtp, uint8_t nfproto)
278 {
279 xtables_init();
280 xtables_set_nfproto(nfproto);
281 return xtables_set_params(xtp);
282 }
283
284 /**
285 * xtables_*alloc - wrappers that exit on failure
286 */
xtables_calloc(size_t count,size_t size)287 void *xtables_calloc(size_t count, size_t size)
288 {
289 void *p;
290
291 if ((p = calloc(count, size)) == NULL) {
292 perror("ip[6]tables: calloc failed");
293 exit(1);
294 }
295
296 return p;
297 }
298
xtables_malloc(size_t size)299 void *xtables_malloc(size_t size)
300 {
301 void *p;
302
303 if ((p = malloc(size)) == NULL) {
304 perror("ip[6]tables: malloc failed");
305 exit(1);
306 }
307
308 return p;
309 }
310
xtables_realloc(void * ptr,size_t size)311 void *xtables_realloc(void *ptr, size_t size)
312 {
313 void *p;
314
315 if ((p = realloc(ptr, size)) == NULL) {
316 perror("ip[6]tables: realloc failed");
317 exit(1);
318 }
319
320 return p;
321 }
322
get_modprobe(void)323 static char *get_modprobe(void)
324 {
325 int procfile;
326 char *ret;
327 int count;
328
329 procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
330 if (procfile < 0)
331 return NULL;
332 if (fcntl(procfile, F_SETFD, FD_CLOEXEC) == -1) {
333 fprintf(stderr, "Could not set close on exec: %s\n",
334 strerror(errno));
335 exit(1);
336 }
337
338 ret = malloc(PATH_MAX);
339 if (ret) {
340 count = read(procfile, ret, PATH_MAX);
341 if (count > 0 && count < PATH_MAX)
342 {
343 if (ret[count - 1] == '\n')
344 ret[count - 1] = '\0';
345 else
346 ret[count] = '\0';
347 close(procfile);
348 return ret;
349 }
350 }
351 free(ret);
352 close(procfile);
353 return NULL;
354 }
355
xtables_insmod(const char * modname,const char * modprobe,bool quiet)356 int xtables_insmod(const char *modname, const char *modprobe, bool quiet)
357 {
358 char *buf = NULL;
359 char *argv[4];
360 int status;
361
362 /* If they don't explicitly set it, read out of kernel */
363 if (!modprobe) {
364 buf = get_modprobe();
365 if (!buf)
366 return -1;
367 modprobe = buf;
368 }
369
370 argv[0] = (char *)modprobe;
371 argv[1] = (char *)modname;
372 argv[2] = quiet ? "-q" : NULL;
373 argv[3] = NULL;
374
375 /*
376 * Need to flush the buffer, or the child may output it again
377 * when switching the program thru execv.
378 */
379 fflush(stdout);
380
381 switch (vfork()) {
382 case 0:
383 execv(argv[0], argv);
384
385 /* not usually reached */
386 _exit(1);
387 case -1:
388 free(buf);
389 return -1;
390
391 default: /* parent */
392 wait(&status);
393 }
394
395 free(buf);
396 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
397 return 0;
398 return -1;
399 }
400
401 /* return true if a given file exists within procfs */
proc_file_exists(const char * filename)402 static bool proc_file_exists(const char *filename)
403 {
404 struct stat s;
405 struct statfs f;
406
407 if (lstat(filename, &s))
408 return false;
409 if (!S_ISREG(s.st_mode))
410 return false;
411 if (statfs(filename, &f))
412 return false;
413 if (f.f_type != PROC_SUPER_MAGIC)
414 return false;
415 return true;
416 }
417
xtables_load_ko(const char * modprobe,bool quiet)418 int xtables_load_ko(const char *modprobe, bool quiet)
419 {
420 static bool loaded = false;
421 int ret;
422
423 if (loaded)
424 return 0;
425
426 if (proc_file_exists(afinfo->proc_exists)) {
427 loaded = true;
428 return 0;
429 };
430
431 ret = xtables_insmod(afinfo->kmod, modprobe, quiet);
432 if (ret == 0)
433 loaded = true;
434
435 return ret;
436 }
437
438 /**
439 * xtables_strtou{i,l} - string to number conversion
440 * @s: input string
441 * @end: like strtoul's "end" pointer
442 * @value: pointer for result
443 * @min: minimum accepted value
444 * @max: maximum accepted value
445 *
446 * If @end is NULL, we assume the caller wants a "strict strtoul", and hence
447 * "15a" is rejected.
448 * In either case, the value obtained is compared for min-max compliance.
449 * Base is always 0, i.e. autodetect depending on @s.
450 *
451 * Returns true/false whether number was accepted. On failure, *value has
452 * undefined contents.
453 */
xtables_strtoul(const char * s,char ** end,uintmax_t * value,uintmax_t min,uintmax_t max)454 bool xtables_strtoul(const char *s, char **end, uintmax_t *value,
455 uintmax_t min, uintmax_t max)
456 {
457 uintmax_t v;
458 const char *p;
459 char *my_end;
460
461 errno = 0;
462 /* Since strtoul allows leading minus, we have to check for ourself. */
463 for (p = s; isspace(*p); ++p)
464 ;
465 if (*p == '-')
466 return false;
467 v = strtoumax(s, &my_end, 0);
468 if (my_end == s)
469 return false;
470 if (end != NULL)
471 *end = my_end;
472
473 if (errno != ERANGE && min <= v && (max == 0 || v <= max)) {
474 if (value != NULL)
475 *value = v;
476 if (end == NULL)
477 return *my_end == '\0';
478 return true;
479 }
480
481 return false;
482 }
483
xtables_strtoui(const char * s,char ** end,unsigned int * value,unsigned int min,unsigned int max)484 bool xtables_strtoui(const char *s, char **end, unsigned int *value,
485 unsigned int min, unsigned int max)
486 {
487 uintmax_t v;
488 bool ret;
489
490 ret = xtables_strtoul(s, end, &v, min, max);
491 if (value != NULL)
492 *value = v;
493 return ret;
494 }
495
xtables_service_to_port(const char * name,const char * proto)496 int xtables_service_to_port(const char *name, const char *proto)
497 {
498 struct servent *service;
499
500 if ((service = getservbyname(name, proto)) != NULL)
501 return ntohs((unsigned short) service->s_port);
502
503 return -1;
504 }
505
xtables_parse_port(const char * port,const char * proto)506 uint16_t xtables_parse_port(const char *port, const char *proto)
507 {
508 unsigned int portnum;
509
510 if (xtables_strtoui(port, NULL, &portnum, 0, UINT16_MAX) ||
511 (portnum = xtables_service_to_port(port, proto)) != (unsigned)-1)
512 return portnum;
513
514 xt_params->exit_err(PARAMETER_PROBLEM,
515 "invalid port/service `%s' specified", port);
516 }
517
xtables_parse_interface(const char * arg,char * vianame,unsigned char * mask)518 void xtables_parse_interface(const char *arg, char *vianame,
519 unsigned char *mask)
520 {
521 unsigned int vialen = strlen(arg);
522 unsigned int i;
523
524 memset(mask, 0, IFNAMSIZ);
525 memset(vianame, 0, IFNAMSIZ);
526
527 if (vialen + 1 > IFNAMSIZ)
528 xt_params->exit_err(PARAMETER_PROBLEM,
529 "interface name `%s' must be shorter than IFNAMSIZ"
530 " (%i)", arg, IFNAMSIZ-1);
531
532 strcpy(vianame, arg);
533 if (vialen == 0)
534 return;
535 else if (vianame[vialen - 1] == '+') {
536 memset(mask, 0xFF, vialen - 1);
537 /* Don't remove `+' here! -HW */
538 } else {
539 /* Include nul-terminator in match */
540 memset(mask, 0xFF, vialen + 1);
541 for (i = 0; vianame[i]; i++) {
542 if (vianame[i] == '/' ||
543 vianame[i] == ' ') {
544 fprintf(stderr,
545 "Warning: weird character in interface"
546 " `%s' ('/' and ' ' are not allowed by the kernel).\n",
547 vianame);
548 break;
549 }
550 }
551 }
552 }
553
554 #ifndef NO_SHARED_LIBS
load_extension(const char * search_path,const char * af_prefix,const char * name,bool is_target)555 static void *load_extension(const char *search_path, const char *af_prefix,
556 const char *name, bool is_target)
557 {
558 const char *all_prefixes[] = {af_prefix, "libxt_", NULL};
559 const char **prefix;
560 const char *dir = search_path, *next;
561 void *ptr = NULL;
562 struct stat sb;
563 char path[256];
564
565 do {
566 next = strchr(dir, ':');
567 if (next == NULL)
568 next = dir + strlen(dir);
569
570 for (prefix = all_prefixes; *prefix != NULL; ++prefix) {
571 snprintf(path, sizeof(path), "%.*s/%s%s.so",
572 (unsigned int)(next - dir), dir,
573 *prefix, name);
574
575 if (stat(path, &sb) != 0) {
576 if (errno == ENOENT)
577 continue;
578 fprintf(stderr, "%s: %s\n", path,
579 strerror(errno));
580 return NULL;
581 }
582 if (dlopen(path, RTLD_NOW) == NULL) {
583 fprintf(stderr, "%s: %s\n", path, dlerror());
584 break;
585 }
586
587 if (is_target)
588 ptr = xtables_find_target(name, XTF_DONT_LOAD);
589 else
590 ptr = xtables_find_match(name,
591 XTF_DONT_LOAD, NULL);
592
593 if (ptr != NULL)
594 return ptr;
595
596 errno = ENOENT;
597 return NULL;
598 }
599 dir = next + 1;
600 } while (*next != '\0');
601
602 return NULL;
603 }
604 #endif
605
extension_cmp(const char * name1,const char * name2,uint32_t family)606 static bool extension_cmp(const char *name1, const char *name2, uint32_t family)
607 {
608 if (strcmp(name1, name2) == 0 &&
609 (family == afinfo->family ||
610 family == NFPROTO_UNSPEC))
611 return true;
612
613 return false;
614 }
615
616 struct xtables_match *
xtables_find_match(const char * name,enum xtables_tryload tryload,struct xtables_rule_match ** matches)617 xtables_find_match(const char *name, enum xtables_tryload tryload,
618 struct xtables_rule_match **matches)
619 {
620 struct xtables_match **dptr;
621 struct xtables_match *ptr;
622 const char *icmp6 = "icmp6";
623
624 if (strlen(name) >= XT_EXTENSION_MAXNAMELEN)
625 xtables_error(PARAMETER_PROBLEM,
626 "Invalid match name \"%s\" (%u chars max)",
627 name, XT_EXTENSION_MAXNAMELEN - 1);
628
629 /* This is ugly as hell. Nonetheless, there is no way of changing
630 * this without hurting backwards compatibility */
631 if ( (strcmp(name,"icmpv6") == 0) ||
632 (strcmp(name,"ipv6-icmp") == 0) ||
633 (strcmp(name,"icmp6") == 0) )
634 name = icmp6;
635
636 /* Trigger delayed initialization */
637 for (dptr = &xtables_pending_matches; *dptr; ) {
638 if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) {
639 ptr = *dptr;
640 *dptr = (*dptr)->next;
641 ptr->next = NULL;
642 xtables_fully_register_pending_match(ptr);
643 } else {
644 dptr = &((*dptr)->next);
645 }
646 }
647
648 for (ptr = xtables_matches; ptr; ptr = ptr->next) {
649 if (extension_cmp(name, ptr->name, ptr->family)) {
650 struct xtables_match *clone;
651
652 /* First match of this type: */
653 if (ptr->m == NULL)
654 break;
655
656 /* Second and subsequent clones */
657 clone = xtables_malloc(sizeof(struct xtables_match));
658 memcpy(clone, ptr, sizeof(struct xtables_match));
659 clone->udata = NULL;
660 clone->mflags = 0;
661 /* This is a clone: */
662 clone->next = clone;
663
664 ptr = clone;
665 break;
666 }
667 }
668
669 #ifndef NO_SHARED_LIBS
670 if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
671 ptr = load_extension(xtables_libdir, afinfo->libprefix,
672 name, false);
673
674 if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
675 xt_params->exit_err(PARAMETER_PROBLEM,
676 "Couldn't load match `%s':%s\n",
677 name, strerror(errno));
678 }
679 #else
680 if (ptr && !ptr->loaded) {
681 if (tryload != XTF_DONT_LOAD)
682 ptr->loaded = 1;
683 else
684 ptr = NULL;
685 }
686 if(!ptr && (tryload == XTF_LOAD_MUST_SUCCEED)) {
687 xt_params->exit_err(PARAMETER_PROBLEM,
688 "Couldn't find match `%s'\n", name);
689 }
690 #endif
691
692 if (ptr && matches) {
693 struct xtables_rule_match **i;
694 struct xtables_rule_match *newentry;
695
696 newentry = xtables_malloc(sizeof(struct xtables_rule_match));
697
698 for (i = matches; *i; i = &(*i)->next) {
699 if (extension_cmp(name, (*i)->match->name,
700 (*i)->match->family))
701 (*i)->completed = true;
702 }
703 newentry->match = ptr;
704 newentry->completed = false;
705 newentry->next = NULL;
706 *i = newentry;
707 }
708
709 return ptr;
710 }
711
712 struct xtables_target *
xtables_find_target(const char * name,enum xtables_tryload tryload)713 xtables_find_target(const char *name, enum xtables_tryload tryload)
714 {
715 struct xtables_target **dptr;
716 struct xtables_target *ptr;
717
718 /* Standard target? */
719 if (strcmp(name, "") == 0
720 || strcmp(name, XTC_LABEL_ACCEPT) == 0
721 || strcmp(name, XTC_LABEL_DROP) == 0
722 || strcmp(name, XTC_LABEL_QUEUE) == 0
723 || strcmp(name, XTC_LABEL_RETURN) == 0)
724 name = "standard";
725
726 /* Trigger delayed initialization */
727 for (dptr = &xtables_pending_targets; *dptr; ) {
728 if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) {
729 ptr = *dptr;
730 *dptr = (*dptr)->next;
731 ptr->next = NULL;
732 xtables_fully_register_pending_target(ptr);
733 } else {
734 dptr = &((*dptr)->next);
735 }
736 }
737
738 for (ptr = xtables_targets; ptr; ptr = ptr->next) {
739 if (extension_cmp(name, ptr->name, ptr->family))
740 break;
741 }
742
743 #ifndef NO_SHARED_LIBS
744 if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
745 ptr = load_extension(xtables_libdir, afinfo->libprefix,
746 name, true);
747
748 if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
749 xt_params->exit_err(PARAMETER_PROBLEM,
750 "Couldn't load target `%s':%s\n",
751 name, strerror(errno));
752 }
753 #else
754 if (ptr && !ptr->loaded) {
755 if (tryload != XTF_DONT_LOAD)
756 ptr->loaded = 1;
757 else
758 ptr = NULL;
759 }
760 if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED) {
761 xt_params->exit_err(PARAMETER_PROBLEM,
762 "Couldn't find target `%s'\n", name);
763 }
764 #endif
765
766 if (ptr)
767 ptr->used = 1;
768
769 return ptr;
770 }
771
xtables_compatible_revision(const char * name,uint8_t revision,int opt)772 int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
773 {
774 struct xt_get_revision rev;
775 socklen_t s = sizeof(rev);
776 int max_rev, sockfd;
777
778 sockfd = socket(afinfo->family, SOCK_RAW, IPPROTO_RAW);
779 if (sockfd < 0) {
780 if (errno == EPERM) {
781 /* revision 0 is always supported. */
782 if (revision != 0)
783 fprintf(stderr, "%s: Could not determine whether "
784 "revision %u is supported, "
785 "assuming it is.\n",
786 name, revision);
787 return 1;
788 }
789 fprintf(stderr, "Could not open socket to kernel: %s\n",
790 strerror(errno));
791 exit(1);
792 }
793
794 if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
795 fprintf(stderr, "Could not set close on exec: %s\n",
796 strerror(errno));
797 exit(1);
798 }
799
800 xtables_load_ko(xtables_modprobe_program, true);
801
802 strcpy(rev.name, name);
803 rev.revision = revision;
804
805 max_rev = getsockopt(sockfd, afinfo->ipproto, opt, &rev, &s);
806 if (max_rev < 0) {
807 /* Definitely don't support this? */
808 if (errno == ENOENT || errno == EPROTONOSUPPORT) {
809 close(sockfd);
810 return 0;
811 } else if (errno == ENOPROTOOPT) {
812 close(sockfd);
813 /* Assume only revision 0 support (old kernel) */
814 return (revision == 0);
815 } else {
816 fprintf(stderr, "getsockopt failed strangely: %s\n",
817 strerror(errno));
818 exit(1);
819 }
820 }
821 close(sockfd);
822 return 1;
823 }
824
825
compatible_match_revision(const char * name,uint8_t revision)826 static int compatible_match_revision(const char *name, uint8_t revision)
827 {
828 return xt_params->compat_rev(name, revision, afinfo->so_rev_match);
829 }
830
compatible_target_revision(const char * name,uint8_t revision)831 static int compatible_target_revision(const char *name, uint8_t revision)
832 {
833 return xt_params->compat_rev(name, revision, afinfo->so_rev_target);
834 }
835
xtables_check_options(const char * name,const struct option * opt)836 static void xtables_check_options(const char *name, const struct option *opt)
837 {
838 for (; opt->name != NULL; ++opt)
839 if (opt->val < 0 || opt->val >= XT_OPTION_OFFSET_SCALE) {
840 fprintf(stderr, "%s: Extension %s uses invalid "
841 "option value %d\n",xt_params->program_name,
842 name, opt->val);
843 exit(1);
844 }
845 }
846
xtables_register_match(struct xtables_match * me)847 void xtables_register_match(struct xtables_match *me)
848 {
849 if (me->version == NULL) {
850 fprintf(stderr, "%s: match %s<%u> is missing a version\n",
851 xt_params->program_name, me->name, me->revision);
852 exit(1);
853 }
854 if (strcmp(me->version, XTABLES_VERSION) != 0) {
855 fprintf(stderr, "%s: match \"%s\" has version \"%s\", "
856 "but \"%s\" is required.\n",
857 xt_params->program_name, me->name,
858 me->version, XTABLES_VERSION);
859 exit(1);
860 }
861
862 if (strlen(me->name) >= XT_EXTENSION_MAXNAMELEN) {
863 fprintf(stderr, "%s: match `%s' has invalid name\n",
864 xt_params->program_name, me->name);
865 exit(1);
866 }
867
868 if (me->family >= NPROTO) {
869 fprintf(stderr,
870 "%s: BUG: match %s has invalid protocol family\n",
871 xt_params->program_name, me->name);
872 exit(1);
873 }
874
875 if (me->x6_options != NULL)
876 xtables_option_metavalidate(me->name, me->x6_options);
877 if (me->extra_opts != NULL)
878 xtables_check_options(me->name, me->extra_opts);
879
880 /* ignore not interested match */
881 if (me->family != afinfo->family && me->family != AF_UNSPEC)
882 return;
883
884 /* place on linked list of matches pending full registration */
885 me->next = xtables_pending_matches;
886 xtables_pending_matches = me;
887 }
888
889 /**
890 * Compare two actions for their preference
891 * @a: one action
892 * @b: another
893 *
894 * Like strcmp, returns a negative number if @a is less preferred than @b,
895 * positive number if @a is more preferred than @b, or zero if equally
896 * preferred.
897 */
898 static int
xtables_mt_prefer(bool a_alias,unsigned int a_rev,unsigned int a_fam,bool b_alias,unsigned int b_rev,unsigned int b_fam)899 xtables_mt_prefer(bool a_alias, unsigned int a_rev, unsigned int a_fam,
900 bool b_alias, unsigned int b_rev, unsigned int b_fam)
901 {
902 /*
903 * Alias ranks higher than no alias.
904 * (We want the new action to be used whenever possible.)
905 */
906 if (!a_alias && b_alias)
907 return -1;
908 if (a_alias && !b_alias)
909 return 1;
910
911 /* Higher revision ranks higher. */
912 if (a_rev < b_rev)
913 return -1;
914 if (a_rev > b_rev)
915 return 1;
916
917 /* NFPROTO_<specific> ranks higher than NFPROTO_UNSPEC. */
918 if (a_fam == NFPROTO_UNSPEC && b_fam != NFPROTO_UNSPEC)
919 return -1;
920 if (a_fam != NFPROTO_UNSPEC && b_fam == NFPROTO_UNSPEC)
921 return 1;
922
923 /* Must be the same thing. */
924 return 0;
925 }
926
xtables_match_prefer(const struct xtables_match * a,const struct xtables_match * b)927 static int xtables_match_prefer(const struct xtables_match *a,
928 const struct xtables_match *b)
929 {
930 return xtables_mt_prefer(a->real_name != NULL,
931 a->revision, a->family,
932 b->real_name != NULL,
933 b->revision, b->family);
934 }
935
xtables_target_prefer(const struct xtables_target * a,const struct xtables_target * b)936 static int xtables_target_prefer(const struct xtables_target *a,
937 const struct xtables_target *b)
938 {
939 /*
940 * Note that if x->real_name==NULL, it will be set to x->name in
941 * xtables_register_*; the direct pointer comparison here is therefore
942 * legitimate to detect an alias.
943 */
944 return xtables_mt_prefer(a->real_name != NULL,
945 a->revision, a->family,
946 b->real_name != NULL,
947 b->revision, b->family);
948 }
949
xtables_fully_register_pending_match(struct xtables_match * me)950 static void xtables_fully_register_pending_match(struct xtables_match *me)
951 {
952 struct xtables_match **i, *old;
953 const char *rn;
954 int compare;
955
956 old = xtables_find_match(me->name, XTF_DURING_LOAD, NULL);
957 if (old) {
958 compare = xtables_match_prefer(old, me);
959 if (compare == 0) {
960 fprintf(stderr,
961 "%s: match `%s' already registered.\n",
962 xt_params->program_name, me->name);
963 exit(1);
964 }
965
966 /* Now we have two (or more) options, check compatibility. */
967 rn = (old->real_name != NULL) ? old->real_name : old->name;
968 if (compare > 0 &&
969 compatible_match_revision(rn, old->revision))
970 return;
971
972 /* See if new match can be used. */
973 rn = (me->real_name != NULL) ? me->real_name : me->name;
974 if (!compatible_match_revision(rn, me->revision))
975 return;
976
977 /* Delete old one. */
978 for (i = &xtables_matches; *i!=old; i = &(*i)->next);
979 *i = old->next;
980 }
981
982 if (me->size != XT_ALIGN(me->size)) {
983 fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
984 xt_params->program_name, me->name,
985 (unsigned int)me->size);
986 exit(1);
987 }
988
989 /* Append to list. */
990 for (i = &xtables_matches; *i; i = &(*i)->next);
991 me->next = NULL;
992 *i = me;
993
994 me->m = NULL;
995 me->mflags = 0;
996 }
997
xtables_register_matches(struct xtables_match * match,unsigned int n)998 void xtables_register_matches(struct xtables_match *match, unsigned int n)
999 {
1000 do {
1001 xtables_register_match(&match[--n]);
1002 } while (n > 0);
1003 }
1004
xtables_register_target(struct xtables_target * me)1005 void xtables_register_target(struct xtables_target *me)
1006 {
1007 if (me->version == NULL) {
1008 fprintf(stderr, "%s: target %s<%u> is missing a version\n",
1009 xt_params->program_name, me->name, me->revision);
1010 exit(1);
1011 }
1012 if (strcmp(me->version, XTABLES_VERSION) != 0) {
1013 fprintf(stderr, "%s: target \"%s\" has version \"%s\", "
1014 "but \"%s\" is required.\n",
1015 xt_params->program_name, me->name,
1016 me->version, XTABLES_VERSION);
1017 exit(1);
1018 }
1019
1020 if (strlen(me->name) >= XT_EXTENSION_MAXNAMELEN) {
1021 fprintf(stderr, "%s: target `%s' has invalid name\n",
1022 xt_params->program_name, me->name);
1023 exit(1);
1024 }
1025
1026 if (me->family >= NPROTO) {
1027 fprintf(stderr,
1028 "%s: BUG: target %s has invalid protocol family\n",
1029 xt_params->program_name, me->name);
1030 exit(1);
1031 }
1032
1033 if (me->x6_options != NULL)
1034 xtables_option_metavalidate(me->name, me->x6_options);
1035 if (me->extra_opts != NULL)
1036 xtables_check_options(me->name, me->extra_opts);
1037
1038 /* ignore not interested target */
1039 if (me->family != afinfo->family && me->family != AF_UNSPEC)
1040 return;
1041
1042 /* place on linked list of targets pending full registration */
1043 me->next = xtables_pending_targets;
1044 xtables_pending_targets = me;
1045 }
1046
xtables_fully_register_pending_target(struct xtables_target * me)1047 static void xtables_fully_register_pending_target(struct xtables_target *me)
1048 {
1049 struct xtables_target *old;
1050 const char *rn;
1051 int compare;
1052
1053 old = xtables_find_target(me->name, XTF_DURING_LOAD);
1054 if (old) {
1055 struct xtables_target **i;
1056
1057 compare = xtables_target_prefer(old, me);
1058 if (compare == 0) {
1059 fprintf(stderr,
1060 "%s: target `%s' already registered.\n",
1061 xt_params->program_name, me->name);
1062 exit(1);
1063 }
1064
1065 /* Now we have two (or more) options, check compatibility. */
1066 rn = (old->real_name != NULL) ? old->real_name : old->name;
1067 if (compare > 0 &&
1068 compatible_target_revision(rn, old->revision))
1069 return;
1070
1071 /* See if new target can be used. */
1072 rn = (me->real_name != NULL) ? me->real_name : me->name;
1073 if (!compatible_target_revision(rn, me->revision))
1074 return;
1075
1076 /* Delete old one. */
1077 for (i = &xtables_targets; *i!=old; i = &(*i)->next);
1078 *i = old->next;
1079 }
1080
1081 if (me->size != XT_ALIGN(me->size)) {
1082 fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
1083 xt_params->program_name, me->name,
1084 (unsigned int)me->size);
1085 exit(1);
1086 }
1087
1088 /* Prepend to list. */
1089 me->next = xtables_targets;
1090 xtables_targets = me;
1091 me->t = NULL;
1092 me->tflags = 0;
1093 }
1094
xtables_register_targets(struct xtables_target * target,unsigned int n)1095 void xtables_register_targets(struct xtables_target *target, unsigned int n)
1096 {
1097 do {
1098 xtables_register_target(&target[--n]);
1099 } while (n > 0);
1100 }
1101
1102 /* receives a list of xtables_rule_match, release them */
xtables_rule_matches_free(struct xtables_rule_match ** matches)1103 void xtables_rule_matches_free(struct xtables_rule_match **matches)
1104 {
1105 struct xtables_rule_match *matchp, *tmp;
1106
1107 for (matchp = *matches; matchp;) {
1108 tmp = matchp->next;
1109 if (matchp->match->m) {
1110 free(matchp->match->m);
1111 matchp->match->m = NULL;
1112 }
1113 if (matchp->match == matchp->match->next) {
1114 free(matchp->match);
1115 matchp->match = NULL;
1116 }
1117 free(matchp);
1118 matchp = tmp;
1119 }
1120
1121 *matches = NULL;
1122 }
1123
1124 /**
1125 * xtables_param_act - act on condition
1126 * @status: a constant from enum xtables_exittype
1127 *
1128 * %XTF_ONLY_ONCE: print error message that option may only be used once.
1129 * @p1: module name (e.g. "mark")
1130 * @p2(...): option in conflict (e.g. "--mark")
1131 * @p3(...): condition to match on (see extensions/ for examples)
1132 *
1133 * %XTF_NO_INVERT: option does not support inversion
1134 * @p1: module name
1135 * @p2: option in conflict
1136 * @p3: condition to match on
1137 *
1138 * %XTF_BAD_VALUE: bad value for option
1139 * @p1: module name
1140 * @p2: option with which the problem occurred (e.g. "--mark")
1141 * @p3: string the user passed in (e.g. "99999999999999")
1142 *
1143 * %XTF_ONE_ACTION: two mutually exclusive actions have been specified
1144 * @p1: module name
1145 *
1146 * Displays an error message and exits the program.
1147 */
xtables_param_act(unsigned int status,const char * p1,...)1148 void xtables_param_act(unsigned int status, const char *p1, ...)
1149 {
1150 const char *p2, *p3;
1151 va_list args;
1152 bool b;
1153
1154 va_start(args, p1);
1155
1156 switch (status) {
1157 case XTF_ONLY_ONCE:
1158 p2 = va_arg(args, const char *);
1159 b = va_arg(args, unsigned int);
1160 if (!b) {
1161 va_end(args);
1162 return;
1163 }
1164 xt_params->exit_err(PARAMETER_PROBLEM,
1165 "%s: \"%s\" option may only be specified once",
1166 p1, p2);
1167 break;
1168 case XTF_NO_INVERT:
1169 p2 = va_arg(args, const char *);
1170 b = va_arg(args, unsigned int);
1171 if (!b) {
1172 va_end(args);
1173 return;
1174 }
1175 xt_params->exit_err(PARAMETER_PROBLEM,
1176 "%s: \"%s\" option cannot be inverted", p1, p2);
1177 break;
1178 case XTF_BAD_VALUE:
1179 p2 = va_arg(args, const char *);
1180 p3 = va_arg(args, const char *);
1181 xt_params->exit_err(PARAMETER_PROBLEM,
1182 "%s: Bad value for \"%s\" option: \"%s\"",
1183 p1, p2, p3);
1184 break;
1185 case XTF_ONE_ACTION:
1186 b = va_arg(args, unsigned int);
1187 if (!b) {
1188 va_end(args);
1189 return;
1190 }
1191 xt_params->exit_err(PARAMETER_PROBLEM,
1192 "%s: At most one action is possible", p1);
1193 break;
1194 default:
1195 xt_params->exit_err(status, p1, args);
1196 break;
1197 }
1198
1199 va_end(args);
1200 }
1201
xtables_ipaddr_to_numeric(const struct in_addr * addrp)1202 const char *xtables_ipaddr_to_numeric(const struct in_addr *addrp)
1203 {
1204 static char buf[20];
1205 const unsigned char *bytep = (const void *)&addrp->s_addr;
1206
1207 sprintf(buf, "%u.%u.%u.%u", bytep[0], bytep[1], bytep[2], bytep[3]);
1208 return buf;
1209 }
1210
ipaddr_to_host(const struct in_addr * addr)1211 static const char *ipaddr_to_host(const struct in_addr *addr)
1212 {
1213 static char hostname[NI_MAXHOST];
1214 struct sockaddr_in saddr = {
1215 .sin_family = AF_INET,
1216 .sin_addr = *addr,
1217 };
1218 int err;
1219
1220
1221 err = getnameinfo((const void *)&saddr, sizeof(struct sockaddr_in),
1222 hostname, sizeof(hostname) - 1, NULL, 0, 0);
1223 if (err != 0)
1224 return NULL;
1225
1226 return hostname;
1227 }
1228
ipaddr_to_network(const struct in_addr * addr)1229 static const char *ipaddr_to_network(const struct in_addr *addr)
1230 {
1231 struct netent *net;
1232
1233 if ((net = getnetbyaddr(ntohl(addr->s_addr), AF_INET)) != NULL)
1234 return net->n_name;
1235
1236 return NULL;
1237 }
1238
xtables_ipaddr_to_anyname(const struct in_addr * addr)1239 const char *xtables_ipaddr_to_anyname(const struct in_addr *addr)
1240 {
1241 const char *name;
1242
1243 if ((name = ipaddr_to_host(addr)) != NULL ||
1244 (name = ipaddr_to_network(addr)) != NULL)
1245 return name;
1246
1247 return xtables_ipaddr_to_numeric(addr);
1248 }
1249
xtables_ipmask_to_cidr(const struct in_addr * mask)1250 int xtables_ipmask_to_cidr(const struct in_addr *mask)
1251 {
1252 uint32_t maskaddr, bits;
1253 int i;
1254
1255 maskaddr = ntohl(mask->s_addr);
1256 /* shortcut for /32 networks */
1257 if (maskaddr == 0xFFFFFFFFL)
1258 return 32;
1259
1260 i = 32;
1261 bits = 0xFFFFFFFEL;
1262 while (--i >= 0 && maskaddr != bits)
1263 bits <<= 1;
1264 if (i >= 0)
1265 return i;
1266
1267 /* this mask cannot be converted to CIDR notation */
1268 return -1;
1269 }
1270
xtables_ipmask_to_numeric(const struct in_addr * mask)1271 const char *xtables_ipmask_to_numeric(const struct in_addr *mask)
1272 {
1273 static char buf[20];
1274 uint32_t cidr;
1275
1276 cidr = xtables_ipmask_to_cidr(mask);
1277 if (cidr == (unsigned int)-1) {
1278 /* mask was not a decent combination of 1's and 0's */
1279 sprintf(buf, "/%s", xtables_ipaddr_to_numeric(mask));
1280 return buf;
1281 } else if (cidr == 32) {
1282 /* we don't want to see "/32" */
1283 return "";
1284 }
1285
1286 sprintf(buf, "/%d", cidr);
1287 return buf;
1288 }
1289
__numeric_to_ipaddr(const char * dotted,bool is_mask)1290 static struct in_addr *__numeric_to_ipaddr(const char *dotted, bool is_mask)
1291 {
1292 static struct in_addr addr;
1293 unsigned char *addrp;
1294 unsigned int onebyte;
1295 char buf[20], *p, *q;
1296 int i;
1297
1298 /* copy dotted string, because we need to modify it */
1299 strncpy(buf, dotted, sizeof(buf) - 1);
1300 buf[sizeof(buf) - 1] = '\0';
1301 addrp = (void *)&addr.s_addr;
1302
1303 p = buf;
1304 for (i = 0; i < 3; ++i) {
1305 if ((q = strchr(p, '.')) == NULL) {
1306 if (is_mask)
1307 return NULL;
1308
1309 /* autocomplete, this is a network address */
1310 if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
1311 return NULL;
1312
1313 addrp[i] = onebyte;
1314 while (i < 3)
1315 addrp[++i] = 0;
1316
1317 return &addr;
1318 }
1319
1320 *q = '\0';
1321 if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
1322 return NULL;
1323
1324 addrp[i] = onebyte;
1325 p = q + 1;
1326 }
1327
1328 /* we have checked 3 bytes, now we check the last one */
1329 if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
1330 return NULL;
1331
1332 addrp[3] = onebyte;
1333 return &addr;
1334 }
1335
xtables_numeric_to_ipaddr(const char * dotted)1336 struct in_addr *xtables_numeric_to_ipaddr(const char *dotted)
1337 {
1338 return __numeric_to_ipaddr(dotted, false);
1339 }
1340
xtables_numeric_to_ipmask(const char * dotted)1341 struct in_addr *xtables_numeric_to_ipmask(const char *dotted)
1342 {
1343 return __numeric_to_ipaddr(dotted, true);
1344 }
1345
network_to_ipaddr(const char * name)1346 static struct in_addr *network_to_ipaddr(const char *name)
1347 {
1348 static struct in_addr addr;
1349 struct netent *net;
1350
1351 if ((net = getnetbyname(name)) != NULL) {
1352 if (net->n_addrtype != AF_INET)
1353 return NULL;
1354 addr.s_addr = htonl(net->n_net);
1355 return &addr;
1356 }
1357
1358 return NULL;
1359 }
1360
host_to_ipaddr(const char * name,unsigned int * naddr)1361 static struct in_addr *host_to_ipaddr(const char *name, unsigned int *naddr)
1362 {
1363 struct in_addr *addr;
1364 struct addrinfo hints;
1365 struct addrinfo *res, *p;
1366 int err;
1367 unsigned int i;
1368
1369 memset(&hints, 0, sizeof(hints));
1370 hints.ai_flags = AI_CANONNAME;
1371 hints.ai_family = AF_INET;
1372 hints.ai_socktype = SOCK_RAW;
1373
1374 *naddr = 0;
1375 if ((err = getaddrinfo(name, NULL, &hints, &res)) != 0) {
1376 return NULL;
1377 } else {
1378 for (p = res; p != NULL; p = p->ai_next)
1379 ++*naddr;
1380 addr = xtables_calloc(*naddr, sizeof(struct in_addr));
1381 for (i = 0, p = res; p != NULL; p = p->ai_next)
1382 memcpy(&addr[i++],
1383 &((const struct sockaddr_in *)p->ai_addr)->sin_addr,
1384 sizeof(struct in_addr));
1385 freeaddrinfo(res);
1386 return addr;
1387 }
1388
1389 return NULL;
1390 }
1391
1392 static struct in_addr *
ipparse_hostnetwork(const char * name,unsigned int * naddrs)1393 ipparse_hostnetwork(const char *name, unsigned int *naddrs)
1394 {
1395 struct in_addr *addrptmp, *addrp;
1396
1397 if ((addrptmp = xtables_numeric_to_ipaddr(name)) != NULL ||
1398 (addrptmp = network_to_ipaddr(name)) != NULL) {
1399 addrp = xtables_malloc(sizeof(struct in_addr));
1400 memcpy(addrp, addrptmp, sizeof(*addrp));
1401 *naddrs = 1;
1402 return addrp;
1403 }
1404 if ((addrptmp = host_to_ipaddr(name, naddrs)) != NULL)
1405 return addrptmp;
1406
1407 xt_params->exit_err(PARAMETER_PROBLEM, "host/network `%s' not found", name);
1408 }
1409
parse_ipmask(const char * mask)1410 static struct in_addr *parse_ipmask(const char *mask)
1411 {
1412 static struct in_addr maskaddr;
1413 struct in_addr *addrp;
1414 unsigned int bits;
1415
1416 if (mask == NULL) {
1417 /* no mask at all defaults to 32 bits */
1418 maskaddr.s_addr = 0xFFFFFFFF;
1419 return &maskaddr;
1420 }
1421 if ((addrp = xtables_numeric_to_ipmask(mask)) != NULL)
1422 /* dotted_to_addr already returns a network byte order addr */
1423 return addrp;
1424 if (!xtables_strtoui(mask, NULL, &bits, 0, 32))
1425 xt_params->exit_err(PARAMETER_PROBLEM,
1426 "invalid mask `%s' specified", mask);
1427 if (bits != 0) {
1428 maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
1429 return &maskaddr;
1430 }
1431
1432 maskaddr.s_addr = 0U;
1433 return &maskaddr;
1434 }
1435
xtables_ipparse_multiple(const char * name,struct in_addr ** addrpp,struct in_addr ** maskpp,unsigned int * naddrs)1436 void xtables_ipparse_multiple(const char *name, struct in_addr **addrpp,
1437 struct in_addr **maskpp, unsigned int *naddrs)
1438 {
1439 struct in_addr *addrp;
1440 char buf[256], *p, *next;
1441 unsigned int len, i, j, n, count = 1;
1442 const char *loop = name;
1443
1444 while ((loop = strchr(loop, ',')) != NULL) {
1445 ++count;
1446 ++loop; /* skip ',' */
1447 }
1448
1449 *addrpp = xtables_malloc(sizeof(struct in_addr) * count);
1450 *maskpp = xtables_malloc(sizeof(struct in_addr) * count);
1451
1452 loop = name;
1453
1454 for (i = 0; i < count; ++i) {
1455 while (isspace(*loop))
1456 ++loop;
1457 next = strchr(loop, ',');
1458 if (next != NULL)
1459 len = next - loop;
1460 else
1461 len = strlen(loop);
1462 if (len > sizeof(buf) - 1)
1463 xt_params->exit_err(PARAMETER_PROBLEM,
1464 "Hostname too long");
1465
1466 strncpy(buf, loop, len);
1467 buf[len] = '\0';
1468 if ((p = strrchr(buf, '/')) != NULL) {
1469 *p = '\0';
1470 addrp = parse_ipmask(p + 1);
1471 } else {
1472 addrp = parse_ipmask(NULL);
1473 }
1474 memcpy(*maskpp + i, addrp, sizeof(*addrp));
1475
1476 /* if a null mask is given, the name is ignored, like in "any/0" */
1477 if ((*maskpp + i)->s_addr == 0)
1478 /*
1479 * A bit pointless to process multiple addresses
1480 * in this case...
1481 */
1482 strcpy(buf, "0.0.0.0");
1483
1484 addrp = ipparse_hostnetwork(buf, &n);
1485 if (n > 1) {
1486 count += n - 1;
1487 *addrpp = xtables_realloc(*addrpp,
1488 sizeof(struct in_addr) * count);
1489 *maskpp = xtables_realloc(*maskpp,
1490 sizeof(struct in_addr) * count);
1491 for (j = 0; j < n; ++j)
1492 /* for each new addr */
1493 memcpy(*addrpp + i + j, addrp + j,
1494 sizeof(*addrp));
1495 for (j = 1; j < n; ++j)
1496 /* for each new mask */
1497 memcpy(*maskpp + i + j, *maskpp + i,
1498 sizeof(*addrp));
1499 i += n - 1;
1500 } else {
1501 memcpy(*addrpp + i, addrp, sizeof(*addrp));
1502 }
1503 /* free what ipparse_hostnetwork had allocated: */
1504 free(addrp);
1505 if (next == NULL)
1506 break;
1507 loop = next + 1;
1508 }
1509 *naddrs = count;
1510 for (i = 0; i < count; ++i)
1511 (*addrpp+i)->s_addr &= (*maskpp+i)->s_addr;
1512 }
1513
1514
1515 /**
1516 * xtables_ipparse_any - transform arbitrary name to in_addr
1517 *
1518 * Possible inputs (pseudo regex):
1519 * m{^($hostname|$networkname|$ipaddr)(/$mask)?}
1520 * "1.2.3.4/5", "1.2.3.4", "hostname", "networkname"
1521 */
xtables_ipparse_any(const char * name,struct in_addr ** addrpp,struct in_addr * maskp,unsigned int * naddrs)1522 void xtables_ipparse_any(const char *name, struct in_addr **addrpp,
1523 struct in_addr *maskp, unsigned int *naddrs)
1524 {
1525 unsigned int i, j, k, n;
1526 struct in_addr *addrp;
1527 char buf[256], *p;
1528
1529 strncpy(buf, name, sizeof(buf) - 1);
1530 buf[sizeof(buf) - 1] = '\0';
1531 if ((p = strrchr(buf, '/')) != NULL) {
1532 *p = '\0';
1533 addrp = parse_ipmask(p + 1);
1534 } else {
1535 addrp = parse_ipmask(NULL);
1536 }
1537 memcpy(maskp, addrp, sizeof(*maskp));
1538
1539 /* if a null mask is given, the name is ignored, like in "any/0" */
1540 if (maskp->s_addr == 0U)
1541 strcpy(buf, "0.0.0.0");
1542
1543 addrp = *addrpp = ipparse_hostnetwork(buf, naddrs);
1544 n = *naddrs;
1545 for (i = 0, j = 0; i < n; ++i) {
1546 addrp[j++].s_addr &= maskp->s_addr;
1547 for (k = 0; k < j - 1; ++k)
1548 if (addrp[k].s_addr == addrp[j-1].s_addr) {
1549 /*
1550 * Nuke the dup by copying an address from the
1551 * tail here, and check the current position
1552 * again (--j).
1553 */
1554 memcpy(&addrp[--j], &addrp[--*naddrs],
1555 sizeof(struct in_addr));
1556 break;
1557 }
1558 }
1559 }
1560
xtables_ip6addr_to_numeric(const struct in6_addr * addrp)1561 const char *xtables_ip6addr_to_numeric(const struct in6_addr *addrp)
1562 {
1563 /* 0000:0000:0000:0000:0000:0000:000.000.000.000
1564 * 0000:0000:0000:0000:0000:0000:0000:0000 */
1565 static char buf[50+1];
1566 return inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
1567 }
1568
ip6addr_to_host(const struct in6_addr * addr)1569 static const char *ip6addr_to_host(const struct in6_addr *addr)
1570 {
1571 static char hostname[NI_MAXHOST];
1572 struct sockaddr_in6 saddr;
1573 int err;
1574
1575 memset(&saddr, 0, sizeof(struct sockaddr_in6));
1576 memcpy(&saddr.sin6_addr, addr, sizeof(*addr));
1577 saddr.sin6_family = AF_INET6;
1578
1579 err = getnameinfo((const void *)&saddr, sizeof(struct sockaddr_in6),
1580 hostname, sizeof(hostname) - 1, NULL, 0, 0);
1581 if (err != 0)
1582 return NULL;
1583
1584 return hostname;
1585 }
1586
xtables_ip6addr_to_anyname(const struct in6_addr * addr)1587 const char *xtables_ip6addr_to_anyname(const struct in6_addr *addr)
1588 {
1589 const char *name;
1590
1591 if ((name = ip6addr_to_host(addr)) != NULL)
1592 return name;
1593
1594 return xtables_ip6addr_to_numeric(addr);
1595 }
1596
xtables_ip6mask_to_cidr(const struct in6_addr * k)1597 int xtables_ip6mask_to_cidr(const struct in6_addr *k)
1598 {
1599 unsigned int bits = 0;
1600 uint32_t a, b, c, d;
1601
1602 a = ntohl(k->s6_addr32[0]);
1603 b = ntohl(k->s6_addr32[1]);
1604 c = ntohl(k->s6_addr32[2]);
1605 d = ntohl(k->s6_addr32[3]);
1606 while (a & 0x80000000U) {
1607 ++bits;
1608 a <<= 1;
1609 a |= (b >> 31) & 1;
1610 b <<= 1;
1611 b |= (c >> 31) & 1;
1612 c <<= 1;
1613 c |= (d >> 31) & 1;
1614 d <<= 1;
1615 }
1616 if (a != 0 || b != 0 || c != 0 || d != 0)
1617 return -1;
1618 return bits;
1619 }
1620
xtables_ip6mask_to_numeric(const struct in6_addr * addrp)1621 const char *xtables_ip6mask_to_numeric(const struct in6_addr *addrp)
1622 {
1623 static char buf[50+2];
1624 int l = xtables_ip6mask_to_cidr(addrp);
1625
1626 if (l == -1) {
1627 strcpy(buf, "/");
1628 strcat(buf, xtables_ip6addr_to_numeric(addrp));
1629 return buf;
1630 }
1631 /* we don't want to see "/128" */
1632 if (l == 128)
1633 return "";
1634 else
1635 sprintf(buf, "/%d", l);
1636 return buf;
1637 }
1638
xtables_numeric_to_ip6addr(const char * num)1639 struct in6_addr *xtables_numeric_to_ip6addr(const char *num)
1640 {
1641 static struct in6_addr ap;
1642 int err;
1643
1644 if ((err = inet_pton(AF_INET6, num, &ap)) == 1)
1645 return ≈
1646
1647 return NULL;
1648 }
1649
1650 static struct in6_addr *
host_to_ip6addr(const char * name,unsigned int * naddr)1651 host_to_ip6addr(const char *name, unsigned int *naddr)
1652 {
1653 struct in6_addr *addr;
1654 struct addrinfo hints;
1655 struct addrinfo *res, *p;
1656 int err;
1657 unsigned int i;
1658
1659 memset(&hints, 0, sizeof(hints));
1660 hints.ai_flags = AI_CANONNAME;
1661 hints.ai_family = AF_INET6;
1662 hints.ai_socktype = SOCK_RAW;
1663
1664 *naddr = 0;
1665 if ((err = getaddrinfo(name, NULL, &hints, &res)) != 0) {
1666 return NULL;
1667 } else {
1668 /* Find length of address chain */
1669 for (p = res; p != NULL; p = p->ai_next)
1670 ++*naddr;
1671 /* Copy each element of the address chain */
1672 addr = xtables_calloc(*naddr, sizeof(struct in6_addr));
1673 for (i = 0, p = res; p != NULL; p = p->ai_next)
1674 memcpy(&addr[i++],
1675 &((const struct sockaddr_in6 *)p->ai_addr)->sin6_addr,
1676 sizeof(struct in6_addr));
1677 freeaddrinfo(res);
1678 return addr;
1679 }
1680
1681 return NULL;
1682 }
1683
network_to_ip6addr(const char * name)1684 static struct in6_addr *network_to_ip6addr(const char *name)
1685 {
1686 /* abort();*/
1687 /* TODO: not implemented yet, but the exception breaks the
1688 * name resolvation */
1689 return NULL;
1690 }
1691
1692 static struct in6_addr *
ip6parse_hostnetwork(const char * name,unsigned int * naddrs)1693 ip6parse_hostnetwork(const char *name, unsigned int *naddrs)
1694 {
1695 struct in6_addr *addrp, *addrptmp;
1696
1697 if ((addrptmp = xtables_numeric_to_ip6addr(name)) != NULL ||
1698 (addrptmp = network_to_ip6addr(name)) != NULL) {
1699 addrp = xtables_malloc(sizeof(struct in6_addr));
1700 memcpy(addrp, addrptmp, sizeof(*addrp));
1701 *naddrs = 1;
1702 return addrp;
1703 }
1704 if ((addrp = host_to_ip6addr(name, naddrs)) != NULL)
1705 return addrp;
1706
1707 xt_params->exit_err(PARAMETER_PROBLEM, "host/network `%s' not found", name);
1708 }
1709
parse_ip6mask(char * mask)1710 static struct in6_addr *parse_ip6mask(char *mask)
1711 {
1712 static struct in6_addr maskaddr;
1713 struct in6_addr *addrp;
1714 unsigned int bits;
1715
1716 if (mask == NULL) {
1717 /* no mask at all defaults to 128 bits */
1718 memset(&maskaddr, 0xff, sizeof maskaddr);
1719 return &maskaddr;
1720 }
1721 if ((addrp = xtables_numeric_to_ip6addr(mask)) != NULL)
1722 return addrp;
1723 if (!xtables_strtoui(mask, NULL, &bits, 0, 128))
1724 xt_params->exit_err(PARAMETER_PROBLEM,
1725 "invalid mask `%s' specified", mask);
1726 if (bits != 0) {
1727 char *p = (void *)&maskaddr;
1728 memset(p, 0xff, bits / 8);
1729 memset(p + ((bits + 7) / 8), 0, (128 - bits) / 8);
1730 if (bits < 128)
1731 p[bits/8] = 0xff << (8 - (bits & 7));
1732 return &maskaddr;
1733 }
1734
1735 memset(&maskaddr, 0, sizeof(maskaddr));
1736 return &maskaddr;
1737 }
1738
1739 void
xtables_ip6parse_multiple(const char * name,struct in6_addr ** addrpp,struct in6_addr ** maskpp,unsigned int * naddrs)1740 xtables_ip6parse_multiple(const char *name, struct in6_addr **addrpp,
1741 struct in6_addr **maskpp, unsigned int *naddrs)
1742 {
1743 static const struct in6_addr zero_addr;
1744 struct in6_addr *addrp;
1745 char buf[256], *p, *next;
1746 unsigned int len, i, j, n, count = 1;
1747 const char *loop = name;
1748
1749 while ((loop = strchr(loop, ',')) != NULL) {
1750 ++count;
1751 ++loop; /* skip ',' */
1752 }
1753
1754 *addrpp = xtables_malloc(sizeof(struct in6_addr) * count);
1755 *maskpp = xtables_malloc(sizeof(struct in6_addr) * count);
1756
1757 loop = name;
1758
1759 for (i = 0; i < count /*NB: count can grow*/; ++i) {
1760 while (isspace(*loop))
1761 ++loop;
1762 next = strchr(loop, ',');
1763 if (next != NULL)
1764 len = next - loop;
1765 else
1766 len = strlen(loop);
1767 if (len > sizeof(buf) - 1)
1768 xt_params->exit_err(PARAMETER_PROBLEM,
1769 "Hostname too long");
1770
1771 strncpy(buf, loop, len);
1772 buf[len] = '\0';
1773 if ((p = strrchr(buf, '/')) != NULL) {
1774 *p = '\0';
1775 addrp = parse_ip6mask(p + 1);
1776 } else {
1777 addrp = parse_ip6mask(NULL);
1778 }
1779 memcpy(*maskpp + i, addrp, sizeof(*addrp));
1780
1781 /* if a null mask is given, the name is ignored, like in "any/0" */
1782 if (memcmp(*maskpp + i, &zero_addr, sizeof(zero_addr)) == 0)
1783 strcpy(buf, "::");
1784
1785 addrp = ip6parse_hostnetwork(buf, &n);
1786 if (n > 1) {
1787 count += n - 1;
1788 *addrpp = xtables_realloc(*addrpp,
1789 sizeof(struct in6_addr) * count);
1790 *maskpp = xtables_realloc(*maskpp,
1791 sizeof(struct in6_addr) * count);
1792 for (j = 0; j < n; ++j)
1793 /* for each new addr */
1794 memcpy(*addrpp + i + j, addrp + j,
1795 sizeof(*addrp));
1796 for (j = 1; j < n; ++j)
1797 /* for each new mask */
1798 memcpy(*maskpp + i + j, *maskpp + i,
1799 sizeof(*addrp));
1800 i += n - 1;
1801 } else {
1802 memcpy(*addrpp + i, addrp, sizeof(*addrp));
1803 }
1804 /* free what ip6parse_hostnetwork had allocated: */
1805 free(addrp);
1806 if (next == NULL)
1807 break;
1808 loop = next + 1;
1809 }
1810 *naddrs = count;
1811 for (i = 0; i < count; ++i)
1812 for (j = 0; j < 4; ++j)
1813 (*addrpp+i)->s6_addr32[j] &= (*maskpp+i)->s6_addr32[j];
1814 }
1815
xtables_ip6parse_any(const char * name,struct in6_addr ** addrpp,struct in6_addr * maskp,unsigned int * naddrs)1816 void xtables_ip6parse_any(const char *name, struct in6_addr **addrpp,
1817 struct in6_addr *maskp, unsigned int *naddrs)
1818 {
1819 static const struct in6_addr zero_addr;
1820 struct in6_addr *addrp;
1821 unsigned int i, j, k, n;
1822 char buf[256], *p;
1823
1824 strncpy(buf, name, sizeof(buf) - 1);
1825 buf[sizeof(buf)-1] = '\0';
1826 if ((p = strrchr(buf, '/')) != NULL) {
1827 *p = '\0';
1828 addrp = parse_ip6mask(p + 1);
1829 } else {
1830 addrp = parse_ip6mask(NULL);
1831 }
1832 memcpy(maskp, addrp, sizeof(*maskp));
1833
1834 /* if a null mask is given, the name is ignored, like in "any/0" */
1835 if (memcmp(maskp, &zero_addr, sizeof(zero_addr)) == 0)
1836 strcpy(buf, "::");
1837
1838 addrp = *addrpp = ip6parse_hostnetwork(buf, naddrs);
1839 n = *naddrs;
1840 for (i = 0, j = 0; i < n; ++i) {
1841 for (k = 0; k < 4; ++k)
1842 addrp[j].s6_addr32[k] &= maskp->s6_addr32[k];
1843 ++j;
1844 for (k = 0; k < j - 1; ++k)
1845 if (IN6_ARE_ADDR_EQUAL(&addrp[k], &addrp[j - 1])) {
1846 /*
1847 * Nuke the dup by copying an address from the
1848 * tail here, and check the current position
1849 * again (--j).
1850 */
1851 memcpy(&addrp[--j], &addrp[--*naddrs],
1852 sizeof(struct in_addr));
1853 break;
1854 }
1855 }
1856 }
1857
xtables_save_string(const char * value)1858 void xtables_save_string(const char *value)
1859 {
1860 static const char no_quote_chars[] = "_-0123456789"
1861 "abcdefghijklmnopqrstuvwxyz"
1862 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1863 static const char escape_chars[] = "\"\\'";
1864 size_t length;
1865 const char *p;
1866
1867 length = strspn(value, no_quote_chars);
1868 if (length > 0 && value[length] == 0) {
1869 /* no quoting required */
1870 putchar(' ');
1871 fputs(value, stdout);
1872 } else {
1873 /* there is at least one dangerous character in the
1874 value, which we have to quote. Write double quotes
1875 around the value and escape special characters with
1876 a backslash */
1877 printf(" \"");
1878
1879 for (p = strpbrk(value, escape_chars); p != NULL;
1880 p = strpbrk(value, escape_chars)) {
1881 if (p > value)
1882 fwrite(value, 1, p - value, stdout);
1883 putchar('\\');
1884 putchar(*p);
1885 value = p + 1;
1886 }
1887
1888 /* print the rest and finish the double quoted
1889 string */
1890 fputs(value, stdout);
1891 putchar('\"');
1892 }
1893 }
1894
1895 const struct xtables_pprot xtables_chain_protos[] = {
1896 {"tcp", IPPROTO_TCP},
1897 {"sctp", IPPROTO_SCTP},
1898 {"udp", IPPROTO_UDP},
1899 {"udplite", IPPROTO_UDPLITE},
1900 {"icmp", IPPROTO_ICMP},
1901 {"icmpv6", IPPROTO_ICMPV6},
1902 {"ipv6-icmp", IPPROTO_ICMPV6},
1903 {"esp", IPPROTO_ESP},
1904 {"ah", IPPROTO_AH},
1905 {"ipv6-mh", IPPROTO_MH},
1906 {"mh", IPPROTO_MH},
1907 {"all", 0},
1908 {NULL},
1909 };
1910
1911 uint16_t
xtables_parse_protocol(const char * s)1912 xtables_parse_protocol(const char *s)
1913 {
1914 const struct protoent *pent;
1915 unsigned int proto, i;
1916
1917 if (xtables_strtoui(s, NULL, &proto, 0, UINT8_MAX))
1918 return proto;
1919
1920 /* first deal with the special case of 'all' to prevent
1921 * people from being able to redefine 'all' in nsswitch
1922 * and/or provoke expensive [not working] ldap/nis/...
1923 * lookups */
1924 if (strcmp(s, "all") == 0)
1925 return 0;
1926
1927 pent = getprotobyname(s);
1928 if (pent != NULL)
1929 return pent->p_proto;
1930
1931 for (i = 0; i < ARRAY_SIZE(xtables_chain_protos); ++i) {
1932 if (xtables_chain_protos[i].name == NULL)
1933 continue;
1934 if (strcmp(s, xtables_chain_protos[i].name) == 0)
1935 return xtables_chain_protos[i].num;
1936 }
1937 xt_params->exit_err(PARAMETER_PROBLEM,
1938 "unknown protocol \"%s\" specified", s);
1939 return -1;
1940 }
1941
xtables_print_num(uint64_t number,unsigned int format)1942 void xtables_print_num(uint64_t number, unsigned int format)
1943 {
1944 if (!(format & FMT_KILOMEGAGIGA)) {
1945 printf(FMT("%8llu ","%llu "), (unsigned long long)number);
1946 return;
1947 }
1948 if (number <= 99999) {
1949 printf(FMT("%5llu ","%llu "), (unsigned long long)number);
1950 return;
1951 }
1952 number = (number + 500) / 1000;
1953 if (number <= 9999) {
1954 printf(FMT("%4lluK ","%lluK "), (unsigned long long)number);
1955 return;
1956 }
1957 number = (number + 500) / 1000;
1958 if (number <= 9999) {
1959 printf(FMT("%4lluM ","%lluM "), (unsigned long long)number);
1960 return;
1961 }
1962 number = (number + 500) / 1000;
1963 if (number <= 9999) {
1964 printf(FMT("%4lluG ","%lluG "), (unsigned long long)number);
1965 return;
1966 }
1967 number = (number + 500) / 1000;
1968 printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
1969 }
1970
1971 int kernel_version;
1972
get_kernel_version(void)1973 void get_kernel_version(void)
1974 {
1975 static struct utsname uts;
1976 int x = 0, y = 0, z = 0;
1977
1978 if (uname(&uts) == -1) {
1979 fprintf(stderr, "Unable to retrieve kernel version.\n");
1980 xtables_free_opts(1);
1981 exit(1);
1982 }
1983
1984 sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
1985 kernel_version = LINUX_VERSION(x, y, z);
1986 }
1987
1988 #include <linux/netfilter/nf_tables.h>
1989
1990 struct xt_xlate {
1991 struct {
1992 char *data;
1993 int size;
1994 int rem;
1995 int off;
1996 } buf;
1997 char comment[NFT_USERDATA_MAXLEN];
1998 };
1999
xt_xlate_alloc(int size)2000 struct xt_xlate *xt_xlate_alloc(int size)
2001 {
2002 struct xt_xlate *xl;
2003
2004 xl = malloc(sizeof(struct xt_xlate));
2005 if (xl == NULL)
2006 xtables_error(RESOURCE_PROBLEM, "OOM");
2007
2008 xl->buf.data = malloc(size);
2009 if (xl->buf.data == NULL)
2010 xtables_error(RESOURCE_PROBLEM, "OOM");
2011
2012 xl->buf.size = size;
2013 xl->buf.rem = size;
2014 xl->buf.off = 0;
2015 xl->comment[0] = '\0';
2016
2017 return xl;
2018 }
2019
xt_xlate_free(struct xt_xlate * xl)2020 void xt_xlate_free(struct xt_xlate *xl)
2021 {
2022 free(xl->buf.data);
2023 free(xl);
2024 }
2025
xt_xlate_add(struct xt_xlate * xl,const char * fmt,...)2026 void xt_xlate_add(struct xt_xlate *xl, const char *fmt, ...)
2027 {
2028 va_list ap;
2029 int len;
2030
2031 va_start(ap, fmt);
2032 len = vsnprintf(xl->buf.data + xl->buf.off, xl->buf.rem, fmt, ap);
2033 if (len < 0 || len >= xl->buf.rem)
2034 xtables_error(RESOURCE_PROBLEM, "OOM");
2035
2036 va_end(ap);
2037 xl->buf.rem -= len;
2038 xl->buf.off += len;
2039 }
2040
xt_xlate_add_comment(struct xt_xlate * xl,const char * comment)2041 void xt_xlate_add_comment(struct xt_xlate *xl, const char *comment)
2042 {
2043 strncpy(xl->comment, comment, NFT_USERDATA_MAXLEN - 1);
2044 xl->comment[NFT_USERDATA_MAXLEN - 1] = '\0';
2045 }
2046
xt_xlate_get_comment(struct xt_xlate * xl)2047 const char *xt_xlate_get_comment(struct xt_xlate *xl)
2048 {
2049 return xl->comment[0] ? xl->comment : NULL;
2050 }
2051
xt_xlate_get(struct xt_xlate * xl)2052 const char *xt_xlate_get(struct xt_xlate *xl)
2053 {
2054 return xl->buf.data;
2055 }
2056