diff --git a/SOURCES/0002-lib-split-parser-from-command-execution.patch b/SOURCES/0002-lib-split-parser-from-command-execution.patch new file mode 100644 index 0000000..fb5ae8e --- /dev/null +++ b/SOURCES/0002-lib-split-parser-from-command-execution.patch @@ -0,0 +1,84 @@ +From c371154c65f7b09aa0582b7ae2f68aaf13113ee1 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Fri, 25 Jun 2021 22:30:40 +0200 +Subject: [PATCH] lib: split parser from command execution + +ipset_parse_argv() parses, builds and send the netlink messages to the +kernel. This patch extracts the parser and wrap it around the new +ipset_parser() function. + +This patch comes is preparation for the ipset to nftables translation +infrastructure. + +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Jozsef Kadlecsik +(cherry picked from commit 4dd0a5e5755ec058b78e3bd6da39fe2bb7bbb4f3) +--- + lib/ipset.c | 44 ++++++++++++++++++++++++++++++-------------- + 1 file changed, 30 insertions(+), 14 deletions(-) + +diff --git a/lib/ipset.c b/lib/ipset.c +index 6729919657707..3077f9793f841 100644 +--- a/lib/ipset.c ++++ b/lib/ipset.c +@@ -923,20 +923,8 @@ static const char *cmd_prefix[] = { + [IPSET_TEST] = "test SETNAME", + }; + +-/* Workhorses */ +- +-/** +- * ipset_parse_argv - parse and argv array and execute the command +- * @ipset: ipset structure +- * @argc: length of the array +- * @argv: array of strings +- * +- * Parse an array of strings and execute the ipset command. +- * +- * Returns 0 on success or a negative error code. +- */ +-int +-ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[]) ++static int ++ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) + { + int ret = 0; + enum ipset_cmd cmd = IPSET_CMD_NONE; +@@ -1280,6 +1268,34 @@ ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[]) + if (argc > 1) + return ipset->custom_error(ipset, p, IPSET_PARAMETER_PROBLEM, + "Unknown argument %s", argv[1]); ++ ++ return cmd; ++} ++ ++/* Workhorses */ ++ ++/** ++ * ipset_parse_argv - parse and argv array and execute the command ++ * @ipset: ipset structure ++ * @argc: length of the array ++ * @argv: array of strings ++ * ++ * Parse an array of strings and execute the ipset command. ++ * ++ * Returns 0 on success or a negative error code. ++ */ ++int ++ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[]) ++{ ++ struct ipset_session *session = ipset->session; ++ void *p = ipset_session_printf_private(session); ++ enum ipset_cmd cmd; ++ int ret; ++ ++ cmd = ipset_parser(ipset, oargc, oargv); ++ if (cmd < 0) ++ return cmd; ++ + ret = ipset_cmd(session, cmd, ipset->restore_line); + D("ret %d", ret); + /* In the case of warning, the return code is success */ +-- +2.38.0 + diff --git a/SOURCES/0003-lib-Detach-restore-routine-from-parser.patch b/SOURCES/0003-lib-Detach-restore-routine-from-parser.patch new file mode 100644 index 0000000..0cd4cbf --- /dev/null +++ b/SOURCES/0003-lib-Detach-restore-routine-from-parser.patch @@ -0,0 +1,44 @@ +From 5f8dc543a936f7f962165977cfb8e9e108659eb5 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Fri, 25 Jun 2021 22:30:41 +0200 +Subject: [PATCH] lib: Detach restore routine from parser + +Do not call restore() from ipset_parser(). Instead, ipset_parser() +returns the IPSET_CMD_RESTORE command and the caller invokes restore(). + +This patch comes in preparation for the ipset to nftables translation +infrastructure. + +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Jozsef Kadlecsik +(cherry picked from commit ff7f000ef2dbe81444a4e204dbab9a2177c35e21) +--- + lib/ipset.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/lib/ipset.c b/lib/ipset.c +index 3077f9793f841..5232d8b76c46f 100644 +--- a/lib/ipset.c ++++ b/lib/ipset.c +@@ -1231,7 +1231,7 @@ ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) + return ipset->custom_error(ipset, + p, IPSET_PARAMETER_PROBLEM, + "Unknown argument %s", argv[1]); +- return restore(ipset); ++ return IPSET_CMD_RESTORE; + case IPSET_CMD_ADD: + case IPSET_CMD_DEL: + case IPSET_CMD_TEST: +@@ -1296,6 +1296,9 @@ ipset_parse_argv(struct ipset *ipset, int oargc, char *oargv[]) + if (cmd < 0) + return cmd; + ++ if (cmd == IPSET_CMD_RESTORE) ++ return restore(ipset); ++ + ret = ipset_cmd(session, cmd, ipset->restore_line); + D("ret %d", ret); + /* In the case of warning, the return code is success */ +-- +2.38.0 + diff --git a/SOURCES/0004-add-ipset-to-nftables-translation-infrastructure.patch b/SOURCES/0004-add-ipset-to-nftables-translation-infrastructure.patch new file mode 100644 index 0000000..140699d --- /dev/null +++ b/SOURCES/0004-add-ipset-to-nftables-translation-infrastructure.patch @@ -0,0 +1,825 @@ +From 469387c1fce52280daf0ed71a1e8cf1953551e8d Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Fri, 25 Jun 2021 22:30:42 +0200 +Subject: [PATCH] add ipset to nftables translation infrastructure + +This patch provides the ipset-translate utility which allows you to +translate your existing ipset file to nftables. + +The ipset-translate utility is actually a symlink to ipset, which checks +for 'argv[0] == ipset-translate' to exercise the translation path. + +You can translate your ipset file through: + + ipset-translate restore < sets.ipt + +This patch reuses the existing parser and API to represent the sets and +the elements. + +There is a new ipset_xlate_set dummy object that allows to store a +created set to fetch the type without interactions with the kernel. + +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Jozsef Kadlecsik +(cherry picked from commit 325af556cd3a6d1636c0cd355b494c87f58397e0) +--- + configure.ac | 1 + + include/libipset/Makefile.am | 3 +- + include/libipset/xlate.h | 6 + + lib/ipset.c | 541 ++++++++++++++++++++++++++++++++++- + src/Makefile.am | 8 +- + src/ipset-translate.8 | 91 ++++++ + src/ipset.c | 8 +- + 7 files changed, 654 insertions(+), 4 deletions(-) + create mode 100644 include/libipset/xlate.h + create mode 100644 src/ipset-translate.8 + +diff --git a/configure.ac b/configure.ac +index bd6116ca7f0a3..3ba3e17137d34 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -7,6 +7,7 @@ AC_CONFIG_HEADER([config.h]) + AM_INIT_AUTOMAKE([foreign subdir-objects tar-pax]) + m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + ++AC_PROG_LN_S + AC_ENABLE_STATIC + LT_INIT([dlopen]) + LT_CONFIG_LTDL_DIR([libltdl]) +diff --git a/include/libipset/Makefile.am b/include/libipset/Makefile.am +index c7f7b2bfce487..2c040291abc06 100644 +--- a/include/libipset/Makefile.am ++++ b/include/libipset/Makefile.am +@@ -17,6 +17,7 @@ pkginclude_HEADERS = \ + transport.h \ + types.h \ + ipset.h \ +- utils.h ++ utils.h \ ++ xlate.h + + EXTRA_DIST = debug.h icmp.h icmpv6.h +diff --git a/include/libipset/xlate.h b/include/libipset/xlate.h +new file mode 100644 +index 0000000000000..65697682f722d +--- /dev/null ++++ b/include/libipset/xlate.h +@@ -0,0 +1,6 @@ ++#ifndef LIBIPSET_XLATE_H ++#define LIBIPSET_XLATE_H ++ ++int ipset_xlate_argv(struct ipset *ipset, int argc, char *argv[]); ++ ++#endif +diff --git a/lib/ipset.c b/lib/ipset.c +index 5232d8b76c46f..73e67db88e0d1 100644 +--- a/lib/ipset.c ++++ b/lib/ipset.c +@@ -13,6 +13,7 @@ + #include /* printf */ + #include /* exit */ + #include /* str* */ ++#include /* PRIu64 */ + + #include + +@@ -28,6 +29,7 @@ + #include /* STREQ */ + #include /* prototypes */ + #include /* compiler attributes */ ++#include /* lists */ + + static char program_name[] = PACKAGE; + static char program_version[] = PACKAGE_VERSION; +@@ -50,6 +52,17 @@ struct ipset { + char *newargv[MAX_ARGS]; + int newargc; + const char *filename; /* Input/output filename */ ++ bool xlate; ++ struct list_head xlate_sets; ++}; ++ ++struct ipset_xlate_set { ++ struct list_head list; ++ char name[IPSET_MAXNAMELEN]; ++ uint8_t netmask; ++ uint8_t family; ++ bool interval; ++ const struct ipset_type *type; + }; + + /* Commands and environment options */ +@@ -923,6 +936,31 @@ static const char *cmd_prefix[] = { + [IPSET_TEST] = "test SETNAME", + }; + ++static const struct ipset_xlate_set * ++ipset_xlate_set_get(struct ipset *ipset, const char *name) ++{ ++ const struct ipset_xlate_set *set; ++ ++ list_for_each_entry(set, &ipset->xlate_sets, list) { ++ if (!strcmp(set->name, name)) ++ return set; ++ } ++ ++ return NULL; ++} ++ ++static const struct ipset_type *ipset_xlate_type_get(struct ipset *ipset, ++ const char *name) ++{ ++ const struct ipset_xlate_set *set; ++ ++ set = ipset_xlate_set_get(ipset, name); ++ if (!set) ++ return NULL; ++ ++ return set->type; ++} ++ + static int + ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) + { +@@ -1241,7 +1279,12 @@ ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) + if (ret < 0) + return ipset->standard_error(ipset, p); + +- type = ipset_type_get(session, cmd); ++ if (!ipset->xlate) { ++ type = ipset_type_get(session, cmd); ++ } else { ++ type = ipset_xlate_type_get(ipset, arg0); ++ ipset_session_data_set(session, IPSET_OPT_TYPE, type); ++ } + if (type == NULL) + return ipset->standard_error(ipset, p); + +@@ -1474,6 +1517,9 @@ ipset_init(void) + return NULL; + } + ipset_custom_printf(ipset, NULL, NULL, NULL, NULL); ++ ++ INIT_LIST_HEAD(&ipset->xlate_sets); ++ + return ipset; + } + +@@ -1488,6 +1534,8 @@ ipset_init(void) + int + ipset_fini(struct ipset *ipset) + { ++ struct ipset_xlate_set *xlate_set, *next; ++ + assert(ipset); + + if (ipset->session) +@@ -1496,6 +1544,497 @@ ipset_fini(struct ipset *ipset) + if (ipset->newargv[0]) + free(ipset->newargv[0]); + ++ list_for_each_entry_safe(xlate_set, next, &ipset->xlate_sets, list) ++ free(xlate_set); ++ + free(ipset); + return 0; + } ++ ++/* Ignore the set family, use inet. */ ++static const char *ipset_xlate_family(uint8_t family) ++{ ++ return "inet"; ++} ++ ++enum ipset_xlate_set_type { ++ IPSET_XLATE_TYPE_UNKNOWN = 0, ++ IPSET_XLATE_TYPE_HASH_MAC, ++ IPSET_XLATE_TYPE_HASH_IP, ++ IPSET_XLATE_TYPE_HASH_IP_MAC, ++ IPSET_XLATE_TYPE_HASH_NET_IFACE, ++ IPSET_XLATE_TYPE_HASH_NET_PORT, ++ IPSET_XLATE_TYPE_HASH_NET_PORT_NET, ++ IPSET_XLATE_TYPE_HASH_NET_NET, ++ IPSET_XLATE_TYPE_HASH_NET, ++ IPSET_XLATE_TYPE_HASH_IP_PORT_NET, ++ IPSET_XLATE_TYPE_HASH_IP_PORT_IP, ++ IPSET_XLATE_TYPE_HASH_IP_MARK, ++ IPSET_XLATE_TYPE_HASH_IP_PORT, ++ IPSET_XLATE_TYPE_BITMAP_PORT, ++ IPSET_XLATE_TYPE_BITMAP_IP_MAC, ++ IPSET_XLATE_TYPE_BITMAP_IP, ++}; ++ ++static enum ipset_xlate_set_type ipset_xlate_set_type(const char *typename) ++{ ++ if (!strcmp(typename, "hash:mac")) ++ return IPSET_XLATE_TYPE_HASH_MAC; ++ else if (!strcmp(typename, "hash:ip")) ++ return IPSET_XLATE_TYPE_HASH_IP; ++ else if (!strcmp(typename, "hash:ip,mac")) ++ return IPSET_XLATE_TYPE_HASH_IP_MAC; ++ else if (!strcmp(typename, "hash:net,iface")) ++ return IPSET_XLATE_TYPE_HASH_NET_IFACE; ++ else if (!strcmp(typename, "hash:net,port")) ++ return IPSET_XLATE_TYPE_HASH_NET_PORT; ++ else if (!strcmp(typename, "hash:net,port,net")) ++ return IPSET_XLATE_TYPE_HASH_NET_PORT_NET; ++ else if (!strcmp(typename, "hash:net,net")) ++ return IPSET_XLATE_TYPE_HASH_NET_NET; ++ else if (!strcmp(typename, "hash:net")) ++ return IPSET_XLATE_TYPE_HASH_NET; ++ else if (!strcmp(typename, "hash:ip,port,net")) ++ return IPSET_XLATE_TYPE_HASH_IP_PORT_NET; ++ else if (!strcmp(typename, "hash:ip,port,ip")) ++ return IPSET_XLATE_TYPE_HASH_IP_PORT_IP; ++ else if (!strcmp(typename, "hash:ip,mark")) ++ return IPSET_XLATE_TYPE_HASH_IP_MARK; ++ else if (!strcmp(typename, "hash:ip,port")) ++ return IPSET_XLATE_TYPE_HASH_IP_PORT; ++ else if (!strcmp(typename, "hash:ip")) ++ return IPSET_XLATE_TYPE_HASH_IP; ++ else if (!strcmp(typename, "bitmap:port")) ++ return IPSET_XLATE_TYPE_BITMAP_PORT; ++ else if (!strcmp(typename, "bitmap:ip,mac")) ++ return IPSET_XLATE_TYPE_BITMAP_IP_MAC; ++ else if (!strcmp(typename, "bitmap:ip")) ++ return IPSET_XLATE_TYPE_BITMAP_IP; ++ ++ return IPSET_XLATE_TYPE_UNKNOWN; ++} ++ ++#define NFT_SET_INTERVAL (1 << 0) ++ ++static const char * ++ipset_xlate_type_to_nftables(int family, enum ipset_xlate_set_type type, ++ uint32_t *flags) ++{ ++ switch (type) { ++ case IPSET_XLATE_TYPE_HASH_MAC: ++ return "ether_addr"; ++ case IPSET_XLATE_TYPE_HASH_IP: ++ if (family == AF_INET) ++ return "ipv4_addr"; ++ else if (family == AF_INET6) ++ return "ipv6_addr"; ++ break; ++ case IPSET_XLATE_TYPE_HASH_IP_MAC: ++ if (family == AF_INET) ++ return "ipv4_addr . ether_addr"; ++ else if (family == AF_INET6) ++ return "ipv6_addr . ether_addr"; ++ break; ++ case IPSET_XLATE_TYPE_HASH_NET_IFACE: ++ *flags |= NFT_SET_INTERVAL; ++ if (family == AF_INET) ++ return "ipv4_addr . ifname"; ++ else if (family == AF_INET6) ++ return "ipv6_addr . ifname"; ++ break; ++ case IPSET_XLATE_TYPE_HASH_NET_PORT: ++ *flags |= NFT_SET_INTERVAL; ++ if (family == AF_INET) ++ return "ipv4_addr . inet_proto . inet_service"; ++ else if (family == AF_INET6) ++ return "ipv6_addr . inet_proto . inet_service"; ++ break; ++ case IPSET_XLATE_TYPE_HASH_NET_PORT_NET: ++ *flags |= NFT_SET_INTERVAL; ++ if (family == AF_INET) ++ return "ipv4_addr . inet_proto . inet_service . ipv4_addr"; ++ else if (family == AF_INET6) ++ return "ipv6_addr . inet_proto . inet_service . ipv6_addr"; ++ break; ++ case IPSET_XLATE_TYPE_HASH_NET_NET: ++ *flags |= NFT_SET_INTERVAL; ++ if (family == AF_INET) ++ return "ipv4_addr . ipv4_addr"; ++ else if (family == AF_INET6) ++ return "ipv6_addr . ipv6_addr"; ++ break; ++ case IPSET_XLATE_TYPE_HASH_NET: ++ *flags |= NFT_SET_INTERVAL; ++ if (family == AF_INET) ++ return "ipv4_addr"; ++ else if (family == AF_INET6) ++ return "ipv6_addr"; ++ break; ++ case IPSET_XLATE_TYPE_HASH_IP_PORT_NET: ++ *flags |= NFT_SET_INTERVAL; ++ if (family == AF_INET) ++ return "ipv4_addr . inet_proto . inet_service . ipv4_addr"; ++ else if (family == AF_INET6) ++ return "ipv6_addr . inet_proto . inet_service . ipv6_addr"; ++ break; ++ case IPSET_XLATE_TYPE_HASH_IP_PORT_IP: ++ if (family == AF_INET) ++ return "ipv4_addr . inet_proto . inet_service . ipv4_addr"; ++ else if (family == AF_INET6) ++ return "ipv6_addr . inet_proto . inet_service . ipv6_addr"; ++ break; ++ case IPSET_XLATE_TYPE_HASH_IP_MARK: ++ if (family == AF_INET) ++ return "ipv4_addr . mark"; ++ else if (family == AF_INET6) ++ return "ipv6_addr . mark"; ++ break; ++ case IPSET_XLATE_TYPE_HASH_IP_PORT: ++ if (family == AF_INET) ++ return "ipv4_addr . inet_proto . inet_service"; ++ else if (family == AF_INET6) ++ return "ipv6_addr . inet_proto . inet_service"; ++ break; ++ case IPSET_XLATE_TYPE_BITMAP_PORT: ++ return "inet_service"; ++ case IPSET_XLATE_TYPE_BITMAP_IP_MAC: ++ if (family == AF_INET) ++ return "ipv4_addr . ether_addr"; ++ else if (family == AF_INET6) ++ return "ipv6_addr . ether_addr"; ++ break; ++ case IPSET_XLATE_TYPE_BITMAP_IP: ++ if (family == AF_INET) ++ return "ipv4_addr"; ++ else if (family == AF_INET6) ++ return "ipv6_addr"; ++ break; ++ } ++ /* This should not ever happen. */ ++ return "unknown"; ++} ++ ++static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, ++ const char *table) ++{ ++ const char *set, *typename, *nft_type; ++ const struct ipset_type *ipset_type; ++ struct ipset_xlate_set *xlate_set; ++ enum ipset_xlate_set_type type; ++ struct ipset_session *session; ++ const uint32_t *cadt_flags; ++ const uint32_t *timeout; ++ const uint32_t *maxelem; ++ struct ipset_data *data; ++ const uint8_t *netmask; ++ const char *comment; ++ uint32_t flags = 0; ++ uint8_t family; ++ char buf[64]; ++ bool concat; ++ char *term; ++ int i; ++ ++ session = ipset_session(ipset); ++ data = ipset_session_data(session); ++ ++ set = ipset_data_get(data, IPSET_SETNAME); ++ family = ipset_data_family(data); ++ ++ switch (cmd) { ++ case IPSET_CMD_CREATE: ++ /* Not supported. */ ++ if (ipset_data_test(data, IPSET_OPT_MARKMASK)) { ++ printf("# %s", ipset->cmdline); ++ break; ++ } ++ cadt_flags = ipset_data_get(data, IPSET_OPT_CADT_FLAGS); ++ ++ /* Ignore: ++ * - IPSET_FLAG_WITH_COMMENT ++ * - IPSET_FLAG_WITH_FORCEADD ++ */ ++ if (cadt_flags && ++ (*cadt_flags & (IPSET_FLAG_BEFORE | ++ IPSET_FLAG_PHYSDEV | ++ IPSET_FLAG_NOMATCH | ++ IPSET_FLAG_WITH_SKBINFO | ++ IPSET_FLAG_IFACE_WILDCARD))) { ++ printf("# %s", ipset->cmdline); ++ break; ++ } ++ ++ typename = ipset_data_get(data, IPSET_OPT_TYPENAME); ++ type = ipset_xlate_set_type(typename); ++ nft_type = ipset_xlate_type_to_nftables(family, type, &flags); ++ ++ printf("add set %s %s %s { type %s; ", ++ ipset_xlate_family(family), table, set, nft_type); ++ if (cadt_flags) { ++ if (*cadt_flags & IPSET_FLAG_WITH_COUNTERS) ++ printf("counter; "); ++ } ++ timeout = ipset_data_get(data, IPSET_OPT_TIMEOUT); ++ if (timeout) ++ printf("timeout %us; ", *timeout); ++ maxelem = ipset_data_get(data, IPSET_OPT_MAXELEM); ++ if (maxelem) ++ printf("size %u; ", *maxelem); ++ ++ netmask = ipset_data_get(data, IPSET_OPT_NETMASK); ++ if (netmask && ++ ((family == AF_INET && *netmask < 32) || ++ (family == AF_INET6 && *netmask < 128))) ++ flags |= NFT_SET_INTERVAL; ++ ++ if (flags & NFT_SET_INTERVAL) ++ printf("flags interval; "); ++ ++ /* These create-specific options are safe to be ignored: ++ * - IPSET_OPT_GC ++ * - IPSET_OPT_HASHSIZE ++ * - IPSET_OPT_PROBES ++ * - IPSET_OPT_RESIZE ++ * - IPSET_OPT_SIZE ++ * - IPSET_OPT_FORCEADD ++ * ++ * Ranges and CIDR are safe to be ignored too: ++ * - IPSET_OPT_IP_FROM ++ * - IPSET_OPT_IP_TO ++ * - IPSET_OPT_PORT_FROM ++ * - IPSET_OPT_PORT_TO ++ */ ++ ++ printf("}\n"); ++ ++ xlate_set = calloc(1, sizeof(*xlate_set)); ++ if (!xlate_set) ++ return -1; ++ ++ snprintf(xlate_set->name, sizeof(xlate_set->name), "%s", set); ++ ipset_type = ipset_types(); ++ while (ipset_type) { ++ if (!strcmp(ipset_type->name, typename)) ++ break; ++ ipset_type = ipset_type->next; ++ } ++ ++ xlate_set->family = family; ++ xlate_set->type = ipset_type; ++ if (netmask) { ++ xlate_set->netmask = *netmask; ++ xlate_set->interval = true; ++ } ++ list_add_tail(&xlate_set->list, &ipset->xlate_sets); ++ break; ++ case IPSET_CMD_DESTROY: ++ printf("del set %s %s %s\n", ++ ipset_xlate_family(family), table, set); ++ break; ++ case IPSET_CMD_FLUSH: ++ if (!set) { ++ printf("# %s", ipset->cmdline); ++ } else { ++ printf("flush set %s %s %s\n", ++ ipset_xlate_family(family), table, set); ++ } ++ break; ++ case IPSET_CMD_RENAME: ++ printf("# %s", ipset->cmdline); ++ return -1; ++ case IPSET_CMD_SWAP: ++ printf("# %s", ipset->cmdline); ++ return -1; ++ case IPSET_CMD_LIST: ++ if (!set) { ++ printf("list sets %s\n", ++ ipset_xlate_family(family), table); ++ } else { ++ printf("list set %s %s %s\n", ++ ipset_xlate_family(family), table, set); ++ } ++ break; ++ case IPSET_CMD_SAVE: ++ printf("# %s", ipset->cmdline); ++ return -1; ++ case IPSET_CMD_ADD: ++ case IPSET_CMD_DEL: ++ case IPSET_CMD_TEST: ++ /* Not supported. */ ++ if (ipset_data_test(data, IPSET_OPT_NOMATCH) || ++ ipset_data_test(data, IPSET_OPT_SKBINFO) || ++ ipset_data_test(data, IPSET_OPT_SKBMARK) || ++ ipset_data_test(data, IPSET_OPT_SKBPRIO) || ++ ipset_data_test(data, IPSET_OPT_SKBQUEUE) || ++ ipset_data_test(data, IPSET_OPT_IFACE_WILDCARD)) { ++ printf("# %s", ipset->cmdline); ++ break; ++ } ++ printf("%s element %s %s %s { ", ++ cmd == IPSET_CMD_ADD ? "add" : ++ cmd == IPSET_CMD_DEL ? "delete" : "get", ++ ipset_xlate_family(family), table, set); ++ ++ typename = ipset_data_get(data, IPSET_OPT_TYPENAME); ++ type = ipset_xlate_set_type(typename); ++ ++ xlate_set = (struct ipset_xlate_set *) ++ ipset_xlate_set_get(ipset, set); ++ if (xlate_set && xlate_set->interval) ++ netmask = &xlate_set->netmask; ++ else ++ netmask = NULL; ++ ++ concat = false; ++ if (ipset_data_test(data, IPSET_OPT_IP)) { ++ ipset_print_data(buf, sizeof(buf), data, IPSET_OPT_IP, 0); ++ printf("%s", buf); ++ if (netmask) ++ printf("/%u ", *netmask); ++ else ++ printf(" "); ++ ++ concat = true; ++ } ++ if (ipset_data_test(data, IPSET_OPT_MARK)) { ++ ipset_print_mark(buf, sizeof(buf), data, IPSET_OPT_MARK, 0); ++ printf("%s%s ", concat ? ". " : "", buf); ++ } ++ if (ipset_data_test(data, IPSET_OPT_IFACE)) { ++ ipset_print_data(buf, sizeof(buf), data, IPSET_OPT_IFACE, 0); ++ printf("%s%s ", concat ? ". " : "", buf); ++ } ++ if (ipset_data_test(data, IPSET_OPT_ETHER)) { ++ ipset_print_ether(buf, sizeof(buf), data, IPSET_OPT_ETHER, 0); ++ for (i = 0; i < strlen(buf); i++) ++ buf[i] = tolower(buf[i]); ++ ++ printf("%s%s ", concat ? ". " : "", buf); ++ concat = true; ++ } ++ if (ipset_data_test(data, IPSET_OPT_PORT)) { ++ ipset_print_proto_port(buf, sizeof(buf), data, IPSET_OPT_PORT, 0); ++ term = strchr(buf, ':'); ++ if (term) { ++ *term = '\0'; ++ printf("%s%s ", concat ? ". " : "", buf); ++ } ++ ipset_print_data(buf, sizeof(buf), data, IPSET_OPT_PORT, 0); ++ printf("%s%s ", concat ? ". " : "", buf); ++ } ++ if (ipset_data_test(data, IPSET_OPT_IP2)) { ++ ipset_print_ip(buf, sizeof(buf), data, IPSET_OPT_IP2, 0); ++ printf("%s%s", concat ? ". " : "", buf); ++ if (netmask) ++ printf("/%u ", *netmask); ++ else ++ printf(" "); ++ } ++ if (ipset_data_test(data, IPSET_OPT_PACKETS) && ++ ipset_data_test(data, IPSET_OPT_BYTES)) { ++ const uint64_t *pkts, *bytes; ++ ++ pkts = ipset_data_get(data, IPSET_OPT_PACKETS); ++ bytes = ipset_data_get(data, IPSET_OPT_BYTES); ++ ++ printf("counter packets %" PRIu64 " bytes %" PRIu64 " ", ++ *pkts, *bytes); ++ } ++ timeout = ipset_data_get(data, IPSET_OPT_TIMEOUT); ++ if (timeout) ++ printf("timeout %us ", *timeout); ++ ++ comment = ipset_data_get(data, IPSET_OPT_ADT_COMMENT); ++ if (comment) ++ printf("comment \"%s\" ", comment); ++ ++ printf("}\n"); ++ break; ++ case IPSET_CMD_GET_BYNAME: ++ printf("# %s", ipset->cmdline); ++ return -1; ++ case IPSET_CMD_GET_BYINDEX: ++ printf("# %s", ipset->cmdline); ++ return -1; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int ipset_xlate_restore(struct ipset *ipset) ++{ ++ struct ipset_session *session = ipset_session(ipset); ++ struct ipset_data *data = ipset_session_data(session); ++ void *p = ipset_session_printf_private(session); ++ const char *filename; ++ enum ipset_cmd cmd; ++ FILE *f = stdin; ++ int ret = 0; ++ char *c; ++ ++ if (ipset->filename) { ++ f = fopen(ipset->filename, "r"); ++ if (!f) { ++ fprintf(stderr, "cannot open file `%s'\n", filename); ++ return -1; ++ } ++ } ++ ++ /* TODO: Allow to specify the table name other than 'global'. */ ++ printf("add table inet global\n"); ++ ++ while (fgets(ipset->cmdline, sizeof(ipset->cmdline), f)) { ++ ipset->restore_line++; ++ c = ipset->cmdline; ++ while (isspace(c[0])) ++ c++; ++ if (c[0] == '\0' || c[0] == '#') ++ continue; ++ else if (STREQ(c, "COMMIT\n") || STREQ(c, "COMMIT\r\n")) ++ continue; ++ ++ ret = build_argv(ipset, c); ++ if (ret < 0) ++ return ret; ++ ++ cmd = ipset_parser(ipset, ipset->newargc, ipset->newargv); ++ if (cmd < 0) ++ ipset->standard_error(ipset, p); ++ ++ /* TODO: Allow to specify the table name other than 'global'. */ ++ ret = ipset_xlate(ipset, cmd, "global"); ++ if (ret < 0) ++ break; ++ ++ ipset_data_reset(data); ++ } ++ ++ if (filename) ++ fclose(f); ++ ++ return ret; ++} ++ ++int ipset_xlate_argv(struct ipset *ipset, int argc, char *argv[]) ++{ ++ enum ipset_cmd cmd; ++ int ret; ++ ++ ipset->xlate = true; ++ ++ cmd = ipset_parser(ipset, argc, argv); ++ if (cmd < 0) ++ return cmd; ++ ++ if (cmd == IPSET_CMD_RESTORE) { ++ ret = ipset_xlate_restore(ipset); ++ } else { ++ fprintf(stderr, "This command is not supported, " ++ "use `ipset-translate restore < file'\n"); ++ ret = -1; ++ } ++ ++ return ret; ++} +diff --git a/src/Makefile.am b/src/Makefile.am +index 438fcec0f1f10..95dea07701391 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -12,10 +12,16 @@ AM_LDFLAGS = -static + endif + endif + +-dist_man_MANS = ipset.8 ++dist_man_MANS = ipset.8 ipset-translate.8 + + sparse-check: $(ipset_SOURCES:.c=.d) + + %.d: %.c + $(IPSET_AM_V_CHECK)\ + $(SPARSE) -I.. $(SPARSE_FLAGS) $(AM_CFLAGS) $(AM_CPPFLAGS) $< || : ++ ++install-exec-hook: ++ ${LN_S} -f "${sbindir}/ipset" "${DESTDIR}${sbindir}/ipset-translate"; ++ ++uninstall-hook: ++ rm -f ${DESTDIR}${sbindir}/ipset-translate +diff --git a/src/ipset-translate.8 b/src/ipset-translate.8 +new file mode 100644 +index 0000000000000..bb4e737e14806 +--- /dev/null ++++ b/src/ipset-translate.8 +@@ -0,0 +1,91 @@ ++.\" ++.\" (C) Copyright 2021, Pablo Neira Ayuso ++.\" ++.\" %%%LICENSE_START(GPLv2+_DOC_FULL) ++.\" This is free documentation; you can redistribute it and/or ++.\" modify it under the terms of the GNU General Public License as ++.\" published by the Free Software Foundation; either version 2 of ++.\" the License, or (at your option) any later version. ++.\" ++.\" The GNU General Public License's references to "object code" ++.\" and "executables" are to be interpreted as the output of any ++.\" document formatting or typesetting system, including ++.\" intermediate and printed output. ++.\" ++.\" This manual is distributed in the hope that it will be useful, ++.\" but WITHOUT ANY WARRANTY; without even the implied warranty of ++.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++.\" GNU General Public License for more details. ++.\" ++.\" You should have received a copy of the GNU General Public ++.\" License along with this manual; if not, see ++.\" . ++.\" %%%LICENSE_END ++.\" ++.TH IPSET-TRANSLATE 8 "May 31, 2021" ++ ++.SH NAME ++ipset-translate \(em translation tool to migrate from ipset to nftables ++.SH DESCRIPTION ++This tool allows system administrators to translate a given IP sets file ++to \fBnftables(8)\fP. ++ ++The only available command is: ++ ++.IP \[bu] 2 ++ipset-translate restores < file.ipt ++ ++.SH USAGE ++The \fBipset-translate\fP tool reads an IP sets file in the syntax produced by ++\fBipset(8)\fP save. No set modifications occur, this tool is a text converter. ++ ++.SH EXAMPLES ++Basic operation examples. ++ ++Single command translation, assuming the original file: ++ ++.nf ++create test1 hash:ip,port family inet counters timeout 300 hashsize 1024 maxelem 65536 bucketsize 12 initval 0xb5c4be5d ++add test1 1.1.1.1,udp:20 ++add test1 1.1.1.1,21 ++create test2 hash:ip,port family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0xb5c4be5d ++.fi ++ ++which results in the following translation: ++ ++.nf ++root@machine:~# ipset-translate restore < file.ipt ++add set inet global test1 { type ipv4_addr . inet_proto . inet_service; counter; timeout 300s; size 65536; } ++add element inet global test1 { 1.1.1.1 . udp . 20 } ++add element inet global test1 { 1.1.1.1 . tcp . 21 } ++add set inet global test2 { type ipv4_addr . inet_proto . inet_service; size 65536; } ++.fi ++ ++.SH LIMITATIONS ++A few IP sets options may be not supported because they are not yet implemented ++in \fBnftables(8)\fP. ++ ++Contrary to \fBnftables(8)\fP, IP sets are not attached to a specific table. ++The translation utility assumes that sets are created in a table whose name ++is \fBglobal\fP and family is \fBinet\fP. You might want to update the ++resulting translation to use a different table name and family for your sets. ++ ++To get up-to-date information about this, please head to ++\fBhttps://wiki.nftables.org/\fP. ++ ++.SH SEE ALSO ++\fBnft(8)\fP, \fBipset(8)\fP ++ ++.SH AUTHORS ++The nftables framework has been written by the Netfilter Project ++(https://www.netfilter.org). ++ ++This manual page was written by Pablo Neira Ayuso ++. ++ ++This documentation is free/libre under the terms of the GPLv2+. ++ ++This tool was funded through the NGI0 PET Fund, a fund established by NLnet with ++financial support from the European Commission's Next Generation Internet ++programme, under the aegis of DG Communications Networks, Content and Technology ++under grant agreement No 825310. +diff --git a/src/ipset.c b/src/ipset.c +index ee36a06e595de..6d42b60d2fe9d 100644 +--- a/src/ipset.c ++++ b/src/ipset.c +@@ -9,9 +9,11 @@ + #include /* assert */ + #include /* fprintf */ + #include /* exit */ ++#include /* strcmp */ + + #include + #include /* ipset library */ ++#include /* translate to nftables */ + + int + main(int argc, char *argv[]) +@@ -29,7 +31,11 @@ main(int argc, char *argv[]) + exit(1); + } + +- ret = ipset_parse_argv(ipset, argc, argv); ++ if (!strcmp(argv[0], "ipset-translate")) { ++ ret = ipset_xlate_argv(ipset, argc, argv); ++ } else { ++ ret = ipset_parse_argv(ipset, argc, argv); ++ } + + ipset_fini(ipset); + +-- +2.38.0 + diff --git a/SOURCES/0005-tests-add-tests-ipset-to-nftables.patch b/SOURCES/0005-tests-add-tests-ipset-to-nftables.patch new file mode 100644 index 0000000..c93986e --- /dev/null +++ b/SOURCES/0005-tests-add-tests-ipset-to-nftables.patch @@ -0,0 +1,186 @@ +From 55554de816520471e11f39b99468d5777ae57937 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Fri, 25 Jun 2021 22:30:43 +0200 +Subject: [PATCH] tests: add tests ipset to nftables + +This test checks that the translation from ipset to nftables is correct. + +term$ cd tests/xlate +term$ ./runtest.sh + +in case that the translation is not correct, it shows the diff with expected +translation output. + +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Jozsef Kadlecsik +(cherry picked from commit 7587d1c4b5465f3b5315536b439b63a5ffe0311d) +--- + tests/xlate/runtest.sh | 29 +++++++++++++++++++++ + tests/xlate/xlate.t | 55 ++++++++++++++++++++++++++++++++++++++++ + tests/xlate/xlate.t.nft | 56 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 140 insertions(+) + create mode 100755 tests/xlate/runtest.sh + create mode 100644 tests/xlate/xlate.t + create mode 100644 tests/xlate/xlate.t.nft + +diff --git a/tests/xlate/runtest.sh b/tests/xlate/runtest.sh +new file mode 100755 +index 0000000000000..a2a02c05d7573 +--- /dev/null ++++ b/tests/xlate/runtest.sh +@@ -0,0 +1,29 @@ ++#!/bin/bash ++ ++DIFF=$(which diff) ++if [ ! -x "$DIFF" ] ; then ++ echo "ERROR: missing diff" ++ exit 1 ++fi ++ ++IPSET_XLATE=$(which ipset-translate) ++if [ ! -x "$IPSET_XLATE" ] ; then ++ echo "ERROR: ipset-translate is not installed yet" ++ exit 1 ++fi ++ ++TMP=$(mktemp) ++ipset-translate restore < xlate.t &> $TMP ++if [ $? -ne 0 ] ++then ++ cat $TMP ++ echo -e "[\033[0;31mERROR\033[0m] failed to run ipset-translate" ++ exit 1 ++fi ++${DIFF} -u xlate.t.nft $TMP ++if [ $? -eq 0 ] ++then ++ echo -e "[\033[0;32mOK\033[0m] tests are fine!" ++else ++ echo -e "[\033[0;31mERROR\033[0m] unexpected ipset to nftables translation" ++fi +diff --git a/tests/xlate/xlate.t b/tests/xlate/xlate.t +new file mode 100644 +index 0000000000000..b1e7d288e2a98 +--- /dev/null ++++ b/tests/xlate/xlate.t +@@ -0,0 +1,55 @@ ++create hip1 hash:ip ++add hip1 192.168.10.2 ++add hip1 192.168.10.3 ++create hip2 hash:ip hashsize 128 bucketsize 255 timeout 4 ++add hip2 192.168.10.3 ++add hip2 192.168.10.4 timeout 10 ++create hip3 hash:ip counters ++add hip3 192.168.10.3 packets 5 bytes 3456 ++create hip4 hash:ip netmask 24 ++add hip4 192.168.10.0 ++create hip5 hash:ip maxelem 24 ++add hip5 192.168.10.0 ++create hip6 hash:ip comment ++add hip5 192.168.10.1 ++add hip5 192.168.10.2 comment "this is a comment" ++create ipp1 hash:ip,port ++add ipp1 192.168.10.1,0 ++add ipp1 192.168.10.2,5 ++create ipp2 hash:ip,port timeout 4 ++add ipp2 192.168.10.1,0 timeout 12 ++add ipp2 192.168.10.2,5 ++create ipp3 hash:ip,port counters ++add ipp3 192.168.10.3,20 packets 5 bytes 3456 ++create ipp4 hash:ip,port timeout 4 counters ++add ipp4 192.168.10.3,20 packets 5 bytes 3456 ++create bip1 bitmap:ip range 2.0.0.1-2.1.0.1 timeout 5 ++create bip2 bitmap:ip range 10.0.0.0/8 netmask 24 timeout 5 ++add bip2 10.10.10.0 ++add bip2 10.10.20.0 timeout 12 ++create net1 hash:net ++add net1 192.168.10.0/24 ++create net2 hash:net,net ++add net2 192.168.10.0/24,192.168.20.0/24 ++create hm1 hash:mac ++add hm1 aa:bb:cc:dd:ee:ff ++create him1 hash:ip,mac ++add him1 1.1.1.1,aa:bb:cc:dd:ee:ff ++create ni1 hash:net,iface ++add ni1 1.1.1.0/24,eth0 ++create nip1 hash:net,port ++add nip1 1.1.1.0/24,22 ++create npn1 hash:net,port,net ++add npn1 1.1.1.0/24,22,2.2.2.0/24 ++create nn1 hash:net,net ++add nn1 1.1.1.0/24,2.2.2.0/24 ++create ipn1 hash:ip,port,net ++add ipn1 1.1.1.1,22,2.2.2.0/24 ++create ipi1 hash:ip,port,ip ++add ipi1 1.1.1.1,22,2.2.2.2 ++create im1 hash:ip,mark ++add im1 1.1.1.1,0x123456 ++create bp1 bitmap:port range 1-1024 ++add bp1 22 ++create bim1 bitmap:ip,mac range 1.1.1.0/24 ++add bim1 1.1.1.1,aa:bb:cc:dd:ee:ff +diff --git a/tests/xlate/xlate.t.nft b/tests/xlate/xlate.t.nft +new file mode 100644 +index 0000000000000..96eba3b0175ea +--- /dev/null ++++ b/tests/xlate/xlate.t.nft +@@ -0,0 +1,56 @@ ++add table inet global ++add set inet global hip1 { type ipv4_addr; } ++add element inet global hip1 { 192.168.10.2 } ++add element inet global hip1 { 192.168.10.3 } ++add set inet global hip2 { type ipv4_addr; timeout 4s; } ++add element inet global hip2 { 192.168.10.3 } ++add element inet global hip2 { 192.168.10.4 timeout 10s } ++add set inet global hip3 { type ipv4_addr; counter; } ++add element inet global hip3 { 192.168.10.3 counter packets 5 bytes 3456 } ++add set inet global hip4 { type ipv4_addr; flags interval; } ++add element inet global hip4 { 192.168.10.0/24 } ++add set inet global hip5 { type ipv4_addr; size 24; } ++add element inet global hip5 { 192.168.10.0 } ++add set inet global hip6 { type ipv4_addr; } ++add element inet global hip5 { 192.168.10.1 } ++add element inet global hip5 { 192.168.10.2 comment "this is a comment" } ++add set inet global ipp1 { type ipv4_addr . inet_proto . inet_service; } ++add element inet global ipp1 { 192.168.10.1 . tcp . 0 } ++add element inet global ipp1 { 192.168.10.2 . tcp . 5 } ++add set inet global ipp2 { type ipv4_addr . inet_proto . inet_service; timeout 4s; } ++add element inet global ipp2 { 192.168.10.1 . tcp . 0 timeout 12s } ++add element inet global ipp2 { 192.168.10.2 . tcp . 5 } ++add set inet global ipp3 { type ipv4_addr . inet_proto . inet_service; counter; } ++add element inet global ipp3 { 192.168.10.3 . tcp . 20 counter packets 5 bytes 3456 } ++add set inet global ipp4 { type ipv4_addr . inet_proto . inet_service; counter; timeout 4s; } ++add element inet global ipp4 { 192.168.10.3 . tcp . 20 counter packets 5 bytes 3456 } ++add set inet global bip1 { type ipv4_addr; timeout 5s; } ++add set inet global bip2 { type ipv4_addr; timeout 5s; flags interval; } ++add element inet global bip2 { 10.10.10.0/24 } ++add element inet global bip2 { 10.10.20.0/24 timeout 12s } ++add set inet global net1 { type ipv4_addr; flags interval; } ++add element inet global net1 { 192.168.10.0/24 } ++add set inet global net2 { type ipv4_addr . ipv4_addr; flags interval; } ++add element inet global net2 { 192.168.10.0/24 . 192.168.20.0/24 } ++add set inet global hm1 { type ether_addr; } ++add element inet global hm1 { aa:bb:cc:dd:ee:ff } ++add set inet global him1 { type ipv4_addr . ether_addr; } ++add element inet global him1 { 1.1.1.1 . aa:bb:cc:dd:ee:ff } ++add set inet global ni1 { type ipv4_addr . ifname; flags interval; } ++add element inet global ni1 { 1.1.1.0/24 . eth0 } ++add set inet global nip1 { type ipv4_addr . inet_proto . inet_service; flags interval; } ++add element inet global nip1 { 1.1.1.0/24 . tcp . 22 } ++add set inet global npn1 { type ipv4_addr . inet_proto . inet_service . ipv4_addr; flags interval; } ++add element inet global npn1 { 1.1.1.0/24 . tcp . 22 . 2.2.2.0/24 } ++add set inet global nn1 { type ipv4_addr . ipv4_addr; flags interval; } ++add element inet global nn1 { 1.1.1.0/24 . 2.2.2.0/24 } ++add set inet global ipn1 { type ipv4_addr . inet_proto . inet_service . ipv4_addr; flags interval; } ++add element inet global ipn1 { 1.1.1.1 . tcp . 22 . 2.2.2.0/24 } ++add set inet global ipi1 { type ipv4_addr . inet_proto . inet_service . ipv4_addr; } ++add element inet global ipi1 { 1.1.1.1 . tcp . 22 . 2.2.2.2 } ++add set inet global im1 { type ipv4_addr . mark; } ++add element inet global im1 { 1.1.1.1 . 0x00123456 } ++add set inet global bp1 { type inet_service; } ++add element inet global bp1 { 22 } ++add set inet global bim1 { type ipv4_addr . ether_addr; } ++add element inet global bim1 { 1.1.1.1 . aa:bb:cc:dd:ee:ff } +-- +2.38.0 + diff --git a/SOURCES/0006-Fix-typo-in-ipset-translate-man-page.patch b/SOURCES/0006-Fix-typo-in-ipset-translate-man-page.patch new file mode 100644 index 0000000..1ecbfab --- /dev/null +++ b/SOURCES/0006-Fix-typo-in-ipset-translate-man-page.patch @@ -0,0 +1,32 @@ +From ad4513664b99f5913578ee9771836997f88f4c96 Mon Sep 17 00:00:00 2001 +From: "Bernhard M. Wiedemann" +Date: Wed, 29 Sep 2021 09:55:43 +0200 +Subject: [PATCH] Fix typo in ipset-translate man page + +originally reported in +https://lists.opensuse.org/archives/list/factory@lists.opensuse.org/thread/ZIXKNQHSSCQ4ZLEGYYKLAXQ4PQ5EYFGZ/ +by Larry Len Rainey + +Signed-off-by: Bernhard M. Wiedemann +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit c74a420471fd693f89e0b0e19f93c88af22fb7de) +--- + src/ipset-translate.8 | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/ipset-translate.8 b/src/ipset-translate.8 +index bb4e737e14806..55ce2a99f2cf2 100644 +--- a/src/ipset-translate.8 ++++ b/src/ipset-translate.8 +@@ -33,7 +33,7 @@ to \fBnftables(8)\fP. + The only available command is: + + .IP \[bu] 2 +-ipset-translate restores < file.ipt ++ipset-translate restore < file.ipt + + .SH USAGE + The \fBipset-translate\fP tool reads an IP sets file in the syntax produced by +-- +2.38.0 + diff --git a/SOURCES/0007-Fix-IPv6-sets-nftables-translation.patch b/SOURCES/0007-Fix-IPv6-sets-nftables-translation.patch new file mode 100644 index 0000000..86b6371 --- /dev/null +++ b/SOURCES/0007-Fix-IPv6-sets-nftables-translation.patch @@ -0,0 +1,92 @@ +From 343650906603dff56f766d1fbcef1de64a98e14a Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Mon, 28 Feb 2022 19:52:57 +0100 +Subject: [PATCH] Fix IPv6 sets nftables translation + +The parser assumes the set is an IPv4 ipset because IPSET_OPT_FAMILY is +not set. + + # ipset-translate restore < ./ipset-mwan3_set_connected_ipv6.dump + add table inet global + add set inet global mwan3_connected_v6 { type ipv6_addr; flags interval; } + flush set inet global mwan3_connected_v6 + ipset v7.15: Error in line 4: Syntax error: '64' is out of range 0-32 + +Remove ipset_xlate_type_get(), call ipset_xlate_set_get() instead to +obtain the set type and family. + +Reported-by: Florian Eckert +Fixes: 325af556cd3a ("add ipset to nftables translation infrastructure") +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit be7f6099feb7b5d34715b06f9308877cdcdc404a) +--- + lib/ipset.c | 24 ++++++++++-------------- + tests/xlate/xlate.t | 2 ++ + tests/xlate/xlate.t.nft | 2 ++ + 3 files changed, 14 insertions(+), 14 deletions(-) + +diff --git a/lib/ipset.c b/lib/ipset.c +index 73e67db88e0d1..50f86aee045bc 100644 +--- a/lib/ipset.c ++++ b/lib/ipset.c +@@ -949,18 +949,6 @@ ipset_xlate_set_get(struct ipset *ipset, const char *name) + return NULL; + } + +-static const struct ipset_type *ipset_xlate_type_get(struct ipset *ipset, +- const char *name) +-{ +- const struct ipset_xlate_set *set; +- +- set = ipset_xlate_set_get(ipset, name); +- if (!set) +- return NULL; +- +- return set->type; +-} +- + static int + ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) + { +@@ -1282,8 +1270,16 @@ ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) + if (!ipset->xlate) { + type = ipset_type_get(session, cmd); + } else { +- type = ipset_xlate_type_get(ipset, arg0); +- ipset_session_data_set(session, IPSET_OPT_TYPE, type); ++ const struct ipset_xlate_set *xlate_set; ++ ++ xlate_set = ipset_xlate_set_get(ipset, arg0); ++ if (xlate_set) { ++ ipset_session_data_set(session, IPSET_OPT_TYPE, ++ xlate_set->type); ++ ipset_session_data_set(session, IPSET_OPT_FAMILY, ++ &xlate_set->family); ++ type = xlate_set->type; ++ } + } + if (type == NULL) + return ipset->standard_error(ipset, p); +diff --git a/tests/xlate/xlate.t b/tests/xlate/xlate.t +index b1e7d288e2a98..f09cb202bb6c0 100644 +--- a/tests/xlate/xlate.t ++++ b/tests/xlate/xlate.t +@@ -53,3 +53,5 @@ create bp1 bitmap:port range 1-1024 + add bp1 22 + create bim1 bitmap:ip,mac range 1.1.1.0/24 + add bim1 1.1.1.1,aa:bb:cc:dd:ee:ff ++create hn6 hash:net family inet6 ++add hn6 fe80::/64 +diff --git a/tests/xlate/xlate.t.nft b/tests/xlate/xlate.t.nft +index 96eba3b0175ea..0152a30811258 100644 +--- a/tests/xlate/xlate.t.nft ++++ b/tests/xlate/xlate.t.nft +@@ -54,3 +54,5 @@ add set inet global bp1 { type inet_service; } + add element inet global bp1 { 22 } + add set inet global bim1 { type ipv4_addr . ether_addr; } + add element inet global bim1 { 1.1.1.1 . aa:bb:cc:dd:ee:ff } ++add set inet global hn6 { type ipv6_addr; flags interval; } ++add element inet global hn6 { fe80::/64 } +-- +2.38.0 + diff --git a/SOURCES/0008-ipset-translate-allow-invoking-with-a-path-name.patch b/SOURCES/0008-ipset-translate-allow-invoking-with-a-path-name.patch new file mode 100644 index 0000000..3cb85e9 --- /dev/null +++ b/SOURCES/0008-ipset-translate-allow-invoking-with-a-path-name.patch @@ -0,0 +1,47 @@ +From a7d1e05c0fcae89fffcd5aa235ea32d16becbd21 Mon Sep 17 00:00:00 2001 +From: Quentin Armitage +Date: Thu, 11 Aug 2022 17:52:18 +0100 +Subject: [PATCH] ipset-translate: allow invoking with a path name + +Executing /usr/sbin/ipset-translate results in the ipset functionality being run, rather than the ipset-translate functionality. + + # ipset-translate destroy fred + This command is not supported, use `ipset-translate restore < file' + + # /usr/sbin/ipset-translate destroy fred + ipset v7.15: The set with the given name does not exist + +use basename() to resolve the issue. + +Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1626 +Signed-off-by: Quentin Armitage +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit e1b60b2a93356c313cccb2abfdae4b58d530e02b) +--- + src/ipset.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/ipset.c b/src/ipset.c +index 6d42b60d2fe9d..162f477d49cd0 100644 +--- a/src/ipset.c ++++ b/src/ipset.c +@@ -6,6 +6,7 @@ + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ ++#define _GNU_SOURCE + #include /* assert */ + #include /* fprintf */ + #include /* exit */ +@@ -31,7 +32,7 @@ main(int argc, char *argv[]) + exit(1); + } + +- if (!strcmp(argv[0], "ipset-translate")) { ++ if (!strcmp(basename(argv[0]), "ipset-translate")) { + ret = ipset_xlate_argv(ipset, argc, argv); + } else { + ret = ipset_parse_argv(ipset, argc, argv); +-- +2.38.0 + diff --git a/SOURCES/0009-Fix-all-debug-mode-warnings.patch b/SOURCES/0009-Fix-all-debug-mode-warnings.patch new file mode 100644 index 0000000..4fb3cc2 --- /dev/null +++ b/SOURCES/0009-Fix-all-debug-mode-warnings.patch @@ -0,0 +1,171 @@ +From aba564ecc621345fcfea2fe883cbfd8d02e54026 Mon Sep 17 00:00:00 2001 +From: Jozsef Kadlecsik +Date: Sun, 20 Nov 2022 22:43:59 +0100 +Subject: [PATCH] Fix all debug mode warnings + +(cherry picked from commit e39e3466d2d38cdfe83447f391b550e607bc3ce8) + +Conflicts: + lib/parse.c +- Fixed code does not exist due to missing commit 79184e760edfb + ("Add missing hunk to patch "Allow specifying protocols by number"") +--- + include/libipset/list_sort.h | 4 ++-- + lib/ipset.c | 32 +++++++++++++++++++------------- + 2 files changed, 21 insertions(+), 15 deletions(-) + +diff --git a/include/libipset/list_sort.h b/include/libipset/list_sort.h +index 70bb02d3d1b68..d9d7b36b8380f 100644 +--- a/include/libipset/list_sort.h ++++ b/include/libipset/list_sort.h +@@ -61,7 +61,7 @@ static inline void list_del(struct list_head *entry) + // entry->prev = (void *) 0; + } + +-static inline void __list_splice(const struct list_head *list, ++static inline void __list_splice(struct list_head *list, + struct list_head *prev, + struct list_head *next) + { +@@ -75,7 +75,7 @@ static inline void __list_splice(const struct list_head *list, + next->prev = last; + } + +-static inline void list_splice(const struct list_head *list, ++static inline void list_splice(struct list_head *list, + struct list_head *head) + { + if (!list_empty(list)) +diff --git a/lib/ipset.c b/lib/ipset.c +index 50f86aee045bc..f57b07413cba5 100644 +--- a/lib/ipset.c ++++ b/lib/ipset.c +@@ -30,6 +30,7 @@ + #include /* prototypes */ + #include /* compiler attributes */ + #include /* lists */ ++#include /* ipset_xlate_argv */ + + static char program_name[] = PACKAGE; + static char program_version[] = PACKAGE_VERSION; +@@ -936,10 +937,10 @@ static const char *cmd_prefix[] = { + [IPSET_TEST] = "test SETNAME", + }; + +-static const struct ipset_xlate_set * ++static struct ipset_xlate_set * + ipset_xlate_set_get(struct ipset *ipset, const char *name) + { +- const struct ipset_xlate_set *set; ++ struct ipset_xlate_set *set; + + list_for_each_entry(set, &ipset->xlate_sets, list) { + if (!strcmp(set->name, name)) +@@ -958,7 +959,7 @@ ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) + char *arg0 = NULL, *arg1 = NULL; + const struct ipset_envopts *opt; + const struct ipset_commands *command; +- const struct ipset_type *type; ++ const struct ipset_type *type = NULL; + struct ipset_session *session = ipset->session; + void *p = ipset_session_printf_private(session); + int argc = oargc; +@@ -1127,6 +1128,7 @@ ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) + if (arg0) { + const struct ipset_arg *arg; + int k; ++ enum ipset_adt c; + + /* Type-specific help, without kernel checking */ + type = type_find(arg0); +@@ -1136,11 +1138,11 @@ ipset_parser(struct ipset *ipset, int oargc, char *oargv[]) + "Unknown settype: `%s'", arg0); + printf("\n%s type specific options:\n\n", type->name); + for (i = 0; cmd_help_order[i] != IPSET_CADT_MAX; i++) { +- cmd = cmd_help_order[i]; ++ c = cmd_help_order[i]; + printf("%s %s %s\n", +- cmd_prefix[cmd], type->name, type->cmd[cmd].help); +- for (k = 0; type->cmd[cmd].args[k] != IPSET_ARG_NONE; k++) { +- arg = ipset_keyword(type->cmd[cmd].args[k]); ++ cmd_prefix[c], type->name, type->cmd[c].help); ++ for (k = 0; type->cmd[c].args[k] != IPSET_ARG_NONE; k++) { ++ arg = ipset_keyword(type->cmd[c].args[k]); + if (!arg->help || arg->help[0] == '\0') + continue; + printf(" %s\n", arg->help); +@@ -1548,7 +1550,7 @@ ipset_fini(struct ipset *ipset) + } + + /* Ignore the set family, use inet. */ +-static const char *ipset_xlate_family(uint8_t family) ++static const char *ipset_xlate_family(uint8_t family UNUSED) + { + return "inet"; + } +@@ -1705,6 +1707,10 @@ ipset_xlate_type_to_nftables(int family, enum ipset_xlate_set_type type, + else if (family == AF_INET6) + return "ipv6_addr"; + break; ++ case IPSET_XLATE_TYPE_UNKNOWN: ++ break; ++ default: ++ break; + } + /* This should not ever happen. */ + return "unknown"; +@@ -1729,7 +1735,6 @@ static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, + char buf[64]; + bool concat; + char *term; +- int i; + + session = ipset_session(ipset); + data = ipset_session_data(session); +@@ -1843,7 +1848,7 @@ static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, + return -1; + case IPSET_CMD_LIST: + if (!set) { +- printf("list sets %s\n", ++ printf("list sets %s %s\n", + ipset_xlate_family(family), table); + } else { + printf("list set %s %s %s\n", +@@ -1902,6 +1907,8 @@ static int ipset_xlate(struct ipset *ipset, enum ipset_cmd cmd, + } + if (ipset_data_test(data, IPSET_OPT_ETHER)) { + ipset_print_ether(buf, sizeof(buf), data, IPSET_OPT_ETHER, 0); ++ size_t i; ++ + for (i = 0; i < strlen(buf); i++) + buf[i] = tolower(buf[i]); + +@@ -1964,7 +1971,6 @@ static int ipset_xlate_restore(struct ipset *ipset) + struct ipset_session *session = ipset_session(ipset); + struct ipset_data *data = ipset_session_data(session); + void *p = ipset_session_printf_private(session); +- const char *filename; + enum ipset_cmd cmd; + FILE *f = stdin; + int ret = 0; +@@ -1973,7 +1979,7 @@ static int ipset_xlate_restore(struct ipset *ipset) + if (ipset->filename) { + f = fopen(ipset->filename, "r"); + if (!f) { +- fprintf(stderr, "cannot open file `%s'\n", filename); ++ fprintf(stderr, "cannot open file `%s'\n", ipset->filename); + return -1; + } + } +@@ -2007,7 +2013,7 @@ static int ipset_xlate_restore(struct ipset *ipset) + ipset_data_reset(data); + } + +- if (filename) ++ if (ipset->filename) + fclose(f); + + return ret; +-- +2.38.0 + diff --git a/SOURCES/0010-Add-missing-function-to-libipset.map-and-bump-librar.patch b/SOURCES/0010-Add-missing-function-to-libipset.map-and-bump-librar.patch new file mode 100644 index 0000000..8771094 --- /dev/null +++ b/SOURCES/0010-Add-missing-function-to-libipset.map-and-bump-librar.patch @@ -0,0 +1,44 @@ +From 68a945a5c140b3067e90baae3f35441dd8bacf25 Mon Sep 17 00:00:00 2001 +From: Jozsef Kadlecsik +Date: Wed, 28 Jul 2021 16:02:49 +0200 +Subject: [PATCH] Add missing function to libipset.map and bump library version + +A new function was not added to libipset.map at the previous release, +fix it. Reported by Jan Engelhardt. + +Signed-off-by: Jozsef Kadlecsik +(cherry picked from commit 8a0df0c759cf9f0ca6f0cfa512ebf4832fd73729) +--- + Make_global.am | 2 +- + lib/libipset.map | 5 +++++ + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/Make_global.am b/Make_global.am +index 3ad7f73b28479..ed92cfec7a207 100644 +--- a/Make_global.am ++++ b/Make_global.am +@@ -69,7 +69,7 @@ + # interface. + + # curr:rev:age +-LIBVERSION = 15:0:2 ++LIBVERSION = 16:0:3 + + AM_CPPFLAGS = $(kinclude_CFLAGS) $(all_includes) -I$(top_srcdir)/include + +diff --git a/lib/libipset.map b/lib/libipset.map +index 12d16a4faf53c..c380f9cde2edc 100644 +--- a/lib/libipset.map ++++ b/lib/libipset.map +@@ -208,3 +208,8 @@ LIBIPSET_4.10 { + ipset_print_hexnumber; + } LIBIPSET_4.9; + ++LIBIPSET_4.11 { ++global: ++ ipset_xlate_argv; ++} LIBIPSET_4.10; ++ +-- +2.38.0 + diff --git a/SOURCES/ipset.start-stop b/SOURCES/ipset.start-stop index 0c493dc..377beed 100644 --- a/SOURCES/ipset.start-stop +++ b/SOURCES/ipset.start-stop @@ -257,7 +257,13 @@ load() { CLEAN_FILES="${CLEAN_FILES} ${mangled}" chmod 600 "${mangled}" - awk '/^(add|create) ('"${conflicts}"')/ { printf "%s ",$1; system("echo '${salt}'" $2 " | md5sum | head -c31"); $1=""; $2=""; print; next} {print}' "${merged}" > "${mangled}" + cat "${merged}" > "${mangled}" + IFS='|' + for set in ${conflicts}; do + new_name=$(echo "${salt}${set}" | md5sum | head -c31) + echo "s/^(add|create) $set /\1 $new_name /" + done | sed -i -r -f - "${mangled}" + unset IFS if ! ipset_restore "${mangled}"; then err "Failed to restore configured sets" exit 1 diff --git a/SPECS/ipset.spec b/SPECS/ipset.spec index 8bdf960..4b34a59 100644 --- a/SPECS/ipset.spec +++ b/SPECS/ipset.spec @@ -3,7 +3,7 @@ Name: ipset Version: 7.11 -Release: 6%{?dist} +Release: 8%{?dist} Summary: Manage Linux IP sets License: GPLv2 @@ -15,6 +15,15 @@ Source3: %{name}-config Source4: %{name}.save-legacy Patch1: 0001-Add-deprecation-notice-to-ipset.8.patch +Patch2: 0002-lib-split-parser-from-command-execution.patch +Patch3: 0003-lib-Detach-restore-routine-from-parser.patch +Patch4: 0004-add-ipset-to-nftables-translation-infrastructure.patch +Patch5: 0005-tests-add-tests-ipset-to-nftables.patch +Patch6: 0006-Fix-typo-in-ipset-translate-man-page.patch +Patch7: 0007-Fix-IPv6-sets-nftables-translation.patch +Patch8: 0008-ipset-translate-allow-invoking-with-a-path-name.patch +Patch9: 0009-Fix-all-debug-mode-warnings.patch +Patch10: 0010-Add-missing-function-to-libipset.map-and-bump-librar.patch BuildRequires: libmnl-devel BuildRequires: automake @@ -118,6 +127,8 @@ install -c -m 755 %{SOURCE4} %{buildroot}/%{legacy_actions}/ipset/save # Create directory for configuration mkdir -p %{buildroot}%{_sysconfdir}/%{name} +# Turn absolute symlink into a relative one +ln -sf %{name} %{buildroot}/%{_sbindir}/%{name}-translate %preun if [[ $1 -eq 0 && -n $(lsmod | grep "^xt_set ") ]]; then @@ -147,7 +158,9 @@ fi %doc ChangeLog %license COPYING %{_mandir}/man8/%{name}.8.* +%{_mandir}/man8/%{name}-translate.8.* %{_sbindir}/%{name} +%{_sbindir}/%{name}-translate %files libs %license COPYING @@ -170,6 +183,21 @@ fi %changelog +* Fri Nov 25 2022 Phil Sutter - 7.11-8 +- Ship iptables-translate utility with ipset package +- Add missing function to libipset.map and bump library version +- Fix all debug mode warnings +- ipset-translate: allow invoking with a path name +- Fix IPv6 sets nftables translation +- Fix typo in ipset-translate man page +- tests: add tests ipset to nftables +- add ipset to nftables translation infrastructure +- lib: Detach restore routine from parser +- lib: split parser from command execution + +* Mon Jan 31 2022 Phil Sutter - 7.11-7 +- Fix for bad performance restoring large sets which are in use + * Mon Nov 08 2021 Phil Sutter - 7.11-6 - Sync ipset.service with RHEL8, fix the required package name