2 * (C) 2000-2006 by the netfilter coreteam <coreteam@netfilter.org>:
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.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <sys/socket.h>
30 #include <sys/types.h>
32 #include <arpa/inet.h>
35 #include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */
36 #include <linux/netfilter_ipv4/ip_tables.h>
37 #include <linux/netfilter_ipv6/ip6_tables.h>
38 #include <libiptc/libxtc.h>
40 #ifndef NO_SHARED_LIBS
43 #ifndef IPT_SO_GET_REVISION_MATCH /* Old kernel source. */
44 # define IPT_SO_GET_REVISION_MATCH (IPT_BASE_CTL + 2)
45 # define IPT_SO_GET_REVISION_TARGET (IPT_BASE_CTL + 3)
47 #ifndef IP6T_SO_GET_REVISION_MATCH /* Old kernel source. */
48 # define IP6T_SO_GET_REVISION_MATCH 68
49 # define IP6T_SO_GET_REVISION_TARGET 69
56 #ifndef PROC_SYS_MODPROBE
57 #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
60 void basic_exit_err(enum xtables_exittype status
, const char *msg
, ...) __attribute__((noreturn
, format(printf
,2,3)));
62 struct xtables_globals
*xt_params
= NULL
;
64 void basic_exit_err(enum xtables_exittype status
, const char *msg
, ...)
69 fprintf(stderr
, "%s v%s: ", xt_params
->program_name
, xt_params
->program_version
);
70 vfprintf(stderr
, msg
, args
);
72 fprintf(stderr
, "\n");
77 void xtables_free_opts(int reset_offset
)
79 if (xt_params
->opts
!= xt_params
->orig_opts
) {
80 free(xt_params
->opts
);
81 xt_params
->opts
= xt_params
->orig_opts
;
83 xt_params
->option_offset
= 0;
87 struct option
*xtables_merge_options(struct option
*oldopts
,
88 const struct option
*newopts
,
89 unsigned int *option_offset
)
91 unsigned int num_old
, num_new
, i
;
97 for (num_old
= 0; oldopts
[num_old
].name
; num_old
++) ;
98 for (num_new
= 0; newopts
[num_new
].name
; num_new
++) ;
100 xt_params
->option_offset
+= 256;
101 *option_offset
= xt_params
->option_offset
;
103 merge
= malloc(sizeof(struct option
) * (num_new
+ num_old
+ 1));
106 memcpy(merge
, oldopts
, num_old
* sizeof(struct option
));
107 xtables_free_opts(0); /* Release any old options merged */
108 for (i
= 0; i
< num_new
; i
++) {
109 merge
[num_old
+ i
] = newopts
[i
];
110 merge
[num_old
+ i
].val
+= *option_offset
;
112 memset(merge
+ num_old
+ num_new
, 0, sizeof(struct option
));
117 void xtables_set_revision(char *name
, u_int8_t revision
)
119 /* Old kernel sources don't have ".revision" field,
120 * but we stole a byte from name. */
121 name
[XT_FUNCTION_MAXNAMELEN
- 2] = '\0';
122 name
[XT_FUNCTION_MAXNAMELEN
- 1] = revision
;
126 * xtables_afinfo - protocol family dependent information
127 * @kmod: kernel module basename (e.g. "ip_tables")
128 * @libprefix: prefix of .so library name (e.g. "libipt_")
129 * @family: nfproto family
130 * @ipproto: used by setsockopt (e.g. IPPROTO_IP)
131 * @so_rev_match: optname to check revision support of match
132 * @so_rev_target: optname to check revision support of target
134 struct xtables_afinfo
{
136 const char *libprefix
;
143 static const struct xtables_afinfo afinfo_ipv4
= {
145 .libprefix
= "libipt_",
146 .family
= NFPROTO_IPV4
,
147 .ipproto
= IPPROTO_IP
,
148 .so_rev_match
= IPT_SO_GET_REVISION_MATCH
,
149 .so_rev_target
= IPT_SO_GET_REVISION_TARGET
,
152 static const struct xtables_afinfo afinfo_ipv6
= {
153 .kmod
= "ip6_tables",
154 .libprefix
= "libip6t_",
155 .family
= NFPROTO_IPV6
,
156 .ipproto
= IPPROTO_IPV6
,
157 .so_rev_match
= IP6T_SO_GET_REVISION_MATCH
,
158 .so_rev_target
= IP6T_SO_GET_REVISION_TARGET
,
161 static const struct xtables_afinfo
*afinfo
;
163 /* Search path for Xtables .so files */
164 static const char *xtables_libdir
;
166 /* the path to command to load kernel module */
167 const char *xtables_modprobe_program
;
169 /* Keeping track of external matches and targets: linked lists. */
170 struct xtables_match
*xtables_matches
;
171 struct xtables_target
*xtables_targets
;
173 void xtables_init(void)
175 xtables_libdir
= getenv("XTABLES_LIBDIR");
176 if (xtables_libdir
!= NULL
)
178 xtables_libdir
= getenv("IPTABLES_LIB_DIR");
179 if (xtables_libdir
!= NULL
) {
180 fprintf(stderr
, "IPTABLES_LIB_DIR is deprecated, "
181 "use XTABLES_LIBDIR.\n");
185 * Well yes, IP6TABLES_LIB_DIR is of lower priority over
186 * IPTABLES_LIB_DIR since this moved to libxtables; I think that is ok
187 * for these env vars are deprecated anyhow, and in light of the
188 * (shared) libxt_*.so files, makes less sense to have
189 * IPTABLES_LIB_DIR != IP6TABLES_LIB_DIR.
191 xtables_libdir
= getenv("IP6TABLES_LIB_DIR");
192 if (xtables_libdir
!= NULL
) {
193 fprintf(stderr
, "IP6TABLES_LIB_DIR is deprecated, "
194 "use XTABLES_LIBDIR.\n");
197 xtables_libdir
= XTABLES_LIBDIR
;
200 void xtables_set_nfproto(uint8_t nfproto
)
204 afinfo
= &afinfo_ipv4
;
207 afinfo
= &afinfo_ipv6
;
210 fprintf(stderr
, "libxtables: unhandled NFPROTO in %s\n",
216 * xtables_set_params - set the global parameters used by xtables
217 * @xtp: input xtables_globals structure
219 * The app is expected to pass a valid xtables_globals data-filled
221 * @xtp cannot be NULL
223 * Returns -1 on failure to set and 0 on success
225 int xtables_set_params(struct xtables_globals
*xtp
)
228 fprintf(stderr
, "%s: Illegal global params\n",__func__
);
234 if (!xt_params
->exit_err
)
235 xt_params
->exit_err
= basic_exit_err
;
240 int xtables_init_all(struct xtables_globals
*xtp
, uint8_t nfproto
)
243 xtables_set_nfproto(nfproto
);
244 return xtables_set_params(xtp
);
248 * xtables_*alloc - wrappers that exit on failure
250 void *xtables_calloc(size_t count
, size_t size
)
254 if ((p
= calloc(count
, size
)) == NULL
) {
255 perror("ip[6]tables: calloc failed");
262 void *xtables_malloc(size_t size
)
266 if ((p
= malloc(size
)) == NULL
) {
267 perror("ip[6]tables: malloc failed");
274 static char *get_modprobe(void)
279 #define PROCFILE_BUFSIZ 1024
280 procfile
= open(PROC_SYS_MODPROBE
, O_RDONLY
);
284 ret
= (char *) malloc(PROCFILE_BUFSIZ
);
286 memset(ret
, 0, PROCFILE_BUFSIZ
);
287 switch (read(procfile
, ret
, PROCFILE_BUFSIZ
)) {
289 case PROCFILE_BUFSIZ
: goto fail
; /* Partial read. Wierd */
291 if (ret
[strlen(ret
)-1]=='\n')
292 ret
[strlen(ret
)-1]=0;
302 int xtables_insmod(const char *modname
, const char *modprobe
, bool quiet
)
308 /* If they don't explicitly set it, read out of kernel */
310 buf
= get_modprobe();
317 * Need to flush the buffer, or the child may output it again
318 * when switching the program thru execv.
324 argv
[0] = (char *)modprobe
;
325 argv
[1] = (char *)modname
;
333 execv(argv
[0], argv
);
335 /* not usually reached */
340 default: /* parent */
345 if (WIFEXITED(status
) && WEXITSTATUS(status
) == 0)
350 int xtables_load_ko(const char *modprobe
, bool quiet
)
352 static bool loaded
= false;
356 ret
= xtables_insmod(afinfo
->kmod
, modprobe
, quiet
);
364 * xtables_strtou{i,l} - string to number conversion
366 * @end: like strtoul's "end" pointer
367 * @value: pointer for result
368 * @min: minimum accepted value
369 * @max: maximum accepted value
371 * If @end is NULL, we assume the caller wants a "strict strtoul", and hence
373 * In either case, the value obtained is compared for min-max compliance.
374 * Base is always 0, i.e. autodetect depending on @s.
376 * Returns true/false whether number was accepted. On failure, *value has
377 * undefined contents.
379 bool xtables_strtoul(const char *s
, char **end
, unsigned long *value
,
380 unsigned long min
, unsigned long max
)
386 v
= strtoul(s
, &my_end
, 0);
393 if (errno
!= ERANGE
&& min
<= v
&& (max
== 0 || v
<= max
)) {
397 return *my_end
== '\0';
404 bool xtables_strtoui(const char *s
, char **end
, unsigned int *value
,
405 unsigned int min
, unsigned int max
)
410 ret
= xtables_strtoul(s
, end
, &v
, min
, max
);
416 int xtables_service_to_port(const char *name
, const char *proto
)
418 struct servent
*service
;
420 if ((service
= getservbyname(name
, proto
)) != NULL
)
421 return ntohs((unsigned short) service
->s_port
);
426 u_int16_t
xtables_parse_port(const char *port
, const char *proto
)
428 unsigned int portnum
;
430 if (xtables_strtoui(port
, NULL
, &portnum
, 0, UINT16_MAX
) ||
431 (portnum
= xtables_service_to_port(port
, proto
)) != (unsigned)-1)
434 xt_params
->exit_err(PARAMETER_PROBLEM
,
435 "invalid port/service `%s' specified", port
);
438 void xtables_parse_interface(const char *arg
, char *vianame
,
441 int vialen
= strlen(arg
);
444 memset(mask
, 0, IFNAMSIZ
);
445 memset(vianame
, 0, IFNAMSIZ
);
447 if (vialen
+ 1 > IFNAMSIZ
)
448 xt_params
->exit_err(PARAMETER_PROBLEM
,
449 "interface name `%s' must be shorter than IFNAMSIZ"
450 " (%i)", arg
, IFNAMSIZ
-1);
452 strcpy(vianame
, arg
);
453 if ((vialen
== 0) || (vialen
== 1 && vianame
[0] == '+'))
454 memset(mask
, 0, IFNAMSIZ
);
455 else if (vianame
[vialen
- 1] == '+') {
456 memset(mask
, 0xFF, vialen
- 1);
457 memset(mask
+ vialen
- 1, 0, IFNAMSIZ
- vialen
+ 1);
458 /* Don't remove `+' here! -HW */
460 /* Include nul-terminator in match */
461 memset(mask
, 0xFF, vialen
+ 1);
462 memset(mask
+ vialen
+ 1, 0, IFNAMSIZ
- vialen
- 1);
463 for (i
= 0; vianame
[i
]; i
++) {
464 if (vianame
[i
] == ':' ||
468 "Warning: weird character in interface"
469 " `%s' (No aliases, :, ! or *).\n",
477 #ifndef NO_SHARED_LIBS
478 static void *load_extension(const char *search_path
, const char *prefix
,
479 const char *name
, bool is_target
)
481 const char *dir
= search_path
, *next
;
487 next
= strchr(dir
, ':');
489 next
= dir
+ strlen(dir
);
490 snprintf(path
, sizeof(path
), "%.*s/libxt_%s.so",
491 (unsigned int)(next
- dir
), dir
, name
);
493 if (dlopen(path
, RTLD_NOW
) != NULL
) {
494 /* Found library. If it didn't register itself,
495 maybe they specified target as match. */
497 ptr
= xtables_find_target(name
, XTF_DONT_LOAD
);
499 ptr
= xtables_find_match(name
,
500 XTF_DONT_LOAD
, NULL
);
501 } else if (stat(path
, &sb
) == 0) {
502 fprintf(stderr
, "%s: %s\n", path
, dlerror());
508 snprintf(path
, sizeof(path
), "%.*s/%s%s.so",
509 (unsigned int)(next
- dir
), dir
, prefix
, name
);
510 if (dlopen(path
, RTLD_NOW
) != NULL
) {
512 ptr
= xtables_find_target(name
, XTF_DONT_LOAD
);
514 ptr
= xtables_find_match(name
,
515 XTF_DONT_LOAD
, NULL
);
516 } else if (stat(path
, &sb
) == 0) {
517 fprintf(stderr
, "%s: %s\n", path
, dlerror());
524 } while (*next
!= '\0');
530 struct xtables_match
*
531 xtables_find_match(const char *name
, enum xtables_tryload tryload
,
532 struct xtables_rule_match
**matches
)
534 struct xtables_match
*ptr
;
535 const char *icmp6
= "icmp6";
537 /* This is ugly as hell. Nonetheless, there is no way of changing
538 * this without hurting backwards compatibility */
539 if ( (strcmp(name
,"icmpv6") == 0) ||
540 (strcmp(name
,"ipv6-icmp") == 0) ||
541 (strcmp(name
,"icmp6") == 0) )
544 for (ptr
= xtables_matches
; ptr
; ptr
= ptr
->next
) {
545 if (strcmp(name
, ptr
->name
) == 0) {
546 struct xtables_match
*clone
;
548 /* First match of this type: */
552 /* Second and subsequent clones */
553 clone
= xtables_malloc(sizeof(struct xtables_match
));
554 memcpy(clone
, ptr
, sizeof(struct xtables_match
));
556 /* This is a clone: */
564 #ifndef NO_SHARED_LIBS
565 if (!ptr
&& tryload
!= XTF_DONT_LOAD
&& tryload
!= XTF_DURING_LOAD
) {
566 ptr
= load_extension(xtables_libdir
, afinfo
->libprefix
,
569 if (ptr
== NULL
&& tryload
== XTF_LOAD_MUST_SUCCEED
)
570 xt_params
->exit_err(PARAMETER_PROBLEM
,
571 "Couldn't load match `%s':%s\n",
575 if (ptr
&& !ptr
->loaded
) {
576 if (tryload
!= XTF_DONT_LOAD
)
581 if(!ptr
&& (tryload
== XTF_LOAD_MUST_SUCCEED
)) {
582 xt_params
->exit_err(PARAMETER_PROBLEM
,
583 "Couldn't find match `%s'\n", name
);
587 if (ptr
&& matches
) {
588 struct xtables_rule_match
**i
;
589 struct xtables_rule_match
*newentry
;
591 newentry
= xtables_malloc(sizeof(struct xtables_rule_match
));
593 for (i
= matches
; *i
; i
= &(*i
)->next
) {
594 if (strcmp(name
, (*i
)->match
->name
) == 0)
595 (*i
)->completed
= true;
597 newentry
->match
= ptr
;
598 newentry
->completed
= false;
599 newentry
->next
= NULL
;
606 struct xtables_target
*
607 xtables_find_target(const char *name
, enum xtables_tryload tryload
)
609 struct xtables_target
*ptr
;
611 /* Standard target? */
612 if (strcmp(name
, "") == 0
613 || strcmp(name
, XTC_LABEL_ACCEPT
) == 0
614 || strcmp(name
, XTC_LABEL_DROP
) == 0
615 || strcmp(name
, XTC_LABEL_QUEUE
) == 0
616 || strcmp(name
, XTC_LABEL_RETURN
) == 0)
619 for (ptr
= xtables_targets
; ptr
; ptr
= ptr
->next
) {
620 if (strcmp(name
, ptr
->name
) == 0)
624 #ifndef NO_SHARED_LIBS
625 if (!ptr
&& tryload
!= XTF_DONT_LOAD
&& tryload
!= XTF_DURING_LOAD
) {
626 ptr
= load_extension(xtables_libdir
, afinfo
->libprefix
,
629 if (ptr
== NULL
&& tryload
== XTF_LOAD_MUST_SUCCEED
)
630 xt_params
->exit_err(PARAMETER_PROBLEM
,
631 "Couldn't load target `%s':%s\n",
635 if (ptr
&& !ptr
->loaded
) {
636 if (tryload
!= XTF_DONT_LOAD
)
641 if (ptr
== NULL
&& tryload
== XTF_LOAD_MUST_SUCCEED
) {
642 xt_params
->exit_err(PARAMETER_PROBLEM
,
643 "Couldn't find target `%s'\n", name
);
653 static int compatible_revision(const char *name
, u_int8_t revision
, int opt
)
655 struct xt_get_revision rev
;
656 socklen_t s
= sizeof(rev
);
659 sockfd
= socket(afinfo
->family
, SOCK_RAW
, IPPROTO_RAW
);
661 if (errno
== EPERM
) {
662 /* revision 0 is always supported. */
664 fprintf(stderr
, "Could not determine whether "
665 "revision %u is supported, "
670 fprintf(stderr
, "Could not open socket to kernel: %s\n",
675 xtables_load_ko(xtables_modprobe_program
, true);
677 strcpy(rev
.name
, name
);
678 rev
.revision
= revision
;
680 max_rev
= getsockopt(sockfd
, afinfo
->ipproto
, opt
, &rev
, &s
);
682 /* Definitely don't support this? */
683 if (errno
== ENOENT
|| errno
== EPROTONOSUPPORT
) {
686 } else if (errno
== ENOPROTOOPT
) {
688 /* Assume only revision 0 support (old kernel) */
689 return (revision
== 0);
691 fprintf(stderr
, "getsockopt failed strangely: %s\n",
701 static int compatible_match_revision(const char *name
, u_int8_t revision
)
703 return compatible_revision(name
, revision
, afinfo
->so_rev_match
);
706 static int compatible_target_revision(const char *name
, u_int8_t revision
)
708 return compatible_revision(name
, revision
, afinfo
->so_rev_target
);
711 void xtables_register_match(struct xtables_match
*me
)
713 struct xtables_match
**i
, *old
;
715 if (strcmp(me
->version
, XTABLES_VERSION
) != 0) {
716 fprintf(stderr
, "%s: match \"%s\" has version \"%s\", "
717 "but \"%s\" is required.\n",
718 xt_params
->program_name
, me
->name
,
719 me
->version
, XTABLES_VERSION
);
723 /* Revision field stole a char from name. */
724 if (strlen(me
->name
) >= XT_FUNCTION_MAXNAMELEN
-1) {
725 fprintf(stderr
, "%s: target `%s' has invalid name\n",
726 xt_params
->program_name
, me
->name
);
730 if (me
->family
>= NPROTO
) {
732 "%s: BUG: match %s has invalid protocol family\n",
733 xt_params
->program_name
, me
->name
);
737 /* ignore not interested match */
738 if (me
->family
!= afinfo
->family
&& me
->family
!= AF_UNSPEC
)
741 old
= xtables_find_match(me
->name
, XTF_DURING_LOAD
, NULL
);
743 if (old
->revision
== me
->revision
&&
744 old
->family
== me
->family
) {
746 "%s: match `%s' already registered.\n",
747 xt_params
->program_name
, me
->name
);
751 /* Now we have two (or more) options, check compatibility. */
752 if (compatible_match_revision(old
->name
, old
->revision
)
753 && old
->revision
> me
->revision
)
756 /* See if new match can be used. */
757 if (!compatible_match_revision(me
->name
, me
->revision
))
760 /* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
761 if (old
->revision
== me
->revision
&& me
->family
== AF_UNSPEC
)
764 /* Delete old one. */
765 for (i
= &xtables_matches
; *i
!=old
; i
= &(*i
)->next
);
769 if (me
->size
!= XT_ALIGN(me
->size
)) {
770 fprintf(stderr
, "%s: match `%s' has invalid size %u.\n",
771 xt_params
->program_name
, me
->name
,
772 (unsigned int)me
->size
);
776 /* Append to list. */
777 for (i
= &xtables_matches
; *i
; i
= &(*i
)->next
);
785 void xtables_register_target(struct xtables_target
*me
)
787 struct xtables_target
*old
;
789 if (strcmp(me
->version
, XTABLES_VERSION
) != 0) {
790 fprintf(stderr
, "%s: target \"%s\" has version \"%s\", "
791 "but \"%s\" is required.\n",
792 xt_params
->program_name
, me
->name
,
793 me
->version
, XTABLES_VERSION
);
797 /* Revision field stole a char from name. */
798 if (strlen(me
->name
) >= XT_FUNCTION_MAXNAMELEN
-1) {
799 fprintf(stderr
, "%s: target `%s' has invalid name\n",
800 xt_params
->program_name
, me
->name
);
804 if (me
->family
>= NPROTO
) {
806 "%s: BUG: target %s has invalid protocol family\n",
807 xt_params
->program_name
, me
->name
);
811 /* ignore not interested target */
812 if (me
->family
!= afinfo
->family
&& me
->family
!= AF_UNSPEC
)
815 old
= xtables_find_target(me
->name
, XTF_DURING_LOAD
);
817 struct xtables_target
**i
;
819 if (old
->revision
== me
->revision
&&
820 old
->family
== me
->family
) {
822 "%s: target `%s' already registered.\n",
823 xt_params
->program_name
, me
->name
);
827 /* Now we have two (or more) options, check compatibility. */
828 if (compatible_target_revision(old
->name
, old
->revision
)
829 && old
->revision
> me
->revision
)
832 /* See if new target can be used. */
833 if (!compatible_target_revision(me
->name
, me
->revision
))
836 /* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
837 if (old
->revision
== me
->revision
&& me
->family
== AF_UNSPEC
)
840 /* Delete old one. */
841 for (i
= &xtables_targets
; *i
!=old
; i
= &(*i
)->next
);
845 if (me
->size
!= XT_ALIGN(me
->size
)) {
846 fprintf(stderr
, "%s: target `%s' has invalid size %u.\n",
847 xt_params
->program_name
, me
->name
,
848 (unsigned int)me
->size
);
852 /* Prepend to list. */
853 me
->next
= xtables_targets
;
854 xtables_targets
= me
;
860 * xtables_param_act - act on condition
861 * @status: a constant from enum xtables_exittype
863 * %XTF_ONLY_ONCE: print error message that option may only be used once.
864 * @p1: module name (e.g. "mark")
865 * @p2(...): option in conflict (e.g. "--mark")
866 * @p3(...): condition to match on (see extensions/ for examples)
868 * %XTF_NO_INVERT: option does not support inversion
870 * @p2: option in conflict
871 * @p3: condition to match on
873 * %XTF_BAD_VALUE: bad value for option
875 * @p2: option with which the problem occured (e.g. "--mark")
876 * @p3: string the user passed in (e.g. "99999999999999")
878 * %XTF_ONE_ACTION: two mutually exclusive actions have been specified
881 * Displays an error message and exits the program.
883 void xtables_param_act(unsigned int status
, const char *p1
, ...)
893 p2
= va_arg(args
, const char *);
894 b
= va_arg(args
, unsigned int);
897 xt_params
->exit_err(PARAMETER_PROBLEM
,
898 "%s: \"%s\" option may only be specified once",
902 p2
= va_arg(args
, const char *);
903 b
= va_arg(args
, unsigned int);
906 xt_params
->exit_err(PARAMETER_PROBLEM
,
907 "%s: \"%s\" option cannot be inverted", p1
, p2
);
910 p2
= va_arg(args
, const char *);
911 p3
= va_arg(args
, const char *);
912 xt_params
->exit_err(PARAMETER_PROBLEM
,
913 "%s: Bad value for \"%s\" option: \"%s\"",
917 b
= va_arg(args
, unsigned int);
920 xt_params
->exit_err(PARAMETER_PROBLEM
,
921 "%s: At most one action is possible", p1
);
924 xt_params
->exit_err(status
, p1
, args
);
931 const char *xtables_ipaddr_to_numeric(const struct in_addr
*addrp
)
934 const unsigned char *bytep
= (const void *)&addrp
->s_addr
;
936 sprintf(buf
, "%u.%u.%u.%u", bytep
[0], bytep
[1], bytep
[2], bytep
[3]);
940 static const char *ipaddr_to_host(const struct in_addr
*addr
)
942 struct hostent
*host
;
944 host
= gethostbyaddr(addr
, sizeof(struct in_addr
), AF_INET
);
951 static const char *ipaddr_to_network(const struct in_addr
*addr
)
955 if ((net
= getnetbyaddr(ntohl(addr
->s_addr
), AF_INET
)) != NULL
)
961 const char *xtables_ipaddr_to_anyname(const struct in_addr
*addr
)
965 if ((name
= ipaddr_to_host(addr
)) != NULL
||
966 (name
= ipaddr_to_network(addr
)) != NULL
)
969 return xtables_ipaddr_to_numeric(addr
);
972 const char *xtables_ipmask_to_numeric(const struct in_addr
*mask
)
975 uint32_t maskaddr
, bits
;
978 maskaddr
= ntohl(mask
->s_addr
);
980 if (maskaddr
== 0xFFFFFFFFL
)
981 /* we don't want to see "/32" */
986 while (--i
>= 0 && maskaddr
!= bits
)
989 sprintf(buf
, "/%d", i
);
991 /* mask was not a decent combination of 1's and 0's */
992 sprintf(buf
, "/%s", xtables_ipaddr_to_numeric(mask
));
997 static struct in_addr
*__numeric_to_ipaddr(const char *dotted
, bool is_mask
)
999 static struct in_addr addr
;
1000 unsigned char *addrp
;
1001 unsigned int onebyte
;
1002 char buf
[20], *p
, *q
;
1005 /* copy dotted string, because we need to modify it */
1006 strncpy(buf
, dotted
, sizeof(buf
) - 1);
1007 buf
[sizeof(buf
) - 1] = '\0';
1008 addrp
= (void *)&addr
.s_addr
;
1011 for (i
= 0; i
< 3; ++i
) {
1012 if ((q
= strchr(p
, '.')) == NULL
) {
1016 /* autocomplete, this is a network address */
1017 if (!xtables_strtoui(p
, NULL
, &onebyte
, 0, UINT8_MAX
))
1028 if (!xtables_strtoui(p
, NULL
, &onebyte
, 0, UINT8_MAX
))
1035 /* we have checked 3 bytes, now we check the last one */
1036 if (!xtables_strtoui(p
, NULL
, &onebyte
, 0, UINT8_MAX
))
1043 struct in_addr
*xtables_numeric_to_ipaddr(const char *dotted
)
1045 return __numeric_to_ipaddr(dotted
, false);
1048 struct in_addr
*xtables_numeric_to_ipmask(const char *dotted
)
1050 return __numeric_to_ipaddr(dotted
, true);
1053 static struct in_addr
*network_to_ipaddr(const char *name
)
1055 static struct in_addr addr
;
1058 if ((net
= getnetbyname(name
)) != NULL
) {
1059 if (net
->n_addrtype
!= AF_INET
)
1061 addr
.s_addr
= htonl(net
->n_net
);
1068 static struct in_addr
*host_to_ipaddr(const char *name
, unsigned int *naddr
)
1070 struct hostent
*host
;
1071 struct in_addr
*addr
;
1075 if ((host
= gethostbyname(name
)) != NULL
) {
1076 if (host
->h_addrtype
!= AF_INET
||
1077 host
->h_length
!= sizeof(struct in_addr
))
1080 while (host
->h_addr_list
[*naddr
] != NULL
)
1082 addr
= xtables_calloc(*naddr
, sizeof(struct in_addr
) * *naddr
);
1083 for (i
= 0; i
< *naddr
; i
++)
1084 memcpy(&addr
[i
], host
->h_addr_list
[i
],
1085 sizeof(struct in_addr
));
1092 static struct in_addr
*
1093 ipparse_hostnetwork(const char *name
, unsigned int *naddrs
)
1095 struct in_addr
*addrptmp
, *addrp
;
1097 if ((addrptmp
= xtables_numeric_to_ipaddr(name
)) != NULL
||
1098 (addrptmp
= network_to_ipaddr(name
)) != NULL
) {
1099 addrp
= xtables_malloc(sizeof(struct in_addr
));
1100 memcpy(addrp
, addrptmp
, sizeof(*addrp
));
1104 if ((addrptmp
= host_to_ipaddr(name
, naddrs
)) != NULL
)
1107 xt_params
->exit_err(PARAMETER_PROBLEM
, "host/network `%s' not found", name
);
1110 static struct in_addr
*parse_ipmask(const char *mask
)
1112 static struct in_addr maskaddr
;
1113 struct in_addr
*addrp
;
1117 /* no mask at all defaults to 32 bits */
1118 maskaddr
.s_addr
= 0xFFFFFFFF;
1121 if ((addrp
= xtables_numeric_to_ipmask(mask
)) != NULL
)
1122 /* dotted_to_addr already returns a network byte order addr */
1124 if (!xtables_strtoui(mask
, NULL
, &bits
, 0, 32))
1125 xt_params
->exit_err(PARAMETER_PROBLEM
,
1126 "invalid mask `%s' specified", mask
);
1128 maskaddr
.s_addr
= htonl(0xFFFFFFFF << (32 - bits
));
1132 maskaddr
.s_addr
= 0U;
1137 * xtables_ipparse_any - transform arbitrary name to in_addr
1139 * Possible inputs (pseudo regex):
1140 * m{^($hostname|$networkname|$ipaddr)(/$mask)?}
1141 * "1.2.3.4/5", "1.2.3.4", "hostname", "networkname"
1143 void xtables_ipparse_any(const char *name
, struct in_addr
**addrpp
,
1144 struct in_addr
*maskp
, unsigned int *naddrs
)
1146 unsigned int i
, j
, k
, n
;
1147 struct in_addr
*addrp
;
1150 strncpy(buf
, name
, sizeof(buf
) - 1);
1151 buf
[sizeof(buf
) - 1] = '\0';
1152 if ((p
= strrchr(buf
, '/')) != NULL
) {
1154 addrp
= parse_ipmask(p
+ 1);
1156 addrp
= parse_ipmask(NULL
);
1158 memcpy(maskp
, addrp
, sizeof(*maskp
));
1160 /* if a null mask is given, the name is ignored, like in "any/0" */
1161 if (maskp
->s_addr
== 0U)
1162 strcpy(buf
, "0.0.0.0");
1164 addrp
= *addrpp
= ipparse_hostnetwork(buf
, naddrs
);
1166 for (i
= 0, j
= 0; i
< n
; ++i
) {
1167 addrp
[j
++].s_addr
&= maskp
->s_addr
;
1168 for (k
= 0; k
< j
- 1; ++k
)
1169 if (addrp
[k
].s_addr
== addrp
[j
-1].s_addr
) {
1177 const char *xtables_ip6addr_to_numeric(const struct in6_addr
*addrp
)
1179 /* 0000:0000:0000:0000:0000:000.000.000.000
1180 * 0000:0000:0000:0000:0000:0000:0000:0000 */
1181 static char buf
[50+1];
1182 return inet_ntop(AF_INET6
, addrp
, buf
, sizeof(buf
));
1185 static const char *ip6addr_to_host(const struct in6_addr
*addr
)
1187 static char hostname
[NI_MAXHOST
];
1188 struct sockaddr_in6 saddr
;
1191 memset(&saddr
, 0, sizeof(struct sockaddr_in6
));
1192 memcpy(&saddr
.sin6_addr
, addr
, sizeof(*addr
));
1193 saddr
.sin6_family
= AF_INET6
;
1195 err
= getnameinfo((const void *)&saddr
, sizeof(struct sockaddr_in6
),
1196 hostname
, sizeof(hostname
) - 1, NULL
, 0, 0);
1199 fprintf(stderr
,"IP2Name: %s\n",gai_strerror(err
));
1205 fprintf (stderr
, "\naddr2host: %s\n", hostname
);
1210 const char *xtables_ip6addr_to_anyname(const struct in6_addr
*addr
)
1214 if ((name
= ip6addr_to_host(addr
)) != NULL
)
1217 return xtables_ip6addr_to_numeric(addr
);
1220 static int ip6addr_prefix_length(const struct in6_addr
*k
)
1222 unsigned int bits
= 0;
1223 uint32_t a
, b
, c
, d
;
1225 a
= ntohl(k
->s6_addr32
[0]);
1226 b
= ntohl(k
->s6_addr32
[1]);
1227 c
= ntohl(k
->s6_addr32
[2]);
1228 d
= ntohl(k
->s6_addr32
[3]);
1229 while (a
& 0x80000000U
) {
1239 if (a
!= 0 || b
!= 0 || c
!= 0 || d
!= 0)
1244 const char *xtables_ip6mask_to_numeric(const struct in6_addr
*addrp
)
1246 static char buf
[50+2];
1247 int l
= ip6addr_prefix_length(addrp
);
1251 strcat(buf
, xtables_ip6addr_to_numeric(addrp
));
1254 sprintf(buf
, "/%d", l
);
1258 struct in6_addr
*xtables_numeric_to_ip6addr(const char *num
)
1260 static struct in6_addr ap
;
1263 if ((err
= inet_pton(AF_INET6
, num
, &ap
)) == 1)
1266 fprintf(stderr
, "\nnumeric2addr: %d\n", err
);
1271 static struct in6_addr
*
1272 host_to_ip6addr(const char *name
, unsigned int *naddr
)
1274 static struct in6_addr
*addr
;
1275 struct addrinfo hints
;
1276 struct addrinfo
*res
;
1279 memset(&hints
, 0, sizeof(hints
));
1280 hints
.ai_flags
= AI_CANONNAME
;
1281 hints
.ai_family
= AF_INET6
;
1282 hints
.ai_socktype
= SOCK_RAW
;
1283 hints
.ai_protocol
= IPPROTO_IPV6
;
1284 hints
.ai_next
= NULL
;
1287 if ((err
= getaddrinfo(name
, NULL
, &hints
, &res
)) != 0) {
1289 fprintf(stderr
,"Name2IP: %s\n",gai_strerror(err
));
1293 if (res
->ai_family
!= AF_INET6
||
1294 res
->ai_addrlen
!= sizeof(struct sockaddr_in6
))
1298 fprintf(stderr
, "resolved: len=%d %s ", res
->ai_addrlen
,
1299 ip6addr_to_numeric(&((struct sockaddr_in6
*)res
->ai_addr
)->sin6_addr
));
1301 /* Get the first element of the address-chain */
1302 addr
= xtables_malloc(sizeof(struct in6_addr
));
1303 memcpy(addr
, &((const struct sockaddr_in6
*)res
->ai_addr
)->sin6_addr
,
1304 sizeof(struct in6_addr
));
1313 static struct in6_addr
*network_to_ip6addr(const char *name
)
1316 /* TODO: not implemented yet, but the exception breaks the
1317 * name resolvation */
1321 static struct in6_addr
*
1322 ip6parse_hostnetwork(const char *name
, unsigned int *naddrs
)
1324 struct in6_addr
*addrp
, *addrptmp
;
1326 if ((addrptmp
= xtables_numeric_to_ip6addr(name
)) != NULL
||
1327 (addrptmp
= network_to_ip6addr(name
)) != NULL
) {
1328 addrp
= xtables_malloc(sizeof(struct in6_addr
));
1329 memcpy(addrp
, addrptmp
, sizeof(*addrp
));
1333 if ((addrp
= host_to_ip6addr(name
, naddrs
)) != NULL
)
1336 xt_params
->exit_err(PARAMETER_PROBLEM
, "host/network `%s' not found", name
);
1339 static struct in6_addr
*parse_ip6mask(char *mask
)
1341 static struct in6_addr maskaddr
;
1342 struct in6_addr
*addrp
;
1346 /* no mask at all defaults to 128 bits */
1347 memset(&maskaddr
, 0xff, sizeof maskaddr
);
1350 if ((addrp
= xtables_numeric_to_ip6addr(mask
)) != NULL
)
1352 if (!xtables_strtoui(mask
, NULL
, &bits
, 0, 128))
1353 xt_params
->exit_err(PARAMETER_PROBLEM
,
1354 "invalid mask `%s' specified", mask
);
1356 char *p
= (void *)&maskaddr
;
1357 memset(p
, 0xff, bits
/ 8);
1358 memset(p
+ (bits
/ 8) + 1, 0, (128 - bits
) / 8);
1359 p
[bits
/8] = 0xff << (8 - (bits
& 7));
1363 memset(&maskaddr
, 0, sizeof(maskaddr
));
1367 void xtables_ip6parse_any(const char *name
, struct in6_addr
**addrpp
,
1368 struct in6_addr
*maskp
, unsigned int *naddrs
)
1370 static const struct in6_addr zero_addr
;
1371 struct in6_addr
*addrp
;
1372 unsigned int i
, j
, k
, n
;
1375 strncpy(buf
, name
, sizeof(buf
) - 1);
1376 buf
[sizeof(buf
)-1] = '\0';
1377 if ((p
= strrchr(buf
, '/')) != NULL
) {
1379 addrp
= parse_ip6mask(p
+ 1);
1381 addrp
= parse_ip6mask(NULL
);
1383 memcpy(maskp
, addrp
, sizeof(*maskp
));
1385 /* if a null mask is given, the name is ignored, like in "any/0" */
1386 if (memcmp(maskp
, &zero_addr
, sizeof(zero_addr
)) == 0)
1389 addrp
= *addrpp
= ip6parse_hostnetwork(buf
, naddrs
);
1391 for (i
= 0, j
= 0; i
< n
; ++i
) {
1392 for (k
= 0; k
< 4; ++k
)
1393 addrp
[j
].s6_addr32
[k
] &= maskp
->s6_addr32
[k
];
1395 for (k
= 0; k
< j
- 1; ++k
)
1396 if (IN6_ARE_ADDR_EQUAL(&addrp
[k
], &addrp
[j
- 1])) {
1404 void xtables_save_string(const char *value
)
1406 static const char no_quote_chars
[] = "_-0123456789"
1407 "abcdefghijklmnopqrstuvwxyz"
1408 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1409 static const char escape_chars
[] = "\"\\'";
1413 length
= strcspn(value
, no_quote_chars
);
1414 if (length
> 0 && value
[length
] == 0) {
1415 /* no quoting required */
1416 fputs(value
, stdout
);
1419 /* there is at least one dangerous character in the
1420 value, which we have to quote. Write double quotes
1421 around the value and escape special characters with
1425 for (p
= strpbrk(value
, escape_chars
); p
!= NULL
;
1426 p
= strpbrk(value
, escape_chars
)) {
1428 fwrite(value
, 1, p
- value
, stdout
);
1434 /* print the rest and finish the double quoted
1436 fputs(value
, stdout
);
1442 * Check for option-intrapositional negation.
1443 * Do not use in new code.
1445 int xtables_check_inverse(const char option
[], int *invert
,
1446 int *my_optind
, int argc
)
1448 if (option
&& strcmp(option
, "!") == 0) {
1449 fprintf(stderr
, "Using intrapositioned negation "
1450 "(`--option ! this`) is deprecated in favor of "
1451 "extrapositioned (`! --option this`).\n");
1454 xt_params
->exit_err(PARAMETER_PROBLEM
,
1455 "Multiple `!' flags not allowed");
1457 if (my_optind
!= NULL
) {
1459 if (argc
&& *my_optind
> argc
)
1460 xt_params
->exit_err(PARAMETER_PROBLEM
,
1461 "no argument following `!'");
1469 const struct xtables_pprot xtables_chain_protos
[] = {
1470 {"tcp", IPPROTO_TCP
},
1471 {"sctp", IPPROTO_SCTP
},
1472 {"udp", IPPROTO_UDP
},
1473 {"udplite", IPPROTO_UDPLITE
},
1474 {"icmp", IPPROTO_ICMP
},
1475 {"icmpv6", IPPROTO_ICMPV6
},
1476 {"ipv6-icmp", IPPROTO_ICMPV6
},
1477 {"esp", IPPROTO_ESP
},
1479 {"ipv6-mh", IPPROTO_MH
},
1486 xtables_parse_protocol(const char *s
)
1490 if (!xtables_strtoui(s
, NULL
, &proto
, 0, UINT8_MAX
)) {
1491 struct protoent
*pent
;
1493 /* first deal with the special case of 'all' to prevent
1494 * people from being able to redefine 'all' in nsswitch
1495 * and/or provoke expensive [not working] ldap/nis/...
1497 if (!strcmp(s
, "all"))
1500 if ((pent
= getprotobyname(s
)))
1501 proto
= pent
->p_proto
;
1504 for (i
= 0; i
< ARRAY_SIZE(xtables_chain_protos
); ++i
) {
1505 if (xtables_chain_protos
[i
].name
== NULL
)
1508 if (strcmp(s
, xtables_chain_protos
[i
].name
) == 0) {
1509 proto
= xtables_chain_protos
[i
].num
;
1513 if (i
== ARRAY_SIZE(xtables_chain_protos
))
1514 xt_params
->exit_err(PARAMETER_PROBLEM
,
1515 "unknown protocol `%s' specified",