summaryrefslogtreecommitdiffstats
path: root/libxtables
diff options
context:
space:
mode:
Diffstat (limited to 'libxtables')
-rw-r--r--libxtables/xtables.c69
1 files changed, 53 insertions, 16 deletions
diff --git a/libxtables/xtables.c b/libxtables/xtables.c
index d8185796..7f0d3ccb 100644
--- a/libxtables/xtables.c
+++ b/libxtables/xtables.c
@@ -862,14 +862,58 @@ void xtables_register_match(struct xtables_match *me)
xtables_pending_matches = me;
}
+/**
+ * Compare two actions for their preference
+ * @a: one action
+ * @b: another
+ *
+ * Like strcmp, returns a negative number if @a is less preferred than @b,
+ * positive number if @a is more preferred than @b, or zero if equally
+ * preferred.
+ */
+static int
+xtables_mt_prefer(unsigned int a_rev, unsigned int a_fam,
+ unsigned int b_rev, unsigned int b_fam)
+{
+ /* Higher revision ranks higher. */
+ if (a_rev < b_rev)
+ return -1;
+ if (a_rev > b_rev)
+ return 1;
+
+ /* NFPROTO_<specific> ranks higher than NFPROTO_UNSPEC. */
+ if (a_fam == NFPROTO_UNSPEC && b_fam != NFPROTO_UNSPEC)
+ return -1;
+ if (a_fam != NFPROTO_UNSPEC && b_fam == NFPROTO_UNSPEC)
+ return 1;
+
+ /* Must be the same thing. */
+ return 0;
+}
+
+static int xtables_match_prefer(const struct xtables_match *a,
+ const struct xtables_match *b)
+{
+ return xtables_mt_prefer(a->revision, a->family,
+ b->revision, b->family);
+}
+
+static int xtables_target_prefer(const struct xtables_target *a,
+ const struct xtables_target *b)
+{
+ return xtables_mt_prefer(a->revision, a->family,
+ b->revision, b->family);
+}
+
static void xtables_fully_register_pending_match(struct xtables_match *me)
{
struct xtables_match **i, *old;
+ int compare;
old = xtables_find_match(me->name, XTF_DURING_LOAD, NULL);
if (old) {
- if (old->revision == me->revision &&
- old->family == me->family) {
+ compare = xtables_match_prefer(old, me);
+ if (compare == 0) {
fprintf(stderr,
"%s: match `%s' already registered.\n",
xt_params->program_name, me->name);
@@ -877,18 +921,14 @@ static void xtables_fully_register_pending_match(struct xtables_match *me)
}
/* Now we have two (or more) options, check compatibility. */
- if (compatible_match_revision(old->name, old->revision)
- && old->revision > me->revision)
+ if (compare > 0 &&
+ compatible_match_revision(old->name, old->revision))
return;
/* See if new match can be used. */
if (!compatible_match_revision(me->name, me->revision))
return;
- /* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
- if (old->revision == me->revision && me->family == AF_UNSPEC)
- return;
-
/* Delete old one. */
for (i = &xtables_matches; *i!=old; i = &(*i)->next);
*i = old->next;
@@ -962,13 +1002,14 @@ void xtables_register_target(struct xtables_target *me)
static void xtables_fully_register_pending_target(struct xtables_target *me)
{
struct xtables_target *old;
+ int compare;
old = xtables_find_target(me->name, XTF_DURING_LOAD);
if (old) {
struct xtables_target **i;
- if (old->revision == me->revision &&
- old->family == me->family) {
+ compare = xtables_target_prefer(old, me);
+ if (compare == 0) {
fprintf(stderr,
"%s: target `%s' already registered.\n",
xt_params->program_name, me->name);
@@ -976,18 +1017,14 @@ static void xtables_fully_register_pending_target(struct xtables_target *me)
}
/* Now we have two (or more) options, check compatibility. */
- if (compatible_target_revision(old->name, old->revision)
- && old->revision > me->revision)
+ if (compare > 0 &&
+ compatible_target_revision(old->name, old->revision))
return;
/* See if new target can be used. */
if (!compatible_target_revision(me->name, me->revision))
return;
- /* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
- if (old->revision == me->revision && me->family == AF_UNSPEC)
- return;
-
/* Delete old one. */
for (i = &xtables_targets; *i!=old; i = &(*i)->next);
*i = old->next;