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 : *------------------------------------------------------------------
18 : * ad-flow.c - SRv6 Flow-based Dynamic Proxy (AD.Flow) behavior
19 : *------------------------------------------------------------------
20 : */
21 :
22 : #include <vnet/vnet.h>
23 : #include <vnet/adj/adj.h>
24 : #include <vnet/plugin/plugin.h>
25 : #include <vpp/app/version.h>
26 : #include <srv6-ad-flow/ad-flow.h>
27 :
28 : #include <vppinfra/bihash_template.c>
29 :
30 : #define SID_CREATE_IFACE_FEATURE_ERROR -1
31 : #define SID_CREATE_INVALID_IFACE_TYPE -3
32 : #define SID_CREATE_INVALID_IFACE_INDEX -4
33 : #define SID_CREATE_INVALID_ADJ_INDEX -5
34 :
35 : unsigned char function_name[] = "SRv6-AD-Flow-plugin";
36 : unsigned char keyword_str[] = "End.AD.Flow";
37 : unsigned char def_str[] =
38 : "Endpoint with flow-based dynamic proxy to SR-unaware appliance";
39 : unsigned char params_str[] = "nh <next-hop> oif <iface-out> iif <iface-in>";
40 :
41 : srv6_ad_flow_main_t srv6_ad_flow_main;
42 :
43 : static u32
44 2 : ad_flow_calc_bihash_buckets (u32 n_elts)
45 : {
46 2 : return 1 << (max_log2 (n_elts >> 1) + 1);
47 : }
48 :
49 : static u32
50 2 : ad_flow_calc_bihash_memory (u32 n_buckets, uword kv_size)
51 : {
52 2 : return n_buckets * (8 + kv_size * 4);
53 : }
54 :
55 : /*****************************************/
56 : /* SRv6 LocalSID instantiation and removal functions */
57 : static int
58 2 : srv6_ad_flow_localsid_creation_fn (ip6_sr_localsid_t *localsid)
59 : {
60 2 : ip6_sr_main_t *srm = &sr_main;
61 2 : srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
62 2 : srv6_ad_flow_localsid_t *ls_mem = localsid->plugin_mem;
63 2 : u32 localsid_index = localsid - srm->localsids;
64 :
65 : /* Step 1: Prepare xconnect adjacency for sending packets to the VNF */
66 :
67 : /* Retrieve the adjacency corresponding to the (OIF, next_hop) */
68 2 : adj_index_t nh_adj_index = ADJ_INDEX_INVALID;
69 2 : if (ls_mem->inner_type == AD_TYPE_IP4)
70 : nh_adj_index =
71 1 : adj_nbr_add_or_lock (FIB_PROTOCOL_IP4, VNET_LINK_IP4, &ls_mem->nh_addr,
72 : ls_mem->sw_if_index_out);
73 1 : else if (ls_mem->inner_type == AD_TYPE_IP6)
74 : nh_adj_index =
75 1 : adj_nbr_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6, &ls_mem->nh_addr,
76 : ls_mem->sw_if_index_out);
77 :
78 2 : if (nh_adj_index == ADJ_INDEX_INVALID)
79 : {
80 0 : clib_mem_free (ls_mem);
81 0 : return SID_CREATE_INVALID_ADJ_INDEX;
82 : }
83 :
84 2 : ls_mem->nh_adj = nh_adj_index;
85 :
86 : /* Step 2: Prepare inbound policy for packets returning from the VNF */
87 :
88 : /* Sanitise the SW_IF_INDEX */
89 2 : if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces,
90 2 : ls_mem->sw_if_index_in))
91 : {
92 0 : adj_unlock (ls_mem->nh_adj);
93 0 : clib_mem_free (ls_mem);
94 0 : return SID_CREATE_INVALID_IFACE_INDEX;
95 : }
96 :
97 :
98 2 : if (ls_mem->inner_type == AD_TYPE_IP4)
99 : {
100 : /* Enable End.AD4 rewrite node for this interface */
101 : int ret =
102 1 : vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-flow-rewrite",
103 : ls_mem->sw_if_index_in, 1, 0, 0);
104 1 : if (ret != 0)
105 : {
106 0 : adj_unlock (ls_mem->nh_adj);
107 0 : clib_mem_free (ls_mem);
108 0 : return SID_CREATE_IFACE_FEATURE_ERROR;
109 : }
110 :
111 : /* Associate local SID index to this interface (resize vector if needed)
112 : */
113 1 : if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid4))
114 : {
115 1 : vec_resize (sm->sw_iface_localsid4,
116 : (pool_len (sm->vnet_main->interface_main.sw_interfaces) -
117 : vec_len (sm->sw_iface_localsid4)));
118 : }
119 1 : sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
120 : }
121 1 : else if (ls_mem->inner_type == AD_TYPE_IP6)
122 : {
123 : /* Enable End.AD6 rewrite node for this interface */
124 : int ret =
125 1 : vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-flow-rewrite",
126 : ls_mem->sw_if_index_in, 1, 0, 0);
127 1 : if (ret != 0)
128 : {
129 0 : adj_unlock (ls_mem->nh_adj);
130 0 : clib_mem_free (ls_mem);
131 0 : return SID_CREATE_IFACE_FEATURE_ERROR;
132 : }
133 :
134 : /* Associate local SID index to this interface (resize vector if needed)
135 : */
136 1 : if (ls_mem->sw_if_index_in >= vec_len (sm->sw_iface_localsid6))
137 : {
138 1 : vec_resize (sm->sw_iface_localsid6,
139 : (pool_len (sm->vnet_main->interface_main.sw_interfaces) -
140 : vec_len (sm->sw_iface_localsid6)));
141 : }
142 1 : sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
143 : }
144 :
145 : /* Initialize flow and cache tables */
146 2 : ls_mem->cache_size = SRV6_AD_FLOW_DEFAULT_CACHE_SIZE;
147 2 : ls_mem->cache_buckets = ad_flow_calc_bihash_buckets (ls_mem->cache_size);
148 2 : ls_mem->cache_memory_size = ad_flow_calc_bihash_memory (
149 : ls_mem->cache_buckets, sizeof (clib_bihash_40_8_t));
150 :
151 2 : pool_alloc (ls_mem->cache, ls_mem->cache_size);
152 2 : pool_alloc (ls_mem->lru_pool, ls_mem->cache_size);
153 :
154 : dlist_elt_t *head;
155 2 : pool_get (ls_mem->lru_pool, head);
156 2 : ls_mem->lru_head_index = head - ls_mem->lru_pool;
157 2 : clib_dlist_init (ls_mem->lru_pool, ls_mem->lru_head_index);
158 :
159 2 : clib_bihash_init_40_8 (&ls_mem->ftable, "ad-flow", ls_mem->cache_buckets,
160 : ls_mem->cache_memory_size);
161 :
162 : /* Step 3: Initialize rewrite counters */
163 : srv6_ad_flow_localsid_t **ls_p;
164 2 : pool_get (sm->sids, ls_p);
165 2 : *ls_p = ls_mem;
166 2 : ls_mem->index = ls_p - sm->sids;
167 :
168 2 : vlib_validate_combined_counter (&(sm->sid_bypass_counters), ls_mem->index);
169 2 : vlib_validate_combined_counter (&(sm->sid_punt_counters), ls_mem->index);
170 2 : vlib_validate_combined_counter (&(sm->sid_cache_full_counters),
171 : ls_mem->index);
172 2 : vlib_validate_combined_counter (&(sm->rw_valid_counters), ls_mem->index);
173 2 : vlib_validate_combined_counter (&(sm->rw_invalid_counters), ls_mem->index);
174 :
175 2 : vlib_zero_combined_counter (&(sm->sid_bypass_counters), ls_mem->index);
176 2 : vlib_zero_combined_counter (&(sm->sid_punt_counters), ls_mem->index);
177 2 : vlib_zero_combined_counter (&(sm->sid_cache_full_counters), ls_mem->index);
178 2 : vlib_zero_combined_counter (&(sm->rw_valid_counters), ls_mem->index);
179 2 : vlib_zero_combined_counter (&(sm->rw_invalid_counters), ls_mem->index);
180 :
181 2 : return 0;
182 : }
183 :
184 : static int
185 2 : srv6_ad_flow_localsid_removal_fn (ip6_sr_localsid_t *localsid)
186 : {
187 2 : srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
188 2 : srv6_ad_flow_localsid_t *ls_mem = localsid->plugin_mem;
189 :
190 2 : if (ls_mem->inner_type == AD_TYPE_IP4)
191 : {
192 : /* Disable End.AD4 rewrite node for this interface */
193 : int ret =
194 1 : vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-flow-rewrite",
195 : ls_mem->sw_if_index_in, 0, 0, 0);
196 1 : if (ret != 0)
197 0 : return -1;
198 :
199 : /* Remove local SID pointer from interface table */
200 1 : sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = ~(u32) 0;
201 : }
202 1 : else if (ls_mem->inner_type == AD_TYPE_IP6)
203 : {
204 : /* Disable End.AD6 rewrite node for this interface */
205 : int ret =
206 1 : vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-flow-rewrite",
207 : ls_mem->sw_if_index_in, 0, 0, 0);
208 1 : if (ret != 0)
209 0 : return -1;
210 :
211 : /* Remove local SID pointer from interface table */
212 1 : sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = ~(u32) 0;
213 : }
214 :
215 : /* Unlock (OIF, NHOP) adjacency */
216 2 : adj_unlock (ls_mem->nh_adj);
217 :
218 : /* Delete SID entry */
219 2 : pool_put (sm->sids, pool_elt_at_index (sm->sids, ls_mem->index));
220 :
221 : /* Clean up local SID memory */
222 : srv6_ad_flow_entry_t *e;
223 4 : pool_foreach (e, ls_mem->cache)
224 : {
225 2 : vec_free (e->rw_data);
226 : }
227 2 : pool_free (ls_mem->cache);
228 2 : pool_free (ls_mem->lru_pool);
229 2 : clib_bihash_free_40_8 (&ls_mem->ftable);
230 2 : clib_mem_free (localsid->plugin_mem);
231 :
232 2 : return 0;
233 : }
234 :
235 : /**********************************/
236 : /* SRv6 LocalSID format functions */
237 : /*
238 : * Prints nicely the parameters of a localsid
239 : * Example: print "Table 5"
240 : */
241 : u8 *
242 6 : format_srv6_ad_flow_localsid (u8 *s, va_list *args)
243 : {
244 6 : srv6_ad_flow_localsid_t *ls_mem = va_arg (*args, void *);
245 :
246 6 : vnet_main_t *vnm = vnet_get_main ();
247 6 : srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
248 :
249 6 : if (ls_mem->inner_type == AD_TYPE_IP4)
250 : {
251 3 : s = format (s, "Next-hop:\t%U\n\t", format_ip4_address,
252 : &ls_mem->nh_addr.ip4);
253 : }
254 3 : else if (ls_mem->inner_type == AD_TYPE_IP6)
255 : {
256 3 : s = format (s, "Next-hop:\t%U\n\t", format_ip6_address,
257 : &ls_mem->nh_addr.ip6);
258 : }
259 :
260 6 : s = format (s, "Outgoing iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
261 : ls_mem->sw_if_index_out);
262 6 : s = format (s, "\tIncoming iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
263 : ls_mem->sw_if_index_in);
264 :
265 : vlib_counter_t sid_bypass, sid_punt, sid_full, rw_valid, rw_invalid;
266 6 : vlib_get_combined_counter (&(sm->sid_bypass_counters), ls_mem->index,
267 : &sid_bypass);
268 6 : vlib_get_combined_counter (&(sm->sid_punt_counters), ls_mem->index,
269 : &sid_punt);
270 6 : vlib_get_combined_counter (&(sm->sid_cache_full_counters), ls_mem->index,
271 : &sid_full);
272 6 : vlib_get_combined_counter (&(sm->rw_valid_counters), ls_mem->index,
273 : &rw_valid);
274 6 : vlib_get_combined_counter (&(sm->rw_invalid_counters), ls_mem->index,
275 : &rw_invalid);
276 :
277 : s =
278 6 : format (s, "\tTraffic that bypassed the NF: \t[%Ld packets : %Ld bytes]\n",
279 : sid_bypass.packets, sid_bypass.bytes);
280 6 : s = format (s, "\tPunted traffic: \t[%Ld packets : %Ld bytes]\n",
281 : sid_punt.packets, sid_punt.bytes);
282 : s =
283 6 : format (s, "\tDropped traffic (cache full): \t[%Ld packets : %Ld bytes]\n",
284 : sid_full.packets, sid_full.bytes);
285 6 : s = format (s, "\tGood rewrite traffic: \t[%Ld packets : %Ld bytes]\n",
286 : rw_valid.packets, rw_valid.bytes);
287 6 : s = format (s, "\tBad rewrite traffic: \t[%Ld packets : %Ld bytes]\n",
288 : rw_invalid.packets, rw_invalid.bytes);
289 :
290 6 : return s;
291 : }
292 :
293 : /*
294 : * Process the parameters of a localsid
295 : * Example: process from:
296 : * sr localsid address cafe::1 behavior new_srv6_localsid 5
297 : * everything from behavior on... so in this case 'new_srv6_localsid 5'
298 : * Notice that it MUST match the keyword_str and params_str defined above.
299 : */
300 : uword
301 11 : unformat_srv6_ad_flow_localsid (unformat_input_t *input, va_list *args)
302 : {
303 11 : void **plugin_mem_p = va_arg (*args, void **);
304 : srv6_ad_flow_localsid_t *ls_mem;
305 :
306 11 : vnet_main_t *vnm = vnet_get_main ();
307 :
308 11 : u8 inner_type = AD_TYPE_IP4;
309 : ip46_address_t nh_addr;
310 : u32 sw_if_index_out;
311 : u32 sw_if_index_in;
312 :
313 11 : u8 params = 0;
314 : #define PARAM_AD_NH (1 << 0)
315 : #define PARAM_AD_OIF (1 << 1)
316 : #define PARAM_AD_IIF (1 << 2)
317 :
318 11 : if (!unformat (input, "end.ad.flow"))
319 9 : return 0;
320 :
321 7 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
322 : {
323 7 : if (!(params & PARAM_AD_NH) &&
324 2 : unformat (input, "nh %U", unformat_ip4_address, &nh_addr.ip4))
325 : {
326 1 : inner_type = AD_TYPE_IP4;
327 1 : params |= PARAM_AD_NH;
328 : }
329 6 : if (!(params & PARAM_AD_NH) &&
330 1 : unformat (input, "nh %U", unformat_ip6_address, &nh_addr.ip6))
331 : {
332 1 : inner_type = AD_TYPE_IP6;
333 1 : params |= PARAM_AD_NH;
334 : }
335 6 : else if (!(params & PARAM_AD_OIF) &&
336 2 : unformat (input, "oif %U", unformat_vnet_sw_interface, vnm,
337 : &sw_if_index_out))
338 : {
339 2 : params |= PARAM_AD_OIF;
340 : }
341 4 : else if (!(params & PARAM_AD_IIF) &&
342 2 : unformat (input, "iif %U", unformat_vnet_sw_interface, vnm,
343 : &sw_if_index_in))
344 : {
345 2 : params |= PARAM_AD_IIF;
346 : }
347 : else
348 : {
349 : break;
350 : }
351 : }
352 :
353 : /* Make sure that all parameters are supplied */
354 2 : u8 params_chk = (PARAM_AD_NH | PARAM_AD_OIF | PARAM_AD_IIF);
355 2 : if ((params & params_chk) != params_chk)
356 : {
357 0 : return 0;
358 : }
359 :
360 : /* Allocate and initialize memory block for local SID parameters */
361 2 : ls_mem = clib_mem_alloc (sizeof *ls_mem);
362 2 : clib_memset (ls_mem, 0, sizeof *ls_mem);
363 2 : *plugin_mem_p = ls_mem;
364 :
365 : /* Set local SID parameters */
366 2 : ls_mem->inner_type = inner_type;
367 2 : if (inner_type == AD_TYPE_IP4)
368 1 : ls_mem->nh_addr.ip4 = nh_addr.ip4;
369 1 : else if (inner_type == AD_TYPE_IP6)
370 1 : ls_mem->nh_addr.ip6 = nh_addr.ip6;
371 2 : ls_mem->sw_if_index_out = sw_if_index_out;
372 2 : ls_mem->sw_if_index_in = sw_if_index_in;
373 :
374 2 : return 1;
375 : }
376 :
377 : /*************************/
378 : /* SRv6 LocalSID FIB DPO */
379 : static u8 *
380 0 : format_srv6_ad_flow_dpo (u8 *s, va_list *args)
381 : {
382 0 : index_t index = va_arg (*args, index_t);
383 0 : CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
384 :
385 0 : return (format (s, "SR: dynamic_proxy_index:[%u]", index));
386 : }
387 :
388 : void
389 12 : srv6_ad_flow_dpo_lock (dpo_id_t *dpo)
390 : {
391 12 : }
392 :
393 : void
394 12 : srv6_ad_flow_dpo_unlock (dpo_id_t *dpo)
395 : {
396 12 : }
397 :
398 : const static dpo_vft_t srv6_ad_flow_vft = {
399 : .dv_lock = srv6_ad_flow_dpo_lock,
400 : .dv_unlock = srv6_ad_flow_dpo_unlock,
401 : .dv_format = format_srv6_ad_flow_dpo,
402 : };
403 :
404 : const static char *const srv6_ad_flow_ip6_nodes[] = {
405 : "srv6-ad-flow-localsid",
406 : NULL,
407 : };
408 :
409 : const static char *const *const srv6_ad_flow_nodes[DPO_PROTO_NUM] = {
410 : [DPO_PROTO_IP6] = srv6_ad_flow_ip6_nodes,
411 : };
412 :
413 : /**********************/
414 : static clib_error_t *
415 559 : srv6_ad_flow_init (vlib_main_t *vm)
416 : {
417 559 : srv6_ad_flow_main_t *sm = &srv6_ad_flow_main;
418 559 : int rv = 0;
419 :
420 559 : sm->vlib_main = vm;
421 559 : sm->vnet_main = vnet_get_main ();
422 :
423 : /* Create DPO */
424 559 : sm->srv6_ad_flow_dpo_type =
425 559 : dpo_register_new_type (&srv6_ad_flow_vft, srv6_ad_flow_nodes);
426 :
427 : /* Register SRv6 LocalSID */
428 559 : rv = sr_localsid_register_function (
429 : vm, function_name, keyword_str, def_str, params_str, 128,
430 : &sm->srv6_ad_flow_dpo_type, format_srv6_ad_flow_localsid,
431 : unformat_srv6_ad_flow_localsid, srv6_ad_flow_localsid_creation_fn,
432 : srv6_ad_flow_localsid_removal_fn);
433 559 : if (rv < 0)
434 0 : clib_error_return (0, "SRv6 LocalSID function could not be registered.");
435 : else
436 559 : sm->srv6_localsid_behavior_id = rv;
437 :
438 559 : return 0;
439 : }
440 :
441 17919 : VNET_FEATURE_INIT (srv6_ad4_flow_rewrite, static) = {
442 : .arc_name = "ip4-unicast",
443 : .node_name = "srv6-ad4-flow-rewrite",
444 : .runs_before = 0,
445 : };
446 :
447 17919 : VNET_FEATURE_INIT (srv6_ad6_flow_rewrite, static) = {
448 : .arc_name = "ip6-unicast",
449 : .node_name = "srv6-ad6-flow-rewrite",
450 : .runs_before = 0,
451 : };
452 :
453 1119 : VLIB_INIT_FUNCTION (srv6_ad_flow_init);
454 :
455 : VLIB_PLUGIN_REGISTER () = {
456 : .version = VPP_BUILD_VER,
457 : .description = "Dynamic Segment Routing for IPv6 (SRv6) Proxy",
458 : };
459 :
460 : /*
461 : * fd.io coding-style-patch-verification: ON
462 : *
463 : * Local Variables:
464 : * eval: (c-set-style "gnu")
465 : * End:
466 : */
|