Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: Add new protocol attribute to IP addresses

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>

authored by

Jacques de Laval and committed by
Jakub Kicinski
47f0bd50 6e2e59ea

+41 -7
+1
include/linux/inetdevice.h
··· 150 150 __be32 ifa_broadcast; 151 151 unsigned char ifa_scope; 152 152 unsigned char ifa_prefixlen; 153 + unsigned char ifa_proto; 153 154 __u32 ifa_flags; 154 155 char ifa_label[IFNAMSIZ]; 155 156
+2
include/net/addrconf.h
··· 64 64 const struct in6_addr *pfx; 65 65 unsigned int plen; 66 66 67 + u8 ifa_proto; 68 + 67 69 const struct in6_addr *peer_pfx; 68 70 69 71 u32 rt_priority;
+2
include/net/if_inet6.h
··· 71 71 72 72 bool tokenized; 73 73 74 + u8 ifa_proto; 75 + 74 76 struct rcu_head rcu; 75 77 struct in6_addr peer_addr; 76 78 };
+8 -1
include/uapi/linux/if_addr.h
··· 33 33 IFA_CACHEINFO, 34 34 IFA_MULTICAST, 35 35 IFA_FLAGS, 36 - IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */ 36 + IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */ 37 37 IFA_TARGET_NETNSID, 38 + IFA_PROTO, /* u8, address protocol */ 38 39 __IFA_MAX, 39 40 }; 40 41 ··· 69 68 #define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) 70 69 #define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) 71 70 #endif 71 + 72 + /* ifa_proto */ 73 + #define IFAPROT_UNSPEC 0 74 + #define IFAPROT_KERNEL_LO 1 /* loopback */ 75 + #define IFAPROT_KERNEL_RA 2 /* set by kernel from router announcement */ 76 + #define IFAPROT_KERNEL_LL 3 /* link-local set by kernel */ 72 77 73 78 #endif
+7
net/ipv4/devinet.c
··· 104 104 [IFA_FLAGS] = { .type = NLA_U32 }, 105 105 [IFA_RT_PRIORITY] = { .type = NLA_U32 }, 106 106 [IFA_TARGET_NETNSID] = { .type = NLA_S32 }, 107 + [IFA_PROTO] = { .type = NLA_U8 }, 107 108 }; 108 109 109 110 struct inet_fill_args { ··· 890 889 if (tb[IFA_RT_PRIORITY]) 891 890 ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); 892 891 892 + if (tb[IFA_PROTO]) 893 + ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]); 894 + 893 895 if (tb[IFA_CACHEINFO]) { 894 896 struct ifa_cacheinfo *ci; 895 897 ··· 1629 1625 + nla_total_size(4) /* IFA_BROADCAST */ 1630 1626 + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ 1631 1627 + nla_total_size(4) /* IFA_FLAGS */ 1628 + + nla_total_size(1) /* IFA_PROTO */ 1632 1629 + nla_total_size(4) /* IFA_RT_PRIORITY */ 1633 1630 + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ 1634 1631 } ··· 1704 1699 nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1705 1700 (ifa->ifa_label[0] && 1706 1701 nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 1702 + (ifa->ifa_proto && 1703 + nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) || 1707 1704 nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) || 1708 1705 (ifa->ifa_rt_priority && 1709 1706 nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) ||
+21 -6
net/ipv6/addrconf.c
··· 1115 1115 ifa->prefix_len = cfg->plen; 1116 1116 ifa->rt_priority = cfg->rt_priority; 1117 1117 ifa->flags = cfg->ifa_flags; 1118 + ifa->ifa_proto = cfg->ifa_proto; 1118 1119 /* No need to add the TENTATIVE flag for addresses with NODAD */ 1119 1120 if (!(cfg->ifa_flags & IFA_F_NODAD)) 1120 1121 ifa->flags |= IFA_F_TENTATIVE; ··· 2594 2593 .valid_lft = valid_lft, 2595 2594 .preferred_lft = prefered_lft, 2596 2595 .scope = addr_type & IPV6_ADDR_SCOPE_MASK, 2596 + .ifa_proto = IFAPROT_KERNEL_RA 2597 2597 }; 2598 2598 2599 2599 #ifdef CONFIG_IPV6_OPTIMISTIC_DAD ··· 3079 3077 } 3080 3078 3081 3079 static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr, 3082 - int plen, int scope) 3080 + int plen, int scope, u8 proto) 3083 3081 { 3084 3082 struct inet6_ifaddr *ifp; 3085 3083 struct ifa6_config cfg = { ··· 3088 3086 .ifa_flags = IFA_F_PERMANENT, 3089 3087 .valid_lft = INFINITY_LIFE_TIME, 3090 3088 .preferred_lft = INFINITY_LIFE_TIME, 3091 - .scope = scope 3089 + .scope = scope, 3090 + .ifa_proto = proto 3092 3091 }; 3093 3092 3094 3093 ifp = ipv6_add_addr(idev, &cfg, true, NULL); ··· 3134 3131 } 3135 3132 3136 3133 if (addr.s6_addr32[3]) { 3137 - add_addr(idev, &addr, plen, scope); 3134 + add_addr(idev, &addr, plen, scope, IFAPROT_UNSPEC); 3138 3135 addrconf_prefix_route(&addr, plen, 0, idev->dev, 0, pflags, 3139 3136 GFP_KERNEL); 3140 3137 return; ··· 3157 3154 flag |= IFA_HOST; 3158 3155 } 3159 3156 3160 - add_addr(idev, &addr, plen, flag); 3157 + add_addr(idev, &addr, plen, flag, 3158 + IFAPROT_UNSPEC); 3161 3159 addrconf_prefix_route(&addr, plen, 0, idev->dev, 3162 3160 0, pflags, GFP_KERNEL); 3163 3161 } ··· 3181 3177 return; 3182 3178 } 3183 3179 3184 - add_addr(idev, &in6addr_loopback, 128, IFA_HOST); 3180 + add_addr(idev, &in6addr_loopback, 128, IFA_HOST, IFAPROT_KERNEL_LO); 3185 3181 } 3186 3182 3187 3183 void addrconf_add_linklocal(struct inet6_dev *idev, ··· 3193 3189 .ifa_flags = flags | IFA_F_PERMANENT, 3194 3190 .valid_lft = INFINITY_LIFE_TIME, 3195 3191 .preferred_lft = INFINITY_LIFE_TIME, 3196 - .scope = IFA_LINK 3192 + .scope = IFA_LINK, 3193 + .ifa_proto = IFAPROT_KERNEL_LL 3197 3194 }; 3198 3195 struct inet6_ifaddr *ifp; 3199 3196 ··· 4632 4627 [IFA_FLAGS] = { .len = sizeof(u32) }, 4633 4628 [IFA_RT_PRIORITY] = { .len = sizeof(u32) }, 4634 4629 [IFA_TARGET_NETNSID] = { .type = NLA_S32 }, 4630 + [IFA_PROTO] = { .type = NLA_U8 }, 4635 4631 }; 4636 4632 4637 4633 static int ··· 4758 4752 ifp->tstamp = jiffies; 4759 4753 ifp->valid_lft = cfg->valid_lft; 4760 4754 ifp->prefered_lft = cfg->preferred_lft; 4755 + ifp->ifa_proto = cfg->ifa_proto; 4761 4756 4762 4757 if (cfg->rt_priority && cfg->rt_priority != ifp->rt_priority) 4763 4758 ifp->rt_priority = cfg->rt_priority; ··· 4851 4844 cfg.plen = ifm->ifa_prefixlen; 4852 4845 if (tb[IFA_RT_PRIORITY]) 4853 4846 cfg.rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); 4847 + 4848 + if (tb[IFA_PROTO]) 4849 + cfg.ifa_proto = nla_get_u8(tb[IFA_PROTO]); 4854 4850 4855 4851 cfg.valid_lft = INFINITY_LIFE_TIME; 4856 4852 cfg.preferred_lft = INFINITY_LIFE_TIME; ··· 4958 4948 + nla_total_size(16) /* IFA_ADDRESS */ 4959 4949 + nla_total_size(sizeof(struct ifa_cacheinfo)) 4960 4950 + nla_total_size(4) /* IFA_FLAGS */ 4951 + + nla_total_size(1) /* IFA_PROTO */ 4961 4952 + nla_total_size(4) /* IFA_RT_PRIORITY */; 4962 4953 } 4963 4954 ··· 5034 5023 goto error; 5035 5024 5036 5025 if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0) 5026 + goto error; 5027 + 5028 + if (ifa->ifa_proto && 5029 + nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) 5037 5030 goto error; 5038 5031 5039 5032 nlmsg_end(skb, nlh);