#include <linux/types.h>
 
-/* The protocol version */
-#define IPSET_PROTOCOL         6
+/* The protocol versions */
+#define IPSET_PROTOCOL         7
+#define IPSET_PROTOCOL_MIN     6
 
 /* The max length of strings including NUL: set and type identifiers */
 #define IPSET_MAXNAMELEN       32
        IPSET_CMD_TEST,         /* 11: Test an element in a set */
        IPSET_CMD_HEADER,       /* 12: Get set header data only */
        IPSET_CMD_TYPE,         /* 13: Get set type */
+       IPSET_CMD_GET_BYNAME,   /* 14: Get set index by name */
+       IPSET_CMD_GET_BYINDEX,  /* 15: Get set name by index */
        IPSET_MSG_MAX,          /* Netlink message commands */
 
        /* Commands in userspace: */
-       IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */
-       IPSET_CMD_HELP,         /* 15: Get help */
-       IPSET_CMD_VERSION,      /* 16: Get program version */
-       IPSET_CMD_QUIT,         /* 17: Quit from interactive mode */
+       IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 16: Enter restore mode */
+       IPSET_CMD_HELP,         /* 17: Get help */
+       IPSET_CMD_VERSION,      /* 18: Get program version */
+       IPSET_CMD_QUIT,         /* 19: Quit from interactive mode */
 
        IPSET_CMD_MAX,
 
-       IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: Commit buffered commands */
+       IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 20: Commit buffered commands */
 };
 
 /* Attributes at command level */
        IPSET_ATTR_LINENO,      /* 9: Restore lineno */
        IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */
        IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */
+       IPSET_ATTR_INDEX,       /* 11: Kernel index of set */
        __IPSET_ATTR_CMD_MAX,
 };
 #define IPSET_ATTR_CMD_MAX     (__IPSET_ATTR_CMD_MAX - 1)
 
 /* Sets are identified by an index in kernel space. Tweak with ip_set_id_t
  * and IPSET_INVALID_ID if you want to increase the max number of sets.
+ * Also, IPSET_ATTR_INDEX must be changed.
  */
 typedef __u16 ip_set_id_t;
 
 
  * The commands are serialized by the nfnl mutex.
  */
 
+static inline u8 protocol(const struct nlattr * const tb[])
+{
+       return nla_get_u8(tb[IPSET_ATTR_PROTOCOL]);
+}
+
 static inline bool
 protocol_failed(const struct nlattr * const tb[])
 {
-       return !tb[IPSET_ATTR_PROTOCOL] ||
-              nla_get_u8(tb[IPSET_ATTR_PROTOCOL]) != IPSET_PROTOCOL;
+       return !tb[IPSET_ATTR_PROTOCOL] || protocol(tb) != IPSET_PROTOCOL;
+}
+
+static inline bool
+protocol_min_failed(const struct nlattr * const tb[])
+{
+       return !tb[IPSET_ATTR_PROTOCOL] || protocol(tb) < IPSET_PROTOCOL_MIN;
 }
 
 static inline u32
        u32 flags = flag_exist(nlh);
        int ret = 0;
 
-       if (unlikely(protocol_failed(attr) ||
+       if (unlikely(protocol_min_failed(attr) ||
                     !attr[IPSET_ATTR_SETNAME] ||
                     !attr[IPSET_ATTR_TYPENAME] ||
                     !attr[IPSET_ATTR_REVISION] ||
        ip_set_id_t i;
        int ret = 0;
 
-       if (unlikely(protocol_failed(attr)))
+       if (unlikely(protocol_min_failed(attr)))
                return -IPSET_ERR_PROTOCOL;
 
        /* Must wait for flush to be really finished in list:set */
        struct ip_set *s;
        ip_set_id_t i;
 
-       if (unlikely(protocol_failed(attr)))
+       if (unlikely(protocol_min_failed(attr)))
                return -IPSET_ERR_PROTOCOL;
 
        if (!attr[IPSET_ATTR_SETNAME]) {
        ip_set_id_t i;
        int ret = 0;
 
-       if (unlikely(protocol_failed(attr) ||
+       if (unlikely(protocol_min_failed(attr) ||
                     !attr[IPSET_ATTR_SETNAME] ||
                     !attr[IPSET_ATTR_SETNAME2]))
                return -IPSET_ERR_PROTOCOL;
        ip_set_id_t from_id, to_id;
        char from_name[IPSET_MAXNAMELEN];
 
-       if (unlikely(protocol_failed(attr) ||
+       if (unlikely(protocol_min_failed(attr) ||
                     !attr[IPSET_ATTR_SETNAME] ||
                     !attr[IPSET_ATTR_SETNAME2]))
                return -IPSET_ERR_PROTOCOL;
        nla_parse(cda, IPSET_ATTR_CMD_MAX, attr, nlh->nlmsg_len - min_len,
                  ip_set_setname_policy, NULL);
 
+       cb->args[IPSET_CB_PROTO] = nla_get_u8(cda[IPSET_ATTR_PROTOCOL]);
        if (cda[IPSET_ATTR_SETNAME]) {
                struct ip_set *set;
 
                        ret = -EMSGSIZE;
                        goto release_refcount;
                }
-               if (nla_put_u8(skb, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL) ||
+               if (nla_put_u8(skb, IPSET_ATTR_PROTOCOL,
+                              cb->args[IPSET_CB_PROTO]) ||
                    nla_put_string(skb, IPSET_ATTR_SETNAME, set->name))
                        goto nla_put_failure;
                if (dump_flags & IPSET_FLAG_LIST_SETNAME)
                            nla_put_u8(skb, IPSET_ATTR_REVISION,
                                       set->revision))
                                goto nla_put_failure;
+                       if (cb->args[IPSET_CB_PROTO] > IPSET_PROTOCOL_MIN &&
+                           nla_put_net16(skb, IPSET_ATTR_INDEX, htons(index)))
+                               goto nla_put_failure;
                        ret = set->variant->head(set, skb);
                        if (ret < 0)
                                goto release_refcount;
                       const struct nlattr * const attr[],
                       struct netlink_ext_ack *extack)
 {
-       if (unlikely(protocol_failed(attr)))
+       if (unlikely(protocol_min_failed(attr)))
                return -IPSET_ERR_PROTOCOL;
 
        {
        bool use_lineno;
        int ret = 0;
 
-       if (unlikely(protocol_failed(attr) ||
+       if (unlikely(protocol_min_failed(attr) ||
                     !attr[IPSET_ATTR_SETNAME] ||
                     !((attr[IPSET_ATTR_DATA] != NULL) ^
                       (attr[IPSET_ATTR_ADT] != NULL)) ||
        bool use_lineno;
        int ret = 0;
 
-       if (unlikely(protocol_failed(attr) ||
+       if (unlikely(protocol_min_failed(attr) ||
                     !attr[IPSET_ATTR_SETNAME] ||
                     !((attr[IPSET_ATTR_DATA] != NULL) ^
                       (attr[IPSET_ATTR_ADT] != NULL)) ||
        struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {};
        int ret = 0;
 
-       if (unlikely(protocol_failed(attr) ||
+       if (unlikely(protocol_min_failed(attr) ||
                     !attr[IPSET_ATTR_SETNAME] ||
                     !attr[IPSET_ATTR_DATA] ||
                     !flag_nested(attr[IPSET_ATTR_DATA])))
        struct nlmsghdr *nlh2;
        int ret = 0;
 
-       if (unlikely(protocol_failed(attr) ||
+       if (unlikely(protocol_min_failed(attr) ||
                     !attr[IPSET_ATTR_SETNAME]))
                return -IPSET_ERR_PROTOCOL;
 
                         IPSET_CMD_HEADER);
        if (!nlh2)
                goto nlmsg_failure;
-       if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL) ||
+       if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, protocol(attr)) ||
            nla_put_string(skb2, IPSET_ATTR_SETNAME, set->name) ||
            nla_put_string(skb2, IPSET_ATTR_TYPENAME, set->type->name) ||
            nla_put_u8(skb2, IPSET_ATTR_FAMILY, set->family) ||
        const char *typename;
        int ret = 0;
 
-       if (unlikely(protocol_failed(attr) ||
+       if (unlikely(protocol_min_failed(attr) ||
                     !attr[IPSET_ATTR_TYPENAME] ||
                     !attr[IPSET_ATTR_FAMILY]))
                return -IPSET_ERR_PROTOCOL;
                         IPSET_CMD_TYPE);
        if (!nlh2)
                goto nlmsg_failure;
-       if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL) ||
+       if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, protocol(attr)) ||
            nla_put_string(skb2, IPSET_ATTR_TYPENAME, typename) ||
            nla_put_u8(skb2, IPSET_ATTR_FAMILY, family) ||
            nla_put_u8(skb2, IPSET_ATTR_REVISION, max) ||
                goto nlmsg_failure;
        if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL))
                goto nla_put_failure;
+       if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL_MIN, IPSET_PROTOCOL_MIN))
+               goto nla_put_failure;
+       nlmsg_end(skb2, nlh2);
+
+       ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+
+nla_put_failure:
+       nlmsg_cancel(skb2, nlh2);
+nlmsg_failure:
+       kfree_skb(skb2);
+       return -EMSGSIZE;
+}
+
+/* Get set by name or index, from userspace */
+
+static int 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 ip_set_net *inst = ip_set_pernet(net);
+       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]))
+               return -IPSET_ERR_PROTOCOL;
+
+       set = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]), &id);
+       if (id == IPSET_INVALID_ID)
+               return -ENOENT;
+
+       skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!skb2)
+               return -ENOMEM;
+
+       nlh2 = start_msg(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
+                        IPSET_CMD_GET_BYNAME);
+       if (!nlh2)
+               goto nlmsg_failure;
+       if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, protocol(attr)) ||
+           nla_put_u8(skb2, IPSET_ATTR_FAMILY, set->family) ||
+           nla_put_net16(skb2, IPSET_ATTR_INDEX, htons(id)))
+               goto nla_put_failure;
+       nlmsg_end(skb2, nlh2);
+
+       ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+
+nla_put_failure:
+       nlmsg_cancel(skb2, nlh2);
+nlmsg_failure:
+       kfree_skb(skb2);
+       return -EMSGSIZE;
+}
+
+static const struct nla_policy ip_set_index_policy[IPSET_ATTR_CMD_MAX + 1] = {
+       [IPSET_ATTR_PROTOCOL]   = { .type = NLA_U8 },
+       [IPSET_ATTR_INDEX]      = { .type = NLA_U16 },
+};
+
+static int 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 ip_set_net *inst = ip_set_pernet(net);
+       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]))
+               return -IPSET_ERR_PROTOCOL;
+
+       id = ip_set_get_h16(attr[IPSET_ATTR_INDEX]);
+       if (id >= inst->ip_set_max)
+               return -ENOENT;
+       set = ip_set(inst, id);
+       if (set == NULL)
+               return -ENOENT;
+
+       skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!skb2)
+               return -ENOMEM;
+
+       nlh2 = start_msg(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
+                        IPSET_CMD_GET_BYINDEX);
+       if (!nlh2)
+               goto nlmsg_failure;
+       if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, protocol(attr)) ||
+           nla_put_string(skb, IPSET_ATTR_SETNAME, set->name))
+               goto nla_put_failure;
        nlmsg_end(skb2, nlh2);
 
        ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT);
                .attr_count     = IPSET_ATTR_CMD_MAX,
                .policy         = ip_set_protocol_policy,
        },
+       [IPSET_CMD_GET_BYNAME]  = {
+               .call           = ip_set_byname,
+               .attr_count     = IPSET_ATTR_CMD_MAX,
+               .policy         = ip_set_setname_policy,
+       },
+       [IPSET_CMD_GET_BYINDEX] = {
+               .call           = ip_set_byindex,
+               .attr_count     = IPSET_ATTR_CMD_MAX,
+               .policy         = ip_set_index_policy,
+       },
 };
 
 static struct nfnetlink_subsystem ip_set_netlink_subsys __read_mostly = {
                        goto done;
                }
 
-               if (req_version->version != IPSET_PROTOCOL) {
+               if (req_version->version < IPSET_PROTOCOL_MIN) {
                        ret = -EPROTO;
                        goto done;
                }