Initial commit of vpp code.
[vpp.git] / vnet / vnet / l2 / l2_output_acl.c
1 /*
2  * l2_output_acl.c : layer 2 output acl processing
3  *
4  * Copyright (c) 2013 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 <vlib/vlib.h>
19 #include <vnet/vnet.h>
20 #include <vnet/pg/pg.h>
21 #include <vnet/ethernet/ethernet.h>
22 #include <vnet/ethernet/packet.h>
23 #include <vnet/ip/ip_packet.h>
24 #include <vnet/ip/ip4_packet.h>
25 #include <vnet/ip/ip6_packet.h>
26 #include <vlib/cli.h>
27 #include <vnet/l2/feat_bitmap.h>
28 #include <vnet/l2/l2_output.h>
29
30 #include <vppinfra/error.h>
31 #include <vppinfra/hash.h>
32 #include <vppinfra/cache.h>
33
34
35 typedef struct {
36   // Next nodes for features and output interfaces
37   l2_output_next_nodes_st next_nodes;
38
39   /* convenience variables */
40   vlib_main_t * vlib_main;
41   vnet_main_t * vnet_main;
42 } l2_outacl_main_t;
43
44
45
46 typedef struct {
47   /* per-pkt trace data */ 
48   u8 src[6];
49   u8 dst[6];
50   u32 next_index;
51   u32 sw_if_index;
52 } l2_outacl_trace_t;
53
54 /* packet trace format function */
55 static u8 * format_l2_outacl_trace (u8 * s, va_list * args)
56 {
57   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
58   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
59   l2_outacl_trace_t * t = va_arg (*args, l2_outacl_trace_t *);
60   
61   s = format (s, "l2-output-acl: sw_if_index %d dst %U src %U",
62               t->sw_if_index,
63               format_ethernet_address, t->dst,
64               format_ethernet_address, t->src);
65   return s;
66 }
67
68 l2_outacl_main_t l2_outacl_main;
69
70 static vlib_node_registration_t l2_outacl_node;
71
72 #define foreach_l2_outacl_error                 \
73 _(L2_OUTACL,    "L2 output ACL packets")        \
74 _(DROP,         "L2 output drops")
75
76 typedef enum {
77 #define _(sym,str) L2_OUTACL_ERROR_##sym,
78   foreach_l2_outacl_error
79 #undef _
80   L2_OUTACL_N_ERROR,
81 } l2_outacl_error_t;
82
83 static char * l2_outacl_error_strings[] = {
84 #define _(sym,string) string,
85   foreach_l2_outacl_error
86 #undef _
87 };
88
89 typedef enum {  
90   L2_OUTACL_NEXT_DROP,
91   L2_OUTACL_N_NEXT,
92 } l2_outacl_next_t;
93
94
95
96 static uword
97 l2_outacl_node_fn (vlib_main_t * vm,
98                   vlib_node_runtime_t * node,
99                   vlib_frame_t * frame)
100 {
101   u32 n_left_from, * from, * to_next;
102   l2_outacl_next_t next_index;
103   l2_outacl_main_t * msm = &l2_outacl_main;
104   vlib_node_t *n = vlib_get_node (vm, l2_outacl_node.index);
105   u32 node_counter_base_index = n->error_heap_index;
106   vlib_error_main_t * em = &vm->error_main;
107   u32 cached_sw_if_index = (u32)~0;
108   u32 cached_next_index = (u32)~0;
109
110   from = vlib_frame_vector_args (frame);
111   n_left_from = frame->n_vectors; /* number of packets to process */
112   next_index = node->cached_next_index;
113
114   while (n_left_from > 0)
115     {
116       u32 n_left_to_next;
117
118       /* get space to enqueue frame to graph node "next_index" */
119       vlib_get_next_frame (vm, node, next_index,
120                            to_next, n_left_to_next);
121
122       while (0 && n_left_from >= 4 && n_left_to_next >= 2)
123         {
124           u32 bi0, bi1;
125           vlib_buffer_t * b0, * b1;
126           u32 next0, next1;
127           u32 sw_if_index0, sw_if_index1;
128           ethernet_header_t * h0, * h1;
129           
130           /* Prefetch next iteration. */
131           {
132             vlib_buffer_t * p2, * p3;
133             
134             p2 = vlib_get_buffer (vm, from[2]);
135             p3 = vlib_get_buffer (vm, from[3]);
136             
137             vlib_prefetch_buffer_header (p2, LOAD);
138             vlib_prefetch_buffer_header (p3, LOAD);
139
140             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
141             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
142           }
143
144           /* speculatively enqueue b0 and b1 to the current next frame */
145           /* bi is "buffer index", b is pointer to the buffer */
146           to_next[0] = bi0 = from[0];
147           to_next[1] = bi1 = from[1];
148           from += 2;
149           to_next += 2;
150           n_left_from -= 2;
151           n_left_to_next -= 2;
152
153           b0 = vlib_get_buffer (vm, bi0);
154           b1 = vlib_get_buffer (vm, bi1);
155  
156           /* TX interface handles */
157           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX];
158           sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_TX];
159
160           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
161             {
162               if (b0->flags & VLIB_BUFFER_IS_TRACED) 
163                 {
164                     l2_outacl_trace_t *t = 
165                       vlib_add_trace (vm, node, b0, sizeof (*t));
166                     t->sw_if_index = sw_if_index0;
167                     t->next_index = next0;
168                     memcpy(t->src, h0->src_address, 6);
169                     memcpy(t->dst, h0->dst_address, 6);
170                   }
171                 if (b1->flags & VLIB_BUFFER_IS_TRACED) 
172                   {
173                     l2_outacl_trace_t *t = 
174                       vlib_add_trace (vm, node, b1, sizeof (*t));
175                     t->sw_if_index = sw_if_index1;
176                     t->next_index = next1;
177                     memcpy(t->src, h1->src_address, 6);
178                     memcpy(t->dst, h1->dst_address, 6);
179                   }
180               }
181
182             em->counters[node_counter_base_index + L2_OUTACL_ERROR_L2_OUTACL] += 2;
183
184             /* add core loop code here */
185
186             /* verify speculative enqueues, maybe switch current next frame */
187             /* if next0==next1==next_index then nothing special needs to be done */
188             vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
189                                              to_next, n_left_to_next,
190                                              bi0, bi1, next0, next1);
191         }
192       
193       while (n_left_from > 0 && n_left_to_next > 0)
194         {
195           u32 bi0;
196           vlib_buffer_t * b0;
197           u32 next0;
198           u32 sw_if_index0;
199           ethernet_header_t * h0;
200           u32 feature_bitmap0;
201
202           /* speculatively enqueue b0 to the current next frame */
203           bi0 = from[0];
204           to_next[0] = bi0;
205           from += 1;
206           to_next += 1;
207           n_left_from -= 1;
208           n_left_to_next -= 1;
209
210           b0 = vlib_get_buffer (vm, bi0);
211           h0 = vlib_buffer_get_current (b0);
212
213           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX];
214
215           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
216                             && (b0->flags & VLIB_BUFFER_IS_TRACED))) {
217             l2_outacl_trace_t *t = 
218                vlib_add_trace (vm, node, b0, sizeof (*t));
219             t->sw_if_index = sw_if_index0;
220             memcpy(t->src, h0->src_address, 6);
221             memcpy(t->dst, h0->dst_address, 6);
222             }
223
224           em->counters[node_counter_base_index + L2_OUTACL_ERROR_L2_OUTACL] += 1;
225
226           // L2_OUTACL code
227           // Dummy for now, just go to next feature node
228  
229
230           // Remove ourself from the feature bitmap
231           feature_bitmap0 = vnet_buffer(b0)->l2.feature_bitmap & ~L2OUTPUT_FEAT_ACL;
232
233           // Determine next node
234           l2_output_dispatch (msm->vlib_main,
235                               msm->vnet_main,
236                               node,
237                               l2_outacl_node.index,
238                               &cached_sw_if_index,
239                               &cached_next_index,
240                               &msm->next_nodes,
241                               b0,
242                               sw_if_index0,
243                               feature_bitmap0,
244                               &next0);
245
246           /* verify speculative enqueue, maybe switch current next frame */
247           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
248                                            to_next, n_left_to_next,
249                                            bi0, next0);
250         }
251
252       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
253     }
254
255   return frame->n_vectors;
256 }
257
258
259 VLIB_REGISTER_NODE (l2_outacl_node,static) = {
260   .function = l2_outacl_node_fn,
261   .name = "l2-output-acl",
262   .vector_size = sizeof (u32),
263   .format_trace = format_l2_outacl_trace,
264   .type = VLIB_NODE_TYPE_INTERNAL,
265   
266   .n_errors = ARRAY_LEN(l2_outacl_error_strings),
267   .error_strings = l2_outacl_error_strings,
268
269   .n_next_nodes = L2_OUTACL_N_NEXT,
270
271   /* edit / add dispositions here */
272   .next_nodes = {
273        [L2_OUTACL_NEXT_DROP]  = "error-drop",
274   },
275 };
276
277 clib_error_t *l2_outacl_init (vlib_main_t *vm)
278 {
279   l2_outacl_main_t * mp = &l2_outacl_main;
280  
281   mp->vlib_main = vm;
282   mp->vnet_main = vnet_get_main();
283
284   // Initialize the feature next-node indexes
285   feat_bitmap_init_next_nodes(vm,
286                               l2_outacl_node.index,
287                               L2OUTPUT_N_FEAT,
288                               l2output_get_feat_names(),
289                               mp->next_nodes.feat_next_node_index);
290
291   // Initialize the output node mapping table
292   l2output_init_output_node_vec(&mp->next_nodes.output_node_index_vec);
293
294   return 0;
295 }
296
297 VLIB_INIT_FUNCTION (l2_outacl_init);
298
299 // set subinterface outacl enable/disable
300 // The CLI format is:
301 //    set interface acl output <interface> [disable]
302 static clib_error_t *
303 int_l2_outacl (vlib_main_t * vm,
304                unformat_input_t * input,
305                vlib_cli_command_t * cmd)
306 {
307   vnet_main_t * vnm = vnet_get_main();
308   clib_error_t * error = 0;
309   u32 sw_if_index;
310   u32 enable;
311
312   if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
313     {
314       error = clib_error_return (0, "unknown interface `%U'",
315                                  format_unformat_error, input);
316       goto done;
317     }
318
319   enable = 1;
320   if (unformat (input, "disable")) {
321     enable = 0;
322   }
323
324   // set the interface flag
325   l2output_intf_bitmap_enable(sw_if_index, L2OUTPUT_FEAT_ACL, enable);
326
327  done:
328   return error;
329 }
330
331 VLIB_CLI_COMMAND (int_l2_outacl_cli, static) = {
332   .path = "set interface acl output",
333   .short_help = "set interface acl output <interface> [disable]",
334   .function = int_l2_outacl,
335 };