]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - net/ipv6/seg6_local.c
ipv6: sr: define core operations for seg6local lightweight tunnel
[mirror_ubuntu-jammy-kernel.git] / net / ipv6 / seg6_local.c
CommitLineData
d1df6fd8
DL
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/types.h>
15#include <linux/skbuff.h>
16#include <linux/net.h>
17#include <linux/module.h>
18#include <net/ip.h>
19#include <net/lwtunnel.h>
20#include <net/netevent.h>
21#include <net/netns/generic.h>
22#include <net/ip6_fib.h>
23#include <net/route.h>
24#include <net/seg6.h>
25#include <linux/seg6.h>
26#include <linux/seg6_local.h>
27#include <net/addrconf.h>
28#include <net/ip6_route.h>
29#include <net/dst_cache.h>
30#ifdef CONFIG_IPV6_SEG6_HMAC
31#include <net/seg6_hmac.h>
32#endif
33
34struct seg6_local_lwt;
35
36struct seg6_action_desc {
37 int action;
38 unsigned long attrs;
39 int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
40 int static_headroom;
41};
42
43struct seg6_local_lwt {
44 int action;
45 struct ipv6_sr_hdr *srh;
46 int table;
47 struct in_addr nh4;
48 struct in6_addr nh6;
49 int iif;
50 int oif;
51
52 int headroom;
53 struct seg6_action_desc *desc;
54};
55
56static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt)
57{
58 return (struct seg6_local_lwt *)lwt->data;
59}
60
61static struct seg6_action_desc seg6_action_table[] = {
62 {
63 .action = SEG6_LOCAL_ACTION_END,
64 .attrs = 0,
65 },
66};
67
68static struct seg6_action_desc *__get_action_desc(int action)
69{
70 struct seg6_action_desc *desc;
71 int i, count;
72
73 count = sizeof(seg6_action_table) / sizeof(struct seg6_action_desc);
74 for (i = 0; i < count; i++) {
75 desc = &seg6_action_table[i];
76 if (desc->action == action)
77 return desc;
78 }
79
80 return NULL;
81}
82
83static int seg6_local_input(struct sk_buff *skb)
84{
85 struct dst_entry *orig_dst = skb_dst(skb);
86 struct seg6_action_desc *desc;
87 struct seg6_local_lwt *slwt;
88
89 slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
90 desc = slwt->desc;
91
92 return desc->input(skb, slwt);
93}
94
95static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = {
96 [SEG6_LOCAL_ACTION] = { .type = NLA_U32 },
97 [SEG6_LOCAL_SRH] = { .type = NLA_BINARY },
98 [SEG6_LOCAL_TABLE] = { .type = NLA_U32 },
99 [SEG6_LOCAL_NH4] = { .type = NLA_BINARY,
100 .len = sizeof(struct in_addr) },
101 [SEG6_LOCAL_NH6] = { .type = NLA_BINARY,
102 .len = sizeof(struct in6_addr) },
103 [SEG6_LOCAL_IIF] = { .type = NLA_U32 },
104 [SEG6_LOCAL_OIF] = { .type = NLA_U32 },
105};
106
107struct seg6_action_param {
108 int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt);
109 int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
110 int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b);
111};
112
113static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = {
114 [SEG6_LOCAL_SRH] = { .parse = NULL,
115 .put = NULL,
116 .cmp = NULL },
117
118 [SEG6_LOCAL_TABLE] = { .parse = NULL,
119 .put = NULL,
120 .cmp = NULL },
121
122 [SEG6_LOCAL_NH4] = { .parse = NULL,
123 .put = NULL,
124 .cmp = NULL },
125
126 [SEG6_LOCAL_NH6] = { .parse = NULL,
127 .put = NULL,
128 .cmp = NULL },
129
130 [SEG6_LOCAL_IIF] = { .parse = NULL,
131 .put = NULL,
132 .cmp = NULL },
133
134 [SEG6_LOCAL_OIF] = { .parse = NULL,
135 .put = NULL,
136 .cmp = NULL },
137};
138
139static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt)
140{
141 struct seg6_action_param *param;
142 struct seg6_action_desc *desc;
143 int i, err;
144
145 desc = __get_action_desc(slwt->action);
146 if (!desc)
147 return -EINVAL;
148
149 if (!desc->input)
150 return -EOPNOTSUPP;
151
152 slwt->desc = desc;
153 slwt->headroom += desc->static_headroom;
154
155 for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
156 if (desc->attrs & (1 << i)) {
157 if (!attrs[i])
158 return -EINVAL;
159
160 param = &seg6_action_params[i];
161
162 err = param->parse(attrs, slwt);
163 if (err < 0)
164 return err;
165 }
166 }
167
168 return 0;
169}
170
171static int seg6_local_build_state(struct nlattr *nla, unsigned int family,
172 const void *cfg, struct lwtunnel_state **ts,
173 struct netlink_ext_ack *extack)
174{
175 struct nlattr *tb[SEG6_LOCAL_MAX + 1];
176 struct lwtunnel_state *newts;
177 struct seg6_local_lwt *slwt;
178 int err;
179
180 err = nla_parse_nested(tb, SEG6_LOCAL_MAX, nla, seg6_local_policy,
181 extack);
182
183 if (err < 0)
184 return err;
185
186 if (!tb[SEG6_LOCAL_ACTION])
187 return -EINVAL;
188
189 newts = lwtunnel_state_alloc(sizeof(*slwt));
190 if (!newts)
191 return -ENOMEM;
192
193 slwt = seg6_local_lwtunnel(newts);
194 slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]);
195
196 err = parse_nla_action(tb, slwt);
197 if (err < 0)
198 goto out_free;
199
200 newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL;
201 newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT;
202 newts->headroom = slwt->headroom;
203
204 *ts = newts;
205
206 return 0;
207
208out_free:
209 kfree(slwt->srh);
210 kfree(newts);
211 return err;
212}
213
214static void seg6_local_destroy_state(struct lwtunnel_state *lwt)
215{
216 struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
217
218 kfree(slwt->srh);
219}
220
221static int seg6_local_fill_encap(struct sk_buff *skb,
222 struct lwtunnel_state *lwt)
223{
224 struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
225 struct seg6_action_param *param;
226 int i, err;
227
228 if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action))
229 return -EMSGSIZE;
230
231 for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
232 if (slwt->desc->attrs & (1 << i)) {
233 param = &seg6_action_params[i];
234 err = param->put(skb, slwt);
235 if (err < 0)
236 return err;
237 }
238 }
239
240 return 0;
241}
242
243static int seg6_local_get_encap_size(struct lwtunnel_state *lwt)
244{
245 struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
246 unsigned long attrs;
247 int nlsize;
248
249 nlsize = nla_total_size(4); /* action */
250
251 attrs = slwt->desc->attrs;
252
253 if (attrs & (1 << SEG6_LOCAL_SRH))
254 nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3);
255
256 if (attrs & (1 << SEG6_LOCAL_TABLE))
257 nlsize += nla_total_size(4);
258
259 if (attrs & (1 << SEG6_LOCAL_NH4))
260 nlsize += nla_total_size(4);
261
262 if (attrs & (1 << SEG6_LOCAL_NH6))
263 nlsize += nla_total_size(16);
264
265 if (attrs & (1 << SEG6_LOCAL_IIF))
266 nlsize += nla_total_size(4);
267
268 if (attrs & (1 << SEG6_LOCAL_OIF))
269 nlsize += nla_total_size(4);
270
271 return nlsize;
272}
273
274static int seg6_local_cmp_encap(struct lwtunnel_state *a,
275 struct lwtunnel_state *b)
276{
277 struct seg6_local_lwt *slwt_a, *slwt_b;
278 struct seg6_action_param *param;
279 int i;
280
281 slwt_a = seg6_local_lwtunnel(a);
282 slwt_b = seg6_local_lwtunnel(b);
283
284 if (slwt_a->action != slwt_b->action)
285 return 1;
286
287 if (slwt_a->desc->attrs != slwt_b->desc->attrs)
288 return 1;
289
290 for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) {
291 if (slwt_a->desc->attrs & (1 << i)) {
292 param = &seg6_action_params[i];
293 if (param->cmp(slwt_a, slwt_b))
294 return 1;
295 }
296 }
297
298 return 0;
299}
300
301static const struct lwtunnel_encap_ops seg6_local_ops = {
302 .build_state = seg6_local_build_state,
303 .destroy_state = seg6_local_destroy_state,
304 .input = seg6_local_input,
305 .fill_encap = seg6_local_fill_encap,
306 .get_encap_size = seg6_local_get_encap_size,
307 .cmp_encap = seg6_local_cmp_encap,
308 .owner = THIS_MODULE,
309};
310
311int __init seg6_local_init(void)
312{
313 return lwtunnel_encap_add_ops(&seg6_local_ops,
314 LWTUNNEL_ENCAP_SEG6_LOCAL);
315}
316
317void seg6_local_exit(void)
318{
319 lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL);
320}