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

ipv6: sr: add code base for control plane support of SR-IPv6

This patch adds the necessary hooks and structures to provide support
for SR-IPv6 control plane, essentially the Generic Netlink commands
that will be used for userspace control over the Segment Routing
kernel structures.

The genetlink commands provide control over two different structures:
tunnel source and HMAC data. The tunnel source is the source address
that will be used by default when encapsulating packets into an
outer IPv6 header + SRH. If the tunnel source is set to :: then an
address of the outgoing interface will be selected as the source.

The HMAC commands currently just return ENOTSUPP and will be implemented
in a future patch.

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

David Lebrun and committed by
David S. Miller
915d7e5e 1ababeba

+278 -2
+6
include/linux/seg6_genl.h
··· 1 + #ifndef _LINUX_SEG6_GENL_H 2 + #define _LINUX_SEG6_GENL_H 3 + 4 + #include <uapi/linux/seg6_genl.h> 5 + 6 + #endif
+1
include/net/netns/ipv6.h
··· 85 85 #endif 86 86 atomic_t dev_addr_genid; 87 87 atomic_t fib6_sernum; 88 + struct seg6_pernet_data *seg6_data; 88 89 }; 89 90 90 91 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
+16
include/net/seg6.h
··· 14 14 #ifndef _NET_SEG6_H 15 15 #define _NET_SEG6_H 16 16 17 + #include <linux/net.h> 18 + #include <linux/ipv6.h> 19 + 17 20 static inline void update_csum_diff4(struct sk_buff *skb, __be32 from, 18 21 __be32 to) 19 22 { ··· 35 32 36 33 skb->csum = ~csum_partial((char *)diff, sizeof(diff), ~skb->csum); 37 34 } 35 + 36 + struct seg6_pernet_data { 37 + struct mutex lock; 38 + struct in6_addr __rcu *tun_src; 39 + }; 40 + 41 + static inline struct seg6_pernet_data *seg6_pernet(struct net *net) 42 + { 43 + return net->ipv6.seg6_data; 44 + } 45 + 46 + extern int seg6_init(void); 47 + extern void seg6_exit(void); 38 48 39 49 #endif
+32
include/uapi/linux/seg6_genl.h
··· 1 + #ifndef _UAPI_LINUX_SEG6_GENL_H 2 + #define _UAPI_LINUX_SEG6_GENL_H 3 + 4 + #define SEG6_GENL_NAME "SEG6" 5 + #define SEG6_GENL_VERSION 0x1 6 + 7 + enum { 8 + SEG6_ATTR_UNSPEC, 9 + SEG6_ATTR_DST, 10 + SEG6_ATTR_DSTLEN, 11 + SEG6_ATTR_HMACKEYID, 12 + SEG6_ATTR_SECRET, 13 + SEG6_ATTR_SECRETLEN, 14 + SEG6_ATTR_ALGID, 15 + SEG6_ATTR_HMACINFO, 16 + __SEG6_ATTR_MAX, 17 + }; 18 + 19 + #define SEG6_ATTR_MAX (__SEG6_ATTR_MAX - 1) 20 + 21 + enum { 22 + SEG6_CMD_UNSPEC, 23 + SEG6_CMD_SETHMAC, 24 + SEG6_CMD_DUMPHMAC, 25 + SEG6_CMD_SET_TUNSRC, 26 + SEG6_CMD_GET_TUNSRC, 27 + __SEG6_CMD_MAX, 28 + }; 29 + 30 + #define SEG6_CMD_MAX (__SEG6_CMD_MAX - 1) 31 + 32 + #endif
+1 -1
net/ipv6/Makefile
··· 9 9 route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ 10 10 raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ 11 11 exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \ 12 - udp_offload.o 12 + udp_offload.o seg6.o 13 13 14 14 ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o 15 15
+8 -1
net/ipv6/af_inet6.c
··· 61 61 #include <net/ip6_tunnel.h> 62 62 #endif 63 63 #include <net/calipso.h> 64 + #include <net/seg6.h> 64 65 65 66 #include <asm/uaccess.h> 66 67 #include <linux/mroute6.h> ··· 992 991 if (err) 993 992 goto calipso_fail; 994 993 994 + err = seg6_init(); 995 + if (err) 996 + goto seg6_fail; 997 + 995 998 #ifdef CONFIG_SYSCTL 996 999 err = ipv6_sysctl_register(); 997 1000 if (err) ··· 1006 1001 1007 1002 #ifdef CONFIG_SYSCTL 1008 1003 sysctl_fail: 1009 - calipso_exit(); 1004 + seg6_exit(); 1010 1005 #endif 1006 + seg6_fail: 1007 + calipso_exit(); 1011 1008 calipso_fail: 1012 1009 pingv6_exit(); 1013 1010 pingv6_fail:
+214
net/ipv6/seg6.c
··· 1 + /* 2 + * SR-IPv6 implementation 3 + * 4 + * Author: 5 + * David Lebrun <david.lebrun@uclouvain.be> 6 + * 7 + * 8 + * This program is free software; you can redistribute it and/or 9 + * modify it under the terms of the GNU General Public License 10 + * as published by the Free Software Foundation; either version 11 + * 2 of the License, or (at your option) any later version. 12 + */ 13 + 14 + #include <linux/errno.h> 15 + #include <linux/types.h> 16 + #include <linux/socket.h> 17 + #include <linux/net.h> 18 + #include <linux/in6.h> 19 + #include <linux/slab.h> 20 + 21 + #include <net/ipv6.h> 22 + #include <net/protocol.h> 23 + 24 + #include <net/seg6.h> 25 + #include <net/genetlink.h> 26 + #include <linux/seg6.h> 27 + #include <linux/seg6_genl.h> 28 + 29 + static struct genl_family seg6_genl_family; 30 + 31 + static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = { 32 + [SEG6_ATTR_DST] = { .type = NLA_BINARY, 33 + .len = sizeof(struct in6_addr) }, 34 + [SEG6_ATTR_DSTLEN] = { .type = NLA_S32, }, 35 + [SEG6_ATTR_HMACKEYID] = { .type = NLA_U32, }, 36 + [SEG6_ATTR_SECRET] = { .type = NLA_BINARY, }, 37 + [SEG6_ATTR_SECRETLEN] = { .type = NLA_U8, }, 38 + [SEG6_ATTR_ALGID] = { .type = NLA_U8, }, 39 + [SEG6_ATTR_HMACINFO] = { .type = NLA_NESTED, }, 40 + }; 41 + 42 + static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info) 43 + { 44 + return -ENOTSUPP; 45 + } 46 + 47 + static int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info) 48 + { 49 + struct net *net = genl_info_net(info); 50 + struct in6_addr *val, *t_old, *t_new; 51 + struct seg6_pernet_data *sdata; 52 + 53 + sdata = seg6_pernet(net); 54 + 55 + if (!info->attrs[SEG6_ATTR_DST]) 56 + return -EINVAL; 57 + 58 + val = nla_data(info->attrs[SEG6_ATTR_DST]); 59 + t_new = kmemdup(val, sizeof(*val), GFP_KERNEL); 60 + 61 + mutex_lock(&sdata->lock); 62 + 63 + t_old = sdata->tun_src; 64 + rcu_assign_pointer(sdata->tun_src, t_new); 65 + 66 + mutex_unlock(&sdata->lock); 67 + 68 + synchronize_net(); 69 + kfree(t_old); 70 + 71 + return 0; 72 + } 73 + 74 + static int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info) 75 + { 76 + struct net *net = genl_info_net(info); 77 + struct in6_addr *tun_src; 78 + struct sk_buff *msg; 79 + void *hdr; 80 + 81 + msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 82 + if (!msg) 83 + return -ENOMEM; 84 + 85 + hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, 86 + &seg6_genl_family, 0, SEG6_CMD_GET_TUNSRC); 87 + if (!hdr) 88 + goto free_msg; 89 + 90 + rcu_read_lock(); 91 + tun_src = rcu_dereference(seg6_pernet(net)->tun_src); 92 + 93 + if (nla_put(msg, SEG6_ATTR_DST, sizeof(struct in6_addr), tun_src)) 94 + goto nla_put_failure; 95 + 96 + rcu_read_unlock(); 97 + 98 + genlmsg_end(msg, hdr); 99 + genlmsg_reply(msg, info); 100 + 101 + return 0; 102 + 103 + nla_put_failure: 104 + rcu_read_unlock(); 105 + genlmsg_cancel(msg, hdr); 106 + free_msg: 107 + nlmsg_free(msg); 108 + return -ENOMEM; 109 + } 110 + 111 + static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb) 112 + { 113 + return -ENOTSUPP; 114 + } 115 + 116 + static int __net_init seg6_net_init(struct net *net) 117 + { 118 + struct seg6_pernet_data *sdata; 119 + 120 + sdata = kzalloc(sizeof(*sdata), GFP_KERNEL); 121 + if (!sdata) 122 + return -ENOMEM; 123 + 124 + mutex_init(&sdata->lock); 125 + 126 + sdata->tun_src = kzalloc(sizeof(*sdata->tun_src), GFP_KERNEL); 127 + if (!sdata->tun_src) { 128 + kfree(sdata); 129 + return -ENOMEM; 130 + } 131 + 132 + net->ipv6.seg6_data = sdata; 133 + 134 + return 0; 135 + } 136 + 137 + static void __net_exit seg6_net_exit(struct net *net) 138 + { 139 + struct seg6_pernet_data *sdata = seg6_pernet(net); 140 + 141 + kfree(sdata->tun_src); 142 + kfree(sdata); 143 + } 144 + 145 + static struct pernet_operations ip6_segments_ops = { 146 + .init = seg6_net_init, 147 + .exit = seg6_net_exit, 148 + }; 149 + 150 + static const struct genl_ops seg6_genl_ops[] = { 151 + { 152 + .cmd = SEG6_CMD_SETHMAC, 153 + .doit = seg6_genl_sethmac, 154 + .policy = seg6_genl_policy, 155 + .flags = GENL_ADMIN_PERM, 156 + }, 157 + { 158 + .cmd = SEG6_CMD_DUMPHMAC, 159 + .dumpit = seg6_genl_dumphmac, 160 + .policy = seg6_genl_policy, 161 + .flags = GENL_ADMIN_PERM, 162 + }, 163 + { 164 + .cmd = SEG6_CMD_SET_TUNSRC, 165 + .doit = seg6_genl_set_tunsrc, 166 + .policy = seg6_genl_policy, 167 + .flags = GENL_ADMIN_PERM, 168 + }, 169 + { 170 + .cmd = SEG6_CMD_GET_TUNSRC, 171 + .doit = seg6_genl_get_tunsrc, 172 + .policy = seg6_genl_policy, 173 + .flags = GENL_ADMIN_PERM, 174 + }, 175 + }; 176 + 177 + static struct genl_family seg6_genl_family __ro_after_init = { 178 + .hdrsize = 0, 179 + .name = SEG6_GENL_NAME, 180 + .version = SEG6_GENL_VERSION, 181 + .maxattr = SEG6_ATTR_MAX, 182 + .netnsok = true, 183 + .parallel_ops = true, 184 + .ops = seg6_genl_ops, 185 + .n_ops = ARRAY_SIZE(seg6_genl_ops), 186 + .module = THIS_MODULE, 187 + }; 188 + 189 + int __init seg6_init(void) 190 + { 191 + int err = -ENOMEM; 192 + 193 + err = genl_register_family(&seg6_genl_family); 194 + if (err) 195 + goto out; 196 + 197 + err = register_pernet_subsys(&ip6_segments_ops); 198 + if (err) 199 + goto out_unregister_genl; 200 + 201 + pr_info("Segment Routing with IPv6\n"); 202 + 203 + out: 204 + return err; 205 + out_unregister_genl: 206 + genl_unregister_family(&seg6_genl_family); 207 + goto out; 208 + } 209 + 210 + void seg6_exit(void) 211 + { 212 + unregister_pernet_subsys(&ip6_segments_ops); 213 + genl_unregister_family(&seg6_genl_family); 214 + }