summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Lebrun <david.lebrun@uclouvain.be>2017-08-05 06:38:26 -0400
committerDavid S. Miller <davem@davemloft.net>2017-08-07 17:16:22 -0400
commitd1df6fd8a1d22d37cffa0075ab8ad423ce656777 (patch)
treedbdac3144dfdcf40f42aba921267e0c349ede55a
parentb04c80d3a7e228cfb832cdb1c9ce8151f174669c (diff)
ipv6: sr: define core operations for seg6local lightweight tunnel
This patch implements a new type of lightweight tunnel named seg6local. A seg6local lwt is defined by a type of action and a set of parameters. The action represents the operation to perform on the packets matching the lwt's route, and is not necessarily an encapsulation. The set of parameters are arguments for the processing function. Each action is defined in a struct seg6_action_desc within seg6_action_table[]. This structure contains the action, mandatory attributes, the processing function, and a static headroom size required by the action. The mandatory attributes are encoded as a bitmask field. The static headroom is set to a non-zero value when the processing function always add a constant number of bytes to the skb (e.g. the header size for encapsulations). To facilitate rtnetlink-related operations such as parsing, fill_encap, and cmp_encap, each type of action parameter is associated to three function pointers, in seg6_action_params[]. All actions defined in seg6_local.h are detailed in [1]. [1] https://tools.ietf.org/html/draft-filsfils-spring-srv6-network-programming-01 Signed-off-by: David Lebrun <david.lebrun@uclouvain.be> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/seg6_local.h6
-rw-r--r--include/net/seg6.h2
-rw-r--r--include/uapi/linux/lwtunnel.h1
-rw-r--r--include/uapi/linux/seg6_local.h68
-rw-r--r--net/core/lwtunnel.c2
-rw-r--r--net/ipv6/Kconfig3
-rw-r--r--net/ipv6/Makefile2
-rw-r--r--net/ipv6/seg6.c5
-rw-r--r--net/ipv6/seg6_local.c320
9 files changed, 407 insertions, 2 deletions
diff --git a/include/linux/seg6_local.h b/include/linux/seg6_local.h
new file mode 100644
index 000000000000..ee63e76fe0c7
--- /dev/null
+++ b/include/linux/seg6_local.h
@@ -0,0 +1,6 @@
1#ifndef _LINUX_SEG6_LOCAL_H
2#define _LINUX_SEG6_LOCAL_H
3
4#include <uapi/linux/seg6_local.h>
5
6#endif
diff --git a/include/net/seg6.h b/include/net/seg6.h
index a32abb040e1d..5379f550f521 100644
--- a/include/net/seg6.h
+++ b/include/net/seg6.h
@@ -56,6 +56,8 @@ extern int seg6_init(void);
56extern void seg6_exit(void); 56extern void seg6_exit(void);
57extern int seg6_iptunnel_init(void); 57extern int seg6_iptunnel_init(void);
58extern void seg6_iptunnel_exit(void); 58extern void seg6_iptunnel_exit(void);
59extern int seg6_local_init(void);
60extern void seg6_local_exit(void);
59 61
60extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len); 62extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len);
61extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh); 63extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh);
diff --git a/include/uapi/linux/lwtunnel.h b/include/uapi/linux/lwtunnel.h
index 92724cba1eba..7fdd19ca7511 100644
--- a/include/uapi/linux/lwtunnel.h
+++ b/include/uapi/linux/lwtunnel.h
@@ -11,6 +11,7 @@ enum lwtunnel_encap_types {
11 LWTUNNEL_ENCAP_IP6, 11 LWTUNNEL_ENCAP_IP6,
12 LWTUNNEL_ENCAP_SEG6, 12 LWTUNNEL_ENCAP_SEG6,
13 LWTUNNEL_ENCAP_BPF, 13 LWTUNNEL_ENCAP_BPF,
14 LWTUNNEL_ENCAP_SEG6_LOCAL,
14 __LWTUNNEL_ENCAP_MAX, 15 __LWTUNNEL_ENCAP_MAX,
15}; 16};
16 17
diff --git a/include/uapi/linux/seg6_local.h b/include/uapi/linux/seg6_local.h
new file mode 100644
index 000000000000..ef2d8c3e76c1
--- /dev/null
+++ b/include/uapi/linux/seg6_local.h
@@ -0,0 +1,68 @@
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#ifndef _UAPI_LINUX_SEG6_LOCAL_H
15#define _UAPI_LINUX_SEG6_LOCAL_H
16
17#include <linux/seg6.h>
18
19enum {
20 SEG6_LOCAL_UNSPEC,
21 SEG6_LOCAL_ACTION,
22 SEG6_LOCAL_SRH,
23 SEG6_LOCAL_TABLE,
24 SEG6_LOCAL_NH4,
25 SEG6_LOCAL_NH6,
26 SEG6_LOCAL_IIF,
27 SEG6_LOCAL_OIF,
28 __SEG6_LOCAL_MAX,
29};
30#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1)
31
32enum {
33 SEG6_LOCAL_ACTION_UNSPEC = 0,
34 /* node segment */
35 SEG6_LOCAL_ACTION_END = 1,
36 /* adjacency segment (IPv6 cross-connect) */
37 SEG6_LOCAL_ACTION_END_X = 2,
38 /* lookup of next seg NH in table */
39 SEG6_LOCAL_ACTION_END_T = 3,
40 /* decap and L2 cross-connect */
41 SEG6_LOCAL_ACTION_END_DX2 = 4,
42 /* decap and IPv6 cross-connect */
43 SEG6_LOCAL_ACTION_END_DX6 = 5,
44 /* decap and IPv4 cross-connect */
45 SEG6_LOCAL_ACTION_END_DX4 = 6,
46 /* decap and lookup of DA in v6 table */
47 SEG6_LOCAL_ACTION_END_DT6 = 7,
48 /* decap and lookup of DA in v4 table */
49 SEG6_LOCAL_ACTION_END_DT4 = 8,
50 /* binding segment with insertion */
51 SEG6_LOCAL_ACTION_END_B6 = 9,
52 /* binding segment with encapsulation */
53 SEG6_LOCAL_ACTION_END_B6_ENCAP = 10,
54 /* binding segment with MPLS encap */
55 SEG6_LOCAL_ACTION_END_BM = 11,
56 /* lookup last seg in table */
57 SEG6_LOCAL_ACTION_END_S = 12,
58 /* forward to SR-unaware VNF with static proxy */
59 SEG6_LOCAL_ACTION_END_AS = 13,
60 /* forward to SR-unaware VNF with masquerading */
61 SEG6_LOCAL_ACTION_END_AM = 14,
62
63 __SEG6_LOCAL_ACTION_MAX,
64};
65
66#define SEG6_LOCAL_ACTION_MAX (__SEG6_LOCAL_ACTION_MAX - 1)
67
68#endif
diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c
index 435f35f9a61c..0b171756453c 100644
--- a/net/core/lwtunnel.c
+++ b/net/core/lwtunnel.c
@@ -44,6 +44,8 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
44 return "SEG6"; 44 return "SEG6";
45 case LWTUNNEL_ENCAP_BPF: 45 case LWTUNNEL_ENCAP_BPF:
46 return "BPF"; 46 return "BPF";
47 case LWTUNNEL_ENCAP_SEG6_LOCAL:
48 return "SEG6LOCAL";
47 case LWTUNNEL_ENCAP_IP6: 49 case LWTUNNEL_ENCAP_IP6:
48 case LWTUNNEL_ENCAP_IP: 50 case LWTUNNEL_ENCAP_IP:
49 case LWTUNNEL_ENCAP_NONE: 51 case LWTUNNEL_ENCAP_NONE:
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index 50181a96923e..0d722396dce6 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -311,7 +311,8 @@ config IPV6_SEG6_LWTUNNEL
311 ---help--- 311 ---help---
312 Support for encapsulation of packets within an outer IPv6 312 Support for encapsulation of packets within an outer IPv6
313 header and a Segment Routing Header using the lightweight 313 header and a Segment Routing Header using the lightweight
314 tunnels mechanism. 314 tunnels mechanism. Also enable support for advanced local
315 processing of SRv6 packets based on their active segment.
315 316
316 If unsure, say N. 317 If unsure, say N.
317 318
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index f8b24c2e0d77..10e342363793 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -23,7 +23,7 @@ ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o
23ipv6-$(CONFIG_PROC_FS) += proc.o 23ipv6-$(CONFIG_PROC_FS) += proc.o
24ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o 24ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o
25ipv6-$(CONFIG_NETLABEL) += calipso.o 25ipv6-$(CONFIG_NETLABEL) += calipso.o
26ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o 26ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o seg6_local.o
27ipv6-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o 27ipv6-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o
28 28
29ipv6-objs += $(ipv6-y) 29ipv6-objs += $(ipv6-y)
diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
index 81c2339b3285..c81407770956 100644
--- a/net/ipv6/seg6.c
+++ b/net/ipv6/seg6.c
@@ -456,6 +456,10 @@ int __init seg6_init(void)
456 err = seg6_iptunnel_init(); 456 err = seg6_iptunnel_init();
457 if (err) 457 if (err)
458 goto out_unregister_pernet; 458 goto out_unregister_pernet;
459
460 err = seg6_local_init();
461 if (err)
462 goto out_unregister_pernet;
459#endif 463#endif
460 464
461#ifdef CONFIG_IPV6_SEG6_HMAC 465#ifdef CONFIG_IPV6_SEG6_HMAC
@@ -471,6 +475,7 @@ out:
471#ifdef CONFIG_IPV6_SEG6_HMAC 475#ifdef CONFIG_IPV6_SEG6_HMAC
472out_unregister_iptun: 476out_unregister_iptun:
473#ifdef CONFIG_IPV6_SEG6_LWTUNNEL 477#ifdef CONFIG_IPV6_SEG6_LWTUNNEL
478 seg6_local_exit();
474 seg6_iptunnel_exit(); 479 seg6_iptunnel_exit();
475#endif 480#endif
476#endif 481#endif
diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c
new file mode 100644
index 000000000000..53615d7e0723
--- /dev/null
+++ b/net/ipv6/seg6_local.c
@@ -0,0 +1,320 @@
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}