162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SR-IPv6 implementation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: 662306a36Sopenharmony_ci * David Lebrun <david.lebrun@uclouvain.be> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <linux/socket.h> 1262306a36Sopenharmony_ci#include <linux/net.h> 1362306a36Sopenharmony_ci#include <linux/in6.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/rhashtable.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <net/ipv6.h> 1862306a36Sopenharmony_ci#include <net/protocol.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <net/seg6.h> 2162306a36Sopenharmony_ci#include <net/genetlink.h> 2262306a36Sopenharmony_ci#include <linux/seg6.h> 2362306a36Sopenharmony_ci#include <linux/seg6_genl.h> 2462306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 2562306a36Sopenharmony_ci#include <net/seg6_hmac.h> 2662306a36Sopenharmony_ci#endif 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cibool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci unsigned int tlv_offset; 3162306a36Sopenharmony_ci int max_last_entry; 3262306a36Sopenharmony_ci int trailing; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (srh->type != IPV6_SRCRT_TYPE_4) 3562306a36Sopenharmony_ci return false; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (((srh->hdrlen + 1) << 3) != len) 3862306a36Sopenharmony_ci return false; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (!reduced && srh->segments_left > srh->first_segment) { 4162306a36Sopenharmony_ci return false; 4262306a36Sopenharmony_ci } else { 4362306a36Sopenharmony_ci max_last_entry = (srh->hdrlen / 2) - 1; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (srh->first_segment > max_last_entry) 4662306a36Sopenharmony_ci return false; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (srh->segments_left > srh->first_segment + 1) 4962306a36Sopenharmony_ci return false; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci trailing = len - tlv_offset; 5562306a36Sopenharmony_ci if (trailing < 0) 5662306a36Sopenharmony_ci return false; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci while (trailing) { 5962306a36Sopenharmony_ci struct sr6_tlv *tlv; 6062306a36Sopenharmony_ci unsigned int tlv_len; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (trailing < sizeof(*tlv)) 6362306a36Sopenharmony_ci return false; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci tlv = (struct sr6_tlv *)((unsigned char *)srh + tlv_offset); 6662306a36Sopenharmony_ci tlv_len = sizeof(*tlv) + tlv->len; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci trailing -= tlv_len; 6962306a36Sopenharmony_ci if (trailing < 0) 7062306a36Sopenharmony_ci return false; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci tlv_offset += tlv_len; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return true; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistruct ipv6_sr_hdr *seg6_get_srh(struct sk_buff *skb, int flags) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct ipv6_sr_hdr *srh; 8162306a36Sopenharmony_ci int len, srhoff = 0; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, &flags) < 0) 8462306a36Sopenharmony_ci return NULL; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!pskb_may_pull(skb, srhoff + sizeof(*srh))) 8762306a36Sopenharmony_ci return NULL; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci len = (srh->hdrlen + 1) << 3; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (!pskb_may_pull(skb, srhoff + len)) 9462306a36Sopenharmony_ci return NULL; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* note that pskb_may_pull may change pointers in header; 9762306a36Sopenharmony_ci * for this reason it is necessary to reload them when needed. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!seg6_validate_srh(srh, len, true)) 10262306a36Sopenharmony_ci return NULL; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return srh; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* Determine if an ICMP invoking packet contains a segment routing 10862306a36Sopenharmony_ci * header. If it does, extract the offset to the true destination 10962306a36Sopenharmony_ci * address, which is in the first segment address. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_civoid seg6_icmp_srh(struct sk_buff *skb, struct inet6_skb_parm *opt) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci __u16 network_header = skb->network_header; 11462306a36Sopenharmony_ci struct ipv6_sr_hdr *srh; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* Update network header to point to the invoking packet 11762306a36Sopenharmony_ci * inside the ICMP packet, so we can use the seg6_get_srh() 11862306a36Sopenharmony_ci * helper. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci skb_reset_network_header(skb); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci srh = seg6_get_srh(skb, 0); 12362306a36Sopenharmony_ci if (!srh) 12462306a36Sopenharmony_ci goto out; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (srh->type != IPV6_SRCRT_TYPE_4) 12762306a36Sopenharmony_ci goto out; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci opt->flags |= IP6SKB_SEG6; 13062306a36Sopenharmony_ci opt->srhoff = (unsigned char *)srh - skb->data; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ciout: 13362306a36Sopenharmony_ci /* Restore the network header back to the ICMP packet */ 13462306a36Sopenharmony_ci skb->network_header = network_header; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic struct genl_family seg6_genl_family; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = { 14062306a36Sopenharmony_ci [SEG6_ATTR_DST] = { .type = NLA_BINARY, 14162306a36Sopenharmony_ci .len = sizeof(struct in6_addr) }, 14262306a36Sopenharmony_ci [SEG6_ATTR_DSTLEN] = { .type = NLA_S32, }, 14362306a36Sopenharmony_ci [SEG6_ATTR_HMACKEYID] = { .type = NLA_U32, }, 14462306a36Sopenharmony_ci [SEG6_ATTR_SECRET] = { .type = NLA_BINARY, }, 14562306a36Sopenharmony_ci [SEG6_ATTR_SECRETLEN] = { .type = NLA_U8, }, 14662306a36Sopenharmony_ci [SEG6_ATTR_ALGID] = { .type = NLA_U8, }, 14762306a36Sopenharmony_ci [SEG6_ATTR_HMACINFO] = { .type = NLA_NESTED, }, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct net *net = genl_info_net(info); 15562306a36Sopenharmony_ci struct seg6_pernet_data *sdata; 15662306a36Sopenharmony_ci struct seg6_hmac_info *hinfo; 15762306a36Sopenharmony_ci u32 hmackeyid; 15862306a36Sopenharmony_ci char *secret; 15962306a36Sopenharmony_ci int err = 0; 16062306a36Sopenharmony_ci u8 algid; 16162306a36Sopenharmony_ci u8 slen; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci sdata = seg6_pernet(net); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!info->attrs[SEG6_ATTR_HMACKEYID] || 16662306a36Sopenharmony_ci !info->attrs[SEG6_ATTR_SECRETLEN] || 16762306a36Sopenharmony_ci !info->attrs[SEG6_ATTR_ALGID]) 16862306a36Sopenharmony_ci return -EINVAL; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci hmackeyid = nla_get_u32(info->attrs[SEG6_ATTR_HMACKEYID]); 17162306a36Sopenharmony_ci slen = nla_get_u8(info->attrs[SEG6_ATTR_SECRETLEN]); 17262306a36Sopenharmony_ci algid = nla_get_u8(info->attrs[SEG6_ATTR_ALGID]); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (hmackeyid == 0) 17562306a36Sopenharmony_ci return -EINVAL; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (slen > SEG6_HMAC_SECRET_LEN) 17862306a36Sopenharmony_ci return -EINVAL; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci mutex_lock(&sdata->lock); 18162306a36Sopenharmony_ci hinfo = seg6_hmac_info_lookup(net, hmackeyid); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (!slen) { 18462306a36Sopenharmony_ci err = seg6_hmac_info_del(net, hmackeyid); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci goto out_unlock; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (!info->attrs[SEG6_ATTR_SECRET]) { 19062306a36Sopenharmony_ci err = -EINVAL; 19162306a36Sopenharmony_ci goto out_unlock; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (slen > nla_len(info->attrs[SEG6_ATTR_SECRET])) { 19562306a36Sopenharmony_ci err = -EINVAL; 19662306a36Sopenharmony_ci goto out_unlock; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (hinfo) { 20062306a36Sopenharmony_ci err = seg6_hmac_info_del(net, hmackeyid); 20162306a36Sopenharmony_ci if (err) 20262306a36Sopenharmony_ci goto out_unlock; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci secret = (char *)nla_data(info->attrs[SEG6_ATTR_SECRET]); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci hinfo = kzalloc(sizeof(*hinfo), GFP_KERNEL); 20862306a36Sopenharmony_ci if (!hinfo) { 20962306a36Sopenharmony_ci err = -ENOMEM; 21062306a36Sopenharmony_ci goto out_unlock; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci memcpy(hinfo->secret, secret, slen); 21462306a36Sopenharmony_ci hinfo->slen = slen; 21562306a36Sopenharmony_ci hinfo->alg_id = algid; 21662306a36Sopenharmony_ci hinfo->hmackeyid = hmackeyid; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci err = seg6_hmac_info_add(net, hmackeyid, hinfo); 21962306a36Sopenharmony_ci if (err) 22062306a36Sopenharmony_ci kfree(hinfo); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciout_unlock: 22362306a36Sopenharmony_ci mutex_unlock(&sdata->lock); 22462306a36Sopenharmony_ci return err; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci#else 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci return -ENOTSUPP; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci#endif 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct net *net = genl_info_net(info); 23962306a36Sopenharmony_ci struct in6_addr *val, *t_old, *t_new; 24062306a36Sopenharmony_ci struct seg6_pernet_data *sdata; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci sdata = seg6_pernet(net); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (!info->attrs[SEG6_ATTR_DST]) 24562306a36Sopenharmony_ci return -EINVAL; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci val = nla_data(info->attrs[SEG6_ATTR_DST]); 24862306a36Sopenharmony_ci t_new = kmemdup(val, sizeof(*val), GFP_KERNEL); 24962306a36Sopenharmony_ci if (!t_new) 25062306a36Sopenharmony_ci return -ENOMEM; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci mutex_lock(&sdata->lock); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci t_old = sdata->tun_src; 25562306a36Sopenharmony_ci rcu_assign_pointer(sdata->tun_src, t_new); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci mutex_unlock(&sdata->lock); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci synchronize_net(); 26062306a36Sopenharmony_ci kfree(t_old); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct net *net = genl_info_net(info); 26862306a36Sopenharmony_ci struct in6_addr *tun_src; 26962306a36Sopenharmony_ci struct sk_buff *msg; 27062306a36Sopenharmony_ci void *hdr; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 27362306a36Sopenharmony_ci if (!msg) 27462306a36Sopenharmony_ci return -ENOMEM; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, 27762306a36Sopenharmony_ci &seg6_genl_family, 0, SEG6_CMD_GET_TUNSRC); 27862306a36Sopenharmony_ci if (!hdr) 27962306a36Sopenharmony_ci goto free_msg; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci rcu_read_lock(); 28262306a36Sopenharmony_ci tun_src = rcu_dereference(seg6_pernet(net)->tun_src); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (nla_put(msg, SEG6_ATTR_DST, sizeof(struct in6_addr), tun_src)) 28562306a36Sopenharmony_ci goto nla_put_failure; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci rcu_read_unlock(); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci genlmsg_end(msg, hdr); 29062306a36Sopenharmony_ci return genlmsg_reply(msg, info); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cinla_put_failure: 29362306a36Sopenharmony_ci rcu_read_unlock(); 29462306a36Sopenharmony_cifree_msg: 29562306a36Sopenharmony_ci nlmsg_free(msg); 29662306a36Sopenharmony_ci return -ENOMEM; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int __seg6_hmac_fill_info(struct seg6_hmac_info *hinfo, 30262306a36Sopenharmony_ci struct sk_buff *msg) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci if (nla_put_u32(msg, SEG6_ATTR_HMACKEYID, hinfo->hmackeyid) || 30562306a36Sopenharmony_ci nla_put_u8(msg, SEG6_ATTR_SECRETLEN, hinfo->slen) || 30662306a36Sopenharmony_ci nla_put(msg, SEG6_ATTR_SECRET, hinfo->slen, hinfo->secret) || 30762306a36Sopenharmony_ci nla_put_u8(msg, SEG6_ATTR_ALGID, hinfo->alg_id)) 30862306a36Sopenharmony_ci return -1; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int __seg6_genl_dumphmac_element(struct seg6_hmac_info *hinfo, 31462306a36Sopenharmony_ci u32 portid, u32 seq, u32 flags, 31562306a36Sopenharmony_ci struct sk_buff *skb, u8 cmd) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci void *hdr; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci hdr = genlmsg_put(skb, portid, seq, &seg6_genl_family, flags, cmd); 32062306a36Sopenharmony_ci if (!hdr) 32162306a36Sopenharmony_ci return -ENOMEM; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (__seg6_hmac_fill_info(hinfo, skb) < 0) 32462306a36Sopenharmony_ci goto nla_put_failure; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci genlmsg_end(skb, hdr); 32762306a36Sopenharmony_ci return 0; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cinla_put_failure: 33062306a36Sopenharmony_ci genlmsg_cancel(skb, hdr); 33162306a36Sopenharmony_ci return -EMSGSIZE; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int seg6_genl_dumphmac_start(struct netlink_callback *cb) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct net *net = sock_net(cb->skb->sk); 33762306a36Sopenharmony_ci struct seg6_pernet_data *sdata; 33862306a36Sopenharmony_ci struct rhashtable_iter *iter; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci sdata = seg6_pernet(net); 34162306a36Sopenharmony_ci iter = (struct rhashtable_iter *)cb->args[0]; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (!iter) { 34462306a36Sopenharmony_ci iter = kmalloc(sizeof(*iter), GFP_KERNEL); 34562306a36Sopenharmony_ci if (!iter) 34662306a36Sopenharmony_ci return -ENOMEM; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci cb->args[0] = (long)iter; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci rhashtable_walk_enter(&sdata->hmac_infos, iter); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int seg6_genl_dumphmac_done(struct netlink_callback *cb) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci rhashtable_walk_exit(iter); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci kfree(iter); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0]; 37062306a36Sopenharmony_ci struct seg6_hmac_info *hinfo; 37162306a36Sopenharmony_ci int ret; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci rhashtable_walk_start(iter); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci for (;;) { 37662306a36Sopenharmony_ci hinfo = rhashtable_walk_next(iter); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (IS_ERR(hinfo)) { 37962306a36Sopenharmony_ci if (PTR_ERR(hinfo) == -EAGAIN) 38062306a36Sopenharmony_ci continue; 38162306a36Sopenharmony_ci ret = PTR_ERR(hinfo); 38262306a36Sopenharmony_ci goto done; 38362306a36Sopenharmony_ci } else if (!hinfo) { 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci ret = __seg6_genl_dumphmac_element(hinfo, 38862306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 38962306a36Sopenharmony_ci cb->nlh->nlmsg_seq, 39062306a36Sopenharmony_ci NLM_F_MULTI, 39162306a36Sopenharmony_ci skb, SEG6_CMD_DUMPHMAC); 39262306a36Sopenharmony_ci if (ret) 39362306a36Sopenharmony_ci goto done; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ret = skb->len; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cidone: 39962306a36Sopenharmony_ci rhashtable_walk_stop(iter); 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci#else 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int seg6_genl_dumphmac_start(struct netlink_callback *cb) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int seg6_genl_dumphmac_done(struct netlink_callback *cb) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci return -ENOTSUPP; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci#endif 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int __net_init seg6_net_init(struct net *net) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct seg6_pernet_data *sdata; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci sdata = kzalloc(sizeof(*sdata), GFP_KERNEL); 42762306a36Sopenharmony_ci if (!sdata) 42862306a36Sopenharmony_ci return -ENOMEM; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci mutex_init(&sdata->lock); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci sdata->tun_src = kzalloc(sizeof(*sdata->tun_src), GFP_KERNEL); 43362306a36Sopenharmony_ci if (!sdata->tun_src) { 43462306a36Sopenharmony_ci kfree(sdata); 43562306a36Sopenharmony_ci return -ENOMEM; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci net->ipv6.seg6_data = sdata; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 44162306a36Sopenharmony_ci if (seg6_hmac_net_init(net)) { 44262306a36Sopenharmony_ci kfree(rcu_dereference_raw(sdata->tun_src)); 44362306a36Sopenharmony_ci kfree(sdata); 44462306a36Sopenharmony_ci return -ENOMEM; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci#endif 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic void __net_exit seg6_net_exit(struct net *net) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct seg6_pernet_data *sdata = seg6_pernet(net); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 45662306a36Sopenharmony_ci seg6_hmac_net_exit(net); 45762306a36Sopenharmony_ci#endif 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci kfree(rcu_dereference_raw(sdata->tun_src)); 46062306a36Sopenharmony_ci kfree(sdata); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic struct pernet_operations ip6_segments_ops = { 46462306a36Sopenharmony_ci .init = seg6_net_init, 46562306a36Sopenharmony_ci .exit = seg6_net_exit, 46662306a36Sopenharmony_ci}; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic const struct genl_ops seg6_genl_ops[] = { 46962306a36Sopenharmony_ci { 47062306a36Sopenharmony_ci .cmd = SEG6_CMD_SETHMAC, 47162306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 47262306a36Sopenharmony_ci .doit = seg6_genl_sethmac, 47362306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 47462306a36Sopenharmony_ci }, 47562306a36Sopenharmony_ci { 47662306a36Sopenharmony_ci .cmd = SEG6_CMD_DUMPHMAC, 47762306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 47862306a36Sopenharmony_ci .start = seg6_genl_dumphmac_start, 47962306a36Sopenharmony_ci .dumpit = seg6_genl_dumphmac, 48062306a36Sopenharmony_ci .done = seg6_genl_dumphmac_done, 48162306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 48262306a36Sopenharmony_ci }, 48362306a36Sopenharmony_ci { 48462306a36Sopenharmony_ci .cmd = SEG6_CMD_SET_TUNSRC, 48562306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 48662306a36Sopenharmony_ci .doit = seg6_genl_set_tunsrc, 48762306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 48862306a36Sopenharmony_ci }, 48962306a36Sopenharmony_ci { 49062306a36Sopenharmony_ci .cmd = SEG6_CMD_GET_TUNSRC, 49162306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 49262306a36Sopenharmony_ci .doit = seg6_genl_get_tunsrc, 49362306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 49462306a36Sopenharmony_ci }, 49562306a36Sopenharmony_ci}; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic struct genl_family seg6_genl_family __ro_after_init = { 49862306a36Sopenharmony_ci .hdrsize = 0, 49962306a36Sopenharmony_ci .name = SEG6_GENL_NAME, 50062306a36Sopenharmony_ci .version = SEG6_GENL_VERSION, 50162306a36Sopenharmony_ci .maxattr = SEG6_ATTR_MAX, 50262306a36Sopenharmony_ci .policy = seg6_genl_policy, 50362306a36Sopenharmony_ci .netnsok = true, 50462306a36Sopenharmony_ci .parallel_ops = true, 50562306a36Sopenharmony_ci .ops = seg6_genl_ops, 50662306a36Sopenharmony_ci .n_ops = ARRAY_SIZE(seg6_genl_ops), 50762306a36Sopenharmony_ci .resv_start_op = SEG6_CMD_GET_TUNSRC + 1, 50862306a36Sopenharmony_ci .module = THIS_MODULE, 50962306a36Sopenharmony_ci}; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ciint __init seg6_init(void) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci int err; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci err = register_pernet_subsys(&ip6_segments_ops); 51662306a36Sopenharmony_ci if (err) 51762306a36Sopenharmony_ci goto out; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci err = genl_register_family(&seg6_genl_family); 52062306a36Sopenharmony_ci if (err) 52162306a36Sopenharmony_ci goto out_unregister_pernet; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL 52462306a36Sopenharmony_ci err = seg6_iptunnel_init(); 52562306a36Sopenharmony_ci if (err) 52662306a36Sopenharmony_ci goto out_unregister_genl; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci err = seg6_local_init(); 52962306a36Sopenharmony_ci if (err) { 53062306a36Sopenharmony_ci seg6_iptunnel_exit(); 53162306a36Sopenharmony_ci goto out_unregister_genl; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci#endif 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 53662306a36Sopenharmony_ci err = seg6_hmac_init(); 53762306a36Sopenharmony_ci if (err) 53862306a36Sopenharmony_ci goto out_unregister_iptun; 53962306a36Sopenharmony_ci#endif 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci pr_info("Segment Routing with IPv6\n"); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ciout: 54462306a36Sopenharmony_ci return err; 54562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 54662306a36Sopenharmony_ciout_unregister_iptun: 54762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL 54862306a36Sopenharmony_ci seg6_local_exit(); 54962306a36Sopenharmony_ci seg6_iptunnel_exit(); 55062306a36Sopenharmony_ci#endif 55162306a36Sopenharmony_ci#endif 55262306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL 55362306a36Sopenharmony_ciout_unregister_genl: 55462306a36Sopenharmony_ci genl_unregister_family(&seg6_genl_family); 55562306a36Sopenharmony_ci#endif 55662306a36Sopenharmony_ciout_unregister_pernet: 55762306a36Sopenharmony_ci unregister_pernet_subsys(&ip6_segments_ops); 55862306a36Sopenharmony_ci goto out; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_civoid seg6_exit(void) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 56462306a36Sopenharmony_ci seg6_hmac_exit(); 56562306a36Sopenharmony_ci#endif 56662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_LWTUNNEL 56762306a36Sopenharmony_ci seg6_iptunnel_exit(); 56862306a36Sopenharmony_ci#endif 56962306a36Sopenharmony_ci unregister_pernet_subsys(&ip6_segments_ops); 57062306a36Sopenharmony_ci genl_unregister_family(&seg6_genl_family); 57162306a36Sopenharmony_ci} 572