Line data Source code
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 : #include <vnet/ip/ip.h>
16 : #include <vnet/ethernet/ethernet.h> /* for ethernet_header_t */
17 : #include <vnet/classify/vnet_classify.h>
18 : #include <vnet/dpo/classify_dpo.h>
19 :
20 : typedef struct
21 : {
22 : u32 next_index;
23 : u32 table_index;
24 : u32 entry_index;
25 : } ip_classify_trace_t;
26 :
27 : /* packet trace format function */
28 : static u8 *
29 0 : format_ip_classify_trace (u8 * s, va_list * args)
30 : {
31 0 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
32 0 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
33 0 : ip_classify_trace_t *t = va_arg (*args, ip_classify_trace_t *);
34 :
35 0 : s = format (s, "IP_CLASSIFY: next_index %d, table %d, entry %d",
36 : t->next_index, t->table_index, t->entry_index);
37 0 : return s;
38 : }
39 :
40 : #define foreach_ip_classify_error \
41 : _(MISS, "Classify misses") \
42 : _(HIT, "Classify hits") \
43 : _(CHAIN_HIT, "Classify hits after chain walk")
44 :
45 : typedef enum
46 : {
47 : #define _(sym,str) IP_CLASSIFY_ERROR_##sym,
48 : foreach_ip_classify_error
49 : #undef _
50 : IP_CLASSIFY_N_ERROR,
51 : } ip_classify_error_t;
52 :
53 : static char *ip_classify_error_strings[] = {
54 : #define _(sym,string) string,
55 : foreach_ip_classify_error
56 : #undef _
57 : };
58 :
59 : static inline uword
60 0 : ip_classify_inline (vlib_main_t * vm,
61 : vlib_node_runtime_t * node,
62 : vlib_frame_t * frame, int is_ip4)
63 : {
64 : u32 n_left_from, *from, *to_next;
65 : ip_lookup_next_t next_index;
66 0 : vnet_classify_main_t *vcm = &vnet_classify_main;
67 0 : f64 now = vlib_time_now (vm);
68 0 : u32 hits = 0;
69 0 : u32 misses = 0;
70 0 : u32 chain_hits = 0;
71 : u32 n_next;
72 :
73 0 : if (is_ip4)
74 : {
75 0 : n_next = IP4_LOOKUP_N_NEXT;
76 : }
77 : else
78 : {
79 0 : n_next = IP6_LOOKUP_N_NEXT;
80 : }
81 :
82 0 : from = vlib_frame_vector_args (frame);
83 0 : n_left_from = frame->n_vectors;
84 :
85 : /* First pass: compute hashes */
86 :
87 0 : while (n_left_from > 2)
88 : {
89 : vlib_buffer_t *b0, *b1;
90 : u32 bi0, bi1;
91 : u8 *h0, *h1;
92 : u32 cd_index0, cd_index1;
93 : classify_dpo_t *cd0, *cd1;
94 : u32 table_index0, table_index1;
95 : vnet_classify_table_t *t0, *t1;
96 :
97 : /* prefetch next iteration */
98 : {
99 : vlib_buffer_t *p1, *p2;
100 :
101 0 : p1 = vlib_get_buffer (vm, from[1]);
102 0 : p2 = vlib_get_buffer (vm, from[2]);
103 :
104 0 : vlib_prefetch_buffer_header (p1, STORE);
105 0 : clib_prefetch_store (p1->data);
106 0 : vlib_prefetch_buffer_header (p2, STORE);
107 0 : clib_prefetch_store (p2->data);
108 : }
109 :
110 0 : bi0 = from[0];
111 0 : b0 = vlib_get_buffer (vm, bi0);
112 0 : h0 = vlib_buffer_get_current (b0) - ethernet_buffer_header_size (b0);
113 :
114 0 : bi1 = from[1];
115 0 : b1 = vlib_get_buffer (vm, bi1);
116 0 : h1 = vlib_buffer_get_current (b1) - ethernet_buffer_header_size (b1);
117 :
118 0 : cd_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
119 0 : cd0 = classify_dpo_get (cd_index0);
120 0 : table_index0 = cd0->cd_table_index;
121 :
122 0 : cd_index1 = vnet_buffer (b1)->ip.adj_index[VLIB_TX];
123 0 : cd1 = classify_dpo_get (cd_index1);
124 0 : table_index1 = cd1->cd_table_index;
125 :
126 0 : t0 = pool_elt_at_index (vcm->tables, table_index0);
127 :
128 0 : t1 = pool_elt_at_index (vcm->tables, table_index1);
129 :
130 0 : vnet_buffer (b0)->l2_classify.hash = vnet_classify_hash_packet (t0, h0);
131 :
132 0 : vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
133 :
134 0 : vnet_buffer (b1)->l2_classify.hash = vnet_classify_hash_packet (t1, h1);
135 :
136 0 : vnet_classify_prefetch_bucket (t1, vnet_buffer (b1)->l2_classify.hash);
137 :
138 0 : vnet_buffer (b0)->l2_classify.table_index = table_index0;
139 :
140 0 : vnet_buffer (b1)->l2_classify.table_index = table_index1;
141 :
142 0 : from += 2;
143 0 : n_left_from -= 2;
144 : }
145 :
146 0 : while (n_left_from > 0)
147 : {
148 : vlib_buffer_t *b0;
149 : u32 bi0;
150 : u8 *h0;
151 : u32 cd_index0;
152 : classify_dpo_t *cd0;
153 : u32 table_index0;
154 : vnet_classify_table_t *t0;
155 :
156 0 : bi0 = from[0];
157 0 : b0 = vlib_get_buffer (vm, bi0);
158 0 : h0 = vlib_buffer_get_current (b0) - ethernet_buffer_header_size (b0);
159 :
160 0 : cd_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
161 0 : cd0 = classify_dpo_get (cd_index0);
162 0 : table_index0 = cd0->cd_table_index;
163 :
164 0 : t0 = pool_elt_at_index (vcm->tables, table_index0);
165 0 : vnet_buffer (b0)->l2_classify.hash = vnet_classify_hash_packet (t0, h0);
166 :
167 0 : vnet_buffer (b0)->l2_classify.table_index = table_index0;
168 0 : vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
169 :
170 0 : from++;
171 0 : n_left_from--;
172 : }
173 :
174 0 : next_index = node->cached_next_index;
175 0 : from = vlib_frame_vector_args (frame);
176 0 : n_left_from = frame->n_vectors;
177 :
178 0 : while (n_left_from > 0)
179 : {
180 : u32 n_left_to_next;
181 :
182 0 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
183 :
184 : /* Not enough load/store slots to dual loop... */
185 0 : while (n_left_from > 0 && n_left_to_next > 0)
186 : {
187 : u32 bi0;
188 : vlib_buffer_t *b0;
189 0 : u32 next0 = IP_LOOKUP_NEXT_DROP;
190 : u32 table_index0;
191 : vnet_classify_table_t *t0;
192 : vnet_classify_entry_t *e0;
193 : u32 hash0;
194 : u8 *h0;
195 :
196 : /* Stride 3 seems to work best */
197 0 : if (PREDICT_TRUE (n_left_from > 3))
198 : {
199 0 : vlib_buffer_t *p1 = vlib_get_buffer (vm, from[3]);
200 : vnet_classify_table_t *tp1;
201 : u32 table_index1;
202 : u32 phash1;
203 :
204 0 : table_index1 = vnet_buffer (p1)->l2_classify.table_index;
205 :
206 0 : if (PREDICT_TRUE (table_index1 != ~0))
207 : {
208 0 : tp1 = pool_elt_at_index (vcm->tables, table_index1);
209 0 : phash1 = vnet_buffer (p1)->l2_classify.hash;
210 0 : vnet_classify_prefetch_entry (tp1, phash1);
211 : }
212 : }
213 :
214 : /* speculatively enqueue b0 to the current next frame */
215 0 : bi0 = from[0];
216 0 : to_next[0] = bi0;
217 0 : from += 1;
218 0 : to_next += 1;
219 0 : n_left_from -= 1;
220 0 : n_left_to_next -= 1;
221 :
222 0 : b0 = vlib_get_buffer (vm, bi0);
223 0 : h0 = b0->data;
224 0 : table_index0 = vnet_buffer (b0)->l2_classify.table_index;
225 0 : e0 = 0;
226 0 : t0 = 0;
227 0 : vnet_buffer (b0)->l2_classify.opaque_index = ~0;
228 :
229 0 : if (PREDICT_TRUE (table_index0 != ~0))
230 : {
231 0 : hash0 = vnet_buffer (b0)->l2_classify.hash;
232 0 : t0 = pool_elt_at_index (vcm->tables, table_index0);
233 :
234 0 : e0 = vnet_classify_find_entry (t0, h0, hash0, now);
235 0 : if (e0)
236 : {
237 0 : vnet_buffer (b0)->l2_classify.opaque_index
238 0 : = e0->opaque_index;
239 0 : vlib_buffer_advance (b0, e0->advance);
240 0 : next0 = (e0->next_index < node->n_next_nodes) ?
241 0 : e0->next_index : next0;
242 0 : hits++;
243 : }
244 : else
245 : {
246 : while (1)
247 : {
248 0 : if (t0->next_table_index != ~0)
249 0 : t0 = pool_elt_at_index (vcm->tables,
250 : t0->next_table_index);
251 : else
252 : {
253 0 : next0 = (t0->miss_next_index < n_next) ?
254 0 : t0->miss_next_index : next0;
255 0 : misses++;
256 0 : break;
257 : }
258 :
259 0 : hash0 = vnet_classify_hash_packet (t0, h0);
260 0 : e0 = vnet_classify_find_entry (t0, h0, hash0, now);
261 0 : if (e0)
262 : {
263 0 : vnet_buffer (b0)->l2_classify.opaque_index
264 0 : = e0->opaque_index;
265 0 : vlib_buffer_advance (b0, e0->advance);
266 0 : next0 = (e0->next_index < node->n_next_nodes) ?
267 0 : e0->next_index : next0;
268 0 : hits++;
269 0 : chain_hits++;
270 0 : break;
271 : }
272 : }
273 : }
274 : }
275 :
276 0 : if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
277 : && (b0->flags & VLIB_BUFFER_IS_TRACED)))
278 : {
279 : ip_classify_trace_t *t =
280 0 : vlib_add_trace (vm, node, b0, sizeof (*t));
281 0 : t->next_index = next0;
282 0 : t->table_index = t0 ? t0 - vcm->tables : ~0;
283 0 : t->entry_index = e0 ? e0->opaque_index : ~0;
284 : }
285 :
286 : /* verify speculative enqueue, maybe switch current next frame */
287 0 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
288 : to_next, n_left_to_next,
289 : bi0, next0);
290 : }
291 :
292 0 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
293 : }
294 :
295 0 : vlib_node_increment_counter (vm, node->node_index,
296 : IP_CLASSIFY_ERROR_MISS, misses);
297 0 : vlib_node_increment_counter (vm, node->node_index,
298 : IP_CLASSIFY_ERROR_HIT, hits);
299 0 : vlib_node_increment_counter (vm, node->node_index,
300 : IP_CLASSIFY_ERROR_CHAIN_HIT, chain_hits);
301 0 : return frame->n_vectors;
302 : }
303 :
304 2300 : VLIB_NODE_FN (ip4_classify_node) (vlib_main_t * vm,
305 : vlib_node_runtime_t * node,
306 : vlib_frame_t * frame)
307 : {
308 0 : return ip_classify_inline (vm, node, frame, 1 /* is_ip4 */ );
309 : }
310 :
311 :
312 : /* *INDENT-OFF* */
313 183788 : VLIB_REGISTER_NODE (ip4_classify_node) = {
314 : .name = "ip4-classify",
315 : .vector_size = sizeof (u32),
316 : .sibling_of = "ip4-lookup",
317 : .format_trace = format_ip_classify_trace,
318 : .n_errors = ARRAY_LEN(ip_classify_error_strings),
319 : .error_strings = ip_classify_error_strings,
320 :
321 : .n_next_nodes = 0,
322 : };
323 : /* *INDENT-ON* */
324 :
325 2300 : VLIB_NODE_FN (ip6_classify_node) (vlib_main_t * vm,
326 : vlib_node_runtime_t * node,
327 : vlib_frame_t * frame)
328 : {
329 0 : return ip_classify_inline (vm, node, frame, 0 /* is_ip4 */ );
330 : }
331 :
332 :
333 : /* *INDENT-OFF* */
334 183788 : VLIB_REGISTER_NODE (ip6_classify_node) = {
335 : .name = "ip6-classify",
336 : .vector_size = sizeof (u32),
337 : .sibling_of = "ip6-lookup",
338 : .format_trace = format_ip_classify_trace,
339 : .n_errors = ARRAY_LEN(ip_classify_error_strings),
340 : .error_strings = ip_classify_error_strings,
341 :
342 : .n_next_nodes = 0,
343 : };
344 : /* *INDENT-ON* */
345 :
346 : #ifndef CLIB_MARCH_VARIANT
347 : static clib_error_t *
348 575 : ip_classify_init (vlib_main_t * vm)
349 : {
350 575 : return 0;
351 : }
352 :
353 13247 : VLIB_INIT_FUNCTION (ip_classify_init);
354 : #endif /* CLIB_MARCH_VARIANT */
355 :
356 : /*
357 : * fd.io coding-style-patch-verification: ON
358 : *
359 : * Local Variables:
360 : * eval: (c-set-style "gnu")
361 : * End:
362 : */
|