From 1aa3f98512a2f389cc883223f831721dbfd3a7b6 Mon Sep 17 00:00:00 2001 From: MSVSphere Packaging Team Date: Fri, 14 Apr 2023 13:36:06 +0300 Subject: [PATCH] import ipset-7.11-8.el9 --- .gitignore | 1 + .ipset.metadata | 1 + ...01-Add-deprecation-notice-to-ipset.8.patch | 48 + ...-split-parser-from-command-execution.patch | 84 ++ ...b-Detach-restore-routine-from-parser.patch | 44 + ...-nftables-translation-infrastructure.patch | 825 ++++++++++++++++++ ...05-tests-add-tests-ipset-to-nftables.patch | 186 ++++ ...Fix-typo-in-ipset-translate-man-page.patch | 32 + ...7-Fix-IPv6-sets-nftables-translation.patch | 92 ++ ...late-allow-invoking-with-a-path-name.patch | 47 + .../0009-Fix-all-debug-mode-warnings.patch | 171 ++++ ...tion-to-libipset.map-and-bump-librar.patch | 44 + SOURCES/ipset-config | 5 + SOURCES/ipset.save-legacy | 2 + SOURCES/ipset.service | 19 + SOURCES/ipset.start-stop | 359 ++++++++ SPECS/ipset.spec | 368 ++++++++ 17 files changed, 2328 insertions(+) create mode 100644 .gitignore create mode 100644 .ipset.metadata create mode 100644 SOURCES/0001-Add-deprecation-notice-to-ipset.8.patch create mode 100644 SOURCES/0002-lib-split-parser-from-command-execution.patch create mode 100644 SOURCES/0003-lib-Detach-restore-routine-from-parser.patch create mode 100644 SOURCES/0004-add-ipset-to-nftables-translation-infrastructure.patch create mode 100644 SOURCES/0005-tests-add-tests-ipset-to-nftables.patch create mode 100644 SOURCES/0006-Fix-typo-in-ipset-translate-man-page.patch create mode 100644 SOURCES/0007-Fix-IPv6-sets-nftables-translation.patch create mode 100644 SOURCES/0008-ipset-translate-allow-invoking-with-a-path-name.patch create mode 100644 SOURCES/0009-Fix-all-debug-mode-warnings.patch create mode 100644 SOURCES/0010-Add-missing-function-to-libipset.map-and-bump-librar.patch create mode 100644 SOURCES/ipset-config create mode 100644 SOURCES/ipset.save-legacy create mode 100644 SOURCES/ipset.service create mode 100644 SOURCES/ipset.start-stop create mode 100644 SPECS/ipset.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7739a2a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/ipset-7.11.tar.bz2 diff --git a/.ipset.metadata b/.ipset.metadata new file mode 100644 index 0000000..9007288 --- /dev/null +++ b/.ipset.metadata @@ -0,0 +1 @@ +a10e4e8f0ed2fa540b653d987a93069c0c276f61 SOURCES/ipset-7.11.tar.bz2 diff --git a/SOURCES/0001-Add-deprecation-notice-to-ipset.8.patch b/SOURCES/0001-Add-deprecation-notice-to-ipset.8.patch new file mode 100644 index 0000000..92ba754 --- /dev/null +++ b/SOURCES/0001-Add-deprecation-notice-to-ipset.8.patch @@ -0,0 +1,48 @@ +From e9398b721855f8fbfb30a21d475d0aeb7f85bbe5 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 25 Jun 2021 13:47:57 +0200 +Subject: [PATCH] Add deprecation notice to ipset.8 + +This is RHEL9 trying to friendly kick people towards nftables. +--- + src/ipset.8 | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +diff --git a/src/ipset.8 b/src/ipset.8 +index 97cece9fb04b5..d68e9bbc22891 100644 +--- a/src/ipset.8 ++++ b/src/ipset.8 +@@ -51,6 +51,18 @@ COMMANDS := { \fBcreate\fR | \fBadd\fR | \fBdel\fR | \fBtest\fR | \fBdestroy\fR + .PP + \fBipset\fR \fB\-\fR + .SH "DESCRIPTION" ++This tool is ++.B deprecated ++in Red Hat Enterprise Linux. It is maintenance only and will not receive new ++features. New setups should use ++.BR nft (8). ++Existing setups should migrate to ++.BR nft (8) ++when possible. See ++.UR https://red.ht/nft_your_tables ++.UE ++for details. ++.PP + \fBipset\fR + is used to set up, maintain and inspect so called IP sets in the Linux + kernel. Depending on the type of the set, an IP set may store IP(v4/v6) +@@ -1099,8 +1111,9 @@ Bugs? No, just funny features. :\-) + OK, just kidding... + .SH "SEE ALSO" + \fBiptables\fR(8), +-\fBip6tables\fR(8) +-\fBiptables-extensions\fR(8) ++\fBip6tables\fR(8), ++\fBiptables-extensions\fR(8), ++\fBnft\fR(8) + .SH "AUTHORS" + Jozsef Kadlecsik wrote ipset, which is based on ippool by + Joakim Axelsson, Patrick Schaaf and Martin Josefsson. +-- +2.31.1 + 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-config b/SOURCES/ipset-config new file mode 100644 index 0000000..decb760 --- /dev/null +++ b/SOURCES/ipset-config @@ -0,0 +1,5 @@ +# Save current ipsets on stop. +# Value: yes|no, default: no +# Saves all ipsets to /etc/ipset/ipset if service gets stopped +# (e.g. on system shutdown). +IPSET_SAVE_ON_STOP="no" diff --git a/SOURCES/ipset.save-legacy b/SOURCES/ipset.save-legacy new file mode 100644 index 0000000..39db1cf --- /dev/null +++ b/SOURCES/ipset.save-legacy @@ -0,0 +1,2 @@ +#!/bin/bash +exec /usr/libexec/ipset/ipset.start-stop save diff --git a/SOURCES/ipset.service b/SOURCES/ipset.service new file mode 100644 index 0000000..65c3121 --- /dev/null +++ b/SOURCES/ipset.service @@ -0,0 +1,19 @@ +[Unit] +Description=IP sets for iptables +Before=iptables.service +Before=ip6tables.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/libexec/ipset/ipset.start-stop start +ExecStop=/usr/libexec/ipset/ipset.start-stop stop +ExecReload=/usr/libexec/ipset/ipset.start-stop reload +# Save current ipset entries on stop. +# Value: yes|no, default: no +# Saves all ipsets to /etc/sysconfig/ipset if ipset gets stopped +Environment=IPSET_SAVE_ON_STOP=no +EnvironmentFile=-/etc/sysconfig/ipset-config + +[Install] +WantedBy=basic.target diff --git a/SOURCES/ipset.start-stop b/SOURCES/ipset.start-stop new file mode 100644 index 0000000..377beed --- /dev/null +++ b/SOURCES/ipset.start-stop @@ -0,0 +1,359 @@ +#!/bin/sh +# +# ipset Start and stop ipset firewall sets +# +# config: /etc/sysconfig/ipset-config + +IPSET_BIN=/usr/sbin/ipset +IPSET_CONFIG=/etc/sysconfig/ipset-config +IPSET_DATA_COMPAT=/etc/sysconfig/ipset +IPSET_DATA_COMPAT_BACKUP=${IPSET_DATA_COMPAT}.save +IPSET_DATA_DIR=/etc/sysconfig/ipset.d +IPSET_DATA_DIR_BACKUP=${IPSET_DATA_DIR}.save +IPSET_DATA_SAVED_FLAG=${IPSET_DATA_DIR}/.saved +IPSET_LOCK=/run/ipset.lock +IPSET_RUN=/run/ipset.run +CLEAN_FILES="" + +trap "rm -rf \${CLEAN_FILES}" EXIT + +info() { + echo "ipset: ${*}" >&2 +} + +warn() { + echo "<4>ipset: ${*}" >&2 +} + +err() { + echo "<3>ipset: ${*}" >&2 +} + +[ -x ${IPSET_BIN} ] || { err "Cannot execute ${IPSET_BIN}"; exit 1; } + +# Source ipset configuration +# shellcheck source=ipset-config +[ -f ${IPSET_CONFIG} ] && . ${IPSET_CONFIG} + +set -f + +lock() { + CLEAN_FILES="${CLEAN_FILES} ${IPSET_LOCK}" + until mkdir ${IPSET_LOCK} 2>/dev/null; do :; done +} + +save() { + fail=0 + + # Make backups of existing configuration first, if any + [ -d ${IPSET_DATA_DIR} ] && mv -Tf ${IPSET_DATA_DIR} ${IPSET_DATA_DIR_BACKUP} + [ -f ${IPSET_DATA_COMPAT} ] && mv -Tf ${IPSET_DATA_COMPAT} ${IPSET_DATA_COMPAT_BACKUP} + + rm -f ${IPSET_DATA_SAVED_FLAG} + + # Save each set in a separate file + mkdir -p ${IPSET_DATA_DIR} + chmod 0700 ${IPSET_DATA_DIR} + IFS=" +" + for set in $(${IPSET_BIN} list -n -t); do + # Empty name allowed, use ".set" as suffix. 'ipset save' doesn't + # quote set names with spaces: if we have a space in the name, + # work around this by quoting it ourselves in the output. + # shellcheck disable=SC2003 # No POSIX equivalent to expr index + if expr index "${set}" " " >/dev/null; then + :> "${IPSET_DATA_DIR}/${set}.set" + for line in $(${IPSET_BIN} save "${set}"); do + create=0 + echo "${line}" | grep -q "^create " && create=1 + if [ $create -eq 1 ]; then + line=${line#create *} + else + line=${line#add *} + fi + line=${line#${set} *} + set="$(echo "${set}" | sed 's/"/\\"/g')" + if [ $create -eq 1 ]; then + echo "create \"${set}\" ${line}" >> "${IPSET_DATA_DIR}/${set}.set" + else + echo "add \"${set}\" ${line}" >> "${IPSET_DATA_DIR}/${set}.set" + fi + done + else + ${IPSET_BIN} save "${set}" > "${IPSET_DATA_DIR}/${set}.set" || fail=1 + fi + [ -f "${IPSET_DATA_DIR}/${set}.set" ] && chmod 600 "${IPSET_DATA_DIR}/${set}.set" + [ $fail -eq 1 ] && err "Cannot save set ${set}" && unset IFS && return 1 + done + touch ${IPSET_DATA_SAVED_FLAG} || { unset IFS; return 1; } + unset IFS + + # Done: remove backups + rm -rf ${IPSET_DATA_DIR_BACKUP} + rm -rf ${IPSET_DATA_COMPAT_BACKUP} + + return 0 +} + +# Generate a grep regexp matching abbreviated command forms. E.g., for create: +# \(c\|cr\|cre\|crea\|creat\|create\) +cmd_short_expr() { + out= + cmd_len=1 + while [ "${cmd_len}" -le "${#1}" ]; do + [ -z "${out}" ] && out='\(' || out="${out}"'\|' + # shellcheck disable=SC2003 # No POSIX equivalent to expr substr + out="${out}$(expr substr "${1}" 1 "${cmd_len}")" + cmd_len=$((cmd_len + 1)) + done + echo "${out}"'\)' +} + +ipset_restore() { + file="${1}" + + retfile="$(mktemp -q /tmp/ipset.XXXXXX)" + CLEAN_FILES="${CLEAN_FILES} ${retfile}" + + # If restore fails due to invalid lines, remove them and retry + while ! restore_err="$( (${IPSET_BIN} -f "${file}" -! restore 2>&1; echo $? >"${retfile}") | head -n1; exit "$(cat "${retfile}")" )"; do + warn "${restore_err}" + case ${restore_err#*: } in + "No command specified"*) + line="$(grep -m1 -n "^${restore_err##* }" "${file}")" + line="${line%:*}" + ;; + "Missing second mandatory argument to command "*) + cmd="${restore_err##* }" + cmd_expr="$(cmd_short_expr "${cmd}")" + line="$(grep -n '^'"${cmd_expr}" "${file}" | grep -m1 -v '^[0-9]\+\:'"${cmd_expr}"'[[:blank:]]\+[^[:blank:]]\+[[:blank:]]\+[^[:blank:]]\+')" + line="${line%:*}" + ;; + "Missing mandatory argument to command "*) + cmd="${restore_err##* }" + cmd_expr="$(cmd_short_expr "${cmd}")" + line="$(grep -n '^'"${cmd_expr}" "${file}" | grep -m1 -v '^[0-9]\+\:'"${cmd_expr}"'[[:blank:]]\+[^[:blank:]]\+')" + line="${line%:*}" + ;; + "Command "*"is invalid in restore mode"*) + restore_err_cmd="${restore_err#*: }" + restore_err_cmd="${restore_err_cmd#*\`}" + restore_err_cmd="${restore_err_cmd%%\'*}" + cmd="${restore_err_cmd##* }" + cmd_expr="$(cmd_short_expr "${cmd}")" + line="$(grep -m1 -ne '^'"${cmd_expr}"'[[:blank:]]\+' -e '^'"${restore_err_cmd}"'$' "${file}")" + line="${line%:*}" + ;; + "Error in line "*) + line="${restore_err%: *}" + line="${line##* }" + ;; + *) + rm "${retfile}" + CLEAN_FILES="${CLEAN_FILES%* ${retfile}}" + return 1 + ;; + esac + + [ -z "${line}" ] && return 1 + + warn "Skipped invalid entry: $(sed "${line}q;d" "${file}")" + sed -i -e "${line}d" "${file}" + + [ -s "${file}" ] || return 1 + done + + rm "${retfile}" + CLEAN_FILES="${CLEAN_FILES%* ${retfile}}" +} + +load() { + if [ -f ${IPSET_DATA_SAVED_FLAG} ]; then + # If we have a cleanly saved directory with all sets, we can + # delete any left-overs and use it + rm -rf ${IPSET_DATA_DIR_BACKUP} + rm -f ${IPSET_DATA_COMPAT_BACKUP} + else + # If sets weren't cleanly saved, restore from backups + [ -d ${IPSET_DATA_DIR_BACKUP} ] && rm -rf ${IPSET_DATA_DIR} && mv -Tf ${IPSET_DATA_DIR_BACKUP} ${IPSET_DATA_DIR} + [ -f ${IPSET_DATA_COMPAT_BACKUP} ] && rm -f ${IPSET_DATA_COMPAT} && mv -Tf ${IPSET_DATA_COMPAT_BACKUP} ${IPSET_DATA_COMPAT} + fi + + if [ ! -d ${IPSET_DATA_DIR} ] && [ ! -f ${IPSET_DATA_COMPAT} ]; then + info "No existing configuration available, none loaded" + touch ${IPSET_RUN} + return + fi + + # Merge all sets into temporary file + merged="$(mktemp -q /tmp/ipset.XXXXXX)" + CLEAN_FILES="${CLEAN_FILES} ${merged}" + chmod 600 "${merged}" + set +f + if [ -d ${IPSET_DATA_DIR} ]; then + # Copy create commands from each saved set first, then the rest: + # list:set entries depend on other sets, so make sure they all + # get created first + for f in "${IPSET_DATA_DIR}"/*; do + [ "${f}" = "${IPSET_DATA_DIR}/*" ] && break + [ -f "${f}" ] || continue + grep '^c' "${f}" >> "${merged}" + done + for f in "${IPSET_DATA_DIR}"/*; do + [ "${f}" = "${IPSET_DATA_DIR}/*" ] && break + [ -f "${f}" ] || continue + grep -v '^c' "${f}" >> "${merged}" + done + fi + set -f + [ -f ${IPSET_DATA_COMPAT} ] && cat ${IPSET_DATA_COMPAT} >> "${merged}" + + # Drop sets that aren't in saved data, mark conflicts with existing sets + conflicts="" + IFS=" +" + for set in $(${IPSET_BIN} list -n -t); do + grep -q "^create ${set} " "${merged}" && conflicts="${conflicts}|${set}" && continue + + # We can't destroy the set if it's in use, flush it instead + if ! ${IPSET_BIN} destroy "${set}" 2>/dev/null; then + ${IPSET_BIN} flush "${set}" + fi + done + unset IFS + conflicts="${conflicts#|*}" + + # Common case: if we have no conflicts, just restore in one shot + if [ -z "${conflicts}" ]; then + if ! ipset_restore "${merged}"; then + err "Failed to restore configured sets" + exit 1 + fi + rm "${merged}" + CLEAN_FILES="${CLEAN_FILES%* ${merged}}" + touch ${IPSET_RUN} + return + fi + + # Find a salt for md5sum that makes names of saved sets unique + salt=0 + while true; do + unique=1 + IFS=" +" + for set in $(${IPSET_BIN} list -n -t); do + if grep -q "^create $(echo "${salt}${set}" | md5sum | head -c31) " "${merged}"; then + unique=0 + break + fi + done + unset IFS + [ ${unique} -eq 1 ] && break + salt=$((salt + 1)) + done + + # Add sets, mangling names for conflicting sets + mangled="$(mktemp -q /tmp/ipset.XXXXXX)" + CLEAN_FILES="${CLEAN_FILES} ${mangled}" + chmod 600 "${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 + fi + + rm "${mangled}" + CLEAN_FILES="${CLEAN_FILES%* ${mangled}}" + + # Swap and delete old sets + IFS='|' + for set in ${conflicts}; do + mangled="$(echo "${salt}${set}" | md5sum | head -c31)" + if ! ${IPSET_BIN} swap "${set}" "${mangled}" 2>/dev/null; then + # This fails if set types are different: try to destroy + # existing set + if ! ${IPSET_BIN} destroy "${set}" 2>/dev/null; then + # Conflicting set is in use, we can only warn + # and flush the existing set + err "Cannot load set \"${set}\", set with same name and conflicting type in use" + ${IPSET_BIN} flush "${set}" + ${IPSET_BIN} destroy "${mangled}" + else + ${IPSET_BIN} rename "${mangled}" "${set}" + fi + else + ${IPSET_BIN} destroy "${mangled}" + fi + done + unset IFS + + rm "${merged}" + CLEAN_FILES="${CLEAN_FILES%* ${merged}}" + touch ${IPSET_RUN} +} + +cleanup() { + ${IPSET_BIN} flush || err "Failed to flush sets" + + # Try to destroy all sets at once. This will fail if some are in use, + # destroy all the other ones in that case + ${IPSET_BIN} destroy 2>/dev/null && return + IFS=" +" + for set in $(${IPSET_BIN} list -n -t); do + if ! ${IPSET_BIN} destroy "${set}"; then + err "Failed to destroy set ${set}" + fi + done + unset IFS +} + +stop() { + [ -f ${IPSET_RUN} ] || { info "Not running"; return 0; } + [ "${IPSET_SAVE_ON_STOP}" = "yes" ] && { save || err "Failed to save sets"; } + + # Nothing to stop if the ip_set module is not loaded + lsmod | grep -q "^ip_set " || { info "Not running"; rm ${IPSET_RUN}; return 0; } + + # If the xt_set module is in use, then iptables is using ipset, so + # refuse to stop the service + if mod="$(lsmod | grep ^xt_set)"; then + if [ "$(echo "${mod}" | tr -s ' ' | cut -d' ' -f3)" != "0" ]; then + err "Current iptables configuration requires ipset" && return 1 + fi + fi + + cleanup + + rm ${IPSET_RUN} + return 0 +} + +lock +case "$1" in +start) + load + ;; +stop) + stop + ;; +reload) + cleanup + load + ;; +save) + save + ;; +*) + info "Usage: $0 {start|stop|reload|save}" + exit 1 +esac + +exit $? diff --git a/SPECS/ipset.spec b/SPECS/ipset.spec new file mode 100644 index 0000000..5731385 --- /dev/null +++ b/SPECS/ipset.spec @@ -0,0 +1,368 @@ +# service legacy actions +%define legacy_actions %{_libexecdir}/initscripts/legacy-actions + +Name: ipset +Version: 7.11 +Release: 8%{?dist} +Summary: Manage Linux IP sets + +License: GPLv2 +URL: http://ipset.netfilter.org/ +Source0: %{url}/%{name}-%{version}.tar.bz2 +Source1: %{name}.service +Source2: %{name}.start-stop +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 +BuildRequires: autoconf +BuildRequires: make +BuildRequires: libtool +BuildRequires: libtool-ltdl-devel + +# An explicit requirement is needed here, to avoid cases where a user would +# explicitly update only one of the two (e.g 'yum update ipset') +Requires: %{name}-libs%{?_isa} = %{version}-%{release} + +%description +IP sets are a framework inside the Linux kernel since version 2.4.x, which can +be administered by the ipset utility. Depending on the type, currently an IP +set may store IP addresses, (TCP/UDP) port numbers or IP addresses with MAC +addresses in a way, which ensures lightning speed when matching an entry +against a set. + +If you want to: + - store multiple IP addresses or port numbers and match against the collection + by iptables at one swoop; + - dynamically update iptables rules against IP addresses or ports without + performance penalty; + - express complex IP address and ports based rulesets with one single iptables + rule and benefit from the speed of IP sets +then ipset may be the proper tool for you. + + +%package libs +Summary: Shared library providing the IP sets functionality + +%description libs +This package contains the libraries which provide the IP sets funcionality. + + +%package devel +Summary: Development files for %{name} +Requires: %{name}-libs%{?_isa} == %{version}-%{release} +Requires: kernel-headers + +%description devel +This package contains the files required to develop software using the %{name} +libraries. + + +%package service +Summary: %{name} service for %{name}s +Requires: %{name} = %{version}-%{release} +BuildRequires: systemd +Requires: iptables-nft-services +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd +BuildArch: noarch + +%description service +This package provides the service %{name} that is split +out of the base package since it is not active by default. + + +%prep +%autosetup -p1 + + +%build +./autogen.sh +%configure --enable-static=no --with-kmod=no + +# Just to make absolutely sure we are not building the bundled kernel module +# I have to do it after the configure run unfortunately +rm -fr kernel + +# Prevent libtool from defining rpath +sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool +sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool + +%make_build + + +%install +%make_install +find %{buildroot} -name '*.la' -exec rm -f '{}' \; + +# install systemd unit file +install -d -m 755 %{buildroot}/%{_unitdir} +install -c -m 644 %{SOURCE1} %{buildroot}/%{_unitdir} + +# install supporting script +install -d -m 755 %{buildroot}%{_libexecdir}/%{name} +install -c -m 755 %{SOURCE2} %{buildroot}%{_libexecdir}/%{name} + +# install ipset-config +install -d -m 755 %{buildroot}%{_sysconfdir}/sysconfig +install -c -m 600 %{SOURCE3} %{buildroot}%{_sysconfdir}/sysconfig/%{name}-config + +# install legacy actions for service command +install -d %{buildroot}/%{legacy_actions}/ipset +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 + rmmod xt_set 2>/dev/null + [[ $? -ne 0 ]] && echo Current iptables configuration requires ipsets && exit 1 +fi + + +%ldconfig_scriptlets libs + + +%post service +%systemd_post %{name}.service + +%preun service +if [[ $1 -eq 0 && -n $(lsmod | grep "^xt_set ") ]]; then + rmmod xt_set 2>/dev/null + [[ $? -ne 0 ]] && echo Current iptables configuration requires ipsets && exit 1 +fi +%systemd_preun %{name}.service + +%postun service +%systemd_postun_with_restart %{name}.service + + +%files +%doc ChangeLog +%license COPYING +%{_mandir}/man8/%{name}.8.* +%{_mandir}/man8/%{name}-translate.8.* +%{_sbindir}/%{name} +%{_sbindir}/%{name}-translate + +%files libs +%license COPYING +%{_libdir}/lib%{name}.so.13* + +%files devel +%{_includedir}/lib%{name} +%{_libdir}/lib%{name}.so +%{_libdir}/pkgconfig/lib%{name}.pc +%{_mandir}/man3/libipset.3.* + +%files service +%{_unitdir}/%{name}.service +%dir %{_libexecdir}/%{name} +%config(noreplace) %attr(0600,root,root) %{_sysconfdir}/sysconfig/ipset-config +%ghost %config(noreplace) %attr(0600,root,root) %{_sysconfdir}/sysconfig/ipset +%attr(0755,root,root) %{_libexecdir}/%{name}/%{name}.start-stop +%dir %{legacy_actions}/ipset +%{legacy_actions}/ipset/save + + +%changelog +* Fri Apr 14 2023 MSVSphere Packaging Team - 7.11-8 +- Rebuilt for MSVSphere 9.2 beta + +* 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 + +* Mon Aug 09 2021 Mohan Boddu - 7.11-5 +- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags + Related: rhbz#1991688 + +* Fri Jul 02 2021 Phil Sutter - 7.11-4 +- Improve deprecation notice a bit + +* Fri Jun 25 2021 Phil Sutter - 7.11-3 +- Add deprecation notice to ipset.8 + +* Fri Apr 16 2021 Mohan Boddu - 7.11-2 +- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 + +* Mon Mar 01 2021 Nicolas Chauvet - 7.11-1 +- Update to 7.11 + +* Tue Jan 26 2021 Fedora Release Engineering - 7.10-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Mon Dec 21 2020 Nicolas Chauvet - 7.10-1 +- Update to 7.10 + +* Wed Dec 16 2020 Nicolas Chauvet - 7.9-1 +- Update to 7.9 + +* Tue Jul 28 2020 Fedora Release Engineering - 7.6-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Mon Feb 24 2020 Nicolas Chauvet - 7.6-1 +- Update to 7.6 + +* Wed Jan 29 2020 Fedora Release Engineering - 7.5-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Fri Jan 10 2020 Nicolas Chauvet - 7.5-1 +- Update to 7.5 + +* Mon Nov 04 2019 Eric Garver - 7.4-1 +- Update to 7.4 + +* Mon Aug 19 2019 Nicolas Chauvet - 7.3-1 +- Update to 7.3 + +* Thu Jul 25 2019 Fedora Release Engineering - 7.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Thu Jul 11 2019 Nicolas Chauvet - 7.2-1 +- Update to 7.2 + +* Fri Feb 01 2019 Fedora Release Engineering - 6.38-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Mon Aug 13 2018 Nicolas Chauvet - 6.38-1 +- Update to 6.38 +- Clean-up spec + +* Fri Jul 13 2018 Fedora Release Engineering - 6.35-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Mon Feb 12 2018 Eric Garver - 6.35-3 +- Patch for missing header file (RHBZ#1543596) + +* Wed Feb 07 2018 Fedora Release Engineering - 6.35-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Mon Jan 08 2018 Nicolas Chauvet - 6.35-1 +- Update to 6.35 + +* Mon Jul 31 2017 Nicolas Chauvet - 6.32-1 +- Update to 6.32 + +* Wed Jul 26 2017 Fedora Release Engineering - 6.29-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Apr 07 2017 Nicolas Chauvet - 6.29-3 +- Userspace needs kernel-headers - rhbz#1420864 + +* Fri Feb 10 2017 Fedora Release Engineering - 6.29-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Mon Apr 18 2016 Thomas Woerner - 6.29-1 +- New upstream version 6.29 (RHBZ#1317208) + - Suppress unnecessary stderr in command loop for resize and list + - Correction in comment test + - Support chroot buildroots (reported by Jan Engelhardt) + - Fix "configure" breakage due to pkg-config related changes + (reported by Jan Engelhardt) + - Support older pkg-config packages + - Add bash completion to the install routine (Mart Frauenlob) + - Fix misleading error message with comment extension + - Test added to check 0.0.0.0/0,iface to be matched in hash:net,iface type + - Fix link with libtool >= 2.4.4 (Olivier Blin) + +* Thu Feb 04 2016 Fedora Release Engineering - 6.27-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Tue Nov 10 2015 Thomas Woerner - 6.27-1 +- New upstream version 6.27 (RHBZ#1145913) + +* Sat Oct 10 2015 Haïkel Guémar - 6.26-1 +- Upstream 6.26 (RHBZ#1145913) + +* Wed Jun 17 2015 Fedora Release Engineering - 6.22-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Thu Sep 18 2014 Mathieu Bridon - 6.22-1 +- New upstream release. + +* Sat Aug 16 2014 Fedora Release Engineering - 6.21.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Sat Jun 07 2014 Fedora Release Engineering - 6.21.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Tue Mar 11 2014 Mathieu Bridon - 6.21.1-2 +- Remove runtime requirement on the kernel. + https://lists.fedoraproject.org/pipermail/devel/2014-March/196565.html + +* Tue Oct 29 2013 Mathieu Bridon - 6.20.1-1 +- New upstream release. + +* Tue Aug 27 2013 Quentin Armitage 6.19-2 +- Add service pkg - adds save and reload functionality on shutdown/startup +- Add requires dependency of ipset on matching ipset-libs + +* Thu Aug 15 2013 Mathieu Bridon - 6.19-1 +- New upstream release. + +* Sat Aug 03 2013 Fedora Release Engineering - 6.16.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Thu Feb 14 2013 Fedora Release Engineering - 6.16.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Wed Sep 26 2012 Mathieu Bridon - 6.16.1-1 +- New upstream release. +- Fix a requirement. + +* Wed Sep 26 2012 Mathieu Bridon - 6.14-1 +- New upstream release. +- Fix scriptlets, ldconfig is needed for the libs subpackage, not the main one. + +* Mon Jul 30 2012 Mathieu Bridon - 6.13-1 +- New upstream release. +- Split out the library in its own subpackage. + +* Thu Jul 19 2012 Fedora Release Engineering - 6.11-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Mon Feb 06 2012 Mathieu Bridon - 6.11-1 +- New upstream release. +- Removed our patch, it has been integrated upstream. As such, we also don't + need to re-run autoreconf any more. + +* Fri Jan 13 2012 Fedora Release Engineering - 6.9.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Fri Sep 16 2011 Mathieu Bridon - 6.9.1-2 +- Some fixes based on Pierre-Yves' review feedback. + +* Wed Sep 14 2011 Mathieu Bridon - 6.9.1-1 +- Initial packaging.