aboutsummaryrefslogtreecommitdiff
path: root/libxtables/xtables.c
diff options
context:
space:
mode:
Diffstat (limited to 'libxtables/xtables.c')
-rw-r--r--libxtables/xtables.c342
1 files changed, 232 insertions, 110 deletions
diff --git a/libxtables/xtables.c b/libxtables/xtables.c
index 895f6988..35fa6258 100644
--- a/libxtables/xtables.c
+++ b/libxtables/xtables.c
@@ -45,6 +45,9 @@
#include <xtables.h>
#include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */
+#ifdef __BIONIC__
+#include <linux/if_ether.h> /* ETH_ALEN */
+#endif
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <libiptc/libxtc.h>
@@ -203,8 +206,44 @@ struct xtables_match *xtables_matches;
struct xtables_target *xtables_targets;
/* Fully register a match/target which was previously partially registered. */
-static bool xtables_fully_register_pending_match(struct xtables_match *me);
-static bool xtables_fully_register_pending_target(struct xtables_target *me);
+static bool xtables_fully_register_pending_match(struct xtables_match *me,
+ struct xtables_match *prev);
+static bool xtables_fully_register_pending_target(struct xtables_target *me,
+ struct xtables_target *prev);
+
+#ifndef NO_SHARED_LIBS
+/* registry for loaded shared objects to close later */
+struct dlreg {
+ struct dlreg *next;
+ void *handle;
+};
+static struct dlreg *dlreg = NULL;
+
+static int dlreg_add(void *handle)
+{
+ struct dlreg *new = malloc(sizeof(*new));
+
+ if (!new)
+ return -1;
+
+ new->handle = handle;
+ new->next = dlreg;
+ dlreg = new;
+ return 0;
+}
+
+static void dlreg_free(void)
+{
+ struct dlreg *next;
+
+ while (dlreg) {
+ next = dlreg->next;
+ dlclose(dlreg->handle);
+ free(dlreg);
+ dlreg = next;
+ }
+}
+#endif
void xtables_init(void)
{
@@ -233,6 +272,13 @@ void xtables_init(void)
xtables_libdir = XTABLES_LIBDIR;
}
+void xtables_fini(void)
+{
+#ifndef NO_SHARED_LIBS
+ dlreg_free();
+#endif
+}
+
void xtables_set_nfproto(uint8_t nfproto)
{
switch (nfproto) {
@@ -567,6 +613,8 @@ static void *load_extension(const char *search_path, const char *af_prefix,
next = dir + strlen(dir);
for (prefix = all_prefixes; *prefix != NULL; ++prefix) {
+ void *handle;
+
snprintf(path, sizeof(path), "%.*s/%s%s.so",
(unsigned int)(next - dir), dir,
*prefix, name);
@@ -578,11 +626,14 @@ static void *load_extension(const char *search_path, const char *af_prefix,
strerror(errno));
return NULL;
}
- if (dlopen(path, RTLD_NOW) == NULL) {
+ handle = dlopen(path, RTLD_NOW);
+ if (handle == NULL) {
fprintf(stderr, "%s: %s\n", path, dlerror());
break;
}
+ dlreg_add(handle);
+
if (is_target)
ptr = xtables_find_target(name, XTF_DONT_LOAD);
else
@@ -616,6 +667,7 @@ struct xtables_match *
xtables_find_match(const char *name, enum xtables_tryload tryload,
struct xtables_rule_match **matches)
{
+ struct xtables_match *prev = NULL;
struct xtables_match **dptr;
struct xtables_match *ptr;
const char *icmp6 = "icmp6";
@@ -637,8 +689,12 @@ 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))
+ if (xtables_fully_register_pending_match(ptr, prev)) {
+ prev = ptr;
+ continue;
+ } else if (prev) {
continue;
+ }
*dptr = ptr;
}
dptr = &((*dptr)->next);
@@ -732,6 +788,7 @@ xtables_find_match_revision(const char *name, enum xtables_tryload tryload,
struct xtables_target *
xtables_find_target(const char *name, enum xtables_tryload tryload)
{
+ struct xtables_target *prev = NULL;
struct xtables_target **dptr;
struct xtables_target *ptr;
@@ -748,8 +805,12 @@ xtables_find_target(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_target(ptr))
+ if (xtables_fully_register_pending_target(ptr, prev)) {
+ prev = ptr;
+ continue;
+ } else if (prev) {
continue;
+ }
*dptr = ptr;
}
dptr = &((*dptr)->next);
@@ -757,6 +818,7 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
for (ptr = xtables_targets; ptr; ptr = ptr->next) {
if (extension_cmp(name, ptr->name, ptr->family)) {
+#if 0 /* Code block below causes memory leak. (Bugs 162925719 and 168688680) */
struct xtables_target *clone;
/* First target of this type: */
@@ -772,6 +834,7 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
clone->next = clone;
ptr = clone;
+#endif
break;
}
}
@@ -856,7 +919,8 @@ int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
xtables_load_ko(xtables_modprobe_program, true);
- strcpy(rev.name, name);
+ strncpy(rev.name, name, XT_EXTENSION_MAXNAMELEN - 1);
+ rev.name[XT_EXTENSION_MAXNAMELEN - 1] = '\0';
rev.revision = revision;
max_rev = getsockopt(sockfd, afinfo->ipproto, opt, &rev, &s);
@@ -901,8 +965,14 @@ static void xtables_check_options(const char *name, const struct option *opt)
}
}
+static int xtables_match_prefer(const struct xtables_match *a,
+ const struct xtables_match *b);
+
void xtables_register_match(struct xtables_match *me)
{
+ struct xtables_match **pos;
+ bool seen_myself = false;
+
if (me->next) {
fprintf(stderr, "%s: match \"%s\" already registered\n",
xt_params->program_name, me->name);
@@ -954,10 +1024,34 @@ void xtables_register_match(struct xtables_match *me)
if (me->extra_opts != NULL)
xtables_check_options(me->name, me->extra_opts);
-
- /* place on linked list of matches pending full registration */
- me->next = xtables_pending_matches;
- xtables_pending_matches = me;
+ /* order into linked list of matches pending full registration */
+ for (pos = &xtables_pending_matches; *pos; pos = &(*pos)->next) {
+ /* group by name and family */
+ if (strcmp(me->name, (*pos)->name) ||
+ me->family != (*pos)->family) {
+ if (seen_myself)
+ break; /* end of own group, append to it */
+ continue;
+ }
+ /* found own group */
+ seen_myself = true;
+ if (xtables_match_prefer(me, *pos) >= 0)
+ break; /* put preferred items first in group */
+ }
+ /* if own group was not found, prepend item */
+ if (!*pos && !seen_myself)
+ pos = &xtables_pending_matches;
+
+ me->next = *pos;
+ *pos = me;
+#ifdef DEBUG
+ printf("%s: inserted match %s (family %d, revision %d):\n",
+ __func__, me->name, me->family, me->revision);
+ for (pos = &xtables_pending_matches; *pos; pos = &(*pos)->next) {
+ printf("%s:\tmatch %s (family %d, revision %d)\n", __func__,
+ (*pos)->name, (*pos)->family, (*pos)->revision);
+ }
+#endif
}
/**
@@ -1021,64 +1115,27 @@ static int xtables_target_prefer(const struct xtables_target *a,
b->revision, b->family);
}
-static bool xtables_fully_register_pending_match(struct xtables_match *me)
+static bool xtables_fully_register_pending_match(struct xtables_match *me,
+ struct xtables_match *prev)
{
- struct xtables_match **i, *old, *pos = NULL;
+ struct xtables_match **i;
const char *rn;
- int compare;
/* See if new match can be used. */
rn = (me->real_name != NULL) ? me->real_name : me->name;
if (!compatible_match_revision(rn, me->revision))
return false;
- old = xtables_find_match(me->name, XTF_DURING_LOAD, NULL);
- while (old) {
- compare = xtables_match_prefer(old, me);
- if (compare == 0) {
- fprintf(stderr,
- "%s: match `%s' already registered.\n",
- xt_params->program_name, me->name);
- exit(1);
- }
-
- /* Now we have two (or more) options, check compatibility. */
- rn = (old->real_name != NULL) ? old->real_name : old->name;
- if (compare > 0) {
- /* Kernel tells old isn't compatible anymore??? */
- if (!compatible_match_revision(rn, old->revision)) {
- /* Delete old one. */
- for (i = &xtables_matches; *i != old;)
- i = &(*i)->next;
- *i = old->next;
- }
- pos = old;
- old = old->next;
- if (!old)
- break;
- if (!extension_cmp(me->name, old->name, old->family))
- break;
- continue;
- }
-
- /* Found right old */
- pos = old;
- break;
- }
-
- if (!pos) {
+ if (!prev) {
/* Append to list. */
for (i = &xtables_matches; *i; i = &(*i)->next);
- } else if (compare < 0) {
- /* Prepend it */
- for (i = &xtables_matches; *i != pos; i = &(*i)->next);
- } else if (compare > 0) {
+ } else {
/* Append it */
- i = &pos->next;
- pos = pos->next;
+ i = &prev->next;
+ prev = prev->next;
}
- me->next = pos;
+ me->next = prev;
*i = me;
me->m = NULL;
@@ -1089,13 +1146,17 @@ static bool xtables_fully_register_pending_match(struct xtables_match *me)
void xtables_register_matches(struct xtables_match *match, unsigned int n)
{
- do {
- xtables_register_match(&match[--n]);
- } while (n > 0);
+ int i;
+
+ for (i = 0; i < n; i++)
+ xtables_register_match(&match[i]);
}
void xtables_register_target(struct xtables_target *me)
{
+ struct xtables_target **pos;
+ bool seen_myself = false;
+
if (me->next) {
fprintf(stderr, "%s: target \"%s\" already registered\n",
xt_params->program_name, me->name);
@@ -1151,16 +1212,40 @@ void xtables_register_target(struct xtables_target *me)
if (me->family != afinfo->family && me->family != AF_UNSPEC)
return;
- /* place on linked list of targets pending full registration */
- me->next = xtables_pending_targets;
- xtables_pending_targets = me;
+ /* order into linked list of targets pending full registration */
+ for (pos = &xtables_pending_targets; *pos; pos = &(*pos)->next) {
+ /* group by name */
+ if (!extension_cmp(me->name, (*pos)->name, (*pos)->family)) {
+ if (seen_myself)
+ break; /* end of own group, append to it */
+ continue;
+ }
+ /* found own group */
+ seen_myself = true;
+ if (xtables_target_prefer(me, *pos) >= 0)
+ break; /* put preferred items first in group */
+ }
+ /* if own group was not found, prepend item */
+ if (!*pos && !seen_myself)
+ pos = &xtables_pending_targets;
+
+ me->next = *pos;
+ *pos = me;
+#ifdef DEBUG
+ printf("%s: inserted target %s (family %d, revision %d):\n",
+ __func__, me->name, me->family, me->revision);
+ for (pos = &xtables_pending_targets; *pos; pos = &(*pos)->next) {
+ printf("%s:\ttarget %s (family %d, revision %d)\n", __func__,
+ (*pos)->name, (*pos)->family, (*pos)->revision);
+ }
+#endif
}
-static bool xtables_fully_register_pending_target(struct xtables_target *me)
+static bool xtables_fully_register_pending_target(struct xtables_target *me,
+ struct xtables_target *prev)
{
- struct xtables_target **i, *old, *pos = NULL;
+ struct xtables_target **i;
const char *rn;
- int compare;
if (strcmp(me->name, "standard") != 0) {
/* See if new target can be used. */
@@ -1169,54 +1254,17 @@ static bool xtables_fully_register_pending_target(struct xtables_target *me)
return false;
}
- old = xtables_find_target(me->name, XTF_DURING_LOAD);
- while (old) {
- compare = xtables_target_prefer(old, me);
- if (compare == 0) {
- fprintf(stderr,
- "%s: target `%s' already registered.\n",
- xt_params->program_name, me->name);
- exit(1);
- }
-
- /* Now we have two (or more) options, check compatibility. */
- rn = (old->real_name != NULL) ? old->real_name : old->name;
- if (compare > 0) {
- /* Kernel tells old isn't compatible anymore??? */
- if (!compatible_target_revision(rn, old->revision)) {
- /* Delete old one. */
- for (i = &xtables_targets; *i != old;)
- i = &(*i)->next;
- *i = old->next;
- }
- pos = old;
- old = old->next;
- if (!old)
- break;
- if (!extension_cmp(me->name, old->name, old->family))
- break;
- continue;
- }
-
- /* Found right old */
- pos = old;
- break;
- }
-
- if (!pos) {
+ if (!prev) {
/* Prepend to list. */
i = &xtables_targets;
- pos = xtables_targets;
- } else if (compare < 0) {
- /* Prepend it */
- for (i = &xtables_targets; *i != pos; i = &(*i)->next);
- } else if (compare > 0) {
+ prev = xtables_targets;
+ } else {
/* Append it */
- i = &pos->next;
- pos = pos->next;
+ i = &prev->next;
+ prev = prev->next;
}
- me->next = pos;
+ me->next = prev;
*i = me;
me->t = NULL;
@@ -1227,9 +1275,10 @@ static bool xtables_fully_register_pending_target(struct xtables_target *me)
void xtables_register_targets(struct xtables_target *target, unsigned int n)
{
- do {
- xtables_register_target(&target[--n]);
- } while (n > 0);
+ int i;
+
+ for (i = 0; i < n; i++)
+ xtables_register_target(&target[i]);
}
/* receives a list of xtables_rule_match, release them */
@@ -2093,6 +2142,79 @@ 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};
+static const unsigned char msk_type_multicast[ETH_ALEN] = {1};
+#define ALL_ONE_MAC {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+static const unsigned char mac_type_broadcast[ETH_ALEN] = ALL_ONE_MAC;
+static const unsigned char msk_type_broadcast[ETH_ALEN] = ALL_ONE_MAC;
+static const unsigned char mac_type_bridge_group[ETH_ALEN] = {0x01, 0x80, 0xc2};
+static const unsigned char msk_type_bridge_group[ETH_ALEN] = ALL_ONE_MAC;
+#undef ALL_ONE_MAC
+
+int xtables_parse_mac_and_mask(const char *from, void *to, void *mask)
+{
+ char *p;
+ int i;
+ struct ether_addr *addr = NULL;
+
+ if (strcasecmp(from, "Unicast") == 0) {
+ memcpy(to, mac_type_unicast, ETH_ALEN);
+ memcpy(mask, msk_type_unicast, ETH_ALEN);
+ return 0;
+ }
+ if (strcasecmp(from, "Multicast") == 0) {
+ memcpy(to, mac_type_multicast, ETH_ALEN);
+ memcpy(mask, msk_type_multicast, ETH_ALEN);
+ return 0;
+ }
+ if (strcasecmp(from, "Broadcast") == 0) {
+ memcpy(to, mac_type_broadcast, ETH_ALEN);
+ memcpy(mask, msk_type_broadcast, ETH_ALEN);
+ return 0;
+ }
+ if (strcasecmp(from, "BGA") == 0) {
+ memcpy(to, mac_type_bridge_group, ETH_ALEN);
+ memcpy(mask, msk_type_bridge_group, ETH_ALEN);
+ return 0;
+ }
+ if ( (p = strrchr(from, '/')) != NULL) {
+ *p = '\0';
+ if (!(addr = ether_aton(p + 1)))
+ return -1;
+ memcpy(mask, addr, ETH_ALEN);
+ } else
+ memset(mask, 0xff, ETH_ALEN);
+ if (!(addr = ether_aton(from)))
+ return -1;
+ memcpy(to, addr, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++)
+ ((char *)to)[i] &= ((char *)mask)[i];
+ return 0;
+}
+
+int xtables_print_well_known_mac_and_mask(const void *mac, const void *mask)
+{
+ if (!memcmp(mac, mac_type_unicast, ETH_ALEN) &&
+ !memcmp(mask, msk_type_unicast, ETH_ALEN))
+ printf("Unicast");
+ else if (!memcmp(mac, mac_type_multicast, ETH_ALEN) &&
+ !memcmp(mask, msk_type_multicast, ETH_ALEN))
+ printf("Multicast");
+ else if (!memcmp(mac, mac_type_broadcast, ETH_ALEN) &&
+ !memcmp(mask, msk_type_broadcast, ETH_ALEN))
+ printf("Broadcast");
+ else if (!memcmp(mac, mac_type_bridge_group, ETH_ALEN) &&
+ !memcmp(mask, msk_type_bridge_group, ETH_ALEN))
+ printf("BGA");
+ else
+ return -1;
+ return 0;
+}
+
void xtables_print_mac(const unsigned char *macaddress)
{
unsigned int i;