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