net: Add new protocol attribute to IP addresses
authorJacques de Laval <Jacques.De.Laval@westermo.com>
Thu, 17 Feb 2022 15:02:02 +0000 (16:02 +0100)
committerJakub Kicinski <kuba@kernel.org>
Sat, 19 Feb 2022 05:20:06 +0000 (21:20 -0800)
This patch adds a new protocol attribute to IPv4 and IPv6 addresses.
Inspiration was taken from the protocol attribute of routes. User space
applications like iproute2 can set/get the protocol with the Netlink API.

The attribute is stored as an 8-bit unsigned integer.

The protocol attribute is set by kernel for these categories:

- IPv4 and IPv6 loopback addresses
- IPv6 addresses generated from router announcements
- IPv6 link local addresses

User space may pass custom protocols, not defined by the kernel.

Grouping addresses on their origin is useful in scenarios where you want
to distinguish between addresses based on who added them, e.g. kernel
vs. user space.

Tagging addresses with a string label is an existing feature that could be
used as a solution. Unfortunately the max length of a label is
15 characters, and for compatibility reasons the label must be prefixed
with the name of the device followed by a colon. Since device names also
have a max length of 15 characters, only -1 characters is guaranteed to be
available for any origin tag, which is not that much.

A reference implementation of user space setting and getting protocols
is available for iproute2:

https://github.com/westermo/iproute2/commit/9a6ea18bd79f47f293e5edc7780f315ea42ff540

Signed-off-by: Jacques de Laval <Jacques.De.Laval@westermo.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Link: https://lore.kernel.org/r/20220217150202.80802-1-Jacques.De.Laval@westermo.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/linux/inetdevice.h
include/net/addrconf.h
include/net/if_inet6.h
include/uapi/linux/if_addr.h
net/ipv4/devinet.c
net/ipv6/addrconf.c

index 674aeead626089c2740ef844a7c8c8b799ee79dd..ead323243e7b04a95539c4211353ebc4d5665df2 100644 (file)
@@ -150,6 +150,7 @@ struct in_ifaddr {
        __be32                  ifa_broadcast;
        unsigned char           ifa_scope;
        unsigned char           ifa_prefixlen;
+       unsigned char           ifa_proto;
        __u32                   ifa_flags;
        char                    ifa_label[IFNAMSIZ];
 
index 59940e230b782f804a56eb81760eb8ae9e1cc41c..f7506f08e505a97321e27134ad2d7ab0b9c6f7d6 100644 (file)
@@ -64,6 +64,8 @@ struct ifa6_config {
        const struct in6_addr   *pfx;
        unsigned int            plen;
 
+       u8                      ifa_proto;
+
        const struct in6_addr   *peer_pfx;
 
        u32                     rt_priority;
index f026cf08a8e86c54ea5d9f1abddd5f0e3caf402b..4cfdef6ca4f64c68f4a529ffaabf1086d0bdd442 100644 (file)
@@ -71,6 +71,8 @@ struct inet6_ifaddr {
 
        bool                    tokenized;
 
+       u8                      ifa_proto;
+
        struct rcu_head         rcu;
        struct in6_addr         peer_addr;
 };
index dfcf3ce0097fdf934107ab58f46eb9ba5014c6c4..1c392dd95a5eebe006479ebec48bd3a460ea0068 100644 (file)
@@ -33,8 +33,9 @@ enum {
        IFA_CACHEINFO,
        IFA_MULTICAST,
        IFA_FLAGS,
-       IFA_RT_PRIORITY,  /* u32, priority/metric for prefix route */
+       IFA_RT_PRIORITY,        /* u32, priority/metric for prefix route */
        IFA_TARGET_NETNSID,
+       IFA_PROTO,              /* u8, address protocol */
        __IFA_MAX,
 };
 
@@ -69,4 +70,10 @@ struct ifa_cacheinfo {
 #define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
 #endif
 
+/* ifa_proto */
+#define IFAPROT_UNSPEC         0
+#define IFAPROT_KERNEL_LO      1       /* loopback */
+#define IFAPROT_KERNEL_RA      2       /* set by kernel from router announcement */
+#define IFAPROT_KERNEL_LL      3       /* link-local set by kernel */
+
 #endif
index fba2bffd65f7967f390dcaf5183994af1ae5493b..53a6b14dc50a5bd6a2d940b4ed20943826c71b2b 100644 (file)
@@ -104,6 +104,7 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
        [IFA_FLAGS]             = { .type = NLA_U32 },
        [IFA_RT_PRIORITY]       = { .type = NLA_U32 },
        [IFA_TARGET_NETNSID]    = { .type = NLA_S32 },
+       [IFA_PROTO]             = { .type = NLA_U8 },
 };
 
 struct inet_fill_args {
@@ -889,6 +890,9 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
        if (tb[IFA_RT_PRIORITY])
                ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
 
+       if (tb[IFA_PROTO])
+               ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]);
+
        if (tb[IFA_CACHEINFO]) {
                struct ifa_cacheinfo *ci;
 
@@ -1625,6 +1629,7 @@ static size_t inet_nlmsg_size(void)
               + nla_total_size(4) /* IFA_BROADCAST */
               + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
               + nla_total_size(4)  /* IFA_FLAGS */
+              + nla_total_size(1)  /* IFA_PROTO */
               + nla_total_size(4)  /* IFA_RT_PRIORITY */
               + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */
 }
@@ -1699,6 +1704,8 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
             nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
            (ifa->ifa_label[0] &&
             nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
+           (ifa->ifa_proto &&
+            nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) ||
            nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
            (ifa->ifa_rt_priority &&
             nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
index 2f307da17f21209b2499909693bb5f288419f0a9..85bab3a35709bbf410f72d614e825e80d9509df3 100644 (file)
@@ -1115,6 +1115,7 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg,
        ifa->prefix_len = cfg->plen;
        ifa->rt_priority = cfg->rt_priority;
        ifa->flags = cfg->ifa_flags;
+       ifa->ifa_proto = cfg->ifa_proto;
        /* No need to add the TENTATIVE flag for addresses with NODAD */
        if (!(cfg->ifa_flags & IFA_F_NODAD))
                ifa->flags |= IFA_F_TENTATIVE;
@@ -2593,6 +2594,7 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
                        .valid_lft = valid_lft,
                        .preferred_lft = prefered_lft,
                        .scope = addr_type & IPV6_ADDR_SCOPE_MASK,
+                       .ifa_proto = IFAPROT_KERNEL_RA
                };
 
 #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
@@ -3077,7 +3079,7 @@ int addrconf_del_ifaddr(struct net *net, void __user *arg)
 }
 
 static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
-                    int plen, int scope)
+                    int plen, int scope, u8 proto)
 {
        struct inet6_ifaddr *ifp;
        struct ifa6_config cfg = {
@@ -3086,7 +3088,8 @@ static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
                .ifa_flags = IFA_F_PERMANENT,
                .valid_lft = INFINITY_LIFE_TIME,
                .preferred_lft = INFINITY_LIFE_TIME,
-               .scope = scope
+               .scope = scope,
+               .ifa_proto = proto
        };
 
        ifp = ipv6_add_addr(idev, &cfg, true, NULL);
@@ -3131,7 +3134,7 @@ static void add_v4_addrs(struct inet6_dev *idev)
        }
 
        if (addr.s6_addr32[3]) {
-               add_addr(idev, &addr, plen, scope);
+               add_addr(idev, &addr, plen, scope, IFAPROT_UNSPEC);
                addrconf_prefix_route(&addr, plen, 0, idev->dev, 0, pflags,
                                      GFP_KERNEL);
                return;
@@ -3154,7 +3157,8 @@ static void add_v4_addrs(struct inet6_dev *idev)
                                        flag |= IFA_HOST;
                                }
 
-                               add_addr(idev, &addr, plen, flag);
+                               add_addr(idev, &addr, plen, flag,
+                                        IFAPROT_UNSPEC);
                                addrconf_prefix_route(&addr, plen, 0, idev->dev,
                                                      0, pflags, GFP_KERNEL);
                        }
@@ -3177,7 +3181,7 @@ static void init_loopback(struct net_device *dev)
                return;
        }
 
-       add_addr(idev, &in6addr_loopback, 128, IFA_HOST);
+       add_addr(idev, &in6addr_loopback, 128, IFA_HOST, IFAPROT_KERNEL_LO);
 }
 
 void addrconf_add_linklocal(struct inet6_dev *idev,
@@ -3189,7 +3193,8 @@ void addrconf_add_linklocal(struct inet6_dev *idev,
                .ifa_flags = flags | IFA_F_PERMANENT,
                .valid_lft = INFINITY_LIFE_TIME,
                .preferred_lft = INFINITY_LIFE_TIME,
-               .scope = IFA_LINK
+               .scope = IFA_LINK,
+               .ifa_proto = IFAPROT_KERNEL_LL
        };
        struct inet6_ifaddr *ifp;
 
@@ -4627,6 +4632,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {
        [IFA_FLAGS]             = { .len = sizeof(u32) },
        [IFA_RT_PRIORITY]       = { .len = sizeof(u32) },
        [IFA_TARGET_NETNSID]    = { .type = NLA_S32 },
+       [IFA_PROTO]             = { .type = NLA_U8 },
 };
 
 static int
@@ -4752,6 +4758,7 @@ static int inet6_addr_modify(struct net *net, struct inet6_ifaddr *ifp,
        ifp->tstamp = jiffies;
        ifp->valid_lft = cfg->valid_lft;
        ifp->prefered_lft = cfg->preferred_lft;
+       ifp->ifa_proto = cfg->ifa_proto;
 
        if (cfg->rt_priority && cfg->rt_priority != ifp->rt_priority)
                ifp->rt_priority = cfg->rt_priority;
@@ -4845,6 +4852,9 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
        if (tb[IFA_RT_PRIORITY])
                cfg.rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
 
+       if (tb[IFA_PROTO])
+               cfg.ifa_proto = nla_get_u8(tb[IFA_PROTO]);
+
        cfg.valid_lft = INFINITY_LIFE_TIME;
        cfg.preferred_lft = INFINITY_LIFE_TIME;
 
@@ -4948,6 +4958,7 @@ static inline int inet6_ifaddr_msgsize(void)
               + nla_total_size(16) /* IFA_ADDRESS */
               + nla_total_size(sizeof(struct ifa_cacheinfo))
               + nla_total_size(4)  /* IFA_FLAGS */
+              + nla_total_size(1)  /* IFA_PROTO */
               + nla_total_size(4)  /* IFA_RT_PRIORITY */;
 }
 
@@ -5025,6 +5036,10 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
        if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0)
                goto error;
 
+       if (ifa->ifa_proto &&
+           nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto))
+               goto error;
+
        nlmsg_end(skb, nlh);
        return 0;