summaryrefslogtreecommitdiffstats
path: root/lib/session.c
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@netfilter.org>2019-10-31 14:18:48 +0100
committerJozsef Kadlecsik <kadlec@netfilter.org>2019-10-31 14:18:48 +0100
commit0b08f9f17ae95a6f8eded6543922b99e7f29cbf7 (patch)
tree414f4f1a401f8a2584588b73ffa82c62c67b0c85 /lib/session.c
parent128344c2c2f0f38e381e608fedde60e9eaeed876 (diff)
Sort naturally instead of textual sort (bugzilla #1369)
Sort 95.0.0.0 before 107.0.0.0 instead of the textual sorting. Also, in the case of subnets, sort reversed, ie. most specific first.
Diffstat (limited to 'lib/session.c')
-rw-r--r--lib/session.c80
1 files changed, 78 insertions, 2 deletions
diff --git a/lib/session.c b/lib/session.c
index c28c2d8..9e3eae3 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -64,6 +64,7 @@ struct ipset_session {
ipset_print_outfn print_outfn; /* Output function to file */
void *p; /* Private data for print_outfn */
bool sort; /* Print sorted hash:* types */
+ size_t save_elem_prefix; /* "add setname " */
/* Session IO */
bool normal_io, full_io; /* Default/normal/full IO */
FILE *istream, *ostream; /* Session input/output stream */
@@ -979,6 +980,7 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
return MNL_CB_ERROR;
family = ipset_data_family(data);
+ session->save_elem_prefix = strlen(ipset_data_setname(data)) + 5;
switch (session->mode) {
case IPSET_LIST_SAVE:
safe_snprintf(session, "create %s %s",
@@ -1084,15 +1086,89 @@ list_create(struct ipset_session *session, struct nlattr *nla[])
return MNL_CB_OK;
}
+/* "<member><elem>" */
+#define XML_ELEM_PREFIX_LEN 14
+
+/* Core should handle sorting more directly */
static int
bystrcmp(void *priv, struct list_head *a, struct list_head *b)
{
struct ipset_session *session = priv;
struct ipset_sorted *x = list_entry(a, struct ipset_sorted, list);
struct ipset_sorted *y = list_entry(b, struct ipset_sorted, list);
+ char *sep1, *x1 = session->outbuf + x->offset;
+ char *sep2, *x2 = session->outbuf + y->offset;
+ long int n1, n2;
+ int neg = 1;
+
+ if (session->envopts & IPSET_ENV_RESOLVE)
+ return strcmp(x1, x2);
+
+ /* Skip prefix */
+ switch (session->mode) {
+ case IPSET_LIST_SAVE:
+ /* "add setname " */
+ x1 += session->save_elem_prefix;
+ x2 += session->save_elem_prefix;
+ break;
+ case IPSET_LIST_XML:
+ /* "<member><elem>" */
+ x1 += XML_ELEM_PREFIX_LEN;
+ x2 += XML_ELEM_PREFIX_LEN;
+ break;
+ default:
+ break;
+ }
- return strcmp(session->outbuf + x->offset,
- session->outbuf + y->offset);
+ while (*x1 != '\0' && *x2 != '\0') {
+ n1 = strtol(x1, &sep1, 16);
+ n2 = strtol(x2, &sep2, 16);
+ if (x1 == sep1 || x2 == sep2) {
+ /* No leading numbers: proto:port, iface, setname */
+ n1 = strcspn(x1, ":,");
+ n2 = strcspn(x2, ":,");
+ if (n1 != 0 || n2 != 0) {
+ if (n1 == n2) {
+ n2 = strncmp(x1, x2, n1);
+ if (n2 == 0) {
+ x1 += n1 + 1;
+ x2 += n1 + 1;
+ neg = 1;
+ continue;
+ }
+ return n2;
+ }
+ return n1 < n2 ? -1 : 1;
+ }
+ /* Setname or iface */
+ return strcmp(x1, x2);
+ }
+ /* Leading numbers found */
+ if (n1 != n2)
+ return neg * (n1 - n2);
+ /* Check subnet separator */
+ if (*sep1 == '/' || *sep2 == '/') {
+ if (*sep1 == *sep2)
+ neg = *sep1 == '/' ? -1 : 1;
+ else if (*sep1 == '/')
+ return 1;
+ else
+ return -1;
+ }
+ x1 = ++sep1;
+ x2 = ++sep2;
+ /* Handle IPv6 '::' case */
+ if (*x1 == ':' || *x2 == ':') {
+ if (*x1 == *x2) {
+ x1++;
+ x2++;
+ } else if (*x1 == ':')
+ return -1;
+ else
+ return 1;
+ }
+ }
+ return *x1 != '\0' ? 1 : (*x2 != '\0' ? -1 : 0);
}
static int