From 67259089ba583d6cc4f938cf60c37617f681f6f7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jan=20R=C4=99korajski?= Date: Sun, 4 Jul 2021 13:26:47 +0200 Subject: [PATCH] - added fixes from upstream git to allow building with kernel 5.13, rel 2 --- git.patch | 1866 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ipset.spec | 20 +- 2 files changed, 1878 insertions(+), 8 deletions(-) create mode 100644 git.patch diff --git a/git.patch b/git.patch new file mode 100644 index 0000000..ddd3326 --- /dev/null +++ b/git.patch @@ -0,0 +1,1866 @@ +diff --git a/configure.ac b/configure.ac +index bd6116c..eb6c334 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]) +@@ -786,6 +787,56 @@ else + AC_SUBST(HAVE_NLA_POLICY_EXACT_LEN, undef) + fi + ++AC_MSG_CHECKING([kernel source for nfnl_msg_put() in nfnetlink.h]) ++if test -f $ksourcedir/include/linux/netfilter/nfnetlink.h && \ ++ $GREP -q 'nfnl_msg_put' $ksourcedir/include/linux/netfilter/nfnetlink.h; then ++ AC_MSG_RESULT(yes) ++ AC_SUBST(HAVE_NFNL_MSG_PUT, define) ++else ++ AC_MSG_RESULT(no) ++ AC_SUBST(HAVE_NFNL_MSG_PUT, undef) ++fi ++ ++AC_MSG_CHECKING([kernel source for struct nfnl_info in nfnl_callback function]) ++if test -f $ksourcedir/include/linux/netfilter/nfnetlink.h && \ ++ $AWK '/^struct nfnl_callback /,/^}/' $ksourcedir/include/linux/netfilter/nfnetlink.h | $GREP -q 'struct nfnl_info'; then ++ AC_MSG_RESULT(yes) ++ AC_SUBST(HAVE_NFNL_INFO_IN_NFNL_CALLBACK, define) ++else ++ AC_MSG_RESULT(no) ++ AC_SUBST(HAVE_NFNL_INFO_IN_NFNL_CALLBACK, undef) ++fi ++ ++AC_MSG_CHECKING([kernel source for enum nfnl_callback_type]) ++if test -f $ksourcedir/include/linux/netfilter/nfnetlink.h && \ ++ $GREP -q 'enum nfnl_callback_type ' $ksourcedir/include/linux/netfilter/nfnetlink.h; then ++ AC_MSG_RESULT(yes) ++ AC_SUBST(HAVE_NFNL_CALLBACK_TYPE, define) ++else ++ AC_MSG_RESULT(no) ++ AC_SUBST(HAVE_NFNL_CALLBACK_TYPE, undef) ++fi ++ ++AC_MSG_CHECKING([kernel source of handling -EAGAIN in nfnetlink_unicast]) ++if test -f $ksourcedir/net/netfilter/nfnetlink.c && \ ++ $AWK '/nfnetlink_unicast\(/,/^}/' $ksourcedir/net/netfilter/nfnetlink.c | $GREP -q 'err == -EAGAIN'; then ++ AC_MSG_RESULT(yes) ++ AC_SUBST(HAVE_EAGAIN_IN_NFNETLINK_UNICAST, define) ++else ++ AC_MSG_RESULT(no) ++ AC_SUBST(HAVE_EAGAIN_IN_NFNETLINK_UNICAST, undef) ++fi ++ ++AC_MSG_CHECKING([kernel source for nlmsg_unicast which returns zero in case of success]) ++if test -f $ksourcedir/include/net/netlink.h && \ ++ $AWK '/static inline int nlmsg_unicast\(/,/^}/' $ksourcedir/include/net/netlink.h | $GREP -q 'err > 0'; then ++ AC_MSG_RESULT(yes) ++ AC_SUBST(HAVE_NLMSG_UNICAST, define) ++else ++ AC_MSG_RESULT(no) ++ AC_SUBST(HAVE_NLMSG_UNICAST, undef) ++fi ++ + AC_MSG_CHECKING([kernel source for kvzalloc() in mm.h]) + if test -f $ksourcedir/include/linux/mm.h && \ + $GREP -q 'static inline void \*kvzalloc(' $ksourcedir/include/linux/mm.h; then +diff --git a/include/libipset/Makefile.am b/include/libipset/Makefile.am +index c7f7b2b..2c04029 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 0000000..6569768 +--- /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/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in b/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in +index 96a4cf4..4d2c446 100644 +--- a/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in ++++ b/kernel/include/linux/netfilter/ipset/ip_set_compat.h.in +@@ -62,6 +62,11 @@ + #@HAVE_KVZALLOC@ HAVE_KVZALLOC + #@HAVE_GFP_KERNEL_ACCOUNT@ HAVE_GFP_KERNEL_ACCOUNT + #@HAVE_NLA_STRSCPY@ HAVE_NLA_STRSCPY ++#@HAVE_NFNL_MSG_PUT@ HAVE_NFNL_MSG_PUT ++#@HAVE_NFNL_INFO_IN_NFNL_CALLBACK@ HAVE_NFNL_INFO_IN_NFNL_CALLBACK ++#@HAVE_NFNL_CALLBACK_TYPE@ HAVE_NFNL_CALLBACK_TYPE ++#@HAVE_EAGAIN_IN_NFNETLINK_UNICAST@ HAVE_EAGAIN_IN_NFNETLINK_UNICAST ++#@HAVE_NLMSG_UNICAST@ HAVE_NLMSG_UNICAST + + #ifdef HAVE_EXPORT_SYMBOL_GPL_IN_MODULE_H + #include +@@ -348,18 +353,44 @@ static inline int nla_put_in6_addr(struct sk_buff *skb, int attrtype, + } + #endif + +-#ifdef HAVE_PASSING_EXTENDED_ACK_TO_CALLBACKS +-#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e) fn(net, nl, skb, nlh, cda, e) +-#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e) fn(net, nl, skb, ad, nlh, cda, e) +-#define IPSET_SOCK_NET(net, ctnl) net ++#ifdef HAVE_NFNL_INFO_IN_NFNL_CALLBACK ++#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e, i) fn(skb, i, cda) ++#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e, i) fn(net, nl, skb, ad, nlh, cda, i) ++#define IPSET_SOCK_NET(n, ctnl, i) (i)->net ++#define INFO_NLH(i, n) (i)->nlh ++#define INFO_NET(i, n) (i)->net ++#define INFO_SK(i, n) (i)->sk ++#define CALL_AD(net, ctnl, skb, set, tb, adt, flags, l) call_ad(net, ctnl, skb, set, tb, adt, flags, l) ++#elif defined(HAVE_PASSING_EXTENDED_ACK_TO_CALLBACKS) ++#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e, i) fn(net, nl, skb, nlh, cda, e) ++#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e, i) fn(net, nl, skb, ad, nlh, cda, e) ++#define IPSET_SOCK_NET(net, ctnl, i) net ++#define INFO_NLH(i, n) n ++#define INFO_NET(i, n) n ++#define INFO_SK(i, n) n ++#define CALL_AD(net, ctnl, skb, set, tb, adt, flags, l) call_ad(net, ctnl, skb, set, tb, adt, flags, l) + #elif defined(HAVE_NET_IN_NFNL_CALLBACK_FN) +-#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e) fn(net, nl, skb, nlh, cda) +-#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e) fn(net, nl, skb, ad, nlh, cda) +-#define IPSET_SOCK_NET(net, ctnl) net ++#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e, i) fn(net, nl, skb, nlh, cda) ++#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e, i) fn(net, nl, skb, ad, nlh, cda) ++#define IPSET_SOCK_NET(net, ctnl, i) net ++#define INFO_NLH(i, n) n ++#define INFO_NET(i, n) n ++#define INFO_SK(i, n) n ++#define CALL_AD(net, ctnl, skb, set, tb, adt, flags, l) call_ad(net, ctnl, skb, set, tb, adt, flags, l) + #else +-#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e) fn(nl, skb, nlh, cda) +-#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e) fn(nl, skb, ad, nlh, cda) +-#define IPSET_SOCK_NET(net, ctnl) sock_net(ctnl) ++#define IPSET_CBFN(fn, net, nl, skb, nlh, cda, e, i) fn(nl, skb, nlh, cda) ++#define IPSET_CBFN_AD(fn, net, nl, skb, ad, nlh, cda, e, i) fn(nl, skb, ad, nlh, cda) ++#define IPSET_SOCK_NET(net, ctnl, i) sock_net(ctnl) ++#define INFO_NLH(i, n) n ++#define INFO_NET(i, n) n ++#define INFO_SK(i, n) n ++#define CALL_AD(net, ctnl, skb, set, tb, adt, flags, l) call_ad(ctnl, skb, set, tb, adt, flags, l) ++#endif ++ ++#ifdef HAVE_NFNL_CALLBACK_TYPE ++#define SET_NFNL_CALLBACK_TYPE(t) .type = t, ++#else ++#define SET_NFNL_CALLBACK_TYPE(t) + #endif + + #ifndef HAVE_TC_SKB_PROTOCOL +@@ -406,6 +437,36 @@ static inline u16 nfnl_msg_type(u8 subsys, u8 msg_type) + } + #endif + ++#ifndef HAVE_NFNL_MSG_PUT ++#include ++static inline void nfnl_fill_hdr(struct nlmsghdr *nlh, u8 family, u8 version, ++ __be16 res_id) ++{ ++ struct nfgenmsg *nfmsg; ++ ++ nfmsg = nlmsg_data(nlh); ++ nfmsg->nfgen_family = family; ++ nfmsg->version = version; ++ nfmsg->res_id = res_id; ++} ++ ++static inline struct nlmsghdr *nfnl_msg_put(struct sk_buff *skb, u32 portid, ++ u32 seq, int type, int flags, ++ u8 family, u8 version, ++ __be16 res_id) ++{ ++ struct nlmsghdr *nlh; ++ ++ nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg), flags); ++ if (!nlh) ++ return NULL; ++ ++ nfnl_fill_hdr(nlh, family, version, res_id); ++ ++ return nlh; ++} ++#endif ++ + #ifdef HAVE_NETLINK_EXTENDED_ACK + #define NETLINK_ACK(in_skb, nlh, err, extack) netlink_ack(in_skb, nlh, err, extack) + #else +@@ -459,6 +520,23 @@ static inline ssize_t strscpy(char * dest, const char * src, size_t count) + #define nla_strscpy nla_strlcpy + #endif + ++#if !defined(HAVE_EAGAIN_IN_NFNETLINK_UNICAST) || !defined(HAVE_NLMSG_UNICAST) ++#define NFNETLINK_UNICAST(cntl, skb, net, portid) ipset_nfnetlink_unicast(cntl, skb, portid) ++static inline int ipset_nfnetlink_unicast(struct sock *ctnl, struct sk_buff *skb, u32 portid) ++{ ++ int err = netlink_unicast(ctnl, skb, portid, MSG_DONTWAIT); ++ ++ if (err > 0) ++ err = 0; ++ if (err == -EAGAIN) ++ err = -ENOBUFS; ++ ++ return err; ++} ++#else ++#define NFNETLINK_UNICAST(cntl, skb, net, portid) nfnetlink_unicast(skb, net, portid) ++#endif ++ + #ifndef smp_mb__before_atomic + #define smp_mb__before_atomic() smp_mb() + #define smp_mb__after_atomic() smp_mb() +diff --git a/kernel/net/netfilter/ipset/ip_set_core.c b/kernel/net/netfilter/ipset/ip_set_core.c +index 63a7955..0fdafb7 100644 +--- a/kernel/net/netfilter/ipset/ip_set_core.c ++++ b/kernel/net/netfilter/ipset/ip_set_core.c +@@ -964,20 +964,9 @@ static struct nlmsghdr * + start_msg(struct sk_buff *skb, u32 portid, u32 seq, unsigned int flags, + enum ipset_cmd cmd) + { +- struct nlmsghdr *nlh; +- struct nfgenmsg *nfmsg; +- +- nlh = nlmsg_put(skb, portid, seq, nfnl_msg_type(NFNL_SUBSYS_IPSET, cmd), +- sizeof(*nfmsg), flags); +- if (!nlh) +- return NULL; +- +- nfmsg = nlmsg_data(nlh); +- nfmsg->nfgen_family = NFPROTO_IPV4; +- nfmsg->version = NFNETLINK_V0; +- nfmsg->res_id = 0; +- +- return nlh; ++ return nfnl_msg_put(skb, portid, seq, ++ nfnl_msg_type(NFNL_SUBSYS_IPSET, cmd), flags, ++ NFPROTO_IPV4, NFNETLINK_V0, 0); + } + + /* Create a set */ +@@ -1047,7 +1036,8 @@ static int + IPSET_CBFN(ip_set_none, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { + return -EOPNOTSUPP; + } +@@ -1056,16 +1046,17 @@ static int + IPSET_CBFN(ip_set_create, struct net *n, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { +- struct net *net = IPSET_SOCK_NET(n, ctnl); ++ struct net *net = IPSET_SOCK_NET(n, ctnl, info); + struct ip_set_net *inst = ip_set_pernet(net); + struct ip_set *set, *clash = NULL; + ip_set_id_t index = IPSET_INVALID_ID; + struct nlattr *tb[IPSET_ATTR_CREATE_MAX + 1] = {}; + const char *name, *typename; + u8 family, revision; +- u32 flags = flag_exist(nlh); ++ u32 flags = flag_exist(INFO_NLH(info, nlh)); + int ret = 0; + + if (unlikely(protocol_min_failed(attr) || +@@ -1116,7 +1107,7 @@ IPSET_CBFN(ip_set_create, struct net *n, struct sock *ctnl, + /* Set create flags depending on the type revision */ + set->flags |= set->type->create_flags[revision]; + +- ret = set->type->create(net, set, tb, flags); ++ ret = set->type->create(INFO_NET(info, net), set, tb, flags); + if (ret != 0) + goto put_out; + +@@ -1202,9 +1193,10 @@ static int + IPSET_CBFN(ip_set_destroy, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { +- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl)); ++ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info)); + struct ip_set *s; + ip_set_id_t i; + int ret = 0; +@@ -1246,7 +1238,7 @@ IPSET_CBFN(ip_set_destroy, struct net *net, struct sock *ctnl, + /* Modified by ip_set_destroy() only, which is serialized */ + inst->is_destroyed = false; + } else { +- u32 flags = flag_exist(nlh); ++ u32 flags = flag_exist(INFO_NLH(info, nlh)); + s = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]), + &i); + if (!s) { +@@ -1284,9 +1276,10 @@ static int + IPSET_CBFN(ip_set_flush, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { +- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl)); ++ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info)); + struct ip_set *s; + ip_set_id_t i; + +@@ -1325,9 +1318,10 @@ static int + IPSET_CBFN(ip_set_rename, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { +- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl)); ++ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info)); + struct ip_set *set, *s; + const char *name2; + ip_set_id_t i; +@@ -1376,9 +1370,10 @@ static int + IPSET_CBFN(ip_set_swap, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { +- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl)); ++ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info)); + struct ip_set *from, *to; + ip_set_id_t from_id, to_id; + char from_name[IPSET_MAXNAMELEN]; +@@ -1706,7 +1701,8 @@ static int + IPSET_CBFN(ip_set_dump, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { + if (unlikely(protocol_min_failed(attr))) + return -IPSET_ERR_PROTOCOL; +@@ -1728,7 +1724,7 @@ IPSET_CBFN(ip_set_dump, struct net *net, struct sock *ctnl, + .dump = ip_set_dump_do, + .done = ip_set_dump_done, + }; +- return netlink_dump_start(ctnl, skb, nlh, &c); ++ return netlink_dump_start(INFO_SK(info, ctnl), skb, INFO_NLH(info, nlh), &c); + } + #endif + } +@@ -1745,8 +1741,8 @@ static const struct nla_policy ip_set_adt_policy[IPSET_ATTR_CMD_MAX + 1] = { + }; + + static int +-call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set, +- struct nlattr *tb[], enum ipset_adt adt, ++CALL_AD(struct net *net, struct sock *ctnl, struct sk_buff *skb, ++ struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, + u32 flags, bool use_lineno) + { + int ret; +@@ -1798,8 +1794,7 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set, + + *errline = lineno; + +- netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb), +- MSG_DONTWAIT); ++ NFNETLINK_UNICAST(ctnl, skb2, net, NETLINK_PORTID(skb)); + /* Signal netlink not to send its ACK/errmsg. */ + return -EINTR; + } +@@ -1807,19 +1802,18 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set, + return ret; + } + +-static int +-IPSET_CBFN_AD(ip_set_ad, struct net *net, struct sock *ctnl, +- struct sk_buff *skb, +- enum ipset_adt adt, +- const struct nlmsghdr *nlh, +- const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) +-{ +- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl)); ++static int IPSET_CBFN_AD(ip_set_ad, struct net *net, struct sock *ctnl, ++ struct sk_buff *skb, ++ enum ipset_adt adt, ++ const struct nlmsghdr *nlh, ++ const struct nlattr * const attr[], ++ struct netlink_ext_ack *extack, const struct nfnl_info *info) ++{ ++ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info)); + struct ip_set *set; + struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {}; + const struct nlattr *nla; +- u32 flags = flag_exist(nlh); ++ u32 flags = flag_exist(INFO_NLH(info, nlh)); + bool use_lineno; + int ret = 0; + +@@ -1844,7 +1838,7 @@ IPSET_CBFN_AD(ip_set_ad, struct net *net, struct sock *ctnl, + attr[IPSET_ATTR_DATA], + set->type->adt_policy, NULL)) + return -IPSET_ERR_PROTOCOL; +- ret = call_ad(ctnl, skb, set, tb, adt, flags, ++ ret = CALL_AD(net, ctnl, skb, set, tb, adt, flags, + use_lineno); + } else { + int nla_rem; +@@ -1855,7 +1849,7 @@ IPSET_CBFN_AD(ip_set_ad, struct net *net, struct sock *ctnl, + NLA_PARSE_NESTED(tb, IPSET_ATTR_ADT_MAX, nla, + set->type->adt_policy, NULL)) + return -IPSET_ERR_PROTOCOL; +- ret = call_ad(ctnl, skb, set, tb, adt, ++ ret = CALL_AD(net, ctnl, skb, set, tb, adt, + flags, use_lineno); + if (ret < 0) + return ret; +@@ -1868,20 +1862,22 @@ static int + IPSET_CBFN(ip_set_uadd, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { +- return IPSET_CBFN_AD(ip_set_ad, net, ctnl, skb, +- IPSET_ADD, nlh, attr, extack); ++ return IPSET_CBFN_AD(ip_set_ad, INFO_NET(info, net), INFO_SK(info, ctnl), skb, ++ IPSET_ADD, INFO_NLH(info, nlh), attr, extack, info); + } + + static int + IPSET_CBFN(ip_set_udel, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { +- return IPSET_CBFN_AD(ip_set_ad, net, ctnl, skb, +- IPSET_DEL, nlh, attr, extack); ++ return IPSET_CBFN_AD(ip_set_ad, INFO_NET(info, net), INFO_SK(info, ctnl), skb, ++ IPSET_DEL, INFO_NLH(info, nlh), attr, extack, info); + } + + static int +@@ -1889,9 +1885,10 @@ IPSET_CBFN(ip_set_utest, struct net *net, struct sock *ctnl, + struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { +- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl)); ++ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info)); + struct ip_set *set; + struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {}; + int ret = 0; +@@ -1927,13 +1924,13 @@ static int + IPSET_CBFN(ip_set_header, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { +- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl)); ++ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info)); + const struct ip_set *set; + struct sk_buff *skb2; + struct nlmsghdr *nlh2; +- int ret = 0; + + if (unlikely(protocol_min_failed(attr) || + !attr[IPSET_ATTR_SETNAME])) +@@ -1947,7 +1944,7 @@ IPSET_CBFN(ip_set_header, struct net *net, struct sock *ctnl, + if (!skb2) + return -ENOMEM; + +- nlh2 = start_msg(skb2, NETLINK_PORTID(skb), nlh->nlmsg_seq, 0, ++ nlh2 = start_msg(skb2, NETLINK_PORTID(skb), INFO_NLH(info, nlh)->nlmsg_seq, 0, + IPSET_CMD_HEADER); + if (!nlh2) + goto nlmsg_failure; +@@ -1959,11 +1956,7 @@ IPSET_CBFN(ip_set_header, struct net *net, struct sock *ctnl, + goto nla_put_failure; + nlmsg_end(skb2, nlh2); + +- ret = netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb), MSG_DONTWAIT); +- if (ret < 0) +- return ret; +- +- return 0; ++ return NFNETLINK_UNICAST(INFO_SK(info, ctnl), skb2, INFO_NET(info, net), NETLINK_PORTID(skb)); + + nla_put_failure: + nlmsg_cancel(skb2, nlh2); +@@ -1985,7 +1978,8 @@ static int + IPSET_CBFN(ip_set_type, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { + struct sk_buff *skb2; + struct nlmsghdr *nlh2; +@@ -2008,7 +2002,7 @@ IPSET_CBFN(ip_set_type, struct net *net, struct sock *ctnl, + if (!skb2) + return -ENOMEM; + +- nlh2 = start_msg(skb2, NETLINK_PORTID(skb), nlh->nlmsg_seq, 0, ++ nlh2 = start_msg(skb2, NETLINK_PORTID(skb), INFO_NLH(info, nlh)->nlmsg_seq, 0, + IPSET_CMD_TYPE); + if (!nlh2) + goto nlmsg_failure; +@@ -2021,11 +2015,7 @@ IPSET_CBFN(ip_set_type, struct net *net, struct sock *ctnl, + nlmsg_end(skb2, nlh2); + + pr_debug("Send TYPE, nlmsg_len: %u\n", nlh2->nlmsg_len); +- ret = netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb), MSG_DONTWAIT); +- if (ret < 0) +- return ret; +- +- return 0; ++ return NFNETLINK_UNICAST(INFO_SK(info, ctnl), skb2, INFO_NET(info, net), NETLINK_PORTID(skb)); + + nla_put_failure: + nlmsg_cancel(skb2, nlh2); +@@ -2045,11 +2035,11 @@ static int + IPSET_CBFN(ip_set_protocol, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { + struct sk_buff *skb2; + struct nlmsghdr *nlh2; +- int ret = 0; + + if (unlikely(!attr[IPSET_ATTR_PROTOCOL])) + return -IPSET_ERR_PROTOCOL; +@@ -2058,7 +2048,7 @@ IPSET_CBFN(ip_set_protocol, struct net *net, struct sock *ctnl, + if (!skb2) + return -ENOMEM; + +- nlh2 = start_msg(skb2, NETLINK_PORTID(skb), nlh->nlmsg_seq, 0, ++ nlh2 = start_msg(skb2, NETLINK_PORTID(skb), INFO_NLH(info, nlh)->nlmsg_seq, 0, + IPSET_CMD_PROTOCOL); + if (!nlh2) + goto nlmsg_failure; +@@ -2068,11 +2058,7 @@ IPSET_CBFN(ip_set_protocol, struct net *net, struct sock *ctnl, + goto nla_put_failure; + nlmsg_end(skb2, nlh2); + +- ret = netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb), MSG_DONTWAIT); +- if (ret < 0) +- return ret; +- +- return 0; ++ return NFNETLINK_UNICAST(INFO_SK(info, ctnl), skb2, INFO_NET(info, net), NETLINK_PORTID(skb)); + + nla_put_failure: + nlmsg_cancel(skb2, nlh2); +@@ -2087,14 +2073,14 @@ static int + IPSET_CBFN(ip_set_byname, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { +- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl)); ++ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info)); + struct sk_buff *skb2; + struct nlmsghdr *nlh2; + ip_set_id_t id = IPSET_INVALID_ID; + const struct ip_set *set; +- int ret = 0; + + if (unlikely(protocol_failed(attr) || + !attr[IPSET_ATTR_SETNAME])) +@@ -2108,7 +2094,7 @@ IPSET_CBFN(ip_set_byname, struct net *net, struct sock *ctnl, + if (!skb2) + return -ENOMEM; + +- nlh2 = start_msg(skb2, NETLINK_PORTID(skb), nlh->nlmsg_seq, 0, ++ nlh2 = start_msg(skb2, NETLINK_PORTID(skb), INFO_NLH(info, nlh)->nlmsg_seq, 0, + IPSET_CMD_GET_BYNAME); + if (!nlh2) + goto nlmsg_failure; +@@ -2118,11 +2104,7 @@ IPSET_CBFN(ip_set_byname, struct net *net, struct sock *ctnl, + goto nla_put_failure; + nlmsg_end(skb2, nlh2); + +- ret = netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb), MSG_DONTWAIT); +- if (ret < 0) +- return ret; +- +- return 0; ++ return NFNETLINK_UNICAST(INFO_SK(info, ctnl), skb2, INFO_NET(info, net), NETLINK_PORTID(skb)); + + nla_put_failure: + nlmsg_cancel(skb2, nlh2); +@@ -2140,14 +2122,14 @@ static int + IPSET_CBFN(ip_set_byindex, struct net *net, struct sock *ctnl, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const attr[], +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack, ++ const struct nfnl_info *info) + { +- struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl)); ++ struct ip_set_net *inst = ip_set_pernet(IPSET_SOCK_NET(net, ctnl, info)); + struct sk_buff *skb2; + struct nlmsghdr *nlh2; + ip_set_id_t id = IPSET_INVALID_ID; + const struct ip_set *set; +- int ret = 0; + + if (unlikely(protocol_failed(attr) || + !attr[IPSET_ATTR_INDEX])) +@@ -2164,7 +2146,7 @@ IPSET_CBFN(ip_set_byindex, struct net *net, struct sock *ctnl, + if (!skb2) + return -ENOMEM; + +- nlh2 = start_msg(skb2, NETLINK_PORTID(skb), nlh->nlmsg_seq, 0, ++ nlh2 = start_msg(skb2, NETLINK_PORTID(skb), INFO_NLH(info, nlh)->nlmsg_seq, 0, + IPSET_CMD_GET_BYINDEX); + if (!nlh2) + goto nlmsg_failure; +@@ -2173,11 +2155,7 @@ IPSET_CBFN(ip_set_byindex, struct net *net, struct sock *ctnl, + goto nla_put_failure; + nlmsg_end(skb2, nlh2); + +- ret = netlink_unicast(ctnl, skb2, NETLINK_PORTID(skb), MSG_DONTWAIT); +- if (ret < 0) +- return ret; +- +- return 0; ++ return NFNETLINK_UNICAST(INFO_SK(info, ctnl), skb2, INFO_NET(info, net), NETLINK_PORTID(skb)); + + nla_put_failure: + nlmsg_cancel(skb2, nlh2); +@@ -2189,80 +2167,96 @@ nlmsg_failure: + static const struct nfnl_callback ip_set_netlink_subsys_cb[IPSET_MSG_MAX] = { + [IPSET_CMD_NONE] = { + .call = ip_set_none, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + }, + [IPSET_CMD_CREATE] = { + .call = ip_set_create, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_create_policy, + }, + [IPSET_CMD_DESTROY] = { + .call = ip_set_destroy, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname_policy, + }, + [IPSET_CMD_FLUSH] = { + .call = ip_set_flush, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname_policy, + }, + [IPSET_CMD_RENAME] = { + .call = ip_set_rename, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname2_policy, + }, + [IPSET_CMD_SWAP] = { + .call = ip_set_swap, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname2_policy, + }, + [IPSET_CMD_LIST] = { + .call = ip_set_dump, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_dump_policy, + }, + [IPSET_CMD_SAVE] = { + .call = ip_set_dump, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname_policy, + }, + [IPSET_CMD_ADD] = { + .call = ip_set_uadd, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_adt_policy, + }, + [IPSET_CMD_DEL] = { + .call = ip_set_udel, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_adt_policy, + }, + [IPSET_CMD_TEST] = { + .call = ip_set_utest, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_adt_policy, + }, + [IPSET_CMD_HEADER] = { + .call = ip_set_header, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname_policy, + }, + [IPSET_CMD_TYPE] = { + .call = ip_set_type, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_type_policy, + }, + [IPSET_CMD_PROTOCOL] = { + .call = ip_set_protocol, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_protocol_policy, + }, + [IPSET_CMD_GET_BYNAME] = { + .call = ip_set_byname, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_setname_policy, + }, + [IPSET_CMD_GET_BYINDEX] = { + .call = ip_set_byindex, ++ SET_NFNL_CALLBACK_TYPE(NFNL_CB_MUTEX) + .attr_count = IPSET_ATTR_CMD_MAX, + .policy = ip_set_index_policy, + }, +diff --git a/lib/ipset.c b/lib/ipset.c +index 6729919..73e67db 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,20 +936,33 @@ static const char *cmd_prefix[] = { + [IPSET_TEST] = "test SETNAME", + }; + +-/* Workhorses */ ++static const struct ipset_xlate_set * ++ipset_xlate_set_get(struct ipset *ipset, const char *name) ++{ ++ const struct ipset_xlate_set *set; + +-/** +- * 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[]) ++ 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[]) + { + int ret = 0; + enum ipset_cmd cmd = IPSET_CMD_NONE; +@@ -1243,7 +1269,7 @@ ipset_parse_argv(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: +@@ -1253,7 +1279,12 @@ ipset_parse_argv(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); + +@@ -1280,6 +1311,37 @@ 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; ++ ++ 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 */ +@@ -1455,6 +1517,9 @@ ipset_init(void) + return NULL; + } + ipset_custom_printf(ipset, NULL, NULL, NULL, NULL); ++ ++ INIT_LIST_HEAD(&ipset->xlate_sets); ++ + return ipset; + } + +@@ -1469,6 +1534,8 @@ ipset_init(void) + int + ipset_fini(struct ipset *ipset) + { ++ struct ipset_xlate_set *xlate_set, *next; ++ + assert(ipset); + + if (ipset->session) +@@ -1477,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/lib/parse.c b/lib/parse.c +index f3f2d11..9cba252 100644 +--- a/lib/parse.c ++++ b/lib/parse.c +@@ -41,6 +41,9 @@ + #define syntax_err(fmt, args...) \ + ipset_err(session, "Syntax error: " fmt , ## args) + ++#define syntax_err_ll(errtype, fmt, args...) \ ++ ipset_session_report(session, errtype, "Syntax error: " fmt , ## args) ++ + static char * + ipset_strchr(const char *str, const char *sep) + { +@@ -87,7 +90,8 @@ string_to_number_ll(struct ipset_session *session, + const char *str, + unsigned long long min, + unsigned long long max, +- unsigned long long *ret) ++ unsigned long long *ret, ++ enum ipset_err_type errtype) + { + unsigned long long number = 0; + char *end; +@@ -104,13 +108,13 @@ string_to_number_ll(struct ipset_session *session, + errno = ERANGE; + } + if (errno == ERANGE && max) +- return syntax_err("'%s' is out of range %llu-%llu", +- str, min, max); ++ return syntax_err_ll(errtype, "'%s' is out of range %llu-%llu", ++ str, min, max); + else if (errno == ERANGE) +- return syntax_err("'%s' is out of range %llu-%llu", +- str, min, ULLONG_MAX); ++ return syntax_err_ll(errtype, "'%s' is out of range %llu-%llu", ++ str, min, ULLONG_MAX); + else +- return syntax_err("'%s' is invalid as number", str); ++ return syntax_err_ll(errtype, "'%s' is invalid as number", str); + } + + static int +@@ -120,7 +124,7 @@ string_to_u8(struct ipset_session *session, + int err; + unsigned long long num = 0; + +- err = string_to_number_ll(session, str, 0, 255, &num); ++ err = string_to_number_ll(session, str, 0, 255, &num, IPSET_ERROR); + *ret = num; + + return err; +@@ -141,12 +145,13 @@ string_to_cidr(struct ipset_session *session, + + static int + string_to_u16(struct ipset_session *session, +- const char *str, uint16_t *ret) ++ const char *str, uint16_t *ret, ++ enum ipset_err_type errtype) + { + int err; + unsigned long long num = 0; + +- err = string_to_number_ll(session, str, 0, USHRT_MAX, &num); ++ err = string_to_number_ll(session, str, 0, USHRT_MAX, &num, errtype); + *ret = num; + + return err; +@@ -159,7 +164,8 @@ string_to_u32(struct ipset_session *session, + int err; + unsigned long long num = 0; + +- err = string_to_number_ll(session, str, 0, UINT_MAX, &num); ++ err = string_to_number_ll(session, str, 0, UINT_MAX, &num, ++ IPSET_ERROR); + *ret = num; + + return err; +@@ -319,7 +325,7 @@ ipset_parse_port(struct ipset_session *session, + assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO); + assert(str); + +- if (string_to_u16(session, str, &port) == 0) { ++ if (string_to_u16(session, str, &port, IPSET_WARNING) == 0) { + return ipset_session_data_set(session, opt, &port); + } + /* Error is stored as warning in session report */ +@@ -1335,7 +1341,8 @@ ipset_parse_timeout(struct ipset_session *session, + assert(opt == IPSET_OPT_TIMEOUT); + assert(str); + +- err = string_to_number_ll(session, str, 0, (UINT_MAX>>1)/1000, &llnum); ++ err = string_to_number_ll(session, str, 0, (UINT_MAX>>1)/1000, &llnum, ++ IPSET_ERROR); + if (err == 0) { + /* Timeout is expected to be 32bits wide, so we have + to convert it here */ +@@ -1579,7 +1586,8 @@ ipset_parse_uint64(struct ipset_session *session, + assert(session); + assert(str); + +- err = string_to_number_ll(session, str, 0, ULLONG_MAX - 1, &value); ++ err = string_to_number_ll(session, str, 0, ULLONG_MAX - 1, &value, ++ IPSET_ERROR); + if (err) + return err; + +@@ -1623,7 +1631,7 @@ ipset_parse_uint16(struct ipset_session *session, + assert(session); + assert(str); + +- err = string_to_u16(session, str, &value); ++ err = string_to_u16(session, str, &value, IPSET_ERROR); + if (err == 0) + return ipset_session_data_set(session, opt, &value); + +diff --git a/src/Makefile.am b/src/Makefile.am +index 438fcec..95dea07 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 0000000..bb4e737 +--- /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.8 b/src/ipset.8 +index 97cece9..269b9b5 100644 +--- a/src/ipset.8 ++++ b/src/ipset.8 +@@ -333,7 +333,7 @@ are hex without 0x prefix. + .IP + ipset create foo hash:ip skbinfo + .IP +-ipset add foo skbmark 0x1111/0xff00ffff skbprio 1:10 skbqueue 10 ++ipset add foo 192.168.0.1 skbmark 0x1111/0xff00ffff skbprio 1:10 skbqueue 10 + .PP + .SS hashsize + This parameter is valid for the \fBcreate\fR command of all \fBhash\fR type sets. +diff --git a/src/ipset.c b/src/ipset.c +index ee36a06..6d42b60 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); + +diff --git a/tests/xlate/runtest.sh b/tests/xlate/runtest.sh +new file mode 100755 +index 0000000..a2a02c0 +--- /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 0000000..b1e7d28 +--- /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 0000000..96eba3b +--- /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 } diff --git a/ipset.spec b/ipset.spec index 2014b9c..40dc9af 100644 --- a/ipset.spec +++ b/ipset.spec @@ -22,7 +22,7 @@ exit 1 %define _enable_debug_packages 0 %endif -%define rel 1 +%define rel 2 %define pname ipset Summary: IP sets utility Summary(pl.UTF-8): Narzędzie do zarządzania zbiorami IP @@ -35,11 +35,12 @@ Group: Networking/Admin Source0: http://ipset.netfilter.org/%{pname}-%{version}.tar.bz2 # Source0-md5: 7c17aca72bcf852f5bc95582aaa60408 Source1: %{pname}.init +Patch0: git.patch URL: http://ipset.netfilter.org/ BuildRequires: autoconf >= 2.63 BuildRequires: automake -BuildRequires: libmnl-devel >= 1 BuildRequires: libltdl-devel >= 2:2.0 +BuildRequires: libmnl-devel >= 1 BuildRequires: libtool >= 2:2.0 %{?with_userspace:BuildRequires: linux-libc-headers >= 7:2.6.38.6} BuildRequires: pkgconfig @@ -60,11 +61,10 @@ speed when matching an entry against a set. %description -l pl.UTF-8 Zbiory IP to szkielet w jądrze Linuksa 2.4.x i 2.6.x, którym można -administrować przy użyciu narzędzia ipset. W zależności od -rodzaju aktualnie zbiór IP może przechowywać adresy IP, numery -portów (TCP/UDP) lub adresy IP z adresami MAC - w sposób -zapewniający maksymalną szybkość przy dopasowywaniu elementu do -zbioru. +administrować przy użyciu narzędzia ipset. W zależności od rodzaju +aktualnie zbiór IP może przechowywać adresy IP, numery portów +(TCP/UDP) lub adresy IP z adresami MAC - w sposób zapewniający +maksymalną szybkość przy dopasowywaniu elementu do zbioru. %package devel Summary: Header files for ipset interface @@ -185,6 +185,7 @@ done\ %prep %setup -q -n %{pname}-%{version} +%patch0 -p1 %build %{__aclocal} @@ -240,10 +241,12 @@ fi %defattr(644,root,root,755) %doc ChangeLog ChangeLog.ippool README UPGRADE %attr(755,root,root) %{_sbindir}/ipset +%attr(755,root,root) %{_sbindir}/ipset-translate %attr(755,root,root) %{_libdir}/libipset.so.*.*.* %attr(755,root,root) %ghost %{_libdir}/libipset.so.13 -%{_mandir}/man3/libipset.3* %{_mandir}/man8/ipset.8* +%{_mandir}/man8/ipset-translate.8* + %files devel %defattr(644,root,root,755) @@ -251,6 +254,7 @@ fi %{_libdir}/libipset.la %{_includedir}/libipset %{_pkgconfigdir}/libipset.pc +%{_mandir}/man3/libipset.3* %files static %defattr(644,root,root,755) -- 2.44.0