]> git.pld-linux.org Git - packages/ipset.git/commitdiff
- added fixes from upstream git to allow building with kernel 5.13, rel 2 auto/th/ipset-7.11-2
authorJan Rękorajski <baggins@pld-linux.org>
Sun, 4 Jul 2021 11:26:47 +0000 (13:26 +0200)
committerJan Rękorajski <baggins@pld-linux.org>
Sun, 4 Jul 2021 11:26:47 +0000 (13:26 +0200)
git.patch [new file with mode: 0644]
ipset.spec

diff --git a/git.patch b/git.patch
new file mode 100644 (file)
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 <linux/module.h>
+@@ -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 <linux/netfilter/nfnetlink.h>
++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 <stdio.h>                            /* printf */
+ #include <stdlib.h>                           /* exit */
+ #include <string.h>                           /* str* */
++#include <inttypes.h>                         /* PRIu64 */
+ #include <config.h>
+@@ -28,6 +29,7 @@
+ #include <libipset/utils.h>                   /* STREQ */
+ #include <libipset/ipset.h>                   /* prototypes */
+ #include <libipset/ip_set_compiler.h>         /* compiler attributes */
++#include <libipset/list_sort.h>                       /* 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 <pablo@netfilter.org>
++.\"
++.\" %%%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
++.\" <http://www.gnu.org/licenses/>.
++.\" %%%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
++<pablo@netfilter.org>.
++
++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.h>                   /* assert */
+ #include <stdio.h>                    /* fprintf */
+ #include <stdlib.h>                   /* exit */
++#include <string.h>                   /* strcmp */
+ #include <config.h>
+ #include <libipset/ipset.h>           /* ipset library */
++#include <libipset/xlate.h>           /* 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 }
index 2014b9c466adc156025e5c549c9e79cfc929f168..40dc9af7c51ed121515bbd2cbb45584985998ebb 100644 (file)
@@ -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)
This page took 0.140726 seconds and 4 git commands to generate.