GBP: use sclass in the DP for policy
[vpp.git] / src / plugins / gbp / gbp_classify_node.c
1 /*
2  * gbp.h : Group Based Policy
3  *
4  * Copyright (c) 2018 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <plugins/gbp/gbp.h>
19 #include <plugins/gbp/gbp_classify.h>
20 #include <plugins/gbp/gbp_policy_dpo.h>
21 #include <plugins/gbp/gbp_ext_itf.h>
22 #include <vnet/fib/ip4_fib.h>
23 #include <vnet/fib/ip6_fib.h>
24 #include <vnet/dpo/load_balance.h>
25 #include <vnet/l2/l2_input.h>
26 #include <vnet/l2/feat_bitmap.h>
27 #include <vnet/fib/fib_table.h>
28 #include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
29
30 /**
31  * per-packet trace data
32  */
33 typedef struct gbp_classify_trace_t_
34 {
35   /* per-pkt trace data */
36   sclass_t sclass;
37 } gbp_classify_trace_t;
38
39 /*
40  * determine the SRC EPG form the input port
41  */
42 always_inline uword
43 gbp_classify_inline (vlib_main_t * vm,
44                      vlib_node_runtime_t * node,
45                      vlib_frame_t * frame,
46                      gbp_src_classify_type_t type, dpo_proto_t dproto)
47 {
48   gbp_src_classify_main_t *gscm = &gbp_src_classify_main;
49   u32 n_left_from, *from, *to_next;
50   u32 next_index;
51
52   next_index = 0;
53   n_left_from = frame->n_vectors;
54   from = vlib_frame_vector_args (frame);
55
56   while (n_left_from > 0)
57     {
58       u32 n_left_to_next;
59
60       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
61
62       while (n_left_from > 0 && n_left_to_next > 0)
63         {
64           u32 next0, bi0, sw_if_index0;
65           const gbp_endpoint_t *ge0;
66           vlib_buffer_t *b0;
67           sclass_t sclass0;
68
69           bi0 = from[0];
70           to_next[0] = bi0;
71           from += 1;
72           to_next += 1;
73           n_left_from -= 1;
74           n_left_to_next -= 1;
75
76           b0 = vlib_get_buffer (vm, bi0);
77
78           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
79           vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_NONE;
80
81           if (GBP_SRC_CLASSIFY_NULL == type)
82             {
83               sclass0 = SCLASS_INVALID;
84               next0 =
85                 vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type],
86                                       L2INPUT_FEAT_GBP_NULL_CLASSIFY);
87             }
88           else
89             {
90               if (DPO_PROTO_ETHERNET == dproto)
91                 {
92                   const ethernet_header_t *h0;
93
94                   h0 = vlib_buffer_get_current (b0);
95                   next0 =
96                     vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type],
97                                           L2INPUT_FEAT_GBP_SRC_CLASSIFY);
98                   ge0 = gbp_endpoint_find_mac (h0->src_address,
99                                                vnet_buffer (b0)->l2.bd_index);
100                 }
101               else if (DPO_PROTO_IP4 == dproto)
102                 {
103                   const ip4_header_t *h0;
104
105                   h0 = vlib_buffer_get_current (b0);
106
107                   ge0 = gbp_endpoint_find_ip4
108                     (&h0->src_address,
109                      fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
110                                                           sw_if_index0));
111
112
113                   /*
114                    * Go straight to looukp, do not pass go, do not collect $200
115                    */
116                   next0 = 0;
117                 }
118               else if (DPO_PROTO_IP6 == dproto)
119                 {
120                   const ip6_header_t *h0;
121
122                   h0 = vlib_buffer_get_current (b0);
123
124                   ge0 = gbp_endpoint_find_ip6
125                     (&h0->src_address,
126                      fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
127                                                           sw_if_index0));
128
129
130                   /*
131                    * Go straight to lookup, do not pass go, do not collect $200
132                    */
133                   next0 = 0;
134                 }
135               else
136                 {
137                   ge0 = NULL;
138                   next0 = 0;
139                   ASSERT (0);
140                 }
141
142               if (PREDICT_TRUE (NULL != ge0))
143                 sclass0 = ge0->ge_fwd.gef_sclass;
144               else
145                 sclass0 = SCLASS_INVALID;
146             }
147
148           vnet_buffer2 (b0)->gbp.sclass = sclass0;
149
150           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
151             {
152               gbp_classify_trace_t *t =
153                 vlib_add_trace (vm, node, b0, sizeof (*t));
154               t->sclass = sclass0;
155             }
156
157           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
158                                            to_next, n_left_to_next,
159                                            bi0, next0);
160         }
161
162       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
163     }
164
165   return frame->n_vectors;
166 }
167
168 VLIB_NODE_FN (gbp_src_classify_node) (vlib_main_t * vm,
169                                       vlib_node_runtime_t * node,
170                                       vlib_frame_t * frame)
171 {
172   return (gbp_classify_inline (vm, node, frame,
173                                GBP_SRC_CLASSIFY_PORT, DPO_PROTO_ETHERNET));
174 }
175
176 VLIB_NODE_FN (gbp_null_classify_node) (vlib_main_t * vm,
177                                        vlib_node_runtime_t * node,
178                                        vlib_frame_t * frame)
179 {
180   return (gbp_classify_inline (vm, node, frame,
181                                GBP_SRC_CLASSIFY_NULL, DPO_PROTO_ETHERNET));
182 }
183
184 VLIB_NODE_FN (gbp_ip4_src_classify_node) (vlib_main_t * vm,
185                                           vlib_node_runtime_t * node,
186                                           vlib_frame_t * frame)
187 {
188   return (gbp_classify_inline (vm, node, frame,
189                                GBP_SRC_CLASSIFY_PORT, DPO_PROTO_IP4));
190 }
191
192 VLIB_NODE_FN (gbp_ip6_src_classify_node) (vlib_main_t * vm,
193                                           vlib_node_runtime_t * node,
194                                           vlib_frame_t * frame)
195 {
196   return (gbp_classify_inline (vm, node, frame,
197                                GBP_SRC_CLASSIFY_PORT, DPO_PROTO_IP6));
198 }
199
200
201 /* packet trace format function */
202 static u8 *
203 format_gbp_classify_trace (u8 * s, va_list * args)
204 {
205   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
206   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
207   gbp_classify_trace_t *t = va_arg (*args, gbp_classify_trace_t *);
208
209   s = format (s, "sclass:%d", t->sclass);
210
211   return s;
212 }
213
214 /* *INDENT-OFF* */
215 VLIB_REGISTER_NODE (gbp_null_classify_node) = {
216   .name = "gbp-null-classify",
217   .vector_size = sizeof (u32),
218   .format_trace = format_gbp_classify_trace,
219   .type = VLIB_NODE_TYPE_INTERNAL,
220
221   .n_errors = 0,
222   .n_next_nodes = 0,
223 };
224
225 VLIB_REGISTER_NODE (gbp_src_classify_node) = {
226   .name = "gbp-src-classify",
227   .vector_size = sizeof (u32),
228   .format_trace = format_gbp_classify_trace,
229   .type = VLIB_NODE_TYPE_INTERNAL,
230
231   .n_errors = 0,
232   .n_next_nodes = 0,
233 };
234
235 VLIB_REGISTER_NODE (gbp_ip4_src_classify_node) = {
236   .name = "ip4-gbp-src-classify",
237   .vector_size = sizeof (u32),
238   .format_trace = format_gbp_classify_trace,
239   .type = VLIB_NODE_TYPE_INTERNAL,
240
241   .n_errors = 0,
242   .n_next_nodes = 1,
243   .next_nodes = {
244     [0] = "ip4-lookup"
245   },
246 };
247
248 VLIB_REGISTER_NODE (gbp_ip6_src_classify_node) = {
249   .name = "ip6-gbp-src-classify",
250   .vector_size = sizeof (u32),
251   .format_trace = format_gbp_classify_trace,
252   .type = VLIB_NODE_TYPE_INTERNAL,
253
254   .n_errors = 0,
255   .n_next_nodes = 1,
256   .next_nodes = {
257     [0] = "ip6-lookup"
258   },
259 };
260
261 VNET_FEATURE_INIT (gbp_ip4_src_classify_feat_node, static) =
262 {
263   .arc_name = "ip4-unicast",
264   .node_name = "ip4-gbp-src-classify",
265   .runs_before = VNET_FEATURES ("nat44-out2in"),
266 };
267 VNET_FEATURE_INIT (gbp_ip6_src_classify_feat_node, static) =
268 {
269   .arc_name = "ip6-unicast",
270   .node_name = "ip6-gbp-src-classify",
271   .runs_before = VNET_FEATURES ("nat66-out2in"),
272 };
273
274 /* *INDENT-ON* */
275
276 typedef enum gbp_lpm_classify_next_t_
277 {
278   GPB_LPM_CLASSIFY_DROP,
279 } gbp_lpm_classify_next_t;
280
281 always_inline dpo_proto_t
282 ethertype_to_dpo_proto (const ethernet_header_t * eh0)
283 {
284   u16 etype = clib_net_to_host_u16 (eh0->type);
285
286   switch (etype)
287     {
288     case ETHERNET_TYPE_IP4:
289       return (DPO_PROTO_IP4);
290     case ETHERNET_TYPE_IP6:
291       return (DPO_PROTO_IP6);
292     case ETHERNET_TYPE_VLAN:
293       {
294         ethernet_vlan_header_t *vh0;
295
296         vh0 = (ethernet_vlan_header_t *) (eh0 + 1);
297
298         switch (clib_net_to_host_u16 (vh0->type))
299           {
300           case ETHERNET_TYPE_IP4:
301             return (DPO_PROTO_IP4);
302           case ETHERNET_TYPE_IP6:
303             return (DPO_PROTO_IP6);
304           }
305       }
306     }
307
308   return (DPO_PROTO_NONE);
309 }
310
311 /*
312  * Determine the SRC EPG from a LPM
313  */
314 always_inline uword
315 gbp_lpm_classify_inline (vlib_main_t * vm,
316                          vlib_node_runtime_t * node,
317                          vlib_frame_t * frame,
318                          dpo_proto_t dproto, u8 is_recirc)
319 {
320   gbp_src_classify_main_t *gscm = &gbp_src_classify_main;
321   u32 n_left_from, *from, *to_next;
322   u32 next_index;
323
324   next_index = 0;
325   n_left_from = frame->n_vectors;
326   from = vlib_frame_vector_args (frame);
327
328   while (n_left_from > 0)
329     {
330       u32 n_left_to_next;
331
332       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
333
334       while (n_left_from > 0 && n_left_to_next > 0)
335         {
336           u32 bi0, sw_if_index0, fib_index0, lbi0;
337           gbp_lpm_classify_next_t next0;
338           const gbp_policy_dpo_t *gpd0;
339           const gbp_ext_itf_t *gx0;
340           const gbp_recirc_t *gr0;
341           const dpo_id_t *dpo0;
342           load_balance_t *lb0;
343           ip4_header_t *ip4_0;
344           ip6_header_t *ip6_0;
345           vlib_buffer_t *b0;
346           sclass_t sclass0;
347
348           bi0 = from[0];
349           to_next[0] = bi0;
350           from += 1;
351           to_next += 1;
352           n_left_from -= 1;
353           n_left_to_next -= 1;
354           ip4_0 = NULL;
355           ip6_0 = NULL;
356           next0 = GPB_LPM_CLASSIFY_DROP;
357
358           b0 = vlib_get_buffer (vm, bi0);
359
360           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
361           vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_NONE;
362
363           if (DPO_PROTO_IP4 == dproto)
364             ip4_0 = vlib_buffer_get_current (b0);
365           else if (DPO_PROTO_IP6 == dproto)
366             ip6_0 = vlib_buffer_get_current (b0);
367           else if (DPO_PROTO_ETHERNET == dproto)
368             {
369               const ethernet_header_t *eh0;
370
371               eh0 = vlib_buffer_get_current (b0);
372
373               dproto = ethertype_to_dpo_proto (eh0);
374
375               switch (dproto)
376                 {
377                 case DPO_PROTO_IP4:
378                   ip4_0 = (vlib_buffer_get_current (b0) +
379                            vnet_buffer (b0)->l2.l2_len);
380                   break;
381                 case DPO_PROTO_IP6:
382                   ip6_0 = (vlib_buffer_get_current (b0) +
383                            vnet_buffer (b0)->l2.l2_len);
384                   break;
385                 default:
386                   /* not IP so no LPM classify possible */
387                   sclass0 = SCLASS_INVALID;
388                   goto trace;
389                 }
390             }
391
392           if (is_recirc)
393             {
394               gr0 = gbp_recirc_get (sw_if_index0);
395               fib_index0 = gr0->gr_fib_index[dproto];
396
397               vnet_feature_next (&next0, b0);
398             }
399           else
400             {
401               gx0 = gbp_ext_itf_get (sw_if_index0);
402               fib_index0 = gx0->gx_fib_index[dproto];
403
404               next0 = vnet_l2_feature_next
405                 (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM],
406                  L2INPUT_FEAT_GBP_LPM_CLASSIFY);
407             }
408
409           if (DPO_PROTO_IP4 == dproto)
410             {
411               lbi0 = ip4_fib_forwarding_lookup (fib_index0,
412                                                 &ip4_0->src_address);
413             }
414           else if (DPO_PROTO_IP6 == dproto)
415             {
416               lbi0 = ip6_fib_table_fwding_lookup (&ip6_main, fib_index0,
417                                                   &ip6_0->src_address);
418             }
419           else
420             {
421               /* not IP so no LPM classify possible */
422               sclass0 = SCLASS_INVALID;
423               goto trace;
424             }
425           lb0 = load_balance_get (lbi0);
426           dpo0 = load_balance_get_bucket_i (lb0, 0);
427
428           if (gbp_policy_dpo_type == dpo0->dpoi_type)
429             {
430               gpd0 = gbp_policy_dpo_get (dpo0->dpoi_index);
431               sclass0 = gpd0->gpd_sclass;
432             }
433           else
434             {
435               /* could not classify => drop */
436               sclass0 = SCLASS_INVALID;
437               next0 = GPB_LPM_CLASSIFY_DROP;
438             }
439
440         trace:
441           vnet_buffer2 (b0)->gbp.sclass = sclass0;
442
443           if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
444             {
445               gbp_classify_trace_t *t =
446                 vlib_add_trace (vm, node, b0, sizeof (*t));
447               t->sclass = sclass0;
448             }
449
450           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
451                                            to_next, n_left_to_next,
452                                            bi0, next0);
453         }
454
455       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
456     }
457
458   return frame->n_vectors;
459 }
460
461 VLIB_NODE_FN (gbp_ip4_lpm_classify_node) (vlib_main_t * vm,
462                                           vlib_node_runtime_t * node,
463                                           vlib_frame_t * frame)
464 {
465   return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_IP4, 1));
466 }
467
468 VLIB_NODE_FN (gbp_ip6_lpm_classify_node) (vlib_main_t * vm,
469                                           vlib_node_runtime_t * node,
470                                           vlib_frame_t * frame)
471 {
472   return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_IP6, 1));
473 }
474
475 VLIB_NODE_FN (gbp_l2_lpm_classify_node) (vlib_main_t * vm,
476                                          vlib_node_runtime_t * node,
477                                          vlib_frame_t * frame)
478 {
479   return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_ETHERNET, 0));
480 }
481
482 /* *INDENT-OFF* */
483 VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = {
484   .name = "ip4-gbp-lpm-classify",
485   .vector_size = sizeof (u32),
486   .format_trace = format_gbp_classify_trace,
487   .type = VLIB_NODE_TYPE_INTERNAL,
488
489   .n_errors = 0,
490   .n_next_nodes = 1,
491   .next_nodes = {
492     [GPB_LPM_CLASSIFY_DROP] = "ip4-drop"
493   },
494 };
495
496 VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = {
497   .name = "ip6-gbp-lpm-classify",
498   .vector_size = sizeof (u32),
499   .format_trace = format_gbp_classify_trace,
500   .type = VLIB_NODE_TYPE_INTERNAL,
501
502   .n_errors = 0,
503   .n_next_nodes = 1,
504   .next_nodes = {
505     [GPB_LPM_CLASSIFY_DROP] = "ip6-drop"
506   },
507 };
508
509 VLIB_REGISTER_NODE (gbp_l2_lpm_classify_node) = {
510   .name = "l2-gbp-lpm-classify",
511   .vector_size = sizeof (u32),
512   .format_trace = format_gbp_classify_trace,
513   .type = VLIB_NODE_TYPE_INTERNAL,
514
515   .n_errors = 0,
516   .n_next_nodes = 1,
517   .next_nodes = {
518     [GPB_LPM_CLASSIFY_DROP] = "error-drop"
519   },
520 };
521
522 VNET_FEATURE_INIT (gbp_ip4_lpm_classify_feat_node, static) =
523 {
524   .arc_name = "ip4-unicast",
525   .node_name = "ip4-gbp-lpm-classify",
526   .runs_before = VNET_FEATURES ("nat44-out2in"),
527 };
528 VNET_FEATURE_INIT (gbp_ip6_lpm_classify_feat_node, static) =
529 {
530   .arc_name = "ip6-unicast",
531   .node_name = "ip6-gbp-lpm-classify",
532   .runs_before = VNET_FEATURES ("nat66-out2in"),
533 };
534
535 /* *INDENT-ON* */
536
537 /*
538  * fd.io coding-style-patch-verification: ON
539  *
540  * Local Variables:
541  * eval: (c-set-style "gnu")
542  * End:
543  */