MPLS Mcast
[vpp.git] / src / vnet / fib / fib_test.c
1 /*
2  * Copyright (c) 2016 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 #include <vnet/fib/ip6_fib.h>
17 #include <vnet/fib/ip4_fib.h>
18 #include <vnet/fib/mpls_fib.h>
19 #include <vnet/adj/adj.h>
20 #include <vnet/dpo/load_balance.h>
21 #include <vnet/dpo/load_balance_map.h>
22 #include <vnet/dpo/mpls_label_dpo.h>
23 #include <vnet/dpo/lookup_dpo.h>
24 #include <vnet/dpo/drop_dpo.h>
25 #include <vnet/dpo/receive_dpo.h>
26 #include <vnet/dpo/ip_null_dpo.h>
27 #include <vnet/bfd/bfd_main.h>
28 #include <vnet/dpo/interface_dpo.h>
29 #include <vnet/dpo/replicate_dpo.h>
30
31 #include <vnet/mpls/mpls.h>
32
33 #include <vnet/fib/fib_path_list.h>
34 #include <vnet/fib/fib_entry_src.h>
35 #include <vnet/fib/fib_walk.h>
36 #include <vnet/fib/fib_node_list.h>
37 #include <vnet/fib/fib_urpf_list.h>
38
39 /*
40  * Add debugs for passing tests
41  */
42 static int fib_test_do_debug;
43
44 #define FIB_TEST_I(_cond, _comment, _args...)                   \
45 ({                                                              \
46     int _evald = (_cond);                                       \
47     if (!(_evald)) {                                            \
48         fformat(stderr, "FAIL:%d: " _comment "\n",              \
49                 __LINE__, ##_args);                             \
50     } else {                                                    \
51         if (fib_test_do_debug)                                  \
52             fformat(stderr, "PASS:%d: " _comment "\n",          \
53                     __LINE__, ##_args);                         \
54     }                                                           \
55     _evald;                                                     \
56 })
57 #define FIB_TEST(_cond, _comment, _args...)                     \
58 {                                                               \
59     if (!FIB_TEST_I(_cond, _comment, ##_args)) {                \
60         return 1;                                               \
61         ASSERT(!("FAIL: " _comment));                           \
62     }                                                           \
63 }
64
65 /**
66  * A 'i'm not fussed is this is not efficient' store of test data
67  */
68 typedef struct test_main_t_ {
69     /**
70      * HW if indicies
71      */
72     u32 hw_if_indicies[4];
73     /**
74      * HW interfaces
75      */
76     vnet_hw_interface_t * hw[4];
77
78 } test_main_t;
79 static test_main_t test_main;
80
81 /* fake ethernet device class, distinct from "fake-ethX" */
82 static u8 * format_test_interface_name (u8 * s, va_list * args)
83 {
84   u32 dev_instance = va_arg (*args, u32);
85   return format (s, "test-eth%d", dev_instance);
86 }
87
88 static uword dummy_interface_tx (vlib_main_t * vm,
89                                  vlib_node_runtime_t * node,
90                                  vlib_frame_t * frame)
91 {
92   clib_warning ("you shouldn't be here, leaking buffers...");
93   return frame->n_vectors;
94 }
95
96 static clib_error_t *
97 test_interface_admin_up_down (vnet_main_t * vnm,
98                               u32 hw_if_index,
99                               u32 flags)
100 {
101   u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
102     VNET_HW_INTERFACE_FLAG_LINK_UP : 0;
103   vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
104   return 0;
105 }
106
107 VNET_DEVICE_CLASS (test_interface_device_class,static) = {
108   .name = "Test interface",
109   .format_device_name = format_test_interface_name,
110   .tx_function = dummy_interface_tx,
111   .admin_up_down_function = test_interface_admin_up_down,
112 };
113
114 static u8 *hw_address;
115
116 static int
117 fib_test_mk_intf (u32 ninterfaces)
118 {
119     clib_error_t * error = NULL;
120     test_main_t *tm = &test_main;
121     u8 byte;
122     u32 i;
123
124     ASSERT(ninterfaces <= ARRAY_LEN(tm->hw_if_indicies));
125
126     for (i=0; i<6; i++)
127     {
128         byte = 0xd0+i;
129         vec_add1(hw_address, byte);
130     }
131
132     for (i = 0; i < ninterfaces; i++)
133     {
134         hw_address[5] = i;
135
136         error = ethernet_register_interface(vnet_get_main(),
137                                             test_interface_device_class.index,
138                                             i /* instance */,
139                                             hw_address,
140                                             &tm->hw_if_indicies[i], 
141                                             /* flag change */ 0);
142
143         FIB_TEST((NULL == error), "ADD interface %d", i);
144       
145         error = vnet_hw_interface_set_flags(vnet_get_main(),
146                                             tm->hw_if_indicies[i],
147                                             VNET_HW_INTERFACE_FLAG_LINK_UP);
148         tm->hw[i] = vnet_get_hw_interface(vnet_get_main(),
149                                           tm->hw_if_indicies[i]);
150         vec_validate (ip4_main.fib_index_by_sw_if_index,
151                       tm->hw[i]->sw_if_index);
152         vec_validate (ip6_main.fib_index_by_sw_if_index,
153                       tm->hw[i]->sw_if_index);
154         ip4_main.fib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0;
155         ip6_main.fib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0;
156
157         error = vnet_sw_interface_set_flags(vnet_get_main(),
158                                             tm->hw[i]->sw_if_index,
159                                             VNET_SW_INTERFACE_FLAG_ADMIN_UP);
160         FIB_TEST((NULL == error), "UP interface %d", i);
161     }
162     /*
163      * re-eval after the inevitable realloc
164      */
165     for (i = 0; i < ninterfaces; i++)
166     {
167         tm->hw[i] = vnet_get_hw_interface(vnet_get_main(),
168                                           tm->hw_if_indicies[i]);
169     }
170
171     return (0);
172 }
173
174 #define FIB_TEST_REC_FORW(_rec_prefix, _via_prefix, _bucket)            \
175 {                                                                       \
176     const dpo_id_t *_rec_dpo = fib_entry_contribute_ip_forwarding(      \
177         fib_table_lookup_exact_match(fib_index, (_rec_prefix)));        \
178     const dpo_id_t *_via_dpo = fib_entry_contribute_ip_forwarding(      \
179         fib_table_lookup(fib_index, (_via_prefix)));                    \
180     FIB_TEST(!dpo_cmp(_via_dpo,                                         \
181                       load_balance_get_bucket(_rec_dpo->dpoi_index,     \
182                                               _bucket)),                \
183              "%U is recursive via %U",                                  \
184              format_fib_prefix, (_rec_prefix),                          \
185              format_fib_prefix, _via_prefix);                           \
186 }
187
188 #define FIB_TEST_LB_BUCKET_VIA_ADJ(_prefix, _bucket, _ai)               \
189 {                                                                       \
190     const dpo_id_t *_dpo = fib_entry_contribute_ip_forwarding(          \
191         fib_table_lookup_exact_match(fib_index, (_prefix)));            \
192     const dpo_id_t *_dpo1 =                                             \
193         load_balance_get_bucket(_dpo->dpoi_index, _bucket);             \
194     FIB_TEST(DPO_ADJACENCY == _dpo1->dpoi_type, "type is %U",           \
195              format_dpo_type, _dpo1->dpoi_type);                        \
196     FIB_TEST((_ai == _dpo1->dpoi_index),                                \
197              "%U bucket %d resolves via %U",                            \
198              format_fib_prefix, (_prefix),                              \
199              _bucket,                                                   \
200              format_dpo_id, _dpo1, 0);                                  \
201 }
202
203 #define FIB_TEST_RPF(_cond, _comment, _args...)                 \
204 {                                                               \
205     if (!FIB_TEST_I(_cond, _comment, ##_args)) {                \
206         return (0);                                             \
207     }                                                           \
208 }
209
210 static int
211 fib_test_urpf_is_equal (fib_node_index_t fei,
212                        fib_forward_chain_type_t fct,
213                        u32 num, ...)
214 {
215     dpo_id_t dpo = DPO_INVALID;
216     fib_urpf_list_t *urpf;
217     index_t ui;
218     va_list ap;
219     int ii;
220
221     va_start(ap, num);
222
223     fib_entry_contribute_forwarding(fei, fct, &dpo);
224     ui = load_balance_get_urpf(dpo.dpoi_index);
225
226     urpf = fib_urpf_list_get(ui);
227
228     FIB_TEST_RPF(num == vec_len(urpf->furpf_itfs),
229                  "RPF:%U len %d == %d",
230                  format_fib_urpf_list, ui,
231                  num, vec_len(urpf->furpf_itfs));
232     FIB_TEST_RPF(num == fib_urpf_check_size(ui),
233                  "RPF:%U check-size %d == %d",
234                  format_fib_urpf_list, ui,
235                  num, vec_len(urpf->furpf_itfs));
236
237     for (ii = 0; ii < num; ii++)
238     {
239         adj_index_t ai = va_arg(ap, adj_index_t);
240
241         FIB_TEST_RPF(ai == urpf->furpf_itfs[ii],
242                      "RPF:%d item:%d - %d == %d",
243                      ui, ii, ai, urpf->furpf_itfs[ii]);
244         FIB_TEST_RPF(fib_urpf_check(ui, ai),
245                      "RPF:%d %d found",
246                      ui, ai);
247     }
248
249     dpo_reset(&dpo);
250
251     va_end(ap);
252
253     return (1);
254 }
255
256 static u8*
257 fib_test_build_rewrite (u8 *eth_addr)
258 {
259     u8* rewrite = NULL;
260
261     vec_validate(rewrite, 13);
262
263     memcpy(rewrite, eth_addr, 6);
264     memcpy(rewrite+6, eth_addr, 6);
265
266     return (rewrite);
267 }
268
269 typedef enum fib_test_lb_bucket_type_t_ {
270     FT_LB_LABEL_O_ADJ,
271     FT_LB_LABEL_STACK_O_ADJ,
272     FT_LB_LABEL_O_LB,
273     FT_LB_O_LB,
274     FT_LB_SPECIAL,
275     FT_LB_ADJ,
276     FT_LB_INTF,
277 } fib_test_lb_bucket_type_t;
278
279 typedef struct fib_test_lb_bucket_t_ {
280     fib_test_lb_bucket_type_t type;
281
282     union
283     {
284         struct
285         {
286             mpls_eos_bit_t eos;
287             mpls_label_t label;
288             u8 ttl;
289             adj_index_t adj;
290         } label_o_adj;
291         struct
292         {
293             mpls_eos_bit_t eos;
294             mpls_label_t label_stack[8];
295             u8 label_stack_size;
296             u8 ttl;
297             adj_index_t adj;
298         } label_stack_o_adj;
299         struct
300         {
301             mpls_eos_bit_t eos;
302             mpls_label_t label;
303             u8 ttl;
304             index_t lb;
305         } label_o_lb;
306         struct
307         {
308             index_t adj;
309         } adj;
310         struct
311         {
312             index_t lb;
313         } lb;
314         struct
315         {
316             index_t adj;
317         } special;
318     };
319 } fib_test_lb_bucket_t;
320
321 typedef enum fib_test_rep_bucket_type_t_ {
322     FT_REP_LABEL_O_ADJ,
323     FT_REP_DISP_MFIB_LOOKUP,
324     FT_REP_INTF,
325 } fib_test_rep_bucket_type_t;
326
327 typedef struct fib_test_rep_bucket_t_ {
328     fib_test_rep_bucket_type_t type;
329
330     union
331     {
332         struct
333         {
334             mpls_eos_bit_t eos;
335             mpls_label_t label;
336             u8 ttl;
337             adj_index_t adj;
338         } label_o_adj;
339         struct
340         {
341             adj_index_t adj;
342         } adj;
343    };
344 } fib_test_rep_bucket_t;
345
346 #define FIB_TEST_LB(_cond, _comment, _args...)                  \
347 {                                                               \
348     if (!FIB_TEST_I(_cond, _comment, ##_args)) {                \
349         return (0);                                             \
350     }                                                           \
351 }
352
353 int
354 fib_test_validate_rep_v (const replicate_t *rep,
355                          u16 n_buckets,
356                          va_list ap)
357 {
358     const fib_test_rep_bucket_t *exp;
359     const dpo_id_t *dpo;
360     int bucket;
361
362     FIB_TEST_LB((n_buckets == rep->rep_n_buckets),
363                 "n_buckets = %d", rep->rep_n_buckets);
364
365     for (bucket = 0; bucket < n_buckets; bucket++)
366     {
367         exp = va_arg(ap, fib_test_rep_bucket_t*);
368
369         dpo = replicate_get_bucket_i(rep, bucket);
370
371         switch (exp->type)
372         {
373         case FT_REP_LABEL_O_ADJ:
374             {
375                 const mpls_label_dpo_t *mld;
376                 mpls_label_t hdr;
377                 FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
378                             "bucket %d stacks on %U",
379                             bucket,
380                             format_dpo_type, dpo->dpoi_type);
381             
382                 mld = mpls_label_dpo_get(dpo->dpoi_index);
383                 hdr = clib_net_to_host_u32(mld->mld_hdr[0].label_exp_s_ttl);
384
385                 FIB_TEST_LB((vnet_mpls_uc_get_label(hdr) ==
386                              exp->label_o_adj.label),
387                             "bucket %d stacks on label %d",
388                             bucket,
389                             exp->label_o_adj.label);
390
391                 FIB_TEST_LB((vnet_mpls_uc_get_s(hdr) ==
392                              exp->label_o_adj.eos),
393                             "bucket %d stacks on label %d %U",
394                             bucket,
395                             exp->label_o_adj.label,
396                             format_mpls_eos_bit, exp->label_o_adj.eos);
397
398                 FIB_TEST_LB((DPO_ADJACENCY_INCOMPLETE == mld->mld_dpo.dpoi_type),
399                             "bucket %d label stacks on %U",
400                             bucket,
401                             format_dpo_type, mld->mld_dpo.dpoi_type);
402
403                 FIB_TEST_LB((exp->label_o_adj.adj == mld->mld_dpo.dpoi_index),
404                             "bucket %d label stacks on adj %d",
405                             bucket,
406                             exp->label_o_adj.adj);
407             }
408             break;
409         case FT_REP_INTF:
410             FIB_TEST_LB((DPO_INTERFACE == dpo->dpoi_type),
411                         "bucket %d stacks on %U",
412                         bucket,
413                         format_dpo_type, dpo->dpoi_type);
414
415             FIB_TEST_LB((exp->adj.adj == dpo->dpoi_index),
416                         "bucket %d stacks on adj %d",
417                         bucket,
418                         exp->adj.adj);
419             break;
420         case FT_REP_DISP_MFIB_LOOKUP:
421 //            ASSERT(0);
422             break;
423         }
424     }
425
426     return (!0);
427 }
428
429 int
430 fib_test_validate_lb_v (const load_balance_t *lb,
431                         u16 n_buckets,
432                         va_list ap)
433 {
434     const dpo_id_t *dpo;
435     int bucket;
436
437     FIB_TEST_LB((n_buckets == lb->lb_n_buckets), "n_buckets = %d", lb->lb_n_buckets);
438
439     for (bucket = 0; bucket < n_buckets; bucket++)
440     {
441         const fib_test_lb_bucket_t *exp;
442
443         exp = va_arg(ap, fib_test_lb_bucket_t*);
444         dpo = load_balance_get_bucket_i(lb, bucket);
445
446         switch (exp->type)
447         {
448         case FT_LB_LABEL_STACK_O_ADJ:
449             {
450                 const mpls_label_dpo_t *mld;
451                 mpls_label_t hdr;
452                 u32 ii;
453
454                 FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
455                            "bucket %d stacks on %U",
456                            bucket,
457                            format_dpo_type, dpo->dpoi_type);
458             
459                 mld = mpls_label_dpo_get(dpo->dpoi_index);
460
461                 FIB_TEST_LB(exp->label_stack_o_adj.label_stack_size == mld->mld_n_labels,
462                             "label stack size",
463                             mld->mld_n_labels);
464
465                 for (ii = 0; ii < mld->mld_n_labels; ii++)
466                 {
467                     hdr = clib_net_to_host_u32(mld->mld_hdr[ii].label_exp_s_ttl);
468                     FIB_TEST_LB((vnet_mpls_uc_get_label(hdr) ==
469                                  exp->label_stack_o_adj.label_stack[ii]),
470                                 "bucket %d stacks on label %d",
471                                 bucket,
472                                 exp->label_stack_o_adj.label_stack[ii]);
473
474                     if (ii == mld->mld_n_labels-1)
475                     {
476                         FIB_TEST_LB((vnet_mpls_uc_get_s(hdr) ==
477                                      exp->label_o_adj.eos),
478                                     "bucket %d stacks on label %d %U!=%U",
479                                     bucket,
480                                     exp->label_stack_o_adj.label_stack[ii],
481                                     format_mpls_eos_bit, exp->label_o_adj.eos,
482                                     format_mpls_eos_bit, vnet_mpls_uc_get_s(hdr));
483                     }
484                     else
485                     {
486                         FIB_TEST_LB((vnet_mpls_uc_get_s(hdr) == MPLS_NON_EOS),
487                                     "bucket %d stacks on label %d %U",
488                                     bucket,
489                                     exp->label_stack_o_adj.label_stack[ii],
490                                     format_mpls_eos_bit, vnet_mpls_uc_get_s(hdr));
491                     }
492                 }
493
494                 FIB_TEST_LB((DPO_ADJACENCY_INCOMPLETE == mld->mld_dpo.dpoi_type),
495                             "bucket %d label stacks on %U",
496                             bucket,
497                             format_dpo_type, mld->mld_dpo.dpoi_type);
498
499                 FIB_TEST_LB((exp->label_stack_o_adj.adj == mld->mld_dpo.dpoi_index),
500                             "bucket %d label stacks on adj %d",
501                             bucket,
502                             exp->label_stack_o_adj.adj);
503             }
504             break;
505         case FT_LB_LABEL_O_ADJ:
506             {
507                 const mpls_label_dpo_t *mld;
508                 mpls_label_t hdr;
509                 FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
510                            "bucket %d stacks on %U",
511                            bucket,
512                            format_dpo_type, dpo->dpoi_type);
513             
514                 mld = mpls_label_dpo_get(dpo->dpoi_index);
515                 hdr = clib_net_to_host_u32(mld->mld_hdr[0].label_exp_s_ttl);
516
517                 FIB_TEST_LB((vnet_mpls_uc_get_label(hdr) ==
518                              exp->label_o_adj.label),
519                             "bucket %d stacks on label %d",
520                             bucket,
521                             exp->label_o_adj.label);
522
523                 FIB_TEST_LB((vnet_mpls_uc_get_s(hdr) ==
524                              exp->label_o_adj.eos),
525                             "bucket %d stacks on label %d %U",
526                             bucket,
527                             exp->label_o_adj.label,
528                             format_mpls_eos_bit, exp->label_o_adj.eos);
529
530                 FIB_TEST_LB((DPO_ADJACENCY_INCOMPLETE == mld->mld_dpo.dpoi_type),
531                             "bucket %d label stacks on %U",
532                             bucket,
533                             format_dpo_type, mld->mld_dpo.dpoi_type);
534
535                 FIB_TEST_LB((exp->label_o_adj.adj == mld->mld_dpo.dpoi_index),
536                             "bucket %d label stacks on adj %d",
537                             bucket,
538                             exp->label_o_adj.adj);
539             }
540             break;
541         case FT_LB_LABEL_O_LB:
542             {
543                 const mpls_label_dpo_t *mld;
544                 mpls_label_t hdr;
545
546                 FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
547                            "bucket %d stacks on %U",
548                            bucket,
549                            format_dpo_type, dpo->dpoi_type);
550             
551                 mld = mpls_label_dpo_get(dpo->dpoi_index);
552                 hdr = clib_net_to_host_u32(mld->mld_hdr[0].label_exp_s_ttl);
553
554                 FIB_TEST_LB(1 == mld->mld_n_labels, "label stack size",
555                             mld->mld_n_labels);
556                 FIB_TEST_LB((vnet_mpls_uc_get_label(hdr) ==
557                              exp->label_o_lb.label),
558                             "bucket %d stacks on label %d",
559                             bucket,
560                             exp->label_o_lb.label);
561
562                 FIB_TEST_LB((vnet_mpls_uc_get_s(hdr) ==
563                              exp->label_o_lb.eos),
564                             "bucket %d stacks on label %d %U",
565                             bucket,
566                             exp->label_o_lb.label,
567                             format_mpls_eos_bit, exp->label_o_lb.eos);
568
569                 FIB_TEST_LB((DPO_LOAD_BALANCE == mld->mld_dpo.dpoi_type),
570                             "bucket %d label stacks on %U",
571                             bucket,
572                             format_dpo_type, mld->mld_dpo.dpoi_type);
573
574                 FIB_TEST_LB((exp->label_o_lb.lb == mld->mld_dpo.dpoi_index),
575                             "bucket %d label stacks on LB %d",
576                             bucket,
577                             exp->label_o_lb.lb);
578             }
579             break;
580         case FT_LB_ADJ:
581             FIB_TEST_I(((DPO_ADJACENCY == dpo->dpoi_type) ||
582                         (DPO_ADJACENCY_INCOMPLETE == dpo->dpoi_type)),
583                        "bucket %d stacks on %U",
584                        bucket,
585                        format_dpo_type, dpo->dpoi_type);
586             FIB_TEST_LB((exp->adj.adj == dpo->dpoi_index),
587                         "bucket %d stacks on adj %d",
588                         bucket,
589                         exp->adj.adj);
590             break;
591         case FT_LB_INTF:
592             FIB_TEST_I((DPO_INTERFACE == dpo->dpoi_type),
593                        "bucket %d stacks on %U",
594                        bucket,
595                        format_dpo_type, dpo->dpoi_type);
596             FIB_TEST_LB((exp->adj.adj == dpo->dpoi_index),
597                         "bucket %d stacks on adj %d",
598                         bucket,
599                         exp->adj.adj);
600             break;
601         case FT_LB_O_LB:
602             FIB_TEST_I((DPO_LOAD_BALANCE == dpo->dpoi_type),
603                        "bucket %d stacks on %U",
604                        bucket,
605                        format_dpo_type, dpo->dpoi_type);
606             FIB_TEST_LB((exp->lb.lb == dpo->dpoi_index),
607                         "bucket %d stacks on lb %d",
608                         bucket,
609                         exp->lb.lb);
610             break;
611         case FT_LB_SPECIAL:
612             FIB_TEST_I((DPO_DROP == dpo->dpoi_type),
613                        "bucket %d stacks on %U",
614                        bucket,
615                        format_dpo_type, dpo->dpoi_type);
616             FIB_TEST_LB((exp->special.adj == dpo->dpoi_index),
617                         "bucket %d stacks on drop %d",
618                         bucket,
619                         exp->special.adj);
620             break;
621         }
622     }
623     return (!0);
624 }
625
626 int
627 fib_test_validate_entry (fib_node_index_t fei,
628                          fib_forward_chain_type_t fct,
629                          u16 n_buckets,
630                          ...)
631 {
632     dpo_id_t dpo = DPO_INVALID;
633     fib_prefix_t pfx;
634     index_t fw_lbi;
635     u32 fib_index;
636     va_list ap;
637     int res;
638
639     va_start(ap, n_buckets);
640
641     fib_entry_get_prefix(fei, &pfx);
642     fib_index = fib_entry_get_fib_index(fei);
643     fib_entry_contribute_forwarding(fei, fct, &dpo);
644
645     if (DPO_REPLICATE == dpo.dpoi_type)
646     {
647         const replicate_t *rep;
648
649         rep = replicate_get(dpo.dpoi_index);
650         res = fib_test_validate_rep_v(rep, n_buckets, ap);
651     }
652     else
653     {
654         const load_balance_t *lb;
655
656         FIB_TEST_LB((DPO_LOAD_BALANCE == dpo.dpoi_type),
657                     "Entry links to %U",
658                     format_dpo_type, dpo.dpoi_type);
659
660         lb = load_balance_get(dpo.dpoi_index);
661         res = fib_test_validate_lb_v(lb, n_buckets, ap);
662
663         /*
664          * ensure that the LB contributed by the entry is the
665          * same as the LB in the forwarding tables
666          */
667         if (fct == fib_entry_get_default_chain_type(fib_entry_get(fei)))
668         {
669             switch (pfx.fp_proto)
670             {
671             case FIB_PROTOCOL_IP4:
672                 fw_lbi = ip4_fib_forwarding_lookup(fib_index, &pfx.fp_addr.ip4);
673                 break;
674             case FIB_PROTOCOL_IP6:
675                 fw_lbi = ip6_fib_table_fwding_lookup(&ip6_main, fib_index, &pfx.fp_addr.ip6);
676                 break;
677             case FIB_PROTOCOL_MPLS:
678                 {
679                     mpls_unicast_header_t hdr = {
680                         .label_exp_s_ttl = 0,
681                     };
682
683                     vnet_mpls_uc_set_label(&hdr.label_exp_s_ttl, pfx.fp_label);
684                     vnet_mpls_uc_set_s(&hdr.label_exp_s_ttl, pfx.fp_eos);
685                     hdr.label_exp_s_ttl = clib_host_to_net_u32(hdr.label_exp_s_ttl);
686
687                     fw_lbi = mpls_fib_table_forwarding_lookup(fib_index, &hdr);
688                     break;
689                 }
690             default:
691                 fw_lbi = 0;
692             }
693             FIB_TEST_LB((fw_lbi == dpo.dpoi_index),
694                         "Contributed LB = FW LB: %U\n %U",
695                         format_load_balance, fw_lbi, 0,
696                         format_load_balance, dpo.dpoi_index, 0);
697         }
698     }
699
700     dpo_reset(&dpo);
701
702     va_end(ap);
703
704     return (res);
705 }
706
707 static int
708 fib_test_v4 (void)
709 {
710     /*
711      * In the default table check for the presence and correct forwarding
712      * of the special entries
713      */
714     fib_node_index_t dfrt, fei, ai, ai2, locked_ai, ai_01, ai_02, ai_03;
715     const dpo_id_t *dpo, *dpo1, *dpo2, *dpo_drop;
716     const ip_adjacency_t *adj;
717     const load_balance_t *lb;
718     test_main_t *tm;
719     u32 fib_index;
720     int ii;
721
722     /* via 10.10.10.1 */
723     ip46_address_t nh_10_10_10_1 = {
724         .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a01),
725     };
726     /* via 10.10.10.2 */
727     ip46_address_t nh_10_10_10_2 = {
728         .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a02),
729     };
730
731     tm = &test_main;
732
733     /* Find or create FIB table 11 */
734     fib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4, 11);
735
736     for (ii = 0; ii < 4; ii++)
737     {
738         ip4_main.fib_index_by_sw_if_index[tm->hw[ii]->sw_if_index] = fib_index;
739     }
740
741     fib_prefix_t pfx_0_0_0_0_s_0 = {
742         .fp_len = 0,
743         .fp_proto = FIB_PROTOCOL_IP4,
744         .fp_addr = {
745             .ip4 = {
746                 {0}
747             },
748         },
749     };
750
751     fib_prefix_t pfx = {
752         .fp_len = 0,
753         .fp_proto = FIB_PROTOCOL_IP4,
754         .fp_addr = {
755             .ip4 = {
756                 {0}
757             },
758         },
759     };
760
761     dpo_drop = drop_dpo_get(DPO_PROTO_IP4);
762
763     dfrt = fib_table_lookup(fib_index, &pfx_0_0_0_0_s_0);
764     FIB_TEST((FIB_NODE_INDEX_INVALID != dfrt), "default route present");
765     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(dfrt)),
766              "Default route is DROP");
767
768     pfx.fp_len = 32;
769     fei = fib_table_lookup(fib_index, &pfx);
770     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "all zeros route present");
771     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
772              "all 0s route is DROP");
773
774     pfx.fp_addr.ip4.as_u32 = clib_host_to_net_u32(0xffffffff);
775     pfx.fp_len = 32;
776     fei = fib_table_lookup(fib_index, &pfx);
777     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "all ones route present");
778     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
779              "all 1s route is DROP");
780
781     pfx.fp_addr.ip4.as_u32 = clib_host_to_net_u32(0xe0000000);
782     pfx.fp_len = 8;
783     fei = fib_table_lookup(fib_index, &pfx);
784     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "all-mcast route present");
785     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
786              "all-mcast route is DROP");
787
788     pfx.fp_addr.ip4.as_u32 = clib_host_to_net_u32(0xf0000000);
789     pfx.fp_len = 8;
790     fei = fib_table_lookup(fib_index, &pfx);
791     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "class-e route present");
792     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
793              "class-e route is DROP");
794
795     /*
796      * at this stage there are 5 entries in the test FIB (plus 5 in the default),
797      * all of which are special sourced and so none of which share path-lists.
798      * There are also 2 entries, and 2 non-shared path-lists, in the v6 default
799      * table, and 4 path-lists in the v6 MFIB table
800      */
801 #define ENBR (5+5+2)
802 #define PNBR (5+5+6)
803     FIB_TEST((0 == fib_path_list_db_size()),   "path list DB is empty");
804     FIB_TEST((PNBR == fib_path_list_pool_size()), "path list pool size is %d",
805              fib_path_list_pool_size());
806     FIB_TEST((ENBR == fib_entry_pool_size()), "entry pool size is %d",
807              fib_entry_pool_size());
808
809     /*
810      * add interface routes.
811      *  validate presence of /24 attached and /32 recieve.
812      *  test for the presence of the receive address in the glean and local adj
813      */
814     fib_prefix_t local_pfx = {
815         .fp_len = 24,
816         .fp_proto = FIB_PROTOCOL_IP4,
817         .fp_addr = {
818             .ip4 = {
819                 .as_u32 = clib_host_to_net_u32(0x0a0a0a0a),
820             },
821         },
822     };
823
824     fib_table_entry_update_one_path(fib_index, &local_pfx,
825                                     FIB_SOURCE_INTERFACE,
826                                     (FIB_ENTRY_FLAG_CONNECTED |
827                                      FIB_ENTRY_FLAG_ATTACHED),
828                                     FIB_PROTOCOL_IP4,
829                                     NULL,
830                                     tm->hw[0]->sw_if_index,
831                                     ~0, // invalid fib index
832                                     1, // weight
833                                     NULL,
834                                     FIB_ROUTE_PATH_FLAG_NONE);
835     fei = fib_table_lookup(fib_index, &local_pfx);
836     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "attached interface route present");
837     FIB_TEST(((FIB_ENTRY_FLAG_ATTACHED | FIB_ENTRY_FLAG_CONNECTED) ==
838               fib_entry_get_flags(fei)),
839              "Flags set on attached interface");
840
841     ai = fib_entry_get_adj(fei);
842     FIB_TEST((FIB_NODE_INDEX_INVALID != ai), "attached interface route adj present");
843     adj = adj_get(ai);
844     FIB_TEST((IP_LOOKUP_NEXT_GLEAN == adj->lookup_next_index),
845              "attached interface adj is glean");
846     FIB_TEST((0 == ip46_address_cmp(&local_pfx.fp_addr,
847                                     &adj->sub_type.glean.receive_addr)),
848               "attached interface adj is receive ok");
849
850     local_pfx.fp_len = 32;
851     fib_table_entry_update_one_path(fib_index, &local_pfx,
852                                     FIB_SOURCE_INTERFACE,
853                                     (FIB_ENTRY_FLAG_CONNECTED |
854                                      FIB_ENTRY_FLAG_LOCAL),
855                                     FIB_PROTOCOL_IP4,
856                                     NULL,
857                                     tm->hw[0]->sw_if_index,
858                                     ~0, // invalid fib index
859                                     1, // weight
860                                     NULL,
861                                     FIB_ROUTE_PATH_FLAG_NONE);
862     fei = fib_table_lookup(fib_index, &local_pfx);
863     FIB_TEST(((FIB_ENTRY_FLAG_LOCAL | FIB_ENTRY_FLAG_CONNECTED) ==
864               fib_entry_get_flags(fei)),
865              "Flags set on local interface");
866
867     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local interface route present");
868
869     dpo = fib_entry_contribute_ip_forwarding(fei);
870     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 0),
871              "RPF list for local length 0");
872     dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
873     FIB_TEST((DPO_RECEIVE == dpo->dpoi_type),
874              "local interface adj is local");
875     receive_dpo_t *rd = receive_dpo_get(dpo->dpoi_index);
876
877     FIB_TEST((0 == ip46_address_cmp(&local_pfx.fp_addr,
878                                     &rd->rd_addr)),
879               "local interface adj is receive ok");
880
881     FIB_TEST((2 == fib_table_get_num_entries(fib_index,
882                                              FIB_PROTOCOL_IP4,
883                                              FIB_SOURCE_INTERFACE)),
884              "2 Interface Source'd prefixes");
885
886     /*
887      * +2 interface routes +2 non-shared path-lists
888      */
889     FIB_TEST((0 == fib_path_list_db_size()),   "path list DB is empty");
890     FIB_TEST((PNBR+2 == fib_path_list_pool_size()), "path list pool size is%d",
891              fib_path_list_pool_size());
892     FIB_TEST((ENBR+2 == fib_entry_pool_size()), "entry pool size is %d",
893              fib_entry_pool_size());
894
895     /*
896      * Modify the default route to be via an adj not yet known.
897      * this sources the defalut route with the API source, which is
898      * a higher preference to the DEFAULT_ROUTE source
899      */
900     pfx.fp_addr.ip4.as_u32 = 0;
901     pfx.fp_len = 0;
902     fib_table_entry_path_add(fib_index, &pfx,
903                              FIB_SOURCE_API,
904                              FIB_ENTRY_FLAG_NONE,
905                              FIB_PROTOCOL_IP4,
906                              &nh_10_10_10_1,
907                              tm->hw[0]->sw_if_index,
908                              ~0, // invalid fib index
909                              1,
910                              NULL,
911                              FIB_ROUTE_PATH_FLAG_NONE);
912     fei = fib_table_lookup(fib_index, &pfx);
913     FIB_TEST((FIB_ENTRY_FLAG_NONE == fib_entry_get_flags(fei)),
914              "Flags set on API route");
915
916     FIB_TEST((fei == dfrt), "default route same index");
917     ai = fib_entry_get_adj(fei);
918     FIB_TEST((FIB_NODE_INDEX_INVALID != ai), "default route adj present");
919     adj = adj_get(ai);
920     FIB_TEST((IP_LOOKUP_NEXT_ARP == adj->lookup_next_index),
921              "adj is incomplete");
922     FIB_TEST((0 == ip46_address_cmp(&nh_10_10_10_1, &adj->sub_type.nbr.next_hop)),
923               "adj nbr next-hop ok");
924     FIB_TEST((1 == fib_table_get_num_entries(fib_index,
925                                              FIB_PROTOCOL_IP4,
926                                              FIB_SOURCE_API)),
927              "1 API Source'd prefixes");
928
929     /*
930      * find the adj in the shared db
931      */
932     locked_ai = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
933                                     VNET_LINK_IP4,
934                                     &nh_10_10_10_1,
935                                     tm->hw[0]->sw_if_index);
936     FIB_TEST((locked_ai == ai), "ADJ NBR DB find");
937     adj_unlock(locked_ai);
938
939     /*
940      * +1 shared path-list
941      */
942     FIB_TEST((1 == fib_path_list_db_size()),   "path list DB population:%d",
943              fib_path_list_db_size());
944     FIB_TEST((PNBR+3 == fib_path_list_pool_size()), "path list pool size is%d",
945              fib_path_list_pool_size());
946     FIB_TEST((ENBR+2 == fib_entry_pool_size()), "entry pool size is %d",
947              fib_entry_pool_size());
948
949     /*
950      * remove the API source from the default route. We expected
951      * the route to remain, sourced by DEFAULT_ROUTE, and hence a DROP
952      */
953     pfx.fp_addr.ip4.as_u32 = 0;
954     pfx.fp_len = 0;
955     fib_table_entry_path_remove(fib_index, &pfx,
956                                 FIB_SOURCE_API,
957                                 FIB_PROTOCOL_IP4,
958                                 &nh_10_10_10_1,
959                                 tm->hw[0]->sw_if_index,
960                                 ~0, // non-recursive path, so no FIB index
961                                 1,
962                                 FIB_ROUTE_PATH_FLAG_NONE);
963
964     fei = fib_table_lookup(fib_index, &pfx);
965
966     FIB_TEST((fei == dfrt), "default route same index");
967     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
968              "Default route is DROP");
969
970     /*
971      * -1 shared-path-list
972      */
973     FIB_TEST((0 == fib_path_list_db_size()),   "path list DB is empty");
974     FIB_TEST((PNBR+2 == fib_path_list_pool_size()), "path list pool size is%d",
975              fib_path_list_pool_size());
976     FIB_TEST((ENBR+2 == fib_entry_pool_size()), "entry pool size is %d",
977              fib_entry_pool_size());
978
979     /*
980      * Add an 2 ARP entry => a complete ADJ plus adj-fib.
981      */
982     fib_prefix_t pfx_10_10_10_1_s_32 = {
983         .fp_len = 32,
984         .fp_proto = FIB_PROTOCOL_IP4,
985         .fp_addr = {
986             /* 10.10.10.1 */
987             .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a01),
988         },
989     };
990     fib_prefix_t pfx_10_10_10_2_s_32 = {
991         .fp_len = 32,
992         .fp_proto = FIB_PROTOCOL_IP4,
993         .fp_addr = {
994             /* 10.10.10.2 */
995             .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a02),
996         },
997     };
998     fib_prefix_t pfx_11_11_11_11_s_32 = {
999         .fp_len = 32,
1000         .fp_proto = FIB_PROTOCOL_IP4,
1001         .fp_addr = {
1002             /* 11.11.11.11 */
1003             .ip4.as_u32 = clib_host_to_net_u32(0x0b0b0b0b),
1004         },
1005     };
1006     u8 eth_addr[] = {
1007         0xde, 0xde, 0xde, 0xba, 0xba, 0xba,
1008     };
1009
1010     ip46_address_t nh_12_12_12_12 = {
1011         .ip4.as_u32 = clib_host_to_net_u32(0x0c0c0c0c),
1012     };
1013     adj_index_t ai_12_12_12_12;
1014
1015     /*
1016      * Add a route via an incomplete ADJ. then complete the ADJ
1017      * Expect the route LB is updated to use complete adj type.
1018      */
1019     fei = fib_table_entry_update_one_path(fib_index,
1020                                           &pfx_11_11_11_11_s_32,
1021                                           FIB_SOURCE_API,
1022                                           FIB_ENTRY_FLAG_ATTACHED,
1023                                           FIB_PROTOCOL_IP4,
1024                                           &pfx_10_10_10_1_s_32.fp_addr,
1025                                           tm->hw[0]->sw_if_index,
1026                                           ~0, // invalid fib index
1027                                           1,
1028                                           NULL,
1029                                           FIB_ROUTE_PATH_FLAG_NONE);
1030
1031     dpo = fib_entry_contribute_ip_forwarding(fei);
1032     dpo1 = load_balance_get_bucket(dpo->dpoi_index, 0);
1033     FIB_TEST(DPO_ADJACENCY_INCOMPLETE == dpo1->dpoi_type,
1034              "11.11.11.11/32 via incomplete adj");
1035
1036     ai_01 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
1037                                 VNET_LINK_IP4,
1038                                 &pfx_10_10_10_1_s_32.fp_addr,
1039                                 tm->hw[0]->sw_if_index);
1040     FIB_TEST((FIB_NODE_INDEX_INVALID != ai_01), "adj created");
1041     adj = adj_get(ai_01);
1042     FIB_TEST((IP_LOOKUP_NEXT_ARP == adj->lookup_next_index),
1043              "adj is incomplete");
1044     FIB_TEST((0 == ip46_address_cmp(&pfx_10_10_10_1_s_32.fp_addr,
1045                                     &adj->sub_type.nbr.next_hop)),
1046               "adj nbr next-hop ok");
1047
1048     adj_nbr_update_rewrite(ai_01, ADJ_NBR_REWRITE_FLAG_COMPLETE,
1049                            fib_test_build_rewrite(eth_addr));
1050     FIB_TEST((IP_LOOKUP_NEXT_REWRITE == adj->lookup_next_index),
1051              "adj is complete");
1052     FIB_TEST((0 == ip46_address_cmp(&pfx_10_10_10_1_s_32.fp_addr,
1053                                     &adj->sub_type.nbr.next_hop)),
1054               "adj nbr next-hop ok");
1055     ai = fib_entry_get_adj(fei);
1056     FIB_TEST((ai_01 == ai), "ADJ-FIB resolves via adj");
1057
1058     dpo = fib_entry_contribute_ip_forwarding(fei);
1059     dpo1 = load_balance_get_bucket(dpo->dpoi_index, 0);
1060     FIB_TEST(DPO_ADJACENCY == dpo1->dpoi_type,
1061              "11.11.11.11/32 via complete adj");
1062     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 1,
1063                                     tm->hw[0]->sw_if_index),
1064              "RPF list for adj-fib contains adj");
1065
1066     ai_12_12_12_12 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
1067                                          VNET_LINK_IP4,
1068                                          &nh_12_12_12_12,
1069                                          tm->hw[1]->sw_if_index);
1070     FIB_TEST((FIB_NODE_INDEX_INVALID != ai_12_12_12_12), "adj created");
1071     adj = adj_get(ai_12_12_12_12);
1072     FIB_TEST((IP_LOOKUP_NEXT_ARP == adj->lookup_next_index),
1073              "adj is incomplete");
1074     FIB_TEST((0 == ip46_address_cmp(&nh_12_12_12_12,
1075                                     &adj->sub_type.nbr.next_hop)),
1076               "adj nbr next-hop ok");
1077     adj_nbr_update_rewrite(ai_12_12_12_12, ADJ_NBR_REWRITE_FLAG_COMPLETE,
1078                            fib_test_build_rewrite(eth_addr));
1079     FIB_TEST((IP_LOOKUP_NEXT_REWRITE == adj->lookup_next_index),
1080              "adj is complete");
1081
1082     /*
1083      * add the adj fib
1084      */
1085     fei = fib_table_entry_update_one_path(fib_index,
1086                                           &pfx_10_10_10_1_s_32,
1087                                           FIB_SOURCE_ADJ,
1088                                           FIB_ENTRY_FLAG_ATTACHED,
1089                                           FIB_PROTOCOL_IP4,
1090                                           &pfx_10_10_10_1_s_32.fp_addr,
1091                                           tm->hw[0]->sw_if_index,
1092                                           ~0, // invalid fib index
1093                                           1,
1094                                           NULL,
1095                                           FIB_ROUTE_PATH_FLAG_NONE);
1096     FIB_TEST((FIB_ENTRY_FLAG_ATTACHED  == fib_entry_get_flags(fei)),
1097              "Flags set on adj-fib");
1098     ai = fib_entry_get_adj(fei);
1099     FIB_TEST((ai_01 == ai), "ADJ-FIB resolves via adj");
1100
1101     fib_table_entry_path_remove(fib_index,
1102                                 &pfx_11_11_11_11_s_32,
1103                                 FIB_SOURCE_API,
1104                                 FIB_PROTOCOL_IP4,
1105                                 &pfx_10_10_10_1_s_32.fp_addr,
1106                                 tm->hw[0]->sw_if_index,
1107                                 ~0, // invalid fib index
1108                                 1,
1109                                 FIB_ROUTE_PATH_FLAG_NONE);
1110
1111     eth_addr[5] = 0xb2;
1112
1113     ai_02 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
1114                                 VNET_LINK_IP4,
1115                                 &pfx_10_10_10_2_s_32.fp_addr,
1116                                 tm->hw[0]->sw_if_index);
1117     FIB_TEST((FIB_NODE_INDEX_INVALID != ai_02), "adj created");
1118     adj = adj_get(ai_02);
1119     FIB_TEST((IP_LOOKUP_NEXT_ARP == adj->lookup_next_index),
1120              "adj is incomplete");
1121     FIB_TEST((0 == ip46_address_cmp(&pfx_10_10_10_2_s_32.fp_addr,
1122                                     &adj->sub_type.nbr.next_hop)),
1123               "adj nbr next-hop ok");
1124
1125     adj_nbr_update_rewrite(ai_02, ADJ_NBR_REWRITE_FLAG_COMPLETE,
1126                            fib_test_build_rewrite(eth_addr));
1127     FIB_TEST((IP_LOOKUP_NEXT_REWRITE == adj->lookup_next_index),
1128              "adj is complete");
1129     FIB_TEST((0 == ip46_address_cmp(&pfx_10_10_10_2_s_32.fp_addr,
1130                                     &adj->sub_type.nbr.next_hop)),
1131               "adj nbr next-hop ok");
1132     FIB_TEST((ai_01 != ai_02), "ADJs are different");
1133
1134     fib_table_entry_update_one_path(fib_index,
1135                                     &pfx_10_10_10_2_s_32,
1136                                     FIB_SOURCE_ADJ,
1137                                     FIB_ENTRY_FLAG_ATTACHED,
1138                                     FIB_PROTOCOL_IP4,
1139                                     &pfx_10_10_10_2_s_32.fp_addr,
1140                                     tm->hw[0]->sw_if_index,
1141                                     ~0, // invalid fib index
1142                                     1,
1143                                     NULL,
1144                                     FIB_ROUTE_PATH_FLAG_NONE);
1145
1146     fei = fib_table_lookup(fib_index, &pfx_10_10_10_2_s_32);
1147     ai = fib_entry_get_adj(fei);
1148     FIB_TEST((ai_02 == ai), "ADJ-FIB resolves via adj");
1149
1150     /*
1151      * +2 adj-fibs, and their non-shared path-lists
1152      */
1153     FIB_TEST((0 == fib_path_list_db_size()),   "path list DB is empty");
1154     FIB_TEST((PNBR+4 == fib_path_list_pool_size()), "path list pool size is %d",
1155              fib_path_list_pool_size());
1156     FIB_TEST((ENBR+4 == fib_entry_pool_size()), "entry pool size is %d",
1157              fib_entry_pool_size());
1158
1159     /*
1160      * Add 2 routes via the first ADJ. ensure path-list sharing
1161      */
1162     fib_prefix_t pfx_1_1_1_1_s_32 = {
1163         .fp_len = 32,
1164         .fp_proto = FIB_PROTOCOL_IP4,
1165         .fp_addr = {
1166             /* 1.1.1.1/32 */
1167             .ip4.as_u32 = clib_host_to_net_u32(0x01010101),
1168         },
1169     };
1170
1171     fib_table_entry_path_add(fib_index,
1172                              &pfx_1_1_1_1_s_32,
1173                              FIB_SOURCE_API,
1174                              FIB_ENTRY_FLAG_NONE,
1175                              FIB_PROTOCOL_IP4,
1176                              &nh_10_10_10_1,
1177                              tm->hw[0]->sw_if_index,
1178                              ~0, // invalid fib index
1179                              1,
1180                              NULL,
1181                              FIB_ROUTE_PATH_FLAG_NONE);
1182     fei = fib_table_lookup(fib_index, &pfx_1_1_1_1_s_32);
1183     ai = fib_entry_get_adj(fei);
1184     FIB_TEST((ai_01 == ai), "1.1.1.1 resolves via 10.10.10.1");
1185
1186     /*
1187      * +1 entry and a shared path-list
1188      */
1189     FIB_TEST((1 == fib_path_list_db_size()),   "path list DB is empty");
1190     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
1191              fib_path_list_pool_size());
1192     FIB_TEST((ENBR+5 == fib_entry_pool_size()), "entry pool size is %d",
1193              fib_entry_pool_size());
1194
1195     /* 1.1.2.0/24 */
1196     fib_prefix_t pfx_1_1_2_0_s_24 = {
1197         .fp_len = 24,
1198         .fp_proto = FIB_PROTOCOL_IP4,
1199         .fp_addr = {
1200             .ip4.as_u32 = clib_host_to_net_u32(0x01010200),
1201         }
1202     };
1203
1204     fib_table_entry_path_add(fib_index,
1205                              &pfx_1_1_2_0_s_24,
1206                              FIB_SOURCE_API,
1207                              FIB_ENTRY_FLAG_NONE,
1208                              FIB_PROTOCOL_IP4,
1209                              &nh_10_10_10_1,
1210                              tm->hw[0]->sw_if_index,
1211                              ~0, // invalid fib index
1212                              1,
1213                              NULL,
1214                              FIB_ROUTE_PATH_FLAG_NONE);
1215     fei = fib_table_lookup(fib_index, &pfx_1_1_2_0_s_24);
1216     ai = fib_entry_get_adj(fei);
1217     FIB_TEST((ai_01 == ai), "1.1.2.0/24 resolves via 10.10.10.1");
1218
1219     /*
1220      * +1 entry only
1221      */
1222     FIB_TEST((1 == fib_path_list_db_size()),   "path list DB is empty");
1223     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
1224              fib_path_list_pool_size());
1225     FIB_TEST((ENBR+6 == fib_entry_pool_size()), "entry pool size is %d",
1226              fib_entry_pool_size());
1227
1228     /*
1229      * modify 1.1.2.0/24 to use multipath.
1230      */
1231     fib_table_entry_path_add(fib_index,
1232                              &pfx_1_1_2_0_s_24,
1233                              FIB_SOURCE_API,
1234                              FIB_ENTRY_FLAG_NONE,
1235                              FIB_PROTOCOL_IP4,
1236                              &nh_10_10_10_2,
1237                              tm->hw[0]->sw_if_index,
1238                              ~0, // invalid fib index
1239                              1,
1240                              NULL,
1241                              FIB_ROUTE_PATH_FLAG_NONE);
1242     fei = fib_table_lookup(fib_index, &pfx_1_1_2_0_s_24);
1243     dpo = fib_entry_contribute_ip_forwarding(fei);
1244     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
1245                                     1, tm->hw[0]->sw_if_index),
1246              "RPF list for 1.1.2.0/24 contains both adjs");
1247
1248     dpo1 = load_balance_get_bucket(dpo->dpoi_index, 0);
1249     FIB_TEST(DPO_ADJACENCY == dpo1->dpoi_type, "type is %d", dpo1->dpoi_type);
1250     FIB_TEST((ai_01 == dpo1->dpoi_index),
1251              "1.1.2.0/24 bucket 0 resolves via 10.10.10.1 (%d=%d)",
1252              ai_01, dpo1->dpoi_index);
1253
1254     dpo1 = load_balance_get_bucket(dpo->dpoi_index, 1);
1255     FIB_TEST(DPO_ADJACENCY == dpo1->dpoi_type, "type is %d", dpo1->dpoi_type);
1256     FIB_TEST((ai_02 == dpo1->dpoi_index),
1257              "1.1.2.0/24 bucket 1 resolves via 10.10.10.2");
1258
1259     /*
1260      * +1 shared-pathlist
1261      */
1262     FIB_TEST((2 == fib_path_list_db_size()),   "path list DB is empty");
1263     FIB_TEST((PNBR+6 == fib_path_list_pool_size()), "path list pool size is %d",
1264              fib_path_list_pool_size());
1265     FIB_TEST((ENBR+6 == fib_entry_pool_size()), "entry pool size is %d",
1266              fib_entry_pool_size());
1267
1268     /*
1269      * revert the modify
1270      */
1271     fib_table_entry_path_remove(fib_index,
1272                                 &pfx_1_1_2_0_s_24,
1273                                 FIB_SOURCE_API,
1274                                 FIB_PROTOCOL_IP4,
1275                                 &nh_10_10_10_2,
1276                                 tm->hw[0]->sw_if_index,
1277                                 ~0,
1278                                 1,
1279                                 FIB_ROUTE_PATH_FLAG_NONE);
1280     fei = fib_table_lookup(fib_index, &pfx_1_1_2_0_s_24);
1281     dpo = fib_entry_contribute_ip_forwarding(fei);
1282     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
1283                                    1, tm->hw[0]->sw_if_index),
1284              "RPF list for 1.1.2.0/24 contains one adj");
1285
1286     ai = fib_entry_get_adj(fei);
1287     FIB_TEST((ai_01 == ai), "1.1.2.0/24 resolves via 10.10.10.1");
1288
1289     /*
1290      * +1 shared-pathlist
1291      */
1292     FIB_TEST((1 == fib_path_list_db_size()),   "path list DB is %d",
1293              fib_path_list_db_size());
1294     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
1295              fib_path_list_pool_size());
1296     FIB_TEST((ENBR+6 == fib_entry_pool_size()), "entry pool size is %d",
1297              fib_entry_pool_size());
1298
1299     /*
1300      * Add 2 recursive routes:
1301      *   100.100.100.100/32 via 1.1.1.1/32  => the via entry is installed.
1302      *   100.100.100.101/32 via 1.1.1.1/32  => the via entry is installed.
1303      */
1304     fib_prefix_t bgp_100_pfx = {
1305         .fp_len = 32,
1306         .fp_proto = FIB_PROTOCOL_IP4,
1307         .fp_addr = {
1308             /* 100.100.100.100/32 */
1309             .ip4.as_u32 = clib_host_to_net_u32(0x64646464),
1310         },
1311     };
1312     /* via 1.1.1.1 */
1313     ip46_address_t nh_1_1_1_1 = {
1314         .ip4.as_u32 = clib_host_to_net_u32(0x01010101),
1315     };
1316
1317     fei = fib_table_entry_path_add(fib_index,
1318                                    &bgp_100_pfx,
1319                                    FIB_SOURCE_API,
1320                                    FIB_ENTRY_FLAG_NONE,
1321                                    FIB_PROTOCOL_IP4,
1322                                    &nh_1_1_1_1,
1323                                    ~0, // no index provided.
1324                                    fib_index, // nexthop in same fib as route
1325                                    1,
1326                                    NULL,
1327                                    FIB_ROUTE_PATH_FLAG_NONE);
1328
1329     FIB_TEST_REC_FORW(&bgp_100_pfx, &pfx_1_1_1_1_s_32, 0);
1330     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 1,
1331                                     tm->hw[0]->sw_if_index),
1332              "RPF list for adj-fib contains adj");
1333
1334     /*
1335      * +1 entry and +1 shared-path-list
1336      */
1337     FIB_TEST((2  == fib_path_list_db_size()),   "path list DB population:%d",
1338              fib_path_list_db_size());
1339     FIB_TEST((PNBR+6 == fib_path_list_pool_size()), "path list pool size is %d",
1340              fib_path_list_pool_size());
1341     FIB_TEST((ENBR+7 == fib_entry_pool_size()), "entry pool size is %d",
1342              fib_entry_pool_size());
1343
1344     fib_prefix_t bgp_101_pfx = {
1345         .fp_len = 32,
1346         .fp_proto = FIB_PROTOCOL_IP4,
1347         .fp_addr = {
1348             /* 100.100.100.101/32 */
1349             .ip4.as_u32 = clib_host_to_net_u32(0x64646465),
1350         },
1351     };
1352
1353     fib_table_entry_path_add(fib_index,
1354                              &bgp_101_pfx,
1355                              FIB_SOURCE_API,
1356                              FIB_ENTRY_FLAG_NONE,
1357                              FIB_PROTOCOL_IP4,
1358                              &nh_1_1_1_1,
1359                              ~0, // no index provided.
1360                              fib_index, // nexthop in same fib as route
1361                              1,
1362                              NULL,
1363                              FIB_ROUTE_PATH_FLAG_NONE);
1364
1365     FIB_TEST_REC_FORW(&bgp_101_pfx, &pfx_1_1_1_1_s_32, 0);
1366     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 1,
1367                                     tm->hw[0]->sw_if_index),
1368              "RPF list for adj-fib contains adj");
1369
1370     /*
1371      * +1 entry, but the recursive path-list is shared.
1372      */
1373     FIB_TEST((2  == fib_path_list_db_size()),   "path list DB population:%d",
1374              fib_path_list_db_size());
1375     FIB_TEST((PNBR+6 == fib_path_list_pool_size()), "path list pool size is %d",
1376              fib_path_list_pool_size());
1377     FIB_TEST((ENBR+8 == fib_entry_pool_size()), "entry pool size is %d",
1378              fib_entry_pool_size());
1379
1380     /*
1381      * An EXCLUSIVE route; one where the user (me) provides the exclusive
1382      * adjacency through which the route will resovle
1383      */
1384     fib_prefix_t ex_pfx = {
1385         .fp_len = 32,
1386         .fp_proto = FIB_PROTOCOL_IP4,
1387         .fp_addr = {
1388             /* 4.4.4.4/32 */
1389             .ip4.as_u32 = clib_host_to_net_u32(0x04040404),
1390         },
1391     };
1392
1393     fib_table_entry_special_add(fib_index,
1394                                 &ex_pfx,
1395                                 FIB_SOURCE_SPECIAL,
1396                                 FIB_ENTRY_FLAG_EXCLUSIVE,
1397                                 locked_ai);
1398     fei = fib_table_lookup_exact_match(fib_index, &ex_pfx);
1399     FIB_TEST((ai == fib_entry_get_adj(fei)),
1400              "Exclusive route links to user adj");
1401
1402     fib_table_entry_special_remove(fib_index,
1403                                    &ex_pfx,
1404                                    FIB_SOURCE_SPECIAL);
1405     FIB_TEST(FIB_NODE_INDEX_INVALID ==
1406              fib_table_lookup_exact_match(fib_index, &ex_pfx),
1407              "Exclusive reoute removed");
1408
1409     /*
1410      * An EXCLUSIVE route; one where the user (me) provides the exclusive
1411      * adjacency through which the route will resovle
1412      */
1413     dpo_id_t ex_dpo = DPO_INVALID;
1414
1415     lookup_dpo_add_or_lock_w_fib_index(fib_index,
1416                                        DPO_PROTO_IP4,
1417                                        LOOKUP_UNICAST,
1418                                        LOOKUP_INPUT_DST_ADDR,
1419                                        LOOKUP_TABLE_FROM_CONFIG,
1420                                        &ex_dpo);
1421
1422     fib_table_entry_special_dpo_add(fib_index,
1423                                     &ex_pfx,
1424                                     FIB_SOURCE_SPECIAL,
1425                                     FIB_ENTRY_FLAG_EXCLUSIVE,
1426                                     &ex_dpo);
1427     fei = fib_table_lookup_exact_match(fib_index, &ex_pfx);
1428     dpo = fib_entry_contribute_ip_forwarding(fei);
1429     FIB_TEST(!dpo_cmp(&ex_dpo, load_balance_get_bucket(dpo->dpoi_index, 0)),
1430              "exclusive remote uses lookup DPO");
1431
1432     /*
1433      * update the exclusive to use a different DPO
1434      */
1435     ip_null_dpo_add_and_lock(DPO_PROTO_IP4,
1436                              IP_NULL_ACTION_SEND_ICMP_UNREACH,
1437                              &ex_dpo);
1438     fib_table_entry_special_dpo_update(fib_index,
1439                                        &ex_pfx,
1440                                        FIB_SOURCE_SPECIAL,
1441                                        FIB_ENTRY_FLAG_EXCLUSIVE,
1442                                        &ex_dpo);
1443     dpo = fib_entry_contribute_ip_forwarding(fei);
1444     FIB_TEST(!dpo_cmp(&ex_dpo, load_balance_get_bucket(dpo->dpoi_index, 0)),
1445              "exclusive remote uses now uses NULL DPO");
1446
1447     fib_table_entry_special_remove(fib_index,
1448                                    &ex_pfx,
1449                                    FIB_SOURCE_SPECIAL);
1450     FIB_TEST(FIB_NODE_INDEX_INVALID ==
1451              fib_table_lookup_exact_match(fib_index, &ex_pfx),
1452              "Exclusive reoute removed");
1453     dpo_reset(&ex_dpo);
1454
1455     /*
1456      * Add a recursive route:
1457      *   200.200.200.200/32 via 1.1.1.2/32  => the via entry is NOT installed.
1458      */
1459     fib_prefix_t bgp_200_pfx = {
1460         .fp_len = 32,
1461         .fp_proto = FIB_PROTOCOL_IP4,
1462         .fp_addr = {
1463             /* 200.200.200.200/32 */
1464             .ip4.as_u32 = clib_host_to_net_u32(0xc8c8c8c8),
1465         },
1466     };
1467     /* via 1.1.1.2 */
1468     fib_prefix_t pfx_1_1_1_2_s_32 = {
1469         .fp_len = 32,
1470         .fp_proto = FIB_PROTOCOL_IP4,
1471         .fp_addr = {
1472             .ip4.as_u32 = clib_host_to_net_u32(0x01010102),
1473         },
1474     };
1475
1476     fib_table_entry_path_add(fib_index,
1477                              &bgp_200_pfx,
1478                              FIB_SOURCE_API,
1479                              FIB_ENTRY_FLAG_NONE,
1480                              FIB_PROTOCOL_IP4,
1481                              &pfx_1_1_1_2_s_32.fp_addr,
1482                              ~0, // no index provided.
1483                              fib_index, // nexthop in same fib as route
1484                              1,
1485                              NULL,
1486                              FIB_ROUTE_PATH_FLAG_NONE);
1487
1488     FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
1489
1490     /*
1491      * the adj should be recursive via drop, since the route resolves via
1492      * the default route, which is itself a DROP 
1493      */
1494     fei = fib_table_lookup(fib_index, &pfx_1_1_1_2_s_32);
1495     dpo1 = fib_entry_contribute_ip_forwarding(fei);
1496     FIB_TEST(load_balance_is_drop(dpo1), "1.1.1.2/32 is drop");
1497     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 0),
1498              "RPF list for 1.1.1.2/32 contains 0 adjs");
1499
1500     /*
1501      * +2 entry and +1 shared-path-list
1502      */
1503     FIB_TEST((3  == fib_path_list_db_size()),   "path list DB population:%d",
1504              fib_path_list_db_size());
1505     FIB_TEST((PNBR+7 == fib_path_list_pool_size()), "path list pool size is %d",
1506              fib_path_list_pool_size());
1507     FIB_TEST((ENBR+10 == fib_entry_pool_size()), "entry pool size is %d",
1508              fib_entry_pool_size());
1509
1510     /*
1511      * Unequal Cost load-balance. 3:1 ratio. fits in a 4 bucket LB
1512      * The paths are sort by NH first. in this case the the path with greater
1513      * weight is first in the set. This ordering is to test the RPF sort|uniq logic
1514      */
1515     fib_prefix_t pfx_1_2_3_4_s_32 = {
1516         .fp_len = 32,
1517         .fp_proto = FIB_PROTOCOL_IP4,
1518         .fp_addr = {
1519             .ip4.as_u32 = clib_host_to_net_u32(0x01020304),
1520         },
1521     };
1522     fib_table_entry_path_add(fib_index,
1523                              &pfx_1_2_3_4_s_32,
1524                              FIB_SOURCE_API,
1525                              FIB_ENTRY_FLAG_NONE,
1526                              FIB_PROTOCOL_IP4,
1527                              &nh_10_10_10_1,
1528                              tm->hw[0]->sw_if_index,
1529                              ~0,
1530                              1,
1531                              NULL,
1532                              FIB_ROUTE_PATH_FLAG_NONE);
1533     fei = fib_table_entry_path_add(fib_index,
1534                                    &pfx_1_2_3_4_s_32,
1535                                    FIB_SOURCE_API,
1536                                    FIB_ENTRY_FLAG_NONE,
1537                                    FIB_PROTOCOL_IP4,
1538                                    &nh_12_12_12_12,
1539                                    tm->hw[1]->sw_if_index,
1540                                    ~0,
1541                                    3,
1542                                    NULL,
1543                                    FIB_ROUTE_PATH_FLAG_NONE);
1544
1545     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "1.2.3.4/32 presnet");
1546     dpo = fib_entry_contribute_ip_forwarding(fei);
1547     lb = load_balance_get(dpo->dpoi_index);
1548     FIB_TEST((lb->lb_n_buckets == 4),
1549              "1.2.3.4/32 LB has %d bucket",
1550              lb->lb_n_buckets);
1551
1552     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 0, ai_12_12_12_12);
1553     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 1, ai_12_12_12_12);
1554     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 2, ai_12_12_12_12);
1555     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_4_s_32, 3, ai_01);
1556
1557     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 2,
1558                                     tm->hw[0]->sw_if_index,
1559                                     tm->hw[1]->sw_if_index),
1560              "RPF list for 1.2.3.4/32 contains both adjs");
1561
1562
1563     /*
1564      * Unequal Cost load-balance. 4:1 ratio.
1565      *  fits in a 16 bucket LB with ratio 13:3
1566      */
1567     fib_prefix_t pfx_1_2_3_5_s_32 = {
1568         .fp_len = 32,
1569         .fp_proto = FIB_PROTOCOL_IP4,
1570         .fp_addr = {
1571             .ip4.as_u32 = clib_host_to_net_u32(0x01020305),
1572         },
1573     };
1574     fib_table_entry_path_add(fib_index,
1575                              &pfx_1_2_3_5_s_32,
1576                              FIB_SOURCE_API,
1577                              FIB_ENTRY_FLAG_NONE,
1578                              FIB_PROTOCOL_IP4,
1579                              &nh_12_12_12_12,
1580                              tm->hw[1]->sw_if_index,
1581                              ~0,
1582                              1,
1583                              NULL,
1584                              FIB_ROUTE_PATH_FLAG_NONE);
1585     fei = fib_table_entry_path_add(fib_index,
1586                                    &pfx_1_2_3_5_s_32,
1587                                    FIB_SOURCE_API,
1588                                    FIB_ENTRY_FLAG_NONE,
1589                                    FIB_PROTOCOL_IP4,
1590                                    &nh_10_10_10_1,
1591                                    tm->hw[0]->sw_if_index,
1592                                    ~0,
1593                                    4,
1594                                    NULL,
1595                                    FIB_ROUTE_PATH_FLAG_NONE);
1596
1597     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "1.2.3.5/32 presnet");
1598     dpo = fib_entry_contribute_ip_forwarding(fei);
1599     lb = load_balance_get(dpo->dpoi_index);
1600     FIB_TEST((lb->lb_n_buckets == 16),
1601              "1.2.3.5/32 LB has %d bucket",
1602              lb->lb_n_buckets);
1603
1604     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 0, ai_01);
1605     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 1, ai_01);
1606     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 2, ai_01);
1607     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 3, ai_01);
1608     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 4, ai_01);
1609     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 5, ai_01);
1610     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 6, ai_01);
1611     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 7, ai_01);
1612     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 8, ai_01);
1613     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 9, ai_01);
1614     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 10, ai_01);
1615     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 11, ai_01);
1616     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 12, ai_01);
1617     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 13, ai_12_12_12_12);
1618     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 14, ai_12_12_12_12);
1619     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 15, ai_12_12_12_12);
1620
1621     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 2,
1622                                     tm->hw[0]->sw_if_index,
1623                                     tm->hw[1]->sw_if_index),
1624              "RPF list for 1.2.3.4/32 contains both adjs");
1625
1626     /*
1627      * Test UCMP with a large weight skew - this produces load-balance objects with large
1628      * numbers of buckets to accommodate the skew. By updating said load-balances we are
1629      * laso testing the LB in placce modify code when number of buckets is large.
1630      */
1631     fib_prefix_t pfx_6_6_6_6_s_32 = {
1632         .fp_len = 32,
1633         .fp_proto = FIB_PROTOCOL_IP4,
1634         .fp_addr = {
1635             /* 1.1.1.1/32 */
1636             .ip4.as_u32 = clib_host_to_net_u32(0x06060606),
1637         },
1638     };
1639     fib_test_lb_bucket_t ip_6_6_6_6_o_10_10_10_1 = {
1640         .type = FT_LB_ADJ,
1641         .adj = {
1642             .adj = ai_01,
1643         },
1644     };
1645     fib_test_lb_bucket_t ip_6_6_6_6_o_10_10_10_2 = {
1646         .type = FT_LB_ADJ,
1647         .adj = {
1648             .adj = ai_02,
1649         },
1650     };
1651     fib_test_lb_bucket_t ip_6_6_6_6_o_12_12_12_12 = {
1652         .type = FT_LB_ADJ,
1653         .adj = {
1654             .adj = ai_12_12_12_12,
1655         },
1656     };
1657     fib_table_entry_update_one_path(fib_index,
1658                                     &pfx_6_6_6_6_s_32,
1659                                     FIB_SOURCE_API,
1660                                     FIB_ENTRY_FLAG_NONE,
1661                                     FIB_PROTOCOL_IP4,
1662                                     &nh_10_10_10_1,
1663                                     tm->hw[0]->sw_if_index,
1664                                     ~0, // invalid fib index
1665                                     0,  // zero weigth
1666                                     NULL,
1667                                     FIB_ROUTE_PATH_FLAG_NONE);
1668
1669     fei = fib_table_lookup(fib_index, &pfx_6_6_6_6_s_32);
1670     FIB_TEST(fib_test_validate_entry(fei,
1671                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
1672                                      1,
1673                                      &ip_6_6_6_6_o_10_10_10_1),
1674              "6.6.6.6/32 via 10.10.10.1");
1675
1676     fib_table_entry_path_add(fib_index,
1677                              &pfx_6_6_6_6_s_32,
1678                              FIB_SOURCE_API,
1679                              FIB_ENTRY_FLAG_NONE,
1680                              FIB_PROTOCOL_IP4,
1681                              &nh_10_10_10_2,
1682                              tm->hw[0]->sw_if_index,
1683                              ~0, // invalid fib index
1684                              100,
1685                              NULL,
1686                              FIB_ROUTE_PATH_FLAG_NONE);
1687
1688     fei = fib_table_lookup(fib_index, &pfx_6_6_6_6_s_32);
1689     FIB_TEST(fib_test_validate_entry(fei,
1690                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
1691                                      64,
1692                                      &ip_6_6_6_6_o_10_10_10_2,
1693                                      &ip_6_6_6_6_o_10_10_10_2,
1694                                      &ip_6_6_6_6_o_10_10_10_2,
1695                                      &ip_6_6_6_6_o_10_10_10_2,
1696                                      &ip_6_6_6_6_o_10_10_10_2,
1697                                      &ip_6_6_6_6_o_10_10_10_2,
1698                                      &ip_6_6_6_6_o_10_10_10_2,
1699                                      &ip_6_6_6_6_o_10_10_10_2,
1700                                      &ip_6_6_6_6_o_10_10_10_2,
1701                                      &ip_6_6_6_6_o_10_10_10_2,
1702                                      &ip_6_6_6_6_o_10_10_10_2,
1703                                      &ip_6_6_6_6_o_10_10_10_2,
1704                                      &ip_6_6_6_6_o_10_10_10_2,
1705                                      &ip_6_6_6_6_o_10_10_10_2,
1706                                      &ip_6_6_6_6_o_10_10_10_2,
1707                                      &ip_6_6_6_6_o_10_10_10_2,
1708                                      &ip_6_6_6_6_o_10_10_10_2,
1709                                      &ip_6_6_6_6_o_10_10_10_2,
1710                                      &ip_6_6_6_6_o_10_10_10_2,
1711                                      &ip_6_6_6_6_o_10_10_10_2,
1712                                      &ip_6_6_6_6_o_10_10_10_2,
1713                                      &ip_6_6_6_6_o_10_10_10_2,
1714                                      &ip_6_6_6_6_o_10_10_10_2,
1715                                      &ip_6_6_6_6_o_10_10_10_2,
1716                                      &ip_6_6_6_6_o_10_10_10_2,
1717                                      &ip_6_6_6_6_o_10_10_10_2,
1718                                      &ip_6_6_6_6_o_10_10_10_2,
1719                                      &ip_6_6_6_6_o_10_10_10_2,
1720                                      &ip_6_6_6_6_o_10_10_10_2,
1721                                      &ip_6_6_6_6_o_10_10_10_2,
1722                                      &ip_6_6_6_6_o_10_10_10_2,
1723                                      &ip_6_6_6_6_o_10_10_10_2,
1724                                      &ip_6_6_6_6_o_10_10_10_2,
1725                                      &ip_6_6_6_6_o_10_10_10_2,
1726                                      &ip_6_6_6_6_o_10_10_10_2,
1727                                      &ip_6_6_6_6_o_10_10_10_2,
1728                                      &ip_6_6_6_6_o_10_10_10_2,
1729                                      &ip_6_6_6_6_o_10_10_10_2,
1730                                      &ip_6_6_6_6_o_10_10_10_2,
1731                                      &ip_6_6_6_6_o_10_10_10_2,
1732                                      &ip_6_6_6_6_o_10_10_10_2,
1733                                      &ip_6_6_6_6_o_10_10_10_2,
1734                                      &ip_6_6_6_6_o_10_10_10_2,
1735                                      &ip_6_6_6_6_o_10_10_10_2,
1736                                      &ip_6_6_6_6_o_10_10_10_2,
1737                                      &ip_6_6_6_6_o_10_10_10_2,
1738                                      &ip_6_6_6_6_o_10_10_10_2,
1739                                      &ip_6_6_6_6_o_10_10_10_2,
1740                                      &ip_6_6_6_6_o_10_10_10_2,
1741                                      &ip_6_6_6_6_o_10_10_10_2,
1742                                      &ip_6_6_6_6_o_10_10_10_2,
1743                                      &ip_6_6_6_6_o_10_10_10_2,
1744                                      &ip_6_6_6_6_o_10_10_10_2,
1745                                      &ip_6_6_6_6_o_10_10_10_2,
1746                                      &ip_6_6_6_6_o_10_10_10_2,
1747                                      &ip_6_6_6_6_o_10_10_10_2,
1748                                      &ip_6_6_6_6_o_10_10_10_2,
1749                                      &ip_6_6_6_6_o_10_10_10_2,
1750                                      &ip_6_6_6_6_o_10_10_10_2,
1751                                      &ip_6_6_6_6_o_10_10_10_2,
1752                                      &ip_6_6_6_6_o_10_10_10_2,
1753                                      &ip_6_6_6_6_o_10_10_10_2,
1754                                      &ip_6_6_6_6_o_10_10_10_2,
1755                                      &ip_6_6_6_6_o_10_10_10_1),
1756              "6.6.6.6/32 via 10.10.10.1 and 10.10.10.2 in 63:1 ratio");
1757
1758     fib_table_entry_path_add(fib_index,
1759                              &pfx_6_6_6_6_s_32,
1760                              FIB_SOURCE_API,
1761                              FIB_ENTRY_FLAG_NONE,
1762                              FIB_PROTOCOL_IP4,
1763                              &nh_12_12_12_12,
1764                              tm->hw[1]->sw_if_index,
1765                              ~0, // invalid fib index
1766                              100,
1767                              NULL,
1768                              FIB_ROUTE_PATH_FLAG_NONE);
1769
1770     fei = fib_table_lookup(fib_index, &pfx_6_6_6_6_s_32);
1771     FIB_TEST(fib_test_validate_entry(fei,
1772                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
1773                                      128,
1774                                      &ip_6_6_6_6_o_10_10_10_1,
1775                                      &ip_6_6_6_6_o_10_10_10_2,
1776                                      &ip_6_6_6_6_o_10_10_10_2,
1777                                      &ip_6_6_6_6_o_10_10_10_2,
1778                                      &ip_6_6_6_6_o_10_10_10_2,
1779                                      &ip_6_6_6_6_o_10_10_10_2,
1780                                      &ip_6_6_6_6_o_10_10_10_2,
1781                                      &ip_6_6_6_6_o_10_10_10_2,
1782                                      &ip_6_6_6_6_o_10_10_10_2,
1783                                      &ip_6_6_6_6_o_10_10_10_2,
1784                                      &ip_6_6_6_6_o_10_10_10_2,
1785                                      &ip_6_6_6_6_o_10_10_10_2,
1786                                      &ip_6_6_6_6_o_10_10_10_2,
1787                                      &ip_6_6_6_6_o_10_10_10_2,
1788                                      &ip_6_6_6_6_o_10_10_10_2,
1789                                      &ip_6_6_6_6_o_10_10_10_2,
1790                                      &ip_6_6_6_6_o_10_10_10_2,
1791                                      &ip_6_6_6_6_o_10_10_10_2,
1792                                      &ip_6_6_6_6_o_10_10_10_2,
1793                                      &ip_6_6_6_6_o_10_10_10_2,
1794                                      &ip_6_6_6_6_o_10_10_10_2,
1795                                      &ip_6_6_6_6_o_10_10_10_2,
1796                                      &ip_6_6_6_6_o_10_10_10_2,
1797                                      &ip_6_6_6_6_o_10_10_10_2,
1798                                      &ip_6_6_6_6_o_10_10_10_2,
1799                                      &ip_6_6_6_6_o_10_10_10_2,
1800                                      &ip_6_6_6_6_o_10_10_10_2,
1801                                      &ip_6_6_6_6_o_10_10_10_2,
1802                                      &ip_6_6_6_6_o_10_10_10_2,
1803                                      &ip_6_6_6_6_o_10_10_10_2,
1804                                      &ip_6_6_6_6_o_10_10_10_2,
1805                                      &ip_6_6_6_6_o_10_10_10_2,
1806                                      &ip_6_6_6_6_o_10_10_10_2,
1807                                      &ip_6_6_6_6_o_10_10_10_2,
1808                                      &ip_6_6_6_6_o_10_10_10_2,
1809                                      &ip_6_6_6_6_o_10_10_10_2,
1810                                      &ip_6_6_6_6_o_10_10_10_2,
1811                                      &ip_6_6_6_6_o_10_10_10_2,
1812                                      &ip_6_6_6_6_o_10_10_10_2,
1813                                      &ip_6_6_6_6_o_10_10_10_2,
1814                                      &ip_6_6_6_6_o_10_10_10_2,
1815                                      &ip_6_6_6_6_o_10_10_10_2,
1816                                      &ip_6_6_6_6_o_10_10_10_2,
1817                                      &ip_6_6_6_6_o_10_10_10_2,
1818                                      &ip_6_6_6_6_o_10_10_10_2,
1819                                      &ip_6_6_6_6_o_10_10_10_2,
1820                                      &ip_6_6_6_6_o_10_10_10_2,
1821                                      &ip_6_6_6_6_o_10_10_10_2,
1822                                      &ip_6_6_6_6_o_10_10_10_2,
1823                                      &ip_6_6_6_6_o_10_10_10_2,
1824                                      &ip_6_6_6_6_o_10_10_10_2,
1825                                      &ip_6_6_6_6_o_10_10_10_2,
1826                                      &ip_6_6_6_6_o_10_10_10_2,
1827                                      &ip_6_6_6_6_o_10_10_10_2,
1828                                      &ip_6_6_6_6_o_10_10_10_2,
1829                                      &ip_6_6_6_6_o_10_10_10_2,
1830                                      &ip_6_6_6_6_o_10_10_10_2,
1831                                      &ip_6_6_6_6_o_10_10_10_2,
1832                                      &ip_6_6_6_6_o_10_10_10_2,
1833                                      &ip_6_6_6_6_o_10_10_10_2,
1834                                      &ip_6_6_6_6_o_10_10_10_2,
1835                                      &ip_6_6_6_6_o_10_10_10_2,
1836                                      &ip_6_6_6_6_o_10_10_10_2,
1837                                      &ip_6_6_6_6_o_10_10_10_2,
1838                                      &ip_6_6_6_6_o_10_10_10_2,
1839                                      &ip_6_6_6_6_o_12_12_12_12,
1840                                      &ip_6_6_6_6_o_12_12_12_12,
1841                                      &ip_6_6_6_6_o_12_12_12_12,
1842                                      &ip_6_6_6_6_o_12_12_12_12,
1843                                      &ip_6_6_6_6_o_12_12_12_12,
1844                                      &ip_6_6_6_6_o_12_12_12_12,
1845                                      &ip_6_6_6_6_o_12_12_12_12,
1846                                      &ip_6_6_6_6_o_12_12_12_12,
1847                                      &ip_6_6_6_6_o_12_12_12_12,
1848                                      &ip_6_6_6_6_o_12_12_12_12,
1849                                      &ip_6_6_6_6_o_12_12_12_12,
1850                                      &ip_6_6_6_6_o_12_12_12_12,
1851                                      &ip_6_6_6_6_o_12_12_12_12,
1852                                      &ip_6_6_6_6_o_12_12_12_12,
1853                                      &ip_6_6_6_6_o_12_12_12_12,
1854                                      &ip_6_6_6_6_o_12_12_12_12,
1855                                      &ip_6_6_6_6_o_12_12_12_12,
1856                                      &ip_6_6_6_6_o_12_12_12_12,
1857                                      &ip_6_6_6_6_o_12_12_12_12,
1858                                      &ip_6_6_6_6_o_12_12_12_12,
1859                                      &ip_6_6_6_6_o_12_12_12_12,
1860                                      &ip_6_6_6_6_o_12_12_12_12,
1861                                      &ip_6_6_6_6_o_12_12_12_12,
1862                                      &ip_6_6_6_6_o_12_12_12_12,
1863                                      &ip_6_6_6_6_o_12_12_12_12,
1864                                      &ip_6_6_6_6_o_12_12_12_12,
1865                                      &ip_6_6_6_6_o_12_12_12_12,
1866                                      &ip_6_6_6_6_o_12_12_12_12,
1867                                      &ip_6_6_6_6_o_12_12_12_12,
1868                                      &ip_6_6_6_6_o_12_12_12_12,
1869                                      &ip_6_6_6_6_o_12_12_12_12,
1870                                      &ip_6_6_6_6_o_12_12_12_12,
1871                                      &ip_6_6_6_6_o_12_12_12_12,
1872                                      &ip_6_6_6_6_o_12_12_12_12,
1873                                      &ip_6_6_6_6_o_12_12_12_12,
1874                                      &ip_6_6_6_6_o_12_12_12_12,
1875                                      &ip_6_6_6_6_o_12_12_12_12,
1876                                      &ip_6_6_6_6_o_12_12_12_12,
1877                                      &ip_6_6_6_6_o_12_12_12_12,
1878                                      &ip_6_6_6_6_o_12_12_12_12,
1879                                      &ip_6_6_6_6_o_12_12_12_12,
1880                                      &ip_6_6_6_6_o_12_12_12_12,
1881                                      &ip_6_6_6_6_o_12_12_12_12,
1882                                      &ip_6_6_6_6_o_12_12_12_12,
1883                                      &ip_6_6_6_6_o_12_12_12_12,
1884                                      &ip_6_6_6_6_o_12_12_12_12,
1885                                      &ip_6_6_6_6_o_12_12_12_12,
1886                                      &ip_6_6_6_6_o_12_12_12_12,
1887                                      &ip_6_6_6_6_o_12_12_12_12,
1888                                      &ip_6_6_6_6_o_12_12_12_12,
1889                                      &ip_6_6_6_6_o_12_12_12_12,
1890                                      &ip_6_6_6_6_o_12_12_12_12,
1891                                      &ip_6_6_6_6_o_12_12_12_12,
1892                                      &ip_6_6_6_6_o_12_12_12_12,
1893                                      &ip_6_6_6_6_o_12_12_12_12,
1894                                      &ip_6_6_6_6_o_12_12_12_12,
1895                                      &ip_6_6_6_6_o_12_12_12_12,
1896                                      &ip_6_6_6_6_o_12_12_12_12,
1897                                      &ip_6_6_6_6_o_12_12_12_12,
1898                                      &ip_6_6_6_6_o_12_12_12_12,
1899                                      &ip_6_6_6_6_o_12_12_12_12,
1900                                      &ip_6_6_6_6_o_12_12_12_12,
1901                                      &ip_6_6_6_6_o_12_12_12_12),
1902              "6.6.6.6/32 via 10.10.10.1 and 10.10.10.2 in 63:1 ratio");
1903
1904     fib_table_entry_path_remove(fib_index,
1905                                 &pfx_6_6_6_6_s_32,
1906                                 FIB_SOURCE_API,
1907                                 FIB_PROTOCOL_IP4,
1908                                 &nh_12_12_12_12,
1909                                 tm->hw[1]->sw_if_index,
1910                                 ~0, // invalid fib index
1911                                 100,
1912                                 FIB_ROUTE_PATH_FLAG_NONE);
1913
1914     fei = fib_table_lookup(fib_index, &pfx_6_6_6_6_s_32);
1915     FIB_TEST(fib_test_validate_entry(fei,
1916                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
1917                                      64,
1918                                      &ip_6_6_6_6_o_10_10_10_2,
1919                                      &ip_6_6_6_6_o_10_10_10_2,
1920                                      &ip_6_6_6_6_o_10_10_10_2,
1921                                      &ip_6_6_6_6_o_10_10_10_2,
1922                                      &ip_6_6_6_6_o_10_10_10_2,
1923                                      &ip_6_6_6_6_o_10_10_10_2,
1924                                      &ip_6_6_6_6_o_10_10_10_2,
1925                                      &ip_6_6_6_6_o_10_10_10_2,
1926                                      &ip_6_6_6_6_o_10_10_10_2,
1927                                      &ip_6_6_6_6_o_10_10_10_2,
1928                                      &ip_6_6_6_6_o_10_10_10_2,
1929                                      &ip_6_6_6_6_o_10_10_10_2,
1930                                      &ip_6_6_6_6_o_10_10_10_2,
1931                                      &ip_6_6_6_6_o_10_10_10_2,
1932                                      &ip_6_6_6_6_o_10_10_10_2,
1933                                      &ip_6_6_6_6_o_10_10_10_2,
1934                                      &ip_6_6_6_6_o_10_10_10_2,
1935                                      &ip_6_6_6_6_o_10_10_10_2,
1936                                      &ip_6_6_6_6_o_10_10_10_2,
1937                                      &ip_6_6_6_6_o_10_10_10_2,
1938                                      &ip_6_6_6_6_o_10_10_10_2,
1939                                      &ip_6_6_6_6_o_10_10_10_2,
1940                                      &ip_6_6_6_6_o_10_10_10_2,
1941                                      &ip_6_6_6_6_o_10_10_10_2,
1942                                      &ip_6_6_6_6_o_10_10_10_2,
1943                                      &ip_6_6_6_6_o_10_10_10_2,
1944                                      &ip_6_6_6_6_o_10_10_10_2,
1945                                      &ip_6_6_6_6_o_10_10_10_2,
1946                                      &ip_6_6_6_6_o_10_10_10_2,
1947                                      &ip_6_6_6_6_o_10_10_10_2,
1948                                      &ip_6_6_6_6_o_10_10_10_2,
1949                                      &ip_6_6_6_6_o_10_10_10_2,
1950                                      &ip_6_6_6_6_o_10_10_10_2,
1951                                      &ip_6_6_6_6_o_10_10_10_2,
1952                                      &ip_6_6_6_6_o_10_10_10_2,
1953                                      &ip_6_6_6_6_o_10_10_10_2,
1954                                      &ip_6_6_6_6_o_10_10_10_2,
1955                                      &ip_6_6_6_6_o_10_10_10_2,
1956                                      &ip_6_6_6_6_o_10_10_10_2,
1957                                      &ip_6_6_6_6_o_10_10_10_2,
1958                                      &ip_6_6_6_6_o_10_10_10_2,
1959                                      &ip_6_6_6_6_o_10_10_10_2,
1960                                      &ip_6_6_6_6_o_10_10_10_2,
1961                                      &ip_6_6_6_6_o_10_10_10_2,
1962                                      &ip_6_6_6_6_o_10_10_10_2,
1963                                      &ip_6_6_6_6_o_10_10_10_2,
1964                                      &ip_6_6_6_6_o_10_10_10_2,
1965                                      &ip_6_6_6_6_o_10_10_10_2,
1966                                      &ip_6_6_6_6_o_10_10_10_2,
1967                                      &ip_6_6_6_6_o_10_10_10_2,
1968                                      &ip_6_6_6_6_o_10_10_10_2,
1969                                      &ip_6_6_6_6_o_10_10_10_2,
1970                                      &ip_6_6_6_6_o_10_10_10_2,
1971                                      &ip_6_6_6_6_o_10_10_10_2,
1972                                      &ip_6_6_6_6_o_10_10_10_2,
1973                                      &ip_6_6_6_6_o_10_10_10_2,
1974                                      &ip_6_6_6_6_o_10_10_10_2,
1975                                      &ip_6_6_6_6_o_10_10_10_2,
1976                                      &ip_6_6_6_6_o_10_10_10_2,
1977                                      &ip_6_6_6_6_o_10_10_10_2,
1978                                      &ip_6_6_6_6_o_10_10_10_2,
1979                                      &ip_6_6_6_6_o_10_10_10_2,
1980                                      &ip_6_6_6_6_o_10_10_10_2,
1981                                      &ip_6_6_6_6_o_10_10_10_1),
1982              "6.6.6.6/32 via 10.10.10.1 and 10.10.10.2 in 63:1 ratio");
1983
1984     fib_table_entry_path_remove(fib_index,
1985                                 &pfx_6_6_6_6_s_32,
1986                                 FIB_SOURCE_API,
1987                                 FIB_PROTOCOL_IP4,
1988                                 &nh_10_10_10_2,
1989                                 tm->hw[0]->sw_if_index,
1990                                 ~0, // invalid fib index
1991                                 100,
1992                                 FIB_ROUTE_PATH_FLAG_NONE);
1993
1994     fei = fib_table_lookup(fib_index, &pfx_6_6_6_6_s_32);
1995     FIB_TEST(fib_test_validate_entry(fei,
1996                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
1997                                      1,
1998                                      &ip_6_6_6_6_o_10_10_10_1),
1999              "6.6.6.6/32 via 10.10.10.1");
2000
2001     fib_table_entry_delete(fib_index, &pfx_6_6_6_6_s_32, FIB_SOURCE_API);
2002
2003     /*
2004      * A recursive via the two unequal cost entries
2005      */
2006     fib_prefix_t bgp_44_s_32 = {
2007         .fp_len = 32,
2008         .fp_proto = FIB_PROTOCOL_IP4,
2009         .fp_addr = {
2010             /* 200.200.200.201/32 */
2011             .ip4.as_u32 = clib_host_to_net_u32(0x44444444),
2012         },
2013     };
2014     fei = fib_table_entry_path_add(fib_index,
2015                                    &bgp_44_s_32,
2016                                    FIB_SOURCE_API,
2017                                    FIB_ENTRY_FLAG_NONE,
2018                                    FIB_PROTOCOL_IP4,
2019                                    &pfx_1_2_3_4_s_32.fp_addr,
2020                                    ~0,
2021                                    fib_index,
2022                                    1,
2023                                    NULL,
2024                                    FIB_ROUTE_PATH_FLAG_NONE);
2025     fei = fib_table_entry_path_add(fib_index,
2026                                    &bgp_44_s_32,
2027                                    FIB_SOURCE_API,
2028                                    FIB_ENTRY_FLAG_NONE,
2029                                    FIB_PROTOCOL_IP4,
2030                                    &pfx_1_2_3_5_s_32.fp_addr,
2031                                    ~0,
2032                                    fib_index,
2033                                    1,
2034                                    NULL,
2035                                    FIB_ROUTE_PATH_FLAG_NONE);
2036
2037     FIB_TEST_REC_FORW(&bgp_44_s_32, &pfx_1_2_3_4_s_32, 0);
2038     FIB_TEST_REC_FORW(&bgp_44_s_32, &pfx_1_2_3_5_s_32, 1);
2039     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 2,
2040                                     tm->hw[0]->sw_if_index,
2041                                     tm->hw[1]->sw_if_index),
2042              "RPF list for 1.2.3.4/32 contains both adjs");
2043
2044     /*
2045      * test the uRPF check functions
2046      */
2047     dpo_id_t dpo_44 = DPO_INVALID;
2048     index_t urpfi;
2049
2050     fib_entry_contribute_forwarding(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, &dpo_44);
2051     urpfi = load_balance_get_urpf(dpo_44.dpoi_index);
2052
2053     FIB_TEST(fib_urpf_check(urpfi, tm->hw[0]->sw_if_index),
2054              "uRPF check for 68.68.68.68/32 on %d OK",
2055              tm->hw[0]->sw_if_index);
2056     FIB_TEST(fib_urpf_check(urpfi, tm->hw[1]->sw_if_index),
2057              "uRPF check for 68.68.68.68/32 on %d OK",
2058              tm->hw[1]->sw_if_index);
2059     FIB_TEST(!fib_urpf_check(urpfi, 99),
2060              "uRPF check for 68.68.68.68/32 on 99 not-OK",
2061              99);
2062     dpo_reset(&dpo_44);
2063
2064     fib_table_entry_delete(fib_index,
2065                            &bgp_44_s_32,
2066                            FIB_SOURCE_API);
2067     fib_table_entry_delete(fib_index,
2068                            &pfx_1_2_3_5_s_32,
2069                            FIB_SOURCE_API);
2070     fib_table_entry_delete(fib_index,
2071                            &pfx_1_2_3_4_s_32,
2072                            FIB_SOURCE_API);
2073
2074     /*
2075      * Add a recursive route:
2076      *   200.200.200.201/32 via 1.1.1.200/32  => the via entry is NOT installed.
2077      */
2078     fib_prefix_t bgp_201_pfx = {
2079         .fp_len = 32,
2080         .fp_proto = FIB_PROTOCOL_IP4,
2081         .fp_addr = {
2082             /* 200.200.200.201/32 */
2083             .ip4.as_u32 = clib_host_to_net_u32(0xc8c8c8c9),
2084         },
2085     };
2086     /* via 1.1.1.200 */
2087     fib_prefix_t pfx_1_1_1_200_s_32 = {
2088         .fp_len = 32,
2089         .fp_proto = FIB_PROTOCOL_IP4,
2090         .fp_addr = {
2091             .ip4.as_u32 = clib_host_to_net_u32(0x010101c8),
2092         },
2093     };
2094
2095     fib_table_entry_path_add(fib_index,
2096                              &bgp_201_pfx,
2097                              FIB_SOURCE_API,
2098                              FIB_ENTRY_FLAG_NONE,
2099                              FIB_PROTOCOL_IP4,
2100                              &pfx_1_1_1_200_s_32.fp_addr,
2101                              ~0, // no index provided.
2102                              fib_index, // nexthop in same fib as route
2103                              1,
2104                              NULL,
2105                              FIB_ROUTE_PATH_FLAG_NONE);
2106
2107     FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
2108
2109     fei = fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_200_s_32);
2110     FIB_TEST((FIB_ENTRY_FLAG_NONE == fib_entry_get_flags(fei)),
2111              "Flags set on RR via non-attached");
2112     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 0),
2113              "RPF list for BGP route empty");
2114
2115     /*
2116      * +2 entry (BGP & RR) and +1 shared-path-list
2117      */
2118     FIB_TEST((4  == fib_path_list_db_size()),   "path list DB population:%d",
2119              fib_path_list_db_size());
2120     FIB_TEST((PNBR+8 == fib_path_list_pool_size()), "path list pool size is %d",
2121              fib_path_list_pool_size());
2122     FIB_TEST((ENBR+12 == fib_entry_pool_size()), "entry pool size is %d",
2123              fib_entry_pool_size());
2124
2125     /*
2126      * insert a route that covers the missing 1.1.1.2/32. we epxect
2127      * 200.200.200.200/32 and 200.200.200.201/32 to resolve through it.
2128      */
2129     fib_prefix_t pfx_1_1_1_0_s_24 = {
2130         .fp_len = 24,
2131         .fp_proto = FIB_PROTOCOL_IP4,
2132         .fp_addr = {
2133             /* 1.1.1.0/24 */
2134             .ip4.as_u32 = clib_host_to_net_u32(0x01010100),
2135         },
2136     };
2137
2138     fib_table_entry_path_add(fib_index,
2139                              &pfx_1_1_1_0_s_24,
2140                              FIB_SOURCE_API,
2141                              FIB_ENTRY_FLAG_NONE,
2142                              FIB_PROTOCOL_IP4,
2143                              &nh_10_10_10_1,
2144                              tm->hw[0]->sw_if_index,
2145                              ~0, // invalid fib index
2146                              1,
2147                              NULL,
2148                              FIB_ROUTE_PATH_FLAG_NONE);
2149     fei = fib_table_lookup(fib_index, &pfx_1_1_1_0_s_24);
2150     dpo1 = fib_entry_contribute_ip_forwarding(fei);
2151     ai = fib_entry_get_adj(fei);
2152     FIB_TEST((ai_01 == ai), "1.1.1.0/24 resolves via 10.10.10.1");
2153     fei = fib_table_lookup(fib_index, &pfx_1_1_1_2_s_32);
2154     dpo1 = fib_entry_contribute_ip_forwarding(fei);
2155     ai = fib_entry_get_adj(fei);
2156     FIB_TEST((ai_01 == ai), "1.1.1.2/32 resolves via 10.10.10.1");
2157     fei = fib_table_lookup(fib_index, &pfx_1_1_1_200_s_32);
2158     dpo1 = fib_entry_contribute_ip_forwarding(fei);
2159     ai = fib_entry_get_adj(fei);
2160     FIB_TEST((ai_01 == ai), "1.1.1.200/24 resolves via 10.10.10.1");
2161
2162     /*
2163      * +1 entry. 1.1.1.1/32 already uses 10.10.10.1 so no new pah-list
2164      */
2165     FIB_TEST((4  == fib_path_list_db_size()),   "path list DB population:%d",
2166              fib_path_list_db_size());
2167     FIB_TEST((PNBR+8 == fib_path_list_pool_size()), "path list pool size is %d",
2168              fib_path_list_pool_size());
2169     FIB_TEST((ENBR+13 == fib_entry_pool_size()), "entry pool size is %d",
2170              fib_entry_pool_size());
2171
2172     /*
2173      * the recursive adj for 200.200.200.200 should be updated.
2174      */
2175     FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
2176     FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
2177     fei = fib_table_lookup(fib_index, &bgp_200_pfx);
2178     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 1,
2179                                     tm->hw[0]->sw_if_index),
2180              "RPF list for BGP route has itf index 0");
2181
2182     /*
2183      * insert a more specific route than 1.1.1.0/24 that also covers the
2184      * missing 1.1.1.2/32, but not 1.1.1.200/32. we epxect
2185      * 200.200.200.200 to resolve through it.
2186      */
2187     fib_prefix_t pfx_1_1_1_0_s_28 = {
2188         .fp_len = 28,
2189         .fp_proto = FIB_PROTOCOL_IP4,
2190         .fp_addr = {
2191             /* 1.1.1.0/24 */
2192             .ip4.as_u32 = clib_host_to_net_u32(0x01010100),
2193         },
2194     };
2195
2196     fib_table_entry_path_add(fib_index,
2197                              &pfx_1_1_1_0_s_28,
2198                              FIB_SOURCE_API,
2199                              FIB_ENTRY_FLAG_NONE,
2200                              FIB_PROTOCOL_IP4,
2201                              &nh_10_10_10_2,
2202                              tm->hw[0]->sw_if_index,
2203                              ~0, // invalid fib index
2204                              1,
2205                              NULL,
2206                              FIB_ROUTE_PATH_FLAG_NONE);
2207     fei = fib_table_lookup(fib_index, &pfx_1_1_1_0_s_28);
2208     dpo2 = fib_entry_contribute_ip_forwarding(fei);
2209     ai = fib_entry_get_adj(fei);
2210     FIB_TEST((ai_02 == ai), "1.1.1.0/24 resolves via 10.10.10.2");
2211
2212     /*
2213      * +1 entry. +1 shared path-list
2214      */
2215     FIB_TEST((5  == fib_path_list_db_size()),   "path list DB population:%d",
2216              fib_path_list_db_size());
2217     FIB_TEST((PNBR+9 == fib_path_list_pool_size()), "path list pool size is %d",
2218              fib_path_list_pool_size());
2219     FIB_TEST((ENBR+14 == fib_entry_pool_size()), "entry pool size is %d",
2220              fib_entry_pool_size());
2221
2222     /*
2223      * the recursive adj for 200.200.200.200 should be updated.
2224      * 200.200.200.201 remains unchanged.
2225      */
2226     FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
2227     FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
2228
2229     /*
2230      * remove this /28. 200.200.200.200/32 should revert back to via 1.1.1.0/24
2231      */
2232     fib_table_entry_path_remove(fib_index,
2233                                 &pfx_1_1_1_0_s_28,
2234                                 FIB_SOURCE_API,
2235                                 FIB_PROTOCOL_IP4,
2236                                 &nh_10_10_10_2,
2237                                 tm->hw[0]->sw_if_index,
2238                                 ~0,
2239                                 1,
2240                                 FIB_ROUTE_PATH_FLAG_NONE);
2241     FIB_TEST((fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_0_s_28) == 
2242               FIB_NODE_INDEX_INVALID),
2243              "1.1.1.0/28 removed");
2244     FIB_TEST((fib_table_lookup(fib_index, &pfx_1_1_1_0_s_28) == 
2245               fib_table_lookup(fib_index, &pfx_1_1_1_0_s_24)),
2246              "1.1.1.0/28 lookup via /24");
2247     FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
2248     FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
2249
2250     /*
2251      * -1 entry. -1 shared path-list
2252      */
2253     FIB_TEST((4  == fib_path_list_db_size()),   "path list DB population:%d",
2254              fib_path_list_db_size());
2255     FIB_TEST((PNBR+8 == fib_path_list_pool_size()), "path list pool size is %d",
2256              fib_path_list_pool_size());
2257     FIB_TEST((ENBR+13 == fib_entry_pool_size()), "entry pool size is %d",
2258              fib_entry_pool_size());
2259
2260     /*
2261      * remove 1.1.1.0/24. 200.200.200.200/32 should revert back to via 0.0.0.0/0
2262      */
2263     fib_table_entry_path_remove(fib_index,
2264                                 &pfx_1_1_1_0_s_24,
2265                                 FIB_SOURCE_API,
2266                                 FIB_PROTOCOL_IP4,
2267                                 &nh_10_10_10_1,
2268                                 tm->hw[0]->sw_if_index,
2269                                 ~0,
2270                                 1,
2271                                 FIB_ROUTE_PATH_FLAG_NONE);
2272     FIB_TEST((fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_0_s_24) == 
2273               FIB_NODE_INDEX_INVALID),
2274              "1.1.1.0/24 removed");
2275
2276     fei = fib_table_lookup(fib_index, &pfx_1_1_1_2_s_32);
2277     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2278              "1.1.1.2/32 route is DROP");
2279     fei = fib_table_lookup(fib_index, &pfx_1_1_1_200_s_32);
2280     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2281              "1.1.1.200/32 route is DROP");
2282
2283     FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
2284     FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
2285
2286     /*
2287      * -1 entry
2288      */
2289     FIB_TEST((4  == fib_path_list_db_size()),   "path list DB population:%d",
2290         fib_path_list_db_size());
2291     FIB_TEST((PNBR+8 == fib_path_list_pool_size()), "path list pool size is %d",
2292         fib_path_list_pool_size());
2293     FIB_TEST((ENBR+12 == fib_entry_pool_size()), "entry pool size is %d",
2294              fib_entry_pool_size());
2295
2296     /*
2297      * insert the missing 1.1.1.2/32
2298      */
2299     fei = fib_table_entry_path_add(fib_index,
2300                                    &pfx_1_1_1_2_s_32,
2301                                    FIB_SOURCE_API,
2302                                    FIB_ENTRY_FLAG_NONE,
2303                                    FIB_PROTOCOL_IP4,
2304                                    &nh_10_10_10_1,
2305                                    tm->hw[0]->sw_if_index,
2306                                    ~0, // invalid fib index
2307                                    1,
2308                                    NULL,
2309                                    FIB_ROUTE_PATH_FLAG_NONE);
2310     dpo1 = fib_entry_contribute_ip_forwarding(fei);
2311     ai = fib_entry_get_adj(fei);
2312     FIB_TEST((ai = ai_01), "1.1.1.2/32 resolves via 10.10.10.1");
2313
2314     FIB_TEST_REC_FORW(&bgp_201_pfx, &pfx_1_1_1_200_s_32, 0);
2315     FIB_TEST_REC_FORW(&bgp_200_pfx, &pfx_1_1_1_2_s_32, 0);
2316
2317     /*
2318      * no change. 1.1.1.2/32 was already there RR sourced.
2319      */
2320     FIB_TEST((4  == fib_path_list_db_size()),   "path list DB population:%d",
2321              fib_path_list_db_size());
2322     FIB_TEST((PNBR+8 == fib_path_list_pool_size()), "path list pool size is %d",
2323              fib_path_list_pool_size());
2324     FIB_TEST((ENBR+12 == fib_entry_pool_size()), "entry pool size is %d",
2325              fib_entry_pool_size());
2326
2327     /*
2328      * remove 200.200.200.201/32 which does not have a valid via FIB
2329      */
2330     fib_table_entry_path_remove(fib_index,
2331                                 &bgp_201_pfx,
2332                                 FIB_SOURCE_API,
2333                                 FIB_PROTOCOL_IP4,
2334                                 &pfx_1_1_1_200_s_32.fp_addr,
2335                                 ~0, // no index provided.
2336                                 fib_index,
2337                                 1,
2338                                 FIB_ROUTE_PATH_FLAG_NONE);
2339
2340     /*
2341      * -2 entries (BGP and RR). -1 shared path-list;
2342      */
2343     FIB_TEST((fib_table_lookup_exact_match(fib_index, &bgp_201_pfx) == 
2344               FIB_NODE_INDEX_INVALID),
2345              "200.200.200.201/32 removed");
2346     FIB_TEST((fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_200_s_32) == 
2347               FIB_NODE_INDEX_INVALID),
2348              "1.1.1.200/32 removed");
2349
2350     FIB_TEST((3  == fib_path_list_db_size()),   "path list DB population:%d",
2351              fib_path_list_db_size());
2352     FIB_TEST((PNBR+7 == fib_path_list_pool_size()), "path list pool size is %d",
2353              fib_path_list_pool_size());
2354     FIB_TEST((ENBR+10 == fib_entry_pool_size()), "entry pool size is %d",
2355              fib_entry_pool_size());
2356
2357     /*
2358      * remove 200.200.200.200/32 which does have a valid via FIB
2359      */
2360     fib_table_entry_path_remove(fib_index,
2361                                 &bgp_200_pfx,
2362                                 FIB_SOURCE_API,
2363                                 FIB_PROTOCOL_IP4,
2364                                 &pfx_1_1_1_2_s_32.fp_addr,
2365                                 ~0, // no index provided.
2366                                 fib_index,
2367                                 1,
2368                                 FIB_ROUTE_PATH_FLAG_NONE);
2369
2370     FIB_TEST((fib_table_lookup_exact_match(fib_index, &bgp_200_pfx) == 
2371               FIB_NODE_INDEX_INVALID),
2372              "200.200.200.200/32 removed");
2373     FIB_TEST((fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_2_s_32) != 
2374               FIB_NODE_INDEX_INVALID),
2375              "1.1.1.2/32 still present");
2376
2377     /*
2378      * -1 entry (BGP, the RR source is also API sourced). -1 shared path-list;
2379      */
2380     FIB_TEST((2  == fib_path_list_db_size()),   "path list DB population:%d",
2381              fib_path_list_db_size());
2382     FIB_TEST((PNBR+6 == fib_path_list_pool_size()), "path list pool size is %d",
2383              fib_path_list_pool_size());
2384     FIB_TEST((ENBR+9 == fib_entry_pool_size()), "entry pool size is %d",
2385              fib_entry_pool_size());
2386
2387     /*
2388      * A recursive prefix that has a 2 path  load-balance.
2389      * It also shares a next-hop with other BGP prefixes and hence
2390      * test the ref counting of RR sourced prefixes and 2 level LB.
2391      */
2392     const fib_prefix_t bgp_102 = {
2393         .fp_len = 32,
2394         .fp_proto = FIB_PROTOCOL_IP4,
2395         .fp_addr = {
2396             /* 100.100.100.101/32 */
2397             .ip4.as_u32 = clib_host_to_net_u32(0x64646466),
2398         },
2399     };
2400     fib_table_entry_path_add(fib_index,
2401                              &bgp_102,
2402                              FIB_SOURCE_API,
2403                              FIB_ENTRY_FLAG_NONE,
2404                              FIB_PROTOCOL_IP4,
2405                              &pfx_1_1_1_1_s_32.fp_addr,
2406                              ~0, // no index provided.
2407                              fib_index, // same as route
2408                              1,
2409                              NULL,
2410                              FIB_ROUTE_PATH_FLAG_NONE);
2411     fib_table_entry_path_add(fib_index,
2412                              &bgp_102,
2413                              FIB_SOURCE_API,
2414                              FIB_ENTRY_FLAG_NONE,
2415                              FIB_PROTOCOL_IP4,
2416                              &pfx_1_1_1_2_s_32.fp_addr,
2417                              ~0, // no index provided.
2418                              fib_index, // same as route's FIB
2419                              1,
2420                              NULL,
2421                              FIB_ROUTE_PATH_FLAG_NONE);
2422     fei = fib_table_lookup_exact_match(fib_index, &bgp_102);
2423     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "100.100.100.102/32 presnet");
2424     dpo = fib_entry_contribute_ip_forwarding(fei);
2425
2426     fei  = fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_1_s_32);
2427     dpo1 = fib_entry_contribute_ip_forwarding(fei);
2428     fei  = fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_2_s_32);
2429     dpo2 = fib_entry_contribute_ip_forwarding(fei);
2430
2431     lb = load_balance_get(dpo->dpoi_index);
2432     FIB_TEST((lb->lb_n_buckets == 2), "Recursive LB has %d bucket", lb->lb_n_buckets);
2433     FIB_TEST(!dpo_cmp(dpo1, load_balance_get_bucket(dpo->dpoi_index, 0)),
2434              "First via 10.10.10.1");
2435     FIB_TEST(!dpo_cmp(dpo2, load_balance_get_bucket(dpo->dpoi_index, 1)),
2436              "Second via 10.10.10.1");
2437
2438     fib_table_entry_path_remove(fib_index,
2439                                 &bgp_102,
2440                                 FIB_SOURCE_API,
2441                                 FIB_PROTOCOL_IP4,
2442                                 &pfx_1_1_1_1_s_32.fp_addr,
2443                                 ~0, // no index provided.
2444                                 fib_index, // same as route's FIB
2445                                 1,
2446                                 FIB_ROUTE_PATH_FLAG_NONE);
2447     fib_table_entry_path_remove(fib_index,
2448                                 &bgp_102,
2449                                 FIB_SOURCE_API,
2450                                 FIB_PROTOCOL_IP4,
2451                                 &pfx_1_1_1_2_s_32.fp_addr,
2452                                 ~0, // no index provided.
2453                                 fib_index, // same as route's FIB
2454                                 1,
2455                                 FIB_ROUTE_PATH_FLAG_NONE);
2456     fei = fib_table_lookup_exact_match(fib_index, &bgp_102);
2457     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "100.100.100.102/32 removed");
2458
2459     /*
2460      * remove the remaining recursives
2461      */
2462     fib_table_entry_path_remove(fib_index,
2463                                 &bgp_100_pfx,
2464                                 FIB_SOURCE_API,
2465                                 FIB_PROTOCOL_IP4,
2466                                 &pfx_1_1_1_1_s_32.fp_addr,
2467                                 ~0, // no index provided.
2468                                 fib_index, // same as route's FIB
2469                                 1,
2470                                 FIB_ROUTE_PATH_FLAG_NONE);
2471     fib_table_entry_path_remove(fib_index,
2472                                 &bgp_101_pfx,
2473                                 FIB_SOURCE_API,
2474                                 FIB_PROTOCOL_IP4,
2475                                 &pfx_1_1_1_1_s_32.fp_addr,
2476                                 ~0, // no index provided.
2477                                 fib_index, // same as route's FIB
2478                                 1,
2479                                 FIB_ROUTE_PATH_FLAG_NONE);
2480     FIB_TEST((fib_table_lookup_exact_match(fib_index, &bgp_100_pfx) == 
2481               FIB_NODE_INDEX_INVALID),
2482              "100.100.100.100/32 removed");
2483     FIB_TEST((fib_table_lookup_exact_match(fib_index, &bgp_101_pfx) == 
2484               FIB_NODE_INDEX_INVALID),
2485              "100.100.100.101/32 removed");
2486
2487     /*
2488      * -2 entry (2*BGP, the RR source is also API sourced). -1 shared path-list;
2489      */
2490     FIB_TEST((1  == fib_path_list_db_size()),   "path list DB population:%d",
2491              fib_path_list_db_size());
2492     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
2493              fib_path_list_pool_size());
2494     FIB_TEST((ENBR+7 == fib_entry_pool_size()), "entry pool size is %d",
2495              fib_entry_pool_size());
2496
2497     /*
2498      * Add a recursive route via a connected cover, using an adj-fib that does exist
2499      */
2500     fib_table_entry_path_add(fib_index,
2501                              &bgp_200_pfx,
2502                              FIB_SOURCE_API,
2503                              FIB_ENTRY_FLAG_NONE,
2504                              FIB_PROTOCOL_IP4,
2505                              &nh_10_10_10_1,
2506                              ~0, // no index provided.
2507                              fib_index, // Same as route's FIB
2508                              1,
2509                              NULL,
2510                              FIB_ROUTE_PATH_FLAG_NONE);
2511
2512     /*
2513      * +1 entry. +1 shared path-list (recursive via 10.10.10.1)
2514      */
2515     FIB_TEST((2  == fib_path_list_db_size()),   "path list DB population:%d",
2516              fib_path_list_db_size());
2517     FIB_TEST((PNBR+6 == fib_path_list_pool_size()), "path list pool size is %d",
2518              fib_path_list_pool_size());
2519     FIB_TEST((ENBR+8 == fib_entry_pool_size()), "entry pool size is %d",
2520              fib_entry_pool_size());
2521
2522     fei = fib_table_lookup_exact_match(fib_index, &bgp_200_pfx);
2523     dpo = fib_entry_contribute_ip_forwarding(fei);
2524
2525     fei  = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_1_s_32);
2526     dpo1 = fib_entry_contribute_ip_forwarding(fei);
2527
2528     FIB_TEST(!dpo_cmp(dpo1, load_balance_get_bucket(dpo->dpoi_index, 0)),
2529              "200.200.200.200/32 is recursive via adj for 10.10.10.1");
2530
2531     FIB_TEST((FIB_ENTRY_FLAG_ATTACHED  == fib_entry_get_flags(fei)),
2532              "Flags set on RR via existing attached");
2533
2534     /*
2535      * Add a recursive route via a connected cover, using and adj-fib that does
2536      * not exist
2537      */
2538     ip46_address_t nh_10_10_10_3 = {
2539         .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a03),
2540     };
2541     fib_prefix_t pfx_10_10_10_3 = {
2542         .fp_len = 32,
2543         .fp_proto = FIB_PROTOCOL_IP4,
2544         .fp_addr = nh_10_10_10_3,
2545     };
2546
2547     fib_table_entry_path_add(fib_index,
2548                              &bgp_201_pfx,
2549                              FIB_SOURCE_API,
2550                              FIB_ENTRY_FLAG_NONE,
2551                              FIB_PROTOCOL_IP4,
2552                              &nh_10_10_10_3,
2553                              ~0, // no index provided.
2554                              fib_index,
2555                              1,
2556                              NULL,
2557                              FIB_ROUTE_PATH_FLAG_NONE);
2558
2559     /*
2560      * +2 entries (BGP and RR). +1 shared path-list (recursive via 10.10.10.3) and
2561      * one unshared non-recursive via 10.10.10.3
2562      */
2563     FIB_TEST((3  == fib_path_list_db_size()),   "path list DB population:%d",
2564              fib_path_list_db_size());
2565     FIB_TEST((PNBR+8 == fib_path_list_pool_size()), "path list pool size is %d",
2566              fib_path_list_pool_size());
2567     FIB_TEST((ENBR+10 == fib_entry_pool_size()), "entry pool size is %d",
2568              fib_entry_pool_size());
2569
2570     ai_03 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
2571                                 VNET_LINK_IP4,
2572                                 &nh_10_10_10_3,
2573                                 tm->hw[0]->sw_if_index);
2574
2575     fei  = fib_table_lookup_exact_match(fib_index, &bgp_201_pfx);
2576     dpo  = fib_entry_contribute_ip_forwarding(fei);
2577     fei  = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_3);
2578     dpo1 = fib_entry_contribute_ip_forwarding(fei);
2579
2580     ai = fib_entry_get_adj(fei);
2581     FIB_TEST((ai == ai_03), "adj for 10.10.10.3/32 is via adj for 10.10.10.3");
2582     FIB_TEST(((FIB_ENTRY_FLAG_ATTACHED | FIB_ENTRY_FLAG_CONNECTED) ==
2583               fib_entry_get_flags(fei)),
2584              "Flags set on RR via non-existing attached");
2585
2586     FIB_TEST(!dpo_cmp(dpo1, load_balance_get_bucket(dpo->dpoi_index, 0)),
2587              "adj for 200.200.200.200/32 is recursive via adj for 10.10.10.3");
2588
2589     adj_unlock(ai_03);
2590
2591     /*
2592      * remove the recursives
2593      */
2594     fib_table_entry_path_remove(fib_index,
2595                                 &bgp_200_pfx,
2596                                 FIB_SOURCE_API,
2597                                 FIB_PROTOCOL_IP4,
2598                                 &nh_10_10_10_1,
2599                                 ~0, // no index provided.
2600                                 fib_index, // same as route's FIB
2601                                 1,
2602                                 FIB_ROUTE_PATH_FLAG_NONE);
2603     fib_table_entry_path_remove(fib_index,
2604                                 &bgp_201_pfx,
2605                                 FIB_SOURCE_API,
2606                                 FIB_PROTOCOL_IP4,
2607                                 &nh_10_10_10_3,
2608                                 ~0, // no index provided.
2609                                 fib_index, // same as route's FIB
2610                                 1,
2611                                 FIB_ROUTE_PATH_FLAG_NONE);
2612
2613     FIB_TEST((fib_table_lookup_exact_match(fib_index, &bgp_201_pfx) ==
2614               FIB_NODE_INDEX_INVALID),
2615              "200.200.200.201/32 removed");
2616     FIB_TEST((fib_table_lookup_exact_match(fib_index, &bgp_200_pfx) ==
2617               FIB_NODE_INDEX_INVALID),
2618              "200.200.200.200/32 removed");
2619     FIB_TEST((fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_3) ==
2620               FIB_NODE_INDEX_INVALID),
2621              "10.10.10.3/32 removed");
2622
2623     /*
2624      * -3 entries (2*BGP and RR). -2 shared path-list (recursive via 10.10.10.3 &
2625      *  10.10.10.1) and one unshared non-recursive via 10.10.10.3
2626      */
2627     FIB_TEST((1  == fib_path_list_db_size()),   "path list DB population:%d",
2628              fib_path_list_db_size());
2629     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
2630              fib_path_list_pool_size());
2631     FIB_TEST((ENBR+7 == fib_entry_pool_size()), "entry pool size is %d",
2632              fib_entry_pool_size());
2633
2634
2635     /*
2636      * RECURSION LOOPS
2637      *  Add 5.5.5.5/32 -> 5.5.5.6/32 -> 5.5.5.7/32 -> 5.5.5.5/32
2638      */
2639     fib_prefix_t pfx_5_5_5_5_s_32 = {
2640         .fp_len = 32,
2641         .fp_proto = FIB_PROTOCOL_IP4,
2642         .fp_addr = {
2643             .ip4.as_u32 = clib_host_to_net_u32(0x05050505),
2644         },
2645     };
2646     fib_prefix_t pfx_5_5_5_6_s_32 = {
2647         .fp_len = 32,
2648         .fp_proto = FIB_PROTOCOL_IP4,
2649         .fp_addr = {
2650             .ip4.as_u32 = clib_host_to_net_u32(0x05050506),
2651         },
2652     };
2653     fib_prefix_t pfx_5_5_5_7_s_32 = {
2654         .fp_len = 32,
2655         .fp_proto = FIB_PROTOCOL_IP4,
2656         .fp_addr = {
2657             .ip4.as_u32 = clib_host_to_net_u32(0x05050507),
2658         },
2659     };
2660
2661     fib_table_entry_path_add(fib_index,
2662                              &pfx_5_5_5_5_s_32,
2663                              FIB_SOURCE_API,
2664                              FIB_ENTRY_FLAG_NONE,
2665                              FIB_PROTOCOL_IP4,
2666                              &pfx_5_5_5_6_s_32.fp_addr,
2667                              ~0, // no index provided.
2668                              fib_index,
2669                              1,
2670                              NULL,
2671                              FIB_ROUTE_PATH_FLAG_NONE);
2672     fib_table_entry_path_add(fib_index,
2673                              &pfx_5_5_5_6_s_32,
2674                              FIB_SOURCE_API,
2675                              FIB_ENTRY_FLAG_NONE,
2676                              FIB_PROTOCOL_IP4,
2677                              &pfx_5_5_5_7_s_32.fp_addr,
2678                              ~0, // no index provided.
2679                              fib_index,
2680                              1,
2681                              NULL,
2682                              FIB_ROUTE_PATH_FLAG_NONE);
2683     fib_table_entry_path_add(fib_index,
2684                              &pfx_5_5_5_7_s_32,
2685                              FIB_SOURCE_API,
2686                              FIB_ENTRY_FLAG_NONE,
2687                              FIB_PROTOCOL_IP4,
2688                              &pfx_5_5_5_5_s_32.fp_addr,
2689                              ~0, // no index provided.
2690                              fib_index,
2691                              1,
2692                              NULL,
2693                              FIB_ROUTE_PATH_FLAG_NONE);
2694     /*
2695      * +3 entries, +3 shared path-list
2696      */
2697     FIB_TEST((4  == fib_path_list_db_size()),   "path list DB population:%d",
2698              fib_path_list_db_size());
2699     FIB_TEST((PNBR+8 == fib_path_list_pool_size()), "path list pool size is %d",
2700              fib_path_list_pool_size());
2701     FIB_TEST((ENBR+10 == fib_entry_pool_size()), "entry pool size is %d",
2702              fib_entry_pool_size());
2703
2704     /*
2705      * All the entries have only looped paths, so they are all drop
2706      */
2707     fei = fib_table_lookup(fib_index, &pfx_5_5_5_7_s_32);
2708     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2709              "LB for 5.5.5.7/32 is via adj for DROP");
2710     fei = fib_table_lookup(fib_index, &pfx_5_5_5_5_s_32);
2711     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2712              "LB for 5.5.5.5/32 is via adj for DROP");
2713     fei = fib_table_lookup(fib_index, &pfx_5_5_5_6_s_32);
2714     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2715              "LB for 5.5.5.6/32 is via adj for DROP");
2716
2717     /*
2718      * provide 5.5.5.6/32 with alternate path.
2719      * this will allow only 5.5.5.6/32 to forward with this path, the others
2720      * are still drop since the loop is still present.
2721      */
2722     fib_table_entry_path_add(fib_index,
2723                              &pfx_5_5_5_6_s_32,
2724                              FIB_SOURCE_API,
2725                              FIB_ENTRY_FLAG_NONE,
2726                              FIB_PROTOCOL_IP4,
2727                              &nh_10_10_10_1,
2728                              tm->hw[0]->sw_if_index,
2729                              ~0,
2730                              1,
2731                              NULL,
2732                              FIB_ROUTE_PATH_FLAG_NONE);
2733
2734     fei = fib_table_lookup(fib_index, &pfx_5_5_5_6_s_32);
2735     dpo1 = fib_entry_contribute_ip_forwarding(fei);
2736
2737     lb = load_balance_get(dpo1->dpoi_index);
2738     FIB_TEST((lb->lb_n_buckets == 1), "5.5.5.6 LB has %d bucket", lb->lb_n_buckets);
2739
2740     dpo2 = load_balance_get_bucket(dpo1->dpoi_index, 0);
2741     FIB_TEST(DPO_ADJACENCY == dpo2->dpoi_type, "type is %d", dpo2->dpoi_type);
2742     FIB_TEST((ai_01 == dpo2->dpoi_index),
2743              "5.5.5.6 bucket 0 resolves via 10.10.10.2");
2744
2745     fei = fib_table_lookup(fib_index, &pfx_5_5_5_7_s_32);
2746     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2747              "LB for 5.5.5.7/32 is via adj for DROP");
2748     fei = fib_table_lookup(fib_index, &pfx_5_5_5_5_s_32);
2749     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2750              "LB for 5.5.5.5/32 is via adj for DROP");
2751
2752     /*
2753      * remove the alternate path for 5.5.5.6/32
2754      * back to all drop
2755      */
2756     fib_table_entry_path_remove(fib_index,
2757                                 &pfx_5_5_5_6_s_32,
2758                                 FIB_SOURCE_API,
2759                                 FIB_PROTOCOL_IP4,
2760                                 &nh_10_10_10_1,
2761                                 tm->hw[0]->sw_if_index,
2762                                 ~0,
2763                                 1,
2764                                 FIB_ROUTE_PATH_FLAG_NONE);
2765
2766     fei = fib_table_lookup(fib_index, &pfx_5_5_5_7_s_32);
2767     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2768              "LB for 5.5.5.7/32 is via adj for DROP");
2769     fei = fib_table_lookup(fib_index, &pfx_5_5_5_5_s_32);
2770     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2771              "LB for 5.5.5.5/32 is via adj for DROP");
2772     fei = fib_table_lookup(fib_index, &pfx_5_5_5_6_s_32);
2773     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2774              "LB for 5.5.5.6/32 is via adj for DROP");
2775
2776     /*
2777      * break the loop by giving 5.5.5.5/32 a new set of paths
2778      * expect all to forward via this new path.
2779      */
2780     fib_table_entry_update_one_path(fib_index,
2781                                     &pfx_5_5_5_5_s_32,
2782                                     FIB_SOURCE_API,
2783                                     FIB_ENTRY_FLAG_NONE,
2784                                     FIB_PROTOCOL_IP4,
2785                                     &nh_10_10_10_1,
2786                                     tm->hw[0]->sw_if_index,
2787                                     ~0, // invalid fib index
2788                                     1,
2789                                     NULL,
2790                                     FIB_ROUTE_PATH_FLAG_NONE);
2791
2792     fei = fib_table_lookup(fib_index, &pfx_5_5_5_5_s_32);
2793     dpo1 = fib_entry_contribute_ip_forwarding(fei);
2794     lb = load_balance_get(dpo1->dpoi_index);
2795     FIB_TEST((lb->lb_n_buckets == 1), "5.5.5.5 LB has %d bucket", lb->lb_n_buckets);
2796
2797     dpo2 = load_balance_get_bucket(dpo1->dpoi_index, 0);
2798     FIB_TEST(DPO_ADJACENCY == dpo2->dpoi_type, "type is %d", dpo2->dpoi_type);
2799     FIB_TEST((ai_01 == dpo2->dpoi_index),
2800              "5.5.5.5 bucket 0 resolves via 10.10.10.2");
2801
2802     fei = fib_table_lookup_exact_match(fib_index, &pfx_5_5_5_7_s_32);
2803     dpo2 = fib_entry_contribute_ip_forwarding(fei);
2804
2805     lb = load_balance_get(dpo2->dpoi_index);
2806     FIB_TEST((lb->lb_n_buckets == 1), "Recursive LB has %d bucket", lb->lb_n_buckets);
2807     FIB_TEST(!dpo_cmp(dpo1, load_balance_get_bucket(dpo2->dpoi_index, 0)),
2808              "5.5.5.5.7 via 5.5.5.5");
2809
2810     fei = fib_table_lookup_exact_match(fib_index, &pfx_5_5_5_6_s_32);
2811     dpo1 = fib_entry_contribute_ip_forwarding(fei);
2812
2813     lb = load_balance_get(dpo1->dpoi_index);
2814     FIB_TEST((lb->lb_n_buckets == 1), "Recursive LB has %d bucket", lb->lb_n_buckets);
2815     FIB_TEST(!dpo_cmp(dpo2, load_balance_get_bucket(dpo1->dpoi_index, 0)),
2816              "5.5.5.5.6 via 5.5.5.7");
2817
2818     /*
2819      * revert back to the loop. so we can remove the prefixes with
2820      * the loop intact
2821      */
2822     fib_table_entry_update_one_path(fib_index,
2823                                     &pfx_5_5_5_5_s_32,
2824                                     FIB_SOURCE_API,
2825                                     FIB_ENTRY_FLAG_NONE,
2826                                     FIB_PROTOCOL_IP4,
2827                                     &pfx_5_5_5_6_s_32.fp_addr,
2828                                     ~0, // no index provided.
2829                                     fib_index,
2830                                     1,
2831                                     NULL,
2832                                     FIB_ROUTE_PATH_FLAG_NONE);
2833
2834     fei = fib_table_lookup(fib_index, &pfx_5_5_5_7_s_32);
2835     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2836              "LB for 5.5.5.7/32 is via adj for DROP");
2837     fei = fib_table_lookup(fib_index, &pfx_5_5_5_5_s_32);
2838     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2839              "LB for 5.5.5.5/32 is via adj for DROP");
2840     fei = fib_table_lookup(fib_index, &pfx_5_5_5_6_s_32);
2841     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2842              "LB for 5.5.5.6/32 is via adj for DROP");
2843
2844     /*
2845      * remove all the 5.5.5.x/32 prefixes
2846      */
2847     fib_table_entry_path_remove(fib_index,
2848                                 &pfx_5_5_5_5_s_32,
2849                                 FIB_SOURCE_API,
2850                                 FIB_PROTOCOL_IP4,
2851                                 &pfx_5_5_5_6_s_32.fp_addr,
2852                                 ~0, // no index provided.
2853                                 fib_index, // same as route's FIB
2854                                 1,
2855                                 FIB_ROUTE_PATH_FLAG_NONE);
2856     fib_table_entry_path_remove(fib_index,
2857                                 &pfx_5_5_5_6_s_32,
2858                                 FIB_SOURCE_API,
2859                                 FIB_PROTOCOL_IP4,
2860                                 &pfx_5_5_5_7_s_32.fp_addr,
2861                                 ~0, // no index provided.
2862                                 fib_index, // same as route's FIB
2863                                 1,
2864                                 FIB_ROUTE_PATH_FLAG_NONE);
2865     fib_table_entry_path_remove(fib_index,
2866                                 &pfx_5_5_5_7_s_32,
2867                                 FIB_SOURCE_API,
2868                                 FIB_PROTOCOL_IP4,
2869                                 &pfx_5_5_5_5_s_32.fp_addr,
2870                                 ~0, // no index provided.
2871                                 fib_index, // same as route's FIB
2872                                 1,
2873                                 FIB_ROUTE_PATH_FLAG_NONE);
2874     fib_table_entry_path_remove(fib_index,
2875                                 &pfx_5_5_5_6_s_32,
2876                                 FIB_SOURCE_API,
2877                                 FIB_PROTOCOL_IP4,
2878                                 &nh_10_10_10_2,
2879                                 ~0, // no index provided.
2880                                 fib_index, // same as route's FIB
2881                                 1,
2882                                 FIB_ROUTE_PATH_FLAG_NONE);
2883
2884     /*
2885      * -3 entries, -3 shared path-list
2886      */
2887     FIB_TEST((1  == fib_path_list_db_size()),   "path list DB population:%d",
2888              fib_path_list_db_size());
2889     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
2890              fib_path_list_pool_size());
2891     FIB_TEST((ENBR+7 == fib_entry_pool_size()), "entry pool size is %d",
2892              fib_entry_pool_size());
2893
2894     /*
2895      * Single level loop 5.5.5.5/32 via 5.5.5.5/32
2896      */
2897     fib_table_entry_path_add(fib_index,
2898                              &pfx_5_5_5_6_s_32,
2899                              FIB_SOURCE_API,
2900                              FIB_ENTRY_FLAG_NONE,
2901                              FIB_PROTOCOL_IP4,
2902                              &pfx_5_5_5_6_s_32.fp_addr,
2903                              ~0, // no index provided.
2904                              fib_index,
2905                              1,
2906                              NULL,
2907                              FIB_ROUTE_PATH_FLAG_NONE);
2908     fei = fib_table_lookup(fib_index, &pfx_5_5_5_6_s_32);
2909     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
2910              "1-level 5.5.5.6/32 loop is via adj for DROP");
2911  
2912     fib_table_entry_path_remove(fib_index,
2913                                 &pfx_5_5_5_6_s_32,
2914                                 FIB_SOURCE_API,
2915                                 FIB_PROTOCOL_IP4,
2916                                 &pfx_5_5_5_6_s_32.fp_addr,
2917                                 ~0, // no index provided.
2918                                 fib_index, // same as route's FIB
2919                                 1,
2920                                 FIB_ROUTE_PATH_FLAG_NONE);
2921     FIB_TEST(FIB_NODE_INDEX_INVALID ==
2922              fib_table_lookup_exact_match(fib_index, &pfx_5_5_5_6_s_32),
2923              "1-level 5.5.5.6/32 loop is removed");
2924
2925     /*
2926      * A recursive route whose next-hop is covered by the prefix.
2927      * This would mean the via-fib, which inherits forwarding from its
2928      * cover, thus picks up forwarding from the prfix, which is via the
2929      * via-fib, and we have a loop.
2930      */
2931     fib_prefix_t pfx_23_23_23_0_s_24 = {
2932         .fp_len = 24,
2933         .fp_proto = FIB_PROTOCOL_IP4,
2934         .fp_addr = {
2935             .ip4.as_u32 = clib_host_to_net_u32(0x17171700),
2936         },
2937     };
2938     fib_prefix_t pfx_23_23_23_23_s_32 = {
2939         .fp_len = 32,
2940         .fp_proto = FIB_PROTOCOL_IP4,
2941         .fp_addr = {
2942             .ip4.as_u32 = clib_host_to_net_u32(0x17171717),
2943         },
2944     };
2945     fei = fib_table_entry_path_add(fib_index,
2946                                    &pfx_23_23_23_0_s_24,
2947                                    FIB_SOURCE_API,
2948                                    FIB_ENTRY_FLAG_NONE,
2949                                    FIB_PROTOCOL_IP4,
2950                                    &pfx_23_23_23_23_s_32.fp_addr,
2951                                    ~0, // recursive
2952                                    fib_index,
2953                                    1,
2954                                    NULL,
2955                                    FIB_ROUTE_PATH_FLAG_NONE);
2956     dpo = fib_entry_contribute_ip_forwarding(fei);
2957     FIB_TEST(load_balance_is_drop(dpo),
2958              "23.23.23.0/24 via covered is DROP");
2959     fib_table_entry_delete_index(fei, FIB_SOURCE_API);
2960
2961     /*
2962      * add-remove test. no change.
2963      */
2964     FIB_TEST((1  == fib_path_list_db_size()),   "path list DB population:%d",
2965              fib_path_list_db_size());
2966     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
2967              fib_path_list_pool_size());
2968     FIB_TEST((ENBR+7 == fib_entry_pool_size()), "entry pool size is %d",
2969              fib_entry_pool_size());
2970
2971     /*
2972      * A recursive route with recursion constraints.
2973      *  200.200.200.200/32 via 1.1.1.1 is recurse via host constrained
2974      */
2975     fib_table_entry_path_add(fib_index,
2976                              &bgp_200_pfx,
2977                              FIB_SOURCE_API,
2978                              FIB_ENTRY_FLAG_NONE,
2979                              FIB_PROTOCOL_IP4,
2980                              &nh_1_1_1_1,
2981                              ~0,
2982                              fib_index,
2983                              1,
2984                              NULL,
2985                              FIB_ROUTE_PATH_RESOLVE_VIA_HOST);
2986
2987     fei = fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_1_s_32);
2988     dpo2 = fib_entry_contribute_ip_forwarding(fei);
2989
2990     fei = fib_table_lookup_exact_match(fib_index, &bgp_200_pfx);
2991     dpo1 = fib_entry_contribute_ip_forwarding(fei);
2992
2993     FIB_TEST(!dpo_cmp(dpo2, load_balance_get_bucket(dpo1->dpoi_index, 0)),
2994              "adj for 200.200.200.200/32 is recursive via adj for 1.1.1.1");
2995
2996     /*
2997      * save the load-balance. we expect it to be inplace modified
2998      */
2999     lb = load_balance_get(dpo1->dpoi_index);
3000
3001     /*
3002      * add a covering prefix for the via fib that would otherwise serve
3003      * as the resolving route when the host is removed
3004      */
3005     fib_table_entry_path_add(fib_index,
3006                              &pfx_1_1_1_0_s_28,
3007                              FIB_SOURCE_API,
3008                              FIB_ENTRY_FLAG_NONE,
3009                              FIB_PROTOCOL_IP4,
3010                              &nh_10_10_10_1,
3011                              tm->hw[0]->sw_if_index,
3012                              ~0, // invalid fib index
3013                              1,
3014                              NULL,
3015                              FIB_ROUTE_PATH_FLAG_NONE);
3016     fei = fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_0_s_28);
3017     ai = fib_entry_get_adj(fei);
3018     FIB_TEST((ai == ai_01),
3019              "adj for 1.1.1.0/28 is via adj for 1.1.1.1");
3020
3021     /*
3022      * remove the host via FIB - expect the BGP prefix to be drop
3023      */
3024     fib_table_entry_path_remove(fib_index,
3025                                 &pfx_1_1_1_1_s_32,
3026                                 FIB_SOURCE_API,
3027                                 FIB_PROTOCOL_IP4,
3028                                 &nh_10_10_10_1,
3029                                 tm->hw[0]->sw_if_index,
3030                                 ~0, // invalid fib index
3031                                 1,
3032                                 FIB_ROUTE_PATH_FLAG_NONE);
3033
3034     FIB_TEST(!dpo_cmp(dpo_drop, load_balance_get_bucket(dpo1->dpoi_index, 0)),
3035              "adj for 200.200.200.200/32 is recursive via adj for DROP");
3036
3037     /*
3038      * add the via-entry host reoute back. expect to resolve again
3039      */
3040     fib_table_entry_path_add(fib_index,
3041                              &pfx_1_1_1_1_s_32,
3042                              FIB_SOURCE_API,
3043                              FIB_ENTRY_FLAG_NONE,
3044                              FIB_PROTOCOL_IP4,
3045                              &nh_10_10_10_1,
3046                              tm->hw[0]->sw_if_index,
3047                              ~0, // invalid fib index
3048                              1,
3049                              NULL,
3050                              FIB_ROUTE_PATH_FLAG_NONE);
3051     FIB_TEST(!dpo_cmp(dpo2, load_balance_get_bucket(dpo1->dpoi_index, 0)),
3052              "adj for 200.200.200.200/32 is recursive via adj for 1.1.1.1");
3053
3054     /*
3055      * add another path for the recursive. it will then have 2.
3056      */
3057     fib_prefix_t pfx_1_1_1_3_s_32 = {
3058         .fp_len = 32,
3059         .fp_proto = FIB_PROTOCOL_IP4,
3060         .fp_addr = {
3061             .ip4.as_u32 = clib_host_to_net_u32(0x01010103),
3062         },
3063     };
3064     fib_table_entry_path_add(fib_index,
3065                              &pfx_1_1_1_3_s_32,
3066                              FIB_SOURCE_API,
3067                              FIB_ENTRY_FLAG_NONE,
3068                              FIB_PROTOCOL_IP4,
3069                              &nh_10_10_10_2,
3070                              tm->hw[0]->sw_if_index,
3071                              ~0, // invalid fib index
3072                              1,
3073                              NULL,
3074                              FIB_ROUTE_PATH_FLAG_NONE);
3075
3076     fib_table_entry_path_add(fib_index,
3077                              &bgp_200_pfx,
3078                              FIB_SOURCE_API,
3079                              FIB_ENTRY_FLAG_NONE,
3080                              FIB_PROTOCOL_IP4,
3081                              &pfx_1_1_1_3_s_32.fp_addr,
3082                              ~0,
3083                              fib_index,
3084                              1,
3085                              NULL,
3086                              FIB_ROUTE_PATH_RESOLVE_VIA_HOST);
3087
3088     fei = fib_table_lookup_exact_match(fib_index, &bgp_200_pfx);
3089     dpo = fib_entry_contribute_ip_forwarding(fei);
3090
3091     fei = fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_1_s_32);
3092     dpo2 = fib_entry_contribute_ip_forwarding(fei);
3093     FIB_TEST(!dpo_cmp(dpo2, load_balance_get_bucket(dpo->dpoi_index, 0)),
3094              "adj for 200.200.200.200/32 is recursive via adj for 1.1.1.1");
3095     fei = fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_3_s_32);
3096     dpo1 = fib_entry_contribute_ip_forwarding(fei);
3097     FIB_TEST(!dpo_cmp(dpo1, load_balance_get_bucket(dpo->dpoi_index, 1)),
3098              "adj for 200.200.200.200/32 is recursive via adj for 1.1.1.3");
3099
3100     /*
3101      * expect the lb-map used by the recursive's load-balance is using both buckets
3102      */
3103     load_balance_map_t *lbm;
3104     index_t lbmi;
3105
3106     lb = load_balance_get(dpo->dpoi_index);
3107     lbmi = lb->lb_map;
3108     load_balance_map_lock(lbmi);
3109     lbm = load_balance_map_get(lbmi);
3110
3111     FIB_TEST(lbm->lbm_buckets[0] == 0,
3112              "LB maps's bucket 0 is %d",
3113              lbm->lbm_buckets[0]);
3114     FIB_TEST(lbm->lbm_buckets[1] == 1,
3115              "LB maps's bucket 1 is %d",
3116              lbm->lbm_buckets[1]);
3117
3118     /*
3119      * withdraw one of the /32 via-entrys.
3120      * that ECMP path will be unresolved and forwarding should continue on the
3121      * other available path. this is an iBGP PIC edge failover.
3122      * Test the forwarding changes without re-fetching the adj from the
3123      * recursive entry. this ensures its the same one that is updated; i.e. an
3124      * inplace-modify.
3125      */
3126     fib_table_entry_path_remove(fib_index,
3127                                 &pfx_1_1_1_1_s_32,
3128                                 FIB_SOURCE_API,
3129                                 FIB_PROTOCOL_IP4,
3130                                 &nh_10_10_10_1,
3131                                 tm->hw[0]->sw_if_index,
3132                                 ~0, // invalid fib index
3133                                 1,
3134                                 FIB_ROUTE_PATH_FLAG_NONE);
3135
3136     fei = fib_table_lookup_exact_match(fib_index, &bgp_200_pfx);
3137     FIB_TEST(!dpo_cmp(dpo, fib_entry_contribute_ip_forwarding(fei)),
3138              "post PIC 200.200.200.200/32 was inplace modified");
3139
3140     FIB_TEST(!dpo_cmp(dpo1, load_balance_get_bucket_i(lb, 0)),
3141              "post PIC adj for 200.200.200.200/32 is recursive"
3142              " via adj for 1.1.1.3");
3143
3144     /*
3145      * the LB maps that was locked above should have been modified to remove
3146      * the path that was down, and thus its bucket points to a path that is
3147      * still up.
3148      */
3149     FIB_TEST(lbm->lbm_buckets[0] == 1,
3150              "LB maps's bucket 0 is %d",
3151              lbm->lbm_buckets[0]);
3152     FIB_TEST(lbm->lbm_buckets[1] == 1,
3153              "LB maps's bucket 1 is %d",
3154              lbm->lbm_buckets[1]);
3155
3156     load_balance_map_unlock(lb->lb_map);
3157
3158     /*
3159      * add it back. again 
3160      */
3161     fib_table_entry_path_add(fib_index,
3162                              &pfx_1_1_1_1_s_32,
3163                              FIB_SOURCE_API,
3164                              FIB_ENTRY_FLAG_NONE,
3165                              FIB_PROTOCOL_IP4,
3166                              &nh_10_10_10_1,
3167                              tm->hw[0]->sw_if_index,
3168                              ~0, // invalid fib index
3169                              1,
3170                              NULL,
3171                              FIB_ROUTE_PATH_FLAG_NONE);
3172
3173     FIB_TEST(!dpo_cmp(dpo2, load_balance_get_bucket_i(lb, 0)),
3174              "post PIC recovery adj for 200.200.200.200/32 is recursive "
3175              "via adj for 1.1.1.1");
3176     FIB_TEST(!dpo_cmp(dpo1, load_balance_get_bucket_i(lb, 1)),
3177              "post PIC recovery adj for 200.200.200.200/32 is recursive "
3178              "via adj for 1.1.1.3");
3179
3180     fei = fib_table_lookup_exact_match(fib_index, &bgp_200_pfx);
3181     dpo = fib_entry_contribute_ip_forwarding(fei);
3182     FIB_TEST(lb == load_balance_get(dpo->dpoi_index),
3183              "post PIC 200.200.200.200/32 was inplace modified");
3184
3185     /*
3186      * add a 3rd path. this makes the LB 16 buckets. 
3187      */
3188     fib_table_entry_path_add(fib_index,
3189                              &bgp_200_pfx,
3190                              FIB_SOURCE_API,
3191                              FIB_ENTRY_FLAG_NONE,
3192                              FIB_PROTOCOL_IP4,
3193                              &pfx_1_1_1_2_s_32.fp_addr,
3194                              ~0,
3195                              fib_index,
3196                              1,
3197                              NULL,
3198                              FIB_ROUTE_PATH_RESOLVE_VIA_HOST);
3199
3200     fei = fib_table_lookup_exact_match(fib_index, &bgp_200_pfx);
3201     dpo = fib_entry_contribute_ip_forwarding(fei);
3202     FIB_TEST(lb == load_balance_get(dpo->dpoi_index),
3203              "200.200.200.200/32 was inplace modified for 3rd path");
3204     FIB_TEST(16 == lb->lb_n_buckets,
3205              "200.200.200.200/32 was inplace modified for 3rd path to 16 buckets");
3206
3207     lbmi = lb->lb_map;
3208     load_balance_map_lock(lbmi);
3209     lbm = load_balance_map_get(lbmi);
3210
3211     for (ii = 0; ii < 16; ii++)
3212     {
3213         FIB_TEST(lbm->lbm_buckets[ii] == ii,
3214                  "LB Map for 200.200.200.200/32 at %d is %d",
3215                  ii, lbm->lbm_buckets[ii]);
3216     }
3217
3218     /*
3219      * trigger PIC by removing the first via-entry
3220      * the first 6 buckets of the map should map to the next 6
3221      */
3222     fib_table_entry_path_remove(fib_index,
3223                                 &pfx_1_1_1_1_s_32,
3224                                 FIB_SOURCE_API,
3225                                 FIB_PROTOCOL_IP4,
3226                                 &nh_10_10_10_1,
3227                                 tm->hw[0]->sw_if_index,
3228                                 ~0,
3229                                 1,
3230                                 FIB_ROUTE_PATH_FLAG_NONE);
3231
3232     fei = fib_table_lookup_exact_match(fib_index, &bgp_200_pfx);
3233     dpo = fib_entry_contribute_ip_forwarding(fei);
3234     FIB_TEST(lb == load_balance_get(dpo->dpoi_index),
3235              "200.200.200.200/32 was inplace modified for 3rd path");
3236     FIB_TEST(2 == lb->lb_n_buckets,
3237              "200.200.200.200/32 was inplace modified for 3rd path remove to 2 buckets");
3238
3239     for (ii = 0; ii < 6; ii++)
3240     {
3241         FIB_TEST(lbm->lbm_buckets[ii] == ii+6,
3242                  "LB Map for 200.200.200.200/32 at %d is %d",
3243                  ii, lbm->lbm_buckets[ii]);
3244     }
3245     for (ii = 6; ii < 16; ii++)
3246     {
3247         FIB_TEST(lbm->lbm_buckets[ii] == ii,
3248                  "LB Map for 200.200.200.200/32 at %d is %d",
3249                  ii, lbm->lbm_buckets[ii]);
3250     }
3251
3252
3253     /*
3254      * tidy up
3255      */
3256     fib_table_entry_path_add(fib_index,
3257                              &pfx_1_1_1_1_s_32,
3258                              FIB_SOURCE_API,
3259                              FIB_ENTRY_FLAG_NONE,
3260                              FIB_PROTOCOL_IP4,
3261                              &nh_10_10_10_1,
3262                              tm->hw[0]->sw_if_index,
3263                              ~0,
3264                              1,
3265                              NULL,
3266                              FIB_ROUTE_PATH_FLAG_NONE);
3267
3268     fib_table_entry_path_remove(fib_index,
3269                                 &bgp_200_pfx,
3270                                 FIB_SOURCE_API,
3271                                 FIB_PROTOCOL_IP4,
3272                                 &pfx_1_1_1_2_s_32.fp_addr,
3273                                 ~0,
3274                                 fib_index,
3275                                 1,
3276                                 MPLS_LABEL_INVALID);
3277     fib_table_entry_path_remove(fib_index,
3278                                 &bgp_200_pfx,
3279                                 FIB_SOURCE_API,
3280                                 FIB_PROTOCOL_IP4,
3281                                 &nh_1_1_1_1,
3282                                 ~0,
3283                                 fib_index,
3284                                 1,
3285                                 FIB_ROUTE_PATH_RESOLVE_VIA_HOST);
3286     fib_table_entry_path_remove(fib_index,
3287                                 &bgp_200_pfx,
3288                                 FIB_SOURCE_API,
3289                                 FIB_PROTOCOL_IP4,
3290                                 &pfx_1_1_1_3_s_32.fp_addr,
3291                                 ~0,
3292                                 fib_index,
3293                                 1,
3294                                 FIB_ROUTE_PATH_RESOLVE_VIA_HOST);
3295     fib_table_entry_delete(fib_index,
3296                            &pfx_1_1_1_3_s_32,
3297                            FIB_SOURCE_API);
3298     fib_table_entry_delete(fib_index,
3299                            &pfx_1_1_1_0_s_28,
3300                            FIB_SOURCE_API);
3301     FIB_TEST((FIB_NODE_INDEX_INVALID ==
3302               fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_0_s_28)),
3303              "1.1.1.1/28 removed");
3304     FIB_TEST((FIB_NODE_INDEX_INVALID ==
3305               fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_3_s_32)),
3306              "1.1.1.3/32 removed");
3307     FIB_TEST((FIB_NODE_INDEX_INVALID ==
3308               fib_table_lookup_exact_match(fib_index, &bgp_200_pfx)),
3309              "200.200.200.200/32 removed");
3310
3311     /*
3312      * add-remove test. no change.
3313      */
3314     FIB_TEST((1  == fib_path_list_db_size()),   "path list DB population:%d",
3315              fib_path_list_db_size());
3316     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
3317              fib_path_list_pool_size());
3318     FIB_TEST((ENBR+7 == fib_entry_pool_size()), "entry pool size is %d",
3319              fib_entry_pool_size());
3320
3321     /*
3322      * A route whose paths are built up iteratively and then removed
3323      * all at once
3324      */
3325     fib_prefix_t pfx_4_4_4_4_s_32 = {
3326         .fp_len = 32,
3327         .fp_proto = FIB_PROTOCOL_IP4,
3328         .fp_addr = {
3329             /* 4.4.4.4/32 */
3330             .ip4.as_u32 = clib_host_to_net_u32(0x04040404),
3331         },
3332     };
3333
3334     fib_table_entry_path_add(fib_index,
3335                              &pfx_4_4_4_4_s_32,
3336                              FIB_SOURCE_API,
3337                              FIB_ENTRY_FLAG_NONE,
3338                              FIB_PROTOCOL_IP4,
3339                              &nh_10_10_10_1,
3340                              tm->hw[0]->sw_if_index,
3341                              ~0,
3342                              1,
3343                              NULL,
3344                              FIB_ROUTE_PATH_FLAG_NONE);
3345     fib_table_entry_path_add(fib_index,
3346                              &pfx_4_4_4_4_s_32,
3347                              FIB_SOURCE_API,
3348                              FIB_ENTRY_FLAG_NONE,
3349                              FIB_PROTOCOL_IP4,
3350                              &nh_10_10_10_2,
3351                              tm->hw[0]->sw_if_index,
3352                              ~0,
3353                              1,
3354                              NULL,
3355                              FIB_ROUTE_PATH_FLAG_NONE);
3356     fib_table_entry_path_add(fib_index,
3357                              &pfx_4_4_4_4_s_32,
3358                              FIB_SOURCE_API,
3359                              FIB_ENTRY_FLAG_NONE,
3360                              FIB_PROTOCOL_IP4,
3361                              &nh_10_10_10_3,
3362                              tm->hw[0]->sw_if_index,
3363                              ~0,
3364                              1,
3365                              NULL,
3366                              FIB_ROUTE_PATH_FLAG_NONE);
3367     FIB_TEST(FIB_NODE_INDEX_INVALID !=
3368              fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32),
3369              "4.4.4.4/32 present");
3370
3371     fib_table_entry_delete(fib_index,
3372                            &pfx_4_4_4_4_s_32,
3373                            FIB_SOURCE_API);
3374     FIB_TEST(FIB_NODE_INDEX_INVALID ==
3375              fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32),
3376              "4.4.4.4/32 removed");
3377
3378     /*
3379      * add-remove test. no change.
3380      */
3381     FIB_TEST((1  == fib_path_list_db_size()),   "path list DB population:%d",
3382              fib_path_list_db_size());
3383     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
3384              fib_path_list_pool_size());
3385     FIB_TEST((ENBR+7 == fib_entry_pool_size()), "entry pool size is %d",
3386              fib_entry_pool_size());
3387
3388     /*
3389      * A route with multiple paths at once
3390      */
3391     fib_route_path_t *r_paths = NULL;
3392
3393     for (ii = 0; ii < 4; ii++)
3394     {
3395         fib_route_path_t r_path = {
3396             .frp_proto = FIB_PROTOCOL_IP4,
3397             .frp_addr = {
3398                 .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a02 + ii),
3399             },
3400             .frp_sw_if_index = tm->hw[0]->sw_if_index,
3401             .frp_weight = 1,
3402             .frp_fib_index = ~0,
3403         };
3404         vec_add1(r_paths, r_path);
3405     }
3406
3407     fib_table_entry_update(fib_index,
3408                            &pfx_4_4_4_4_s_32,
3409                            FIB_SOURCE_API,
3410                            FIB_ENTRY_FLAG_NONE,
3411                            r_paths);
3412
3413     fei = fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32);
3414     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "4.4.4.4/32 present");
3415     dpo = fib_entry_contribute_ip_forwarding(fei);
3416
3417     lb = load_balance_get(dpo->dpoi_index);
3418     FIB_TEST((lb->lb_n_buckets == 4), "4.4.4.4/32 lb over %d paths", lb->lb_n_buckets);
3419
3420     fib_table_entry_delete(fib_index,
3421                            &pfx_4_4_4_4_s_32,
3422                            FIB_SOURCE_API);
3423     FIB_TEST(FIB_NODE_INDEX_INVALID ==
3424              fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32),
3425              "4.4.4.4/32 removed");
3426     vec_free(r_paths);
3427
3428     /*
3429      * add-remove test. no change.
3430      */
3431     FIB_TEST((1  == fib_path_list_db_size()),   "path list DB population:%d",
3432              fib_path_list_db_size());
3433     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
3434              fib_path_list_pool_size());
3435     FIB_TEST((ENBR+7 == fib_entry_pool_size()), "entry pool size is %d",
3436              fib_entry_pool_size());
3437
3438     /*
3439      * A route deag route
3440      */
3441     fib_table_entry_path_add(fib_index,
3442                              &pfx_4_4_4_4_s_32,
3443                              FIB_SOURCE_API,
3444                              FIB_ENTRY_FLAG_NONE,
3445                              FIB_PROTOCOL_IP4,
3446                              &zero_addr,
3447                              ~0,
3448                              fib_index,
3449                              1,
3450                              NULL,
3451                              FIB_ROUTE_PATH_FLAG_NONE);
3452
3453     fei = fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32);
3454     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "4.4.4.4/32 present");
3455
3456     dpo = fib_entry_contribute_ip_forwarding(fei);
3457     dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
3458     lookup_dpo_t *lkd = lookup_dpo_get(dpo->dpoi_index);
3459
3460     FIB_TEST((fib_index == lkd->lkd_fib_index),
3461              "4.4.4.4/32 is deag in %d %U",
3462              lkd->lkd_fib_index,
3463              format_dpo_id, dpo, 0);
3464
3465     fib_table_entry_delete(fib_index,
3466                            &pfx_4_4_4_4_s_32,
3467                            FIB_SOURCE_API);
3468     FIB_TEST(FIB_NODE_INDEX_INVALID ==
3469              fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32),
3470              "4.4.4.4/32 removed");
3471     vec_free(r_paths);
3472
3473     /*
3474      * add-remove test. no change.
3475      */
3476     FIB_TEST((1  == fib_path_list_db_size()),   "path list DB population:%d",
3477              fib_path_list_db_size());
3478     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
3479              fib_path_list_pool_size());
3480     FIB_TEST((ENBR+7 == fib_entry_pool_size()), "entry pool size is %d",
3481              fib_entry_pool_size());
3482
3483     /*
3484      * Duplicate paths:
3485      *  add a recursive with duplicate paths. Expect the duplicate to be ignored.
3486      */
3487     fib_prefix_t pfx_34_1_1_1_s_32 = {
3488         .fp_len = 32,
3489         .fp_proto = FIB_PROTOCOL_IP4,
3490         .fp_addr = {
3491             .ip4.as_u32 = clib_host_to_net_u32(0x22010101),
3492         },
3493     };
3494     fib_prefix_t pfx_34_34_1_1_s_32 = {
3495         .fp_len = 32,
3496         .fp_proto = FIB_PROTOCOL_IP4,
3497         .fp_addr = {
3498             .ip4.as_u32 = clib_host_to_net_u32(0x22220101),
3499         },
3500     };
3501     fei = fib_table_entry_path_add(fib_index,
3502                                    &pfx_34_1_1_1_s_32,
3503                                    FIB_SOURCE_API,
3504                                    FIB_ENTRY_FLAG_NONE,
3505                                    FIB_PROTOCOL_IP4,
3506                                    &pfx_34_34_1_1_s_32.fp_addr,
3507                                    ~0,
3508                                    fib_index,
3509                                    1,
3510                                    NULL,
3511                                    FIB_ROUTE_PATH_FLAG_NONE);
3512     fei = fib_table_entry_path_add(fib_index,
3513                                    &pfx_34_1_1_1_s_32,
3514                                    FIB_SOURCE_API,
3515                                    FIB_ENTRY_FLAG_NONE,
3516                                    FIB_PROTOCOL_IP4,
3517                                    &pfx_34_34_1_1_s_32.fp_addr,
3518                                    ~0,
3519                                    fib_index,
3520                                    1,
3521                                    NULL,
3522                                    FIB_ROUTE_PATH_FLAG_NONE);
3523     FIB_TEST_REC_FORW(&pfx_34_1_1_1_s_32, &pfx_34_34_1_1_s_32, 0);
3524     fib_table_entry_delete_index(fei, FIB_SOURCE_API);
3525
3526     /*
3527      * CLEANUP
3528      *   remove: 1.1.1.2/32, 1.1.2.0/24 and 1.1.1.1/32
3529      *           all of which are via 10.10.10.1, Itf1
3530      */
3531     fib_table_entry_path_remove(fib_index,
3532                                 &pfx_1_1_1_2_s_32,
3533                                 FIB_SOURCE_API,
3534                                 FIB_PROTOCOL_IP4,
3535                                 &nh_10_10_10_1,
3536                                 tm->hw[0]->sw_if_index,
3537                                 ~0,
3538                                 1,
3539                                 FIB_ROUTE_PATH_FLAG_NONE);
3540     fib_table_entry_path_remove(fib_index,
3541                                 &pfx_1_1_1_1_s_32,
3542                                 FIB_SOURCE_API,
3543                                 FIB_PROTOCOL_IP4,
3544                                 &nh_10_10_10_1,
3545                                 tm->hw[0]->sw_if_index,
3546                                 ~0,
3547                                 1,
3548                                 FIB_ROUTE_PATH_FLAG_NONE);
3549     fib_table_entry_path_remove(fib_index,
3550                                 &pfx_1_1_2_0_s_24,
3551                                 FIB_SOURCE_API,
3552                                 FIB_PROTOCOL_IP4,
3553                                 &nh_10_10_10_1,
3554                                 tm->hw[0]->sw_if_index,
3555                                 ~0,
3556                                 1,
3557                                 FIB_ROUTE_PATH_FLAG_NONE);
3558
3559     FIB_TEST(FIB_NODE_INDEX_INVALID ==
3560              fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_1_s_32),
3561              "1.1.1.1/32 removed");
3562     FIB_TEST(FIB_NODE_INDEX_INVALID ==
3563              fib_table_lookup_exact_match(fib_index, &pfx_1_1_1_2_s_32),
3564              "1.1.1.2/32 removed");
3565     FIB_TEST(FIB_NODE_INDEX_INVALID ==
3566              fib_table_lookup_exact_match(fib_index, &pfx_1_1_2_0_s_24),
3567              "1.1.2.0/24 removed");
3568
3569     /*
3570      * -3 entries and -1 shared path-list
3571      */
3572     FIB_TEST((0  == fib_path_list_db_size()),   "path list DB population:%d",
3573              fib_path_list_db_size());
3574     FIB_TEST((PNBR+4 == fib_path_list_pool_size()), "path list pool size is %d",
3575              fib_path_list_pool_size());
3576     FIB_TEST((ENBR+4 == fib_entry_pool_size()), "entry pool size is %d",
3577              fib_entry_pool_size());
3578
3579     /*
3580      * An attached-host route. Expect to link to the incomplete adj
3581      */
3582     fib_prefix_t pfx_4_1_1_1_s_32 = {
3583         .fp_len = 32,
3584         .fp_proto = FIB_PROTOCOL_IP4,
3585         .fp_addr = {
3586             /* 4.1.1.1/32 */
3587             .ip4.as_u32 = clib_host_to_net_u32(0x04010101),
3588         },
3589     };
3590     fib_table_entry_path_add(fib_index,
3591                              &pfx_4_1_1_1_s_32,
3592                              FIB_SOURCE_API,
3593                              FIB_ENTRY_FLAG_NONE,
3594                              FIB_PROTOCOL_IP4,
3595                              &zero_addr,
3596                              tm->hw[0]->sw_if_index,
3597                              fib_index,
3598                              1,
3599                              NULL,
3600                              FIB_ROUTE_PATH_FLAG_NONE);
3601
3602     fei = fib_table_lookup_exact_match(fib_index, &pfx_4_1_1_1_s_32);
3603     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "4.1.1.1/32 present");
3604     ai = fib_entry_get_adj(fei);
3605
3606     ai2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
3607                               VNET_LINK_IP4,
3608                               &pfx_4_1_1_1_s_32.fp_addr,
3609                               tm->hw[0]->sw_if_index);
3610     FIB_TEST((ai == ai2), "Attached-host link to incomplete ADJ");
3611     adj_unlock(ai2);
3612
3613     /*
3614      * +1 entry and +1 shared path-list
3615      */
3616     FIB_TEST((1  == fib_path_list_db_size()),   "path list DB population:%d",
3617              fib_path_list_db_size());
3618     FIB_TEST((PNBR+5 == fib_path_list_pool_size()), "path list pool size is %d",
3619              fib_path_list_pool_size());
3620     FIB_TEST((ENBR+5 == fib_entry_pool_size()), "entry pool size is %d",
3621              fib_entry_pool_size());
3622
3623     fib_table_entry_delete(fib_index,
3624                            &pfx_4_1_1_1_s_32,
3625                            FIB_SOURCE_API);
3626
3627     FIB_TEST((0  == fib_path_list_db_size()),   "path list DB population:%d",
3628              fib_path_list_db_size());
3629     FIB_TEST((PNBR+4 == fib_path_list_pool_size()), "path list pool size is %d",
3630              fib_path_list_pool_size());
3631     FIB_TEST((ENBR+4 == fib_entry_pool_size()), "entry pool size is %d",
3632              fib_entry_pool_size());
3633
3634     /*
3635      * add a v6 prefix via v4 next-hops
3636      */
3637     fib_prefix_t pfx_2001_s_64 = {
3638         .fp_len = 64,
3639         .fp_proto = FIB_PROTOCOL_IP6,
3640         .fp_addr = {
3641             .ip6.as_u64[0] = clib_host_to_net_u64(0x2001000000000000),
3642         },
3643     };
3644     fei = fib_table_entry_path_add(0, //default v6 table
3645                                    &pfx_2001_s_64,
3646                                    FIB_SOURCE_API,
3647                                    FIB_ENTRY_FLAG_NONE,
3648                                    FIB_PROTOCOL_IP4,
3649                                    &nh_10_10_10_1,
3650                                    tm->hw[0]->sw_if_index,
3651                                    fib_index,
3652                                    1,
3653                                    NULL,
3654                                    FIB_ROUTE_PATH_FLAG_NONE);
3655
3656     fei = fib_table_lookup_exact_match(0, &pfx_2001_s_64);
3657     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "2001::/64 present");
3658     ai = fib_entry_get_adj(fei);
3659     adj = adj_get(ai);
3660     FIB_TEST((adj->lookup_next_index == IP_LOOKUP_NEXT_ARP),
3661              "2001::/64 via ARP-adj");
3662     FIB_TEST((adj->ia_link == VNET_LINK_IP6),
3663              "2001::/64 is link type v6");
3664     FIB_TEST((adj->ia_nh_proto == FIB_PROTOCOL_IP4),
3665              "2001::/64 ADJ-adj is NH proto v4");
3666     fib_table_entry_delete(0, &pfx_2001_s_64, FIB_SOURCE_API);
3667
3668     /*
3669      * add a uRPF exempt prefix:
3670      *  test:
3671      *   - it's forwarding is drop
3672      *   - it's uRPF list is not empty
3673      *   - the uRPF list for the default route (it's cover) is empty
3674      */
3675     fei = fib_table_entry_special_add(fib_index,
3676                                       &pfx_4_1_1_1_s_32,
3677                                       FIB_SOURCE_URPF_EXEMPT,
3678                                       FIB_ENTRY_FLAG_DROP,
3679                                       ADJ_INDEX_INVALID);
3680     dpo = fib_entry_contribute_ip_forwarding(fei);
3681     FIB_TEST(load_balance_is_drop(dpo),
3682              "uRPF exempt 4.1.1.1/32 DROP");
3683     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 1, 0),
3684              "uRPF list for exempt prefix has itf index 0");
3685     fei = fib_table_lookup_exact_match(fib_index, &pfx_0_0_0_0_s_0);
3686     FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 0),
3687              "uRPF list for 0.0.0.0/0 empty");
3688
3689     fib_table_entry_delete(fib_index, &pfx_4_1_1_1_s_32, FIB_SOURCE_URPF_EXEMPT);
3690
3691     /*
3692      * An adj-fib that fails the refinement criteria - no connected cover
3693      */
3694     fib_prefix_t pfx_12_10_10_2_s_32 = {
3695         .fp_len = 32,
3696         .fp_proto = FIB_PROTOCOL_IP4,
3697         .fp_addr = {
3698             /* 12.10.10.2 */
3699             .ip4.as_u32 = clib_host_to_net_u32(0x0c0a0a02),
3700         },
3701     };
3702
3703     fib_table_entry_update_one_path(fib_index,
3704                                     &pfx_12_10_10_2_s_32,
3705                                     FIB_SOURCE_ADJ,
3706                                     FIB_ENTRY_FLAG_ATTACHED,
3707                                     FIB_PROTOCOL_IP4,
3708                                     &pfx_12_10_10_2_s_32.fp_addr,
3709                                     tm->hw[0]->sw_if_index,
3710                                     ~0, // invalid fib index
3711                                     1,
3712                                     NULL,
3713                                     FIB_ROUTE_PATH_FLAG_NONE);
3714
3715     fei = fib_table_lookup_exact_match(fib_index, &pfx_12_10_10_2_s_32);
3716     dpo = fib_entry_contribute_ip_forwarding(fei);
3717     FIB_TEST(!dpo_id_is_valid(dpo),
3718              "no connected cover adj-fib fails refinement");
3719
3720     fib_table_entry_delete(fib_index,
3721                            &pfx_12_10_10_2_s_32,
3722                            FIB_SOURCE_ADJ);
3723
3724     /*
3725      * An adj-fib that fails the refinement criteria - cover is connected
3726      * but on a different interface
3727      */
3728     fib_prefix_t pfx_10_10_10_127_s_32 = {
3729         .fp_len = 32,
3730         .fp_proto = FIB_PROTOCOL_IP4,
3731         .fp_addr = {
3732             /* 10.10.10.127 */
3733             .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a7f),
3734         },
3735     };
3736
3737     fib_table_entry_update_one_path(fib_index,
3738                                     &pfx_10_10_10_127_s_32,
3739                                     FIB_SOURCE_ADJ,
3740                                     FIB_ENTRY_FLAG_ATTACHED,
3741                                     FIB_PROTOCOL_IP4,
3742                                     &pfx_10_10_10_127_s_32.fp_addr,
3743                                     tm->hw[1]->sw_if_index,
3744                                     ~0, // invalid fib index
3745                                     1,
3746                                     NULL,
3747                                     FIB_ROUTE_PATH_FLAG_NONE);
3748
3749     fei = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_127_s_32);
3750     dpo = fib_entry_contribute_ip_forwarding(fei);
3751     FIB_TEST(!dpo_id_is_valid(dpo),
3752              "wrong interface adj-fib fails refinement");
3753
3754     fib_table_entry_delete(fib_index,
3755                            &pfx_10_10_10_127_s_32,
3756                            FIB_SOURCE_ADJ);
3757
3758     /*
3759      * CLEANUP
3760      *    remove adj-fibs: 
3761      */
3762     fib_table_entry_delete(fib_index,
3763                            &pfx_10_10_10_1_s_32,
3764                            FIB_SOURCE_ADJ);
3765     fib_table_entry_delete(fib_index,
3766                            &pfx_10_10_10_2_s_32,
3767                            FIB_SOURCE_ADJ);
3768     FIB_TEST(FIB_NODE_INDEX_INVALID ==
3769              fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_1_s_32),
3770              "10.10.10.1/32 adj-fib removed");
3771     FIB_TEST(FIB_NODE_INDEX_INVALID ==
3772              fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_2_s_32),
3773              "10.10.10.2/32 adj-fib removed");
3774
3775     /*
3776      * -2 entries and -2 non-shared path-list
3777      */
3778     FIB_TEST((0  == fib_path_list_db_size()),   "path list DB population:%d",
3779              fib_path_list_db_size());
3780     FIB_TEST((PNBR+2 == fib_path_list_pool_size()), "path list pool size is %d",
3781              fib_path_list_pool_size());
3782     FIB_TEST((ENBR+2 == fib_entry_pool_size()), "entry pool size is %d",
3783              fib_entry_pool_size());
3784
3785     /*
3786      * unlock the adjacencies for which this test provided a rewrite.
3787      * These are the last locks on these adjs. they should thus go away.
3788      */
3789     adj_unlock(ai_02);
3790     adj_unlock(ai_01);
3791     adj_unlock(ai_12_12_12_12);
3792
3793     FIB_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d",
3794              adj_nbr_db_size());
3795
3796     /*
3797      * CLEANUP
3798      *   remove the interface prefixes
3799      */
3800     local_pfx.fp_len = 32;
3801     fib_table_entry_special_remove(fib_index, &local_pfx,
3802                                    FIB_SOURCE_INTERFACE);
3803     fei = fib_table_lookup(fib_index, &local_pfx);
3804
3805     FIB_TEST(FIB_NODE_INDEX_INVALID ==
3806              fib_table_lookup_exact_match(fib_index, &local_pfx),
3807              "10.10.10.10/32 adj-fib removed");
3808
3809     local_pfx.fp_len = 24;
3810     fib_table_entry_delete(fib_index, &local_pfx,
3811                            FIB_SOURCE_INTERFACE);
3812
3813     FIB_TEST(FIB_NODE_INDEX_INVALID ==
3814              fib_table_lookup_exact_match(fib_index, &local_pfx),
3815              "10.10.10.10/24 adj-fib removed");
3816
3817     /*
3818      * -2 entries and -2 non-shared path-list
3819      */
3820     FIB_TEST((0  == fib_path_list_db_size()),   "path list DB population:%d",
3821              fib_path_list_db_size());
3822     FIB_TEST((PNBR == fib_path_list_pool_size()), "path list pool size is %d",
3823              fib_path_list_pool_size());
3824     FIB_TEST((ENBR == fib_entry_pool_size()), "entry pool size is %d",
3825              fib_entry_pool_size());
3826
3827     /*
3828      * Last but not least, remove the VRF
3829      */
3830     FIB_TEST((0 == fib_table_get_num_entries(fib_index,
3831                                              FIB_PROTOCOL_IP4,
3832                                              FIB_SOURCE_API)),
3833              "NO API Source'd prefixes");
3834     FIB_TEST((0 == fib_table_get_num_entries(fib_index,
3835                                              FIB_PROTOCOL_IP4,
3836                                              FIB_SOURCE_RR)),
3837              "NO RR Source'd prefixes");
3838     FIB_TEST((0 == fib_table_get_num_entries(fib_index,
3839                                              FIB_PROTOCOL_IP4,
3840                                              FIB_SOURCE_INTERFACE)),
3841              "NO INterface Source'd prefixes");
3842
3843     fib_table_unlock(fib_index, FIB_PROTOCOL_IP4);
3844
3845     FIB_TEST((0  == fib_path_list_db_size()), "path list DB population:%d",
3846              fib_path_list_db_size());
3847     FIB_TEST((PNBR-5 == fib_path_list_pool_size()), "path list pool size is %d",
3848              fib_path_list_pool_size());
3849     FIB_TEST((ENBR-5 == fib_entry_pool_size()), "entry pool size is %d",
3850              fib_entry_pool_size());
3851     FIB_TEST((ENBR-5 == pool_elts(fib_urpf_list_pool)), "uRPF pool size is %d",
3852              pool_elts(fib_urpf_list_pool));
3853
3854     return 0;
3855 }
3856
3857 static int
3858 fib_test_v6 (void)
3859 {
3860     /*
3861      * In the default table check for the presence and correct forwarding
3862      * of the special entries
3863      */
3864     fib_node_index_t dfrt, fei, ai, locked_ai, ai_01, ai_02;
3865     const dpo_id_t *dpo, *dpo_drop;
3866     const ip_adjacency_t *adj;
3867     const receive_dpo_t *rd;
3868     test_main_t *tm;
3869     u32 fib_index;
3870     int ii;
3871
3872     FIB_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d",
3873              adj_nbr_db_size());
3874
3875     /* via 2001:0:0:1::2 */
3876     ip46_address_t nh_2001_2 = {
3877         .ip6 = {
3878             .as_u64 = {
3879                 [0] = clib_host_to_net_u64(0x2001000000000001),
3880                 [1] = clib_host_to_net_u64(0x0000000000000002),
3881             },
3882         },
3883     };
3884
3885     tm = &test_main;
3886
3887     dpo_drop = drop_dpo_get(DPO_PROTO_IP6);
3888
3889     /* Find or create FIB table 11 */
3890     fib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP6, 11);
3891
3892     for (ii = 0; ii < 4; ii++)
3893     {
3894         ip6_main.fib_index_by_sw_if_index[tm->hw[ii]->sw_if_index] = fib_index;
3895     }
3896
3897     fib_prefix_t pfx_0_0 = {
3898         .fp_len = 0,
3899         .fp_proto = FIB_PROTOCOL_IP6,
3900         .fp_addr = {
3901             .ip6 = {
3902                 {0, 0},
3903             },
3904         },
3905     };
3906
3907     dfrt = fib_table_lookup(fib_index, &pfx_0_0);
3908     FIB_TEST((FIB_NODE_INDEX_INVALID != dfrt), "default route present");
3909     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(dfrt)),
3910              "Default route is DROP");
3911
3912     dpo = fib_entry_contribute_ip_forwarding(dfrt);
3913     FIB_TEST((dpo->dpoi_index == ip6_fib_table_fwding_lookup(
3914                                      &ip6_main,
3915                                      1,
3916                                      &pfx_0_0.fp_addr.ip6)),
3917              "default-route; fwd and non-fwd tables match");
3918
3919     // FIXME - check specials.
3920
3921     /*
3922      * At this stage there is one v4 FIB with 5 routes and two v6 FIBs
3923      * each with 2 entries and a v6 mfib with 4 path-lists.
3924      * All entries are special so no path-list sharing.
3925      */
3926 #define ENPS (5+4)
3927 #define PNPS (5+4+4)
3928     FIB_TEST((0 == fib_path_list_db_size()),   "path list DB is empty");
3929     FIB_TEST((PNPS == fib_path_list_pool_size()), "path list pool size is %d",
3930              fib_path_list_pool_size());
3931     FIB_TEST((ENPS == fib_entry_pool_size()), "entry pool size is %d",
3932              fib_entry_pool_size());
3933
3934     /*
3935      * add interface routes.
3936      *  validate presence of /64 attached and /128 recieve.
3937      *  test for the presence of the receive address in the glean and local adj
3938      *
3939      * receive on 2001:0:0:1::1/128
3940      */
3941     fib_prefix_t local_pfx = {
3942         .fp_len = 64,
3943         .fp_proto = FIB_PROTOCOL_IP6,
3944         .fp_addr = {
3945             .ip6 = {
3946                 .as_u64 = {
3947                     [0] = clib_host_to_net_u64(0x2001000000000001),
3948                     [1] = clib_host_to_net_u64(0x0000000000000001),
3949                 },
3950             },
3951         }
3952     };
3953
3954     fib_table_entry_update_one_path(fib_index, &local_pfx,
3955                                     FIB_SOURCE_INTERFACE,
3956                                     (FIB_ENTRY_FLAG_CONNECTED |
3957                                      FIB_ENTRY_FLAG_ATTACHED),
3958                                     FIB_PROTOCOL_IP6,
3959                                     NULL,
3960                                     tm->hw[0]->sw_if_index,
3961                                     ~0,
3962                                     1,
3963                                     NULL,
3964                                     FIB_ROUTE_PATH_FLAG_NONE);
3965     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
3966
3967     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "attached interface route present");
3968
3969     ai = fib_entry_get_adj(fei);
3970     FIB_TEST((FIB_NODE_INDEX_INVALID != ai), "attached interface route adj present");
3971     adj = adj_get(ai);
3972     FIB_TEST((IP_LOOKUP_NEXT_GLEAN == adj->lookup_next_index),
3973              "attached interface adj is glean");
3974     FIB_TEST((0 == ip46_address_cmp(&local_pfx.fp_addr,
3975                                     &adj->sub_type.glean.receive_addr)),
3976               "attached interface adj is receive ok");
3977     dpo = fib_entry_contribute_ip_forwarding(fei);
3978     FIB_TEST((dpo->dpoi_index == ip6_fib_table_fwding_lookup(
3979                                      &ip6_main,
3980                                      1,
3981                                      &local_pfx.fp_addr.ip6)),
3982              "attached-route; fwd and non-fwd tables match");
3983
3984     local_pfx.fp_len = 128;
3985     fib_table_entry_update_one_path(fib_index, &local_pfx,
3986                                     FIB_SOURCE_INTERFACE,
3987                                     (FIB_ENTRY_FLAG_CONNECTED |
3988                                      FIB_ENTRY_FLAG_LOCAL),
3989                                     FIB_PROTOCOL_IP6,
3990                                     NULL,
3991                                     tm->hw[0]->sw_if_index,
3992                                     ~0, // invalid fib index
3993                                     1,
3994                                     NULL,
3995                                     FIB_ROUTE_PATH_FLAG_NONE);
3996     fei = fib_table_lookup(fib_index, &local_pfx);
3997
3998     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local interface route present");
3999
4000     dpo = fib_entry_contribute_ip_forwarding(fei);
4001     dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
4002     FIB_TEST((DPO_RECEIVE == dpo->dpoi_type),
4003              "local interface adj is local");
4004     rd = receive_dpo_get(dpo->dpoi_index);
4005
4006     FIB_TEST((0 == ip46_address_cmp(&local_pfx.fp_addr,
4007                                     &rd->rd_addr)),
4008               "local interface adj is receive ok");
4009
4010     dpo = fib_entry_contribute_ip_forwarding(fei);
4011     FIB_TEST((dpo->dpoi_index == ip6_fib_table_fwding_lookup(
4012                                      &ip6_main,
4013                                      1,
4014                                      &local_pfx.fp_addr.ip6)),
4015              "local-route; fwd and non-fwd tables match");
4016
4017     /*
4018      * +2 entries. +2 unshared path-lists
4019      */
4020     FIB_TEST((0 == fib_path_list_db_size()),   "path list DB is empty");
4021     FIB_TEST((PNPS+2 == fib_path_list_pool_size()), "path list pool size is%d",
4022              fib_path_list_pool_size());
4023     FIB_TEST((ENPS+2 == fib_entry_pool_size()), "entry pool size is %d",
4024              fib_entry_pool_size());
4025
4026     /*
4027      * Modify the default route to be via an adj not yet known.
4028      * this sources the defalut route with the API source, which is
4029      * a higher preference to the DEFAULT_ROUTE source
4030      */
4031     fib_table_entry_path_add(fib_index, &pfx_0_0,
4032                              FIB_SOURCE_API,
4033                              FIB_ENTRY_FLAG_NONE,
4034                              FIB_PROTOCOL_IP6,
4035                              &nh_2001_2,
4036                              tm->hw[0]->sw_if_index,
4037                              ~0,
4038                              1,
4039                              NULL,
4040                              FIB_ROUTE_PATH_FLAG_NONE);
4041     fei = fib_table_lookup(fib_index, &pfx_0_0);
4042
4043     FIB_TEST((fei == dfrt), "default route same index");
4044     ai = fib_entry_get_adj(fei);
4045     FIB_TEST((FIB_NODE_INDEX_INVALID != ai), "default route adj present");
4046     adj = adj_get(ai);
4047     FIB_TEST((IP_LOOKUP_NEXT_ARP == adj->lookup_next_index),
4048              "adj is incomplete");
4049     FIB_TEST((0 == ip46_address_cmp(&nh_2001_2, &adj->sub_type.nbr.next_hop)),
4050               "adj nbr next-hop ok");
4051
4052     /*
4053      * find the adj in the shared db
4054      */
4055     locked_ai = adj_nbr_add_or_lock(FIB_PROTOCOL_IP6,
4056                                     VNET_LINK_IP6,
4057                                     &nh_2001_2,
4058                                     tm->hw[0]->sw_if_index);
4059     FIB_TEST((locked_ai == ai), "ADJ NBR DB find");
4060     adj_unlock(locked_ai);
4061
4062     /*
4063      * no more entires. +1 shared path-list
4064      */
4065     FIB_TEST((1 == fib_path_list_db_size()),   "path list DB population:%d",
4066              fib_path_list_db_size());
4067     FIB_TEST((PNPS+3 == fib_path_list_pool_size()), "path list pool size is%d",
4068              fib_path_list_pool_size());
4069     FIB_TEST((ENPS+2 == fib_entry_pool_size()), "entry pool size is %d",
4070              fib_entry_pool_size());
4071
4072     /*
4073      * remove the API source from the default route. We expected
4074      * the route to remain, sourced by DEFAULT_ROUTE, and hence a DROP
4075      */
4076     fib_table_entry_path_remove(fib_index, &pfx_0_0,
4077                                 FIB_SOURCE_API, 
4078                                 FIB_PROTOCOL_IP6,
4079                                 &nh_2001_2,
4080                                 tm->hw[0]->sw_if_index,
4081                                 ~0,
4082                                 1,
4083                                 FIB_ROUTE_PATH_FLAG_NONE);
4084     fei = fib_table_lookup(fib_index, &pfx_0_0);
4085
4086     FIB_TEST((fei == dfrt), "default route same index");
4087     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(dfrt)),
4088              "Default route is DROP");
4089
4090     /*
4091      * no more entires. -1 shared path-list
4092      */
4093     FIB_TEST((0 == fib_path_list_db_size()),   "path list DB population:%d",
4094              fib_path_list_db_size());
4095     FIB_TEST((PNPS+2 == fib_path_list_pool_size()), "path list pool size is%d",
4096              fib_path_list_pool_size());
4097     FIB_TEST((ENPS+2 == fib_entry_pool_size()), "entry pool size is %d",
4098              fib_entry_pool_size());
4099
4100     /*
4101      * Add an 2 ARP entry => a complete ADJ plus adj-fib.
4102      */
4103     fib_prefix_t pfx_2001_1_2_s_128 = {
4104         .fp_len   = 128,
4105         .fp_proto = FIB_PROTOCOL_IP6,
4106         .fp_addr  = {
4107             .ip6 = {
4108                 .as_u64 = {
4109                     [0] = clib_host_to_net_u64(0x2001000000000001),
4110                     [1] = clib_host_to_net_u64(0x0000000000000002),
4111                 },
4112             },
4113         }
4114     };
4115     fib_prefix_t pfx_2001_1_3_s_128 = {
4116         .fp_len   = 128,
4117         .fp_proto = FIB_PROTOCOL_IP6,
4118         .fp_addr  = {
4119             .ip6 = {
4120                 .as_u64 = {
4121                     [0] = clib_host_to_net_u64(0x2001000000000001),
4122                     [1] = clib_host_to_net_u64(0x0000000000000003),
4123                 },
4124             },
4125         }
4126     };
4127     u8 eth_addr[] = {
4128         0xde, 0xde, 0xde, 0xba, 0xba, 0xba,
4129     };
4130
4131     ai_01 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP6,
4132                                 VNET_LINK_IP6,
4133                                 &pfx_2001_1_2_s_128.fp_addr,
4134                                 tm->hw[0]->sw_if_index);
4135     FIB_TEST((FIB_NODE_INDEX_INVALID != ai_01), "adj created");
4136     adj = adj_get(ai_01);
4137     FIB_TEST((IP_LOOKUP_NEXT_ARP == adj->lookup_next_index),
4138              "adj is incomplete");
4139     FIB_TEST((0 == ip46_address_cmp(&pfx_2001_1_2_s_128.fp_addr,
4140                                     &adj->sub_type.nbr.next_hop)),
4141               "adj nbr next-hop ok");
4142
4143     adj_nbr_update_rewrite(ai_01, ADJ_NBR_REWRITE_FLAG_COMPLETE,
4144                            fib_test_build_rewrite(eth_addr));
4145     FIB_TEST((IP_LOOKUP_NEXT_REWRITE == adj->lookup_next_index),
4146              "adj is complete");
4147     FIB_TEST((0 == ip46_address_cmp(&pfx_2001_1_2_s_128.fp_addr,
4148                                     &adj->sub_type.nbr.next_hop)),
4149               "adj nbr next-hop ok");
4150
4151     fib_table_entry_update_one_path(fib_index,
4152                                     &pfx_2001_1_2_s_128,
4153                                     FIB_SOURCE_ADJ,
4154                                     FIB_ENTRY_FLAG_ATTACHED,
4155                                     FIB_PROTOCOL_IP6,
4156                                     &pfx_2001_1_2_s_128.fp_addr,
4157                                     tm->hw[0]->sw_if_index,
4158                                     ~0,
4159                                     1,
4160                                     NULL,
4161                                     FIB_ROUTE_PATH_FLAG_NONE);
4162
4163     fei = fib_table_lookup(fib_index, &pfx_2001_1_2_s_128);
4164     ai = fib_entry_get_adj(fei);
4165     FIB_TEST((ai_01 == ai), "ADJ-FIB resolves via adj");
4166
4167     eth_addr[5] = 0xb2;
4168
4169     ai_02 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP6,
4170                                 VNET_LINK_IP6,
4171                                 &pfx_2001_1_3_s_128.fp_addr,
4172                                 tm->hw[0]->sw_if_index);
4173     FIB_TEST((FIB_NODE_INDEX_INVALID != ai_02), "adj created");
4174     adj = adj_get(ai_02);
4175     FIB_TEST((IP_LOOKUP_NEXT_ARP == adj->lookup_next_index),
4176              "adj is incomplete");
4177     FIB_TEST((0 == ip46_address_cmp(&pfx_2001_1_3_s_128.fp_addr,
4178                                     &adj->sub_type.nbr.next_hop)),
4179               "adj nbr next-hop ok");
4180
4181     adj_nbr_update_rewrite(ai_02, ADJ_NBR_REWRITE_FLAG_COMPLETE,
4182                            fib_test_build_rewrite(eth_addr));
4183     FIB_TEST((IP_LOOKUP_NEXT_REWRITE == adj->lookup_next_index),
4184              "adj is complete");
4185     FIB_TEST((0 == ip46_address_cmp(&pfx_2001_1_3_s_128.fp_addr,
4186                                     &adj->sub_type.nbr.next_hop)),
4187               "adj nbr next-hop ok");
4188     FIB_TEST((ai_01 != ai_02), "ADJs are different");
4189
4190     fib_table_entry_update_one_path(fib_index,
4191                                     &pfx_2001_1_3_s_128,
4192                                     FIB_SOURCE_ADJ,
4193                                     FIB_ENTRY_FLAG_ATTACHED,
4194                                     FIB_PROTOCOL_IP6,
4195                                     &pfx_2001_1_3_s_128.fp_addr,
4196                                     tm->hw[0]->sw_if_index,
4197                                     ~0,
4198                                     1,
4199                                     NULL,
4200                                     FIB_ROUTE_PATH_FLAG_NONE);
4201
4202     fei = fib_table_lookup(fib_index, &pfx_2001_1_3_s_128);
4203     ai = fib_entry_get_adj(fei);
4204     FIB_TEST((ai_02 == ai), "ADJ-FIB resolves via adj");
4205
4206     /*
4207      * +2 entries, +2 unshread path-lists.
4208      */
4209     FIB_TEST((0 == fib_path_list_db_size()),   "path list DB population:%d",
4210              fib_path_list_db_size());
4211     FIB_TEST((PNPS+4 == fib_path_list_pool_size()), "path list pool size is%d",
4212              fib_path_list_pool_size());
4213     FIB_TEST((ENPS+4 == fib_entry_pool_size()), "entry pool size is %d",
4214              fib_entry_pool_size());
4215
4216     /*
4217      * Add a 2 routes via the first ADJ. ensure path-list sharing
4218      */
4219     fib_prefix_t pfx_2001_a_s_64 = {
4220         .fp_len   = 64,
4221         .fp_proto = FIB_PROTOCOL_IP6,
4222         .fp_addr  = {
4223             .ip6 = {
4224                 .as_u64 = {
4225                     [0] = clib_host_to_net_u64(0x200100000000000a),
4226                     [1] = clib_host_to_net_u64(0x0000000000000000),
4227                 },
4228             },
4229         }
4230     };
4231     fib_prefix_t pfx_2001_b_s_64 = {
4232         .fp_len   = 64,
4233         .fp_proto = FIB_PROTOCOL_IP6,
4234         .fp_addr  = {
4235             .ip6 = {
4236                 .as_u64 = {
4237                     [0] = clib_host_to_net_u64(0x200100000000000b),
4238                     [1] = clib_host_to_net_u64(0x0000000000000000),
4239                 },
4240             },
4241         }
4242     };
4243
4244     fib_table_entry_path_add(fib_index,
4245                              &pfx_2001_a_s_64,
4246                              FIB_SOURCE_API,
4247                              FIB_ENTRY_FLAG_NONE,
4248                              FIB_PROTOCOL_IP6,
4249                              &nh_2001_2,
4250                              tm->hw[0]->sw_if_index,
4251                              ~0,
4252                              1,
4253                              NULL,
4254                              FIB_ROUTE_PATH_FLAG_NONE);
4255     fei = fib_table_lookup(fib_index, &pfx_2001_a_s_64);
4256     ai = fib_entry_get_adj(fei);
4257     FIB_TEST((ai_01 == ai), "2001::a/64 resolves via 2001:0:0:1::1");
4258     fib_table_entry_path_add(fib_index,
4259                              &pfx_2001_b_s_64,
4260                              FIB_SOURCE_API,
4261                              FIB_ENTRY_FLAG_NONE,
4262                              FIB_PROTOCOL_IP6,
4263                              &nh_2001_2,
4264                              tm->hw[0]->sw_if_index,
4265                              ~0,
4266                              1,
4267                              NULL,
4268                              FIB_ROUTE_PATH_FLAG_NONE);
4269     fei = fib_table_lookup(fib_index, &pfx_2001_b_s_64);
4270     ai = fib_entry_get_adj(fei);
4271     FIB_TEST((ai_01 == ai), "2001::b/64 resolves via 2001:0:0:1::1");
4272
4273     /*
4274      * +2 entries, +1 shared path-list.
4275      */
4276     FIB_TEST((1 == fib_path_list_db_size()),   "path list DB population:%d",
4277              fib_path_list_db_size());
4278     FIB_TEST((PNPS+5 == fib_path_list_pool_size()), "path list pool size is%d",
4279              fib_path_list_pool_size());
4280     FIB_TEST((ENPS+6 == fib_entry_pool_size()), "entry pool size is %d",
4281              fib_entry_pool_size());
4282
4283     /*
4284      * add a v4 prefix via a v6 next-hop
4285      */
4286     fib_prefix_t pfx_1_1_1_1_s_32 = {
4287         .fp_len = 32,
4288         .fp_proto = FIB_PROTOCOL_IP4,
4289         .fp_addr = {
4290             .ip4.as_u32 = 0x01010101,
4291         },
4292     };
4293     fei = fib_table_entry_path_add(0, // default table
4294                                    &pfx_1_1_1_1_s_32,
4295                                    FIB_SOURCE_API,
4296                                    FIB_ENTRY_FLAG_NONE,
4297                                    FIB_PROTOCOL_IP6,
4298                                    &nh_2001_2,
4299                                    tm->hw[0]->sw_if_index,
4300                                    ~0,
4301                                    1,
4302                                    NULL,
4303                                    FIB_ROUTE_PATH_FLAG_NONE);
4304     FIB_TEST(fei == fib_table_lookup_exact_match(0, &pfx_1_1_1_1_s_32),
4305              "1.1.1.1/32 o v6 route present");
4306     ai = fib_entry_get_adj(fei);
4307     adj = adj_get(ai);
4308     FIB_TEST((adj->lookup_next_index == IP_LOOKUP_NEXT_ARP),
4309              "1.1.1.1/32 via ARP-adj");
4310     FIB_TEST((adj->ia_link == VNET_LINK_IP4),
4311              "1.1.1.1/32 ADJ-adj is link type v4");
4312     FIB_TEST((adj->ia_nh_proto == FIB_PROTOCOL_IP6),
4313              "1.1.1.1/32 ADJ-adj is NH proto v6");
4314     fib_table_entry_delete(0, &pfx_1_1_1_1_s_32, FIB_SOURCE_API);
4315
4316     /*
4317      * An attached route
4318      */
4319     fib_prefix_t pfx_2001_c_s_64 = {
4320         .fp_len   = 64,
4321         .fp_proto = FIB_PROTOCOL_IP6,
4322         .fp_addr  = {
4323             .ip6 = {
4324                 .as_u64 = {
4325                     [0] = clib_host_to_net_u64(0x200100000000000c),
4326                     [1] = clib_host_to_net_u64(0x0000000000000000),
4327                 },
4328             },
4329         }
4330     };
4331     fib_table_entry_path_add(fib_index,
4332                              &pfx_2001_c_s_64,
4333                              FIB_SOURCE_CLI,
4334                              FIB_ENTRY_FLAG_ATTACHED,
4335                              FIB_PROTOCOL_IP6,
4336                              NULL,
4337                              tm->hw[0]->sw_if_index,
4338                              ~0,
4339                              1,
4340                              NULL,
4341                              FIB_ROUTE_PATH_FLAG_NONE);
4342     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_c_s_64);
4343     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "attached route present");
4344     ai = fib_entry_get_adj(fei);
4345     adj = adj_get(ai);
4346     FIB_TEST((adj->lookup_next_index == IP_LOOKUP_NEXT_GLEAN),
4347              "2001:0:0:c/64 attached resolves via glean");
4348
4349     fib_table_entry_path_remove(fib_index,
4350                                 &pfx_2001_c_s_64,
4351                                 FIB_SOURCE_CLI,
4352                                 FIB_PROTOCOL_IP6,
4353                                 NULL,
4354                                 tm->hw[0]->sw_if_index,
4355                                 ~0,
4356                                 1,
4357                                 FIB_ROUTE_PATH_FLAG_NONE);
4358     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_c_s_64);
4359     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "attached route removed");
4360
4361     /*
4362      * Shutdown the interface on which we have a connected and through
4363      * which the routes are reachable.
4364      * This will result in the connected, adj-fibs, and routes linking to drop
4365      * The local/for-us prefix continues to receive.
4366      */
4367     clib_error_t * error;
4368
4369     error = vnet_sw_interface_set_flags(vnet_get_main(),
4370                                         tm->hw[0]->sw_if_index,
4371                                         ~VNET_SW_INTERFACE_FLAG_ADMIN_UP);
4372     FIB_TEST((NULL == error), "Interface shutdown OK");
4373
4374     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_b_s_64);
4375     dpo = fib_entry_contribute_ip_forwarding(fei);
4376     FIB_TEST(!dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4377              "2001::b/64 resolves via drop");
4378
4379     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_a_s_64);
4380     dpo = fib_entry_contribute_ip_forwarding(fei);
4381     FIB_TEST(!dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4382              "2001::a/64 resolves via drop");
4383     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_3_s_128);
4384     dpo = fib_entry_contribute_ip_forwarding(fei);
4385     FIB_TEST(!dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4386              "2001:0:0:1::3/64 resolves via drop");
4387     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_2_s_128);
4388     dpo = fib_entry_contribute_ip_forwarding(fei);
4389     FIB_TEST(!dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4390              "2001:0:0:1::2/64 resolves via drop");
4391     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4392     dpo = fib_entry_contribute_ip_forwarding(fei);
4393     FIB_TEST(dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4394              "2001:0:0:1::1/128 not drop");
4395     local_pfx.fp_len = 64;
4396     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4397     dpo = fib_entry_contribute_ip_forwarding(fei);
4398     FIB_TEST(!dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4399              "2001:0:0:1/64 resolves via drop");
4400
4401     /*
4402      * no change
4403      */
4404     FIB_TEST((1 == fib_path_list_db_size()),   "path list DB population:%d",
4405              fib_path_list_db_size());
4406     FIB_TEST((PNPS+5 == fib_path_list_pool_size()), "path list pool size is%d",
4407              fib_path_list_pool_size());
4408     FIB_TEST((ENPS+6 == fib_entry_pool_size()), "entry pool size is %d",
4409              fib_entry_pool_size());
4410
4411     /*
4412      * shutdown one of the other interfaces, then add a connected.
4413      * and swap one of the routes to it.
4414      */
4415     error = vnet_sw_interface_set_flags(vnet_get_main(),
4416                                         tm->hw[1]->sw_if_index,
4417                                         ~VNET_SW_INTERFACE_FLAG_ADMIN_UP);
4418     FIB_TEST((NULL == error), "Interface 1 shutdown OK");
4419
4420     fib_prefix_t connected_pfx = {
4421         .fp_len = 64,
4422         .fp_proto = FIB_PROTOCOL_IP6,
4423         .fp_addr = {
4424             .ip6 = {
4425                 /* 2001:0:0:2::1/64 */
4426                 .as_u64 = {
4427                     [0] = clib_host_to_net_u64(0x2001000000000002),
4428                     [1] = clib_host_to_net_u64(0x0000000000000001),
4429                 },
4430             },
4431         }
4432     };
4433     fib_table_entry_update_one_path(fib_index, &connected_pfx,
4434                                     FIB_SOURCE_INTERFACE,
4435                                     (FIB_ENTRY_FLAG_CONNECTED |
4436                                      FIB_ENTRY_FLAG_ATTACHED),
4437                                     FIB_PROTOCOL_IP6,
4438                                     NULL,
4439                                     tm->hw[1]->sw_if_index,
4440                                     ~0,
4441                                     1,
4442                                     NULL,
4443                                     FIB_ROUTE_PATH_FLAG_NONE);
4444     fei = fib_table_lookup_exact_match(fib_index, &connected_pfx);
4445     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "attached interface route present");
4446     dpo = fib_entry_contribute_ip_forwarding(fei);
4447     dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
4448     FIB_TEST(!dpo_cmp(dpo, dpo_drop),
4449              "2001:0:0:2/64 not resolves via drop");
4450
4451     connected_pfx.fp_len = 128;
4452     fib_table_entry_update_one_path(fib_index, &connected_pfx,
4453                                     FIB_SOURCE_INTERFACE,
4454                                     (FIB_ENTRY_FLAG_CONNECTED |
4455                                      FIB_ENTRY_FLAG_LOCAL),
4456                                     FIB_PROTOCOL_IP6,
4457                                     NULL,
4458                                     tm->hw[0]->sw_if_index,
4459                                     ~0, // invalid fib index
4460                                     1,
4461                                     NULL,
4462                                     FIB_ROUTE_PATH_FLAG_NONE);
4463     fei = fib_table_lookup(fib_index, &connected_pfx);
4464
4465     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local interface route present");
4466     dpo = fib_entry_contribute_ip_forwarding(fei);
4467     dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
4468     FIB_TEST((DPO_RECEIVE == dpo->dpoi_type),
4469              "local interface adj is local");
4470     rd = receive_dpo_get(dpo->dpoi_index);
4471     FIB_TEST((0 == ip46_address_cmp(&connected_pfx.fp_addr,
4472                                     &rd->rd_addr)),
4473               "local interface adj is receive ok");
4474
4475     /*
4476      * +2 entries, +2 unshared path-lists
4477      */
4478     FIB_TEST((1 == fib_path_list_db_size()),   "path list DB population:%d",
4479              fib_path_list_db_size());
4480     FIB_TEST((PNPS+7 == fib_path_list_pool_size()), "path list pool size is%d",
4481              fib_path_list_pool_size());
4482     FIB_TEST((ENPS+8 == fib_entry_pool_size()), "entry pool size is %d",
4483              fib_entry_pool_size());
4484
4485
4486     /*
4487      * bring the interface back up. we expected the routes to return
4488      * to normal forwarding.
4489      */
4490     error = vnet_sw_interface_set_flags(vnet_get_main(),
4491                                         tm->hw[0]->sw_if_index,
4492                                         VNET_SW_INTERFACE_FLAG_ADMIN_UP);
4493     FIB_TEST((NULL == error), "Interface bring-up OK");
4494     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_a_s_64);
4495     ai = fib_entry_get_adj(fei);
4496     FIB_TEST((ai_01 == ai), "2001::a/64 resolves via 2001:0:0:1::1");
4497     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_b_s_64);
4498     ai = fib_entry_get_adj(fei);
4499     FIB_TEST((ai_01 == ai), "2001::b/64 resolves via 2001:0:0:1::1");
4500     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_3_s_128);
4501     ai = fib_entry_get_adj(fei);
4502     FIB_TEST((ai_02 == ai), "ADJ-FIB resolves via adj");
4503     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_2_s_128);
4504     ai = fib_entry_get_adj(fei);
4505     FIB_TEST((ai_01 == ai), "ADJ-FIB resolves via adj");
4506     local_pfx.fp_len = 64;
4507     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4508     ai = fib_entry_get_adj(fei);
4509     adj = adj_get(ai);
4510     FIB_TEST((IP_LOOKUP_NEXT_GLEAN == adj->lookup_next_index),
4511              "attached interface adj is glean");
4512
4513     /*
4514      * Same test as above, but this time the HW interface goes down
4515      */
4516     error = vnet_hw_interface_set_flags(vnet_get_main(),
4517                                         tm->hw_if_indicies[0],
4518                                         ~VNET_HW_INTERFACE_FLAG_LINK_UP);
4519     FIB_TEST((NULL == error), "Interface shutdown OK");
4520
4521     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_b_s_64);
4522     dpo = fib_entry_contribute_ip_forwarding(fei);
4523     FIB_TEST(!dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4524              "2001::b/64 resolves via drop");
4525     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_a_s_64);
4526     dpo = fib_entry_contribute_ip_forwarding(fei);
4527     FIB_TEST(!dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4528              "2001::a/64 resolves via drop");
4529     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_3_s_128);
4530     dpo = fib_entry_contribute_ip_forwarding(fei);
4531     FIB_TEST(!dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4532              "2001:0:0:1::3/128 resolves via drop");
4533     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_2_s_128);
4534     dpo = fib_entry_contribute_ip_forwarding(fei);
4535     FIB_TEST(!dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4536              "2001:0:0:1::2/128 resolves via drop");
4537     local_pfx.fp_len = 128;
4538     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4539     dpo = fib_entry_contribute_ip_forwarding(fei);
4540     FIB_TEST(dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4541              "2001:0:0:1::1/128 not drop");
4542     local_pfx.fp_len = 64;
4543     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4544     dpo = fib_entry_contribute_ip_forwarding(fei);
4545     FIB_TEST(!dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
4546              "2001:0:0:1/64 resolves via drop");
4547
4548     error = vnet_hw_interface_set_flags(vnet_get_main(),
4549                                         tm->hw_if_indicies[0],
4550                                         VNET_HW_INTERFACE_FLAG_LINK_UP);
4551     FIB_TEST((NULL == error), "Interface bring-up OK");
4552     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_a_s_64);
4553     ai = fib_entry_get_adj(fei);
4554     FIB_TEST((ai_01 == ai), "2001::a/64 resolves via 2001:0:0:1::1");
4555     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_b_s_64);
4556     ai = fib_entry_get_adj(fei);
4557     FIB_TEST((ai_01 == ai), "2001::b/64 resolves via 2001:0:0:1::1");
4558     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_3_s_128);
4559     ai = fib_entry_get_adj(fei);
4560     FIB_TEST((ai_02 == ai), "ADJ-FIB resolves via adj");
4561     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_2_s_128);
4562     ai = fib_entry_get_adj(fei);
4563     FIB_TEST((ai_01 == ai), "ADJ-FIB resolves via adj");
4564     local_pfx.fp_len = 64;
4565     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4566     ai = fib_entry_get_adj(fei);
4567     adj = adj_get(ai);
4568     FIB_TEST((IP_LOOKUP_NEXT_GLEAN == adj->lookup_next_index),
4569              "attached interface adj is glean");
4570
4571     /*
4572      * Delete the interface that the routes reolve through.
4573      * Again no routes are removed. They all point to drop.
4574      *
4575      * This is considered an error case. The control plane should
4576      * not remove interfaces through which routes resolve, but
4577      * such things can happen. ALL affected routes will drop.
4578      */
4579     vnet_delete_hw_interface(vnet_get_main(), tm->hw_if_indicies[0]);
4580
4581     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_b_s_64);
4582     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4583              "2001::b/64 resolves via drop");
4584     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_a_s_64);
4585     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4586              "2001::b/64 resolves via drop");
4587     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_3_s_128);
4588     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4589              "2001:0:0:1::3/64 resolves via drop");
4590     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_2_s_128);
4591     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4592              "2001:0:0:1::2/64 resolves via drop");
4593     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4594     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4595              "2001:0:0:1::1/128 is drop");
4596     local_pfx.fp_len = 64;
4597     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4598     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4599              "2001:0:0:1/64 resolves via drop");
4600
4601     /*
4602      * no change
4603      */
4604     FIB_TEST((1 == fib_path_list_db_size()),   "path list DB population:%d",
4605              fib_path_list_db_size());
4606     FIB_TEST((PNPS+7 == fib_path_list_pool_size()), "path list pool size is%d",
4607              fib_path_list_pool_size());
4608     FIB_TEST((ENPS+8 == fib_entry_pool_size()), "entry pool size is %d",
4609              fib_entry_pool_size());
4610
4611     /*
4612      * Add the interface back. routes stay unresolved.
4613      */
4614     error = ethernet_register_interface(vnet_get_main(),
4615                                         test_interface_device_class.index,
4616                                         0 /* instance */,
4617                                         hw_address,
4618                                         &tm->hw_if_indicies[0],
4619                                         /* flag change */ 0);
4620
4621     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_b_s_64);
4622     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4623              "2001::b/64 resolves via drop");
4624     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_a_s_64);
4625     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4626              "2001::b/64 resolves via drop");
4627     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_3_s_128);
4628     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4629              "2001:0:0:1::3/64 resolves via drop");
4630     fei = fib_table_lookup_exact_match(fib_index, &pfx_2001_1_2_s_128);
4631     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4632              "2001:0:0:1::2/64 resolves via drop");
4633     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4634     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4635              "2001:0:0:1::1/128 is drop");
4636     local_pfx.fp_len = 64;
4637     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4638     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
4639              "2001:0:0:1/64 resolves via drop");
4640
4641     /*
4642      * CLEANUP ALL the routes
4643      */
4644     fib_table_entry_delete(fib_index,
4645                            &pfx_2001_c_s_64,
4646                            FIB_SOURCE_API);
4647     fib_table_entry_delete(fib_index,
4648                            &pfx_2001_a_s_64,
4649                            FIB_SOURCE_API);
4650     fib_table_entry_delete(fib_index,
4651                            &pfx_2001_b_s_64,
4652                            FIB_SOURCE_API);
4653     fib_table_entry_delete(fib_index,
4654                            &pfx_2001_1_3_s_128,
4655                            FIB_SOURCE_ADJ);
4656     fib_table_entry_delete(fib_index,
4657                            &pfx_2001_1_2_s_128,
4658                            FIB_SOURCE_ADJ);
4659     local_pfx.fp_len = 64;
4660     fib_table_entry_delete(fib_index, &local_pfx,
4661                            FIB_SOURCE_INTERFACE);
4662     local_pfx.fp_len = 128;
4663     fib_table_entry_special_remove(fib_index, &local_pfx,
4664                                    FIB_SOURCE_INTERFACE);
4665     connected_pfx.fp_len = 64;
4666     fib_table_entry_delete(fib_index, &connected_pfx,
4667                            FIB_SOURCE_INTERFACE);
4668     connected_pfx.fp_len = 128;
4669     fib_table_entry_special_remove(fib_index, &connected_pfx,
4670                                    FIB_SOURCE_INTERFACE);
4671
4672     FIB_TEST((FIB_NODE_INDEX_INVALID ==
4673               fib_table_lookup_exact_match(fib_index, &pfx_2001_a_s_64)),
4674              "2001::a/64 removed");
4675     FIB_TEST((FIB_NODE_INDEX_INVALID ==
4676               fib_table_lookup_exact_match(fib_index, &pfx_2001_b_s_64)),
4677              "2001::b/64 removed");
4678     FIB_TEST((FIB_NODE_INDEX_INVALID ==
4679               fib_table_lookup_exact_match(fib_index, &pfx_2001_1_3_s_128)),
4680              "2001:0:0:1::3/128 removed");
4681     FIB_TEST((FIB_NODE_INDEX_INVALID ==
4682               fib_table_lookup_exact_match(fib_index, &pfx_2001_1_2_s_128)),
4683              "2001:0:0:1::3/128 removed");
4684     local_pfx.fp_len = 64;
4685     FIB_TEST((FIB_NODE_INDEX_INVALID ==
4686               fib_table_lookup_exact_match(fib_index, &local_pfx)),
4687              "2001:0:0:1/64 removed");
4688     local_pfx.fp_len = 128;
4689     FIB_TEST((FIB_NODE_INDEX_INVALID ==
4690               fib_table_lookup_exact_match(fib_index, &local_pfx)),
4691              "2001:0:0:1::1/128 removed");
4692     connected_pfx.fp_len = 64;
4693     FIB_TEST((FIB_NODE_INDEX_INVALID ==
4694               fib_table_lookup_exact_match(fib_index, &connected_pfx)),
4695              "2001:0:0:2/64 removed");
4696     connected_pfx.fp_len = 128;
4697     FIB_TEST((FIB_NODE_INDEX_INVALID ==
4698               fib_table_lookup_exact_match(fib_index, &connected_pfx)),
4699              "2001:0:0:2::1/128 removed");
4700
4701     /*
4702      * -8 entries. -7 path-lists (1 was shared).
4703      */
4704     FIB_TEST((0 == fib_path_list_db_size()),   "path list DB population:%d",
4705              fib_path_list_db_size());
4706     FIB_TEST((PNPS == fib_path_list_pool_size()), "path list pool size is%d",
4707              fib_path_list_pool_size());
4708     FIB_TEST((ENPS == fib_entry_pool_size()), "entry pool size is %d",
4709              fib_entry_pool_size());
4710
4711     /*
4712      * now remove the VRF
4713      */
4714     fib_table_unlock(fib_index, FIB_PROTOCOL_IP6);
4715
4716     FIB_TEST((0 == fib_path_list_db_size()),   "path list DB population:%d",
4717              fib_path_list_db_size());
4718     FIB_TEST((PNPS-2 == fib_path_list_pool_size()), "path list pool size is%d",
4719              fib_path_list_pool_size());
4720     FIB_TEST((ENPS-2 == fib_entry_pool_size()), "entry pool size is %d",
4721              fib_entry_pool_size());
4722
4723     adj_unlock(ai_02);
4724     adj_unlock(ai_01);
4725
4726     /*
4727      * return the interfaces to up state
4728      */
4729     error = vnet_sw_interface_set_flags(vnet_get_main(),
4730                                         tm->hw[0]->sw_if_index,
4731                                         VNET_SW_INTERFACE_FLAG_ADMIN_UP);
4732     error = vnet_sw_interface_set_flags(vnet_get_main(),
4733                                         tm->hw[1]->sw_if_index,
4734                                         VNET_SW_INTERFACE_FLAG_ADMIN_UP);
4735
4736     FIB_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d",
4737              adj_nbr_db_size());
4738
4739     return (0);
4740 }
4741
4742 /*
4743  * Test Attached Exports
4744  */
4745 static int
4746 fib_test_ae (void)
4747 {
4748     const dpo_id_t *dpo, *dpo_drop;
4749     const u32 fib_index = 0;
4750     fib_node_index_t fei;
4751     test_main_t *tm;
4752     ip4_main_t *im;
4753
4754     tm = &test_main;
4755     im = &ip4_main;
4756
4757     FIB_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d",
4758              adj_nbr_db_size());
4759
4760     /*
4761      * add interface routes. We'll assume this works. It's more rigorously
4762      * tested elsewhere.
4763      */
4764     fib_prefix_t local_pfx = {
4765         .fp_len = 24,
4766         .fp_proto = FIB_PROTOCOL_IP4,
4767         .fp_addr = {
4768             .ip4 = {
4769                 /* 10.10.10.10 */
4770                 .as_u32 = clib_host_to_net_u32(0x0a0a0a0a),
4771             },
4772         },
4773     };
4774
4775     vec_validate(im->fib_index_by_sw_if_index, tm->hw[0]->sw_if_index);
4776     im->fib_index_by_sw_if_index[tm->hw[0]->sw_if_index] = fib_index;
4777
4778     dpo_drop = drop_dpo_get(DPO_PROTO_IP4);
4779
4780     fib_table_entry_update_one_path(fib_index, &local_pfx,
4781                                     FIB_SOURCE_INTERFACE,
4782                                     (FIB_ENTRY_FLAG_CONNECTED |
4783                                      FIB_ENTRY_FLAG_ATTACHED),
4784                                     FIB_PROTOCOL_IP4,
4785                                     NULL,
4786                                     tm->hw[0]->sw_if_index,
4787                                     ~0,
4788                                     1,
4789                                     NULL,
4790                                     FIB_ROUTE_PATH_FLAG_NONE);
4791     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4792     FIB_TEST((FIB_NODE_INDEX_INVALID != fei),
4793              "attached interface route present");
4794
4795     local_pfx.fp_len = 32;
4796     fib_table_entry_update_one_path(fib_index, &local_pfx,
4797                                     FIB_SOURCE_INTERFACE,
4798                                     (FIB_ENTRY_FLAG_CONNECTED |
4799                                      FIB_ENTRY_FLAG_LOCAL),
4800                                     FIB_PROTOCOL_IP4,
4801                                     NULL,
4802                                     tm->hw[0]->sw_if_index,
4803                                     ~0, // invalid fib index
4804                                     1,
4805                                     NULL,
4806                                     FIB_ROUTE_PATH_FLAG_NONE);
4807     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
4808
4809     FIB_TEST((FIB_NODE_INDEX_INVALID != fei),
4810              "local interface route present");
4811
4812     /*
4813      * Add an 2 ARP entry => a complete ADJ plus adj-fib.
4814      */
4815     fib_prefix_t pfx_10_10_10_1_s_32 = {
4816         .fp_len = 32,
4817         .fp_proto = FIB_PROTOCOL_IP4,
4818         .fp_addr = {
4819             /* 10.10.10.1 */
4820             .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a01),
4821         },
4822     };
4823     fib_node_index_t ai;
4824
4825     fib_table_entry_update_one_path(fib_index,
4826                                     &pfx_10_10_10_1_s_32,
4827                                     FIB_SOURCE_ADJ,
4828                                     FIB_ENTRY_FLAG_ATTACHED,
4829                                     FIB_PROTOCOL_IP4,
4830                                     &pfx_10_10_10_1_s_32.fp_addr,
4831                                     tm->hw[0]->sw_if_index,
4832                                     ~0, // invalid fib index
4833                                     1,
4834                                     NULL,
4835                                     FIB_ROUTE_PATH_FLAG_NONE);
4836
4837     fei = fib_table_lookup(fib_index, &pfx_10_10_10_1_s_32);
4838     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib1 created");
4839     ai = fib_entry_get_adj(fei);
4840
4841     /*
4842      * create another FIB table into which routes will be imported
4843      */
4844     u32 import_fib_index1;
4845
4846     import_fib_index1 = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4, 11);
4847
4848     /*
4849      * Add an attached route in the import FIB
4850      */
4851     local_pfx.fp_len = 24;
4852     fib_table_entry_update_one_path(import_fib_index1,
4853                                     &local_pfx,
4854                                     FIB_SOURCE_API,
4855                                     FIB_ENTRY_FLAG_NONE,
4856                                     FIB_PROTOCOL_IP4,
4857                                     NULL,
4858                                     tm->hw[0]->sw_if_index,
4859                                     ~0, // invalid fib index
4860                                     1,
4861                                     NULL,
4862                                     FIB_ROUTE_PATH_FLAG_NONE);
4863     fei = fib_table_lookup_exact_match(import_fib_index1, &local_pfx);
4864     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "attached export created");
4865
4866     /*
4867      * check for the presence of the adj-fibs in the import table
4868      */
4869     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_1_s_32);
4870     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib1 imported");
4871     FIB_TEST((ai == fib_entry_get_adj(fei)),
4872              "adj-fib1 Import uses same adj as export");
4873
4874     /*
4875      * check for the presence of the local in the import table
4876      */
4877     local_pfx.fp_len = 32;
4878     fei = fib_table_lookup_exact_match(import_fib_index1, &local_pfx);
4879     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local imported");
4880
4881     /*
4882      * Add another adj-fin in the export table. Expect this
4883      * to get magically exported;
4884      */
4885     fib_prefix_t pfx_10_10_10_2_s_32 = {
4886         .fp_len = 32,
4887         .fp_proto = FIB_PROTOCOL_IP4,
4888         .fp_addr = {
4889             /* 10.10.10.2 */
4890             .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a02),
4891         },
4892     };
4893
4894     fib_table_entry_update_one_path(fib_index,
4895                                     &pfx_10_10_10_2_s_32,
4896                                     FIB_SOURCE_ADJ,
4897                                     FIB_ENTRY_FLAG_ATTACHED,
4898                                     FIB_PROTOCOL_IP4,
4899                                     &pfx_10_10_10_2_s_32.fp_addr,
4900                                     tm->hw[0]->sw_if_index,
4901                                     ~0, // invalid fib index
4902                                     1,
4903                                     NULL,
4904                                     FIB_ROUTE_PATH_FLAG_NONE);
4905     fei = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_2_s_32);
4906     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib2 present");
4907     ai = fib_entry_get_adj(fei);
4908
4909     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_2_s_32);
4910     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib2 imported");
4911     FIB_TEST((ai == fib_entry_get_adj(fei)),
4912              "Import uses same adj as export");
4913     FIB_TEST((FIB_ENTRY_FLAG_ATTACHED & fib_entry_get_flags(fei)),
4914              "ADJ-fib2 imported flags %d",
4915              fib_entry_get_flags(fei));
4916
4917     /*
4918      * create a 2nd FIB table into which routes will be imported
4919      */
4920     u32 import_fib_index2;
4921
4922     import_fib_index2 = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4, 12);
4923
4924     /*
4925      * Add an attached route in the import FIB
4926      */
4927     local_pfx.fp_len = 24;
4928     fib_table_entry_update_one_path(import_fib_index2,
4929                                     &local_pfx,
4930                                     FIB_SOURCE_API,
4931                                     FIB_ENTRY_FLAG_NONE,
4932                                     FIB_PROTOCOL_IP4,
4933                                     NULL,
4934                                     tm->hw[0]->sw_if_index,
4935                                     ~0, // invalid fib index
4936                                     1,
4937                                     NULL,
4938                                     FIB_ROUTE_PATH_FLAG_NONE);
4939     fei = fib_table_lookup_exact_match(import_fib_index1, &local_pfx);
4940     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "attached export created");
4941
4942     /*
4943      * check for the presence of all the adj-fibs and local in the import table
4944      */
4945     fei = fib_table_lookup_exact_match(import_fib_index2, &pfx_10_10_10_1_s_32);
4946     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib1 imported");
4947     fei = fib_table_lookup_exact_match(import_fib_index2, &pfx_10_10_10_2_s_32);
4948     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib2 imported");
4949     local_pfx.fp_len = 32;
4950     fei = fib_table_lookup_exact_match(import_fib_index2, &local_pfx);
4951     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local imported");
4952
4953     /*
4954      * add a 3rd adj-fib. expect it to be exported to both tables.
4955      */
4956     fib_prefix_t pfx_10_10_10_3_s_32 = {
4957         .fp_len = 32,
4958         .fp_proto = FIB_PROTOCOL_IP4,
4959         .fp_addr = {
4960             /* 10.10.10.3 */
4961             .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a03),
4962         },
4963     };
4964
4965     fib_table_entry_update_one_path(fib_index,
4966                                     &pfx_10_10_10_3_s_32,
4967                                     FIB_SOURCE_ADJ,
4968                                     FIB_ENTRY_FLAG_ATTACHED,
4969                                     FIB_PROTOCOL_IP4,
4970                                     &pfx_10_10_10_3_s_32.fp_addr,
4971                                     tm->hw[0]->sw_if_index,
4972                                     ~0, // invalid fib index
4973                                     1,
4974                                     NULL,
4975                                     FIB_ROUTE_PATH_FLAG_NONE);
4976     fei = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_3_s_32);
4977     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib3 present");
4978     ai = fib_entry_get_adj(fei);
4979
4980     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_3_s_32);
4981     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib3 imported to FIB1");
4982     FIB_TEST((ai == fib_entry_get_adj(fei)),
4983              "Import uses same adj as export");
4984     fei = fib_table_lookup_exact_match(import_fib_index2, &pfx_10_10_10_3_s_32);
4985     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib3 imported to FIB2");
4986     FIB_TEST((ai == fib_entry_get_adj(fei)),
4987              "Import uses same adj as export");
4988
4989     /*
4990      * remove the 3rd adj fib. we expect it to be removed from both FIBs
4991      */
4992     fib_table_entry_delete(fib_index,
4993                            &pfx_10_10_10_3_s_32,
4994                            FIB_SOURCE_ADJ);
4995
4996     fei = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_3_s_32);
4997     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "ADJ-fib3 remved");
4998
4999     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_3_s_32);
5000     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "ADJ-fib3 removed from FIB1");
5001
5002     fei = fib_table_lookup_exact_match(import_fib_index2, &pfx_10_10_10_3_s_32);
5003     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "ADJ-fib3 removed from FIB2");
5004
5005     /*
5006      * remove the attached route from the 2nd FIB. expect the imported
5007      * entires to be removed
5008      */
5009     local_pfx.fp_len = 24;
5010     fib_table_entry_delete(import_fib_index2,
5011                            &local_pfx,
5012                            FIB_SOURCE_API);
5013     fei = fib_table_lookup_exact_match(import_fib_index2, &local_pfx);
5014     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "attached export removed");
5015
5016     fei = fib_table_lookup_exact_match(import_fib_index2, &pfx_10_10_10_1_s_32);
5017     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "ADJ-fib1 removed from FIB2");
5018     fei = fib_table_lookup_exact_match(import_fib_index2, &pfx_10_10_10_2_s_32);
5019     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "ADJ-fib2 removed from FIB2");
5020     local_pfx.fp_len = 32;
5021     fei = fib_table_lookup_exact_match(import_fib_index2, &local_pfx);
5022     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "local removed from FIB2");
5023
5024     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_1_s_32);
5025     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib1 still in FIB1");
5026     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_2_s_32);
5027     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib2 still in FIB1");
5028     local_pfx.fp_len = 32;
5029     fei = fib_table_lookup_exact_match(import_fib_index1, &local_pfx);
5030     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local still in FIB1");
5031
5032     /*
5033      * modify the route in FIB1 so it is no longer attached. expect the imported
5034      * entires to be removed
5035      */
5036     local_pfx.fp_len = 24;
5037     fib_table_entry_update_one_path(import_fib_index1,
5038                                     &local_pfx,
5039                                     FIB_SOURCE_API,
5040                                     FIB_ENTRY_FLAG_NONE,
5041                                     FIB_PROTOCOL_IP4,
5042                                     &pfx_10_10_10_2_s_32.fp_addr,
5043                                     tm->hw[0]->sw_if_index,
5044                                     ~0, // invalid fib index
5045                                     1,
5046                                     NULL,
5047                                     FIB_ROUTE_PATH_FLAG_NONE);
5048     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_1_s_32);
5049     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "ADJ-fib1 removed from FIB1");
5050     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_2_s_32);
5051     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "ADJ-fib2 removed from FIB1");
5052     local_pfx.fp_len = 32;
5053     fei = fib_table_lookup_exact_match(import_fib_index1, &local_pfx);
5054     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "local removed from FIB1");
5055
5056     /*
5057      * modify it back to attached. expect the adj-fibs back
5058      */
5059     local_pfx.fp_len = 24;
5060     fib_table_entry_update_one_path(import_fib_index1,
5061                                     &local_pfx,
5062                                     FIB_SOURCE_API,
5063                                     FIB_ENTRY_FLAG_NONE,
5064                                     FIB_PROTOCOL_IP4,
5065                                     NULL,
5066                                     tm->hw[0]->sw_if_index,
5067                                     ~0, // invalid fib index
5068                                     1,
5069                                     NULL,
5070                                     FIB_ROUTE_PATH_FLAG_NONE);
5071     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_1_s_32);
5072     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib1 imported in FIB1");
5073     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_2_s_32);
5074     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib2 imported in FIB1");
5075     local_pfx.fp_len = 32;
5076     fei = fib_table_lookup_exact_match(import_fib_index1, &local_pfx);
5077     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local imported in FIB1");
5078
5079     /*
5080      * add a covering attached next-hop for the interface address, so we have
5081      * a valid adj to find when we check the forwarding tables
5082      */
5083     fib_prefix_t pfx_10_0_0_0_s_8 = {
5084         .fp_len = 8,
5085         .fp_proto = FIB_PROTOCOL_IP4,
5086         .fp_addr = {
5087             /* 10.0.0.0 */
5088             .ip4.as_u32 = clib_host_to_net_u32(0x0a000000),
5089         },
5090     };
5091
5092     fei = fib_table_entry_update_one_path(fib_index,
5093                                           &pfx_10_0_0_0_s_8,
5094                                           FIB_SOURCE_API,
5095                                           FIB_ENTRY_FLAG_NONE,
5096                                           FIB_PROTOCOL_IP4,
5097                                           &pfx_10_10_10_3_s_32.fp_addr,
5098                                           tm->hw[0]->sw_if_index,
5099                                           ~0, // invalid fib index
5100                                           1,
5101                                           NULL,
5102                                           FIB_ROUTE_PATH_FLAG_NONE);
5103     dpo = fib_entry_contribute_ip_forwarding(fei);
5104
5105     /*
5106      * remove the route in the export fib. expect the adj-fibs to be removed
5107      */
5108     local_pfx.fp_len = 24;
5109     fib_table_entry_delete(fib_index,
5110                            &local_pfx,
5111                            FIB_SOURCE_INTERFACE);
5112
5113     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_1_s_32);
5114     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "Delete export: ADJ-fib1 removed from FIB1");
5115     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_2_s_32);
5116     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "ADJ-fib2 removed from FIB1");
5117     local_pfx.fp_len = 32;
5118     fei = fib_table_lookup_exact_match(import_fib_index1, &local_pfx);
5119     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "local removed from FIB1");
5120
5121     /*
5122      * the adj-fibs in the export VRF are present in the FIB table,
5123      * but not installed in forwarding, since they have no attached cover.
5124      * Consequently a lookup in the MTRIE gives the adj for the covering
5125      * route 10.0.0.0/8.
5126      */
5127     fei = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_1_s_32);
5128     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib1 in export");
5129
5130     index_t lbi;
5131     lbi = ip4_fib_forwarding_lookup(fib_index, &pfx_10_10_10_1_s_32.fp_addr.ip4);
5132     FIB_TEST(lbi == dpo->dpoi_index,
5133              "10.10.10.1 forwards on \n%U not \n%U",
5134              format_load_balance, lbi, 0,
5135              format_dpo_id, dpo, 0);
5136     lbi = ip4_fib_forwarding_lookup(fib_index, &pfx_10_10_10_2_s_32.fp_addr.ip4);
5137     FIB_TEST(lbi == dpo->dpoi_index,
5138              "10.10.10.2 forwards on %U", format_dpo_id, dpo, 0);
5139     lbi = ip4_fib_forwarding_lookup(fib_index, &pfx_10_10_10_3_s_32.fp_addr.ip4);
5140     FIB_TEST(lbi == dpo->dpoi_index,
5141              "10.10.10.3 forwards on %U", format_dpo_id, dpo, 0);
5142
5143     /*
5144      * add the export prefix back, but not as attached.
5145      * No adj-fibs in export nor import tables
5146      */
5147     local_pfx.fp_len = 24;
5148     fei = fib_table_entry_update_one_path(fib_index,
5149                                           &local_pfx,
5150                                           FIB_SOURCE_API,
5151                                           FIB_ENTRY_FLAG_NONE,
5152                                           FIB_PROTOCOL_IP4,
5153                                           &pfx_10_10_10_1_s_32.fp_addr,
5154                                           tm->hw[0]->sw_if_index,
5155                                           ~0, // invalid fib index
5156                                           1,
5157                                           NULL,
5158                                           FIB_ROUTE_PATH_FLAG_NONE);
5159     dpo = fib_entry_contribute_ip_forwarding(fei);
5160
5161     fei = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_1_s_32);
5162     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "non-attached in export: ADJ-fib1 in export");
5163     lbi = ip4_fib_forwarding_lookup(fib_index, &pfx_10_10_10_1_s_32.fp_addr.ip4);
5164     FIB_TEST(lbi == dpo->dpoi_index,
5165              "10.10.10.1 forwards on %U", format_dpo_id, dpo, 0);
5166     fei = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_1_s_32);
5167     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib1 in export");
5168     lbi = ip4_fib_forwarding_lookup(fib_index, &pfx_10_10_10_2_s_32.fp_addr.ip4);
5169     FIB_TEST(lbi == dpo->dpoi_index,
5170              "10.10.10.2 forwards on %U", format_dpo_id, dpo, 0);
5171
5172     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_1_s_32);
5173     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "ADJ-fib1 removed from FIB1");
5174     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_2_s_32);
5175     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "ADJ-fib2 removed from FIB1");
5176     local_pfx.fp_len = 32;
5177     fei = fib_table_lookup_exact_match(import_fib_index1, &local_pfx);
5178     FIB_TEST((FIB_NODE_INDEX_INVALID == fei), "local removed from FIB1");
5179
5180     /*
5181      * modify the export prefix so it is attached. expect all covereds to return
5182      */
5183     local_pfx.fp_len = 24;
5184     fib_table_entry_update_one_path(fib_index,
5185                                     &local_pfx,
5186                                     FIB_SOURCE_API,
5187                                     FIB_ENTRY_FLAG_NONE,
5188                                     FIB_PROTOCOL_IP4,
5189                                     NULL,
5190                                     tm->hw[0]->sw_if_index,
5191                                     ~0, // invalid fib index
5192                                     1,
5193                                     NULL,
5194                                     FIB_ROUTE_PATH_FLAG_NONE);
5195
5196     fei = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_1_s_32);
5197     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib1 reinstalled in export");
5198     dpo = fib_entry_contribute_ip_forwarding(fei);
5199     FIB_TEST(dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
5200              "Adj-fib1 is not drop in export");
5201     fei = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_2_s_32);
5202     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib2 reinstalled in export");
5203     local_pfx.fp_len = 32;
5204     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
5205     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local reinstalled in export");
5206     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_1_s_32);
5207     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "attached in export: ADJ-fib1 imported");
5208     dpo = fib_entry_contribute_ip_forwarding(fei);
5209     FIB_TEST(dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
5210              "Adj-fib1 is not drop in export");
5211     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_1_s_32);
5212     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib1 imported");
5213     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_2_s_32);
5214     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib2 imported");
5215     local_pfx.fp_len = 32;
5216     fei = fib_table_lookup_exact_match(import_fib_index1, &local_pfx);
5217     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local imported");
5218
5219     /*
5220      * modify the export prefix so connected. no change.
5221      */
5222     local_pfx.fp_len = 24;
5223     fib_table_entry_update_one_path(fib_index, &local_pfx,
5224                                     FIB_SOURCE_INTERFACE,
5225                                     (FIB_ENTRY_FLAG_CONNECTED |
5226                                      FIB_ENTRY_FLAG_ATTACHED),
5227                                     FIB_PROTOCOL_IP4,
5228                                     NULL,
5229                                     tm->hw[0]->sw_if_index,
5230                                     ~0,
5231                                     1,
5232                                     NULL,
5233                                     FIB_ROUTE_PATH_FLAG_NONE);
5234
5235     fei = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_1_s_32);
5236     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib1 reinstalled in export");
5237     dpo = fib_entry_contribute_ip_forwarding(fei);
5238     FIB_TEST(dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
5239              "Adj-fib1 is not drop in export");
5240     fei = fib_table_lookup_exact_match(fib_index, &pfx_10_10_10_2_s_32);
5241     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib2 reinstalled in export");
5242     local_pfx.fp_len = 32;
5243     fei = fib_table_lookup_exact_match(fib_index, &local_pfx);
5244     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local reinstalled in export");
5245     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_1_s_32);
5246     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "attached in export: ADJ-fib1 imported");
5247     dpo = fib_entry_contribute_ip_forwarding(fei);
5248     FIB_TEST(dpo_cmp(dpo_drop, load_balance_get_bucket(dpo->dpoi_index, 0)),
5249              "Adj-fib1 is not drop in export");
5250     fei = fib_table_lookup_exact_match(import_fib_index1, &pfx_10_10_10_2_s_32);
5251     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "ADJ-fib2 imported");
5252     local_pfx.fp_len = 32;
5253     fei = fib_table_lookup_exact_match(import_fib_index1, &local_pfx);
5254     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "local imported");
5255
5256     /*
5257      * CLEANUP
5258      */
5259     fib_table_entry_delete(fib_index,
5260                            &pfx_10_0_0_0_s_8,
5261                            FIB_SOURCE_API);
5262     fib_table_entry_delete(fib_index,
5263                            &pfx_10_10_10_1_s_32,
5264                            FIB_SOURCE_ADJ);
5265     fib_table_entry_delete(fib_index,
5266                            &pfx_10_10_10_2_s_32,
5267                            FIB_SOURCE_ADJ);
5268     local_pfx.fp_len = 32;
5269     fib_table_entry_delete(fib_index,
5270                            &local_pfx,
5271                            FIB_SOURCE_INTERFACE);
5272     local_pfx.fp_len = 24;
5273     fib_table_entry_delete(fib_index,
5274                            &local_pfx,
5275                            FIB_SOURCE_API);
5276     fib_table_entry_delete(fib_index,
5277                            &local_pfx,
5278                            FIB_SOURCE_INTERFACE);
5279     local_pfx.fp_len = 24;
5280     fib_table_entry_delete(import_fib_index1,
5281                            &local_pfx,
5282                            FIB_SOURCE_API);
5283
5284     fib_table_unlock(import_fib_index1, FIB_PROTOCOL_IP4);
5285     fib_table_unlock(import_fib_index2, FIB_PROTOCOL_IP4);
5286
5287     FIB_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d",
5288              adj_nbr_db_size());
5289
5290     return (0);
5291 }
5292
5293
5294 /*
5295  * Test the recursive route route handling for GRE tunnels
5296  */
5297 static int
5298 fib_test_label (void)
5299 {
5300     fib_node_index_t fei, ai_mpls_10_10_10_1, ai_v4_10_10_11_1, ai_v4_10_10_11_2, ai_mpls_10_10_11_2, ai_mpls_10_10_11_1;
5301     const u32 fib_index = 0;
5302     test_main_t *tm;
5303     ip4_main_t *im;
5304     int lb_count, ii;
5305
5306     lb_count = pool_elts(load_balance_pool);
5307     tm = &test_main;
5308     im = &ip4_main;
5309
5310     /*
5311      * add interface routes. We'll assume this works. It's more rigorously
5312      * tested elsewhere.
5313      */
5314     fib_prefix_t local0_pfx = {
5315         .fp_len = 24,
5316         .fp_proto = FIB_PROTOCOL_IP4,
5317         .fp_addr = {
5318             .ip4 = {
5319                 /* 10.10.10.10 */
5320                 .as_u32 = clib_host_to_net_u32(0x0a0a0a0a),
5321             },
5322         },
5323     };
5324
5325     FIB_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d",
5326              adj_nbr_db_size());
5327
5328     vec_validate(im->fib_index_by_sw_if_index, tm->hw[0]->sw_if_index);
5329     im->fib_index_by_sw_if_index[tm->hw[0]->sw_if_index] = fib_index;
5330
5331     fib_table_entry_update_one_path(fib_index, &local0_pfx,
5332                                     FIB_SOURCE_INTERFACE,
5333                                     (FIB_ENTRY_FLAG_CONNECTED |
5334                                      FIB_ENTRY_FLAG_ATTACHED),
5335                                     FIB_PROTOCOL_IP4,
5336                                     NULL,
5337                                     tm->hw[0]->sw_if_index,
5338                                     ~0,
5339                                     1,
5340                                     NULL,
5341                                     FIB_ROUTE_PATH_FLAG_NONE);
5342     fei = fib_table_lookup_exact_match(fib_index, &local0_pfx);
5343     FIB_TEST((FIB_NODE_INDEX_INVALID != fei),
5344              "attached interface route present");
5345
5346     local0_pfx.fp_len = 32;
5347     fib_table_entry_update_one_path(fib_index, &local0_pfx,
5348                                     FIB_SOURCE_INTERFACE,
5349                                     (FIB_ENTRY_FLAG_CONNECTED |
5350                                      FIB_ENTRY_FLAG_LOCAL),
5351                                     FIB_PROTOCOL_IP4,
5352                                     NULL,
5353                                     tm->hw[0]->sw_if_index,
5354                                     ~0, // invalid fib index
5355                                     1,
5356                                     NULL,
5357                                     FIB_ROUTE_PATH_FLAG_NONE);
5358     fei = fib_table_lookup_exact_match(fib_index, &local0_pfx);
5359
5360     FIB_TEST((FIB_NODE_INDEX_INVALID != fei),
5361              "local interface route present");
5362
5363     fib_prefix_t local1_pfx = {
5364         .fp_len = 24,
5365         .fp_proto = FIB_PROTOCOL_IP4,
5366         .fp_addr = {
5367             .ip4 = {
5368                 /* 10.10.11.10 */
5369                 .as_u32 = clib_host_to_net_u32(0x0a0a0b0a),
5370             },
5371         },
5372     };
5373
5374     vec_validate(im->fib_index_by_sw_if_index, tm->hw[1]->sw_if_index);
5375     im->fib_index_by_sw_if_index[tm->hw[1]->sw_if_index] = fib_index;
5376
5377     fib_table_entry_update_one_path(fib_index, &local1_pfx,
5378                                     FIB_SOURCE_INTERFACE,
5379                                     (FIB_ENTRY_FLAG_CONNECTED |
5380                                      FIB_ENTRY_FLAG_ATTACHED),
5381                                     FIB_PROTOCOL_IP4,
5382                                     NULL,
5383                                     tm->hw[1]->sw_if_index,
5384                                     ~0,
5385                                     1,
5386                                     NULL,
5387                                     FIB_ROUTE_PATH_FLAG_NONE);
5388     fei = fib_table_lookup_exact_match(fib_index, &local1_pfx);
5389     FIB_TEST((FIB_NODE_INDEX_INVALID != fei),
5390              "attached interface route present");
5391
5392     local1_pfx.fp_len = 32;
5393     fib_table_entry_update_one_path(fib_index, &local1_pfx,
5394                                     FIB_SOURCE_INTERFACE,
5395                                     (FIB_ENTRY_FLAG_CONNECTED |
5396                                      FIB_ENTRY_FLAG_LOCAL),
5397                                     FIB_PROTOCOL_IP4,
5398                                     NULL,
5399                                     tm->hw[1]->sw_if_index,
5400                                     ~0, // invalid fib index
5401                                     1,
5402                                     NULL,
5403                                     FIB_ROUTE_PATH_FLAG_NONE);
5404     fei = fib_table_lookup_exact_match(fib_index, &local1_pfx);
5405
5406     FIB_TEST((FIB_NODE_INDEX_INVALID != fei),
5407              "local interface route present");
5408
5409     ip46_address_t nh_10_10_10_1 = {
5410         .ip4 = {
5411             .as_u32 = clib_host_to_net_u32(0x0a0a0a01),
5412         },
5413     };
5414     ip46_address_t nh_10_10_11_1 = {
5415         .ip4 = {
5416             .as_u32 = clib_host_to_net_u32(0x0a0a0b01),
5417         },
5418     };
5419     ip46_address_t nh_10_10_11_2 = {
5420         .ip4 = {
5421             .as_u32 = clib_host_to_net_u32(0x0a0a0b02),
5422         },
5423     };
5424
5425     ai_v4_10_10_11_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
5426                                            VNET_LINK_IP4,
5427                                            &nh_10_10_11_1,
5428                                            tm->hw[1]->sw_if_index);
5429     ai_v4_10_10_11_2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
5430                                            VNET_LINK_IP4,
5431                                            &nh_10_10_11_2,
5432                                            tm->hw[1]->sw_if_index);
5433     ai_mpls_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
5434                                              VNET_LINK_MPLS,
5435                                              &nh_10_10_10_1,
5436                                              tm->hw[0]->sw_if_index);
5437     ai_mpls_10_10_11_2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
5438                                              VNET_LINK_MPLS,
5439                                              &nh_10_10_11_2,
5440                                              tm->hw[1]->sw_if_index);
5441     ai_mpls_10_10_11_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
5442                                              VNET_LINK_MPLS,
5443                                              &nh_10_10_11_1,
5444                                              tm->hw[1]->sw_if_index);
5445
5446     /*
5447      * Add an etry with one path with a real out-going label
5448      */
5449     fib_prefix_t pfx_1_1_1_1_s_32 = {
5450         .fp_len = 32,
5451         .fp_proto = FIB_PROTOCOL_IP4,
5452         .fp_addr = {
5453             .ip4.as_u32 = clib_host_to_net_u32(0x01010101),
5454         },
5455     };
5456     fib_test_lb_bucket_t l99_eos_o_10_10_10_1 = {
5457         .type = FT_LB_LABEL_O_ADJ,
5458         .label_o_adj = {
5459             .adj = ai_mpls_10_10_10_1,
5460             .label = 99,
5461             .eos = MPLS_EOS,
5462         },
5463     };
5464     fib_test_lb_bucket_t l99_neos_o_10_10_10_1 = {
5465         .type = FT_LB_LABEL_O_ADJ,
5466         .label_o_adj = {
5467             .adj = ai_mpls_10_10_10_1,
5468             .label = 99,
5469             .eos = MPLS_NON_EOS,
5470         },
5471     };
5472     mpls_label_t *l99 = NULL;
5473     vec_add1(l99, 99);
5474
5475     fib_table_entry_update_one_path(fib_index,
5476                                     &pfx_1_1_1_1_s_32,
5477                                     FIB_SOURCE_API,
5478                                     FIB_ENTRY_FLAG_NONE,
5479                                     FIB_PROTOCOL_IP4,
5480                                     &nh_10_10_10_1,
5481                                     tm->hw[0]->sw_if_index,
5482                                     ~0, // invalid fib index
5483                                     1,
5484                                     l99,
5485                                     FIB_ROUTE_PATH_FLAG_NONE);
5486
5487     fei = fib_table_lookup(fib_index, &pfx_1_1_1_1_s_32);
5488     FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "1.1.1.1/32 created");
5489
5490     FIB_TEST(fib_test_validate_entry(fei, 
5491                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
5492                                      1,
5493                                      &l99_eos_o_10_10_10_1),
5494              "1.1.1.1/32 LB 1 bucket via label 99 over 10.10.10.1");
5495
5496     /*
5497      * add a path with an implicit NULL label
5498      */
5499     fib_test_lb_bucket_t a_o_10_10_11_1 = {
5500         .type = FT_LB_ADJ,
5501         .adj = {
5502             .adj = ai_v4_10_10_11_1,
5503         },
5504     };
5505     fib_test_lb_bucket_t a_mpls_o_10_10_11_1 = {
5506         .type = FT_LB_ADJ,
5507         .adj = {
5508             .adj = ai_mpls_10_10_11_1,
5509         },
5510     };
5511     mpls_label_t *l_imp_null = NULL;
5512     vec_add1(l_imp_null, MPLS_IETF_IMPLICIT_NULL_LABEL);
5513
5514     fei = fib_table_entry_path_add(fib_index,
5515                                    &pfx_1_1_1_1_s_32,
5516                                    FIB_SOURCE_API,
5517                                    FIB_ENTRY_FLAG_NONE,
5518                                    FIB_PROTOCOL_IP4,
5519                                    &nh_10_10_11_1,
5520                                    tm->hw[1]->sw_if_index,
5521                                    ~0, // invalid fib index
5522                                    1,
5523                                    l_imp_null,
5524                                    FIB_ROUTE_PATH_FLAG_NONE);
5525
5526     FIB_TEST(fib_test_validate_entry(fei, 
5527                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
5528                                      2,
5529                                      &l99_eos_o_10_10_10_1,
5530                                      &a_o_10_10_11_1),
5531              "1.1.1.1/32 LB 2 buckets via: "
5532              "label 99 over 10.10.10.1, "
5533              "adj over 10.10.11.1");
5534
5535     /*
5536      * assign the route a local label
5537      */
5538     fib_table_entry_local_label_add(fib_index,
5539                                     &pfx_1_1_1_1_s_32,
5540                                     24001);
5541
5542     fib_prefix_t pfx_24001_eos = {
5543         .fp_proto = FIB_PROTOCOL_MPLS,
5544         .fp_label = 24001,
5545         .fp_eos = MPLS_EOS,
5546     };
5547     fib_prefix_t pfx_24001_neos = {
5548         .fp_proto = FIB_PROTOCOL_MPLS,
5549         .fp_label = 24001,
5550         .fp_eos = MPLS_NON_EOS,
5551     };
5552
5553     /*
5554      * The EOS entry should link to both the paths,
5555      *  and use an ip adj for the imp-null
5556      * The NON-EOS entry should link to both the paths,
5557      *  and use an mpls adj for the imp-null
5558      */
5559     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5560                            &pfx_24001_eos);
5561     FIB_TEST(fib_test_validate_entry(fei, 
5562                                      FIB_FORW_CHAIN_TYPE_MPLS_EOS,
5563                                      2,
5564                                      &l99_eos_o_10_10_10_1,
5565                                      &a_o_10_10_11_1),
5566              "24001/eos LB 2 buckets via: "
5567              "label 99 over 10.10.10.1, "
5568              "adj over 10.10.11.1");
5569
5570
5571     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5572                            &pfx_24001_neos);
5573     FIB_TEST(fib_test_validate_entry(fei, 
5574                                      FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
5575                                      2,
5576                                      &l99_neos_o_10_10_10_1,
5577                                      &a_mpls_o_10_10_11_1),
5578              "24001/neos LB 1 bucket via: "
5579              "label 99 over 10.10.10.1 ",
5580              "mpls-adj via 10.10.11.1");
5581
5582     /*
5583      * add an unlabelled path, this is excluded from the neos chains,
5584      */
5585     fib_test_lb_bucket_t adj_o_10_10_11_2 = {
5586         .type = FT_LB_ADJ,
5587         .adj = {
5588             .adj = ai_v4_10_10_11_2,
5589         },
5590     };
5591
5592     fei = fib_table_entry_path_add(fib_index,
5593                                    &pfx_1_1_1_1_s_32,
5594                                    FIB_SOURCE_API,
5595                                    FIB_ENTRY_FLAG_NONE,
5596                                    FIB_PROTOCOL_IP4,
5597                                    &nh_10_10_11_2,
5598                                    tm->hw[1]->sw_if_index,
5599                                    ~0, // invalid fib index
5600                                    1,
5601                                    NULL,
5602                                    FIB_ROUTE_PATH_FLAG_NONE);
5603
5604     FIB_TEST(fib_test_validate_entry(fei, 
5605                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
5606                                      16, // 3 choices spread over 16 buckets
5607                                      &l99_eos_o_10_10_10_1,
5608                                      &l99_eos_o_10_10_10_1,
5609                                      &l99_eos_o_10_10_10_1,
5610                                      &l99_eos_o_10_10_10_1,
5611                                      &l99_eos_o_10_10_10_1,
5612                                      &l99_eos_o_10_10_10_1,
5613                                      &a_o_10_10_11_1,
5614                                      &a_o_10_10_11_1,
5615                                      &a_o_10_10_11_1,
5616                                      &a_o_10_10_11_1,
5617                                      &a_o_10_10_11_1,
5618                                      &adj_o_10_10_11_2,
5619                                      &adj_o_10_10_11_2,
5620                                      &adj_o_10_10_11_2,
5621                                      &adj_o_10_10_11_2,
5622                                      &adj_o_10_10_11_2),
5623              "1.1.1.1/32 LB 16 buckets via: "
5624              "label 99 over 10.10.10.1, "
5625              "adj over 10.10.11.1",
5626              "adj over 10.10.11.2");
5627
5628     /*
5629      * get and lock a reference to the non-eos of the via entry 1.1.1.1/32
5630      */
5631     dpo_id_t non_eos_1_1_1_1 = DPO_INVALID;
5632     fib_entry_contribute_forwarding(fei,
5633                                     FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
5634                                     &non_eos_1_1_1_1);
5635
5636     /*
5637      * n-eos has only the 2 labelled paths
5638      */
5639     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5640                            &pfx_24001_neos);
5641
5642     FIB_TEST(fib_test_validate_entry(fei,
5643                                      FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
5644                                      2,
5645                                      &l99_neos_o_10_10_10_1,
5646                                      &a_mpls_o_10_10_11_1),
5647              "24001/neos LB 2 buckets via: "
5648              "label 99 over 10.10.10.1, "
5649              "adj-mpls over 10.10.11.2");
5650
5651     /*
5652      * A labelled recursive
5653      */
5654     fib_prefix_t pfx_2_2_2_2_s_32 = {
5655         .fp_len = 32,
5656         .fp_proto = FIB_PROTOCOL_IP4,
5657         .fp_addr = {
5658             .ip4.as_u32 = clib_host_to_net_u32(0x02020202),
5659         },
5660     };
5661     fib_test_lb_bucket_t l1600_eos_o_1_1_1_1 = {
5662         .type = FT_LB_LABEL_O_LB,
5663         .label_o_lb = {
5664             .lb = non_eos_1_1_1_1.dpoi_index,
5665             .label = 1600,
5666             .eos = MPLS_EOS,
5667         },
5668     };
5669     mpls_label_t *l1600 = NULL;
5670     vec_add1(l1600, 1600);
5671
5672     fib_table_entry_update_one_path(fib_index,
5673                                     &pfx_2_2_2_2_s_32,
5674                                     FIB_SOURCE_API,
5675                                     FIB_ENTRY_FLAG_NONE,
5676                                     FIB_PROTOCOL_IP4,
5677                                     &pfx_1_1_1_1_s_32.fp_addr,
5678                                     ~0,
5679                                     fib_index,
5680                                     1,
5681                                     l1600,
5682                                     FIB_ROUTE_PATH_FLAG_NONE);
5683
5684     fei = fib_table_lookup(fib_index, &pfx_2_2_2_2_s_32);
5685     FIB_TEST(fib_test_validate_entry(fei, 
5686                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
5687                                      1,
5688                                      &l1600_eos_o_1_1_1_1),
5689              "2.2.2.2.2/32 LB 1 buckets via: "
5690              "label 1600 over 1.1.1.1");
5691
5692     dpo_id_t dpo_44 = DPO_INVALID;
5693     index_t urpfi;
5694
5695     fib_entry_contribute_forwarding(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, &dpo_44);
5696     urpfi = load_balance_get_urpf(dpo_44.dpoi_index);
5697
5698     FIB_TEST(fib_urpf_check(urpfi, tm->hw[0]->sw_if_index),
5699              "uRPF check for 2.2.2.2/32 on %d OK",
5700              tm->hw[0]->sw_if_index);
5701     FIB_TEST(fib_urpf_check(urpfi, tm->hw[1]->sw_if_index),
5702              "uRPF check for 2.2.2.2/32 on %d OK",
5703              tm->hw[1]->sw_if_index);
5704     FIB_TEST(!fib_urpf_check(urpfi, 99),
5705              "uRPF check for 2.2.2.2/32 on 99 not-OK",
5706              99);
5707
5708     fib_entry_contribute_forwarding(fei, FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS, &dpo_44);
5709     FIB_TEST(urpfi == load_balance_get_urpf(dpo_44.dpoi_index),
5710              "Shared uRPF on IP and non-EOS chain");
5711
5712     dpo_reset(&dpo_44);
5713
5714     /*
5715      * we are holding a lock on the non-eos LB of the via-entry.
5716      * do a PIC-core failover by shutting the link of the via-entry.
5717      *
5718      * shut down the link with the valid label
5719      */
5720     vnet_sw_interface_set_flags(vnet_get_main(),
5721                                 tm->hw[0]->sw_if_index,
5722                                 0);
5723
5724     fei = fib_table_lookup(fib_index, &pfx_1_1_1_1_s_32);
5725     FIB_TEST(fib_test_validate_entry(fei, 
5726                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
5727                                      2,
5728                                      &a_o_10_10_11_1,
5729                                      &adj_o_10_10_11_2),
5730              "1.1.1.1/32 LB 2 buckets via: "
5731              "adj over 10.10.11.1, ",
5732              "adj-v4 over 10.10.11.2");
5733
5734     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5735                            &pfx_24001_eos);
5736     FIB_TEST(fib_test_validate_entry(fei, 
5737                                      FIB_FORW_CHAIN_TYPE_MPLS_EOS,
5738                                      2,
5739                                      &a_o_10_10_11_1,
5740                                      &adj_o_10_10_11_2),
5741              "24001/eos LB 2 buckets via: "
5742              "adj over 10.10.11.1, ",
5743              "adj-v4 over 10.10.11.2");
5744
5745     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5746                            &pfx_24001_neos);
5747     FIB_TEST(fib_test_validate_entry(fei,
5748                                      FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
5749                                      1,
5750                                      &a_mpls_o_10_10_11_1),
5751              "24001/neos LB 1 buckets via: "
5752              "adj-mpls over 10.10.11.2");
5753
5754     /*
5755      * test that the pre-failover load-balance has been in-place
5756      * modified
5757      */
5758     dpo_id_t current = DPO_INVALID;
5759     fib_entry_contribute_forwarding(fei,
5760                                     FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
5761                                     &current);
5762
5763     FIB_TEST(!dpo_cmp(&non_eos_1_1_1_1,
5764                       &current),
5765              "PIC-core LB inplace modified %U %U",
5766              format_dpo_id, &non_eos_1_1_1_1, 0,
5767              format_dpo_id, &current, 0);
5768
5769     dpo_reset(&non_eos_1_1_1_1);
5770     dpo_reset(&current);
5771
5772     /*
5773      * no-shut the link with the valid label
5774      */
5775     vnet_sw_interface_set_flags(vnet_get_main(),
5776                                 tm->hw[0]->sw_if_index,
5777                                 VNET_SW_INTERFACE_FLAG_ADMIN_UP);
5778
5779     fei = fib_table_lookup(fib_index, &pfx_1_1_1_1_s_32);
5780     FIB_TEST(fib_test_validate_entry(fei, 
5781                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
5782                                      16, // 3 choices spread over 16 buckets
5783                                      &l99_eos_o_10_10_10_1,
5784                                      &l99_eos_o_10_10_10_1,
5785                                      &l99_eos_o_10_10_10_1,
5786                                      &l99_eos_o_10_10_10_1,
5787                                      &l99_eos_o_10_10_10_1,
5788                                      &l99_eos_o_10_10_10_1,
5789                                      &a_o_10_10_11_1,
5790                                      &a_o_10_10_11_1,
5791                                      &a_o_10_10_11_1,
5792                                      &a_o_10_10_11_1,
5793                                      &a_o_10_10_11_1,
5794                                      &adj_o_10_10_11_2,
5795                                      &adj_o_10_10_11_2,
5796                                      &adj_o_10_10_11_2,
5797                                      &adj_o_10_10_11_2,
5798                                      &adj_o_10_10_11_2),
5799              "1.1.1.1/32 LB 16 buckets via: "
5800              "label 99 over 10.10.10.1, "
5801              "adj over 10.10.11.1",
5802              "adj-v4 over 10.10.11.2");
5803
5804
5805     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5806                            &pfx_24001_eos);
5807     FIB_TEST(fib_test_validate_entry(fei, 
5808                                      FIB_FORW_CHAIN_TYPE_MPLS_EOS,
5809                                      16, // 3 choices spread over 16 buckets
5810                                      &l99_eos_o_10_10_10_1,
5811                                      &l99_eos_o_10_10_10_1,
5812                                      &l99_eos_o_10_10_10_1,
5813                                      &l99_eos_o_10_10_10_1,
5814                                      &l99_eos_o_10_10_10_1,
5815                                      &l99_eos_o_10_10_10_1,
5816                                      &a_o_10_10_11_1,
5817                                      &a_o_10_10_11_1,
5818                                      &a_o_10_10_11_1,
5819                                      &a_o_10_10_11_1,
5820                                      &a_o_10_10_11_1,
5821                                      &adj_o_10_10_11_2,
5822                                      &adj_o_10_10_11_2,
5823                                      &adj_o_10_10_11_2,
5824                                      &adj_o_10_10_11_2,
5825                                      &adj_o_10_10_11_2),
5826              "24001/eos LB 16 buckets via: "
5827              "label 99 over 10.10.10.1, "
5828              "adj over 10.10.11.1",
5829              "adj-v4 over 10.10.11.2");
5830
5831     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5832                            &pfx_24001_neos);
5833     FIB_TEST(fib_test_validate_entry(fei, 
5834                                      FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
5835                                      2,
5836                                      &l99_neos_o_10_10_10_1,
5837                                      &a_mpls_o_10_10_11_1),
5838              "24001/neos LB 2 buckets via: "
5839              "label 99 over 10.10.10.1, "
5840              "adj-mpls over 10.10.11.2");
5841
5842     /*
5843      * remove the first path with the valid label
5844      */
5845     fib_table_entry_path_remove(fib_index,
5846                                 &pfx_1_1_1_1_s_32,
5847                                 FIB_SOURCE_API,
5848                                 FIB_PROTOCOL_IP4,
5849                                 &nh_10_10_10_1,
5850                                 tm->hw[0]->sw_if_index,
5851                                 ~0, // invalid fib index
5852                                 1,
5853                                 FIB_ROUTE_PATH_FLAG_NONE);
5854
5855     fei = fib_table_lookup(fib_index, &pfx_1_1_1_1_s_32);
5856     FIB_TEST(fib_test_validate_entry(fei, 
5857                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
5858                                      2,
5859                                      &a_o_10_10_11_1,
5860                                      &adj_o_10_10_11_2),
5861              "1.1.1.1/32 LB 2 buckets via: "
5862              "adj over 10.10.11.1, "
5863              "adj-v4 over 10.10.11.2");
5864
5865     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5866                            &pfx_24001_eos);
5867     FIB_TEST(fib_test_validate_entry(fei, 
5868                                      FIB_FORW_CHAIN_TYPE_MPLS_EOS,
5869                                      2,
5870                                      &a_o_10_10_11_1,
5871                                      &adj_o_10_10_11_2),
5872              "24001/eos LB 2 buckets via: "
5873              "adj over 10.10.11.1, "
5874              "adj-v4 over 10.10.11.2");
5875
5876     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5877                            &pfx_24001_neos);
5878
5879     FIB_TEST(fib_test_validate_entry(fei, 
5880                                      FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
5881                                      1,
5882                                      &a_mpls_o_10_10_11_1),
5883              "24001/neos LB 1 buckets via: "
5884              "adj-mpls over 10.10.11.2");
5885
5886     /*
5887      * remove the other path with a valid label
5888      */
5889     fib_test_lb_bucket_t bucket_drop = {
5890         .type = FT_LB_SPECIAL,
5891         .special = {
5892             .adj = DPO_PROTO_IP4,
5893         },
5894     };
5895
5896     fib_table_entry_path_remove(fib_index,
5897                                 &pfx_1_1_1_1_s_32,
5898                                 FIB_SOURCE_API,
5899                                 FIB_PROTOCOL_IP4,
5900                                 &nh_10_10_11_1,
5901                                 tm->hw[1]->sw_if_index,
5902                                 ~0, // invalid fib index
5903                                 1,
5904                                 FIB_ROUTE_PATH_FLAG_NONE);
5905
5906     fei = fib_table_lookup(fib_index, &pfx_1_1_1_1_s_32);
5907     FIB_TEST(fib_test_validate_entry(fei, 
5908                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
5909                                      1,
5910                                      &adj_o_10_10_11_2),
5911              "1.1.1.1/32 LB 1 buckets via: "
5912              "adj over 10.10.11.2");
5913
5914     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5915                            &pfx_24001_eos);
5916     FIB_TEST(fib_test_validate_entry(fei, 
5917                                      FIB_FORW_CHAIN_TYPE_MPLS_EOS,
5918                                      1,
5919                                      &adj_o_10_10_11_2),
5920              "24001/eos LB 1 buckets via: "
5921              "adj over 10.10.11.2");
5922
5923     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5924                            &pfx_24001_neos);
5925     FIB_TEST(fib_test_validate_entry(fei, 
5926                                      FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
5927                                       1,
5928                                       &bucket_drop),
5929              "24001/eos LB 1 buckets via: DROP");
5930
5931     /*
5932      * add back the path with the valid label
5933      */
5934     l99 = NULL;
5935     vec_add1(l99, 99);
5936
5937     fib_table_entry_path_add(fib_index,
5938                              &pfx_1_1_1_1_s_32,
5939                              FIB_SOURCE_API,
5940                              FIB_ENTRY_FLAG_NONE,
5941                              FIB_PROTOCOL_IP4,
5942                              &nh_10_10_10_1,
5943                              tm->hw[0]->sw_if_index,
5944                              ~0, // invalid fib index
5945                              1,
5946                              l99,
5947                              FIB_ROUTE_PATH_FLAG_NONE);
5948
5949     fei = fib_table_lookup(fib_index, &pfx_1_1_1_1_s_32);
5950     FIB_TEST(fib_test_validate_entry(fei,
5951                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
5952                                      2,
5953                                      &l99_eos_o_10_10_10_1,
5954                                      &adj_o_10_10_11_2),
5955              "1.1.1.1/32 LB 2 buckets via: "
5956              "label 99 over 10.10.10.1, "
5957              "adj over 10.10.11.2");
5958
5959     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5960                            &pfx_24001_eos);
5961     FIB_TEST(fib_test_validate_entry(fei, 
5962                                      FIB_FORW_CHAIN_TYPE_MPLS_EOS,
5963                                      2,
5964                                      &l99_eos_o_10_10_10_1,
5965                                      &adj_o_10_10_11_2),
5966              "24001/eos LB 2 buckets via: "
5967              "label 99 over 10.10.10.1, "
5968              "adj over 10.10.11.2");
5969
5970     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
5971                            &pfx_24001_neos);
5972     FIB_TEST(fib_test_validate_entry(fei, 
5973                                      FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
5974                                      1,
5975                                      &l99_neos_o_10_10_10_1),
5976              "24001/neos LB 1 buckets via: "
5977              "label 99 over 10.10.10.1");
5978
5979     /*
5980      * change the local label
5981      */
5982     fib_table_entry_local_label_add(fib_index,
5983                                     &pfx_1_1_1_1_s_32,
5984                                     25005);
5985
5986     fib_prefix_t pfx_25005_eos = {
5987         .fp_proto = FIB_PROTOCOL_MPLS,
5988         .fp_label = 25005,
5989         .fp_eos = MPLS_EOS,
5990     };
5991     fib_prefix_t pfx_25005_neos = {
5992         .fp_proto = FIB_PROTOCOL_MPLS,
5993         .fp_label = 25005,
5994         .fp_eos = MPLS_NON_EOS,
5995     };
5996
5997     FIB_TEST((FIB_NODE_INDEX_INVALID ==
5998               fib_table_lookup(fib_index, &pfx_24001_eos)),
5999              "24001/eos removed after label change");
6000     FIB_TEST((FIB_NODE_INDEX_INVALID ==
6001               fib_table_lookup(fib_index, &pfx_24001_neos)),
6002              "24001/eos removed after label change");
6003
6004     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
6005                            &pfx_25005_eos);
6006     FIB_TEST(fib_test_validate_entry(fei,
6007                                      FIB_FORW_CHAIN_TYPE_MPLS_EOS,
6008                                      2,
6009                                      &l99_eos_o_10_10_10_1,
6010                                      &adj_o_10_10_11_2),
6011              "25005/eos LB 2 buckets via: "
6012              "label 99 over 10.10.10.1, "
6013              "adj over 10.10.11.2");
6014
6015     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
6016                            &pfx_25005_neos);
6017     FIB_TEST(fib_test_validate_entry(fei,
6018                                      FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
6019                                      1,
6020                                      &l99_neos_o_10_10_10_1),
6021              "25005/neos LB 1 buckets via: "
6022              "label 99 over 10.10.10.1");
6023
6024     /*
6025      * remove the local label.
6026      * the check that the MPLS entries are gone is done by the fact the
6027      * MPLS table is no longer present.
6028      */
6029     fib_table_entry_local_label_remove(fib_index,
6030                                        &pfx_1_1_1_1_s_32,
6031                                        25005);
6032
6033     fei = fib_table_lookup(fib_index, &pfx_1_1_1_1_s_32);
6034     FIB_TEST(fib_test_validate_entry(fei, 
6035                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6036                                      2,
6037                                      &l99_eos_o_10_10_10_1,
6038                                      &adj_o_10_10_11_2),
6039              "24001/eos LB 2 buckets via: "
6040              "label 99 over 10.10.10.1, "
6041              "adj over 10.10.11.2");
6042
6043     FIB_TEST((FIB_NODE_INDEX_INVALID ==
6044               mpls_fib_index_from_table_id(MPLS_FIB_DEFAULT_TABLE_ID)),
6045              "No more MPLS FIB entries => table removed");
6046
6047     /*
6048      * add another via-entry for the recursive
6049      */
6050     fib_prefix_t pfx_1_1_1_2_s_32 = {
6051         .fp_len = 32,
6052         .fp_proto = FIB_PROTOCOL_IP4,
6053         .fp_addr = {
6054             .ip4.as_u32 = clib_host_to_net_u32(0x01010102),
6055         },
6056     };
6057     fib_test_lb_bucket_t l101_eos_o_10_10_10_1 = {
6058         .type = FT_LB_LABEL_O_ADJ,
6059         .label_o_adj = {
6060             .adj = ai_mpls_10_10_10_1,
6061             .label = 101,
6062             .eos = MPLS_EOS,
6063         },
6064     };
6065     mpls_label_t *l101 = NULL;
6066     vec_add1(l101, 101);
6067
6068     fei = fib_table_entry_update_one_path(fib_index,
6069                                           &pfx_1_1_1_2_s_32,
6070                                           FIB_SOURCE_API,
6071                                           FIB_ENTRY_FLAG_NONE,
6072                                           FIB_PROTOCOL_IP4,
6073                                           &nh_10_10_10_1,
6074                                           tm->hw[0]->sw_if_index,
6075                                           ~0, // invalid fib index
6076                                           1,
6077                                           l101,
6078                                           FIB_ROUTE_PATH_FLAG_NONE);
6079
6080     FIB_TEST(fib_test_validate_entry(fei,
6081                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6082                                      1,
6083                                      &l101_eos_o_10_10_10_1),
6084              "1.1.1.2/32 LB 1 buckets via: "
6085              "label 101 over 10.10.10.1");
6086
6087     dpo_id_t non_eos_1_1_1_2 = DPO_INVALID;
6088     fib_entry_contribute_forwarding(fib_table_lookup(fib_index,
6089                                                      &pfx_1_1_1_1_s_32),
6090                                     FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
6091                                     &non_eos_1_1_1_1);
6092     fib_entry_contribute_forwarding(fib_table_lookup(fib_index,
6093                                                      &pfx_1_1_1_2_s_32),
6094                                     FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
6095                                     &non_eos_1_1_1_2);
6096
6097     fib_test_lb_bucket_t l1601_eos_o_1_1_1_2 = {
6098         .type = FT_LB_LABEL_O_LB,
6099         .label_o_lb = {
6100             .lb = non_eos_1_1_1_2.dpoi_index,
6101             .label = 1601,
6102             .eos = MPLS_EOS,
6103         },
6104     };
6105     mpls_label_t *l1601 = NULL;
6106     vec_add1(l1601, 1601);
6107
6108     l1600_eos_o_1_1_1_1.label_o_lb.lb = non_eos_1_1_1_1.dpoi_index;
6109
6110     fei = fib_table_entry_path_add(fib_index,
6111                                    &pfx_2_2_2_2_s_32,
6112                                    FIB_SOURCE_API,
6113                                    FIB_ENTRY_FLAG_NONE,
6114                                    FIB_PROTOCOL_IP4,
6115                                    &pfx_1_1_1_2_s_32.fp_addr,
6116                                    ~0,
6117                                    fib_index,
6118                                    1,
6119                                    l1601,
6120                                    FIB_ROUTE_PATH_FLAG_NONE);
6121
6122     FIB_TEST(fib_test_validate_entry(fei, 
6123                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6124                                      2,
6125                                      &l1600_eos_o_1_1_1_1,
6126                                      &l1601_eos_o_1_1_1_2),
6127              "2.2.2.2/32 LB 2 buckets via: "
6128              "label 1600 via 1.1,1.1, "
6129              "label 16001 via 1.1.1.2");
6130
6131     /*
6132      * update the via-entry so it no longer has an imp-null path.
6133      * the LB for the recursive can use an imp-null
6134      */
6135     l_imp_null = NULL;
6136     vec_add1(l_imp_null, MPLS_IETF_IMPLICIT_NULL_LABEL);
6137
6138     fei = fib_table_entry_update_one_path(fib_index,
6139                                           &pfx_1_1_1_2_s_32,
6140                                           FIB_SOURCE_API,
6141                                           FIB_ENTRY_FLAG_NONE,
6142                                           FIB_PROTOCOL_IP4,
6143                                           &nh_10_10_11_1,
6144                                           tm->hw[1]->sw_if_index,
6145                                           ~0, // invalid fib index
6146                                           1,
6147                                           l_imp_null,
6148                                           FIB_ROUTE_PATH_FLAG_NONE);
6149
6150     FIB_TEST(fib_test_validate_entry(fei,
6151                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6152                                      1,
6153                                      &a_o_10_10_11_1),
6154              "1.1.1.2/32 LB 1 buckets via: "
6155              "adj 10.10.11.1");
6156  
6157     fei = fib_table_lookup(fib_index, &pfx_2_2_2_2_s_32);
6158     FIB_TEST(fib_test_validate_entry(fei, 
6159                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6160                                      2,
6161                                      &l1600_eos_o_1_1_1_1,
6162                                      &l1601_eos_o_1_1_1_2),
6163              "2.2.2.2/32 LB 2 buckets via: "
6164              "label 1600 via 1.1,1.1, "
6165              "label 16001 via 1.1.1.2");
6166
6167     /*
6168      * update the via-entry so it no longer has labelled paths.
6169      * the LB for the recursive should exclue this via form its LB
6170      */
6171     fei = fib_table_entry_update_one_path(fib_index,
6172                                           &pfx_1_1_1_2_s_32,
6173                                           FIB_SOURCE_API,
6174                                           FIB_ENTRY_FLAG_NONE,
6175                                           FIB_PROTOCOL_IP4,
6176                                           &nh_10_10_11_1,
6177                                           tm->hw[1]->sw_if_index,
6178                                           ~0, // invalid fib index
6179                                           1,
6180                                           NULL,
6181                                           FIB_ROUTE_PATH_FLAG_NONE);
6182
6183     FIB_TEST(fib_test_validate_entry(fei,
6184                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6185                                      1,
6186                                      &a_o_10_10_11_1),
6187              "1.1.1.2/32 LB 1 buckets via: "
6188              "adj 10.10.11.1");
6189  
6190     fei = fib_table_lookup(fib_index, &pfx_2_2_2_2_s_32);
6191     FIB_TEST(fib_test_validate_entry(fei, 
6192                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6193                                      1,
6194                                      &l1600_eos_o_1_1_1_1),
6195              "2.2.2.2/32 LB 1 buckets via: "
6196              "label 1600 via 1.1,1.1");
6197
6198     dpo_reset(&non_eos_1_1_1_1);
6199     dpo_reset(&non_eos_1_1_1_2);
6200
6201     /*
6202      * Add a recursive with no out-labels. We expect to use the IP of the via
6203      */
6204     fib_prefix_t pfx_2_2_2_3_s_32 = {
6205         .fp_len = 32,
6206         .fp_proto = FIB_PROTOCOL_IP4,
6207         .fp_addr = {
6208             .ip4.as_u32 = clib_host_to_net_u32(0x02020203),
6209         },
6210     };
6211     dpo_id_t ip_1_1_1_1 = DPO_INVALID;
6212
6213     fib_table_entry_update_one_path(fib_index,
6214                                     &pfx_2_2_2_3_s_32,
6215                                     FIB_SOURCE_API,
6216                                     FIB_ENTRY_FLAG_NONE,
6217                                     FIB_PROTOCOL_IP4,
6218                                     &pfx_1_1_1_1_s_32.fp_addr,
6219                                     ~0,
6220                                     fib_index,
6221                                     1,
6222                                     NULL,
6223                                     FIB_ROUTE_PATH_FLAG_NONE);
6224
6225     fib_entry_contribute_forwarding(fib_table_lookup(fib_index,
6226                                                      &pfx_1_1_1_1_s_32),
6227                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6228                                     &ip_1_1_1_1);
6229
6230     fib_test_lb_bucket_t ip_o_1_1_1_1 = {
6231         .type = FT_LB_O_LB,
6232         .lb = {
6233             .lb = ip_1_1_1_1.dpoi_index,
6234         },
6235     };
6236
6237     fei = fib_table_lookup(fib_index, &pfx_2_2_2_3_s_32);
6238     FIB_TEST(fib_test_validate_entry(fei, 
6239                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6240                                      1,
6241                                      &ip_o_1_1_1_1),
6242              "2.2.2.2.3/32 LB 1 buckets via: "
6243              "ip 1.1.1.1");
6244
6245     /*
6246      * Add a recursive with an imp-null out-label. 
6247      * We expect to use the IP of the via
6248      */
6249     fib_prefix_t pfx_2_2_2_4_s_32 = {
6250         .fp_len = 32,
6251         .fp_proto = FIB_PROTOCOL_IP4,
6252         .fp_addr = {
6253             .ip4.as_u32 = clib_host_to_net_u32(0x02020204),
6254         },
6255     };
6256
6257     fib_table_entry_update_one_path(fib_index,
6258                                     &pfx_2_2_2_4_s_32,
6259                                     FIB_SOURCE_API,
6260                                     FIB_ENTRY_FLAG_NONE,
6261                                     FIB_PROTOCOL_IP4,
6262                                     &pfx_1_1_1_1_s_32.fp_addr,
6263                                     ~0,
6264                                     fib_index,
6265                                     1,
6266                                     NULL,
6267                                     FIB_ROUTE_PATH_FLAG_NONE);
6268
6269     fei = fib_table_lookup(fib_index, &pfx_2_2_2_4_s_32);
6270     FIB_TEST(fib_test_validate_entry(fei, 
6271                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6272                                      1,
6273                                      &ip_o_1_1_1_1),
6274              "2.2.2.2.4/32 LB 1 buckets via: "
6275              "ip 1.1.1.1");
6276
6277     dpo_reset(&ip_1_1_1_1);
6278
6279     /*
6280      * Create an entry with a deep label stack
6281      */
6282     fib_prefix_t pfx_2_2_5_5_s_32 = {
6283         .fp_len = 32,
6284         .fp_proto = FIB_PROTOCOL_IP4,
6285         .fp_addr = {
6286             .ip4.as_u32 = clib_host_to_net_u32(0x02020505),
6287         },
6288     };
6289     fib_test_lb_bucket_t ls_eos_o_10_10_10_1 = {
6290         .type = FT_LB_LABEL_STACK_O_ADJ,
6291         .label_stack_o_adj = {
6292             .adj = ai_mpls_10_10_11_1,
6293             .label_stack_size = 8,
6294             .label_stack = {
6295                 200, 201, 202, 203, 204, 205, 206, 207
6296             },
6297             .eos = MPLS_EOS,
6298         },
6299     };
6300     mpls_label_t *label_stack = NULL;
6301     vec_validate(label_stack, 7);
6302     for (ii = 0; ii < 8; ii++)
6303     {
6304         label_stack[ii] = ii + 200;
6305     }
6306
6307     fei = fib_table_entry_update_one_path(fib_index,
6308                                           &pfx_2_2_5_5_s_32,
6309                                           FIB_SOURCE_API,
6310                                           FIB_ENTRY_FLAG_NONE,
6311                                           FIB_PROTOCOL_IP4,
6312                                           &nh_10_10_11_1,
6313                                           tm->hw[1]->sw_if_index,
6314                                           ~0, // invalid fib index
6315                                           1,
6316                                           label_stack,
6317                                           FIB_ROUTE_PATH_FLAG_NONE);
6318
6319     FIB_TEST(fib_test_validate_entry(fei,
6320                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6321                                      1,
6322                                      &ls_eos_o_10_10_10_1),
6323              "2.2.5.5/32 LB 1 buckets via: "
6324              "adj 10.10.11.1");
6325     fib_table_entry_delete_index(fei, FIB_SOURCE_API);
6326
6327     /*
6328      * cleanup
6329      */
6330     fib_table_entry_delete(fib_index,
6331                            &pfx_1_1_1_2_s_32,
6332                            FIB_SOURCE_API);
6333
6334     fei = fib_table_lookup(fib_index, &pfx_2_2_2_2_s_32);
6335     FIB_TEST(fib_test_validate_entry(fei, 
6336                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6337                                      1,
6338                                      &l1600_eos_o_1_1_1_1),
6339              "2.2.2.2/32 LB 1 buckets via: "
6340              "label 1600 via 1.1,1.1");
6341
6342     fib_table_entry_delete(fib_index,
6343                            &pfx_1_1_1_1_s_32,
6344                            FIB_SOURCE_API);
6345
6346     FIB_TEST(fib_test_validate_entry(fei, 
6347                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6348                                      1,
6349                                      &bucket_drop),
6350              "2.2.2.2/32 LB 1 buckets via: DROP");
6351
6352     fib_table_entry_delete(fib_index,
6353                            &pfx_2_2_2_2_s_32,
6354                            FIB_SOURCE_API);
6355     fib_table_entry_delete(fib_index,
6356                            &pfx_2_2_2_3_s_32,
6357                            FIB_SOURCE_API);
6358     fib_table_entry_delete(fib_index,
6359                            &pfx_2_2_2_4_s_32,
6360                            FIB_SOURCE_API);
6361
6362     adj_unlock(ai_mpls_10_10_10_1);
6363     adj_unlock(ai_mpls_10_10_11_2);
6364     adj_unlock(ai_v4_10_10_11_1);
6365     adj_unlock(ai_v4_10_10_11_2);
6366     adj_unlock(ai_mpls_10_10_11_1);
6367
6368     FIB_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d",
6369              adj_nbr_db_size());
6370
6371     local0_pfx.fp_len = 32;
6372     fib_table_entry_delete(fib_index,
6373                            &local0_pfx,
6374                            FIB_SOURCE_INTERFACE);
6375     local0_pfx.fp_len = 24;
6376     fib_table_entry_delete(fib_index,
6377                            &local0_pfx,
6378                            FIB_SOURCE_INTERFACE);
6379     local1_pfx.fp_len = 32;
6380     fib_table_entry_delete(fib_index,
6381                            &local1_pfx,
6382                            FIB_SOURCE_INTERFACE);
6383     local1_pfx.fp_len = 24;
6384     fib_table_entry_delete(fib_index,
6385                            &local1_pfx,
6386                            FIB_SOURCE_INTERFACE);
6387
6388     /*
6389      * +1 for the drop LB in the MPLS tables.
6390      */
6391     FIB_TEST(lb_count+1 == pool_elts(load_balance_pool),
6392              "Load-balance resources freed %d of %d",
6393              lb_count+1, pool_elts(load_balance_pool));
6394
6395     return (0);
6396 }
6397
6398 #define N_TEST_CHILDREN 4
6399 #define PARENT_INDEX 0
6400
6401 typedef struct fib_node_test_t_
6402 {
6403     fib_node_t node;
6404     u32 sibling;
6405     u32 index;
6406     fib_node_back_walk_ctx_t *ctxs;
6407     u32 destroyed;
6408 } fib_node_test_t;
6409
6410 static fib_node_test_t fib_test_nodes[N_TEST_CHILDREN+1];
6411
6412 #define PARENT() (&fib_test_nodes[PARENT_INDEX].node)
6413
6414 #define FOR_EACH_TEST_CHILD(_tc)                     \
6415     for (ii = 1, (_tc) = &fib_test_nodes[1];         \
6416          ii < N_TEST_CHILDREN+1;                     \
6417          ii++, (_tc) = &fib_test_nodes[ii])
6418
6419 static fib_node_t *
6420 fib_test_child_get_node (fib_node_index_t index)
6421 {
6422     return (&fib_test_nodes[index].node);
6423 }
6424
6425 static int fib_test_walk_spawns_walks;
6426
6427 static fib_node_back_walk_rc_t
6428 fib_test_child_back_walk_notify (fib_node_t *node,
6429                                  fib_node_back_walk_ctx_t *ctx)
6430 {
6431     fib_node_test_t *tc = (fib_node_test_t*) node;
6432
6433     vec_add1(tc->ctxs, *ctx);
6434
6435     if (1 == fib_test_walk_spawns_walks)
6436         fib_walk_sync(FIB_NODE_TYPE_TEST, tc->index, ctx);
6437     if (2 == fib_test_walk_spawns_walks)
6438         fib_walk_async(FIB_NODE_TYPE_TEST, tc->index,
6439                        FIB_WALK_PRIORITY_HIGH, ctx);
6440
6441     return (FIB_NODE_BACK_WALK_CONTINUE);
6442 }
6443
6444 static void
6445 fib_test_child_last_lock_gone (fib_node_t *node)
6446 {
6447     fib_node_test_t *tc = (fib_node_test_t *)node;
6448
6449     tc->destroyed = 1;
6450 }
6451
6452 /**
6453  * The FIB walk's graph node virtual function table
6454  */
6455 static const fib_node_vft_t fib_test_child_vft = {
6456     .fnv_get = fib_test_child_get_node,
6457     .fnv_last_lock = fib_test_child_last_lock_gone,
6458     .fnv_back_walk = fib_test_child_back_walk_notify,
6459 };
6460
6461 /*
6462  * the function (that should have been static but isn't so I can do this)
6463  * that processes the walk from the async queue,
6464  */
6465 f64 fib_walk_process_queues(vlib_main_t * vm,
6466                             const f64 quota);
6467 u32 fib_walk_queue_get_size(fib_walk_priority_t prio);
6468
6469 static int
6470 fib_test_walk (void)
6471 {
6472     fib_node_back_walk_ctx_t high_ctx = {}, low_ctx = {};
6473     fib_node_test_t *tc;
6474     vlib_main_t *vm;
6475     u32 ii;
6476
6477     vm = vlib_get_main();
6478     fib_node_register_type(FIB_NODE_TYPE_TEST, &fib_test_child_vft);
6479
6480     /*
6481      * init a fake node on which we will add children
6482      */
6483     fib_node_init(&fib_test_nodes[PARENT_INDEX].node,
6484                   FIB_NODE_TYPE_TEST);
6485
6486     FOR_EACH_TEST_CHILD(tc)
6487     {
6488         fib_node_init(&tc->node, FIB_NODE_TYPE_TEST);
6489         fib_node_lock(&tc->node);
6490         tc->ctxs = NULL;
6491         tc->index = ii;
6492         tc->sibling = fib_node_child_add(FIB_NODE_TYPE_TEST,
6493                                          PARENT_INDEX,
6494                                          FIB_NODE_TYPE_TEST, ii);
6495     }
6496
6497     /*
6498      * enqueue a walk across the parents children.
6499      */
6500     high_ctx.fnbw_reason = FIB_NODE_BW_REASON_FLAG_RESOLVE;
6501
6502     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6503                    FIB_WALK_PRIORITY_HIGH, &high_ctx);
6504     FIB_TEST(N_TEST_CHILDREN+1 == fib_node_list_get_size(PARENT()->fn_children),
6505              "Parent has %d children pre-walk",
6506              fib_node_list_get_size(PARENT()->fn_children));
6507
6508     /*
6509      * give the walk a large amount of time so it gets to the end
6510      */
6511     fib_walk_process_queues(vm, 1);
6512
6513     FOR_EACH_TEST_CHILD(tc)
6514     {
6515         FIB_TEST(1 == vec_len(tc->ctxs),
6516                  "%d child visitsed %d times",
6517                  ii, vec_len(tc->ctxs));
6518         vec_free(tc->ctxs);
6519     }
6520     FIB_TEST(0 == fib_walk_queue_get_size(FIB_WALK_PRIORITY_HIGH),
6521              "Queue is empty post walk");
6522     FIB_TEST(N_TEST_CHILDREN == fib_node_list_get_size(PARENT()->fn_children),
6523              "Parent has %d children post walk",
6524              fib_node_list_get_size(PARENT()->fn_children));
6525
6526     /*
6527      * walk again. should be no increase in the number of visits, since
6528      * the walk will have terminated.
6529      */
6530     fib_walk_process_queues(vm, 1);
6531
6532     FOR_EACH_TEST_CHILD(tc)
6533     {
6534         FIB_TEST(0 == vec_len(tc->ctxs),
6535                  "%d child visitsed %d times",
6536                  ii, vec_len(tc->ctxs));
6537     }
6538
6539     /*
6540      * schedule a low and hig priority walk. expect the high to be performed
6541      * before the low.
6542      * schedule the high prio walk first so that it is further from the head
6543      * of the dependency list. that way it won't merge with the low one.
6544      */
6545     high_ctx.fnbw_reason = FIB_NODE_BW_REASON_FLAG_RESOLVE;
6546     low_ctx.fnbw_reason = FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE;
6547
6548     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6549                    FIB_WALK_PRIORITY_HIGH, &high_ctx);
6550     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6551                    FIB_WALK_PRIORITY_LOW, &low_ctx);
6552
6553     fib_walk_process_queues(vm, 1);
6554
6555     FOR_EACH_TEST_CHILD(tc)
6556     {
6557         FIB_TEST(high_ctx.fnbw_reason == tc->ctxs[0].fnbw_reason,
6558                  "%d child visitsed by high prio walk", ii);
6559         FIB_TEST(low_ctx.fnbw_reason  == tc->ctxs[1].fnbw_reason,
6560                  "%d child visitsed by low prio walk", ii);
6561         vec_free(tc->ctxs);
6562     }
6563     FIB_TEST(0 == fib_walk_queue_get_size(FIB_WALK_PRIORITY_HIGH),
6564              "Queue is empty post prio walk");
6565     FIB_TEST(N_TEST_CHILDREN == fib_node_list_get_size(PARENT()->fn_children),
6566              "Parent has %d children post prio walk",
6567              fib_node_list_get_size(PARENT()->fn_children));
6568
6569     /*
6570      * schedule 2 walks of the same priority that can be megred.
6571      * expect that each child is thus visited only once.
6572      */
6573     high_ctx.fnbw_reason = FIB_NODE_BW_REASON_FLAG_RESOLVE;
6574     low_ctx.fnbw_reason  = FIB_NODE_BW_REASON_FLAG_RESOLVE;
6575
6576     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6577                    FIB_WALK_PRIORITY_HIGH, &high_ctx);
6578     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6579                    FIB_WALK_PRIORITY_HIGH, &low_ctx);
6580
6581     fib_walk_process_queues(vm, 1);
6582
6583     FOR_EACH_TEST_CHILD(tc)
6584     {
6585         FIB_TEST(1 == vec_len(tc->ctxs),
6586                  "%d child visitsed %d times during merge walk",
6587                  ii, vec_len(tc->ctxs));
6588         vec_free(tc->ctxs);
6589     }
6590     FIB_TEST(0 == fib_walk_queue_get_size(FIB_WALK_PRIORITY_HIGH),
6591              "Queue is empty post merge walk");
6592     FIB_TEST(N_TEST_CHILDREN == fib_node_list_get_size(PARENT()->fn_children),
6593              "Parent has %d children post merge walk",
6594              fib_node_list_get_size(PARENT()->fn_children));
6595
6596     /*
6597      * schedule 2 walks of the same priority that cannot be megred.
6598      * expect that each child is thus visited twice and in the order
6599      * in which the walks were scheduled.
6600      */
6601     high_ctx.fnbw_reason = FIB_NODE_BW_REASON_FLAG_RESOLVE;
6602     low_ctx.fnbw_reason  = FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE;
6603
6604     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6605                    FIB_WALK_PRIORITY_HIGH, &high_ctx);
6606     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6607                    FIB_WALK_PRIORITY_HIGH, &low_ctx);
6608
6609     fib_walk_process_queues(vm, 1);
6610
6611     FOR_EACH_TEST_CHILD(tc)
6612     {
6613         FIB_TEST(high_ctx.fnbw_reason == tc->ctxs[0].fnbw_reason,
6614                  "%d child visitsed by high prio walk", ii);
6615         FIB_TEST(low_ctx.fnbw_reason  == tc->ctxs[1].fnbw_reason,
6616                  "%d child visitsed by low prio walk", ii);
6617         vec_free(tc->ctxs);
6618     }
6619     FIB_TEST(0 == fib_walk_queue_get_size(FIB_WALK_PRIORITY_HIGH),
6620              "Queue is empty post no-merge walk");
6621     FIB_TEST(N_TEST_CHILDREN == fib_node_list_get_size(PARENT()->fn_children),
6622              "Parent has %d children post no-merge walk",
6623              fib_node_list_get_size(PARENT()->fn_children));
6624
6625     /*
6626      * schedule a walk that makes one one child progress.
6627      * we do this by giving the queue draining process zero
6628      * time quanta. it's a do..while loop, so it does something.
6629      */
6630     high_ctx.fnbw_reason = FIB_NODE_BW_REASON_FLAG_RESOLVE;
6631
6632     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6633                    FIB_WALK_PRIORITY_HIGH, &high_ctx);
6634     fib_walk_process_queues(vm, 0);
6635
6636     FOR_EACH_TEST_CHILD(tc)
6637     {
6638         if (ii == N_TEST_CHILDREN)
6639         {
6640             FIB_TEST(1 == vec_len(tc->ctxs),
6641                      "%d child visitsed %d times in zero quanta walk",
6642                      ii, vec_len(tc->ctxs));
6643         }
6644         else
6645         {
6646             FIB_TEST(0 == vec_len(tc->ctxs),
6647                      "%d child visitsed %d times in 0 quanta walk",
6648                      ii, vec_len(tc->ctxs));
6649         }
6650     }
6651     FIB_TEST(1 == fib_walk_queue_get_size(FIB_WALK_PRIORITY_HIGH),
6652              "Queue is not empty post zero quanta walk");
6653     FIB_TEST(N_TEST_CHILDREN+1 == fib_node_list_get_size(PARENT()->fn_children),
6654              "Parent has %d children post zero qunta walk",
6655              fib_node_list_get_size(PARENT()->fn_children));
6656
6657     /*
6658      * another one step
6659      */
6660     fib_walk_process_queues(vm, 0);
6661
6662     FOR_EACH_TEST_CHILD(tc)
6663     {
6664         if (ii >= N_TEST_CHILDREN-1)
6665         {
6666             FIB_TEST(1 == vec_len(tc->ctxs),
6667                      "%d child visitsed %d times in 2nd zero quanta walk",
6668                      ii, vec_len(tc->ctxs));
6669         }
6670         else
6671         {
6672             FIB_TEST(0 == vec_len(tc->ctxs),
6673                      "%d child visitsed %d times in 2nd 0 quanta walk",
6674                      ii, vec_len(tc->ctxs));
6675         }
6676     }
6677     FIB_TEST(1 == fib_walk_queue_get_size(FIB_WALK_PRIORITY_HIGH),
6678              "Queue is not empty post zero quanta walk");
6679     FIB_TEST(N_TEST_CHILDREN+1 == fib_node_list_get_size(PARENT()->fn_children),
6680              "Parent has %d children post zero qunta walk",
6681              fib_node_list_get_size(PARENT()->fn_children));
6682
6683     /*
6684      * schedule another walk that will catch-up and merge.
6685      */
6686     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6687                    FIB_WALK_PRIORITY_HIGH, &high_ctx);
6688     fib_walk_process_queues(vm, 1);
6689
6690     FOR_EACH_TEST_CHILD(tc)
6691     {
6692         if (ii >= N_TEST_CHILDREN-1)
6693         {
6694             FIB_TEST(2 == vec_len(tc->ctxs),
6695                      "%d child visitsed %d times in 2nd zero quanta merge walk",
6696                      ii, vec_len(tc->ctxs));
6697             vec_free(tc->ctxs);
6698         }
6699         else
6700         {
6701             FIB_TEST(1 == vec_len(tc->ctxs),
6702                      "%d child visitsed %d times in 2nd 0 quanta merge walk",
6703                      ii, vec_len(tc->ctxs));
6704             vec_free(tc->ctxs);
6705         }
6706     }
6707     FIB_TEST(0 == fib_walk_queue_get_size(FIB_WALK_PRIORITY_HIGH),
6708              "Queue is not empty post 2nd zero quanta merge walk");
6709     FIB_TEST(N_TEST_CHILDREN == fib_node_list_get_size(PARENT()->fn_children),
6710              "Parent has %d children post 2nd zero qunta merge walk",
6711              fib_node_list_get_size(PARENT()->fn_children));
6712
6713     /*
6714      * park a async walk in the middle of the list, then have an sync walk catch
6715      * it. same expectations as async catches async.
6716      */
6717     high_ctx.fnbw_reason = FIB_NODE_BW_REASON_FLAG_RESOLVE;
6718
6719     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6720                    FIB_WALK_PRIORITY_HIGH, &high_ctx);
6721
6722     fib_walk_process_queues(vm, 0);
6723     fib_walk_process_queues(vm, 0);
6724
6725     fib_walk_sync(FIB_NODE_TYPE_TEST, PARENT_INDEX, &high_ctx);
6726
6727     FOR_EACH_TEST_CHILD(tc)
6728     {
6729         if (ii >= N_TEST_CHILDREN-1)
6730         {
6731             FIB_TEST(2 == vec_len(tc->ctxs),
6732                      "%d child visitsed %d times in sync catches async walk",
6733                      ii, vec_len(tc->ctxs));
6734             vec_free(tc->ctxs);
6735         }
6736         else
6737         {
6738             FIB_TEST(1 == vec_len(tc->ctxs),
6739                      "%d child visitsed %d times in sync catches async walk",
6740                      ii, vec_len(tc->ctxs));
6741             vec_free(tc->ctxs);
6742         }
6743     }
6744     FIB_TEST(0 == fib_walk_queue_get_size(FIB_WALK_PRIORITY_HIGH),
6745              "Queue is not empty post 2nd zero quanta merge walk");
6746     FIB_TEST(N_TEST_CHILDREN == fib_node_list_get_size(PARENT()->fn_children),
6747              "Parent has %d children post 2nd zero qunta merge walk",
6748              fib_node_list_get_size(PARENT()->fn_children));
6749
6750     /*
6751      * make the parent a child of one of its children, thus inducing a routing loop.
6752      */
6753     fib_test_nodes[PARENT_INDEX].sibling =
6754         fib_node_child_add(FIB_NODE_TYPE_TEST,
6755                            1, // the first child
6756                            FIB_NODE_TYPE_TEST,
6757                            PARENT_INDEX);
6758
6759     /*
6760      * execute a sync walk from the parent. each child visited spawns more sync
6761      * walks. we expect the walk to terminate.
6762      */
6763     fib_test_walk_spawns_walks = 1;
6764
6765     fib_walk_sync(FIB_NODE_TYPE_TEST, PARENT_INDEX, &high_ctx);
6766
6767     FOR_EACH_TEST_CHILD(tc)
6768     {
6769         /*
6770          * child 1 - which is last in the list - has the loop.
6771          * the other children a re thus visitsed first. the we meet
6772          * child 1. we go round the loop again, visting the other children.
6773          * then we meet the walk in the dep list and bail. child 1 is not visitsed
6774          * again.
6775          */
6776         if (1 == ii)
6777         {
6778             FIB_TEST(1 == vec_len(tc->ctxs),
6779                      "child %d visitsed %d times during looped sync walk",
6780                      ii, vec_len(tc->ctxs));
6781         }
6782         else
6783         {
6784             FIB_TEST(2 == vec_len(tc->ctxs),
6785                      "child %d visitsed %d times during looped sync walk",
6786                      ii, vec_len(tc->ctxs));
6787         }
6788         vec_free(tc->ctxs);
6789     }
6790     FIB_TEST(N_TEST_CHILDREN == fib_node_list_get_size(PARENT()->fn_children),
6791              "Parent has %d children post sync loop walk",
6792              fib_node_list_get_size(PARENT()->fn_children));
6793
6794     /*
6795      * the walk doesn't reach the max depth because the infra knows that sync
6796      * meets sync implies a loop and bails early.
6797      */
6798     FIB_TEST(high_ctx.fnbw_depth == 9,
6799              "Walk context depth %d post sync loop walk",
6800              high_ctx.fnbw_depth);
6801
6802     /*
6803      * execute an async walk of the graph loop, with each child spawns sync walks
6804      */
6805     high_ctx.fnbw_depth = 0;
6806     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6807                    FIB_WALK_PRIORITY_HIGH, &high_ctx);
6808
6809     fib_walk_process_queues(vm, 1);
6810
6811     FOR_EACH_TEST_CHILD(tc)
6812     {
6813         /*
6814          * we don't really care how many times the children are visisted, as long as
6815          * it is more than once.
6816          */
6817         FIB_TEST(1 <= vec_len(tc->ctxs),
6818                  "child %d visitsed %d times during looped aync spawns sync walk",
6819                  ii, vec_len(tc->ctxs));
6820         vec_free(tc->ctxs);
6821     }
6822
6823     /*
6824      * execute an async walk of the graph loop, with each child spawns async walks
6825      */
6826     fib_test_walk_spawns_walks = 2;
6827     high_ctx.fnbw_depth = 0;
6828     fib_walk_async(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6829                    FIB_WALK_PRIORITY_HIGH, &high_ctx);
6830
6831     fib_walk_process_queues(vm, 1);
6832
6833     FOR_EACH_TEST_CHILD(tc)
6834     {
6835         /*
6836          * we don't really care how many times the children are visisted, as long as
6837          * it is more than once.
6838          */
6839         FIB_TEST(1 <= vec_len(tc->ctxs),
6840                  "child %d visitsed %d times during looped async spawns async walk",
6841                  ii, vec_len(tc->ctxs));
6842                 vec_free(tc->ctxs);
6843     }
6844
6845
6846     fib_node_child_remove(FIB_NODE_TYPE_TEST,
6847                           1, // the first child
6848                           fib_test_nodes[PARENT_INDEX].sibling);
6849
6850     /*
6851      * cleanup
6852      */
6853     FOR_EACH_TEST_CHILD(tc)
6854     {
6855         fib_node_child_remove(FIB_NODE_TYPE_TEST, PARENT_INDEX,
6856                               tc->sibling);
6857         fib_node_deinit(&tc->node);
6858         fib_node_unlock(&tc->node);
6859     }
6860     fib_node_deinit(PARENT());
6861
6862     /*
6863      * The parent will be destroyed when the last lock on it goes.
6864      * this test ensures all the walk objects are unlocking it.
6865      */
6866     FIB_TEST((1 == fib_test_nodes[PARENT_INDEX].destroyed),
6867              "Parent was destroyed");
6868
6869     return (0);
6870 }
6871
6872 /*
6873  * declaration of the otherwise static callback functions
6874  */
6875 void fib_bfd_notify (bfd_listen_event_e event,
6876                      const bfd_session_t *session);
6877 void adj_bfd_notify (bfd_listen_event_e event,
6878                      const bfd_session_t *session);
6879
6880 /**
6881  * Test BFD session interaction with FIB
6882  */
6883 static int
6884 fib_test_bfd (void)
6885 {
6886     fib_node_index_t fei;
6887     test_main_t *tm;
6888     int n_feis;
6889
6890     /* via 10.10.10.1 */
6891     ip46_address_t nh_10_10_10_1 = {
6892         .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a01),
6893     };
6894     /* via 10.10.10.2 */
6895     ip46_address_t nh_10_10_10_2 = {
6896         .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a02),
6897     };
6898     /* via 10.10.10.10 */
6899     ip46_address_t nh_10_10_10_10 = {
6900         .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a0a),
6901     };
6902     n_feis = fib_entry_pool_size();
6903
6904     tm = &test_main;
6905
6906     /*
6907      * add interface routes. we'll assume this works. it's tested elsewhere
6908      */
6909     fib_prefix_t pfx_10_10_10_10_s_24 = {
6910         .fp_len = 24,
6911         .fp_proto = FIB_PROTOCOL_IP4,
6912         .fp_addr = nh_10_10_10_10,
6913     };
6914
6915     fib_table_entry_update_one_path(0, &pfx_10_10_10_10_s_24,
6916                                     FIB_SOURCE_INTERFACE,
6917                                     (FIB_ENTRY_FLAG_CONNECTED |
6918                                      FIB_ENTRY_FLAG_ATTACHED),
6919                                     FIB_PROTOCOL_IP4,
6920                                     NULL,
6921                                     tm->hw[0]->sw_if_index,
6922                                     ~0, // invalid fib index
6923                                     1, // weight
6924                                     NULL,
6925                                     FIB_ROUTE_PATH_FLAG_NONE);
6926
6927     fib_prefix_t pfx_10_10_10_10_s_32 = {
6928         .fp_len = 32,
6929         .fp_proto = FIB_PROTOCOL_IP4,
6930         .fp_addr = nh_10_10_10_10,
6931     };
6932     fib_table_entry_update_one_path(0, &pfx_10_10_10_10_s_32,
6933                                     FIB_SOURCE_INTERFACE,
6934                                     (FIB_ENTRY_FLAG_CONNECTED |
6935                                      FIB_ENTRY_FLAG_LOCAL),
6936                                     FIB_PROTOCOL_IP4,
6937                                     NULL,
6938                                     tm->hw[0]->sw_if_index,
6939                                     ~0, // invalid fib index
6940                                     1, // weight
6941                                     NULL,
6942                                     FIB_ROUTE_PATH_FLAG_NONE);
6943
6944     /*
6945      * A BFD session via a neighbour we do not yet know
6946      */
6947     bfd_session_t bfd_10_10_10_1 = {
6948         .udp = {
6949             .key = {
6950                 .fib_index = 0,
6951                 .peer_addr = nh_10_10_10_1,
6952             },
6953         },
6954         .hop_type = BFD_HOP_TYPE_MULTI,
6955         .local_state = BFD_STATE_init,
6956     };
6957
6958     fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
6959
6960     /*
6961      * A new entry will be created that forwards via the adj
6962      */
6963     adj_index_t ai_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
6964                                                     VNET_LINK_IP4,
6965                                                     &nh_10_10_10_1,
6966                                                     tm->hw[0]->sw_if_index);
6967     fib_prefix_t pfx_10_10_10_1_s_32 = {
6968         .fp_addr = nh_10_10_10_1,
6969         .fp_len = 32,
6970         .fp_proto = FIB_PROTOCOL_IP4,
6971     };
6972     fib_test_lb_bucket_t adj_o_10_10_10_1 = {
6973         .type = FT_LB_ADJ,
6974         .adj = {
6975             .adj = ai_10_10_10_1,
6976         },
6977     };
6978
6979     fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32);
6980     FIB_TEST(fib_test_validate_entry(fei,
6981                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
6982                                      1,
6983                                      &adj_o_10_10_10_1),
6984              "BFD sourced %U via %U",
6985              format_fib_prefix, &pfx_10_10_10_1_s_32,
6986              format_ip_adjacency, ai_10_10_10_1, FORMAT_IP_ADJACENCY_NONE);
6987
6988     /*
6989      * Delete the BFD session. Expect the fib_entry to be removed
6990      */
6991     fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
6992
6993     fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32);
6994     FIB_TEST(FIB_NODE_INDEX_INVALID == fei,
6995              "BFD sourced %U removed",
6996              format_fib_prefix, &pfx_10_10_10_1_s_32);
6997
6998     /*
6999      * Add the BFD source back
7000      */
7001     fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
7002
7003     /*
7004      * source the entry via the ADJ fib
7005      */
7006     fei = fib_table_entry_update_one_path(0,
7007                                           &pfx_10_10_10_1_s_32,
7008                                           FIB_SOURCE_ADJ,
7009                                           FIB_ENTRY_FLAG_ATTACHED,
7010                                           FIB_PROTOCOL_IP4,
7011                                           &nh_10_10_10_1,
7012                                           tm->hw[0]->sw_if_index,
7013                                           ~0, // invalid fib index
7014                                           1,
7015                                           NULL,
7016                                           FIB_ROUTE_PATH_FLAG_NONE);
7017
7018     /*
7019      * Delete the BFD session. Expect the fib_entry to remain
7020      */
7021     fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
7022
7023     fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32);
7024     FIB_TEST(fib_test_validate_entry(fei,
7025                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7026                                      1,
7027                                      &adj_o_10_10_10_1),
7028              "BFD sourced %U remains via %U",
7029              format_fib_prefix, &pfx_10_10_10_1_s_32,
7030              format_ip_adjacency, ai_10_10_10_1, FORMAT_IP_ADJACENCY_NONE);
7031
7032     /*
7033      * Add the BFD source back
7034      */
7035     fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
7036
7037     /*
7038      * Create another ADJ FIB
7039      */
7040     fib_prefix_t pfx_10_10_10_2_s_32 = {
7041         .fp_addr = nh_10_10_10_2,
7042         .fp_len = 32,
7043         .fp_proto = FIB_PROTOCOL_IP4,
7044     };
7045     fib_table_entry_update_one_path(0,
7046                                     &pfx_10_10_10_2_s_32,
7047                                     FIB_SOURCE_ADJ,
7048                                     FIB_ENTRY_FLAG_ATTACHED,
7049                                     FIB_PROTOCOL_IP4,
7050                                     &nh_10_10_10_2,
7051                                     tm->hw[0]->sw_if_index,
7052                                     ~0, // invalid fib index
7053                                     1,
7054                                     NULL,
7055                                     FIB_ROUTE_PATH_FLAG_NONE);
7056     /*
7057      * A BFD session for the new ADJ FIB
7058      */
7059     bfd_session_t bfd_10_10_10_2 = {
7060         .udp = {
7061             .key = {
7062                 .fib_index = 0,
7063                 .peer_addr = nh_10_10_10_2,
7064             },
7065         },
7066         .hop_type = BFD_HOP_TYPE_MULTI,
7067         .local_state = BFD_STATE_init,
7068     };
7069
7070     fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_2);
7071
7072     /*
7073      * remove the adj-fib source whilst the session is present
7074      * then add it back
7075      */
7076     fib_table_entry_delete(0, &pfx_10_10_10_2_s_32, FIB_SOURCE_ADJ);
7077     fib_table_entry_update_one_path(0,
7078                                     &pfx_10_10_10_2_s_32,
7079                                     FIB_SOURCE_ADJ,
7080                                     FIB_ENTRY_FLAG_ATTACHED,
7081                                     FIB_PROTOCOL_IP4,
7082                                     &nh_10_10_10_2,
7083                                     tm->hw[0]->sw_if_index,
7084                                     ~0, // invalid fib index
7085                                     1,
7086                                     NULL,
7087                                     FIB_ROUTE_PATH_FLAG_NONE);
7088
7089     /*
7090      * Before adding a recursive via the BFD tracked ADJ-FIBs,
7091      * bring one of the sessions UP, leave the other down
7092      */
7093     bfd_10_10_10_1.local_state = BFD_STATE_up;
7094     fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
7095     bfd_10_10_10_2.local_state = BFD_STATE_down;
7096     fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_2);
7097
7098     /*
7099      * A recursive prefix via both of the ADJ FIBs
7100      */
7101     fib_prefix_t pfx_200_0_0_0_s_24 = {
7102         .fp_proto = FIB_PROTOCOL_IP4,
7103         .fp_len = 32,
7104         .fp_addr = {
7105             .ip4.as_u32 = clib_host_to_net_u32(0xc8000000),
7106         },
7107     };
7108     const dpo_id_t *dpo_10_10_10_1, *dpo_10_10_10_2;
7109
7110     dpo_10_10_10_1 =
7111         fib_entry_contribute_ip_forwarding(
7112             fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32));
7113     dpo_10_10_10_2 =
7114         fib_entry_contribute_ip_forwarding(
7115             fib_table_lookup_exact_match(0, &pfx_10_10_10_2_s_32));
7116
7117     fib_test_lb_bucket_t lb_o_10_10_10_1 = {
7118         .type = FT_LB_O_LB,
7119         .lb = {
7120             .lb = dpo_10_10_10_1->dpoi_index,
7121         },
7122     };
7123     fib_test_lb_bucket_t lb_o_10_10_10_2 = {
7124         .type = FT_LB_O_LB,
7125         .lb = {
7126             .lb = dpo_10_10_10_2->dpoi_index,
7127         },
7128     };
7129
7130     /*
7131      * A prefix via the adj-fib that is BFD down => DROP
7132      */
7133     fei = fib_table_entry_path_add(0,
7134                                    &pfx_200_0_0_0_s_24,
7135                                    FIB_SOURCE_API,
7136                                    FIB_ENTRY_FLAG_NONE,
7137                                    FIB_PROTOCOL_IP4,
7138                                    &nh_10_10_10_2,
7139                                    ~0, // recursive
7140                                    0, // default fib index
7141                                    1,
7142                                    NULL,
7143                                    FIB_ROUTE_PATH_FLAG_NONE);
7144     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
7145              "%U resolves via drop",
7146              format_fib_prefix, &pfx_200_0_0_0_s_24);
7147
7148     /*
7149      * add a path via the UP BFD adj-fib.
7150      *  we expect that the DOWN BFD ADJ FIB is not used.
7151      */
7152     fei = fib_table_entry_path_add(0,
7153                                    &pfx_200_0_0_0_s_24,
7154                                    FIB_SOURCE_API,
7155                                    FIB_ENTRY_FLAG_NONE,
7156                                    FIB_PROTOCOL_IP4,
7157                                    &nh_10_10_10_1,
7158                                    ~0, // recursive
7159                                    0, // default fib index
7160                                    1,
7161                                    NULL,
7162                                    FIB_ROUTE_PATH_FLAG_NONE);
7163
7164     FIB_TEST(fib_test_validate_entry(fei,
7165                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7166                                      1,
7167                                      &lb_o_10_10_10_1),
7168              "Recursive %U only UP BFD adj-fibs",
7169              format_fib_prefix, &pfx_200_0_0_0_s_24);
7170
7171     /*
7172      * Send a BFD state change to UP - both sessions are now up
7173      *  the recursive prefix should LB over both
7174      */
7175     bfd_10_10_10_2.local_state = BFD_STATE_up;
7176     fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_2);
7177
7178
7179     FIB_TEST(fib_test_validate_entry(fei,
7180                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7181                                      2,
7182                                      &lb_o_10_10_10_1,
7183                                      &lb_o_10_10_10_2),
7184              "Recursive %U via both UP BFD adj-fibs",
7185              format_fib_prefix, &pfx_200_0_0_0_s_24);
7186
7187     /*
7188      * Send a BFD state change to DOWN
7189      *  the recursive prefix should exclude the down
7190      */
7191     bfd_10_10_10_2.local_state = BFD_STATE_down;
7192     fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_2);
7193
7194
7195     FIB_TEST(fib_test_validate_entry(fei,
7196                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7197                                      1,
7198                                      &lb_o_10_10_10_1),
7199              "Recursive %U via only UP",
7200              format_fib_prefix, &pfx_200_0_0_0_s_24);
7201
7202     /*
7203      * Delete the BFD session while it is in the DOWN state.
7204      *  FIB should consider the entry's state as back up
7205      */
7206     fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_2);
7207
7208     FIB_TEST(fib_test_validate_entry(fei,
7209                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7210                                      2,
7211                                      &lb_o_10_10_10_1,
7212                                      &lb_o_10_10_10_2),
7213              "Recursive %U via both UP BFD adj-fibs post down session delete",
7214              format_fib_prefix, &pfx_200_0_0_0_s_24);
7215
7216     /*
7217      * Delete the BFD other session while it is in the UP state.
7218      */
7219     fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
7220
7221     FIB_TEST(fib_test_validate_entry(fei,
7222                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7223                                      2,
7224                                      &lb_o_10_10_10_1,
7225                                      &lb_o_10_10_10_2),
7226              "Recursive %U via both UP BFD adj-fibs post up session delete",
7227              format_fib_prefix, &pfx_200_0_0_0_s_24);
7228
7229     /*
7230      * cleaup
7231      */
7232     fib_table_entry_delete(0, &pfx_200_0_0_0_s_24, FIB_SOURCE_API);
7233     fib_table_entry_delete(0, &pfx_10_10_10_1_s_32, FIB_SOURCE_ADJ);
7234     fib_table_entry_delete(0, &pfx_10_10_10_2_s_32, FIB_SOURCE_ADJ);
7235
7236     fib_table_entry_delete(0, &pfx_10_10_10_10_s_32, FIB_SOURCE_INTERFACE);
7237     fib_table_entry_delete(0, &pfx_10_10_10_10_s_24, FIB_SOURCE_INTERFACE);
7238
7239     adj_unlock(ai_10_10_10_1);
7240      /*
7241      * test no-one left behind
7242      */
7243     FIB_TEST((n_feis == fib_entry_pool_size()), "Entries gone");
7244     FIB_TEST(0 == adj_nbr_db_size(), "All adjacencies removed");
7245
7246     /*
7247      * Single-hop BFD tests
7248      */
7249     bfd_10_10_10_1.hop_type = BFD_HOP_TYPE_SINGLE;
7250     bfd_10_10_10_1.udp.key.sw_if_index = tm->hw[0]->sw_if_index;
7251
7252     adj_bfd_notify(BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
7253
7254     ai_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
7255                                         VNET_LINK_IP4,
7256                                         &nh_10_10_10_1,
7257                                         tm->hw[0]->sw_if_index);
7258     /*
7259      * whilst the BFD session is not signalled, the adj is up
7260      */
7261     FIB_TEST(adj_is_up(ai_10_10_10_1), "Adj state up on uninit session");
7262
7263     /*
7264      * bring the BFD session up
7265      */
7266     bfd_10_10_10_1.local_state = BFD_STATE_up;
7267     adj_bfd_notify(BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
7268     FIB_TEST(adj_is_up(ai_10_10_10_1), "Adj state up on UP session");
7269
7270     /*
7271      * bring the BFD session down
7272      */
7273     bfd_10_10_10_1.local_state = BFD_STATE_down;
7274     adj_bfd_notify(BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
7275     FIB_TEST(!adj_is_up(ai_10_10_10_1), "Adj state down on DOWN session");
7276
7277
7278     /*
7279      * add an attached next hop FIB entry via the down adj
7280      */
7281     fib_prefix_t pfx_5_5_5_5_s_32 = {
7282         .fp_addr = {
7283             .ip4 = {
7284                 .as_u32 = clib_host_to_net_u32(0x05050505),
7285             },
7286         },
7287         .fp_len = 32,
7288         .fp_proto = FIB_PROTOCOL_IP4,
7289     };
7290
7291     fei = fib_table_entry_path_add(0,
7292                                    &pfx_5_5_5_5_s_32,
7293                                    FIB_SOURCE_CLI,
7294                                    FIB_ENTRY_FLAG_NONE,
7295                                    FIB_PROTOCOL_IP4,
7296                                    &nh_10_10_10_1,
7297                                    tm->hw[0]->sw_if_index,
7298                                    ~0, // invalid fib index
7299                                    1,
7300                                    NULL,
7301                                    FIB_ROUTE_PATH_FLAG_NONE);
7302     FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
7303              "%U resolves via drop",
7304              format_fib_prefix, &pfx_5_5_5_5_s_32);
7305
7306     /*
7307      * Add a path via an ADJ that is up
7308      */
7309     adj_index_t ai_10_10_10_2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
7310                                                     VNET_LINK_IP4,
7311                                                     &nh_10_10_10_2,
7312                                                     tm->hw[0]->sw_if_index);
7313
7314     fib_test_lb_bucket_t adj_o_10_10_10_2 = {
7315         .type = FT_LB_ADJ,
7316         .adj = {
7317             .adj = ai_10_10_10_2,
7318         },
7319     };
7320     adj_o_10_10_10_1.adj.adj = ai_10_10_10_1;
7321
7322     fei = fib_table_entry_path_add(0,
7323                                    &pfx_5_5_5_5_s_32,
7324                                    FIB_SOURCE_CLI,
7325                                    FIB_ENTRY_FLAG_NONE,
7326                                    FIB_PROTOCOL_IP4,
7327                                    &nh_10_10_10_2,
7328                                    tm->hw[0]->sw_if_index,
7329                                    ~0, // invalid fib index
7330                                    1,
7331                                    NULL,
7332                                    FIB_ROUTE_PATH_FLAG_NONE);
7333
7334     FIB_TEST(fib_test_validate_entry(fei,
7335                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7336                                      1,
7337                                      &adj_o_10_10_10_2),
7338              "BFD sourced %U via %U",
7339              format_fib_prefix, &pfx_5_5_5_5_s_32,
7340              format_ip_adjacency, ai_10_10_10_2, FORMAT_IP_ADJACENCY_NONE);
7341
7342     /*
7343      * Bring up the down session - should now LB
7344      */
7345     bfd_10_10_10_1.local_state = BFD_STATE_up;
7346     adj_bfd_notify(BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
7347     FIB_TEST(fib_test_validate_entry(fei,
7348                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7349                                      2,
7350                                      &adj_o_10_10_10_1,
7351                                      &adj_o_10_10_10_2),
7352              "BFD sourced %U via noth adjs",
7353              format_fib_prefix, &pfx_5_5_5_5_s_32);
7354
7355     /*
7356      * remove the BFD session state from the adj
7357      */
7358     adj_bfd_notify(BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
7359
7360     /*
7361      * clean-up
7362      */
7363     fib_table_entry_delete(0, &pfx_5_5_5_5_s_32, FIB_SOURCE_CLI);
7364     adj_unlock(ai_10_10_10_1);
7365     adj_unlock(ai_10_10_10_2);
7366
7367     /*
7368      * test no-one left behind
7369      */
7370     FIB_TEST((n_feis == fib_entry_pool_size()), "Entries gone");
7371     FIB_TEST(0 == adj_nbr_db_size(), "All adjacencies removed");
7372     return (0);
7373 }
7374
7375 static int
7376 lfib_test (void)
7377 {
7378     const mpls_label_t deag_label = 50;
7379     const u32 lfib_index = 0;
7380     const u32 fib_index = 0;
7381     dpo_id_t dpo = DPO_INVALID;
7382     const dpo_id_t *dpo1;
7383     fib_node_index_t lfe;
7384     lookup_dpo_t *lkd;
7385     test_main_t *tm;
7386     int lb_count;
7387     adj_index_t ai_mpls_10_10_10_1;
7388
7389     tm = &test_main;
7390     lb_count = pool_elts(load_balance_pool);
7391
7392     FIB_TEST((0 == adj_nbr_db_size()), "ADJ DB size is %d",
7393              adj_nbr_db_size());
7394
7395     /*
7396      * MPLS enable an interface so we get the MPLS table created
7397      */
7398     mpls_sw_interface_enable_disable(&mpls_main,
7399                                      tm->hw[0]->sw_if_index,
7400                                      1);
7401
7402     ip46_address_t nh_10_10_10_1 = {
7403         .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a01),
7404     };
7405     ai_mpls_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
7406                                              VNET_LINK_MPLS,
7407                                              &nh_10_10_10_1,
7408                                              tm->hw[0]->sw_if_index);
7409
7410     /*
7411      * Test the specials stack properly.
7412      */
7413     fib_prefix_t exp_null_v6_pfx = {
7414         .fp_proto = FIB_PROTOCOL_MPLS,
7415         .fp_eos = MPLS_EOS,
7416         .fp_label = MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL,
7417         .fp_payload_proto = DPO_PROTO_IP6,
7418     };
7419     lfe = fib_table_lookup(lfib_index, &exp_null_v6_pfx);
7420     FIB_TEST((FIB_NODE_INDEX_INVALID != lfe),
7421              "%U/%U present",
7422              format_mpls_unicast_label, MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL,
7423              format_mpls_eos_bit, MPLS_EOS);
7424     fib_entry_contribute_forwarding(lfe,
7425                                     FIB_FORW_CHAIN_TYPE_MPLS_EOS,
7426                                     &dpo);
7427     dpo1 = load_balance_get_bucket(dpo.dpoi_index, 0);
7428     lkd = lookup_dpo_get(dpo1->dpoi_index);
7429
7430     FIB_TEST((fib_index == lkd->lkd_fib_index),
7431               "%U/%U is deag in %d %U",
7432              format_mpls_unicast_label, deag_label,
7433              format_mpls_eos_bit, MPLS_EOS,
7434              lkd->lkd_fib_index,
7435              format_dpo_id, &dpo, 0);
7436     FIB_TEST((LOOKUP_INPUT_DST_ADDR == lkd->lkd_input),
7437              "%U/%U is dst deag",
7438              format_mpls_unicast_label, deag_label,
7439              format_mpls_eos_bit, MPLS_EOS);
7440     FIB_TEST((LOOKUP_TABLE_FROM_INPUT_INTERFACE == lkd->lkd_table),
7441              "%U/%U is lookup in interface's table",
7442              format_mpls_unicast_label, deag_label,
7443              format_mpls_eos_bit, MPLS_EOS);
7444     FIB_TEST((DPO_PROTO_IP6 == lkd->lkd_proto),
7445              "%U/%U is %U dst deag",
7446              format_mpls_unicast_label, deag_label,
7447              format_mpls_eos_bit, MPLS_EOS,
7448              format_dpo_proto, lkd->lkd_proto);
7449
7450
7451     /*
7452      * A route deag route for EOS
7453      */
7454     fib_prefix_t pfx = {
7455         .fp_proto = FIB_PROTOCOL_MPLS,
7456         .fp_eos = MPLS_EOS,
7457         .fp_label = deag_label,
7458         .fp_payload_proto = DPO_PROTO_IP4,
7459     };
7460     lfe = fib_table_entry_path_add(lfib_index,
7461                                    &pfx,
7462                                    FIB_SOURCE_CLI,
7463                                    FIB_ENTRY_FLAG_NONE,
7464                                    FIB_PROTOCOL_IP4,
7465                                    &zero_addr,
7466                                    ~0,
7467                                    fib_index,
7468                                    1,
7469                                    NULL,
7470                                    FIB_ROUTE_PATH_FLAG_NONE);
7471
7472     FIB_TEST((lfe == fib_table_lookup(lfib_index, &pfx)),
7473               "%U/%U present",
7474               format_mpls_unicast_label, deag_label,
7475               format_mpls_eos_bit, MPLS_EOS);
7476
7477     fib_entry_contribute_forwarding(lfe,
7478                                     FIB_FORW_CHAIN_TYPE_MPLS_EOS,
7479                                     &dpo);
7480     dpo1 = load_balance_get_bucket(dpo.dpoi_index, 0);
7481     lkd = lookup_dpo_get(dpo1->dpoi_index);
7482
7483     FIB_TEST((fib_index == lkd->lkd_fib_index),
7484               "%U/%U is deag in %d %U",
7485              format_mpls_unicast_label, deag_label,
7486              format_mpls_eos_bit, MPLS_EOS,
7487              lkd->lkd_fib_index,
7488              format_dpo_id, &dpo, 0);
7489     FIB_TEST((LOOKUP_INPUT_DST_ADDR == lkd->lkd_input),
7490              "%U/%U is dst deag",
7491              format_mpls_unicast_label, deag_label,
7492              format_mpls_eos_bit, MPLS_EOS);
7493     FIB_TEST((DPO_PROTO_IP4 == lkd->lkd_proto),
7494              "%U/%U is %U dst deag",
7495              format_mpls_unicast_label, deag_label,
7496              format_mpls_eos_bit, MPLS_EOS,
7497              format_dpo_proto, lkd->lkd_proto);
7498
7499     fib_table_entry_delete_index(lfe, FIB_SOURCE_CLI);
7500
7501     FIB_TEST((FIB_NODE_INDEX_INVALID == fib_table_lookup(lfib_index,
7502                                                          &pfx)),
7503               "%U/%U not present",
7504               format_mpls_unicast_label, deag_label,
7505               format_mpls_eos_bit, MPLS_EOS);
7506
7507     /*
7508      * A route deag route for non-EOS
7509      */
7510     pfx.fp_eos = MPLS_NON_EOS;
7511     lfe = fib_table_entry_path_add(lfib_index,
7512                                    &pfx,
7513                                    FIB_SOURCE_CLI,
7514                                    FIB_ENTRY_FLAG_NONE,
7515                                    FIB_PROTOCOL_IP4,
7516                                    &zero_addr,
7517                                    ~0,
7518                                    lfib_index,
7519                                    1,
7520                                    NULL,
7521                                    FIB_ROUTE_PATH_FLAG_NONE);
7522
7523     FIB_TEST((lfe == fib_table_lookup(lfib_index, &pfx)),
7524               "%U/%U present",
7525               format_mpls_unicast_label, deag_label,
7526               format_mpls_eos_bit, MPLS_NON_EOS);
7527
7528     fib_entry_contribute_forwarding(lfe,
7529                                     FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
7530                                     &dpo);
7531     dpo1 = load_balance_get_bucket(dpo.dpoi_index, 0);
7532     lkd = lookup_dpo_get(dpo1->dpoi_index);
7533
7534     FIB_TEST((fib_index == lkd->lkd_fib_index),
7535               "%U/%U is deag in %d %U",
7536              format_mpls_unicast_label, deag_label,
7537              format_mpls_eos_bit, MPLS_NON_EOS,
7538              lkd->lkd_fib_index,
7539              format_dpo_id, &dpo, 0);
7540     FIB_TEST((LOOKUP_INPUT_DST_ADDR == lkd->lkd_input),
7541              "%U/%U is dst deag",
7542              format_mpls_unicast_label, deag_label,
7543              format_mpls_eos_bit, MPLS_NON_EOS);
7544
7545     FIB_TEST((DPO_PROTO_MPLS == lkd->lkd_proto),
7546              "%U/%U is %U dst deag",
7547              format_mpls_unicast_label, deag_label,
7548              format_mpls_eos_bit, MPLS_NON_EOS,
7549              format_dpo_proto, lkd->lkd_proto);
7550
7551     fib_table_entry_delete_index(lfe, FIB_SOURCE_CLI);
7552
7553     FIB_TEST((FIB_NODE_INDEX_INVALID == fib_table_lookup(lfib_index,
7554                                                          &pfx)),
7555               "%U/%U not present",
7556               format_mpls_unicast_label, deag_label,
7557               format_mpls_eos_bit, MPLS_EOS);
7558
7559     dpo_reset(&dpo);
7560
7561     /*
7562      * An MPLS x-connect
7563      */
7564     fib_prefix_t pfx_1200 = {
7565         .fp_len = 21,
7566         .fp_proto = FIB_PROTOCOL_MPLS,
7567         .fp_label = 1200,
7568         .fp_eos = MPLS_NON_EOS,
7569     };
7570     fib_test_lb_bucket_t neos_o_10_10_10_1 = {
7571         .type = FT_LB_LABEL_STACK_O_ADJ,
7572         .label_stack_o_adj = {
7573             .adj = ai_mpls_10_10_10_1,
7574             .label_stack_size = 4,
7575             .label_stack = {
7576                 200, 300, 400, 500,
7577             },
7578             .eos = MPLS_NON_EOS,
7579         },
7580     };
7581     dpo_id_t neos_1200 = DPO_INVALID;
7582     dpo_id_t ip_1200 = DPO_INVALID;
7583     mpls_label_t *l200 = NULL;
7584     vec_add1(l200, 200);
7585     vec_add1(l200, 300);
7586     vec_add1(l200, 400);
7587     vec_add1(l200, 500);
7588
7589     lfe = fib_table_entry_update_one_path(fib_index,
7590                                           &pfx_1200,
7591                                           FIB_SOURCE_API,
7592                                           FIB_ENTRY_FLAG_NONE,
7593                                           FIB_PROTOCOL_IP4,
7594                                           &nh_10_10_10_1,
7595                                           tm->hw[0]->sw_if_index,
7596                                           ~0, // invalid fib index
7597                                           1,
7598                                           l200,
7599                                           FIB_ROUTE_PATH_FLAG_NONE);
7600
7601     FIB_TEST(fib_test_validate_entry(lfe,
7602                                      FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
7603                                      1,
7604                                      &neos_o_10_10_10_1),
7605              "1200/0 LB 1 buckets via: "
7606              "adj 10.10.11.1");
7607
7608     /*
7609      * A recursive route via the MPLS x-connect
7610      */
7611     fib_prefix_t pfx_2_2_2_3_s_32 = {
7612         .fp_len = 32,
7613         .fp_proto = FIB_PROTOCOL_IP4,
7614         .fp_addr = {
7615             .ip4.as_u32 = clib_host_to_net_u32(0x02020203),
7616         },
7617     };
7618     fib_route_path_t *rpaths = NULL, rpath = {
7619         .frp_proto = FIB_PROTOCOL_MPLS,
7620         .frp_local_label = 1200,
7621         .frp_eos = MPLS_NON_EOS,
7622         .frp_sw_if_index = ~0, // recurive
7623         .frp_fib_index = 0, // Default MPLS fib
7624         .frp_weight = 1,
7625         .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
7626         .frp_label_stack = NULL,
7627     };
7628     vec_add1(rpaths, rpath);
7629
7630     fib_table_entry_path_add2(fib_index,
7631                               &pfx_2_2_2_3_s_32,
7632                               FIB_SOURCE_API,
7633                               FIB_ENTRY_FLAG_NONE,
7634                               rpaths);
7635
7636     /*
7637      * A labelled recursive route via the MPLS x-connect
7638      */
7639     fib_prefix_t pfx_2_2_2_4_s_32 = {
7640         .fp_len = 32,
7641         .fp_proto = FIB_PROTOCOL_IP4,
7642         .fp_addr = {
7643             .ip4.as_u32 = clib_host_to_net_u32(0x02020204),
7644         },
7645     };
7646     mpls_label_t *l999 = NULL;
7647     vec_add1(l999, 999);
7648     rpaths[0].frp_label_stack = l999,
7649
7650     fib_table_entry_path_add2(fib_index,
7651                               &pfx_2_2_2_4_s_32,
7652                               FIB_SOURCE_API,
7653                               FIB_ENTRY_FLAG_NONE,
7654                               rpaths);
7655
7656     fib_entry_contribute_forwarding(fib_table_lookup(fib_index, &pfx_1200),
7657                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7658                                     &ip_1200);
7659     fib_entry_contribute_forwarding(fib_table_lookup(fib_index, &pfx_1200),
7660                                     FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS,
7661                                     &neos_1200);
7662
7663     fib_test_lb_bucket_t ip_o_1200 = {
7664         .type = FT_LB_O_LB,
7665         .lb = {
7666             .lb = ip_1200.dpoi_index,
7667         },
7668     };
7669     fib_test_lb_bucket_t mpls_o_1200 = {
7670         .type = FT_LB_LABEL_O_LB,
7671         .label_o_lb = {
7672             .lb = neos_1200.dpoi_index,
7673             .label = 999,
7674             .eos = MPLS_EOS,
7675         },
7676     };
7677
7678     lfe = fib_table_lookup(fib_index, &pfx_2_2_2_3_s_32);
7679     FIB_TEST(fib_test_validate_entry(lfe,
7680                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7681                                      1,
7682                                      &ip_o_1200),
7683              "2.2.2.2.3/32 LB 1 buckets via: label 1200 EOS");
7684     lfe = fib_table_lookup(fib_index, &pfx_2_2_2_4_s_32);
7685     FIB_TEST(fib_test_validate_entry(lfe,
7686                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7687                                      1,
7688                                      &mpls_o_1200),
7689              "2.2.2.2.4/32 LB 1 buckets via: label 1200 non-EOS");
7690
7691     fib_table_entry_delete(fib_index, &pfx_1200, FIB_SOURCE_API);
7692     fib_table_entry_delete(fib_index, &pfx_2_2_2_3_s_32, FIB_SOURCE_API);
7693     fib_table_entry_delete(fib_index, &pfx_2_2_2_4_s_32, FIB_SOURCE_API);
7694
7695     dpo_reset(&neos_1200);
7696     dpo_reset(&ip_1200);
7697
7698     /*
7699      * A recursive via a label that does not exist
7700      */
7701     fib_test_lb_bucket_t bucket_drop = {
7702         .type = FT_LB_SPECIAL,
7703         .special = {
7704             .adj = DPO_PROTO_MPLS,
7705         },
7706     };
7707
7708     rpaths[0].frp_label_stack = NULL;
7709     lfe = fib_table_entry_path_add2(fib_index,
7710                                     &pfx_2_2_2_4_s_32,
7711                                     FIB_SOURCE_API,
7712                                     FIB_ENTRY_FLAG_NONE,
7713                                     rpaths);
7714
7715     fib_entry_contribute_forwarding(fib_table_lookup(fib_index, &pfx_1200),
7716                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7717                                     &ip_1200);
7718     ip_o_1200.lb.lb = ip_1200.dpoi_index;
7719
7720     FIB_TEST(fib_test_validate_entry(lfe,
7721                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7722                                      1,
7723                                      &ip_o_1200),
7724              "2.2.2.2.4/32 LB 1 buckets via: label 1200 EOS");
7725     lfe = fib_table_lookup(fib_index, &pfx_1200);
7726     FIB_TEST(fib_test_validate_entry(lfe,
7727                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
7728                                      1,
7729                                      &bucket_drop),
7730              "2.2.2.4/32 LB 1 buckets via: ip4-DROP");
7731
7732     fib_table_entry_delete(fib_index, &pfx_2_2_2_4_s_32, FIB_SOURCE_API);
7733
7734     dpo_reset(&ip_1200);
7735
7736     /*
7737      * An rx-interface route.
7738      *  like the tail of an mcast LSP
7739      */
7740     dpo_id_t idpo = DPO_INVALID;
7741
7742     interface_dpo_add_or_lock(DPO_PROTO_IP4,
7743                               tm->hw[0]->sw_if_index,
7744                               &idpo);
7745
7746     fib_prefix_t pfx_2500 = {
7747         .fp_len = 21,
7748         .fp_proto = FIB_PROTOCOL_MPLS,
7749         .fp_label = 2500,
7750         .fp_eos = MPLS_EOS,
7751         .fp_payload_proto = DPO_PROTO_IP4,
7752     };
7753     fib_test_lb_bucket_t rx_intf_0 = {
7754         .type = FT_LB_INTF,
7755         .adj = {
7756             .adj = idpo.dpoi_index,
7757         },
7758     };
7759
7760     lfe = fib_table_entry_update_one_path(fib_index,
7761                                           &pfx_2500,
7762                                           FIB_SOURCE_API,
7763                                           FIB_ENTRY_FLAG_NONE,
7764                                           FIB_PROTOCOL_IP4,
7765                                           NULL,
7766                                           tm->hw[0]->sw_if_index,
7767                                           ~0, // invalid fib index
7768                                           0,
7769                                           NULL,
7770                                           FIB_ROUTE_PATH_INTF_RX);
7771     FIB_TEST(fib_test_validate_entry(lfe,
7772                                      FIB_FORW_CHAIN_TYPE_MPLS_EOS,
7773                                      1,
7774                                      &rx_intf_0),
7775              "2500 rx-interface 0");
7776     fib_table_entry_delete(fib_index, &pfx_2500, FIB_SOURCE_API);
7777
7778     /*
7779      * An MPLS mulicast entry
7780      */
7781     fib_prefix_t pfx_3500 = {
7782         .fp_len = 21,
7783         .fp_proto = FIB_PROTOCOL_MPLS,
7784         .fp_label = 3500,
7785         .fp_eos = MPLS_EOS,
7786         .fp_payload_proto = DPO_PROTO_IP4,
7787     };
7788     fib_test_rep_bucket_t mc_0 = {
7789         .type = FT_REP_LABEL_O_ADJ,
7790         .label_o_adj = {
7791             .adj = ai_mpls_10_10_10_1,
7792             .label = 3300,
7793             .eos = MPLS_EOS,
7794         },
7795     };
7796     fib_test_rep_bucket_t mc_intf_0 = {
7797         .type = FT_REP_INTF,
7798         .adj = {
7799             .adj = idpo.dpoi_index,
7800         },
7801     };
7802     mpls_label_t *l3300 = NULL;
7803     vec_add1(l3300, 3300);
7804
7805     lfe = fib_table_entry_update_one_path(lfib_index,
7806                                           &pfx_3500,
7807                                           FIB_SOURCE_API,
7808                                           FIB_ENTRY_FLAG_MULTICAST,
7809                                           FIB_PROTOCOL_IP4,
7810                                           &nh_10_10_10_1,
7811                                           tm->hw[0]->sw_if_index,
7812                                           ~0, // invalid fib index
7813                                           1,
7814                                           l3300,
7815                                           FIB_ROUTE_PATH_FLAG_NONE);
7816     FIB_TEST(fib_test_validate_entry(lfe,
7817                                      FIB_FORW_CHAIN_TYPE_MPLS_EOS,
7818                                      1,
7819                                      &mc_0),
7820              "3500 via replicate over 10.10.10.1");
7821
7822     /*
7823      * MPLS Bud-node. Add a replication via an interface-receieve path
7824      */
7825     lfe = fib_table_entry_path_add(lfib_index,
7826                                    &pfx_3500,
7827                                    FIB_SOURCE_API,
7828                                    FIB_ENTRY_FLAG_MULTICAST,
7829                                    FIB_PROTOCOL_IP4,
7830                                    NULL,
7831                                    tm->hw[0]->sw_if_index,
7832                                    ~0, // invalid fib index
7833                                    0,
7834                                    NULL,
7835                                    FIB_ROUTE_PATH_INTF_RX);
7836     FIB_TEST(fib_test_validate_entry(lfe,
7837                                      FIB_FORW_CHAIN_TYPE_MPLS_EOS,
7838                                      2,
7839                                      &mc_0,
7840                                      &mc_intf_0),
7841              "3500 via replicate over 10.10.10.1 and interface-rx");
7842
7843     /*
7844      * Add a replication via an interface-free for-us path
7845      */
7846     fib_test_rep_bucket_t mc_disp = {
7847         .type = FT_REP_DISP_MFIB_LOOKUP,
7848         .adj = {
7849             .adj = idpo.dpoi_index,
7850         },
7851     };
7852     lfe = fib_table_entry_path_add(lfib_index,
7853                                    &pfx_3500,
7854                                    FIB_SOURCE_API,
7855                                    FIB_ENTRY_FLAG_MULTICAST,
7856                                    FIB_PROTOCOL_IP4,
7857                                    NULL,
7858                                    5, // rpf-id
7859                                    0, // default table
7860                                    0,
7861                                    NULL,
7862                                    FIB_ROUTE_PATH_RPF_ID);
7863     FIB_TEST(fib_test_validate_entry(lfe,
7864                                      FIB_FORW_CHAIN_TYPE_MPLS_EOS,
7865                                      3,
7866                                      &mc_0,
7867                                      &mc_disp,
7868                                      &mc_intf_0),
7869              "3500 via replicate over 10.10.10.1 and interface-rx");
7870
7871
7872     
7873     fib_table_entry_delete(fib_index, &pfx_3500, FIB_SOURCE_API);
7874     dpo_reset(&idpo);
7875
7876     /*
7877      * cleanup
7878      */
7879     mpls_sw_interface_enable_disable(&mpls_main,
7880                                      tm->hw[0]->sw_if_index,
7881                                      0);
7882
7883     FIB_TEST(lb_count == pool_elts(load_balance_pool),
7884              "Load-balance resources freed %d of %d",
7885              lb_count, pool_elts(load_balance_pool));
7886     FIB_TEST(0 == pool_elts(interface_dpo_pool),
7887              "interface_dpo resources freed %d of %d",
7888              0, pool_elts(interface_dpo_pool));
7889
7890     return (0);
7891 }
7892
7893 static clib_error_t *
7894 fib_test (vlib_main_t * vm, 
7895           unformat_input_t * input,
7896           vlib_cli_command_t * cmd_arg)
7897 {
7898     int res;
7899
7900     res = 0;
7901     fib_test_mk_intf(4);
7902
7903     if (unformat (input, "debug"))
7904     {
7905         fib_test_do_debug = 1;
7906     }
7907
7908     if (unformat (input, "ip"))
7909     {
7910         res += fib_test_v4();
7911         res += fib_test_v6();
7912     }
7913     else if (unformat (input, "label"))
7914     {
7915         res += fib_test_label();
7916     }
7917     else if (unformat (input, "ae"))
7918     {
7919         res += fib_test_ae();
7920     }
7921     else if (unformat (input, "lfib"))
7922     {
7923         res += lfib_test();
7924     }
7925     else if (unformat (input, "walk"))
7926     {
7927         res += fib_test_walk();
7928     }
7929     else if (unformat (input, "bfd"))
7930     {
7931         res += fib_test_bfd();
7932     }
7933     else
7934     {
7935         /*
7936          * These walk UT aren't run as part of the full suite, since the
7937          * fib-walk process must be disabled in order for the tests to work
7938          *
7939          * fib_test_walk();
7940          */
7941         res += fib_test_v4();
7942         res += fib_test_v6();
7943         res += fib_test_ae();
7944         res += fib_test_bfd();
7945         res += fib_test_label();
7946         res += lfib_test();
7947     }
7948
7949     if (res)
7950     {
7951         return clib_error_return(0, "FIB Unit Test Failed");
7952     }
7953     else
7954     {
7955         return (NULL);
7956     }
7957 }
7958
7959 VLIB_CLI_COMMAND (test_fib_command, static) = {
7960     .path = "test fib",
7961     .short_help = "fib unit tests - DO NOT RUN ON A LIVE SYSTEM",
7962     .function = fib_test,
7963 };
7964
7965 clib_error_t *
7966 fib_test_init (vlib_main_t *vm)
7967 {
7968     return 0;
7969 }
7970
7971 VLIB_INIT_FUNCTION (fib_test_init);