blob: e246b0ba12ac7d2b672ab401035e21e81733ea41 [file] [log] [blame]
David Lebrun915d7e52016-11-08 14:57:40 +01001/*
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
29static struct genl_family seg6_genl_family;
30
31static 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
42static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
43{
44 return -ENOTSUPP;
45}
46
47static 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
74static 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
103nla_put_failure:
104 rcu_read_unlock();
105 genlmsg_cancel(msg, hdr);
106free_msg:
107 nlmsg_free(msg);
108 return -ENOMEM;
109}
110
111static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
112{
113 return -ENOTSUPP;
114}
115
116static 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
137static 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
145static struct pernet_operations ip6_segments_ops = {
146 .init = seg6_net_init,
147 .exit = seg6_net_exit,
148};
149
150static 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
177static 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
189int __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
203out:
204 return err;
205out_unregister_genl:
206 genl_unregister_family(&seg6_genl_family);
207 goto out;
208}
209
210void seg6_exit(void)
211{
212 unregister_pernet_subsys(&ip6_segments_ops);
213 genl_unregister_family(&seg6_genl_family);
214}