Initial commit of vpp code.
[vpp.git] / vnet / vnet / llc / node.c
1 /*
2  * Copyright (c) 2015 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  * llc_node.c: llc packet processing
17  *
18  * Copyright (c) 2010 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #include <vlib/vlib.h>
41 #include <vnet/pg/pg.h>
42 #include <vnet/llc/llc.h>
43
44 #define foreach_llc_input_next                  \
45   _ (PUNT, "error-punt")                        \
46   _ (DROP, "error-drop")
47
48 typedef enum {
49 #define _(s,n) LLC_INPUT_NEXT_##s,
50   foreach_llc_input_next
51 #undef _
52   LLC_INPUT_N_NEXT,
53 } llc_input_next_t;
54
55 typedef struct {
56   u8 packet_data[32];
57 } llc_input_trace_t;
58
59 static u8 * format_llc_input_trace (u8 * s, va_list * va)
60 {
61   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
62   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
63   llc_input_trace_t * t = va_arg (*va, llc_input_trace_t *);
64
65   s = format (s, "%U", format_llc_header, t->packet_data);
66
67   return s;
68 }
69
70 static uword
71 llc_input (vlib_main_t * vm,
72            vlib_node_runtime_t * node,
73            vlib_frame_t * from_frame)
74 {
75   llc_main_t * lm = &llc_main;
76   u32 n_left_from, next_index, * from, * to_next;
77
78   from = vlib_frame_vector_args (from_frame);
79   n_left_from = from_frame->n_vectors;
80
81   if (node->flags & VLIB_NODE_FLAG_TRACE)
82     vlib_trace_frame_buffers_only (vm, node,
83                                    from,
84                                    n_left_from,
85                                    sizeof (from[0]),
86                                    sizeof (llc_input_trace_t));
87
88   next_index = node->cached_next_index;
89
90   while (n_left_from > 0)
91     {
92       u32 n_left_to_next;
93
94       vlib_get_next_frame (vm, node, next_index,
95                            to_next, n_left_to_next);
96
97       while (n_left_from >= 4 && n_left_to_next >= 2)
98         {
99           u32 bi0, bi1;
100           vlib_buffer_t * b0, * b1;
101           llc_header_t * h0, * h1;
102           u8 next0, next1, len0, len1, enqueue_code;
103
104           /* Prefetch next iteration. */
105           {
106             vlib_buffer_t * b2, * b3;
107
108             b2 = vlib_get_buffer (vm, from[2]);
109             b3 = vlib_get_buffer (vm, from[3]);
110
111             vlib_prefetch_buffer_header (b2, LOAD);
112             vlib_prefetch_buffer_header (b3, LOAD);
113
114             CLIB_PREFETCH (b2->data, sizeof (h0[0]), LOAD);
115             CLIB_PREFETCH (b3->data, sizeof (h1[0]), LOAD);
116           }
117
118           bi0 = from[0];
119           bi1 = from[1];
120           to_next[0] = bi0;
121           to_next[1] = bi1;
122           from += 2;
123           to_next += 2;
124           n_left_to_next -= 2;
125           n_left_from -= 2;
126
127           b0 = vlib_get_buffer (vm, bi0);
128           b1 = vlib_get_buffer (vm, bi1);
129
130           h0 = (void *) (b0->data + b0->current_data);
131           h1 = (void *) (b1->data + b1->current_data);
132
133           len0 = llc_header_length (h0);
134           len1 = llc_header_length (h1);
135
136           b0->current_data += len0;
137           b1->current_data += len1;
138
139           b0->current_length -= len0;
140           b1->current_length -= len1;
141
142           next0 = lm->input_next_by_protocol[h0->dst_sap];
143           next1 = lm->input_next_by_protocol[h1->dst_sap];
144
145           b0->error = node->errors[next0 == LLC_INPUT_NEXT_DROP ? LLC_ERROR_UNKNOWN_PROTOCOL : LLC_ERROR_NONE];
146           b1->error = node->errors[next1 == LLC_INPUT_NEXT_DROP ? LLC_ERROR_UNKNOWN_PROTOCOL : LLC_ERROR_NONE];
147
148           enqueue_code = (next0 != next_index) + 2*(next1 != next_index);
149
150           if (PREDICT_FALSE (enqueue_code != 0))
151             {
152               switch (enqueue_code)
153                 {
154                 case 1:
155                   /* A B A */
156                   to_next[-2] = bi1;
157                   to_next -= 1;
158                   n_left_to_next += 1;
159                   vlib_set_next_frame_buffer (vm, node, next0, bi0);
160                   break;
161
162                 case 2:
163                   /* A A B */
164                   to_next -= 1;
165                   n_left_to_next += 1;
166                   vlib_set_next_frame_buffer (vm, node, next1, bi1);
167                   break;
168
169                 case 3:
170                   /* A B B or A B C */
171                   to_next -= 2;
172                   n_left_to_next += 2;
173                   vlib_set_next_frame_buffer (vm, node, next0, bi0);
174                   vlib_set_next_frame_buffer (vm, node, next1, bi1);
175                   if (next0 == next1)
176                     {
177                       vlib_put_next_frame (vm, node, next_index,
178                                            n_left_to_next);
179                       next_index = next1;
180                       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
181                     }
182                 }
183             }
184         }
185     
186       while (n_left_from > 0 && n_left_to_next > 0)
187         {
188           u32 bi0;
189           vlib_buffer_t * b0;
190           llc_header_t * h0;
191           u8 next0, len0;
192
193           bi0 = from[0];
194           to_next[0] = bi0;
195           from += 1;
196           to_next += 1;
197           n_left_from -= 1;
198           n_left_to_next -= 1;
199
200           b0 = vlib_get_buffer (vm, bi0);
201
202           h0 = (void *) (b0->data + b0->current_data);
203
204           len0 = llc_header_length (h0);
205
206           b0->current_data += len0;
207
208           b0->current_length -= len0;
209
210           next0 = lm->input_next_by_protocol[h0->dst_sap];
211
212           b0->error = node->errors[next0 == LLC_INPUT_NEXT_DROP ? LLC_ERROR_UNKNOWN_PROTOCOL : LLC_ERROR_NONE];
213
214           /* Sent packet to wrong next? */
215           if (PREDICT_FALSE (next0 != next_index))
216             {
217               /* Return old frame; remove incorrectly enqueued packet. */
218               vlib_put_next_frame (vm, node, next_index, n_left_to_next + 1);
219
220               /* Send to correct next. */
221               next_index = next0;
222               vlib_get_next_frame (vm, node, next_index,
223                                    to_next, n_left_to_next);
224
225               to_next[0] = bi0;
226               to_next += 1;
227               n_left_to_next -= 1;
228             }
229         }
230
231       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
232     }
233
234   return from_frame->n_vectors;
235 }
236
237 static char * llc_error_strings[] = {
238 #define _(f,s) s,
239   foreach_llc_error
240 #undef _
241 };
242
243 VLIB_REGISTER_NODE (llc_input_node) = {
244   .function = llc_input,
245   .name = "llc-input",
246   /* Takes a vector of packets. */
247   .vector_size = sizeof (u32),
248
249   .n_errors = LLC_N_ERROR,
250   .error_strings = llc_error_strings,
251
252   .n_next_nodes = LLC_INPUT_N_NEXT,
253   .next_nodes = {
254 #define _(s,n) [LLC_INPUT_NEXT_##s] = n,
255     foreach_llc_input_next
256 #undef _
257   },
258
259   .format_buffer = format_llc_header_with_length,
260   .format_trace = format_llc_input_trace,
261   .unformat_buffer = unformat_llc_header,
262 };
263
264 static clib_error_t * llc_input_init (vlib_main_t * vm)
265 {
266   llc_main_t * lm = &llc_main;
267
268   {
269     clib_error_t * error = vlib_call_init_function (vm, llc_init);
270     if (error)
271       clib_error_report (error);
272   }
273
274   llc_setup_node (vm, llc_input_node.index);
275
276   {
277     int i;
278     for (i = 0; i < ARRAY_LEN (lm->input_next_by_protocol); i++)
279       lm->input_next_by_protocol[i] = LLC_INPUT_NEXT_DROP;
280   }
281
282   return 0;
283 }
284
285 VLIB_INIT_FUNCTION (llc_input_init);
286
287 void
288 llc_register_input_protocol (vlib_main_t * vm,
289                              llc_protocol_t protocol,
290                              u32 node_index)
291 {
292   llc_main_t * lm = &llc_main;
293   llc_protocol_info_t * pi;
294
295   {
296     clib_error_t * error = vlib_call_init_function (vm, llc_input_init);
297     if (error)
298       clib_error_report (error);
299   }
300
301   pi = llc_get_protocol_info (lm, protocol);
302   pi->node_index = node_index;
303   pi->next_index = vlib_node_add_next (vm, 
304                                        llc_input_node.index,
305                                        node_index);
306
307   lm->input_next_by_protocol[protocol] = pi->next_index;
308 }