blob: 5b7526c83b2e1f0a91972e0c1a36f6e66fcb3fec [file] [log] [blame]
Yasuyuki KOZAKAI52088062007-07-24 05:44:11 +00001/*
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., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
Yasuyuki KOZAKAI3dfa4482007-07-24 05:45:33 +000019#include <errno.h>
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +000020#include <fcntl.h>
Yasuyuki KOZAKAI04f8c542007-07-24 05:53:48 +000021#include <netdb.h>
Jan Engelhardtaafd2692008-01-20 13:19:40 +000022#include <stdarg.h>
Jan Engelhardtcd9e7aa2008-01-20 13:18:54 +000023#include <stdbool.h>
Yasuyuki KOZAKAI3dfa4482007-07-24 05:45:33 +000024#include <stdio.h>
25#include <stdlib.h>
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +000026#include <string.h>
27#include <unistd.h>
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +000028#include <sys/socket.h>
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +000029#include <sys/stat.h>
30#include <sys/types.h>
31#include <sys/wait.h>
Jan Engelhardt08b16162008-01-20 13:36:08 +000032#include <arpa/inet.h>
Yasuyuki KOZAKAI3dfa4482007-07-24 05:45:33 +000033
Yasuyuki KOZAKAI52088062007-07-24 05:44:11 +000034#include <xtables.h>
Jan Engelhardt4e418542009-02-21 03:46:37 +010035#include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */
Jan Engelhardt77f48c22009-02-07 19:59:53 +010036#include <linux/netfilter_ipv4/ip_tables.h>
37#include <linux/netfilter_ipv6/ip6_tables.h>
Jan Engelhardtef18e812008-08-04 12:47:48 +020038#include <libiptc/libxtc.h>
Yasuyuki KOZAKAI3dfa4482007-07-24 05:45:33 +000039
Mike Frysinger5a26b5f2007-12-19 14:51:17 +000040#ifndef NO_SHARED_LIBS
41#include <dlfcn.h>
42#endif
Jan Engelhardtc31870f2009-02-10 10:48:28 +010043#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)
46#endif
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
50#endif
Jamal Hadi Salim70581922009-02-13 08:36:44 -050051#include <getopt.h>
Jan Engelhardt1e128bd2011-01-08 02:25:28 +010052#include "xshared.h"
Mike Frysinger5a26b5f2007-12-19 14:51:17 +000053
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +000054#define NPROTO 255
55
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +000056#ifndef PROC_SYS_MODPROBE
57#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
58#endif
59
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +010060void basic_exit_err(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
61
Jamal Hadi Salim40a83432009-02-11 13:02:21 +010062struct xtables_globals *xt_params = NULL;
63
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +010064void basic_exit_err(enum xtables_exittype status, const char *msg, ...)
Jamal Hadi Salim40a83432009-02-11 13:02:21 +010065{
66 va_list args;
67
68 va_start(args, msg);
69 fprintf(stderr, "%s v%s: ", xt_params->program_name, xt_params->program_version);
70 vfprintf(stderr, msg, args);
71 va_end(args);
72 fprintf(stderr, "\n");
73 exit(status);
74}
75
Jan Engelhardt600f38d2010-10-29 18:57:42 +020076void xtables_free_opts(int unused)
Jamal Hadi Salim84c30552009-02-11 13:00:02 +010077{
Jan Engelhardt59e81142010-11-15 13:19:48 +010078 if (xt_params->opts != xt_params->orig_opts)
79 free(xt_params->opts);
Jamal Hadi Salim84c30552009-02-11 13:00:02 +010080}
81
Jan Engelhardt600f38d2010-10-29 18:57:42 +020082struct option *xtables_merge_options(struct option *orig_opts,
83 struct option *oldopts,
Jamal Hadi Salim70581922009-02-13 08:36:44 -050084 const struct option *newopts,
85 unsigned int *option_offset)
86{
Jan Engelhardt600f38d2010-10-29 18:57:42 +020087 unsigned int num_oold = 0, num_old = 0, num_new = 0, i;
88 struct option *merge, *mp;
Jamal Hadi Salim70581922009-02-13 08:36:44 -050089
90 if (newopts == NULL)
91 return oldopts;
92
Jan Engelhardt600f38d2010-10-29 18:57:42 +020093 for (num_oold = 0; orig_opts[num_oold].name; num_oold++) ;
94 if (oldopts != NULL)
95 for (num_old = 0; oldopts[num_old].name; num_old++) ;
Jamal Hadi Salim70581922009-02-13 08:36:44 -050096 for (num_new = 0; newopts[num_new].name; num_new++) ;
97
Jan Engelhardt1dc27392011-01-08 02:10:52 +010098 /*
99 * Since @oldopts also has @orig_opts already (and does so at the
100 * start), skip these entries.
101 */
102 oldopts += num_oold;
103 num_old -= num_oold;
104
Jan Engelhardt600f38d2010-10-29 18:57:42 +0200105 merge = malloc(sizeof(*mp) * (num_oold + num_old + num_new + 1));
Jamal Hadi Salim70581922009-02-13 08:36:44 -0500106 if (merge == NULL)
107 return NULL;
Jamal Hadi Salim70581922009-02-13 08:36:44 -0500108
Jan Engelhardt600f38d2010-10-29 18:57:42 +0200109 /* Let the base options -[ADI...] have precedence over everything */
110 memcpy(merge, orig_opts, sizeof(*mp) * num_oold);
111 mp = merge + num_oold;
112
113 /* Second, the new options */
Jan Engelhardt1e128bd2011-01-08 02:25:28 +0100114 xt_params->option_offset += XT_OPTION_OFFSET_SCALE;
Jan Engelhardt600f38d2010-10-29 18:57:42 +0200115 *option_offset = xt_params->option_offset;
116 memcpy(mp, newopts, sizeof(*mp) * num_new);
117
118 for (i = 0; i < num_new; ++i, ++mp)
119 mp->val += *option_offset;
120
121 /* Third, the old options */
122 memcpy(mp, oldopts, sizeof(*mp) * num_old);
123 mp += num_old;
124 xtables_free_opts(0);
125
126 /* Clear trailing entry */
127 memset(mp, 0, sizeof(*mp));
Jamal Hadi Salim70581922009-02-13 08:36:44 -0500128 return merge;
129}
130
Jan Engelhardtdacafa52009-01-27 20:56:23 +0100131/**
Jan Engelhardt77f48c22009-02-07 19:59:53 +0100132 * xtables_afinfo - protocol family dependent information
133 * @kmod: kernel module basename (e.g. "ip_tables")
134 * @libprefix: prefix of .so library name (e.g. "libipt_")
135 * @family: nfproto family
136 * @ipproto: used by setsockopt (e.g. IPPROTO_IP)
137 * @so_rev_match: optname to check revision support of match
138 * @so_rev_target: optname to check revision support of target
139 */
140struct xtables_afinfo {
141 const char *kmod;
142 const char *libprefix;
143 uint8_t family;
144 uint8_t ipproto;
145 int so_rev_match;
146 int so_rev_target;
147};
148
149static const struct xtables_afinfo afinfo_ipv4 = {
150 .kmod = "ip_tables",
151 .libprefix = "libipt_",
152 .family = NFPROTO_IPV4,
153 .ipproto = IPPROTO_IP,
154 .so_rev_match = IPT_SO_GET_REVISION_MATCH,
155 .so_rev_target = IPT_SO_GET_REVISION_TARGET,
156};
157
158static const struct xtables_afinfo afinfo_ipv6 = {
159 .kmod = "ip6_tables",
160 .libprefix = "libip6t_",
161 .family = NFPROTO_IPV6,
162 .ipproto = IPPROTO_IPV6,
163 .so_rev_match = IP6T_SO_GET_REVISION_MATCH,
164 .so_rev_target = IP6T_SO_GET_REVISION_TARGET,
165};
166
167static const struct xtables_afinfo *afinfo;
168
Jan Engelhardt39bf9c82009-01-27 15:59:06 +0100169/* Search path for Xtables .so files */
170static const char *xtables_libdir;
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000171
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +0000172/* the path to command to load kernel module */
Jan Engelhardtc021c3c2009-01-27 15:10:05 +0100173const char *xtables_modprobe_program;
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +0000174
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000175/* Keeping track of external matches and targets: linked lists. */
176struct xtables_match *xtables_matches;
177struct xtables_target *xtables_targets;
178
Jan Engelhardt39bf9c82009-01-27 15:59:06 +0100179void xtables_init(void)
180{
181 xtables_libdir = getenv("XTABLES_LIBDIR");
182 if (xtables_libdir != NULL)
183 return;
184 xtables_libdir = getenv("IPTABLES_LIB_DIR");
185 if (xtables_libdir != NULL) {
186 fprintf(stderr, "IPTABLES_LIB_DIR is deprecated, "
187 "use XTABLES_LIBDIR.\n");
188 return;
189 }
Jan Engelhardtec934192009-02-10 09:54:04 +0100190 /*
191 * Well yes, IP6TABLES_LIB_DIR is of lower priority over
192 * IPTABLES_LIB_DIR since this moved to libxtables; I think that is ok
193 * for these env vars are deprecated anyhow, and in light of the
194 * (shared) libxt_*.so files, makes less sense to have
195 * IPTABLES_LIB_DIR != IP6TABLES_LIB_DIR.
196 */
197 xtables_libdir = getenv("IP6TABLES_LIB_DIR");
198 if (xtables_libdir != NULL) {
199 fprintf(stderr, "IP6TABLES_LIB_DIR is deprecated, "
200 "use XTABLES_LIBDIR.\n");
201 return;
202 }
Jan Engelhardt39bf9c82009-01-27 15:59:06 +0100203 xtables_libdir = XTABLES_LIBDIR;
204}
205
Jan Engelhardt77f48c22009-02-07 19:59:53 +0100206void xtables_set_nfproto(uint8_t nfproto)
207{
208 switch (nfproto) {
209 case NFPROTO_IPV4:
210 afinfo = &afinfo_ipv4;
211 break;
212 case NFPROTO_IPV6:
213 afinfo = &afinfo_ipv6;
214 break;
215 default:
216 fprintf(stderr, "libxtables: unhandled NFPROTO in %s\n",
217 __func__);
218 }
219}
220
Jan Engelhardt630ef482009-01-27 14:58:41 +0100221/**
Jamal Hadi Salim7e4db2f2009-02-13 09:14:17 -0500222 * xtables_set_params - set the global parameters used by xtables
223 * @xtp: input xtables_globals structure
224 *
225 * The app is expected to pass a valid xtables_globals data-filled
226 * with proper values
227 * @xtp cannot be NULL
228 *
229 * Returns -1 on failure to set and 0 on success
230 */
231int xtables_set_params(struct xtables_globals *xtp)
232{
233 if (!xtp) {
234 fprintf(stderr, "%s: Illegal global params\n",__func__);
235 return -1;
236 }
237
238 xt_params = xtp;
239
240 if (!xt_params->exit_err)
241 xt_params->exit_err = basic_exit_err;
242
243 return 0;
244}
245
246int xtables_init_all(struct xtables_globals *xtp, uint8_t nfproto)
247{
248 xtables_init();
249 xtables_set_nfproto(nfproto);
250 return xtables_set_params(xtp);
251}
252
253/**
Jan Engelhardt630ef482009-01-27 14:58:41 +0100254 * xtables_*alloc - wrappers that exit on failure
255 */
256void *xtables_calloc(size_t count, size_t size)
Yasuyuki KOZAKAI3dfa4482007-07-24 05:45:33 +0000257{
258 void *p;
259
260 if ((p = calloc(count, size)) == NULL) {
261 perror("ip[6]tables: calloc failed");
262 exit(1);
263 }
264
265 return p;
266}
267
Jan Engelhardt630ef482009-01-27 14:58:41 +0100268void *xtables_malloc(size_t size)
Yasuyuki KOZAKAI3dfa4482007-07-24 05:45:33 +0000269{
270 void *p;
271
272 if ((p = malloc(size)) == NULL) {
273 perror("ip[6]tables: malloc failed");
274 exit(1);
275 }
276
277 return p;
278}
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +0000279
Michael Granzow332e4ac2009-04-09 18:24:36 +0100280void *xtables_realloc(void *ptr, size_t size)
281{
282 void *p;
283
284 if ((p = realloc(ptr, size)) == NULL) {
285 perror("ip[6]tables: realloc failed");
286 exit(1);
287 }
288
289 return p;
290}
291
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +0000292static char *get_modprobe(void)
293{
294 int procfile;
295 char *ret;
296
297#define PROCFILE_BUFSIZ 1024
298 procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
299 if (procfile < 0)
300 return NULL;
301
Jan Engelhardt371cea22010-07-25 23:36:17 +0200302 ret = malloc(PROCFILE_BUFSIZ);
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +0000303 if (ret) {
304 memset(ret, 0, PROCFILE_BUFSIZ);
305 switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
306 case -1: goto fail;
307 case PROCFILE_BUFSIZ: goto fail; /* Partial read. Wierd */
308 }
309 if (ret[strlen(ret)-1]=='\n')
310 ret[strlen(ret)-1]=0;
311 close(procfile);
312 return ret;
313 }
314 fail:
315 free(ret);
316 close(procfile);
317 return NULL;
318}
319
Jan Engelhardtc021c3c2009-01-27 15:10:05 +0100320int xtables_insmod(const char *modname, const char *modprobe, bool quiet)
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +0000321{
322 char *buf = NULL;
323 char *argv[4];
324 int status;
325
326 /* If they don't explicitly set it, read out of kernel */
327 if (!modprobe) {
328 buf = get_modprobe();
329 if (!buf)
330 return -1;
331 modprobe = buf;
332 }
333
Jan Engelhardtc19f8802009-02-12 01:28:35 +0100334 /*
335 * Need to flush the buffer, or the child may output it again
336 * when switching the program thru execv.
337 */
338 fflush(stdout);
339
Jan Engelhardt94aa2ea2009-10-11 03:56:18 -0400340 switch (vfork()) {
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +0000341 case 0:
342 argv[0] = (char *)modprobe;
343 argv[1] = (char *)modname;
344 if (quiet) {
345 argv[2] = "-q";
346 argv[3] = NULL;
347 } else {
348 argv[2] = NULL;
349 argv[3] = NULL;
350 }
351 execv(argv[0], argv);
352
353 /* not usually reached */
354 exit(1);
355 case -1:
356 return -1;
357
358 default: /* parent */
359 wait(&status);
360 }
361
362 free(buf);
363 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
364 return 0;
365 return -1;
366}
367
Jan Engelhardtc021c3c2009-01-27 15:10:05 +0100368int xtables_load_ko(const char *modprobe, bool quiet)
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000369{
Jan Engelhardtc021c3c2009-01-27 15:10:05 +0100370 static bool loaded = false;
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000371 static int ret = -1;
372
373 if (!loaded) {
Jan Engelhardt77f48c22009-02-07 19:59:53 +0100374 ret = xtables_insmod(afinfo->kmod, modprobe, quiet);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000375 loaded = (ret == 0);
376 }
377
378 return ret;
379}
380
Jan Engelhardt5f2922c2009-01-27 18:43:01 +0100381/**
382 * xtables_strtou{i,l} - string to number conversion
383 * @s: input string
384 * @end: like strtoul's "end" pointer
385 * @value: pointer for result
386 * @min: minimum accepted value
387 * @max: maximum accepted value
Jan Engelhardtcd9e7aa2008-01-20 13:18:54 +0000388 *
Jan Engelhardt5f2922c2009-01-27 18:43:01 +0100389 * If @end is NULL, we assume the caller wants a "strict strtoul", and hence
390 * "15a" is rejected.
391 * In either case, the value obtained is compared for min-max compliance.
392 * Base is always 0, i.e. autodetect depending on @s.
393 *
394 * Returns true/false whether number was accepted. On failure, *value has
395 * undefined contents.
Jan Engelhardtcd9e7aa2008-01-20 13:18:54 +0000396 */
Jan Engelhardt5f2922c2009-01-27 18:43:01 +0100397bool xtables_strtoul(const char *s, char **end, unsigned long *value,
398 unsigned long min, unsigned long max)
Jan Engelhardtcd9e7aa2008-01-20 13:18:54 +0000399{
400 unsigned long v;
401 char *my_end;
402
403 errno = 0;
404 v = strtoul(s, &my_end, 0);
405
406 if (my_end == s)
407 return false;
408 if (end != NULL)
409 *end = my_end;
410
411 if (errno != ERANGE && min <= v && (max == 0 || v <= max)) {
412 if (value != NULL)
413 *value = v;
414 if (end == NULL)
415 return *my_end == '\0';
416 return true;
417 }
418
419 return false;
420}
421
Jan Engelhardt5f2922c2009-01-27 18:43:01 +0100422bool xtables_strtoui(const char *s, char **end, unsigned int *value,
423 unsigned int min, unsigned int max)
Jan Engelhardtcd9e7aa2008-01-20 13:18:54 +0000424{
425 unsigned long v;
426 bool ret;
427
Jan Engelhardt5f2922c2009-01-27 18:43:01 +0100428 ret = xtables_strtoul(s, end, &v, min, max);
Jan Engelhardtcd9e7aa2008-01-20 13:18:54 +0000429 if (value != NULL)
430 *value = v;
431 return ret;
432}
433
Jan Engelhardtaae6be92009-01-30 04:24:47 +0100434int xtables_service_to_port(const char *name, const char *proto)
Yasuyuki KOZAKAI04f8c542007-07-24 05:53:48 +0000435{
436 struct servent *service;
437
438 if ((service = getservbyname(name, proto)) != NULL)
439 return ntohs((unsigned short) service->s_port);
440
441 return -1;
442}
443
Jan Engelhardt7ac40522011-01-07 12:34:04 +0100444uint16_t xtables_parse_port(const char *port, const char *proto)
Yasuyuki KOZAKAI04f8c542007-07-24 05:53:48 +0000445{
Jan Engelhardt7a236f42008-03-03 12:30:41 +0100446 unsigned int portnum;
Yasuyuki KOZAKAI04f8c542007-07-24 05:53:48 +0000447
Jan Engelhardt5f2922c2009-01-27 18:43:01 +0100448 if (xtables_strtoui(port, NULL, &portnum, 0, UINT16_MAX) ||
Jan Engelhardtaae6be92009-01-30 04:24:47 +0100449 (portnum = xtables_service_to_port(port, proto)) != (unsigned)-1)
Jan Engelhardt213e1852009-01-27 17:24:34 +0100450 return portnum;
Yasuyuki KOZAKAI04f8c542007-07-24 05:53:48 +0000451
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +0100452 xt_params->exit_err(PARAMETER_PROBLEM,
Yasuyuki KOZAKAI04f8c542007-07-24 05:53:48 +0000453 "invalid port/service `%s' specified", port);
454}
455
Jan Engelhardtaae6be92009-01-30 04:24:47 +0100456void xtables_parse_interface(const char *arg, char *vianame,
457 unsigned char *mask)
Yasuyuki KOZAKAI04f8c542007-07-24 05:53:48 +0000458{
Jan Engelhardtfcf57232010-02-09 15:59:13 +0100459 unsigned int vialen = strlen(arg);
Yasuyuki KOZAKAI04f8c542007-07-24 05:53:48 +0000460 unsigned int i;
461
462 memset(mask, 0, IFNAMSIZ);
463 memset(vianame, 0, IFNAMSIZ);
464
465 if (vialen + 1 > IFNAMSIZ)
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +0100466 xt_params->exit_err(PARAMETER_PROBLEM,
Yasuyuki KOZAKAI04f8c542007-07-24 05:53:48 +0000467 "interface name `%s' must be shorter than IFNAMSIZ"
468 " (%i)", arg, IFNAMSIZ-1);
469
470 strcpy(vianame, arg);
Jan Engelhardtfcf57232010-02-09 15:59:13 +0100471 if (vialen == 0)
Yasuyuki KOZAKAI04f8c542007-07-24 05:53:48 +0000472 memset(mask, 0, IFNAMSIZ);
473 else if (vianame[vialen - 1] == '+') {
474 memset(mask, 0xFF, vialen - 1);
475 memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
476 /* Don't remove `+' here! -HW */
477 } else {
478 /* Include nul-terminator in match */
479 memset(mask, 0xFF, vialen + 1);
480 memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
481 for (i = 0; vianame[i]; i++) {
Jan Engelhardtfcf57232010-02-09 15:59:13 +0100482 if (vianame[i] == '/' ||
483 vianame[i] == ' ') {
Max Kellermannaae4f822007-10-17 16:36:49 +0000484 fprintf(stderr,
485 "Warning: weird character in interface"
Jan Engelhardtfcf57232010-02-09 15:59:13 +0100486 " `%s' ('/' and ' ' are not allowed by the kernel).\n",
Max Kellermannaae4f822007-10-17 16:36:49 +0000487 vianame);
Yasuyuki KOZAKAI04f8c542007-07-24 05:53:48 +0000488 break;
489 }
490 }
491 }
492}
493
Jan Engelhardtcb25af82008-04-14 18:37:57 +0200494#ifndef NO_SHARED_LIBS
Jan Engelhardt21b41ee2008-02-11 01:02:00 +0100495static void *load_extension(const char *search_path, const char *prefix,
496 const char *name, bool is_target)
497{
498 const char *dir = search_path, *next;
499 void *ptr = NULL;
500 struct stat sb;
501 char path[256];
502
503 do {
504 next = strchr(dir, ':');
505 if (next == NULL)
506 next = dir + strlen(dir);
507 snprintf(path, sizeof(path), "%.*s/libxt_%s.so",
Jan Engelhardt2c0a0c92008-04-14 15:58:17 +0200508 (unsigned int)(next - dir), dir, name);
Jan Engelhardt21b41ee2008-02-11 01:02:00 +0100509
510 if (dlopen(path, RTLD_NOW) != NULL) {
511 /* Found library. If it didn't register itself,
512 maybe they specified target as match. */
513 if (is_target)
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100514 ptr = xtables_find_target(name, XTF_DONT_LOAD);
Jan Engelhardt21b41ee2008-02-11 01:02:00 +0100515 else
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100516 ptr = xtables_find_match(name,
517 XTF_DONT_LOAD, NULL);
Jan Engelhardt21b41ee2008-02-11 01:02:00 +0100518 } else if (stat(path, &sb) == 0) {
519 fprintf(stderr, "%s: %s\n", path, dlerror());
520 }
521
522 if (ptr != NULL)
523 return ptr;
524
525 snprintf(path, sizeof(path), "%.*s/%s%s.so",
Jan Engelhardt2c0a0c92008-04-14 15:58:17 +0200526 (unsigned int)(next - dir), dir, prefix, name);
Jan Engelhardt21b41ee2008-02-11 01:02:00 +0100527 if (dlopen(path, RTLD_NOW) != NULL) {
528 if (is_target)
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100529 ptr = xtables_find_target(name, XTF_DONT_LOAD);
Jan Engelhardt21b41ee2008-02-11 01:02:00 +0100530 else
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100531 ptr = xtables_find_match(name,
532 XTF_DONT_LOAD, NULL);
Jan Engelhardt21b41ee2008-02-11 01:02:00 +0100533 } else if (stat(path, &sb) == 0) {
534 fprintf(stderr, "%s: %s\n", path, dlerror());
535 }
536
537 if (ptr != NULL)
538 return ptr;
539
540 dir = next + 1;
541 } while (*next != '\0');
542
543 return NULL;
544}
Jan Engelhardtcb25af82008-04-14 18:37:57 +0200545#endif
Jan Engelhardt21b41ee2008-02-11 01:02:00 +0100546
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100547struct xtables_match *
548xtables_find_match(const char *name, enum xtables_tryload tryload,
549 struct xtables_rule_match **matches)
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000550{
551 struct xtables_match *ptr;
552 const char *icmp6 = "icmp6";
553
Jan Engelhardt0cb675b2010-06-07 11:50:25 +0200554 if (strlen(name) >= XT_EXTENSION_MAXNAMELEN)
Jan Engelhardt21d12832010-03-16 16:49:21 +0100555 xtables_error(PARAMETER_PROBLEM,
556 "Invalid match name \"%s\" (%u chars max)",
Jan Engelhardt0cb675b2010-06-07 11:50:25 +0200557 name, XT_EXTENSION_MAXNAMELEN - 1);
Jan Engelhardt21d12832010-03-16 16:49:21 +0100558
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000559 /* This is ugly as hell. Nonetheless, there is no way of changing
560 * this without hurting backwards compatibility */
561 if ( (strcmp(name,"icmpv6") == 0) ||
562 (strcmp(name,"ipv6-icmp") == 0) ||
563 (strcmp(name,"icmp6") == 0) )
564 name = icmp6;
565
566 for (ptr = xtables_matches; ptr; ptr = ptr->next) {
567 if (strcmp(name, ptr->name) == 0) {
568 struct xtables_match *clone;
569
570 /* First match of this type: */
571 if (ptr->m == NULL)
572 break;
573
574 /* Second and subsequent clones */
Jan Engelhardt630ef482009-01-27 14:58:41 +0100575 clone = xtables_malloc(sizeof(struct xtables_match));
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000576 memcpy(clone, ptr, sizeof(struct xtables_match));
577 clone->mflags = 0;
578 /* This is a clone: */
579 clone->next = clone;
580
581 ptr = clone;
582 break;
583 }
584 }
585
586#ifndef NO_SHARED_LIBS
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100587 if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
Jan Engelhardt77f48c22009-02-07 19:59:53 +0100588 ptr = load_extension(xtables_libdir, afinfo->libprefix,
Jan Engelhardt39bf9c82009-01-27 15:59:06 +0100589 name, false);
Yasuyuki KOZAKAI170af8c2007-08-04 05:22:17 +0000590
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100591 if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +0100592 xt_params->exit_err(PARAMETER_PROBLEM,
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000593 "Couldn't load match `%s':%s\n",
594 name, dlerror());
595 }
596#else
597 if (ptr && !ptr->loaded) {
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100598 if (tryload != XTF_DONT_LOAD)
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000599 ptr->loaded = 1;
600 else
601 ptr = NULL;
602 }
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100603 if(!ptr && (tryload == XTF_LOAD_MUST_SUCCEED)) {
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +0100604 xt_params->exit_err(PARAMETER_PROBLEM,
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000605 "Couldn't find match `%s'\n", name);
606 }
607#endif
608
609 if (ptr && matches) {
610 struct xtables_rule_match **i;
611 struct xtables_rule_match *newentry;
612
Jan Engelhardt630ef482009-01-27 14:58:41 +0100613 newentry = xtables_malloc(sizeof(struct xtables_rule_match));
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000614
615 for (i = matches; *i; i = &(*i)->next) {
616 if (strcmp(name, (*i)->match->name) == 0)
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100617 (*i)->completed = true;
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000618 }
619 newentry->match = ptr;
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100620 newentry->completed = false;
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000621 newentry->next = NULL;
622 *i = newentry;
623 }
624
625 return ptr;
626}
627
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100628struct xtables_target *
629xtables_find_target(const char *name, enum xtables_tryload tryload)
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000630{
631 struct xtables_target *ptr;
632
633 /* Standard target? */
634 if (strcmp(name, "") == 0
635 || strcmp(name, XTC_LABEL_ACCEPT) == 0
636 || strcmp(name, XTC_LABEL_DROP) == 0
637 || strcmp(name, XTC_LABEL_QUEUE) == 0
638 || strcmp(name, XTC_LABEL_RETURN) == 0)
639 name = "standard";
640
641 for (ptr = xtables_targets; ptr; ptr = ptr->next) {
642 if (strcmp(name, ptr->name) == 0)
643 break;
644 }
645
646#ifndef NO_SHARED_LIBS
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100647 if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
Jan Engelhardt77f48c22009-02-07 19:59:53 +0100648 ptr = load_extension(xtables_libdir, afinfo->libprefix,
Jan Engelhardt39bf9c82009-01-27 15:59:06 +0100649 name, true);
Yasuyuki KOZAKAI170af8c2007-08-04 05:22:17 +0000650
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100651 if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +0100652 xt_params->exit_err(PARAMETER_PROBLEM,
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000653 "Couldn't load target `%s':%s\n",
654 name, dlerror());
655 }
656#else
657 if (ptr && !ptr->loaded) {
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100658 if (tryload != XTF_DONT_LOAD)
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000659 ptr->loaded = 1;
660 else
661 ptr = NULL;
662 }
Peter Volkov854d2d92009-03-24 11:09:16 +0300663 if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED) {
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +0100664 xt_params->exit_err(PARAMETER_PROBLEM,
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000665 "Couldn't find target `%s'\n", name);
666 }
667#endif
668
669 if (ptr)
670 ptr->used = 1;
671
672 return ptr;
673}
674
Jan Engelhardt7ac40522011-01-07 12:34:04 +0100675static int compatible_revision(const char *name, uint8_t revision, int opt)
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000676{
677 struct xt_get_revision rev;
678 socklen_t s = sizeof(rev);
679 int max_rev, sockfd;
680
Jan Engelhardt77f48c22009-02-07 19:59:53 +0100681 sockfd = socket(afinfo->family, SOCK_RAW, IPPROTO_RAW);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000682 if (sockfd < 0) {
Patrick McHardydf1ef382007-12-03 15:32:28 +0000683 if (errno == EPERM) {
684 /* revision 0 is always supported. */
685 if (revision != 0)
686 fprintf(stderr, "Could not determine whether "
687 "revision %u is supported, "
688 "assuming it is.\n",
689 revision);
690 return 1;
691 }
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000692 fprintf(stderr, "Could not open socket to kernel: %s\n",
693 strerror(errno));
694 exit(1);
695 }
696
Jan Engelhardtc021c3c2009-01-27 15:10:05 +0100697 xtables_load_ko(xtables_modprobe_program, true);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000698
699 strcpy(rev.name, name);
700 rev.revision = revision;
701
Jan Engelhardt77f48c22009-02-07 19:59:53 +0100702 max_rev = getsockopt(sockfd, afinfo->ipproto, opt, &rev, &s);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000703 if (max_rev < 0) {
704 /* Definitely don't support this? */
705 if (errno == ENOENT || errno == EPROTONOSUPPORT) {
706 close(sockfd);
707 return 0;
708 } else if (errno == ENOPROTOOPT) {
709 close(sockfd);
710 /* Assume only revision 0 support (old kernel) */
711 return (revision == 0);
712 } else {
713 fprintf(stderr, "getsockopt failed strangely: %s\n",
714 strerror(errno));
715 exit(1);
716 }
717 }
718 close(sockfd);
719 return 1;
720}
721
722
Jan Engelhardt7ac40522011-01-07 12:34:04 +0100723static int compatible_match_revision(const char *name, uint8_t revision)
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000724{
Jan Engelhardt77f48c22009-02-07 19:59:53 +0100725 return compatible_revision(name, revision, afinfo->so_rev_match);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000726}
727
Jan Engelhardt7ac40522011-01-07 12:34:04 +0100728static int compatible_target_revision(const char *name, uint8_t revision)
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000729{
Jan Engelhardt77f48c22009-02-07 19:59:53 +0100730 return compatible_revision(name, revision, afinfo->so_rev_target);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000731}
732
733void xtables_register_match(struct xtables_match *me)
734{
735 struct xtables_match **i, *old;
736
Jan Engelhardtc284de52009-06-25 21:25:24 +0200737 if (me->version == NULL) {
738 fprintf(stderr, "%s: match %s<%u> is missing a version\n",
739 xt_params->program_name, me->name, me->revision);
740 exit(1);
741 }
Jan Engelhardtdacafa52009-01-27 20:56:23 +0100742 if (strcmp(me->version, XTABLES_VERSION) != 0) {
743 fprintf(stderr, "%s: match \"%s\" has version \"%s\", "
744 "but \"%s\" is required.\n",
Jamal Hadi Salim5dd19de2009-02-13 10:42:24 -0500745 xt_params->program_name, me->name,
Jan Engelhardtdacafa52009-01-27 20:56:23 +0100746 me->version, XTABLES_VERSION);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000747 exit(1);
748 }
749
Jan Engelhardt0cb675b2010-06-07 11:50:25 +0200750 if (strlen(me->name) >= XT_EXTENSION_MAXNAMELEN) {
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000751 fprintf(stderr, "%s: target `%s' has invalid name\n",
Jamal Hadi Salim5dd19de2009-02-13 10:42:24 -0500752 xt_params->program_name, me->name);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000753 exit(1);
754 }
755
756 if (me->family >= NPROTO) {
757 fprintf(stderr,
758 "%s: BUG: match %s has invalid protocol family\n",
Jamal Hadi Salim5dd19de2009-02-13 10:42:24 -0500759 xt_params->program_name, me->name);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000760 exit(1);
761 }
762
763 /* ignore not interested match */
Jan Engelhardt77f48c22009-02-07 19:59:53 +0100764 if (me->family != afinfo->family && me->family != AF_UNSPEC)
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000765 return;
766
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100767 old = xtables_find_match(me->name, XTF_DURING_LOAD, NULL);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000768 if (old) {
Jan Engelhardt23545c22008-02-14 04:23:04 +0100769 if (old->revision == me->revision &&
770 old->family == me->family) {
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000771 fprintf(stderr,
772 "%s: match `%s' already registered.\n",
Jamal Hadi Salim5dd19de2009-02-13 10:42:24 -0500773 xt_params->program_name, me->name);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000774 exit(1);
775 }
776
777 /* Now we have two (or more) options, check compatibility. */
778 if (compatible_match_revision(old->name, old->revision)
779 && old->revision > me->revision)
780 return;
781
Jan Engelhardt23545c22008-02-14 04:23:04 +0100782 /* See if new match can be used. */
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000783 if (!compatible_match_revision(me->name, me->revision))
784 return;
785
Jan Engelhardt23545c22008-02-14 04:23:04 +0100786 /* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
787 if (old->revision == me->revision && me->family == AF_UNSPEC)
788 return;
789
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000790 /* Delete old one. */
791 for (i = &xtables_matches; *i!=old; i = &(*i)->next);
792 *i = old->next;
793 }
794
795 if (me->size != XT_ALIGN(me->size)) {
796 fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
Jamal Hadi Salim5dd19de2009-02-13 10:42:24 -0500797 xt_params->program_name, me->name,
798 (unsigned int)me->size);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000799 exit(1);
800 }
801
802 /* Append to list. */
803 for (i = &xtables_matches; *i; i = &(*i)->next);
804 me->next = NULL;
805 *i = me;
806
807 me->m = NULL;
808 me->mflags = 0;
809}
810
Jan Engelhardt9a8fc4f2009-06-25 17:13:46 +0200811void xtables_register_matches(struct xtables_match *match, unsigned int n)
812{
813 do {
814 xtables_register_match(&match[--n]);
815 } while (n > 0);
816}
817
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000818void xtables_register_target(struct xtables_target *me)
819{
820 struct xtables_target *old;
821
Jan Engelhardtc284de52009-06-25 21:25:24 +0200822 if (me->version == NULL) {
823 fprintf(stderr, "%s: target %s<%u> is missing a version\n",
824 xt_params->program_name, me->name, me->revision);
825 exit(1);
826 }
Jan Engelhardtdacafa52009-01-27 20:56:23 +0100827 if (strcmp(me->version, XTABLES_VERSION) != 0) {
828 fprintf(stderr, "%s: target \"%s\" has version \"%s\", "
829 "but \"%s\" is required.\n",
Jamal Hadi Salim5dd19de2009-02-13 10:42:24 -0500830 xt_params->program_name, me->name,
Jan Engelhardtdacafa52009-01-27 20:56:23 +0100831 me->version, XTABLES_VERSION);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000832 exit(1);
833 }
834
Jan Engelhardt0cb675b2010-06-07 11:50:25 +0200835 if (strlen(me->name) >= XT_EXTENSION_MAXNAMELEN) {
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000836 fprintf(stderr, "%s: target `%s' has invalid name\n",
Jamal Hadi Salim5dd19de2009-02-13 10:42:24 -0500837 xt_params->program_name, me->name);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000838 exit(1);
839 }
840
841 if (me->family >= NPROTO) {
842 fprintf(stderr,
843 "%s: BUG: target %s has invalid protocol family\n",
Jamal Hadi Salim5dd19de2009-02-13 10:42:24 -0500844 xt_params->program_name, me->name);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000845 exit(1);
846 }
847
848 /* ignore not interested target */
Jan Engelhardt77f48c22009-02-07 19:59:53 +0100849 if (me->family != afinfo->family && me->family != AF_UNSPEC)
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000850 return;
851
Jan Engelhardt2338efd2009-01-27 15:23:01 +0100852 old = xtables_find_target(me->name, XTF_DURING_LOAD);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000853 if (old) {
854 struct xtables_target **i;
855
Jan Engelhardt23545c22008-02-14 04:23:04 +0100856 if (old->revision == me->revision &&
857 old->family == me->family) {
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000858 fprintf(stderr,
859 "%s: target `%s' already registered.\n",
Jamal Hadi Salim5dd19de2009-02-13 10:42:24 -0500860 xt_params->program_name, me->name);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000861 exit(1);
862 }
863
864 /* Now we have two (or more) options, check compatibility. */
865 if (compatible_target_revision(old->name, old->revision)
866 && old->revision > me->revision)
867 return;
868
Jan Engelhardt23545c22008-02-14 04:23:04 +0100869 /* See if new target can be used. */
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000870 if (!compatible_target_revision(me->name, me->revision))
871 return;
872
Jan Engelhardt23545c22008-02-14 04:23:04 +0100873 /* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
874 if (old->revision == me->revision && me->family == AF_UNSPEC)
875 return;
876
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000877 /* Delete old one. */
878 for (i = &xtables_targets; *i!=old; i = &(*i)->next);
879 *i = old->next;
880 }
881
882 if (me->size != XT_ALIGN(me->size)) {
883 fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
Jamal Hadi Salim5dd19de2009-02-13 10:42:24 -0500884 xt_params->program_name, me->name,
885 (unsigned int)me->size);
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000886 exit(1);
887 }
888
889 /* Prepend to list. */
890 me->next = xtables_targets;
891 xtables_targets = me;
892 me->t = NULL;
893 me->tflags = 0;
894}
Jan Engelhardtaafd2692008-01-20 13:19:40 +0000895
Jan Engelhardt9a8fc4f2009-06-25 17:13:46 +0200896void xtables_register_targets(struct xtables_target *target, unsigned int n)
897{
898 do {
899 xtables_register_target(&target[--n]);
900 } while (n > 0);
901}
902
Jan Engelhardta41545c2009-01-27 21:27:19 +0100903/**
904 * xtables_param_act - act on condition
905 * @status: a constant from enum xtables_exittype
906 *
907 * %XTF_ONLY_ONCE: print error message that option may only be used once.
908 * @p1: module name (e.g. "mark")
909 * @p2(...): option in conflict (e.g. "--mark")
910 * @p3(...): condition to match on (see extensions/ for examples)
911 *
912 * %XTF_NO_INVERT: option does not support inversion
913 * @p1: module name
914 * @p2: option in conflict
915 * @p3: condition to match on
916 *
917 * %XTF_BAD_VALUE: bad value for option
918 * @p1: module name
919 * @p2: option with which the problem occured (e.g. "--mark")
920 * @p3: string the user passed in (e.g. "99999999999999")
921 *
922 * %XTF_ONE_ACTION: two mutually exclusive actions have been specified
923 * @p1: module name
924 *
925 * Displays an error message and exits the program.
926 */
927void xtables_param_act(unsigned int status, const char *p1, ...)
Jan Engelhardtaafd2692008-01-20 13:19:40 +0000928{
929 const char *p2, *p3;
930 va_list args;
931 bool b;
932
933 va_start(args, p1);
934
935 switch (status) {
Jan Engelhardta41545c2009-01-27 21:27:19 +0100936 case XTF_ONLY_ONCE:
Jan Engelhardtaafd2692008-01-20 13:19:40 +0000937 p2 = va_arg(args, const char *);
938 b = va_arg(args, unsigned int);
939 if (!b)
940 return;
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +0100941 xt_params->exit_err(PARAMETER_PROBLEM,
Jan Engelhardtaafd2692008-01-20 13:19:40 +0000942 "%s: \"%s\" option may only be specified once",
943 p1, p2);
944 break;
Jan Engelhardta41545c2009-01-27 21:27:19 +0100945 case XTF_NO_INVERT:
Jan Engelhardtaafd2692008-01-20 13:19:40 +0000946 p2 = va_arg(args, const char *);
947 b = va_arg(args, unsigned int);
948 if (!b)
949 return;
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +0100950 xt_params->exit_err(PARAMETER_PROBLEM,
Jan Engelhardtaafd2692008-01-20 13:19:40 +0000951 "%s: \"%s\" option cannot be inverted", p1, p2);
952 break;
Jan Engelhardta41545c2009-01-27 21:27:19 +0100953 case XTF_BAD_VALUE:
Jan Engelhardtaafd2692008-01-20 13:19:40 +0000954 p2 = va_arg(args, const char *);
955 p3 = va_arg(args, const char *);
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +0100956 xt_params->exit_err(PARAMETER_PROBLEM,
Jan Engelhardtaafd2692008-01-20 13:19:40 +0000957 "%s: Bad value for \"%s\" option: \"%s\"",
958 p1, p2, p3);
959 break;
Jan Engelhardta41545c2009-01-27 21:27:19 +0100960 case XTF_ONE_ACTION:
Jan Engelhardtaafd2692008-01-20 13:19:40 +0000961 b = va_arg(args, unsigned int);
962 if (!b)
963 return;
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +0100964 xt_params->exit_err(PARAMETER_PROBLEM,
Jan Engelhardtaafd2692008-01-20 13:19:40 +0000965 "%s: At most one action is possible", p1);
966 break;
967 default:
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +0100968 xt_params->exit_err(status, p1, args);
Jan Engelhardtaafd2692008-01-20 13:19:40 +0000969 break;
970 }
971
972 va_end(args);
973}
Jan Engelhardt08b16162008-01-20 13:36:08 +0000974
Jan Engelhardte44ea7f2009-01-30 03:55:09 +0100975const char *xtables_ipaddr_to_numeric(const struct in_addr *addrp)
Jan Engelhardt08b16162008-01-20 13:36:08 +0000976{
977 static char buf[20];
978 const unsigned char *bytep = (const void *)&addrp->s_addr;
979
980 sprintf(buf, "%u.%u.%u.%u", bytep[0], bytep[1], bytep[2], bytep[3]);
981 return buf;
982}
983
984static const char *ipaddr_to_host(const struct in_addr *addr)
985{
986 struct hostent *host;
987
988 host = gethostbyaddr(addr, sizeof(struct in_addr), AF_INET);
989 if (host == NULL)
990 return NULL;
991
992 return host->h_name;
993}
994
995static const char *ipaddr_to_network(const struct in_addr *addr)
996{
997 struct netent *net;
998
999 if ((net = getnetbyaddr(ntohl(addr->s_addr), AF_INET)) != NULL)
1000 return net->n_name;
1001
1002 return NULL;
1003}
1004
Jan Engelhardte44ea7f2009-01-30 03:55:09 +01001005const char *xtables_ipaddr_to_anyname(const struct in_addr *addr)
Jan Engelhardt08b16162008-01-20 13:36:08 +00001006{
1007 const char *name;
1008
1009 if ((name = ipaddr_to_host(addr)) != NULL ||
1010 (name = ipaddr_to_network(addr)) != NULL)
1011 return name;
1012
Jan Engelhardte44ea7f2009-01-30 03:55:09 +01001013 return xtables_ipaddr_to_numeric(addr);
Jan Engelhardt08b16162008-01-20 13:36:08 +00001014}
1015
Jan Engelhardte44ea7f2009-01-30 03:55:09 +01001016const char *xtables_ipmask_to_numeric(const struct in_addr *mask)
Jan Engelhardt08b16162008-01-20 13:36:08 +00001017{
1018 static char buf[20];
1019 uint32_t maskaddr, bits;
1020 int i;
1021
1022 maskaddr = ntohl(mask->s_addr);
1023
1024 if (maskaddr == 0xFFFFFFFFL)
1025 /* we don't want to see "/32" */
1026 return "";
1027
1028 i = 32;
1029 bits = 0xFFFFFFFEL;
1030 while (--i >= 0 && maskaddr != bits)
1031 bits <<= 1;
1032 if (i >= 0)
1033 sprintf(buf, "/%d", i);
1034 else
1035 /* mask was not a decent combination of 1's and 0's */
Jan Engelhardte44ea7f2009-01-30 03:55:09 +01001036 sprintf(buf, "/%s", xtables_ipaddr_to_numeric(mask));
Jan Engelhardt08b16162008-01-20 13:36:08 +00001037
1038 return buf;
1039}
1040
Jan Engelhardtbd943842008-01-20 13:38:08 +00001041static struct in_addr *__numeric_to_ipaddr(const char *dotted, bool is_mask)
1042{
1043 static struct in_addr addr;
1044 unsigned char *addrp;
1045 unsigned int onebyte;
1046 char buf[20], *p, *q;
1047 int i;
1048
1049 /* copy dotted string, because we need to modify it */
1050 strncpy(buf, dotted, sizeof(buf) - 1);
1051 buf[sizeof(buf) - 1] = '\0';
1052 addrp = (void *)&addr.s_addr;
1053
1054 p = buf;
1055 for (i = 0; i < 3; ++i) {
1056 if ((q = strchr(p, '.')) == NULL) {
1057 if (is_mask)
1058 return NULL;
1059
1060 /* autocomplete, this is a network address */
Jan Engelhardt5f2922c2009-01-27 18:43:01 +01001061 if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
Jan Engelhardtbd943842008-01-20 13:38:08 +00001062 return NULL;
1063
1064 addrp[i] = onebyte;
1065 while (i < 3)
1066 addrp[++i] = 0;
1067
1068 return &addr;
1069 }
1070
1071 *q = '\0';
Jan Engelhardt5f2922c2009-01-27 18:43:01 +01001072 if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
Jan Engelhardtbd943842008-01-20 13:38:08 +00001073 return NULL;
1074
1075 addrp[i] = onebyte;
1076 p = q + 1;
1077 }
1078
1079 /* we have checked 3 bytes, now we check the last one */
Jan Engelhardt5f2922c2009-01-27 18:43:01 +01001080 if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
Jan Engelhardtbd943842008-01-20 13:38:08 +00001081 return NULL;
1082
1083 addrp[3] = onebyte;
1084 return &addr;
1085}
1086
Jan Engelhardt1e01b0b2009-01-30 04:20:32 +01001087struct in_addr *xtables_numeric_to_ipaddr(const char *dotted)
Jan Engelhardtbd943842008-01-20 13:38:08 +00001088{
1089 return __numeric_to_ipaddr(dotted, false);
1090}
1091
Jan Engelhardt1e01b0b2009-01-30 04:20:32 +01001092struct in_addr *xtables_numeric_to_ipmask(const char *dotted)
Jan Engelhardtbd943842008-01-20 13:38:08 +00001093{
1094 return __numeric_to_ipaddr(dotted, true);
1095}
1096
1097static struct in_addr *network_to_ipaddr(const char *name)
1098{
1099 static struct in_addr addr;
1100 struct netent *net;
1101
1102 if ((net = getnetbyname(name)) != NULL) {
1103 if (net->n_addrtype != AF_INET)
1104 return NULL;
1105 addr.s_addr = htonl(net->n_net);
1106 return &addr;
1107 }
1108
1109 return NULL;
1110}
1111
1112static struct in_addr *host_to_ipaddr(const char *name, unsigned int *naddr)
1113{
1114 struct hostent *host;
1115 struct in_addr *addr;
1116 unsigned int i;
1117
1118 *naddr = 0;
1119 if ((host = gethostbyname(name)) != NULL) {
1120 if (host->h_addrtype != AF_INET ||
1121 host->h_length != sizeof(struct in_addr))
1122 return NULL;
1123
1124 while (host->h_addr_list[*naddr] != NULL)
1125 ++*naddr;
Jan Engelhardt630ef482009-01-27 14:58:41 +01001126 addr = xtables_calloc(*naddr, sizeof(struct in_addr) * *naddr);
Jan Engelhardtbd943842008-01-20 13:38:08 +00001127 for (i = 0; i < *naddr; i++)
1128 memcpy(&addr[i], host->h_addr_list[i],
1129 sizeof(struct in_addr));
1130 return addr;
1131 }
1132
1133 return NULL;
1134}
1135
1136static struct in_addr *
1137ipparse_hostnetwork(const char *name, unsigned int *naddrs)
1138{
1139 struct in_addr *addrptmp, *addrp;
1140
Jan Engelhardt1e01b0b2009-01-30 04:20:32 +01001141 if ((addrptmp = xtables_numeric_to_ipaddr(name)) != NULL ||
Jan Engelhardtbd943842008-01-20 13:38:08 +00001142 (addrptmp = network_to_ipaddr(name)) != NULL) {
Jan Engelhardt630ef482009-01-27 14:58:41 +01001143 addrp = xtables_malloc(sizeof(struct in_addr));
Jan Engelhardtbd943842008-01-20 13:38:08 +00001144 memcpy(addrp, addrptmp, sizeof(*addrp));
1145 *naddrs = 1;
1146 return addrp;
1147 }
1148 if ((addrptmp = host_to_ipaddr(name, naddrs)) != NULL)
1149 return addrptmp;
1150
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +01001151 xt_params->exit_err(PARAMETER_PROBLEM, "host/network `%s' not found", name);
Jan Engelhardtbd943842008-01-20 13:38:08 +00001152}
1153
1154static struct in_addr *parse_ipmask(const char *mask)
1155{
1156 static struct in_addr maskaddr;
1157 struct in_addr *addrp;
1158 unsigned int bits;
1159
1160 if (mask == NULL) {
1161 /* no mask at all defaults to 32 bits */
1162 maskaddr.s_addr = 0xFFFFFFFF;
1163 return &maskaddr;
1164 }
Jan Engelhardt1e01b0b2009-01-30 04:20:32 +01001165 if ((addrp = xtables_numeric_to_ipmask(mask)) != NULL)
Jan Engelhardtbd943842008-01-20 13:38:08 +00001166 /* dotted_to_addr already returns a network byte order addr */
1167 return addrp;
Jan Engelhardt5f2922c2009-01-27 18:43:01 +01001168 if (!xtables_strtoui(mask, NULL, &bits, 0, 32))
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +01001169 xt_params->exit_err(PARAMETER_PROBLEM,
Jan Engelhardtbd943842008-01-20 13:38:08 +00001170 "invalid mask `%s' specified", mask);
1171 if (bits != 0) {
1172 maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
1173 return &maskaddr;
1174 }
1175
1176 maskaddr.s_addr = 0U;
1177 return &maskaddr;
1178}
1179
Michael Granzow332e4ac2009-04-09 18:24:36 +01001180void xtables_ipparse_multiple(const char *name, struct in_addr **addrpp,
1181 struct in_addr **maskpp, unsigned int *naddrs)
1182{
1183 struct in_addr *addrp;
1184 char buf[256], *p;
1185 unsigned int len, i, j, n, count = 1;
1186 const char *loop = name;
1187
1188 while ((loop = strchr(loop, ',')) != NULL) {
1189 ++count;
1190 ++loop; /* skip ',' */
1191 }
1192
1193 *addrpp = xtables_malloc(sizeof(struct in_addr) * count);
1194 *maskpp = xtables_malloc(sizeof(struct in_addr) * count);
1195
1196 loop = name;
1197
1198 for (i = 0; i < count; ++i) {
1199 if (loop == NULL)
1200 break;
1201 if (*loop == ',')
1202 ++loop;
1203 if (*loop == '\0')
1204 break;
1205 p = strchr(loop, ',');
1206 if (p != NULL)
1207 len = p - loop;
1208 else
1209 len = strlen(loop);
1210 if (len == 0 || sizeof(buf) - 1 < len)
1211 break;
1212
1213 strncpy(buf, loop, len);
1214 buf[len] = '\0';
1215 loop += len;
1216 if ((p = strrchr(buf, '/')) != NULL) {
1217 *p = '\0';
1218 addrp = parse_ipmask(p + 1);
1219 } else {
1220 addrp = parse_ipmask(NULL);
1221 }
1222 memcpy(*maskpp + i, addrp, sizeof(*addrp));
1223
1224 /* if a null mask is given, the name is ignored, like in "any/0" */
1225 if ((*maskpp + i)->s_addr == 0)
1226 /*
1227 * A bit pointless to process multiple addresses
1228 * in this case...
1229 */
1230 strcpy(buf, "0.0.0.0");
1231
1232 addrp = ipparse_hostnetwork(buf, &n);
1233 if (n > 1) {
1234 count += n - 1;
1235 *addrpp = xtables_realloc(*addrpp,
1236 sizeof(struct in_addr) * count);
1237 *maskpp = xtables_realloc(*maskpp,
1238 sizeof(struct in_addr) * count);
1239 for (j = 0; j < n; ++j)
1240 /* for each new addr */
1241 memcpy(*addrpp + i + j, addrp + j,
1242 sizeof(*addrp));
1243 for (j = 1; j < n; ++j)
1244 /* for each new mask */
1245 memcpy(*maskpp + i + j, *maskpp + i,
1246 sizeof(*addrp));
1247 i += n - 1;
1248 } else {
1249 memcpy(*addrpp + i, addrp, sizeof(*addrp));
1250 }
1251 /* free what ipparse_hostnetwork had allocated: */
1252 free(addrp);
1253 }
1254 *naddrs = count;
1255 for (i = 0; i < n; ++i)
1256 (*addrpp+i)->s_addr &= (*maskpp+i)->s_addr;
1257}
1258
1259
Jan Engelhardta0baae82009-01-30 04:32:50 +01001260/**
1261 * xtables_ipparse_any - transform arbitrary name to in_addr
1262 *
1263 * Possible inputs (pseudo regex):
1264 * m{^($hostname|$networkname|$ipaddr)(/$mask)?}
1265 * "1.2.3.4/5", "1.2.3.4", "hostname", "networkname"
1266 */
1267void xtables_ipparse_any(const char *name, struct in_addr **addrpp,
1268 struct in_addr *maskp, unsigned int *naddrs)
Jan Engelhardtbd943842008-01-20 13:38:08 +00001269{
1270 unsigned int i, j, k, n;
1271 struct in_addr *addrp;
1272 char buf[256], *p;
1273
1274 strncpy(buf, name, sizeof(buf) - 1);
1275 buf[sizeof(buf) - 1] = '\0';
1276 if ((p = strrchr(buf, '/')) != NULL) {
1277 *p = '\0';
1278 addrp = parse_ipmask(p + 1);
1279 } else {
1280 addrp = parse_ipmask(NULL);
1281 }
1282 memcpy(maskp, addrp, sizeof(*maskp));
1283
1284 /* if a null mask is given, the name is ignored, like in "any/0" */
1285 if (maskp->s_addr == 0U)
1286 strcpy(buf, "0.0.0.0");
1287
1288 addrp = *addrpp = ipparse_hostnetwork(buf, naddrs);
1289 n = *naddrs;
1290 for (i = 0, j = 0; i < n; ++i) {
1291 addrp[j++].s_addr &= maskp->s_addr;
1292 for (k = 0; k < j - 1; ++k)
1293 if (addrp[k].s_addr == addrp[j-1].s_addr) {
1294 --*naddrs;
1295 --j;
1296 break;
1297 }
1298 }
1299}
1300
Jan Engelhardte44ea7f2009-01-30 03:55:09 +01001301const char *xtables_ip6addr_to_numeric(const struct in6_addr *addrp)
Jan Engelhardt08b16162008-01-20 13:36:08 +00001302{
1303 /* 0000:0000:0000:0000:0000:000.000.000.000
1304 * 0000:0000:0000:0000:0000:0000:0000:0000 */
1305 static char buf[50+1];
1306 return inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
1307}
1308
1309static const char *ip6addr_to_host(const struct in6_addr *addr)
1310{
1311 static char hostname[NI_MAXHOST];
1312 struct sockaddr_in6 saddr;
1313 int err;
1314
1315 memset(&saddr, 0, sizeof(struct sockaddr_in6));
1316 memcpy(&saddr.sin6_addr, addr, sizeof(*addr));
1317 saddr.sin6_family = AF_INET6;
1318
1319 err = getnameinfo((const void *)&saddr, sizeof(struct sockaddr_in6),
1320 hostname, sizeof(hostname) - 1, NULL, 0, 0);
1321 if (err != 0) {
1322#ifdef DEBUG
1323 fprintf(stderr,"IP2Name: %s\n",gai_strerror(err));
1324#endif
1325 return NULL;
1326 }
1327
1328#ifdef DEBUG
1329 fprintf (stderr, "\naddr2host: %s\n", hostname);
1330#endif
1331 return hostname;
1332}
1333
Jan Engelhardte44ea7f2009-01-30 03:55:09 +01001334const char *xtables_ip6addr_to_anyname(const struct in6_addr *addr)
Jan Engelhardt08b16162008-01-20 13:36:08 +00001335{
1336 const char *name;
1337
1338 if ((name = ip6addr_to_host(addr)) != NULL)
1339 return name;
1340
Jan Engelhardte44ea7f2009-01-30 03:55:09 +01001341 return xtables_ip6addr_to_numeric(addr);
Jan Engelhardt08b16162008-01-20 13:36:08 +00001342}
1343
1344static int ip6addr_prefix_length(const struct in6_addr *k)
1345{
1346 unsigned int bits = 0;
1347 uint32_t a, b, c, d;
1348
Jan Engelhardt48607812008-06-10 15:17:53 +02001349 a = ntohl(k->s6_addr32[0]);
1350 b = ntohl(k->s6_addr32[1]);
1351 c = ntohl(k->s6_addr32[2]);
1352 d = ntohl(k->s6_addr32[3]);
Jan Engelhardt08b16162008-01-20 13:36:08 +00001353 while (a & 0x80000000U) {
1354 ++bits;
1355 a <<= 1;
1356 a |= (b >> 31) & 1;
1357 b <<= 1;
1358 b |= (c >> 31) & 1;
1359 c <<= 1;
1360 c |= (d >> 31) & 1;
1361 d <<= 1;
1362 }
1363 if (a != 0 || b != 0 || c != 0 || d != 0)
1364 return -1;
1365 return bits;
1366}
1367
Jan Engelhardte44ea7f2009-01-30 03:55:09 +01001368const char *xtables_ip6mask_to_numeric(const struct in6_addr *addrp)
Jan Engelhardt08b16162008-01-20 13:36:08 +00001369{
1370 static char buf[50+2];
1371 int l = ip6addr_prefix_length(addrp);
1372
1373 if (l == -1) {
1374 strcpy(buf, "/");
Jan Engelhardte44ea7f2009-01-30 03:55:09 +01001375 strcat(buf, xtables_ip6addr_to_numeric(addrp));
Jan Engelhardt08b16162008-01-20 13:36:08 +00001376 return buf;
1377 }
1378 sprintf(buf, "/%d", l);
1379 return buf;
1380}
Jan Engelhardtbd943842008-01-20 13:38:08 +00001381
Jan Engelhardt1e01b0b2009-01-30 04:20:32 +01001382struct in6_addr *xtables_numeric_to_ip6addr(const char *num)
Jan Engelhardtbd943842008-01-20 13:38:08 +00001383{
1384 static struct in6_addr ap;
1385 int err;
1386
1387 if ((err = inet_pton(AF_INET6, num, &ap)) == 1)
1388 return &ap;
1389#ifdef DEBUG
1390 fprintf(stderr, "\nnumeric2addr: %d\n", err);
1391#endif
1392 return NULL;
1393}
1394
1395static struct in6_addr *
1396host_to_ip6addr(const char *name, unsigned int *naddr)
1397{
1398 static struct in6_addr *addr;
1399 struct addrinfo hints;
1400 struct addrinfo *res;
1401 int err;
1402
1403 memset(&hints, 0, sizeof(hints));
1404 hints.ai_flags = AI_CANONNAME;
1405 hints.ai_family = AF_INET6;
1406 hints.ai_socktype = SOCK_RAW;
1407 hints.ai_protocol = IPPROTO_IPV6;
1408 hints.ai_next = NULL;
1409
1410 *naddr = 0;
1411 if ((err = getaddrinfo(name, NULL, &hints, &res)) != 0) {
1412#ifdef DEBUG
1413 fprintf(stderr,"Name2IP: %s\n",gai_strerror(err));
1414#endif
1415 return NULL;
1416 } else {
1417 if (res->ai_family != AF_INET6 ||
1418 res->ai_addrlen != sizeof(struct sockaddr_in6))
1419 return NULL;
1420
1421#ifdef DEBUG
1422 fprintf(stderr, "resolved: len=%d %s ", res->ai_addrlen,
Patrick McHardy30290ae2010-05-20 15:41:03 +02001423 xtables_ip6addr_to_numeric(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr));
Jan Engelhardtbd943842008-01-20 13:38:08 +00001424#endif
1425 /* Get the first element of the address-chain */
Jan Engelhardt630ef482009-01-27 14:58:41 +01001426 addr = xtables_malloc(sizeof(struct in6_addr));
Jan Engelhardtbd943842008-01-20 13:38:08 +00001427 memcpy(addr, &((const struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
1428 sizeof(struct in6_addr));
1429 freeaddrinfo(res);
1430 *naddr = 1;
1431 return addr;
1432 }
1433
1434 return NULL;
1435}
1436
1437static struct in6_addr *network_to_ip6addr(const char *name)
1438{
1439 /* abort();*/
1440 /* TODO: not implemented yet, but the exception breaks the
1441 * name resolvation */
1442 return NULL;
1443}
1444
1445static struct in6_addr *
1446ip6parse_hostnetwork(const char *name, unsigned int *naddrs)
1447{
1448 struct in6_addr *addrp, *addrptmp;
1449
Jan Engelhardt1e01b0b2009-01-30 04:20:32 +01001450 if ((addrptmp = xtables_numeric_to_ip6addr(name)) != NULL ||
Jan Engelhardtbd943842008-01-20 13:38:08 +00001451 (addrptmp = network_to_ip6addr(name)) != NULL) {
Jan Engelhardt630ef482009-01-27 14:58:41 +01001452 addrp = xtables_malloc(sizeof(struct in6_addr));
Jan Engelhardtbd943842008-01-20 13:38:08 +00001453 memcpy(addrp, addrptmp, sizeof(*addrp));
1454 *naddrs = 1;
1455 return addrp;
1456 }
1457 if ((addrp = host_to_ip6addr(name, naddrs)) != NULL)
1458 return addrp;
1459
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +01001460 xt_params->exit_err(PARAMETER_PROBLEM, "host/network `%s' not found", name);
Jan Engelhardtbd943842008-01-20 13:38:08 +00001461}
1462
1463static struct in6_addr *parse_ip6mask(char *mask)
1464{
1465 static struct in6_addr maskaddr;
1466 struct in6_addr *addrp;
1467 unsigned int bits;
1468
1469 if (mask == NULL) {
1470 /* no mask at all defaults to 128 bits */
1471 memset(&maskaddr, 0xff, sizeof maskaddr);
1472 return &maskaddr;
1473 }
Jan Engelhardt1e01b0b2009-01-30 04:20:32 +01001474 if ((addrp = xtables_numeric_to_ip6addr(mask)) != NULL)
Jan Engelhardtbd943842008-01-20 13:38:08 +00001475 return addrp;
Jan Engelhardt5f2922c2009-01-27 18:43:01 +01001476 if (!xtables_strtoui(mask, NULL, &bits, 0, 128))
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +01001477 xt_params->exit_err(PARAMETER_PROBLEM,
Jan Engelhardtbd943842008-01-20 13:38:08 +00001478 "invalid mask `%s' specified", mask);
1479 if (bits != 0) {
1480 char *p = (void *)&maskaddr;
1481 memset(p, 0xff, bits / 8);
1482 memset(p + (bits / 8) + 1, 0, (128 - bits) / 8);
1483 p[bits/8] = 0xff << (8 - (bits & 7));
1484 return &maskaddr;
1485 }
1486
1487 memset(&maskaddr, 0, sizeof(maskaddr));
1488 return &maskaddr;
1489}
1490
Michael Granzow332e4ac2009-04-09 18:24:36 +01001491void
1492xtables_ip6parse_multiple(const char *name, struct in6_addr **addrpp,
1493 struct in6_addr **maskpp, unsigned int *naddrs)
1494{
Olaf Rempel58df9012009-09-20 13:24:11 +02001495 static const struct in6_addr zero_addr;
Michael Granzow332e4ac2009-04-09 18:24:36 +01001496 struct in6_addr *addrp;
1497 char buf[256], *p;
1498 unsigned int len, i, j, n, count = 1;
1499 const char *loop = name;
1500
1501 while ((loop = strchr(loop, ',')) != NULL) {
1502 ++count;
1503 ++loop; /* skip ',' */
1504 }
1505
1506 *addrpp = xtables_malloc(sizeof(struct in6_addr) * count);
1507 *maskpp = xtables_malloc(sizeof(struct in6_addr) * count);
1508
1509 loop = name;
1510
1511 for (i = 0; i < count /*NB: count can grow*/; ++i) {
1512 if (loop == NULL)
1513 break;
1514 if (*loop == ',')
1515 ++loop;
1516 if (*loop == '\0')
1517 break;
1518 p = strchr(loop, ',');
1519 if (p != NULL)
1520 len = p - loop;
1521 else
1522 len = strlen(loop);
1523 if (len == 0 || sizeof(buf) - 1 < len)
1524 break;
1525
1526 strncpy(buf, loop, len);
1527 buf[len] = '\0';
1528 loop += len;
1529 if ((p = strrchr(buf, '/')) != NULL) {
1530 *p = '\0';
1531 addrp = parse_ip6mask(p + 1);
1532 } else {
1533 addrp = parse_ip6mask(NULL);
1534 }
1535 memcpy(*maskpp + i, addrp, sizeof(*addrp));
1536
1537 /* if a null mask is given, the name is ignored, like in "any/0" */
Olaf Rempel58df9012009-09-20 13:24:11 +02001538 if (memcmp(*maskpp + i, &zero_addr, sizeof(zero_addr)) == 0)
Michael Granzow332e4ac2009-04-09 18:24:36 +01001539 strcpy(buf, "::");
1540
1541 addrp = ip6parse_hostnetwork(buf, &n);
1542 /* ip6parse_hostnetwork only ever returns one IP
1543 address (it exits if the resolution fails).
1544 Therefore, n will always be 1 here. Leaving the
1545 code below in anyway in case ip6parse_hostnetwork
1546 is improved some day to behave like
1547 ipparse_hostnetwork: */
1548 if (n > 1) {
1549 count += n - 1;
1550 *addrpp = xtables_realloc(*addrpp,
1551 sizeof(struct in6_addr) * count);
1552 *maskpp = xtables_realloc(*maskpp,
1553 sizeof(struct in6_addr) * count);
1554 for (j = 0; j < n; ++j)
1555 /* for each new addr */
1556 memcpy(*addrpp + i + j, addrp + j,
1557 sizeof(*addrp));
1558 for (j = 1; j < n; ++j)
1559 /* for each new mask */
1560 memcpy(*maskpp + i + j, *maskpp + i,
1561 sizeof(*addrp));
1562 i += n - 1;
1563 } else {
1564 memcpy(*addrpp + i, addrp, sizeof(*addrp));
1565 }
1566 /* free what ip6parse_hostnetwork had allocated: */
1567 free(addrp);
1568 }
1569 *naddrs = count;
1570 for (i = 0; i < n; ++i)
1571 for (j = 0; j < 4; ++j)
1572 (*addrpp+i)->s6_addr32[j] &= (*maskpp+i)->s6_addr32[j];
1573}
1574
Jan Engelhardta0baae82009-01-30 04:32:50 +01001575void xtables_ip6parse_any(const char *name, struct in6_addr **addrpp,
1576 struct in6_addr *maskp, unsigned int *naddrs)
Jan Engelhardtbd943842008-01-20 13:38:08 +00001577{
Jan Engelhardt9c0fa7d2009-04-03 22:40:35 +02001578 static const struct in6_addr zero_addr;
Jan Engelhardtbd943842008-01-20 13:38:08 +00001579 struct in6_addr *addrp;
1580 unsigned int i, j, k, n;
1581 char buf[256], *p;
1582
1583 strncpy(buf, name, sizeof(buf) - 1);
1584 buf[sizeof(buf)-1] = '\0';
1585 if ((p = strrchr(buf, '/')) != NULL) {
1586 *p = '\0';
1587 addrp = parse_ip6mask(p + 1);
1588 } else {
1589 addrp = parse_ip6mask(NULL);
1590 }
1591 memcpy(maskp, addrp, sizeof(*maskp));
1592
1593 /* if a null mask is given, the name is ignored, like in "any/0" */
Jan Engelhardt9c0fa7d2009-04-03 22:40:35 +02001594 if (memcmp(maskp, &zero_addr, sizeof(zero_addr)) == 0)
Jan Engelhardtbd943842008-01-20 13:38:08 +00001595 strcpy(buf, "::");
1596
1597 addrp = *addrpp = ip6parse_hostnetwork(buf, naddrs);
1598 n = *naddrs;
1599 for (i = 0, j = 0; i < n; ++i) {
1600 for (k = 0; k < 4; ++k)
Yasuyuki Kozakai5a2208c2008-06-04 15:16:03 +02001601 addrp[j].s6_addr32[k] &= maskp->s6_addr32[k];
Jan Engelhardtbd943842008-01-20 13:38:08 +00001602 ++j;
1603 for (k = 0; k < j - 1; ++k)
1604 if (IN6_ARE_ADDR_EQUAL(&addrp[k], &addrp[j - 1])) {
1605 --*naddrs;
1606 --j;
1607 break;
1608 }
1609 }
1610}
Max Kellermanna5d09942008-01-29 13:44:34 +00001611
Jan Engelhardta0baae82009-01-30 04:32:50 +01001612void xtables_save_string(const char *value)
Max Kellermanna5d09942008-01-29 13:44:34 +00001613{
1614 static const char no_quote_chars[] = "_-0123456789"
1615 "abcdefghijklmnopqrstuvwxyz"
1616 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1617 static const char escape_chars[] = "\"\\'";
1618 size_t length;
1619 const char *p;
1620
1621 length = strcspn(value, no_quote_chars);
1622 if (length > 0 && value[length] == 0) {
1623 /* no quoting required */
1624 fputs(value, stdout);
1625 putchar(' ');
1626 } else {
1627 /* there is at least one dangerous character in the
1628 value, which we have to quote. Write double quotes
1629 around the value and escape special characters with
1630 a backslash */
1631 putchar('"');
1632
1633 for (p = strpbrk(value, escape_chars); p != NULL;
1634 p = strpbrk(value, escape_chars)) {
1635 if (p > value)
1636 fwrite(value, 1, p - value, stdout);
1637 putchar('\\');
1638 putchar(*p);
1639 value = p + 1;
1640 }
1641
1642 /* print the rest and finish the double quoted
1643 string */
1644 fputs(value, stdout);
1645 printf("\" ");
1646 }
1647}
Jan Engelhardt0f16c722009-01-30 04:55:38 +01001648
1649/**
1650 * Check for option-intrapositional negation.
1651 * Do not use in new code.
1652 */
1653int xtables_check_inverse(const char option[], int *invert,
Jan Engelhardtbf971282009-11-03 19:55:11 +01001654 int *my_optind, int argc, char **argv)
Jan Engelhardt0f16c722009-01-30 04:55:38 +01001655{
Jan Engelhardt2be22fb2009-10-24 00:08:09 +02001656 if (option == NULL || strcmp(option, "!") != 0)
1657 return false;
Jan Engelhardt0f16c722009-01-30 04:55:38 +01001658
Jan Engelhardt2be22fb2009-10-24 00:08:09 +02001659 fprintf(stderr, "Using intrapositioned negation "
1660 "(`--option ! this`) is deprecated in favor of "
1661 "extrapositioned (`! --option this`).\n");
1662
1663 if (*invert)
1664 xt_params->exit_err(PARAMETER_PROBLEM,
1665 "Multiple `!' flags not allowed");
1666 *invert = true;
1667 if (my_optind != NULL) {
Jan Engelhardtbf971282009-11-03 19:55:11 +01001668 optarg = argv[*my_optind];
Jan Engelhardt2be22fb2009-10-24 00:08:09 +02001669 ++*my_optind;
1670 if (argc && *my_optind > argc)
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +01001671 xt_params->exit_err(PARAMETER_PROBLEM,
Jan Engelhardt2be22fb2009-10-24 00:08:09 +02001672 "no argument following `!'");
Jan Engelhardt0f16c722009-01-30 04:55:38 +01001673 }
Jan Engelhardt2be22fb2009-10-24 00:08:09 +02001674
1675 return true;
Jan Engelhardt0f16c722009-01-30 04:55:38 +01001676}
Jan Engelhardt1de7edf2009-01-30 05:38:11 +01001677
1678const struct xtables_pprot xtables_chain_protos[] = {
1679 {"tcp", IPPROTO_TCP},
1680 {"sctp", IPPROTO_SCTP},
1681 {"udp", IPPROTO_UDP},
1682 {"udplite", IPPROTO_UDPLITE},
1683 {"icmp", IPPROTO_ICMP},
1684 {"icmpv6", IPPROTO_ICMPV6},
1685 {"ipv6-icmp", IPPROTO_ICMPV6},
1686 {"esp", IPPROTO_ESP},
1687 {"ah", IPPROTO_AH},
1688 {"ipv6-mh", IPPROTO_MH},
1689 {"mh", IPPROTO_MH},
1690 {"all", 0},
1691 {NULL},
1692};
1693
Jan Engelhardt7ac40522011-01-07 12:34:04 +01001694uint16_t
Jan Engelhardt1de7edf2009-01-30 05:38:11 +01001695xtables_parse_protocol(const char *s)
1696{
1697 unsigned int proto;
1698
1699 if (!xtables_strtoui(s, NULL, &proto, 0, UINT8_MAX)) {
1700 struct protoent *pent;
1701
1702 /* first deal with the special case of 'all' to prevent
1703 * people from being able to redefine 'all' in nsswitch
1704 * and/or provoke expensive [not working] ldap/nis/...
1705 * lookups */
1706 if (!strcmp(s, "all"))
1707 return 0;
1708
1709 if ((pent = getprotobyname(s)))
1710 proto = pent->p_proto;
1711 else {
1712 unsigned int i;
1713 for (i = 0; i < ARRAY_SIZE(xtables_chain_protos); ++i) {
Pablo Neira Ayusoe55cc4a2009-05-12 09:51:26 +02001714 if (xtables_chain_protos[i].name == NULL)
1715 continue;
1716
Jan Engelhardt1de7edf2009-01-30 05:38:11 +01001717 if (strcmp(s, xtables_chain_protos[i].name) == 0) {
1718 proto = xtables_chain_protos[i].num;
1719 break;
1720 }
1721 }
1722 if (i == ARRAY_SIZE(xtables_chain_protos))
Jamal Hadi Salim8b7baeb2009-02-11 13:05:43 +01001723 xt_params->exit_err(PARAMETER_PROBLEM,
Jan Engelhardt1de7edf2009-01-30 05:38:11 +01001724 "unknown protocol `%s' specified",
1725 s);
1726 }
1727 }
1728
1729 return proto;
1730}