sr: SRv6 Path Tracing Midpoint behaviour
[vpp.git] / src / vnet / srv6 / sr_pt.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright(c) 2022 Cisco Systems, Inc.
3  */
4
5 /**
6  * @file
7  * @brief SR Path Tracing (PT)
8  *
9  * SR PT CLI
10  *
11  */
12
13 #include <vlib/vlib.h>
14 #include <vnet/vnet.h>
15 #include <vnet/srv6/sr.h>
16 #include <vnet/ip/ip.h>
17 #include <vnet/srv6/sr_packet.h>
18 #include <vnet/ip/ip6_packet.h>
19 #include <vnet/fib/ip6_fib.h>
20 #include <vnet/dpo/dpo.h>
21 #include <vnet/adj/adj.h>
22 #include <vnet/srv6/sr_pt.h>
23
24 #include <vppinfra/error.h>
25 #include <vppinfra/elog.h>
26
27 sr_pt_main_t sr_pt_main;
28
29 void *
30 sr_pt_find_iface (u32 iface)
31 {
32   sr_pt_main_t *sr_pt = &sr_pt_main;
33   uword *p;
34
35   /* Search for the item */
36   p = mhash_get (&sr_pt->sr_pt_iface_index_hash, &iface);
37   if (p)
38     {
39       /* Retrieve sr_pt_iface */
40       return pool_elt_at_index (sr_pt->sr_pt_iface, p[0]);
41     }
42   return NULL;
43 }
44
45 int
46 sr_pt_add_iface (u32 iface, u16 id, u8 ingress_load, u8 egress_load,
47                  u8 tts_template)
48 {
49   sr_pt_main_t *sr_pt = &sr_pt_main;
50   uword *p;
51
52   sr_pt_iface_t *ls = 0;
53
54   if (iface == (u32) ~0)
55     return SR_PT_ERR_IFACE_INVALID;
56
57   /* Search for the item */
58   p = mhash_get (&sr_pt->sr_pt_iface_index_hash, &iface);
59
60   if (p)
61     return SR_PT_ERR_EXIST;
62
63   if (id > SR_PT_ID_MAX)
64     return SR_PT_ERR_ID_INVALID;
65
66   if (ingress_load > SR_PT_LOAD_MAX || egress_load > SR_PT_LOAD_MAX)
67     return SR_PT_ERR_LOAD_INVALID;
68
69   if (tts_template > SR_PT_TTS_TEMPLATE_MAX)
70     return SR_PT_ERR_TTS_TEMPLATE_INVALID;
71
72   vnet_feature_enable_disable ("ip6-output", "pt", iface, 1, 0, 0);
73
74   /* Create a new sr_pt_iface */
75   pool_get_zero (sr_pt->sr_pt_iface, ls);
76   ls->iface = iface;
77   ls->id = id;
78   ls->ingress_load = ingress_load;
79   ls->egress_load = egress_load;
80   ls->tts_template = tts_template;
81
82   /* Set hash key for searching sr_pt_iface by iface */
83   mhash_set (&sr_pt->sr_pt_iface_index_hash, &iface, ls - sr_pt->sr_pt_iface,
84              NULL);
85   return 0;
86 }
87
88 int
89 sr_pt_del_iface (u32 iface)
90 {
91   sr_pt_main_t *sr_pt = &sr_pt_main;
92   uword *p;
93
94   sr_pt_iface_t *ls = 0;
95
96   if (iface == (u32) ~0)
97     return SR_PT_ERR_IFACE_INVALID;
98
99   /* Search for the item */
100   p = mhash_get (&sr_pt->sr_pt_iface_index_hash, &iface);
101
102   if (p)
103     {
104       /* Retrieve sr_pt_iface */
105       ls = pool_elt_at_index (sr_pt->sr_pt_iface, p[0]);
106       vnet_feature_enable_disable ("ip6-output", "pt", iface, 0, 0, 0);
107       /* Delete sr_pt_iface */
108       pool_put (sr_pt->sr_pt_iface, ls);
109       mhash_unset (&sr_pt->sr_pt_iface_index_hash, &iface, NULL);
110     }
111   else
112     {
113       return SR_PT_ERR_NOENT;
114     }
115   return 0;
116 }
117
118 /**
119  * @brief "sr pt add iface" CLI function.
120  *
121  * @see sr_pt_add_iface
122  */
123 static clib_error_t *
124 sr_pt_add_iface_command_fn (vlib_main_t *vm, unformat_input_t *input,
125                             vlib_cli_command_t *cmd)
126 {
127   vnet_main_t *vnm = vnet_get_main ();
128   u32 iface = (u32) ~0;
129   u32 id = (u32) ~0;
130   u32 ingress_load = 0;
131   u32 egress_load = 0;
132   u32 tts_template = SR_PT_TTS_TEMPLATE_DEFAULT;
133
134   int rv;
135
136   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
137     {
138       if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, &iface))
139         ;
140       else if (unformat (input, "id %u", &id))
141         ;
142       else if (unformat (input, "ingress-load %u", &ingress_load))
143         ;
144       else if (unformat (input, "egress-load %u", &egress_load))
145         ;
146       else if (unformat (input, "tts-template %u", &tts_template))
147         ;
148       else
149         break;
150     }
151
152   rv = sr_pt_add_iface (iface, id, ingress_load, egress_load, tts_template);
153
154   switch (rv)
155     {
156     case 0:
157       break;
158     case SR_PT_ERR_EXIST:
159       return clib_error_return (0, "Error: Identical iface already exists.");
160     case SR_PT_ERR_IFACE_INVALID:
161       return clib_error_return (0, "Error: The iface name invalid.");
162     case SR_PT_ERR_ID_INVALID:
163       return clib_error_return (0, "Error: The iface id value invalid.");
164     case SR_PT_ERR_LOAD_INVALID:
165       return clib_error_return (
166         0, "Error: The iface ingress or egress load value invalid.");
167     case SR_PT_ERR_TTS_TEMPLATE_INVALID:
168       return clib_error_return (
169         0, "Error: The iface TTS Template value invalid.");
170     default:
171       return clib_error_return (0, "Error: unknown error.");
172     }
173   return 0;
174 }
175
176 /**
177  * @brief "sr pt del iface" CLI function.
178  *
179  * @see sr_pt_del_iface
180  */
181 static clib_error_t *
182 sr_pt_del_iface_command_fn (vlib_main_t *vm, unformat_input_t *input,
183                             vlib_cli_command_t *cmd)
184 {
185   vnet_main_t *vnm = vnet_get_main ();
186   u32 iface = (u32) ~0;
187
188   int rv;
189
190   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
191     {
192       if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, &iface))
193         ;
194       else
195         break;
196     }
197
198   rv = sr_pt_del_iface (iface);
199
200   switch (rv)
201     {
202     case 0:
203       break;
204     case SR_PT_ERR_NOENT:
205       return clib_error_return (0, "Error: No such iface.");
206     case SR_PT_ERR_IFACE_INVALID:
207       return clib_error_return (0, "Error: The iface name is not valid.");
208     default:
209       return clib_error_return (0, "Error: unknown error.");
210     }
211   return 0;
212 }
213
214 /**
215  * @brief CLI function to show all SR PT interfcaes
216  */
217 static clib_error_t *
218 sr_pt_show_iface_command_fn (vlib_main_t *vm, unformat_input_t *input,
219                              vlib_cli_command_t *cmd)
220 {
221   vnet_main_t *vnm = vnet_get_main ();
222   sr_pt_main_t *sr_pt = &sr_pt_main;
223   sr_pt_iface_t **sr_pt_iface_list = 0;
224   sr_pt_iface_t *ls;
225   int i;
226
227   vlib_cli_output (vm, "SR PT Interfaces");
228   vlib_cli_output (vm, "==================================");
229
230   pool_foreach (ls, sr_pt->sr_pt_iface)
231     {
232       vec_add1 (sr_pt_iface_list, ls);
233     };
234
235   for (i = 0; i < vec_len (sr_pt_iface_list); i++)
236     {
237       ls = sr_pt_iface_list[i];
238       vlib_cli_output (
239         vm,
240         "\tiface       : \t%U\n\tid          : \t%d\n\tingress-load: "
241         "\t%d\n\tegress-load : \t%d\n\ttts-template: \t%d  ",
242         format_vnet_sw_if_index_name, vnm, ls->iface, ls->id, ls->ingress_load,
243         ls->egress_load, ls->tts_template);
244       vlib_cli_output (vm, "--------------------------------");
245     }
246
247   return 0;
248 }
249
250 VLIB_CLI_COMMAND (sr_pt_add_iface_command, static) = {
251   .path = "sr pt add iface",
252   .short_help = "sr pt add iface <iface-name> id <pt-iface-id> ingress-load "
253                 "<ingress-load-value> egress-load <egress-load-value> "
254                 "tts-template <tts-template-value>",
255   .function = sr_pt_add_iface_command_fn,
256 };
257
258 VLIB_CLI_COMMAND (sr_pt_del_iface_command, static) = {
259   .path = "sr pt del iface",
260   .short_help = "sr pt del iface <iface-name>",
261   .function = sr_pt_del_iface_command_fn,
262 };
263
264 VLIB_CLI_COMMAND (sr_pt_show_iface_command, static) = {
265   .path = "sr pt show iface",
266   .short_help = "sr pt show iface",
267   .function = sr_pt_show_iface_command_fn,
268 };
269
270 /**
271  *  * @brief SR PT initialization
272  *   */
273 clib_error_t *
274 sr_pt_init (vlib_main_t *vm)
275 {
276   sr_pt_main_t *pt = &sr_pt_main;
277   mhash_init (&pt->sr_pt_iface_index_hash, sizeof (uword), sizeof (u32));
278   return 0;
279 }
280
281 VLIB_INIT_FUNCTION (sr_pt_init);