summaryrefslogtreecommitdiffstats
path: root/libxtables/xtables.c
diff options
context:
space:
mode:
Diffstat (limited to 'libxtables/xtables.c')
-rw-r--r--libxtables/xtables.c273
1 files changed, 214 insertions, 59 deletions
diff --git a/libxtables/xtables.c b/libxtables/xtables.c
index d670175d..7b370d48 100644
--- a/libxtables/xtables.c
+++ b/libxtables/xtables.c
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <netinet/ether.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/statfs.h>
@@ -45,10 +46,10 @@
#include <xtables.h>
#include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */
-#include <linux/if_ether.h> /* ETH_ALEN */
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <libiptc/libxtc.h>
+#include <libiptc/linux_list.h>
#ifndef NO_SHARED_LIBS
#include <dlfcn.h>
@@ -63,7 +64,6 @@
#endif
#include <getopt.h>
#include "iptables/internal.h"
-#include "xshared.h"
#define NPROTO 255
@@ -71,6 +71,10 @@
#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
#endif
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+
/* we need this for ip6?tables-restore. ip6?tables-restore.c sets line to the
* current line of the input file, in order to give a more precise error
* message. ip6?tables itself doesn't need this, so it is initialized to the
@@ -90,15 +94,25 @@ void basic_exit_err(enum xtables_exittype status, const char *msg, ...)
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, "\n");
+ if (status == PARAMETER_PROBLEM) {
+ if (line != -1)
+ fprintf(stderr, "Error occurred at line: %d\n", line);
+ fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+ xt_params->program_name, xt_params->program_name);
+ } else if (status == VERSION_PROBLEM) {
+ fprintf(stderr,
+ "Perhaps %s or your kernel needs to be upgraded.\n",
+ xt_params->program_name);
+ }
+ /* On error paths, make sure that we don't leak memory */
+ xtables_free_opts(1);
exit(status);
}
void xtables_free_opts(int unused)
{
- if (xt_params->opts != xt_params->orig_opts) {
- free(xt_params->opts);
- xt_params->opts = NULL;
- }
+ free(xt_params->opts);
+ xt_params->opts = NULL;
}
struct option *xtables_merge_options(struct option *orig_opts,
@@ -243,6 +257,77 @@ static void dlreg_free(void)
}
#endif
+struct notarget {
+ struct hlist_node node;
+ char name[];
+};
+
+#define NOTARGET_HSIZE 512
+static struct hlist_head notargets[NOTARGET_HSIZE];
+
+static void notargets_hlist_init(void)
+{
+ int i;
+
+ for (i = 0; i < NOTARGET_HSIZE; i++)
+ INIT_HLIST_HEAD(&notargets[i]);
+}
+
+static void notargets_hlist_free(void)
+{
+ struct hlist_node *pos, *n;
+ struct notarget *cur;
+ int i;
+
+ for (i = 0; i < NOTARGET_HSIZE; i++) {
+ hlist_for_each_entry_safe(cur, pos, n, &notargets[i], node) {
+ hlist_del(&cur->node);
+ free(cur);
+ }
+ }
+}
+
+static uint32_t djb_hash(const char *key)
+{
+ uint32_t i, hash = 5381;
+
+ for (i = 0; i < strlen(key); i++)
+ hash = ((hash << 5) + hash) + key[i];
+
+ return hash;
+}
+
+static struct notarget *notargets_hlist_lookup(const char *name)
+{
+ uint32_t key = djb_hash(name) % NOTARGET_HSIZE;
+ struct hlist_node *node;
+ struct notarget *cur;
+
+ hlist_for_each_entry(cur, node, &notargets[key], node) {
+ if (!strcmp(name, cur->name))
+ return cur;
+ }
+ return NULL;
+}
+
+static void notargets_hlist_insert(const char *name)
+{
+ struct notarget *cur;
+
+ if (!name)
+ return;
+
+ cur = xtables_malloc(sizeof(*cur) + strlen(name) + 1);
+ strcpy(cur->name, name);
+ hlist_add_head(&cur->node, &notargets[djb_hash(name) % NOTARGET_HSIZE]);
+}
+
+void xtables_announce_chain(const char *name)
+{
+ if (!notargets_hlist_lookup(name))
+ notargets_hlist_insert(name);
+}
+
void xtables_init(void)
{
/* xtables cannot be used with setuid in a safe way. */
@@ -272,6 +357,8 @@ void xtables_init(void)
return;
}
xtables_libdir = XTABLES_LIBDIR;
+
+ notargets_hlist_init();
}
void xtables_fini(void)
@@ -279,6 +366,7 @@ void xtables_fini(void)
#ifndef NO_SHARED_LIBS
dlreg_free();
#endif
+ notargets_hlist_free();
}
void xtables_set_nfproto(uint8_t nfproto)
@@ -391,14 +479,9 @@ static char *get_modprobe(void)
char *ret;
int count;
- procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+ procfile = open(PROC_SYS_MODPROBE, O_RDONLY | O_CLOEXEC);
if (procfile < 0)
return NULL;
- if (fcntl(procfile, F_SETFD, FD_CLOEXEC) == -1) {
- fprintf(stderr, "Could not set close on exec: %s\n",
- strerror(errno));
- exit(1);
- }
ret = malloc(PATH_MAX);
if (ret) {
@@ -495,23 +578,23 @@ int xtables_load_ko(const char *modprobe, bool quiet)
}
/**
- * xtables_strtou{i,l} - string to number conversion
+ * xtables_strtoul_base - string to number conversion
* @s: input string
* @end: like strtoul's "end" pointer
* @value: pointer for result
* @min: minimum accepted value
* @max: maximum accepted value
+ * @base: assumed base of value
*
* If @end is NULL, we assume the caller wants a "strict strtoul", and hence
* "15a" is rejected.
* In either case, the value obtained is compared for min-max compliance.
- * Base is always 0, i.e. autodetect depending on @s.
*
* Returns true/false whether number was accepted. On failure, *value has
* undefined contents.
*/
-bool xtables_strtoul(const char *s, char **end, uintmax_t *value,
- uintmax_t min, uintmax_t max)
+bool xtables_strtoul_base(const char *s, char **end, uintmax_t *value,
+ uintmax_t min, uintmax_t max, unsigned int base)
{
uintmax_t v;
const char *p;
@@ -523,7 +606,7 @@ bool xtables_strtoul(const char *s, char **end, uintmax_t *value,
;
if (*p == '-')
return false;
- v = strtoumax(s, &my_end, 0);
+ v = strtoumax(s, &my_end, base);
if (my_end == s)
return false;
if (end != NULL)
@@ -540,6 +623,12 @@ bool xtables_strtoul(const char *s, char **end, uintmax_t *value,
return false;
}
+bool xtables_strtoul(const char *s, char **end, uintmax_t *value,
+ uintmax_t min, uintmax_t max)
+{
+ return xtables_strtoul_base(s, end, value, min, max, 0);
+}
+
bool xtables_strtoui(const char *s, char **end, unsigned int *value,
unsigned int min, unsigned int max)
{
@@ -685,6 +774,8 @@ xtables_find_match(const char *name, enum xtables_tryload tryload,
struct xtables_match **dptr;
struct xtables_match *ptr;
const char *icmp6 = "icmp6";
+ bool found = false;
+ bool seen = false;
if (strlen(name) >= XT_EXTENSION_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
@@ -703,7 +794,10 @@ xtables_find_match(const char *name, enum xtables_tryload tryload,
if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) {
ptr = *dptr;
*dptr = (*dptr)->next;
- if (xtables_fully_register_pending_match(ptr, prev)) {
+ seen = true;
+ if (!found &&
+ xtables_fully_register_pending_match(ptr, prev)) {
+ found = true;
prev = ptr;
continue;
} else if (prev) {
@@ -714,6 +808,11 @@ xtables_find_match(const char *name, enum xtables_tryload tryload,
dptr = &((*dptr)->next);
}
+ if (seen && !found)
+ fprintf(stderr,
+ "Warning: Extension %s is not supported, missing kernel module?\n",
+ name);
+
for (ptr = xtables_matches; ptr; ptr = ptr->next) {
if (extension_cmp(name, ptr->name, ptr->family)) {
struct xtables_match *clone;
@@ -805,6 +904,8 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
struct xtables_target *prev = NULL;
struct xtables_target **dptr;
struct xtables_target *ptr;
+ bool found = false;
+ bool seen = false;
/* Standard target? */
if (strcmp(name, "") == 0
@@ -813,13 +914,20 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
|| strcmp(name, XTC_LABEL_QUEUE) == 0
|| strcmp(name, XTC_LABEL_RETURN) == 0)
name = "standard";
+ /* known non-target? */
+ else if (notargets_hlist_lookup(name) &&
+ tryload != XTF_LOAD_MUST_SUCCEED)
+ return NULL;
/* Trigger delayed initialization */
for (dptr = &xtables_pending_targets; *dptr; ) {
if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) {
ptr = *dptr;
*dptr = (*dptr)->next;
- if (xtables_fully_register_pending_target(ptr, prev)) {
+ seen = true;
+ if (!found &&
+ xtables_fully_register_pending_target(ptr, prev)) {
+ found = true;
prev = ptr;
continue;
} else if (prev) {
@@ -830,6 +938,11 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
dptr = &((*dptr)->next);
}
+ if (seen && !found)
+ fprintf(stderr,
+ "Warning: Extension %s is not supported, missing kernel module?\n",
+ name);
+
for (ptr = xtables_targets; ptr; ptr = ptr->next) {
if (extension_cmp(name, ptr->name, ptr->family)) {
struct xtables_target *clone;
@@ -876,6 +989,8 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
if (ptr)
ptr->used = 1;
+ else
+ notargets_hlist_insert(name);
return ptr;
}
@@ -907,7 +1022,7 @@ int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
socklen_t s = sizeof(rev);
int max_rev, sockfd;
- sockfd = socket(afinfo->family, SOCK_RAW, IPPROTO_RAW);
+ sockfd = socket(afinfo->family, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
if (sockfd < 0) {
if (errno == EPERM) {
/* revision 0 is always supported. */
@@ -923,12 +1038,6 @@ int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
exit(1);
}
- if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
- fprintf(stderr, "Could not set close on exec: %s\n",
- strerror(errno));
- exit(1);
- }
-
xtables_load_ko(xtables_modprobe_program, true);
strncpy(rev.name, name, XT_EXTENSION_MAXNAMELEN - 1);
@@ -940,7 +1049,12 @@ int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
/* Definitely don't support this? */
if (errno == ENOENT || errno == EPROTONOSUPPORT) {
close(sockfd);
- return 0;
+ /* Pretend revision 0 support for better error messaging */
+ if (revision == 0)
+ fprintf(stderr,
+ "Warning: Extension %s revision 0 not supported, missing kernel module?\n",
+ name);
+ return (revision == 0);
} else if (errno == ENOPROTOOPT) {
close(sockfd);
/* Assume only revision 0 support (old kernel) */
@@ -1304,6 +1418,10 @@ void xtables_rule_matches_free(struct xtables_rule_match **matches)
free(matchp->match->m);
matchp->match->m = NULL;
}
+ if (matchp->match->udata_size) {
+ free(matchp->match->udata);
+ matchp->match->udata = NULL;
+ }
if (matchp->match == matchp->match->next) {
free(matchp->match);
matchp->match = NULL;
@@ -1395,11 +1513,9 @@ void xtables_param_act(unsigned int status, const char *p1, ...)
const char *xtables_ipaddr_to_numeric(const struct in_addr *addrp)
{
- static char buf[20];
- const unsigned char *bytep = (const void *)&addrp->s_addr;
+ static char buf[INET_ADDRSTRLEN];
- sprintf(buf, "%u.%u.%u.%u", bytep[0], bytep[1], bytep[2], bytep[3]);
- return buf;
+ return inet_ntop(AF_INET, addrp, buf, sizeof(buf));
}
static const char *ipaddr_to_host(const struct in_addr *addr)
@@ -1459,13 +1575,14 @@ int xtables_ipmask_to_cidr(const struct in_addr *mask)
const char *xtables_ipmask_to_numeric(const struct in_addr *mask)
{
- static char buf[20];
+ static char buf[INET_ADDRSTRLEN + 1];
uint32_t cidr;
cidr = xtables_ipmask_to_cidr(mask);
if (cidr == (unsigned int)-1) {
/* mask was not a decent combination of 1's and 0's */
- sprintf(buf, "/%s", xtables_ipaddr_to_numeric(mask));
+ buf[0] = '/';
+ inet_ntop(AF_INET, mask, buf + 1, sizeof(buf) - 1);
return buf;
} else if (cidr == 32) {
/* we don't want to see "/32" */
@@ -1745,9 +1862,8 @@ void xtables_ipparse_any(const char *name, struct in_addr **addrpp,
const char *xtables_ip6addr_to_numeric(const struct in6_addr *addrp)
{
- /* 0000:0000:0000:0000:0000:0000:000.000.000.000
- * 0000:0000:0000:0000:0000:0000:0000:0000 */
- static char buf[50+1];
+ static char buf[INET6_ADDRSTRLEN];
+
return inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
}
@@ -1805,12 +1921,12 @@ int xtables_ip6mask_to_cidr(const struct in6_addr *k)
const char *xtables_ip6mask_to_numeric(const struct in6_addr *addrp)
{
- static char buf[50+2];
+ static char buf[INET6_ADDRSTRLEN + 1];
int l = xtables_ip6mask_to_cidr(addrp);
if (l == -1) {
strcpy(buf, "/");
- strcat(buf, xtables_ip6addr_to_numeric(addrp));
+ inet_ntop(AF_INET6, addrp, buf + 1, sizeof(buf) - 1);
return buf;
}
/* we don't want to see "/128" */
@@ -2078,12 +2194,15 @@ const struct xtables_pprot xtables_chain_protos[] = {
{"udp", IPPROTO_UDP},
{"udplite", IPPROTO_UDPLITE},
{"icmp", IPPROTO_ICMP},
- {"icmpv6", IPPROTO_ICMPV6},
{"ipv6-icmp", IPPROTO_ICMPV6},
+ {"icmpv6", IPPROTO_ICMPV6},
{"esp", IPPROTO_ESP},
{"ah", IPPROTO_AH},
+ {"mobility-header", IPPROTO_MH},
{"ipv6-mh", IPPROTO_MH},
{"mh", IPPROTO_MH},
+ {"dccp", IPPROTO_DCCP},
+ {"ipcomp", IPPROTO_COMP},
{"all", 0},
{NULL},
};
@@ -2097,23 +2216,15 @@ xtables_parse_protocol(const char *s)
if (xtables_strtoui(s, NULL, &proto, 0, UINT8_MAX))
return proto;
- /* first deal with the special case of 'all' to prevent
- * people from being able to redefine 'all' in nsswitch
- * and/or provoke expensive [not working] ldap/nis/...
- * lookups */
- if (strcmp(s, "all") == 0)
- return 0;
+ for (i = 0; xtables_chain_protos[i].name != NULL; ++i) {
+ if (strcmp(s, xtables_chain_protos[i].name) == 0)
+ return xtables_chain_protos[i].num;
+ }
pent = getprotobyname(s);
if (pent != NULL)
return pent->p_proto;
- for (i = 0; i < ARRAY_SIZE(xtables_chain_protos); ++i) {
- if (xtables_chain_protos[i].name == NULL)
- continue;
- if (strcmp(s, xtables_chain_protos[i].name) == 0)
- return xtables_chain_protos[i].num;
- }
xt_params->exit_err(PARAMETER_PROBLEM,
"unknown protocol \"%s\" specified", s);
return -1;
@@ -2148,8 +2259,6 @@ void xtables_print_num(uint64_t number, unsigned int format)
printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
}
-#include <netinet/ether.h>
-
static const unsigned char mac_type_unicast[ETH_ALEN] = {};
static const unsigned char msk_type_unicast[ETH_ALEN] = {1};
static const unsigned char mac_type_multicast[ETH_ALEN] = {1};
@@ -2378,16 +2487,39 @@ void xt_xlate_free(struct xt_xlate *xl)
free(xl);
}
+static bool isbrace(char c)
+{
+ switch (c) {
+ case '(':
+ case ')':
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ return true;
+ }
+ return false;
+}
+
static void __xt_xlate_add(struct xt_xlate *xl, enum xt_xlate_type type,
- const char *fmt, va_list ap)
+ bool space, const char *fmt, va_list ap)
{
struct xt_xlate_buf *buf = &xl->buf[type];
+ char tmpbuf[1024] = "";
int len;
- len = vsnprintf(buf->data + buf->off, buf->rem, fmt, ap);
- if (len < 0 || len >= buf->rem)
+ len = vsnprintf(tmpbuf, 1024, fmt, ap);
+ if (len < 0 || len >= buf->rem - 1)
xtables_error(RESOURCE_PROBLEM, "OOM");
+ if (space && buf->off &&
+ !isspace(buf->data[buf->off - 1]) &&
+ (isalnum(tmpbuf[0]) || isbrace(tmpbuf[0]))) {
+ buf->data[buf->off] = ' ';
+ buf->off++;
+ buf->rem--;
+ }
+ sprintf(buf->data + buf->off, "%s", tmpbuf);
buf->rem -= len;
buf->off += len;
}
@@ -2397,7 +2529,16 @@ void xt_xlate_rule_add(struct xt_xlate *xl, const char *fmt, ...)
va_list ap;
va_start(ap, fmt);
- __xt_xlate_add(xl, XT_XLATE_RULE, fmt, ap);
+ __xt_xlate_add(xl, XT_XLATE_RULE, true, fmt, ap);
+ va_end(ap);
+}
+
+void xt_xlate_rule_add_nospc(struct xt_xlate *xl, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ __xt_xlate_add(xl, XT_XLATE_RULE, false, fmt, ap);
va_end(ap);
}
@@ -2406,7 +2547,16 @@ void xt_xlate_set_add(struct xt_xlate *xl, const char *fmt, ...)
va_list ap;
va_start(ap, fmt);
- __xt_xlate_add(xl, XT_XLATE_SET, fmt, ap);
+ __xt_xlate_add(xl, XT_XLATE_SET, true, fmt, ap);
+ va_end(ap);
+}
+
+void xt_xlate_set_add_nospc(struct xt_xlate *xl, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ __xt_xlate_add(xl, XT_XLATE_SET, false, fmt, ap);
va_end(ap);
}
@@ -2433,7 +2583,12 @@ uint8_t xt_xlate_get_family(struct xt_xlate *xl)
const char *xt_xlate_get(struct xt_xlate *xl)
{
- return xl->buf[XT_XLATE_RULE].data;
+ struct xt_xlate_buf *buf = &xl->buf[XT_XLATE_RULE];
+
+ while (buf->off && isspace(buf->data[buf->off - 1]))
+ buf->data[--buf->off] = '\0';
+
+ return buf->data;
}
const char *xt_xlate_set_get(struct xt_xlate *xl)