ipv6: sr: implement API to control SR HMAC structure

This patch provides an implementation of the genetlink commands
to associate a given HMAC key identifier with an hashing algorithm
and a secret.

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David Lebrun 2016-11-08 14:59:18 +01:00 committed by David S. Miller
parent bf355b8d2c
commit 4f4853dc1c

View File

@ -25,6 +25,9 @@
#include <net/genetlink.h>
#include <linux/seg6.h>
#include <linux/seg6_genl.h>
#ifdef CONFIG_IPV6_SEG6_HMAC
#include <net/seg6_hmac.h>
#endif
bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
{
@ -76,11 +79,90 @@ static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = {
[SEG6_ATTR_HMACINFO] = { .type = NLA_NESTED, },
};
#ifdef CONFIG_IPV6_SEG6_HMAC
static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
{
struct net *net = genl_info_net(info);
struct seg6_pernet_data *sdata;
struct seg6_hmac_info *hinfo;
u32 hmackeyid;
char *secret;
int err = 0;
u8 algid;
u8 slen;
sdata = seg6_pernet(net);
if (!info->attrs[SEG6_ATTR_HMACKEYID] ||
!info->attrs[SEG6_ATTR_SECRETLEN] ||
!info->attrs[SEG6_ATTR_ALGID])
return -EINVAL;
hmackeyid = nla_get_u32(info->attrs[SEG6_ATTR_HMACKEYID]);
slen = nla_get_u8(info->attrs[SEG6_ATTR_SECRETLEN]);
algid = nla_get_u8(info->attrs[SEG6_ATTR_ALGID]);
if (hmackeyid == 0)
return -EINVAL;
if (slen > SEG6_HMAC_SECRET_LEN)
return -EINVAL;
mutex_lock(&sdata->lock);
hinfo = seg6_hmac_info_lookup(net, hmackeyid);
if (!slen) {
if (!hinfo)
err = -ENOENT;
err = seg6_hmac_info_del(net, hmackeyid);
goto out_unlock;
}
if (!info->attrs[SEG6_ATTR_SECRET]) {
err = -EINVAL;
goto out_unlock;
}
if (hinfo) {
err = seg6_hmac_info_del(net, hmackeyid);
if (err)
goto out_unlock;
}
secret = (char *)nla_data(info->attrs[SEG6_ATTR_SECRET]);
hinfo = kzalloc(sizeof(*hinfo), GFP_KERNEL);
if (!hinfo) {
err = -ENOMEM;
goto out_unlock;
}
memcpy(hinfo->secret, secret, slen);
hinfo->slen = slen;
hinfo->alg_id = algid;
hinfo->hmackeyid = hmackeyid;
err = seg6_hmac_info_add(net, hmackeyid, hinfo);
if (err)
kfree(hinfo);
out_unlock:
mutex_unlock(&sdata->lock);
return err;
}
#else
static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
{
return -ENOTSUPP;
}
#endif
static int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info)
{
struct net *net = genl_info_net(info);
@ -145,11 +227,135 @@ free_msg:
return -ENOMEM;
}
#ifdef CONFIG_IPV6_SEG6_HMAC
static int __seg6_hmac_fill_info(struct seg6_hmac_info *hinfo,
struct sk_buff *msg)
{
if (nla_put_u32(msg, SEG6_ATTR_HMACKEYID, hinfo->hmackeyid) ||
nla_put_u8(msg, SEG6_ATTR_SECRETLEN, hinfo->slen) ||
nla_put(msg, SEG6_ATTR_SECRET, hinfo->slen, hinfo->secret) ||
nla_put_u8(msg, SEG6_ATTR_ALGID, hinfo->alg_id))
return -1;
return 0;
}
static int __seg6_genl_dumphmac_element(struct seg6_hmac_info *hinfo,
u32 portid, u32 seq, u32 flags,
struct sk_buff *skb, u8 cmd)
{
void *hdr;
hdr = genlmsg_put(skb, portid, seq, &seg6_genl_family, flags, cmd);
if (!hdr)
return -ENOMEM;
if (__seg6_hmac_fill_info(hinfo, skb) < 0)
goto nla_put_failure;
genlmsg_end(skb, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(skb, hdr);
return -EMSGSIZE;
}
static int seg6_genl_dumphmac_start(struct netlink_callback *cb)
{
struct net *net = sock_net(cb->skb->sk);
struct seg6_pernet_data *sdata;
struct rhashtable_iter *iter;
sdata = seg6_pernet(net);
iter = (struct rhashtable_iter *)cb->args[0];
if (!iter) {
iter = kmalloc(sizeof(*iter), GFP_KERNEL);
if (!iter)
return -ENOMEM;
cb->args[0] = (long)iter;
}
rhashtable_walk_enter(&sdata->hmac_infos, iter);
return 0;
}
static int seg6_genl_dumphmac_done(struct netlink_callback *cb)
{
struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
rhashtable_walk_exit(iter);
kfree(iter);
return 0;
}
static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
{
struct rhashtable_iter *iter = (struct rhashtable_iter *)cb->args[0];
struct net *net = sock_net(skb->sk);
struct seg6_pernet_data *sdata;
struct seg6_hmac_info *hinfo;
int ret;
sdata = seg6_pernet(net);
ret = rhashtable_walk_start(iter);
if (ret && ret != -EAGAIN)
goto done;
for (;;) {
hinfo = rhashtable_walk_next(iter);
if (IS_ERR(hinfo)) {
if (PTR_ERR(hinfo) == -EAGAIN)
continue;
ret = PTR_ERR(hinfo);
goto done;
} else if (!hinfo) {
break;
}
ret = __seg6_genl_dumphmac_element(hinfo,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
NLM_F_MULTI,
skb, SEG6_CMD_DUMPHMAC);
if (ret)
goto done;
}
ret = skb->len;
done:
rhashtable_walk_stop(iter);
return ret;
}
#else
static int seg6_genl_dumphmac_start(struct netlink_callback *cb)
{
return 0;
}
static int seg6_genl_dumphmac_done(struct netlink_callback *cb)
{
return 0;
}
static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
{
return -ENOTSUPP;
}
#endif
static int __net_init seg6_net_init(struct net *net)
{
struct seg6_pernet_data *sdata;
@ -168,6 +374,10 @@ static int __net_init seg6_net_init(struct net *net)
net->ipv6.seg6_data = sdata;
#ifdef CONFIG_IPV6_SEG6_HMAC
seg6_hmac_net_init(net);
#endif
return 0;
}
@ -175,6 +385,10 @@ static void __net_exit seg6_net_exit(struct net *net)
{
struct seg6_pernet_data *sdata = seg6_pernet(net);
#ifdef CONFIG_IPV6_SEG6_HMAC
seg6_hmac_net_exit(net);
#endif
kfree(sdata->tun_src);
kfree(sdata);
}
@ -193,7 +407,9 @@ static const struct genl_ops seg6_genl_ops[] = {
},
{
.cmd = SEG6_CMD_DUMPHMAC,
.start = seg6_genl_dumphmac_start,
.dumpit = seg6_genl_dumphmac,
.done = seg6_genl_dumphmac_done,
.policy = seg6_genl_policy,
.flags = GENL_ADMIN_PERM,
},
@ -239,10 +455,20 @@ int __init seg6_init(void)
if (err)
goto out_unregister_pernet;
#ifdef CONFIG_IPV6_SEG6_HMAC
err = seg6_hmac_init();
if (err)
goto out_unregister_iptun;
#endif
pr_info("Segment Routing with IPv6\n");
out:
return err;
#ifdef CONFIG_IPV6_SEG6_HMAC
out_unregister_iptun:
seg6_iptunnel_exit();
#endif
out_unregister_pernet:
unregister_pernet_subsys(&ip6_segments_ops);
out_unregister_genl:
@ -252,6 +478,9 @@ out_unregister_genl:
void seg6_exit(void)
{
#ifdef CONFIG_IPV6_SEG6_HMAC
seg6_hmac_exit();
#endif
seg6_iptunnel_exit();
unregister_pernet_subsys(&ip6_segments_ops);
genl_unregister_family(&seg6_genl_family);