Line data Source code
1 : /*
2 : * Copyright (c) 2015 Cisco and/or its affiliates.
3 : * Licensed under the Apache License, Version 2.0 (the "License");
4 : * you may not use this file except in compliance with the License.
5 : * You may obtain a copy of the License at:
6 : *
7 : * http://www.apache.org/licenses/LICENSE-2.0
8 : *
9 : * Unless required by applicable law or agreed to in writing, software
10 : * distributed under the License is distributed on an "AS IS" BASIS,
11 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : * See the License for the specific language governing permissions and
13 : * limitations under the License.
14 : */
15 : /*
16 : *------------------------------------------------------------------
17 : * ad.c - SRv6 Dynamic Proxy (AD) function
18 : *------------------------------------------------------------------
19 : */
20 :
21 : #include <vnet/vnet.h>
22 : #include <vnet/adj/adj.h>
23 : #include <vnet/plugin/plugin.h>
24 : #include <vpp/app/version.h>
25 : #include <srv6-ad/ad.h>
26 :
27 : #define SID_CREATE_IFACE_FEATURE_ERROR -1
28 : #define SID_CREATE_INVALID_IFACE_TYPE -3
29 : #define SID_CREATE_INVALID_IFACE_INDEX -4
30 : #define SID_CREATE_INVALID_ADJ_INDEX -5
31 :
32 : unsigned char function_name[] = "SRv6-AD-plugin";
33 : unsigned char keyword_str[] = "End.AD";
34 : unsigned char def_str[] =
35 : "Endpoint with dynamic proxy to SR-unaware appliance";
36 : unsigned char params_str[] = "nh <next-hop> oif <iface-out> iif <iface-in>";
37 :
38 : srv6_ad_main_t srv6_ad_main;
39 :
40 : /*****************************************/
41 : /* SRv6 LocalSID instantiation and removal functions */
42 : static int
43 3 : srv6_ad_localsid_creation_fn (ip6_sr_localsid_t * localsid)
44 : {
45 3 : ip6_sr_main_t *srm = &sr_main;
46 3 : srv6_ad_main_t *sm = &srv6_ad_main;
47 3 : srv6_ad_localsid_t *ls_mem = localsid->plugin_mem;
48 3 : u32 localsid_index = localsid - srm->localsids;
49 :
50 : /* Step 1: Prepare xconnect adjacency for sending packets to the VNF */
51 :
52 : /* Retrieve the adjacency corresponding to the (OIF, next_hop) */
53 3 : adj_index_t nh_adj_index = ADJ_INDEX_INVALID;
54 3 : if (ls_mem->inner_type != AD_TYPE_L2)
55 : {
56 2 : if (ls_mem->inner_type == AD_TYPE_IP4)
57 1 : nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4,
58 1 : VNET_LINK_IP4, &ls_mem->nh_addr,
59 : ls_mem->sw_if_index_out);
60 1 : else if (ls_mem->inner_type == AD_TYPE_IP6)
61 1 : nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6,
62 1 : VNET_LINK_IP6, &ls_mem->nh_addr,
63 : ls_mem->sw_if_index_out);
64 2 : if (nh_adj_index == ADJ_INDEX_INVALID)
65 : {
66 0 : clib_mem_free (ls_mem);
67 0 : return SID_CREATE_INVALID_ADJ_INDEX;
68 : }
69 : }
70 :
71 3 : ls_mem->nh_adj = nh_adj_index;
72 :
73 :
74 : /* Step 2: Prepare inbound policy for packets returning from the VNF */
75 :
76 : /* Sanitise the SW_IF_INDEX */
77 3 : if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces,
78 3 : ls_mem->sw_if_index_in))
79 : {
80 0 : adj_unlock (ls_mem->nh_adj);
81 0 : clib_mem_free (ls_mem);
82 0 : return SID_CREATE_INVALID_IFACE_INDEX;
83 : }
84 :
85 3 : vnet_sw_interface_t *sw = vnet_get_sw_interface (sm->vnet_main,
86 : ls_mem->sw_if_index_in);
87 3 : if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
88 : {
89 0 : adj_unlock (ls_mem->nh_adj);
90 0 : clib_mem_free (ls_mem);
91 0 : return SID_CREATE_INVALID_IFACE_TYPE;
92 : }
93 :
94 3 : if (ls_mem->inner_type == AD_TYPE_L2)
95 : {
96 : /* Enable End.AD2 rewrite node for this interface */
97 : int ret =
98 1 : vnet_feature_enable_disable ("device-input", "srv6-ad2-rewrite",
99 : ls_mem->sw_if_index_in, 1, 0, 0);
100 1 : if (ret != 0)
101 : {
102 0 : clib_mem_free (ls_mem);
103 0 : return SID_CREATE_IFACE_FEATURE_ERROR;
104 : }
105 :
106 : /* Set interface in promiscuous mode */
107 1 : vnet_main_t *vnm = vnet_get_main ();
108 : vnet_hw_interface_t *hi =
109 1 : vnet_get_sup_hw_interface (vnm, ls_mem->sw_if_index_in);
110 : /* Make sure it is main interface */
111 1 : if (hi->sw_if_index == ls_mem->sw_if_index_in)
112 1 : ethernet_set_flags (vnm, hi->hw_if_index,
113 : ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
114 :
115 : /* Associate local SID index to this interface (resize vector if needed) */
116 1 : if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid2))
117 : {
118 1 : vec_resize (sm->sw_iface_localsid2,
119 : (pool_len (sm->vnet_main->interface_main.sw_interfaces)
120 : - vec_len (sm->sw_iface_localsid2)));
121 : }
122 1 : sm->sw_iface_localsid2[ls_mem->sw_if_index_in] = localsid_index;
123 : }
124 2 : else if (ls_mem->inner_type == AD_TYPE_IP4)
125 : {
126 : /* Enable End.AD4 rewrite node for this interface */
127 : int ret =
128 1 : vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-rewrite",
129 : ls_mem->sw_if_index_in, 1, 0, 0);
130 1 : if (ret != 0)
131 : {
132 0 : adj_unlock (ls_mem->nh_adj);
133 0 : clib_mem_free (ls_mem);
134 0 : return SID_CREATE_IFACE_FEATURE_ERROR;
135 : }
136 :
137 : /* Associate local SID index to this interface (resize vector if needed) */
138 1 : if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid4))
139 : {
140 1 : vec_resize (sm->sw_iface_localsid4,
141 : (pool_len (sm->vnet_main->interface_main.sw_interfaces)
142 : - vec_len (sm->sw_iface_localsid4)));
143 : }
144 1 : sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
145 : }
146 1 : else if (ls_mem->inner_type == AD_TYPE_IP6)
147 : {
148 : /* Enable End.AD6 rewrite node for this interface */
149 : int ret =
150 1 : vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-rewrite",
151 : ls_mem->sw_if_index_in, 1, 0, 0);
152 1 : if (ret != 0)
153 : {
154 0 : adj_unlock (ls_mem->nh_adj);
155 0 : clib_mem_free (ls_mem);
156 0 : return SID_CREATE_IFACE_FEATURE_ERROR;
157 : }
158 :
159 : /* Associate local SID index to this interface (resize vector if needed) */
160 1 : if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid6))
161 : {
162 1 : vec_resize (sm->sw_iface_localsid6,
163 : (pool_len (sm->vnet_main->interface_main.sw_interfaces)
164 : - vec_len (sm->sw_iface_localsid6)));
165 : }
166 1 : sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
167 : }
168 :
169 3 : ls_mem->rw_len = 0;
170 :
171 : /* Step 3: Initialize rewrite counters */
172 : srv6_ad_localsid_t **ls_p;
173 3 : pool_get (sm->sids, ls_p);
174 3 : *ls_p = ls_mem;
175 3 : ls_mem->index = ls_p - sm->sids;
176 :
177 3 : vlib_validate_combined_counter (&(sm->valid_counters), ls_mem->index);
178 3 : vlib_validate_combined_counter (&(sm->invalid_counters), ls_mem->index);
179 :
180 3 : vlib_zero_combined_counter (&(sm->valid_counters), ls_mem->index);
181 3 : vlib_zero_combined_counter (&(sm->invalid_counters), ls_mem->index);
182 :
183 3 : return 0;
184 : }
185 :
186 : static int
187 3 : srv6_ad_localsid_removal_fn (ip6_sr_localsid_t * localsid)
188 : {
189 3 : srv6_ad_main_t *sm = &srv6_ad_main;
190 3 : srv6_ad_localsid_t *ls_mem = localsid->plugin_mem;
191 :
192 3 : if (ls_mem->inner_type == AD_TYPE_L2)
193 : {
194 : /* Disable End.AD2 rewrite node for this interface */
195 : int ret =
196 1 : vnet_feature_enable_disable ("device-input", "srv6-ad2-rewrite",
197 : ls_mem->sw_if_index_in, 0, 0, 0);
198 1 : if (ret != 0)
199 0 : return -1;
200 :
201 : /* Disable promiscuous mode on the interface */
202 1 : vnet_main_t *vnm = vnet_get_main ();
203 : vnet_hw_interface_t *hi =
204 1 : vnet_get_sup_hw_interface (vnm, ls_mem->sw_if_index_in);
205 : /* Make sure it is main interface */
206 1 : if (hi->sw_if_index == ls_mem->sw_if_index_in)
207 1 : ethernet_set_flags (vnm, hi->hw_if_index, 0);
208 :
209 : /* Remove local SID index from interface table */
210 1 : sm->sw_iface_localsid2[ls_mem->sw_if_index_in] = ~(u32) 0;
211 : }
212 2 : else if (ls_mem->inner_type == AD_TYPE_IP4)
213 : {
214 : /* Disable End.AD4 rewrite node for this interface */
215 : int ret =
216 1 : vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-rewrite",
217 : ls_mem->sw_if_index_in, 0, 0, 0);
218 1 : if (ret != 0)
219 0 : return -1;
220 :
221 : /* Remove local SID pointer from interface table */
222 1 : sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = ~(u32) 0;
223 : }
224 1 : else if (ls_mem->inner_type == AD_TYPE_IP6)
225 : {
226 : /* Disable End.AD6 rewrite node for this interface */
227 : int ret =
228 1 : vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-rewrite",
229 : ls_mem->sw_if_index_in, 0, 0, 0);
230 1 : if (ret != 0)
231 0 : return -1;
232 :
233 : /* Remove local SID pointer from interface table */
234 1 : sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = ~(u32) 0;
235 : }
236 :
237 :
238 : /* Unlock (OIF, NHOP) adjacency */
239 3 : adj_unlock (ls_mem->nh_adj);
240 :
241 : /* Delete SID entry */
242 3 : pool_put (sm->sids, pool_elt_at_index (sm->sids, ls_mem->index));
243 :
244 : /* Clean up local SID memory */
245 3 : vec_free (ls_mem->rewrite);
246 3 : clib_mem_free (localsid->plugin_mem);
247 :
248 3 : return 0;
249 : }
250 :
251 : /**********************************/
252 : /* SRv6 LocalSID format functions */
253 : /*
254 : * Prints nicely the parameters of a localsid
255 : * Example: print "Table 5"
256 : */
257 : u8 *
258 9 : format_srv6_ad_localsid (u8 * s, va_list * args)
259 : {
260 9 : srv6_ad_localsid_t *ls_mem = va_arg (*args, void *);
261 :
262 9 : vnet_main_t *vnm = vnet_get_main ();
263 9 : srv6_ad_main_t *sm = &srv6_ad_main;
264 :
265 9 : if (ls_mem->inner_type == AD_TYPE_IP4)
266 : {
267 : s =
268 3 : format (s, "Next-hop:\t%U\n\t", format_ip4_address,
269 : &ls_mem->nh_addr.ip4);
270 : }
271 6 : else if (ls_mem->inner_type == AD_TYPE_IP6)
272 : {
273 : s =
274 3 : format (s, "Next-hop:\t%U\n\t", format_ip6_address,
275 : &ls_mem->nh_addr.ip6);
276 : }
277 :
278 9 : s = format (s, "Outgoing iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
279 : ls_mem->sw_if_index_out);
280 9 : s = format (s, "\tIncoming iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
281 : ls_mem->sw_if_index_in);
282 :
283 : vlib_counter_t valid, invalid;
284 9 : vlib_get_combined_counter (&(sm->valid_counters), ls_mem->index, &valid);
285 9 : vlib_get_combined_counter (&(sm->invalid_counters), ls_mem->index,
286 : &invalid);
287 9 : s = format (s, "\tGood rewrite traffic: \t[%Ld packets : %Ld bytes]\n",
288 : valid.packets, valid.bytes);
289 9 : s = format (s, "\tBad rewrite traffic: \t[%Ld packets : %Ld bytes]\n",
290 : invalid.packets, invalid.bytes);
291 :
292 9 : return s;
293 : }
294 :
295 : /*
296 : * Process the parameters of a localsid
297 : * Example: process from:
298 : * sr localsid address cafe::1 behavior new_srv6_localsid 5
299 : * everything from behavior on... so in this case 'new_srv6_localsid 5'
300 : * Notice that it MUST match the keyword_str and params_str defined above.
301 : */
302 : uword
303 14 : unformat_srv6_ad_localsid (unformat_input_t * input, va_list * args)
304 : {
305 14 : void **plugin_mem_p = va_arg (*args, void **);
306 : srv6_ad_localsid_t *ls_mem;
307 :
308 14 : vnet_main_t *vnm = vnet_get_main ();
309 :
310 14 : u8 inner_type = AD_TYPE_L2;
311 : ip46_address_t nh_addr;
312 : u32 sw_if_index_out;
313 : u32 sw_if_index_in;
314 :
315 14 : u8 params = 0;
316 : #define PARAM_AD_NH (1 << 0)
317 : #define PARAM_AD_OIF (1 << 1)
318 : #define PARAM_AD_IIF (1 << 2)
319 :
320 14 : if (!unformat (input, "end.ad"))
321 9 : return 0;
322 :
323 12 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
324 : {
325 9 : if (!(params & PARAM_AD_NH) && unformat (input, "nh %U",
326 : unformat_ip4_address,
327 : &nh_addr.ip4))
328 : {
329 1 : inner_type = AD_TYPE_IP4;
330 1 : params |= PARAM_AD_NH;
331 : }
332 9 : if (!(params & PARAM_AD_NH) && unformat (input, "nh %U",
333 : unformat_ip6_address,
334 : &nh_addr.ip6))
335 : {
336 1 : inner_type = AD_TYPE_IP6;
337 1 : params |= PARAM_AD_NH;
338 : }
339 8 : else if (!(params & PARAM_AD_OIF) && unformat (input, "oif %U",
340 : unformat_vnet_sw_interface,
341 : vnm, &sw_if_index_out))
342 : {
343 3 : params |= PARAM_AD_OIF;
344 : }
345 5 : else if (!(params & PARAM_AD_IIF) && unformat (input, "iif %U",
346 : unformat_vnet_sw_interface,
347 : vnm, &sw_if_index_in))
348 : {
349 3 : params |= PARAM_AD_IIF;
350 : }
351 : else
352 : {
353 : break;
354 : }
355 : }
356 :
357 : /* Make sure that all parameters are supplied */
358 5 : u8 params_chk = (PARAM_AD_OIF | PARAM_AD_IIF);
359 5 : if ((params & params_chk) != params_chk)
360 : {
361 2 : return 0;
362 : }
363 :
364 : /* Allocate and initialize memory block for local SID parameters */
365 3 : ls_mem = clib_mem_alloc (sizeof *ls_mem);
366 3 : clib_memset (ls_mem, 0, sizeof *ls_mem);
367 3 : *plugin_mem_p = ls_mem;
368 :
369 : /* Set local SID parameters */
370 3 : ls_mem->inner_type = inner_type;
371 3 : if (inner_type == AD_TYPE_IP4)
372 1 : ls_mem->nh_addr.ip4 = nh_addr.ip4;
373 2 : else if (inner_type == AD_TYPE_IP6)
374 1 : ls_mem->nh_addr.ip6 = nh_addr.ip6;
375 3 : ls_mem->sw_if_index_out = sw_if_index_out;
376 3 : ls_mem->sw_if_index_in = sw_if_index_in;
377 :
378 3 : return 1;
379 : }
380 :
381 : /*************************/
382 : /* SRv6 LocalSID FIB DPO */
383 : static u8 *
384 0 : format_srv6_ad_dpo (u8 * s, va_list * args)
385 : {
386 0 : index_t index = va_arg (*args, index_t);
387 0 : CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
388 :
389 0 : return (format (s, "SR: dynamic_proxy_index:[%u]", index));
390 : }
391 :
392 : void
393 18 : srv6_ad_dpo_lock (dpo_id_t * dpo)
394 : {
395 18 : }
396 :
397 : void
398 18 : srv6_ad_dpo_unlock (dpo_id_t * dpo)
399 : {
400 18 : }
401 :
402 : const static dpo_vft_t srv6_ad_vft = {
403 : .dv_lock = srv6_ad_dpo_lock,
404 : .dv_unlock = srv6_ad_dpo_unlock,
405 : .dv_format = format_srv6_ad_dpo,
406 : };
407 :
408 : const static char *const srv6_ad_ip6_nodes[] = {
409 : "srv6-ad-localsid",
410 : NULL,
411 : };
412 :
413 : const static char *const *const srv6_ad_nodes[DPO_PROTO_NUM] = {
414 : [DPO_PROTO_IP6] = srv6_ad_ip6_nodes,
415 : };
416 :
417 : /**********************/
418 : static clib_error_t *
419 559 : srv6_ad_init (vlib_main_t * vm)
420 : {
421 559 : srv6_ad_main_t *sm = &srv6_ad_main;
422 559 : int rv = 0;
423 :
424 559 : sm->vlib_main = vm;
425 559 : sm->vnet_main = vnet_get_main ();
426 :
427 : /* Create DPO */
428 559 : sm->srv6_ad_dpo_type = dpo_register_new_type (&srv6_ad_vft, srv6_ad_nodes);
429 :
430 : /* Register SRv6 LocalSID */
431 559 : rv = sr_localsid_register_function (vm,
432 : function_name,
433 : keyword_str,
434 : def_str,
435 : params_str,
436 : 128,
437 : &sm->srv6_ad_dpo_type,
438 : format_srv6_ad_localsid,
439 : unformat_srv6_ad_localsid,
440 : srv6_ad_localsid_creation_fn,
441 : srv6_ad_localsid_removal_fn);
442 559 : if (rv < 0)
443 0 : clib_error_return (0, "SRv6 LocalSID function could not be registered.");
444 : else
445 559 : sm->srv6_localsid_behavior_id = rv;
446 :
447 559 : return 0;
448 : }
449 :
450 : /* *INDENT-OFF* */
451 19039 : VNET_FEATURE_INIT (srv6_ad2_rewrite, static) =
452 : {
453 : .arc_name = "device-input",
454 : .node_name = "srv6-ad2-rewrite",
455 : .runs_before = VNET_FEATURES ("ethernet-input"),
456 : };
457 :
458 19039 : VNET_FEATURE_INIT (srv6_ad4_rewrite, static) =
459 : {
460 : .arc_name = "ip4-unicast",
461 : .node_name = "srv6-ad4-rewrite",
462 : .runs_before = 0,
463 : };
464 :
465 19039 : VNET_FEATURE_INIT (srv6_ad6_rewrite, static) =
466 : {
467 : .arc_name = "ip6-unicast",
468 : .node_name = "srv6-ad6-rewrite",
469 : .runs_before = 0,
470 : };
471 :
472 1119 : VLIB_INIT_FUNCTION (srv6_ad_init);
473 :
474 : VLIB_PLUGIN_REGISTER () = {
475 : .version = VPP_BUILD_VER,
476 : .description = "Dynamic Segment Routing for IPv6 (SRv6) Proxy",
477 : };
478 : /* *INDENT-ON* */
479 :
480 : /*
481 : * fd.io coding-style-patch-verification: ON
482 : *
483 : * Local Variables:
484 : * eval: (c-set-style "gnu")
485 : * End:
486 : */
|