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