srv6-ad: Adding rewrite counters
[vpp.git] / src / plugins / srv6-ad / ad.c
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 unsigned char function_name[] = "SRv6-AD-plugin";
28 unsigned char keyword_str[] = "End.AD";
29 unsigned char def_str[] =
30   "Endpoint with dynamic proxy to SR-unaware appliance";
31 unsigned char params_str[] = "nh <next-hop> oif <iface-out> iif <iface-in>";
32
33
34 /*****************************************/
35 /* SRv6 LocalSID instantiation and removal functions */
36 static int
37 srv6_ad_localsid_creation_fn (ip6_sr_localsid_t * localsid)
38 {
39   ip6_sr_main_t *srm = &sr_main;
40   srv6_ad_main_t *sm = &srv6_ad_main;
41   srv6_ad_localsid_t *ls_mem = localsid->plugin_mem;
42   u32 localsid_index = localsid - srm->localsids;
43
44   /* Step 1: Prepare xconnect adjacency for sending packets to the VNF */
45
46   /* Retrieve the adjacency corresponding to the (OIF, next_hop) */
47   adj_index_t nh_adj_index = ADJ_INDEX_INVALID;
48   if (ls_mem->ip_version == DA_IP4)
49     nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4,
50                                         VNET_LINK_IP4, &ls_mem->nh_addr,
51                                         ls_mem->sw_if_index_out);
52   else if (ls_mem->ip_version == DA_IP6)
53     nh_adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP6,
54                                         VNET_LINK_IP6, &ls_mem->nh_addr,
55                                         ls_mem->sw_if_index_out);
56   if (nh_adj_index == ADJ_INDEX_INVALID)
57     return -5;
58
59   ls_mem->nh_adj = nh_adj_index;
60
61
62   /* Step 2: Prepare inbound policy for packets returning from the VNF */
63
64   /* Sanitise the SW_IF_INDEX */
65   if (pool_is_free_index (sm->vnet_main->interface_main.sw_interfaces,
66                           ls_mem->sw_if_index_in))
67     return -3;
68
69   vnet_sw_interface_t *sw = vnet_get_sw_interface (sm->vnet_main,
70                                                    ls_mem->sw_if_index_in);
71   if (sw->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
72     return -3;
73
74   int ret = -1;
75   if (ls_mem->ip_version == DA_IP4)
76     {
77       ret = vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-rewrite",
78                                          ls_mem->sw_if_index_in, 1, 0, 0);
79       if (ret != 0)
80         return -1;
81
82       /* FIB API calls - Recursive route through the BindingSID */
83       if (ls_mem->sw_if_index_in < vec_len (sm->sw_iface_localsid4))
84         {
85           sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
86         }
87       else
88         {
89           vec_resize (sm->sw_iface_localsid4,
90                       (pool_len (sm->vnet_main->interface_main.sw_interfaces)
91                        - vec_len (sm->sw_iface_localsid4)));
92           sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = localsid_index;
93         }
94     }
95   else if (ls_mem->ip_version == DA_IP6)
96     {
97       ret = vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-rewrite",
98                                          ls_mem->sw_if_index_in, 1, 0, 0);
99       if (ret != 0)
100         return -1;
101
102       if (ls_mem->sw_if_index_in < vec_len (sm->sw_iface_localsid6))
103         {
104           sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
105         }
106       else
107         {
108           vec_resize (sm->sw_iface_localsid6,
109                       (pool_len (sm->vnet_main->interface_main.sw_interfaces)
110                        - vec_len (sm->sw_iface_localsid6)));
111           sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = localsid_index;
112         }
113     }
114
115   ls_mem->rw_len = 0;
116
117   /* Step 3: Initialize rewrite counters */
118   srv6_ad_localsid_t **ls_p;
119   pool_get (sm->sids, ls_p);
120   *ls_p = ls_mem;
121   ls_mem->index = ls_p - sm->sids;
122
123   vlib_validate_combined_counter (&(sm->valid_counters), ls_mem->index);
124   vlib_validate_combined_counter (&(sm->invalid_counters), ls_mem->index);
125
126   vlib_zero_combined_counter (&(sm->valid_counters), ls_mem->index);
127   vlib_zero_combined_counter (&(sm->invalid_counters), ls_mem->index);
128
129   return 0;
130 }
131
132 static int
133 srv6_ad_localsid_removal_fn (ip6_sr_localsid_t * localsid)
134 {
135   srv6_ad_main_t *sm = &srv6_ad_main;
136   srv6_ad_localsid_t *ls_mem = localsid->plugin_mem;
137
138   int ret = -1;
139   if (ls_mem->ip_version == DA_IP4)
140     {
141       /* Remove hardware indirection (from sr_steering.c:137) */
142       ret = vnet_feature_enable_disable ("ip4-unicast", "srv6-ad4-rewrite",
143                                          ls_mem->sw_if_index_in, 0, 0, 0);
144       if (ret != 0)
145         return -1;
146
147       /* Remove local SID pointer from interface table (from sr_steering.c:139) */
148       sm->sw_iface_localsid4[ls_mem->sw_if_index_in] = ~(u32) 0;
149     }
150   else if (ls_mem->ip_version == DA_IP6)
151     {
152       /* Remove hardware indirection (from sr_steering.c:137) */
153       ret = vnet_feature_enable_disable ("ip6-unicast", "srv6-ad6-rewrite",
154                                          ls_mem->sw_if_index_in, 0, 0, 0);
155       if (ret != 0)
156         return -1;
157
158       /* Remove local SID pointer from interface table (from sr_steering.c:139) */
159       sm->sw_iface_localsid6[ls_mem->sw_if_index_in] = ~(u32) 0;
160     }
161
162
163   /* Unlock (OIF, NHOP) adjacency (from sr_localsid.c:103) */
164   adj_unlock (ls_mem->nh_adj);
165
166   /* Delete SID entry */
167   pool_put (sm->sids, pool_elt_at_index (sm->sids, ls_mem->index));
168
169   /* Clean up local SID memory */
170   clib_mem_free (localsid->plugin_mem);
171
172   return 0;
173 }
174
175 /**********************************/
176 /* SRv6 LocalSID format functions */
177 /*
178  * Prints nicely the parameters of a localsid
179  * Example: print "Table 5"
180  */
181 u8 *
182 format_srv6_ad_localsid (u8 * s, va_list * args)
183 {
184   srv6_ad_localsid_t *ls_mem = va_arg (*args, void *);
185
186   vnet_main_t *vnm = vnet_get_main ();
187   srv6_ad_main_t *sm = &srv6_ad_main;
188
189   if (ls_mem->ip_version == DA_IP4)
190     {
191       s =
192         format (s, "Next-hop:\t%U\n", format_ip4_address,
193                 &ls_mem->nh_addr.ip4);
194     }
195   else
196     {
197       s =
198         format (s, "Next-hop:\t%U\n", format_ip6_address,
199                 &ls_mem->nh_addr.ip6);
200     }
201
202   s = format (s, "\tOutgoing iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
203               ls_mem->sw_if_index_out);
204   s = format (s, "\tIncoming iface:\t%U\n", format_vnet_sw_if_index_name, vnm,
205               ls_mem->sw_if_index_in);
206
207   vlib_counter_t valid, invalid;
208   vlib_get_combined_counter (&(sm->valid_counters), ls_mem->index, &valid);
209   vlib_get_combined_counter (&(sm->invalid_counters), ls_mem->index,
210                              &invalid);
211   s = format (s, "\tGood rewrite traffic: \t[%Ld packets : %Ld bytes]\n",
212               valid.packets, valid.bytes);
213   s = format (s, "\tBad rewrite traffic:  \t[%Ld packets : %Ld bytes]\n",
214               invalid.packets, invalid.bytes);
215
216   return s;
217 }
218
219 /*
220  * Process the parameters of a localsid
221  * Example: process from:
222  * sr localsid address cafe::1 behavior new_srv6_localsid 5
223  * everything from behavior on... so in this case 'new_srv6_localsid 5'
224  * Notice that it MUST match the keyword_str and params_str defined above.
225  */
226 uword
227 unformat_srv6_ad_localsid (unformat_input_t * input, va_list * args)
228 {
229   void **plugin_mem_p = va_arg (*args, void **);
230   srv6_ad_localsid_t *ls_mem;
231
232   vnet_main_t *vnm = vnet_get_main ();
233
234   ip46_address_t nh_addr;
235   u32 sw_if_index_out;
236   u32 sw_if_index_in;
237
238   if (unformat (input, "end.ad nh %U oif %U iif %U",
239                 unformat_ip4_address, &nh_addr.ip4,
240                 unformat_vnet_sw_interface, vnm, &sw_if_index_out,
241                 unformat_vnet_sw_interface, vnm, &sw_if_index_in))
242     {
243       /* Allocate a portion of memory */
244       ls_mem = clib_mem_alloc_aligned_at_offset (sizeof *ls_mem, 0, 0, 1);
245
246       /* Set to zero the memory */
247       memset (ls_mem, 0, sizeof *ls_mem);
248
249       /* Our brand-new car is ready */
250       ls_mem->ip_version = DA_IP4;
251       clib_memcpy (&ls_mem->nh_addr.ip4, &nh_addr.ip4,
252                    sizeof (ip4_address_t));
253       ls_mem->sw_if_index_out = sw_if_index_out;
254       ls_mem->sw_if_index_in = sw_if_index_in;
255
256       /* Dont forget to add it to the localsid */
257       *plugin_mem_p = ls_mem;
258       return 1;
259     }
260   else if (unformat (input, "end.ad nh %U oif %U iif %U",
261                      unformat_ip6_address, &nh_addr.ip6,
262                      unformat_vnet_sw_interface, vnm, &sw_if_index_out,
263                      unformat_vnet_sw_interface, vnm, &sw_if_index_in))
264     {
265       /* Allocate a portion of memory */
266       ls_mem = clib_mem_alloc_aligned_at_offset (sizeof *ls_mem, 0, 0, 1);
267
268       /* Set to zero the memory */
269       memset (ls_mem, 0, sizeof *ls_mem);
270
271       /* Our brand-new car is ready */
272       ls_mem->ip_version = DA_IP6;
273       clib_memcpy (&ls_mem->nh_addr.ip6, &nh_addr.ip6,
274                    sizeof (ip6_address_t));
275       ls_mem->sw_if_index_out = sw_if_index_out;
276       ls_mem->sw_if_index_in = sw_if_index_in;
277
278       /* Dont forget to add it to the localsid */
279       *plugin_mem_p = ls_mem;
280       return 1;
281     }
282   return 0;
283 }
284
285 /*************************/
286 /* SRv6 LocalSID FIB DPO */
287 static u8 *
288 format_srv6_ad_dpo (u8 * s, va_list * args)
289 {
290   index_t index = va_arg (*args, index_t);
291   CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
292
293   return (format (s, "SR: dynamic_proxy_index:[%u]", index));
294 }
295
296 void
297 srv6_ad_dpo_lock (dpo_id_t * dpo)
298 {
299 }
300
301 void
302 srv6_ad_dpo_unlock (dpo_id_t * dpo)
303 {
304 }
305
306 const static dpo_vft_t srv6_ad_vft = {
307   .dv_lock = srv6_ad_dpo_lock,
308   .dv_unlock = srv6_ad_dpo_unlock,
309   .dv_format = format_srv6_ad_dpo,
310 };
311
312 const static char *const srv6_ad_ip6_nodes[] = {
313   "srv6-ad-localsid",
314   NULL,
315 };
316
317 const static char *const *const srv6_ad_nodes[DPO_PROTO_NUM] = {
318   [DPO_PROTO_IP6] = srv6_ad_ip6_nodes,
319 };
320
321 /**********************/
322 static clib_error_t *
323 srv6_ad_init (vlib_main_t * vm)
324 {
325   srv6_ad_main_t *sm = &srv6_ad_main;
326   int rv = 0;
327
328   sm->vlib_main = vm;
329   sm->vnet_main = vnet_get_main ();
330
331   /* Create DPO */
332   sm->srv6_ad_dpo_type = dpo_register_new_type (&srv6_ad_vft, srv6_ad_nodes);
333
334   /* Register SRv6 LocalSID */
335   rv = sr_localsid_register_function (vm,
336                                       function_name,
337                                       keyword_str,
338                                       def_str,
339                                       params_str,
340                                       &sm->srv6_ad_dpo_type,
341                                       format_srv6_ad_localsid,
342                                       unformat_srv6_ad_localsid,
343                                       srv6_ad_localsid_creation_fn,
344                                       srv6_ad_localsid_removal_fn);
345   if (rv < 0)
346     clib_error_return (0, "SRv6 LocalSID function could not be registered.");
347   else
348     sm->srv6_localsid_behavior_id = rv;
349
350   return 0;
351 }
352
353 /* *INDENT-OFF* */
354 VNET_FEATURE_INIT (srv6_ad4_rewrite, static) =
355 {
356   .arc_name = "ip4-unicast",
357   .node_name = "srv6-ad4-rewrite",
358   .runs_before = 0,
359 };
360
361 VNET_FEATURE_INIT (srv6_ad6_rewrite, static) =
362 {
363   .arc_name = "ip6-unicast",
364   .node_name = "srv6-ad6-rewrite",
365   .runs_before = 0,
366 };
367
368 VLIB_INIT_FUNCTION (srv6_ad_init);
369
370 VLIB_PLUGIN_REGISTER () = {
371   .version = VPP_BUILD_VER,
372   .description = "Dynamic SRv6 proxy",
373 };
374 /* *INDENT-ON* */
375
376 /*
377 * fd.io coding-style-patch-verification: ON
378 *
379 * Local Variables:
380 * eval: (c-set-style "gnu")
381 * End:
382 */