[iptables,3/4] xtables: Register all match/target revisions supported by us and kernel

Message ID 1520413843-24456-4-git-send-email-serhe.popovych@gmail.com
State Accepted
Delegated to: Pablo Neira
Headers show
Series
  • iptables: Fix [unsupported revision] for matches/targets after update
Related show

Commit Message

Serhey Popovych March 7, 2018, 9:10 a.m.
Keep the order of matches by appending them; keep order between
revisions of same match from most to least recent. All of this
keeps xtables_find_match() happy to find most recent supported
by kernel revision in the given order.

Apply the same for targets, except prepend targets; order between
revisions preserved too.

All this needed to fix nasty bug related to iptables package update
and broken print/save output.

After this change all supported revisions of match/target stored
in corresponding list with following pattern:

         xt_matches                 xt_targets
         ==========                 ==========

     m1  m2  m3     mN             tN     t1  t2  t3
  +-----+--+---+---~~~---+    +---~~~---+---+----+--+
  |43210|10|210|revisions|    |revisions|210|3210|10|
  +-----+--+---+---~~~---+    +---~~~---+---+----+--+

Where new [m]atches added to the list tail and new [t]argets added
to the list head to preserve previous behaviour. Multiple revisions
of single match/target type are grouped together and sorted in
descending order. Both this ensures xtables_find_match() and
xtables_find_target() behaviour remains the same after change: find
highest supported match/target revision given by it's name.

Signed-off-by: Serhey Popovych <serhe.popovych@gmail.com>
---
 libxtables/xtables.c |   95 +++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 71 insertions(+), 24 deletions(-)

Patch

diff --git a/libxtables/xtables.c b/libxtables/xtables.c
index 33fc158..5a115ff 100644
--- a/libxtables/xtables.c
+++ b/libxtables/xtables.c
@@ -963,7 +963,7 @@  static int xtables_target_prefer(const struct xtables_target *a,
 
 static bool xtables_fully_register_pending_match(struct xtables_match *me)
 {
-	struct xtables_match **i, *old;
+	struct xtables_match **i, *old, *pos = NULL;
 	const char *rn;
 	int compare;
 
@@ -973,7 +973,7 @@  static bool xtables_fully_register_pending_match(struct xtables_match *me)
 		return false;
 
 	old = xtables_find_match(me->name, XTF_DURING_LOAD, NULL);
-	if (old) {
+	while (old) {
 		compare = xtables_match_prefer(old, me);
 		if (compare == 0) {
 			fprintf(stderr,
@@ -984,18 +984,41 @@  static bool xtables_fully_register_pending_match(struct xtables_match *me)
 
 		/* Now we have two (or more) options, check compatibility. */
 		rn = (old->real_name != NULL) ? old->real_name : old->name;
-		if (compare > 0 &&
-		    compatible_match_revision(rn, old->revision))
-			return true;
+		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;
+		}
 
-		/* Delete old one. */
-		for (i = &xtables_matches; *i!=old; i = &(*i)->next);
-		*i = old->next;
+		/* Found right old */
+		pos = old;
+		break;
+	}
+
+	if (!pos) {
+		/* 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) {
+		/* Append it */
+		i = &pos->next;
+		pos = pos->next;
 	}
 
-	/* Append to list. */
-	for (i = &xtables_matches; *i; i = &(*i)->next);
-	me->next = NULL;
+	me->next = pos;
 	*i = me;
 
 	me->m = NULL;
@@ -1069,7 +1092,7 @@  void xtables_register_target(struct xtables_target *me)
 
 static bool xtables_fully_register_pending_target(struct xtables_target *me)
 {
-	struct xtables_target *old;
+	struct xtables_target **i, *old, *pos = NULL;
 	const char *rn;
 	int compare;
 
@@ -1081,9 +1104,7 @@  static bool xtables_fully_register_pending_target(struct xtables_target *me)
 	}
 
 	old = xtables_find_target(me->name, XTF_DURING_LOAD);
-	if (old) {
-		struct xtables_target **i;
-
+	while (old) {
 		compare = xtables_target_prefer(old, me);
 		if (compare == 0) {
 			fprintf(stderr,
@@ -1094,18 +1115,44 @@  static bool xtables_fully_register_pending_target(struct xtables_target *me)
 
 		/* Now we have two (or more) options, check compatibility. */
 		rn = (old->real_name != NULL) ? old->real_name : old->name;
-		if (compare > 0 &&
-		    compatible_target_revision(rn, old->revision))
-			return true;
+		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;
+		}
 
-		/* Delete old one. */
-		for (i = &xtables_targets; *i!=old; i = &(*i)->next);
-		*i = old->next;
+		/* Found right old */
+		pos = old;
+		break;
 	}
 
-	/* Prepend to list. */
-	me->next = xtables_targets;
-	xtables_targets = me;
+	if (!pos) {
+		/* 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) {
+		/* Append it */
+		i = &pos->next;
+		pos = pos->next;
+	}
+
+	me->next = pos;
+	*i = me;
+
 	me->t = NULL;
 	me->tflags = 0;