MPLS hash function improvements
[vpp.git] / src / vnet / mpls / mpls_lookup.c
1 /*
2  * mpls_lookup.c: MPLS lookup
3  *
4  * Copyright (c) 2012-2014 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/pg/pg.h>
20 #include <vnet/mpls/mpls.h>
21 #include <vnet/fib/mpls_fib.h>
22 #include <vnet/dpo/load_balance_map.h>
23 #include <vnet/dpo/replicate_dpo.h>
24
25 /**
26  * Static MPLS VLIB forwarding node
27  */
28 static vlib_node_registration_t mpls_lookup_node;
29
30 /**
31  * The arc/edge from the MPLS lookup node to the MPLS replicate node
32  */
33 static u32 mpls_lookup_to_replicate_edge;
34
35 typedef struct {
36   u32 next_index;
37   u32 lb_index;
38   u32 lfib_index;
39   u32 label_net_byte_order;
40   u32 hash;
41 } mpls_lookup_trace_t;
42
43 static u8 *
44 format_mpls_lookup_trace (u8 * s, va_list * args)
45 {
46   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
47   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
48   mpls_lookup_trace_t * t = va_arg (*args, mpls_lookup_trace_t *);
49
50   s = format (s, "MPLS: next [%d], lookup fib index %d, LB index %d hash %x "
51               "label %d eos %d", 
52               t->next_index, t->lfib_index, t->lb_index, t->hash,
53               vnet_mpls_uc_get_label(
54                   clib_net_to_host_u32(t->label_net_byte_order)),
55               vnet_mpls_uc_get_s(t->label_net_byte_order));
56   return s;
57 }
58
59 /*
60  * Compute flow hash. 
61  * We'll use it to select which adjacency to use for this flow.  And other things.
62  */
63 always_inline u32
64 mpls_compute_flow_hash (const mpls_unicast_header_t * hdr,
65                         flow_hash_config_t flow_hash_config)
66 {
67     /*
68      * We need to byte swap so we use the numerical value. i.e. an odd label
69      * leads to an odd bucket. as opposed to a label above and below value X.
70      */
71     u8 next_label_is_entropy;
72     mpls_label_t ho_label;
73     u32 hash, value;
74
75     ho_label = clib_net_to_host_u32(hdr->label_exp_s_ttl);
76     hash = vnet_mpls_uc_get_label(ho_label);
77     next_label_is_entropy = 0;
78
79     while (MPLS_EOS != vnet_mpls_uc_get_s(ho_label))
80     {
81         hdr++;
82         ho_label = clib_net_to_host_u32(hdr->label_exp_s_ttl);
83         value = vnet_mpls_uc_get_label(ho_label);
84
85         if (1 == next_label_is_entropy)
86         {
87             /*
88              * The label is an entropy value, use it alone as the hash
89              */
90             return (ho_label);
91         }
92         if (MPLS_IETF_ENTROPY_LABEL == value)
93         {
94             /*
95              * we've met a label in the stack indicating that tha next
96              * label is an entropy value
97              */
98             next_label_is_entropy = 1;
99         }
100         else
101         {
102             /*
103              * XOR the label values in the stack together to
104              * build up the hash value
105              */
106             hash ^= value;
107         }
108     }
109
110     /*
111      * check the top nibble for v4 and v6
112      */
113     hdr++;
114
115     switch (((u8*)hdr)[0] >> 4)
116     {
117     case 4:
118         /* incorporate the v4 flow-hash */
119         hash ^= ip4_compute_flow_hash ((const ip4_header_t *)hdr,
120                                        IP_FLOW_HASH_DEFAULT);
121         break;
122     case 6:
123         /* incorporate the v6 flow-hash */
124         hash ^= ip6_compute_flow_hash ((const ip6_header_t *)hdr,
125                                        IP_FLOW_HASH_DEFAULT);
126         break;
127     default:
128         break;
129     }
130
131     return (hash);
132 }
133
134 static inline uword
135 mpls_lookup (vlib_main_t * vm,
136              vlib_node_runtime_t * node,
137              vlib_frame_t * from_frame)
138 {
139   vlib_combined_counter_main_t * cm = &load_balance_main.lbm_to_counters;
140   u32 n_left_from, next_index, * from, * to_next;
141   mpls_main_t * mm = &mpls_main;
142   u32 thread_index = vlib_get_thread_index();
143
144   from = vlib_frame_vector_args (from_frame);
145   n_left_from = from_frame->n_vectors;
146   next_index = node->cached_next_index;
147
148   while (n_left_from > 0)
149     {
150       u32 n_left_to_next;
151
152       vlib_get_next_frame (vm, node, next_index,
153                            to_next, n_left_to_next);
154
155       while (n_left_from >= 8 && n_left_to_next >= 4)
156         {
157           u32 lbi0, next0, lfib_index0, bi0, hash_c0;
158           const mpls_unicast_header_t * h0;
159           const load_balance_t *lb0;
160           const dpo_id_t *dpo0;
161           vlib_buffer_t * b0;
162           u32 lbi1, next1, lfib_index1, bi1, hash_c1;
163           const mpls_unicast_header_t * h1;
164           const load_balance_t *lb1;
165           const dpo_id_t *dpo1;
166           vlib_buffer_t * b1;
167           u32 lbi2, next2, lfib_index2, bi2, hash_c2;
168           const mpls_unicast_header_t * h2;
169           const load_balance_t *lb2;
170           const dpo_id_t *dpo2;
171           vlib_buffer_t * b2;
172           u32 lbi3, next3, lfib_index3, bi3, hash_c3;
173           const mpls_unicast_header_t * h3;
174           const load_balance_t *lb3;
175           const dpo_id_t *dpo3;
176           vlib_buffer_t * b3;
177
178            /* Prefetch next iteration. */
179           {
180               vlib_buffer_t * p2, * p3, *p4, *p5;
181
182             p2 = vlib_get_buffer (vm, from[2]);
183             p3 = vlib_get_buffer (vm, from[3]);
184             p4 = vlib_get_buffer (vm, from[4]);
185             p5 = vlib_get_buffer (vm, from[5]);
186
187             vlib_prefetch_buffer_header (p2, STORE);
188             vlib_prefetch_buffer_header (p3, STORE);
189             vlib_prefetch_buffer_header (p4, STORE);
190             vlib_prefetch_buffer_header (p5, STORE);
191
192             CLIB_PREFETCH (p2->data, sizeof (h0[0]), STORE);
193             CLIB_PREFETCH (p3->data, sizeof (h0[0]), STORE);
194             CLIB_PREFETCH (p4->data, sizeof (h0[0]), STORE);
195             CLIB_PREFETCH (p5->data, sizeof (h0[0]), STORE);
196           }
197
198           bi0 = to_next[0] = from[0];
199           bi1 = to_next[1] = from[1];
200           bi2 = to_next[2] = from[2];
201           bi3 = to_next[3] = from[3];
202
203           from += 4;
204           n_left_from -= 4;
205           to_next += 4;
206           n_left_to_next -= 4;
207
208           b0 = vlib_get_buffer (vm, bi0);
209           b1 = vlib_get_buffer (vm, bi1);
210           b2 = vlib_get_buffer (vm, bi2);
211           b3 = vlib_get_buffer (vm, bi3);
212           h0 = vlib_buffer_get_current (b0);
213           h1 = vlib_buffer_get_current (b1);
214           h2 = vlib_buffer_get_current (b2);
215           h3 = vlib_buffer_get_current (b3);
216
217           lfib_index0 = vec_elt(mm->fib_index_by_sw_if_index,
218                                 vnet_buffer(b0)->sw_if_index[VLIB_RX]);
219           lfib_index1 = vec_elt(mm->fib_index_by_sw_if_index,
220                                 vnet_buffer(b1)->sw_if_index[VLIB_RX]);
221           lfib_index2 = vec_elt(mm->fib_index_by_sw_if_index,
222                                 vnet_buffer(b2)->sw_if_index[VLIB_RX]);
223           lfib_index3 = vec_elt(mm->fib_index_by_sw_if_index,
224                                 vnet_buffer(b3)->sw_if_index[VLIB_RX]);
225
226           lbi0 = mpls_fib_table_forwarding_lookup (lfib_index0, h0);
227           lbi1 = mpls_fib_table_forwarding_lookup (lfib_index1, h1);
228           lbi2 = mpls_fib_table_forwarding_lookup (lfib_index2, h2);
229           lbi3 = mpls_fib_table_forwarding_lookup (lfib_index3, h3);
230
231           hash_c0 = vnet_buffer(b0)->ip.flow_hash = 0;
232           hash_c1 = vnet_buffer(b1)->ip.flow_hash = 0;
233           hash_c2 = vnet_buffer(b2)->ip.flow_hash = 0;
234           hash_c3 = vnet_buffer(b3)->ip.flow_hash = 0;
235
236           if (MPLS_IS_REPLICATE & lbi0)
237           {
238               next0 = mpls_lookup_to_replicate_edge;
239               vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
240                   (lbi0 & ~MPLS_IS_REPLICATE);
241           }
242           else
243           {
244               lb0 = load_balance_get(lbi0);
245               ASSERT (lb0->lb_n_buckets > 0);
246               ASSERT (is_pow2 (lb0->lb_n_buckets));
247
248               if (PREDICT_FALSE(lb0->lb_n_buckets > 1))
249               {
250                   hash_c0 = vnet_buffer (b0)->ip.flow_hash =
251                       mpls_compute_flow_hash(h0, lb0->lb_hash_config);
252                   dpo0 = load_balance_get_fwd_bucket
253                       (lb0,
254                        (hash_c0 & (lb0->lb_n_buckets_minus_1)));
255               }
256               else
257               {
258                   dpo0 = load_balance_get_bucket_i (lb0, 0);
259               }
260               next0 = dpo0->dpoi_next_node;
261
262               vnet_buffer (b0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
263
264               vlib_increment_combined_counter
265                   (cm, thread_index, lbi0, 1,
266                    vlib_buffer_length_in_chain (vm, b0));
267           }
268           if (MPLS_IS_REPLICATE & lbi1)
269           {
270               next1 = mpls_lookup_to_replicate_edge;
271               vnet_buffer (b1)->ip.adj_index[VLIB_TX] =
272                   (lbi1 & ~MPLS_IS_REPLICATE);
273           }
274           else
275           {
276               lb1 = load_balance_get(lbi1);
277               ASSERT (lb1->lb_n_buckets > 0);
278               ASSERT (is_pow2 (lb1->lb_n_buckets));
279
280               if (PREDICT_FALSE(lb1->lb_n_buckets > 1))
281               {
282                   hash_c1 = vnet_buffer (b1)->ip.flow_hash =
283                       mpls_compute_flow_hash(h1, lb1->lb_hash_config);
284                   dpo1 = load_balance_get_fwd_bucket
285                       (lb1,
286                        (hash_c1 & (lb1->lb_n_buckets_minus_1)));
287               }
288               else
289               {
290                   dpo1 = load_balance_get_bucket_i (lb1, 0);
291               }
292               next1 = dpo1->dpoi_next_node;
293
294               vnet_buffer (b1)->ip.adj_index[VLIB_TX] = dpo1->dpoi_index;
295
296               vlib_increment_combined_counter
297                   (cm, thread_index, lbi1, 1,
298                    vlib_buffer_length_in_chain (vm, b1));
299           }
300           if (MPLS_IS_REPLICATE & lbi2)
301           {
302               next2 = mpls_lookup_to_replicate_edge;
303               vnet_buffer (b2)->ip.adj_index[VLIB_TX] =
304                   (lbi2 & ~MPLS_IS_REPLICATE);
305           }
306           else
307           {
308               lb2 = load_balance_get(lbi2);
309               ASSERT (lb2->lb_n_buckets > 0);
310               ASSERT (is_pow2 (lb2->lb_n_buckets));
311
312               if (PREDICT_FALSE(lb2->lb_n_buckets > 1))
313               {
314                   hash_c2 = vnet_buffer (b2)->ip.flow_hash =
315                       mpls_compute_flow_hash(h2, lb2->lb_hash_config);
316                   dpo2 = load_balance_get_fwd_bucket
317                       (lb2,
318                        (hash_c2 & (lb2->lb_n_buckets_minus_1)));
319               }
320               else
321               {
322                   dpo2 = load_balance_get_bucket_i (lb2, 0);
323               }
324               next2 = dpo2->dpoi_next_node;
325
326               vnet_buffer (b2)->ip.adj_index[VLIB_TX] = dpo2->dpoi_index;
327
328               vlib_increment_combined_counter
329                   (cm, thread_index, lbi2, 1,
330                    vlib_buffer_length_in_chain (vm, b2));
331           }
332           if (MPLS_IS_REPLICATE & lbi3)
333           {
334               next3 = mpls_lookup_to_replicate_edge;
335               vnet_buffer (b3)->ip.adj_index[VLIB_TX] =
336                   (lbi3 & ~MPLS_IS_REPLICATE);
337           }
338           else
339           {
340               lb3 = load_balance_get(lbi3);
341               ASSERT (lb3->lb_n_buckets > 0);
342               ASSERT (is_pow2 (lb3->lb_n_buckets));
343
344               if (PREDICT_FALSE(lb3->lb_n_buckets > 1))
345               {
346                   hash_c3 = vnet_buffer (b3)->ip.flow_hash =
347                       mpls_compute_flow_hash(h3, lb3->lb_hash_config);
348                   dpo3 = load_balance_get_fwd_bucket
349                       (lb3,
350                        (hash_c3 & (lb3->lb_n_buckets_minus_1)));
351               }
352               else
353               {
354                   dpo3 = load_balance_get_bucket_i (lb3, 0);
355               }
356               next3 = dpo3->dpoi_next_node;
357
358               vnet_buffer (b3)->ip.adj_index[VLIB_TX] = dpo3->dpoi_index;
359
360               vlib_increment_combined_counter
361                   (cm, thread_index, lbi3, 1,
362                    vlib_buffer_length_in_chain (vm, b3));
363           }
364
365           /*
366            * before we pop the label copy th values we need to maintain.
367            * The label header is in network byte order.
368            *  last byte is the TTL.
369            *  bits 2 to 4 inclusive are the EXP bits
370            */
371           vnet_buffer (b0)->mpls.ttl = ((char*)h0)[3];
372           vnet_buffer (b0)->mpls.exp = (((char*)h0)[2] & 0xe) >> 1;
373           vnet_buffer (b0)->mpls.first = 1;
374           vnet_buffer (b1)->mpls.ttl = ((char*)h1)[3];
375           vnet_buffer (b1)->mpls.exp = (((char*)h1)[2] & 0xe) >> 1;
376           vnet_buffer (b1)->mpls.first = 1;
377           vnet_buffer (b2)->mpls.ttl = ((char*)h2)[3];
378           vnet_buffer (b2)->mpls.exp = (((char*)h2)[2] & 0xe) >> 1;
379           vnet_buffer (b2)->mpls.first = 1;
380           vnet_buffer (b3)->mpls.ttl = ((char*)h3)[3];
381           vnet_buffer (b3)->mpls.exp = (((char*)h3)[2] & 0xe) >> 1;
382           vnet_buffer (b3)->mpls.first = 1;
383
384           /*
385            * pop the label that was just used in the lookup
386            */
387           vlib_buffer_advance(b0, sizeof(*h0));
388           vlib_buffer_advance(b1, sizeof(*h1));
389           vlib_buffer_advance(b2, sizeof(*h2));
390           vlib_buffer_advance(b3, sizeof(*h3));
391
392           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
393           {
394               mpls_lookup_trace_t *tr = vlib_add_trace (vm, node,
395                                                         b0, sizeof (*tr));
396               tr->next_index = next0;
397               tr->lb_index = lbi0;
398               tr->lfib_index = lfib_index0;
399               tr->hash = hash_c0;
400               tr->label_net_byte_order = h0->label_exp_s_ttl;
401           }
402
403           if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
404           {
405               mpls_lookup_trace_t *tr = vlib_add_trace (vm, node,
406                                                         b1, sizeof (*tr));
407               tr->next_index = next1;
408               tr->lb_index = lbi1;
409               tr->lfib_index = lfib_index1;
410               tr->hash = hash_c1;
411               tr->label_net_byte_order = h1->label_exp_s_ttl;
412           }
413
414           if (PREDICT_FALSE(b2->flags & VLIB_BUFFER_IS_TRACED))
415           {
416               mpls_lookup_trace_t *tr = vlib_add_trace (vm, node,
417                                                         b2, sizeof (*tr));
418               tr->next_index = next2;
419               tr->lb_index = lbi2;
420               tr->lfib_index = lfib_index2;
421               tr->hash = hash_c2;
422               tr->label_net_byte_order = h2->label_exp_s_ttl;
423           }
424
425           if (PREDICT_FALSE(b3->flags & VLIB_BUFFER_IS_TRACED))
426           {
427               mpls_lookup_trace_t *tr = vlib_add_trace (vm, node,
428                                                         b3, sizeof (*tr));
429               tr->next_index = next3;
430               tr->lb_index = lbi3;
431               tr->lfib_index = lfib_index3;
432               tr->hash = hash_c3;
433               tr->label_net_byte_order = h3->label_exp_s_ttl;
434           }
435
436           vlib_validate_buffer_enqueue_x4 (vm, node, next_index,
437                                            to_next, n_left_to_next,
438                                            bi0, bi1, bi2, bi3,
439                                            next0, next1, next2, next3);
440         }
441
442       while (n_left_from > 0 && n_left_to_next > 0)
443       {
444           u32 lbi0, next0, lfib_index0, bi0, hash_c0;
445           const mpls_unicast_header_t * h0;
446           const load_balance_t *lb0;
447           const dpo_id_t *dpo0;
448           vlib_buffer_t * b0;
449
450           bi0 = from[0];
451           to_next[0] = bi0;
452           from += 1;
453           to_next += 1;
454           n_left_from -= 1;
455           n_left_to_next -= 1;
456
457           b0 = vlib_get_buffer (vm, bi0);
458           h0 = vlib_buffer_get_current (b0);
459
460           lfib_index0 = vec_elt(mm->fib_index_by_sw_if_index,
461                                 vnet_buffer(b0)->sw_if_index[VLIB_RX]);
462
463           lbi0 = mpls_fib_table_forwarding_lookup(lfib_index0, h0);
464           hash_c0 = vnet_buffer(b0)->ip.flow_hash = 0;
465
466           if (MPLS_IS_REPLICATE & lbi0)
467           {
468               next0 = mpls_lookup_to_replicate_edge;
469               vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
470                   (lbi0 & ~MPLS_IS_REPLICATE);
471           }
472           else
473           {
474               lb0 = load_balance_get(lbi0);
475               ASSERT (lb0->lb_n_buckets > 0);
476               ASSERT (is_pow2 (lb0->lb_n_buckets));
477
478               if (PREDICT_FALSE(lb0->lb_n_buckets > 1))
479               {
480                   hash_c0 = vnet_buffer (b0)->ip.flow_hash =
481                       mpls_compute_flow_hash(h0, lb0->lb_hash_config);
482                   dpo0 = load_balance_get_fwd_bucket
483                       (lb0,
484                        (hash_c0 & (lb0->lb_n_buckets_minus_1)));
485               }
486               else
487               {
488                   dpo0 = load_balance_get_bucket_i (lb0, 0);
489               }
490               next0 = dpo0->dpoi_next_node;
491               vnet_buffer (b0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
492
493               vlib_increment_combined_counter
494                   (cm, thread_index, lbi0, 1,
495                    vlib_buffer_length_in_chain (vm, b0));
496           }
497
498           /*
499            * before we pop the label copy, values we need to maintain.
500            * The label header is in network byte order.
501            *  last byte is the TTL.
502            *  bits 2 to 4 inclusive are the EXP bits
503            */
504           vnet_buffer (b0)->mpls.ttl = ((char*)h0)[3];
505           vnet_buffer (b0)->mpls.exp = (((char*)h0)[2] & 0xe) >> 1;
506           vnet_buffer (b0)->mpls.first = 1;
507
508           /*
509            * pop the label that was just used in the lookup
510            */
511           vlib_buffer_advance(b0, sizeof(*h0));
512
513           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
514           {
515               mpls_lookup_trace_t *tr = vlib_add_trace (vm, node,
516                                                         b0, sizeof (*tr));
517               tr->next_index = next0;
518               tr->lb_index = lbi0;
519               tr->lfib_index = lfib_index0;
520               tr->hash = hash_c0;
521               tr->label_net_byte_order = h0->label_exp_s_ttl;
522           }
523
524           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
525                                            to_next, n_left_to_next,
526                                            bi0, next0);
527         }
528
529       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
530     }
531   vlib_node_increment_counter (vm, mpls_lookup_node.index,
532                                MPLS_ERROR_PKTS_DECAP, from_frame->n_vectors);
533   return from_frame->n_vectors;
534 }
535
536 static char * mpls_error_strings[] = {
537 #define mpls_error(n,s) s,
538 #include "error.def"
539 #undef mpls_error
540 };
541
542 VLIB_REGISTER_NODE (mpls_lookup_node, static) = {
543   .function = mpls_lookup,
544   .name = "mpls-lookup",
545   /* Takes a vector of packets. */
546   .vector_size = sizeof (u32),
547   .n_errors = MPLS_N_ERROR,
548   .error_strings = mpls_error_strings,
549
550   .sibling_of = "mpls-load-balance",
551
552   .format_buffer = format_mpls_header,
553   .format_trace = format_mpls_lookup_trace,
554   .unformat_buffer = unformat_mpls_header,
555 };
556
557 VLIB_NODE_FUNCTION_MULTIARCH (mpls_lookup_node, mpls_lookup)
558
559 typedef struct {
560   u32 next_index;
561   u32 lb_index;
562   u32 hash;
563 } mpls_load_balance_trace_t;
564
565 static u8 *
566 format_mpls_load_balance_trace (u8 * s, va_list * args)
567 {
568   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
569   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
570   mpls_load_balance_trace_t * t = va_arg (*args, mpls_load_balance_trace_t *);
571
572   s = format (s, "MPLS: next [%d], LB index %d hash %d",
573               t->next_index, t->lb_index, t->hash);
574   return s;
575 }
576
577 always_inline uword
578 mpls_load_balance (vlib_main_t * vm,
579                   vlib_node_runtime_t * node,
580                   vlib_frame_t * frame)
581 {
582   vlib_combined_counter_main_t * cm = &load_balance_main.lbm_via_counters;
583   u32 n_left_from, n_left_to_next, * from, * to_next;
584   u32 thread_index = vlib_get_thread_index();
585   u32 next;
586
587   from = vlib_frame_vector_args (frame);
588   n_left_from = frame->n_vectors;
589   next = node->cached_next_index;
590
591   while (n_left_from > 0)
592     {
593       vlib_get_next_frame (vm, node, next,
594                            to_next, n_left_to_next);
595
596
597       while (n_left_from >= 4 && n_left_to_next >= 2)
598         {
599           const load_balance_t *lb0, *lb1;
600           vlib_buffer_t * p0, *p1;
601           u32 pi0, lbi0, hc0, pi1, lbi1, hc1, next0, next1;
602           const mpls_unicast_header_t *mpls0, *mpls1;
603           const dpo_id_t *dpo0, *dpo1;
604
605           /* Prefetch next iteration. */
606           {
607             vlib_buffer_t * p2, * p3;
608
609             p2 = vlib_get_buffer (vm, from[2]);
610             p3 = vlib_get_buffer (vm, from[3]);
611
612             vlib_prefetch_buffer_header (p2, STORE);
613             vlib_prefetch_buffer_header (p3, STORE);
614
615             CLIB_PREFETCH (p2->data, sizeof (mpls0[0]), STORE);
616             CLIB_PREFETCH (p3->data, sizeof (mpls0[0]), STORE);
617           }
618
619           pi0 = to_next[0] = from[0];
620           pi1 = to_next[1] = from[1];
621
622           from += 2;
623           n_left_from -= 2;
624           to_next += 2;
625           n_left_to_next -= 2;
626
627           p0 = vlib_get_buffer (vm, pi0);
628           p1 = vlib_get_buffer (vm, pi1);
629
630           mpls0 = vlib_buffer_get_current (p0);
631           mpls1 = vlib_buffer_get_current (p1);
632           lbi0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
633           lbi1 = vnet_buffer (p1)->ip.adj_index[VLIB_TX];
634
635           lb0 = load_balance_get(lbi0);
636           lb1 = load_balance_get(lbi1);
637
638           /*
639            * this node is for via FIBs we can re-use the hash value from the
640            * to node if present.
641            * We don't want to use the same hash value at each level in the recursion
642            * graph as that would lead to polarisation
643            */
644           hc0 = vnet_buffer (p0)->ip.flow_hash = 0;
645           hc1 = vnet_buffer (p1)->ip.flow_hash = 0;
646
647           if (PREDICT_FALSE (lb0->lb_n_buckets > 1))
648           {
649               if (PREDICT_TRUE (vnet_buffer(p0)->ip.flow_hash))
650               {
651                   hc0 = vnet_buffer(p0)->ip.flow_hash = vnet_buffer(p0)->ip.flow_hash >> 1;
652               }
653               else
654               {
655                   hc0 = vnet_buffer(p0)->ip.flow_hash = mpls_compute_flow_hash(mpls0, hc0);
656               }
657               dpo0 = load_balance_get_fwd_bucket(lb0, (hc0 & lb0->lb_n_buckets_minus_1));
658           }
659           else
660           {
661               dpo0 = load_balance_get_bucket_i (lb0, 0);
662           }
663           if (PREDICT_FALSE (lb1->lb_n_buckets > 1))
664           {
665               if (PREDICT_TRUE (vnet_buffer(p1)->ip.flow_hash))
666               {
667                   hc1 = vnet_buffer(p1)->ip.flow_hash = vnet_buffer(p1)->ip.flow_hash >> 1;
668               }
669               else
670               {
671                   hc1 = vnet_buffer(p1)->ip.flow_hash = mpls_compute_flow_hash(mpls1, hc1);
672               }
673               dpo1 = load_balance_get_fwd_bucket(lb1, (hc1 & lb1->lb_n_buckets_minus_1));
674           }
675           else
676           {
677               dpo1 = load_balance_get_bucket_i (lb1, 0);
678           }
679
680           next0 = dpo0->dpoi_next_node;
681           next1 = dpo1->dpoi_next_node;
682
683           vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
684           vnet_buffer (p1)->ip.adj_index[VLIB_TX] = dpo1->dpoi_index;
685
686           vlib_increment_combined_counter
687               (cm, thread_index, lbi0, 1,
688                vlib_buffer_length_in_chain (vm, p0));
689           vlib_increment_combined_counter
690               (cm, thread_index, lbi1, 1,
691                vlib_buffer_length_in_chain (vm, p1));
692
693           if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
694           {
695               mpls_load_balance_trace_t *tr = vlib_add_trace (vm, node,
696                                                               p0, sizeof (*tr));
697               tr->next_index = next0;
698               tr->lb_index = lbi0;
699               tr->hash = hc0;
700           }
701
702           vlib_validate_buffer_enqueue_x2 (vm, node, next,
703                                            to_next, n_left_to_next,
704                                            pi0, pi1, next0, next1);
705        }
706
707       while (n_left_from > 0 && n_left_to_next > 0)
708         {
709           const load_balance_t *lb0;
710           vlib_buffer_t * p0;
711           u32 pi0, lbi0, hc0, next0;
712           const mpls_unicast_header_t *mpls0;
713           const dpo_id_t *dpo0;
714
715           pi0 = from[0];
716           to_next[0] = pi0;
717           from += 1;
718           to_next += 1;
719           n_left_to_next -= 1;
720           n_left_from -= 1;
721
722           p0 = vlib_get_buffer (vm, pi0);
723
724           mpls0 = vlib_buffer_get_current (p0);
725           lbi0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
726
727           lb0 = load_balance_get(lbi0);
728
729           hc0 = vnet_buffer (p0)->ip.flow_hash = 0;
730           if (PREDICT_FALSE (lb0->lb_n_buckets > 1))
731           {
732               if (PREDICT_TRUE (vnet_buffer(p0)->ip.flow_hash))
733               {
734                   hc0 = vnet_buffer(p0)->ip.flow_hash = vnet_buffer(p0)->ip.flow_hash >> 1;
735               }
736               else
737               {
738                   hc0 = vnet_buffer(p0)->ip.flow_hash = mpls_compute_flow_hash(mpls0, hc0);
739               }
740                dpo0 = load_balance_get_fwd_bucket(lb0, (hc0 & lb0->lb_n_buckets_minus_1));
741           }
742           else
743           {
744               dpo0 = load_balance_get_bucket_i (lb0, 0);
745           }
746
747           next0 = dpo0->dpoi_next_node;
748           vnet_buffer (p0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
749
750           vlib_increment_combined_counter
751               (cm, thread_index, lbi0, 1,
752                vlib_buffer_length_in_chain (vm, p0));
753
754           vlib_validate_buffer_enqueue_x1 (vm, node, next,
755                                            to_next, n_left_to_next,
756                                            pi0, next0);
757         }
758
759       vlib_put_next_frame (vm, node, next, n_left_to_next);
760     }
761
762   return frame->n_vectors;
763 }
764
765 VLIB_REGISTER_NODE (mpls_load_balance_node) = {
766   .function = mpls_load_balance,
767   .name = "mpls-load-balance",
768   .vector_size = sizeof (u32),
769   .format_trace = format_mpls_load_balance_trace,
770   .n_next_nodes = 1,
771   .next_nodes =
772   {
773       [0] = "mpls-drop",
774   },
775
776 };
777
778 VLIB_NODE_FUNCTION_MULTIARCH (mpls_load_balance_node, mpls_load_balance)
779
780
781 static clib_error_t *
782 mpls_lookup_init (vlib_main_t * vm)
783 {
784   clib_error_t * error;
785
786   if ((error = vlib_call_init_function (vm, mpls_init)))
787     return error;
788
789   mpls_lookup_to_replicate_edge =
790       vlib_node_add_named_next(vm,
791                                mpls_lookup_node.index,
792                                "mpls-replicate");
793
794   return (NULL);
795 }
796
797 VLIB_INIT_FUNCTION (mpls_lookup_init);