diff options
-rw-r--r-- | include/linux/seg6_local.h | 6 | ||||
-rw-r--r-- | include/net/seg6.h | 2 | ||||
-rw-r--r-- | include/uapi/linux/lwtunnel.h | 1 | ||||
-rw-r--r-- | include/uapi/linux/seg6_local.h | 68 | ||||
-rw-r--r-- | net/core/lwtunnel.c | 2 | ||||
-rw-r--r-- | net/ipv6/Kconfig | 3 | ||||
-rw-r--r-- | net/ipv6/Makefile | 2 | ||||
-rw-r--r-- | net/ipv6/seg6.c | 5 | ||||
-rw-r--r-- | net/ipv6/seg6_local.c | 320 |
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); | |||
56 | extern void seg6_exit(void); | 56 | extern void seg6_exit(void); |
57 | extern int seg6_iptunnel_init(void); | 57 | extern int seg6_iptunnel_init(void); |
58 | extern void seg6_iptunnel_exit(void); | 58 | extern void seg6_iptunnel_exit(void); |
59 | extern int seg6_local_init(void); | ||
60 | extern void seg6_local_exit(void); | ||
59 | 61 | ||
60 | extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len); | 62 | extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len); |
61 | extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh); | 63 | extern 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 | |||
19 | enum { | ||
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 | |||
32 | enum { | ||
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 | |||
23 | ipv6-$(CONFIG_PROC_FS) += proc.o | 23 | ipv6-$(CONFIG_PROC_FS) += proc.o |
24 | ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o | 24 | ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o |
25 | ipv6-$(CONFIG_NETLABEL) += calipso.o | 25 | ipv6-$(CONFIG_NETLABEL) += calipso.o |
26 | ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o | 26 | ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o seg6_local.o |
27 | ipv6-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o | 27 | ipv6-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o |
28 | 28 | ||
29 | ipv6-objs += $(ipv6-y) | 29 | ipv6-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 |
472 | out_unregister_iptun: | 476 | out_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 | |||
34 | struct seg6_local_lwt; | ||
35 | |||
36 | struct 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 | |||
43 | struct 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 | |||
56 | static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) | ||
57 | { | ||
58 | return (struct seg6_local_lwt *)lwt->data; | ||
59 | } | ||
60 | |||
61 | static struct seg6_action_desc seg6_action_table[] = { | ||
62 | { | ||
63 | .action = SEG6_LOCAL_ACTION_END, | ||
64 | .attrs = 0, | ||
65 | }, | ||
66 | }; | ||
67 | |||
68 | static 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 | |||
83 | static 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 | |||
95 | static 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 | |||
107 | struct 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 | |||
113 | static 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 | |||
139 | static 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 | |||
171 | static 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 | |||
208 | out_free: | ||
209 | kfree(slwt->srh); | ||
210 | kfree(newts); | ||
211 | return err; | ||
212 | } | ||
213 | |||
214 | static 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 | |||
221 | static 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 | |||
243 | static 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 | |||
274 | static 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 | |||
301 | static 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 | |||
311 | int __init seg6_local_init(void) | ||
312 | { | ||
313 | return lwtunnel_encap_add_ops(&seg6_local_ops, | ||
314 | LWTUNNEL_ENCAP_SEG6_LOCAL); | ||
315 | } | ||
316 | |||
317 | void seg6_local_exit(void) | ||
318 | { | ||
319 | lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); | ||
320 | } | ||