vlib: Punt reason allocation listener enable/disable callback
[vpp.git] / src / plugins / unittest / punt_test.c
1 /*
2  * Copyright (c) 2018 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 #include <vnet/vnet.h>
17 #include <vnet/adj/rewrite.h>
18 #include <vnet/ethernet/ethernet.h>
19 #include <vnet/adj/adj.h>
20 #include <vnet/ip/ip.h>
21
22 typedef enum punt_next_t_
23 {
24   PUNT_NEXT_DROP,
25   PUNT_N_NEXT,
26 } punt_next_t;
27
28 typedef struct punt_trace_t_
29 {
30   vlib_punt_reason_t pt_reason;
31 } punt_trace_t;
32
33 #define SW_IF_INDEX_PG0 1
34 #define SW_IF_INDEX_PG1 2
35
36 index_t *adjs[FIB_PROTOCOL_IP_MAX];
37
38 static vlib_punt_reason_t punt_reason_v4, punt_reason_v6;
39 static vlib_punt_hdl_t punt_hdl;
40
41 static u8 *
42 format_punt_trace (u8 * s, va_list * args)
43 {
44   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
45   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
46   punt_trace_t *t = va_arg (*args, punt_trace_t *);
47
48   s = format (s, "punt: %U", format_vlib_punt_reason, t->pt_reason);
49
50   return s;
51 }
52
53 always_inline uword
54 punt_test_fwd (vlib_main_t * vm,
55                vlib_node_runtime_t * node,
56                vlib_frame_t * frame, fib_protocol_t fproto, u32 sw_if_index)
57 {
58   u32 n_left_from, *from, *to_next, next_index;
59
60   from = vlib_frame_vector_args (frame);
61   n_left_from = frame->n_vectors;
62   next_index = node->cached_next_index;
63
64   while (n_left_from > 0)
65     {
66       u32 n_left_to_next;
67
68       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
69
70       while (n_left_from > 0 && n_left_to_next > 0)
71         {
72           ip_adjacency_t *adj0;
73           vlib_buffer_t *b0;
74           void *ip0;
75           index_t ai0;
76           u32 bi0;
77
78           bi0 = to_next[0] = from[0];
79           from += 1;
80           to_next += 1;
81           n_left_to_next -= 1;
82           n_left_from -= 1;
83
84           b0 = vlib_get_buffer (vm, bi0);
85           vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index;
86           ai0 = adjs[fproto][sw_if_index];
87
88           adj0 = adj_get (ai0);
89           ip0 = vlib_buffer_get_current (b0);
90
91           vlib_buffer_advance (b0, -adj0->rewrite_header.data_bytes);
92           vnet_rewrite_one_header (adj0[0], ip0, sizeof (ethernet_header_t));
93
94           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
95                                            to_next, n_left_to_next, bi0, 0);
96         }
97       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
98     }
99
100   return frame->n_vectors;
101 }
102
103 always_inline uword
104 punt_test_pg0_ip4 (vlib_main_t * vm,
105                    vlib_node_runtime_t * node, vlib_frame_t * frame)
106 {
107   return (punt_test_fwd (vm, node, frame, FIB_PROTOCOL_IP4, SW_IF_INDEX_PG0));
108 }
109
110 always_inline uword
111 punt_test_pg1_ip4 (vlib_main_t * vm,
112                    vlib_node_runtime_t * node, vlib_frame_t * frame)
113 {
114   return (punt_test_fwd (vm, node, frame, FIB_PROTOCOL_IP4, SW_IF_INDEX_PG1));
115 }
116
117 always_inline uword
118 punt_test_pg0_ip6 (vlib_main_t * vm,
119                    vlib_node_runtime_t * node, vlib_frame_t * frame)
120 {
121   return (punt_test_fwd (vm, node, frame, FIB_PROTOCOL_IP6, SW_IF_INDEX_PG0));
122 }
123
124 always_inline uword
125 punt_test_pg1_ip6 (vlib_main_t * vm,
126                    vlib_node_runtime_t * node, vlib_frame_t * frame)
127 {
128   return (punt_test_fwd (vm, node, frame, FIB_PROTOCOL_IP6, SW_IF_INDEX_PG1));
129 }
130
131 /* *INDENT-OFF* */
132 VLIB_REGISTER_NODE (punt_test_pg0_ip4_node) = {
133   .function = punt_test_pg0_ip4,
134   .name = "punt-test-pg0-ip4",
135   .vector_size = sizeof (u32),
136   .format_trace = format_punt_trace,
137 };
138 VLIB_REGISTER_NODE (punt_test_pg1_ip4_node) = {
139   .function = punt_test_pg1_ip4,
140   .name = "punt-test-pg1-ip4",
141   .vector_size = sizeof (u32),
142   .format_trace = format_punt_trace,
143 };
144 VLIB_REGISTER_NODE (punt_test_pg0_ip6_node) = {
145   .function = punt_test_pg0_ip6,
146   .name = "punt-test-pg0-ip6",
147   .vector_size = sizeof (u32),
148   .format_trace = format_punt_trace,
149 };
150 VLIB_REGISTER_NODE (punt_test_pg1_ip6_node) = {
151   .function = punt_test_pg1_ip6,
152   .name = "punt-test-pg1-ip6",
153   .vector_size = sizeof (u32),
154   .format_trace = format_punt_trace,
155 };
156 /* *INDENT-ON* */
157
158 typedef struct punt_feat_trace_t_
159 {
160   vlib_punt_reason_t pt_reason;
161 } punt_feat_trace_t;
162
163 always_inline uword
164 punt_test_feat_inline (vlib_main_t * vm,
165                        vlib_node_runtime_t * node,
166                        vlib_frame_t * frame, u8 is_ip4)
167 {
168   u32 n_left_from, *from, *to_next, next_index;
169
170   from = vlib_frame_vector_args (frame);
171   n_left_from = frame->n_vectors;
172   next_index = node->cached_next_index;
173
174   while (n_left_from > 0)
175     {
176       u32 n_left_to_next;
177
178       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
179
180       while (n_left_from > 0 && n_left_to_next > 0)
181         {
182           vlib_buffer_t *b0;
183           u32 bi0, next0;
184
185           bi0 = to_next[0] = from[0];
186           from += 1;
187           to_next += 1;
188           n_left_to_next -= 1;
189           n_left_from -= 1;
190           next0 = 0;
191
192           b0 = vlib_get_buffer (vm, bi0);
193
194           if (is_ip4)
195             b0->punt_reason = punt_reason_v4;
196           else
197             b0->punt_reason = punt_reason_v6;
198
199           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
200             {
201               punt_feat_trace_t *t;
202
203               b0 = vlib_get_buffer (vm, bi0);
204
205               t = vlib_add_trace (vm, node, b0, sizeof (*t));
206               t->pt_reason = b0->punt_reason;
207             }
208           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
209                                            to_next, n_left_to_next,
210                                            bi0, next0);
211         }
212       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
213     }
214
215   return frame->n_vectors;
216 }
217
218 static u8 *
219 format_punt_feat_trace (u8 * s, va_list * args)
220 {
221   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
222   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
223   punt_feat_trace_t *t = va_arg (*args, punt_feat_trace_t *);
224
225   s = format (s, "reason: %U", format_vlib_punt_reason, t->pt_reason);
226
227   return s;
228 }
229
230 always_inline uword
231 punt_test_feat_ip4 (vlib_main_t * vm,
232                     vlib_node_runtime_t * node, vlib_frame_t * frame)
233 {
234   return (punt_test_feat_inline (vm, node, frame, 1));
235 }
236
237 always_inline uword
238 punt_test_feat_ip6 (vlib_main_t * vm,
239                     vlib_node_runtime_t * node, vlib_frame_t * frame)
240 {
241   return (punt_test_feat_inline (vm, node, frame, 0));
242 }
243
244 /* *INDENT-OFF* */
245 VLIB_REGISTER_NODE (punt_test_feat_ip6_node) = {
246   .function = punt_test_feat_ip6,
247   .name = "punt-test-feat-ip6",
248   .vector_size = sizeof (u32),
249   .format_trace = format_punt_feat_trace,
250   .n_next_nodes = 1,
251   .next_nodes = {
252     [0] = "punt-dispatch"
253   }
254 };
255 VLIB_REGISTER_NODE (punt_test_feat_ip4_node) = {
256   .function = punt_test_feat_ip4,
257   .name = "punt-test-feat-ip4",
258   .vector_size = sizeof (u32),
259   .format_trace = format_punt_feat_trace,
260   .n_next_nodes = 1,
261   .next_nodes = {
262     [0] = "punt-dispatch"
263   }
264 };
265 VNET_FEATURE_INIT (punt_test_feat_ip6_feature, static) =
266 {
267   .arc_name = "ip6-unicast",
268   .node_name = "punt-test-feat-ip6",
269 };
270 VNET_FEATURE_INIT (punt_test_feat_ip4_feature, static) =
271 {
272   .arc_name = "ip4-unicast",
273   .node_name = "punt-test-feat-ip4",
274 };
275 /* *INDENT-ON* */
276
277 static clib_error_t *
278 punt_test (vlib_main_t * vm,
279            unformat_input_t * input, vlib_cli_command_t * cmd_arg)
280 {
281   ip46_address_t ip46 = ip46_address_initializer;
282   fib_protocol_t fproto;
283   vnet_main_t *vnm;
284   u32 sw_if_index;
285   int rc;
286
287   vnm = vnet_get_main ();
288   fproto = FIB_PROTOCOL_IP4;
289
290   if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
291     {
292       vlib_node_t *from;
293
294       if (unformat (input, "%U", unformat_ip4_address, &ip46.ip4))
295         {
296           fproto = FIB_PROTOCOL_IP4;
297         }
298       else if (unformat (input, "%U", unformat_ip6_address, &ip46.ip6))
299         {
300           fproto = FIB_PROTOCOL_IP6;
301         }
302       else if (unformat (input, "clear"))
303         {
304           vnet_feature_enable_disable ("ip4-unicast",
305                                        "punt-test-feat-ip4",
306                                        sw_if_index, 0, NULL, 0);
307           vnet_feature_enable_disable ("ip6-unicast",
308                                        "punt-test-feat-ip6",
309                                        sw_if_index, 0, NULL, 0);
310           return NULL;
311         }
312       else
313         {
314           /*
315            * allocate a client and a reason
316            */
317           punt_hdl = vlib_punt_client_register ("test");
318
319           rc =
320             vlib_punt_reason_alloc (punt_hdl, "reason-v4",
321                                     NULL, NULL, &punt_reason_v4);
322           rc |=
323             vlib_punt_reason_alloc (punt_hdl, "reason-v6",
324                                     NULL, NULL, &punt_reason_v6);
325           ASSERT (!rc);
326
327           vnet_feature_enable_disable ("ip4-unicast",
328                                        "punt-test-feat-ip4",
329                                        sw_if_index, 1, NULL, 0);
330           vnet_feature_enable_disable ("ip6-unicast",
331                                        "punt-test-feat-ip6",
332                                        sw_if_index, 1, NULL, 0);
333           return NULL;
334         }
335
336       if (SW_IF_INDEX_PG0 == sw_if_index)
337         {
338           if (FIB_PROTOCOL_IP4 == fproto)
339             {
340               /*
341                * register the node that will forward the punted packet
342                */
343               vlib_punt_register (punt_hdl, punt_reason_v4,
344                                   "punt-test-pg0-ip4");
345               from = vlib_get_node_by_name (vm, (u8 *) "punt-test-pg0-ip4");
346             }
347           else
348             {
349               vlib_punt_register (punt_hdl, punt_reason_v6,
350                                   "punt-test-pg0-ip6");
351               from = vlib_get_node_by_name (vm, (u8 *) "punt-test-pg0-ip6");
352             }
353         }
354       else
355         {
356           if (FIB_PROTOCOL_IP4 == fproto)
357             {
358               vlib_punt_register (punt_hdl, punt_reason_v4,
359                                   "punt-test-pg1-ip4");
360               from = vlib_get_node_by_name (vm, (u8 *) "punt-test-pg1-ip4");
361             }
362           else
363             {
364               vlib_punt_register (punt_hdl, punt_reason_v6,
365                                   "punt-test-pg1-ip6");
366               from = vlib_get_node_by_name (vm, (u8 *) "punt-test-pg1-ip6");
367             }
368         }
369
370       vlib_node_add_next (vm, from->index,
371                           vnet_tx_node_index_for_sw_interface
372                           (vnm, sw_if_index));
373
374       vec_validate (adjs[fproto], sw_if_index);
375
376       adjs[fproto][sw_if_index] = adj_nbr_find (fproto,
377                                                 fib_proto_to_link (fproto),
378                                                 &ip46, sw_if_index);
379     }
380
381   return (NULL);
382 }
383
384 /* *INDENT-OFF* */
385 VLIB_CLI_COMMAND (test_fib_command, static) =
386 {
387   .path = "test punt",
388   .short_help = "punt unit tests - DO NOT RUN ON A LIVE SYSTEM",
389   .function = punt_test,
390 };
391 /* *INDENT-ON* */
392
393 /*
394  * fd.io coding-style-patch-verification: ON
395  *
396  * Local Variables:
397  * eval: (c-set-style "gnu")
398  * End:
399  */