summaryrefslogtreecommitdiffstats
path: root/src/vnet/fib/fib_entry.h
blob: 70c662171568996749e2bdc8945883a3715b0382 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __FIB_ENTRY_H__
#define __FIB_ENTRY_H__

#include <vnet/fib/fib_node.h>
#include <vnet/adj/adj.h>
#include <vnet/ip/ip.h>
#include <vnet/dpo/dpo.h>

/**
 * The different sources that can create a route.
 * The sources are defined here the thier relative priority order.
 * The lower the value the higher the priority
 */
typedef enum fib_source_t_ {
    /**
     * Marker. Add new values after this one.
     */
    FIB_SOURCE_FIRST,
    /**
     * Special sources. These are for entries that are added to all
     * FIBs by default, and should never be over-ridden (hence they
     * are the highest priority)
     */
    FIB_SOURCE_SPECIAL = FIB_SOURCE_FIRST,
    /**
     * Classify. A route that links directly to a classify adj
     */
    FIB_SOURCE_CLASSIFY,
    /**
     * A route the is being 'proxied' on behalf of another device
     */
    FIB_SOURCE_PROXY,
    /**
     * Route added as a result of interface configuration.
     * this will also come from the API/CLI, but the distinction is
     * that is from confiiguration on an interface, not a 'ip route' command
     */
    FIB_SOURCE_INTERFACE,
    /**
     * SRv6 and SR-MPLS
     */
    FIB_SOURCE_SR,
    /**
     * A high priority source a plugin can use
     */
    FIB_SOURCE_PLUGIN_HI,
    /**
     * From the BIER subsystem
     */
    FIB_SOURCE_BIER,
    /**
     * From 6RD.
     */
    FIB_SOURCE_6RD,
    /**
     * From the control plane API
     */
    FIB_SOURCE_API,
    /**
     * From the CLI.
     */
    FIB_SOURCE_CLI,
    /**
     * A low (below routing) priority source a plugin can use
     */
    FIB_SOURCE_PLUGIN_LOW,
    /**
     * LISP
     */
    FIB_SOURCE_LISP,
    /**
     * IPv[46] Mapping
     */
    FIB_SOURCE_MAP,
    /**
     * DHCP
     */
    FIB_SOURCE_DHCP,
    /**
     * IPv6 Proxy ND
     */
    FIB_SOURCE_IP6_ND_PROXY,
    /**
     * IPv6 ND (seen in the link-local tables)
     */
    FIB_SOURCE_IP6_ND,
    /**
     * Adjacency source.
     * routes created as a result of ARP/ND entries. This is lower priority
     * then the API/CLI. This is on purpose. trust me.
     */
    FIB_SOURCE_ADJ,
    /**
     * MPLS label. The prefix has been assigned a local label. This source
     * never provides forwarding information, instead it acts as a place-holder
     * so the association of label to prefix can be maintained
     */
    FIB_SOURCE_MPLS,
    /**
     * Attached Export source.
     * routes created as a result of attahced export. routes thus sourced
     * will be present in the export tables
     */
    FIB_SOURCE_AE,
    /**
     * Recursive resolution source.
     * Used to install an entry that is the resolution traget of another.
     */
    FIB_SOURCE_RR,
    /**
     * uRPF bypass/exemption.
     * Used to install an entry that is exempt from the loose uRPF check
     */
    FIB_SOURCE_URPF_EXEMPT,
    /**
     * The default route source.
     * The default route is always added to the FIB table (like the
     * special sources) but we need to be able to over-ride it with
     * 'ip route' sources when provided
     */
    FIB_SOURCE_DEFAULT_ROUTE,
    /**
     * The interpose source.
     * This is not a real source, so don't use it to source a prefix.
     * It exists here to provide a value against which to register to the
     * VFT for providing the interpose actions to a real source.
     */
    FIB_SOURCE_INTERPOSE,
    /**
     * Marker. add new entries before this one.
     */
    FIB_SOURCE_LAST = FIB_SOURCE_INTERPOSE,
} __attribute__ ((packed)) fib_source_t;

STATIC_ASSERT (sizeof(fib_source_t) == 1,
	       "FIB too many sources");

/**
 * The maximum number of sources
 */
#define FIB_SOURCE_MAX (FIB_SOURCE_LAST+1)

#define FIB_SOURCES {					\
    [FIB_SOURCE_SPECIAL] = "special",			\
    [FIB_SOURCE_INTERFACE] = "interface",		\
    [FIB_SOURCE_PROXY] = "proxy",                       \
    [FIB_SOURCE_BIER] = "BIER",			        \
    [FIB_SOURCE_6RD] = "6RD",			        \
    [FIB_SOURCE_API] = "API",			        \
    [FIB_SOURCE_CLI] = "CLI",			        \
    [FIB_SOURCE_ADJ] = "adjacency",			\
    [FIB_SOURCE_MAP] = "MAP",			        \
    [FIB_SOURCE_SR] = "SR",			        \
    [FIB_SOURCE_LISP] = "LISP", 			\
    [FIB_SOURCE_CLASSIFY] = "classify",			\
    [FIB_SOURCE_DHCP] = "DHCP",   			\
    [FIB_SOURCE_IP6_ND_PROXY] = "IPv6-proxy-nd",        \
    [FIB_SOURCE_IP6_ND] = "IPv6-nd",                    \
    [FIB_SOURCE_RR] = "recursive-resolution",	        \
    [FIB_SOURCE_AE] = "attached_export",	        \
    [FIB_SOURCE_MPLS] = "mpls",           	        \
    [FIB_SOURCE_URPF_EXEMPT] = "urpf-exempt",	        \
    [FIB_SOURCE_DEFAULT_ROUTE] = "default-route",	\
    [FIB_SOURCE_PLUGIN_HI] = "plugin-hi",               \
    [FIB_SOURCE_PLUGIN_LOW] = "plugin-low",             \
    [FIB_SOURCE_INTERPOSE] = "interpose",               \
}

#define FOR_EACH_FIB_SOURCE(_item) \
    for (_item = FIB_SOURCE_FIRST; _item < FIB_SOURCE_MAX; _item++)

/**
 * The different sources that can create a route.
 * The sources are defined here the thier relative priority order.
 * The lower the value the higher the priority
 */
typedef enum fib_entry_attribute_t_ {
    /**
     * Marker. Add new values after this one.
     */
    FIB_ENTRY_ATTRIBUTE_FIRST,
    /**
     * Connected. The prefix is configured on an interface.
     */
    FIB_ENTRY_ATTRIBUTE_CONNECTED = FIB_ENTRY_ATTRIBUTE_FIRST,
    /**
     * Attached. The prefix is attached to an interface.
     */
    FIB_ENTRY_ATTRIBUTE_ATTACHED,
    /**
     * The route is an explicit drop.
     */
    FIB_ENTRY_ATTRIBUTE_DROP,
    /**
     * The route is exclusive. The client creating the route is
     * providing an exclusive adjacency.
     */
    FIB_ENTRY_ATTRIBUTE_EXCLUSIVE,
    /**
     * The route is attached cross tables and thus imports covered
     * prefixes from the other table.
     */
    FIB_ENTRY_ATTRIBUTE_IMPORT,
    /**
     * The prefix/address is local to this device
     */
    FIB_ENTRY_ATTRIBUTE_LOCAL,
    /**
     * The prefix/address is a multicast prefix.
     *  this aplies only to MPLS. IP multicast is handled by mfib
     */
    FIB_ENTRY_ATTRIBUTE_MULTICAST,
    /**
     * The prefix/address exempted from loose uRPF check
     * To be used with caution
     */
    FIB_ENTRY_ATTRIBUTE_URPF_EXEMPT,
    /**
     * The prefix/address exempted from attached export
     */
    FIB_ENTRY_ATTRIBUTE_NO_ATTACHED_EXPORT,
    /**
     * This FIB entry imposes its source information on all prefixes
     * that is covers
     */
    FIB_ENTRY_ATTRIBUTE_COVERED_INHERIT,
    /**
     * The interpose attribute.
     * place the forwarding provided by the source infront of the forwarding
     * provided by the best source, or failing that, by the cover.
     */
    FIB_ENTRY_ATTRIBUTE_INTERPOSE,
    /**
     * Marker. add new entries before this one.
     */
    FIB_ENTRY_ATTRIBUTE_LAST = FIB_ENTRY_ATTRIBUTE_INTERPOSE,
} fib_entry_attribute_t;

#define FIB_ENTRY_ATTRIBUTES {		       		\
    [FIB_ENTRY_ATTRIBUTE_CONNECTED] = "connected",	\
    [FIB_ENTRY_ATTRIBUTE_ATTACHED]  = "attached",	\
    [FIB_ENTRY_ATTRIBUTE_IMPORT]    = "import",	        \
    [FIB_ENTRY_ATTRIBUTE_DROP]      = "drop",		\
    [FIB_ENTRY_ATTRIBUTE_EXCLUSIVE] = "exclusive",      \
    [FIB_ENTRY_ATTRIBUTE_LOCAL]     = "local",		\
    [FIB_ENTRY_ATTRIBUTE_URPF_EXEMPT] = "uRPF-exempt",  \
    [FIB_ENTRY_ATTRIBUTE_MULTICAST] = "multicast",	\
    [FIB_ENTRY_ATTRIBUTE_NO_ATTACHED_EXPORT] = "no-attached-export",	\
    [FIB_ENTRY_ATTRIBUTE_COVERED_INHERIT] = "covered-inherit",  \
    [FIB_ENTRY_ATTRIBUTE_INTERPOSE] = "interpose",  \
}

#define FOR_EACH_FIB_ATTRIBUTE(_item)			\
    for (_item = FIB_ENTRY_ATTRIBUTE_FIRST;		\
	 _item <= FIB_ENTRY_ATTRIBUTE_LAST;		\
	 _item++)

typedef enum fib_entry_flag_t_ {
    FIB_ENTRY_FLAG_NONE      = 0,
    FIB_ENTRY_FLAG_CONNECTED = (1 << FIB_ENTRY_ATTRIBUTE_CONNECTED),
    FIB_ENTRY_FLAG_ATTACHED  = (1 << FIB_ENTRY_ATTRIBUTE_ATTACHED),
    FIB_ENTRY_FLAG_DROP      = (1 << FIB_ENTRY_ATTRIBUTE_DROP),
    FIB_ENTRY_FLAG_EXCLUSIVE = (1 << FIB_ENTRY_ATTRIBUTE_EXCLUSIVE),
    FIB_ENTRY_FLAG_LOCAL     = (1 << FIB_ENTRY_ATTRIBUTE_LOCAL),
    FIB_ENTRY_FLAG_IMPORT    = (1 << FIB_ENTRY_ATTRIBUTE_IMPORT),
    FIB_ENTRY_FLAG_NO_ATTACHED_EXPORT = (1 << FIB_ENTRY_ATTRIBUTE_NO_ATTACHED_EXPORT),
    FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT = (1 << FIB_ENTRY_ATTRIBUTE_URPF_EXEMPT),
    FIB_ENTRY_FLAG_MULTICAST = (1 << FIB_ENTRY_ATTRIBUTE_MULTICAST),
    FIB_ENTRY_FLAG_COVERED_INHERIT = (1 << FIB_ENTRY_ATTRIBUTE_COVERED_INHERIT),
    FIB_ENTRY_FLAG_INTERPOSE = (1 << FIB_ENTRY_ATTRIBUTE_INTERPOSE),
} __attribute__((packed)) fib_entry_flag_t;

extern u8 * format_fib_entry_flags(u8 *s, va_list *args);

/**
 * Flags for the source data
 */
typedef enum fib_entry_src_attribute_t_ {
    /**
     * Marker. Add new values after this one.
     */
    FIB_ENTRY_SRC_ATTRIBUTE_FIRST,
    /**
     * the source has been added to the entry
     */
    FIB_ENTRY_SRC_ATTRIBUTE_ADDED = FIB_ENTRY_SRC_ATTRIBUTE_FIRST,
    /**
     * the source is contributing forwarding
     */
    FIB_ENTRY_SRC_ATTRIBUTE_CONTRIBUTING,
    /**
     * the source is active/best
     */
    FIB_ENTRY_SRC_ATTRIBUTE_ACTIVE,
    /**
     * the source is inherited from its cover
     */
    FIB_ENTRY_SRC_ATTRIBUTE_INHERITED,
    /**
     * Marker. add new entries before this one.
     */
    FIB_ENTRY_SRC_ATTRIBUTE_LAST = FIB_ENTRY_SRC_ATTRIBUTE_INHERITED,
} fib_entry_src_attribute_t;


#define FIB_ENTRY_SRC_ATTRIBUTES {		 \
    [FIB_ENTRY_SRC_ATTRIBUTE_ADDED]  = "added",	 \
    [FIB_ENTRY_SRC_ATTRIBUTE_CONTRIBUTING] = "contributing", \
    [FIB_ENTRY_SRC_ATTRIBUTE_ACTIVE] = "active", \
    [FIB_ENTRY_SRC_ATTRIBUTE_INHERITED] = "inherited", \
}

#define FOR_EACH_FIB_SRC_ATTRIBUTE(_item)      		\
    for (_item = FIB_ENTRY_SRC_ATTRIBUTE_FIRST;		\
	 _item <= FIB_ENTRY_SRC_ATTRIBUTE_LAST;		\
	 _item++)

typedef enum fib_entry_src_flag_t_ {
    FIB_ENTRY_SRC_FLAG_NONE   = 0,
    FIB_ENTRY_SRC_FLAG_ADDED  = (1 << FIB_ENTRY_SRC_ATTRIBUTE_ADDED),
    FIB_ENTRY_SRC_FLAG_CONTRIBUTING = (1 << FIB_ENTRY_SRC_ATTRIBUTE_CONTRIBUTING),
    FIB_ENTRY_SRC_FLAG_ACTIVE = (1 << FIB_ENTRY_SRC_ATTRIBUTE_ACTIVE),
    FIB_ENTRY_SRC_FLAG_INHERITED = (1 << FIB_ENTRY_SRC_ATTRIBUTE_INHERITED),
} __attribute__ ((packed)) fib_entry_src_flag_t;

extern u8 * format_fib_entry_src_flags(u8 *s, va_list *args);

/*
 * Keep the size of the flags field to 2 bytes, so it
 * can be placed next to the 2 bytes reference count
 */
STATIC_ASSERT (sizeof(fib_entry_src_flag_t) <= 2,
	       "FIB entry flags field size too big");

/**
 * Information related to the source of a FIB entry
 */
typedef struct fib_entry_src_t_ {
    /**
     * A vector of path extensions
     */
    fib_path_ext_list_t fes_path_exts;

    /**
     * The path-list created by the source
     */
    fib_node_index_t fes_pl;

    /**
     * Flags the source contributes to the entry
     */
    fib_entry_flag_t fes_entry_flags;

    /**
     * Which source this info block is for
     */
    fib_source_t fes_src;

    /**
     * Flags on the source
     */
    fib_entry_src_flag_t fes_flags;

    /**
     * 1 bytes ref count. This is not the number of users of the Entry
     * (which is itself not large, due to path-list sharing), but the number
     * of times a given source has been added. Which is even fewer
     */
    u8 fes_ref_count;
    
    /**
     * Source specific info
     */
    union {
	struct {
	    /**
	     * the index of the FIB entry that is the covering entry
	     */
	    fib_node_index_t fesr_cover;
	    /**
	     * This source's index in the cover's list
	     */
	    u32 fesr_sibling;
	} rr;
	struct {
	    /**
	     * the index of the FIB entry that is the covering entry
	     */
	    fib_node_index_t fesi_cover;
	    /**
	     * This source's index in the cover's list
	     */
	    u32 fesi_sibling;
            /**
             * DPO type to interpose. The dpo type needs to have registered
             * it's 'contribute interpose' callback function.
             */
            dpo_id_t fesi_dpo;
	} interpose;
	struct {
	    /**
	     * the index of the FIB entry that is the covering entry
	     */
	    fib_node_index_t fesa_cover;
	    /**
	     * This source's index in the cover's list
	     */
	    u32 fesa_sibling;
	} adj;
	struct {
	    /**
	     * the index of the FIB entry that is the covering entry
	     */
	    fib_node_index_t fesi_cover;
	    /**
	     * This source's index in the cover's list
	     */
	    u32 fesi_sibling;
	} interface;
	struct {
	    /**
	     * This MPLS local label associated with the prefix.
	     */
	    mpls_label_t fesm_label;

	    /**
	     * the indicies of the LFIB entries created
	     */
	    fib_node_index_t fesm_lfes[2];
	} mpls;
	struct {
	    /**
	     * The source FIB index.
	     */
            fib_node_index_t fesl_fib_index;
	} lisp;
    } u;
} fib_entry_src_t;

/**
 * An entry in a FIB table.
 *
 * This entry represents a route added to the FIB that is stored
 * in one of the FIB tables.
 */
typedef struct fib_entry_t_ {
    /**
     * Base class. The entry's node representation in the graph.
     */
    fib_node_t fe_node;
    /**
     * The prefix of the route. this is const just to be sure.
     * It is the entry's key/identity and so should never change.
     */
    const fib_prefix_t fe_prefix;
    /**
     * The index of the FIB table this entry is in
     */
    u32 fe_fib_index;
    /**
     * The load-balance used for forwarding.
     *
     * We don't share the EOS and non-EOS even in case when they could be
     * because:
     *   - complexity & reliability v. memory
     *       determining the conditions where sharing is possible is non-trivial.
     *   - separate LBs means we can get the EOS bit right in the MPLS label DPO
     *     and so save a few clock cycles in the DP imposition node since we can
     *     paint the header straight on without the need to check the packet
     *     type to derive the EOS bit value.
     */
    dpo_id_t fe_lb;
    /**
     * Vector of source infos.
     * Most entries will only have 1 source. So we optimise for memory usage,
     * which is preferable since we have many entries.
     */
    fib_entry_src_t *fe_srcs;
    /**
     * the path-list for which this entry is a child. This is also the path-list
     * that is contributing forwarding for this entry.
     */
    fib_node_index_t fe_parent;
    /**
     * index of this entry in the parent's child list.
     * This is set when this entry is added as a child, but can also
     * be changed by the parent as it manages its list.
     */
    u32 fe_sibling;

    /**
     * A vector of delegate indices.
     */
    index_t *fe_delegates;
} fib_entry_t;

#define FOR_EACH_FIB_ENTRY_FLAG(_item) \
    for (_item = FIB_ENTRY_FLAG_FIRST; _item < FIB_ENTRY_FLAG_MAX; _item++)

#define FIB_ENTRY_FORMAT_BRIEF   (0x0)
#define FIB_ENTRY_FORMAT_DETAIL  (0x1)
#define FIB_ENTRY_FORMAT_DETAIL2 (0x2)

extern u8 *format_fib_entry (u8 * s, va_list * args);
extern u8 *format_fib_source (u8 * s, va_list * args);

extern fib_node_index_t fib_entry_create_special(u32 fib_index,
						 const fib_prefix_t *prefix,
						 fib_source_t source,
						 fib_entry_flag_t flags,
						 const dpo_id_t *dpo);

extern fib_node_index_t fib_entry_create (u32 fib_index,
					  const fib_prefix_t *prefix,
					  fib_source_t source,
					  fib_entry_flag_t flags,
					  const fib_route_path_t *paths);
extern void fib_entry_update (fib_node_index_t fib_entry_index,
			      fib_source_t source,
			      fib_entry_flag_t flags,
			      const fib_route_path_t *paths);

extern void fib_entry_path_add(fib_node_index_t fib_entry_index,
			       fib_source_t source,
			       fib_entry_flag_t flags,
			       const fib_route_path_t *rpaths);
extern void fib_entry_special_add(fib_node_index_t fib_entry_index,
				  fib_source_t source,
				  fib_entry_flag_t flags,
				  const dpo_id_t *dpo);
extern void fib_entry_special_update(fib_node_index_t fib_entry_index,
				     fib_source_t source,
				     fib_entry_flag_t flags,
				     const dpo_id_t *dpo);
extern fib_entry_src_flag_t fib_entry_special_remove(fib_node_index_t fib_entry_index,
						     fib_source_t source);

extern fib_entry_src_flag_t fib_entry_path_remove(fib_node_index_t fib_entry_index,
						  fib_source_t source,
						  const fib_route_path_t *rpaths);

extern void fib_entry_inherit(fib_node_index_t cover,
                              fib_node_index_t covered);

extern fib_entry_src_flag_t fib_entry_delete(fib_node_index_t fib_entry_index,
					     fib_source_t source);

extern void fib_entry_recalculate_forwarding(
    fib_node_index_t fib_entry_index);
extern void fib_entry_contribute_urpf(fib_node_index_t path_index,
				      index_t urpf);
extern void fib_entry_contribute_forwarding(
    fib_node_index_t fib_entry_index,
    fib_forward_chain_type_t type,
    dpo_id_t *dpo);
extern const dpo_id_t * fib_entry_contribute_ip_forwarding(
    fib_node_index_t fib_entry_index);
extern adj_index_t fib_entry_get_adj_for_source(
    fib_node_index_t fib_entry_index,
    fib_source_t source);
extern const int fib_entry_get_dpo_for_source (
    fib_node_index_t fib_entry_index,
    fib_source_t source,
    dpo_id_t *dpo);

extern adj_index_t fib_entry_get_adj(fib_node_index_t fib_entry_index);

extern int fib_entry_cmp_for_sort(void *i1, void *i2);

extern void fib_entry_cover_changed(fib_node_index_t fib_entry);
extern void fib_entry_cover_updated(fib_node_index_t fib_entry);
extern int fib_entry_recursive_loop_detect(fib_node_index_t entry_index,
					   fib_node_index_t **entry_indicies);

extern void fib_entry_lock(fib_node_index_t fib_entry_index);
extern void fib_entry_unlock(fib_node_index_t fib_entry_index);

extern u32 fib_entry_child_add(fib_node_index_t fib_entry_index,
			       fib_node_type_t type,
			       fib_node_index_t child_index);
extern void fib_entry_child_remove(fib_node_index_t fib_entry_index,
				   u32 sibling_index);
extern u32 fib_entry_get_resolving_interface(fib_node_index_t fib_entry_index);
extern u32 fib_entry_get_resolving_interface_for_source(
    fib_node_index_t fib_entry_index,
    fib_source_t source);

extern fib_route_path_t* fib_entry_encode(fib_node_index_t fib_entry_index);
extern const fib_prefix_t* fib_entry_get_prefix(fib_node_index_t fib_entry_index);
extern u32 fib_entry_get_fib_index(fib_node_index_t fib_entry_index);
extern void fib_entry_set_source_data(fib_node_index_t fib_entry_index,
                                      fib_source_t source,
                                      const void *data);
extern const void* fib_entry_get_source_data(fib_node_index_t fib_entry_index,
                                             fib_source_t source);

extern fib_entry_flag_t fib_entry_get_flags(fib_node_index_t fib_entry_index);
extern fib_entry_flag_t fib_entry_get_flags_for_source(
    fib_node_index_t fib_entry_index,
    fib_source_t source);
extern fib_source_t fib_entry_get_best_source(fib_node_index_t fib_entry_index);
extern int fib_entry_is_sourced(fib_node_index_t fib_entry_index,
                                fib_source_t source);

extern fib_node_index_t fib_entry_get_path_list(fib_node_index_t fib_entry_index);
extern int fib_entry_is_resolved(fib_node_index_t fib_entry_index);
extern int fib_entry_is_host(fib_node_index_t fib_entry_index);
extern void fib_entry_set_flow_hash_config(fib_node_index_t fib_entry_index,
                                           flow_hash_config_t hash_config);

extern void fib_entry_module_init(void);

extern u32 fib_entry_get_stats_index(fib_node_index_t fib_entry_index);

/*
 * unsafe... beware the raw pointer.
 */
extern fib_node_index_t fib_entry_get_index(const fib_entry_t * fib_entry);
extern fib_entry_t * fib_entry_get(fib_node_index_t fib_entry_index);

/*
 * for testing purposes.
 */
extern u32 fib_entry_pool_size(void);

#endif
>src_fib_index, &src_fib_prefix, FIB_SOURCE_LISP, FIB_ENTRY_FLAG_EXCLUSIVE, src_dpo); } return fei; } static fib_route_path_t * lisp_gpe_mk_fib_paths (const lisp_fwd_path_t * paths) { const lisp_gpe_adjacency_t *ladj; fib_route_path_t *rpaths = NULL; fib_protocol_t fp; u8 best_priority; u32 ii; vec_validate (rpaths, vec_len (paths) - 1); best_priority = paths[0].priority; vec_foreach_index (ii, paths) { if (paths[0].priority != best_priority) break; ladj = lisp_gpe_adjacency_get (paths[ii].lisp_adj); ip_address_to_46 (&ladj->remote_rloc, &rpaths[ii].frp_addr, &fp); rpaths[ii].frp_proto = fib_proto_to_dpo (fp); rpaths[ii].frp_sw_if_index = ladj->sw_if_index; rpaths[ii].frp_weight = (paths[ii].weight ? paths[ii].weight : 1); } ASSERT (0 != vec_len (rpaths)); return (rpaths); } /** * @brief Add route to IP4 or IP6 SRC FIB. * * Adds a route to in the LISP SRC FIB for the tunnel. * * @param[in] src_fib_index The index/ID of the SRC FIB * @param[in] src_prefix Source IP prefix. * @param[in] paths The paths from which to construct the * load balance */ static fib_node_index_t ip_src_fib_add_route (u32 src_fib_index, const ip_prefix_t * src_prefix, const lisp_fwd_path_t * paths) { fib_prefix_t src_fib_prefix; fib_route_path_t *rpaths; ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix); rpaths = lisp_gpe_mk_fib_paths (paths); fib_node_index_t fib_entry_index = fib_table_entry_update (src_fib_index, &src_fib_prefix, FIB_SOURCE_LISP, FIB_ENTRY_FLAG_NONE, rpaths); vec_free (rpaths); return fib_entry_index; } static void gpe_native_fwd_add_del_lfe (lisp_gpe_fwd_entry_t * lfe, u8 is_add) { lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); u8 found = 0, ip_version; u32 *lfei, new_lfei; ip_version = ip_prefix_version (&lfe->key->rmt.ippref); new_lfei = lfe - lgm->lisp_fwd_entry_pool; vec_foreach (lfei, lgm->native_fwd_lfes[ip_version]) { lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, lfei[0]); if (lfei[0] == new_lfei) { found = 1; break; } } if (is_add) { if (!found) vec_add1 (lgm->native_fwd_lfes[ip_version], new_lfei); } else { if (found) vec_del1 (lgm->native_fwd_lfes[ip_version], lfei[0]); } } static index_t create_fib_entries (lisp_gpe_fwd_entry_t * lfe) { fib_node_index_t fi; fib_entry_t *fe; lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); dpo_proto_t dproto; ip_prefix_t ippref; fib_prefix_t fib_prefix; u8 ip_version = ip_prefix_version (&lfe->key->rmt.ippref); dproto = (ip_version == IP4 ? DPO_PROTO_IP4 : DPO_PROTO_IP6); if (lfe->is_src_dst) { lfe->src_fib_index = ip_dst_fib_add_route (lfe->eid_fib_index, &lfe->key->rmt.ippref); memcpy (&ippref, &lfe->key->lcl.ippref, sizeof (ippref)); } else { lfe->src_fib_index = lfe->eid_fib_index; memcpy (&ippref, &lfe->key->rmt.ippref, sizeof (ippref)); } if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE == lfe->type) { dpo_id_t dpo = DPO_INVALID; switch (lfe->action) { case LISP_FORWARD_NATIVE: /* TODO handle route overlaps with fib and default route */ if (vec_len (lgm->native_fwd_rpath[ip_version])) { ip_prefix_to_fib_prefix (&lfe->key->rmt.ippref, &fib_prefix); fib_table_entry_update (lfe->eid_fib_index, &fib_prefix, FIB_SOURCE_LISP, FIB_ENTRY_FLAG_NONE, lgm->native_fwd_rpath[ip_version]); gpe_native_fwd_add_del_lfe (lfe, 1); break; } case LISP_NO_ACTION: /* TODO update timers? */ case LISP_SEND_MAP_REQUEST: /* insert tunnel that always sends map-request */ dpo_copy (&dpo, lisp_cp_dpo_get (dproto)); break; case LISP_DROP: /* for drop fwd entries, just add route, no need to add encap tunnel */ dpo_copy (&dpo, drop_dpo_get (dproto)); break; } fi = ip_src_fib_add_route_w_dpo (lfe->src_fib_index, &ippref, &dpo); dpo_reset (&dpo); } else { fi = ip_src_fib_add_route (lfe->src_fib_index, &ippref, lfe->paths); } fe = fib_entry_get (fi); return fe->fe_lb.dpoi_index; } static void delete_fib_entries (lisp_gpe_fwd_entry_t * lfe) { fib_prefix_t dst_fib_prefix; if (lfe->is_src_dst) ip_src_dst_fib_del_route (lfe->src_fib_index, &lfe->key->lcl.ippref, lfe->eid_fib_index, &lfe->key->rmt.ippref); else { ip_prefix_to_fib_prefix (&lfe->key->rmt.ippref, &dst_fib_prefix); fib_table_entry_delete (lfe->src_fib_index, &dst_fib_prefix, FIB_SOURCE_LISP); gpe_native_fwd_add_del_lfe (lfe, 0); } } static lisp_gpe_fwd_entry_t * find_fwd_entry (lisp_gpe_main_t * lgm, vnet_lisp_gpe_add_del_fwd_entry_args_t * a, lisp_gpe_fwd_entry_key_t * key) { uword *p; memset (key, 0, sizeof (*key)); if (GID_ADDR_IP_PREFIX == gid_address_type (&a->rmt_eid)) { /* * the ip version of the source is not set to ip6 when the * source is all zeros. force it. */ ip_prefix_version (&gid_address_ippref (&a->lcl_eid)) = ip_prefix_version (&gid_address_ippref (&a->rmt_eid)); } gid_to_dp_address (&a->rmt_eid, &key->rmt); gid_to_dp_address (&a->lcl_eid, &key->lcl); key->vni = a->vni; p = hash_get_mem (lgm->lisp_gpe_fwd_entries, key); if (NULL != p) { return (pool_elt_at_index (lgm->lisp_fwd_entry_pool, p[0])); } return (NULL); } static int lisp_gpe_fwd_entry_path_sort (void *a1, void *a2) { lisp_fwd_path_t *p1 = a1, *p2 = a2; return (p1->priority - p2->priority); } static void lisp_gpe_fwd_entry_mk_paths (lisp_gpe_fwd_entry_t * lfe, vnet_lisp_gpe_add_del_fwd_entry_args_t * a) { lisp_fwd_path_t *path; u32 index; vec_validate (lfe->paths, vec_len (a->locator_pairs) - 1); vec_foreach_index (index, a->locator_pairs) { path = &lfe->paths[index]; path->priority = a->locator_pairs[index].priority; path->weight = a->locator_pairs[index].weight; path->lisp_adj = lisp_gpe_adjacency_find_or_create_and_lock (&a->locator_pairs [index], a->dp_table, lfe->key->vni); } vec_sort_with_function (lfe->paths, lisp_gpe_fwd_entry_path_sort); } void vnet_lisp_gpe_add_fwd_counters (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, u32 fwd_entry_index) { const lisp_gpe_adjacency_t *ladj; lisp_fwd_path_t *path; lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); u8 *dummy_elt; lisp_gpe_fwd_entry_t *lfe; lisp_gpe_fwd_entry_key_t fe_key; lisp_stats_key_t key; lfe = find_fwd_entry (lgm, a, &fe_key); if (!lfe) return; if (LISP_GPE_FWD_ENTRY_TYPE_NORMAL != lfe->type) return; memset (&key, 0, sizeof (key)); key.fwd_entry_index = fwd_entry_index; vec_foreach (path, lfe->paths) { ladj = lisp_gpe_adjacency_get (path->lisp_adj); key.tunnel_index = ladj->tunnel_index; lisp_stats_key_t *key_copy = clib_mem_alloc (sizeof (*key_copy)); memcpy (key_copy, &key, sizeof (*key_copy)); pool_get (lgm->dummy_stats_pool, dummy_elt); hash_set_mem (lgm->lisp_stats_index_by_key, key_copy, dummy_elt - lgm->dummy_stats_pool); vlib_validate_combined_counter (&lgm->counters, dummy_elt - lgm->dummy_stats_pool); vlib_zero_combined_counter (&lgm->counters, dummy_elt - lgm->dummy_stats_pool); } } /** * @brief Add/Delete LISP IP forwarding entry. * * creation of forwarding entries for IP LISP overlay: * * @param[in] lgm Reference to @ref lisp_gpe_main_t. * @param[in] a Parameters for building the forwarding entry. * * @return 0 on success. */ static int add_ip_fwd_entry (lisp_gpe_main_t * lgm, vnet_lisp_gpe_add_del_fwd_entry_args_t * a) { lisp_gpe_fwd_entry_key_t key; lisp_gpe_fwd_entry_t *lfe; fib_protocol_t fproto; lfe = find_fwd_entry (lgm, a, &key); if (NULL != lfe) /* don't support updates */ return VNET_API_ERROR_INVALID_VALUE; pool_get (lgm->lisp_fwd_entry_pool, lfe); memset (lfe, 0, sizeof (*lfe)); lfe->key = clib_mem_alloc (sizeof (key)); memcpy (lfe->key, &key, sizeof (key)); hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key, lfe - lgm->lisp_fwd_entry_pool); a->fwd_entry_index = lfe - lgm->lisp_fwd_entry_pool; fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6); lfe->type = (a->is_negative ? LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE : LISP_GPE_FWD_ENTRY_TYPE_NORMAL); lfe->tenant = lisp_gpe_tenant_find_or_create (lfe->key->vni); lfe->eid_table_id = a->table_id; lfe->eid_fib_index = fib_table_find_or_create_and_lock (fproto, lfe->eid_table_id); lfe->is_src_dst = a->is_src_dst; if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) { lisp_gpe_fwd_entry_mk_paths (lfe, a); } else { lfe->action = a->action; } lfe->dpoi_index = create_fib_entries (lfe); return (0); } static void del_ip_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe) { lisp_fwd_path_t *path; fib_protocol_t fproto; if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) { vec_foreach (path, lfe->paths) { lisp_gpe_adjacency_unlock (path->lisp_adj); } } delete_fib_entries (lfe); fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6); fib_table_unlock (lfe->eid_fib_index, fproto); hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key); clib_mem_free (lfe->key); pool_put (lgm->lisp_fwd_entry_pool, lfe); } /** * @brief Add/Delete LISP IP forwarding entry. * * removal of forwarding entries for IP LISP overlay: * * @param[in] lgm Reference to @ref lisp_gpe_main_t. * @param[in] a Parameters for building the forwarding entry. * * @return 0 on success. */ static int del_ip_fwd_entry (lisp_gpe_main_t * lgm, vnet_lisp_gpe_add_del_fwd_entry_args_t * a) { lisp_gpe_fwd_entry_key_t key; lisp_gpe_fwd_entry_t *lfe; lfe = find_fwd_entry (lgm, a, &key); if (NULL == lfe) /* no such entry */ return VNET_API_ERROR_INVALID_VALUE; del_ip_fwd_entry_i (lgm, lfe); return (0); } static void make_mac_fib_key (BVT (clib_bihash_kv) * kv, u16 bd_index, u8 src_mac[6], u8 dst_mac[6]) { kv->key[0] = (((u64) bd_index) << 48) | mac_to_u64 (dst_mac); kv->key[1] = mac_to_u64 (src_mac); kv->key[2] = 0; } /** * @brief Lookup L2 SD FIB entry * * Does a vni + dest + source lookup in the L2 LISP FIB. If the lookup fails * it tries a second time with source set to 0 (i.e., a simple dest lookup). * * @param[in] lgm Reference to @ref lisp_gpe_main_t. * @param[in] bd_index Bridge domain index. * @param[in] src_mac Source mac address. * @param[in] dst_mac Destination mac address. * * @return index of mapping matching the lookup key. */ index_t lisp_l2_fib_lookup (lisp_gpe_main_t * lgm, u16 bd_index, u8 src_mac[6], u8 dst_mac[6]) { int rv; BVT (clib_bihash_kv) kv, value; make_mac_fib_key (&kv, bd_index, src_mac, dst_mac); rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value); /* no match, try with src 0, catch all for dst */ if (rv != 0) { kv.key[1] = 0; rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value); if (rv == 0) return value.value; } else return value.value; return lisp_gpe_main.l2_lb_cp_lkup.dpoi_index; } /** * @brief Add/del L2 SD FIB entry * * Inserts value in L2 FIB keyed by vni + dest + source. If entry is * overwritten the associated value is returned. * * @param[in] lgm Reference to @ref lisp_gpe_main_t. * @param[in] bd_index Bridge domain index. * @param[in] src_mac Source mac address. * @param[in] dst_mac Destination mac address. * @param[in] val Value to add. * @param[in] is_add Add/del flag. * * @return ~0 or value of overwritten entry. */ static u32 lisp_l2_fib_add_del_entry (u16 bd_index, u8 src_mac[6], u8 dst_mac[6], const dpo_id_t * dpo, u8 is_add) { lisp_gpe_main_t *lgm = &lisp_gpe_main; BVT (clib_bihash_kv) kv, value; u32 old_val = ~0; make_mac_fib_key (&kv, bd_index, src_mac, dst_mac); if (BV (clib_bihash_search) (&lgm->l2_fib, &kv, &value) == 0) old_val = value.value; if (!is_add) BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 0 /* is_add */ ); else { kv.value = dpo->dpoi_index; BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 1 /* is_add */ ); } return old_val; } #define L2_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024) #define L2_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20) static void l2_fib_init (lisp_gpe_main_t * lgm) { index_t lbi; BV (clib_bihash_init) (&lgm->l2_fib, "l2 fib", 1 << max_log2 (L2_FIB_DEFAULT_HASH_NUM_BUCKETS), L2_FIB_DEFAULT_HASH_MEMORY_SIZE); /* * the result from a 'miss' in a L2 Table */ lbi = load_balance_create (1, DPO_PROTO_ETHERNET, 0); load_balance_set_bucket (lbi, 0, lisp_cp_dpo_get (DPO_PROTO_ETHERNET)); dpo_set (&lgm->l2_lb_cp_lkup, DPO_LOAD_BALANCE, DPO_PROTO_ETHERNET, lbi); } static void del_l2_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe) { lisp_fwd_path_t *path; if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) { vec_foreach (path, lfe->paths) { lisp_gpe_adjacency_unlock (path->lisp_adj); } fib_path_list_child_remove (lfe->l2.path_list_index, lfe->l2.child_index); } lisp_l2_fib_add_del_entry (lfe->l2.eid_bd_index, fid_addr_mac (&lfe->key->lcl), fid_addr_mac (&lfe->key->rmt), NULL, 0); hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key); clib_mem_free (lfe->key); pool_put (lgm->lisp_fwd_entry_pool, lfe); } /** * @brief Delete LISP L2 forwarding entry. * * Coordinates the removal of forwarding entries for L2 LISP overlay: * * @param[in] lgm Reference to @ref lisp_gpe_main_t. * @param[in] a Parameters for building the forwarding entry. * * @return 0 on success. */ static int del_l2_fwd_entry (lisp_gpe_main_t * lgm, vnet_lisp_gpe_add_del_fwd_entry_args_t * a) { lisp_gpe_fwd_entry_key_t key; lisp_gpe_fwd_entry_t *lfe; lfe = find_fwd_entry (lgm, a, &key); if (NULL == lfe) return VNET_API_ERROR_INVALID_VALUE; del_l2_fwd_entry_i (lgm, lfe); return (0); } /** * @brief Construct and insert the forwarding information used by an L2 entry */ static void lisp_gpe_l2_update_fwding (lisp_gpe_fwd_entry_t * lfe) { lisp_gpe_main_t *lgm = &lisp_gpe_main; dpo_id_t dpo = DPO_INVALID; if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) { fib_path_list_contribute_forwarding (lfe->l2.path_list_index, FIB_FORW_CHAIN_TYPE_ETHERNET, &lfe->l2.dpo); dpo_copy (&dpo, &lfe->l2.dpo); } else { switch (lfe->action) { case SEND_MAP_REQUEST: dpo_copy (&dpo, &lgm->l2_lb_cp_lkup); break; case NO_ACTION: case FORWARD_NATIVE: case DROP: dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_ETHERNET)); } } /* add entry to l2 lisp fib */ lisp_l2_fib_add_del_entry (lfe->l2.eid_bd_index, fid_addr_mac (&lfe->key->lcl), fid_addr_mac (&lfe->key->rmt), &dpo, 1); lfe->dpoi_index = dpo.dpoi_index; dpo_reset (&dpo); } /** * @brief Add LISP L2 forwarding entry. * * Coordinates the creation of forwarding entries for L2 LISP overlay: * creates lisp-gpe tunnel and injects new entry in Source/Dest L2 FIB. * * @param[in] lgm Reference to @ref lisp_gpe_main_t. * @param[in] a Parameters for building the forwarding entry. * * @return 0 on success. */ static int add_l2_fwd_entry (lisp_gpe_main_t * lgm, vnet_lisp_gpe_add_del_fwd_entry_args_t * a) { lisp_gpe_fwd_entry_key_t key; bd_main_t *bdm = &bd_main; lisp_gpe_fwd_entry_t *lfe; uword *bd_indexp; bd_indexp = hash_get (bdm->bd_index_by_bd_id, a->bd_id); if (!bd_indexp) { clib_warning ("bridge domain %d doesn't exist", a->bd_id); return -1; } lfe = find_fwd_entry (lgm, a, &key); if (NULL != lfe) /* don't support updates */ return VNET_API_ERROR_INVALID_VALUE; pool_get (lgm->lisp_fwd_entry_pool, lfe); memset (lfe, 0, sizeof (*lfe)); lfe->key = clib_mem_alloc (sizeof (key)); memcpy (lfe->key, &key, sizeof (key)); hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key, lfe - lgm->lisp_fwd_entry_pool); a->fwd_entry_index = lfe - lgm->lisp_fwd_entry_pool; lfe->type = (a->is_negative ? LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE : LISP_GPE_FWD_ENTRY_TYPE_NORMAL); lfe->l2.eid_bd_id = a->bd_id; lfe->l2.eid_bd_index = bd_indexp[0]; lfe->tenant = lisp_gpe_tenant_find_or_create (lfe->key->vni); if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) { fib_route_path_t *rpaths; /* * Make the sorted array of LISP paths with their resp. adjacency */ lisp_gpe_fwd_entry_mk_paths (lfe, a); /* * From the LISP paths, construct a FIB path list that will * contribute a load-balance. */ rpaths = lisp_gpe_mk_fib_paths (lfe->paths); lfe->l2.path_list_index = fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, rpaths); /* * become a child of the path-list so we receive updates when * its forwarding state changes. this includes an implicit lock. */ lfe->l2.child_index = fib_path_list_child_add (lfe->l2.path_list_index, FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, lfe - lgm->lisp_fwd_entry_pool); } else { lfe->action = a->action; } lisp_gpe_l2_update_fwding (lfe); return 0; } /** * @brief Lookup NSH SD FIB entry * * Does an SPI+SI lookup in the NSH LISP FIB. * * @param[in] lgm Reference to @ref lisp_gpe_main_t. * @param[in] spi_si SPI + SI. * * @return next node index. */ const dpo_id_t * lisp_nsh_fib_lookup (lisp_gpe_main_t * lgm, u32 spi_si_net_order) { int rv; BVT (clib_bihash_kv) kv, value; memset (&kv, 0, sizeof (kv)); kv.key[0] = spi_si_net_order; rv = BV (clib_bihash_search_inline_2) (&lgm->nsh_fib, &kv, &value); if (rv != 0) { return lgm->nsh_cp_lkup; } else { lisp_gpe_fwd_entry_t *lfe; lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, value.value); return &lfe->nsh.choice; } } /** * @brief Add/del NSH FIB entry * * Inserts value in NSH FIB keyed by SPI+SI. If entry is * overwritten the associated value is returned. * * @param[in] lgm Reference to @ref lisp_gpe_main_t. * @param[in] spi_si SPI + SI. * @param[in] dpo Load balanced mapped to SPI + SI * * @return ~0 or value of overwritten entry. */ static u32 lisp_nsh_fib_add_del_entry (u32 spi_si_host_order, u32 lfei, u8 is_add) { lisp_gpe_main_t *lgm = &lisp_gpe_main; BVT (clib_bihash_kv) kv, value; u32 old_val = ~0; memset (&kv, 0, sizeof (kv)); kv.key[0] = clib_host_to_net_u32 (spi_si_host_order); kv.value = 0ULL; if (BV (clib_bihash_search) (&lgm->nsh_fib, &kv, &value) == 0) old_val = value.value; if (!is_add) BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 0 /* is_add */ ); else { kv.value = lfei; BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 1 /* is_add */ ); } return old_val; } #define NSH_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024) #define NSH_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20) static void nsh_fib_init (lisp_gpe_main_t * lgm) { BV (clib_bihash_init) (&lgm->nsh_fib, "nsh fib", 1 << max_log2 (NSH_FIB_DEFAULT_HASH_NUM_BUCKETS), NSH_FIB_DEFAULT_HASH_MEMORY_SIZE); /* * the result from a 'miss' in a NSH Table */ lgm->nsh_cp_lkup = lisp_cp_dpo_get (DPO_PROTO_NSH); } static void del_nsh_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe) { lisp_fwd_path_t *path; if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) { vec_foreach (path, lfe->paths) { lisp_gpe_adjacency_unlock (path->lisp_adj); } fib_path_list_child_remove (lfe->nsh.path_list_index, lfe->nsh.child_index); dpo_reset (&lfe->nsh.choice); } lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt), (u32) ~ 0, 0); hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key); clib_mem_free (lfe->key); pool_put (lgm->lisp_fwd_entry_pool, lfe); } /** * @brief Delete LISP NSH forwarding entry. * * Coordinates the removal of forwarding entries for NSH LISP overlay: * * @param[in] lgm Reference to @ref lisp_gpe_main_t. * @param[in] a Parameters for building the forwarding entry. * * @return 0 on success. */ static int del_nsh_fwd_entry (lisp_gpe_main_t * lgm, vnet_lisp_gpe_add_del_fwd_entry_args_t * a) { lisp_gpe_fwd_entry_key_t key; lisp_gpe_fwd_entry_t *lfe; lfe = find_fwd_entry (lgm, a, &key); if (NULL == lfe) return VNET_API_ERROR_INVALID_VALUE; del_nsh_fwd_entry_i (lgm, lfe); return (0); } /** * @brief Construct and insert the forwarding information used by an NSH entry */ static void lisp_gpe_nsh_update_fwding (lisp_gpe_fwd_entry_t * lfe) { lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); dpo_id_t dpo = DPO_INVALID; vnet_hw_interface_t *hi; uword *hip; if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) { fib_path_list_contribute_forwarding (lfe->nsh.path_list_index, FIB_FORW_CHAIN_TYPE_NSH, &lfe->nsh.dpo); /* * LISP encap is always the same for this SPI+SI so we do that hash now * and stack on the choice. */ if (DPO_LOAD_BALANCE == lfe->nsh.dpo.dpoi_type) { const dpo_id_t *tmp; const load_balance_t *lb; int hash; lb = load_balance_get (lfe->nsh.dpo.dpoi_index); hash = fid_addr_nsh (&lfe->key->rmt) % lb->lb_n_buckets; tmp = load_balance_get_bucket_i (lb, hash & lb->lb_n_buckets_minus_1); dpo_copy (&dpo, tmp); } } else { switch (lfe->action) { case SEND_MAP_REQUEST: dpo_copy (&dpo, lgm->nsh_cp_lkup); break; case NO_ACTION: case FORWARD_NATIVE: case DROP: dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_NSH)); } } /* We have only one nsh-lisp interface (no NSH virtualization) */ hip = hash_get (lgm->nsh_ifaces.hw_if_index_by_dp_table, 0); if (hip) { hi = vnet_get_hw_interface (lgm->vnet_main, hip[0]); dpo_stack_from_node (hi->tx_node_index, &lfe->nsh.choice, &dpo); } /* add entry to nsh lisp fib */ lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt), lfe - lgm->lisp_fwd_entry_pool, 1); dpo_reset (&dpo); } /** * @brief Add LISP NSH forwarding entry. * * Coordinates the creation of forwarding entries for L2 LISP overlay: * creates lisp-gpe tunnel and injects new entry in Source/Dest L2 FIB. * * @param[in] lgm Reference to @ref lisp_gpe_main_t. * @param[in] a Parameters for building the forwarding entry. * * @return 0 on success. */ static int add_nsh_fwd_entry (lisp_gpe_main_t * lgm, vnet_lisp_gpe_add_del_fwd_entry_args_t * a) { lisp_gpe_fwd_entry_key_t key; lisp_gpe_fwd_entry_t *lfe; lfe = find_fwd_entry (lgm, a, &key); if (NULL != lfe) /* don't support updates */ return VNET_API_ERROR_INVALID_VALUE; pool_get (lgm->lisp_fwd_entry_pool, lfe); memset (lfe, 0, sizeof (*lfe)); lfe->key = clib_mem_alloc (sizeof (key)); memcpy (lfe->key, &key, sizeof (key)); hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key, lfe - lgm->lisp_fwd_entry_pool); a->fwd_entry_index = lfe - lgm->lisp_fwd_entry_pool; lfe->type = (a->is_negative ? LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE : LISP_GPE_FWD_ENTRY_TYPE_NORMAL); lfe->tenant = 0; if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type) { fib_route_path_t *rpaths; /* * Make the sorted array of LISP paths with their resp. adjacency */ lisp_gpe_fwd_entry_mk_paths (lfe, a); /* * From the LISP paths, construct a FIB path list that will * contribute a load-balance. */ rpaths = lisp_gpe_mk_fib_paths (lfe->paths); lfe->nsh.path_list_index = fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, rpaths); /* * become a child of the path-list so we receive updates when * its forwarding state changes. this includes an implicit lock. */ lfe->nsh.child_index = fib_path_list_child_add (lfe->nsh.path_list_index, FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, lfe - lgm->lisp_fwd_entry_pool); } else { lfe->action = a->action; } lisp_gpe_nsh_update_fwding (lfe); return 0; } /** * @brief conver from the embedded fib_node_t struct to the LSIP entry */ static lisp_gpe_fwd_entry_t * lisp_gpe_fwd_entry_from_fib_node (fib_node_t * node) { return ((lisp_gpe_fwd_entry_t *) (((char *) node) - STRUCT_OFFSET_OF (lisp_gpe_fwd_entry_t, node))); } /** * @brief Function invoked during a backwalk of the FIB graph */ static fib_node_back_walk_rc_t lisp_gpe_fib_node_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx) { lisp_gpe_fwd_entry_t *lfe = lisp_gpe_fwd_entry_from_fib_node (node); if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_MAC) lisp_gpe_l2_update_fwding (lfe); else if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_NSH) lisp_gpe_nsh_update_fwding (lfe); return (FIB_NODE_BACK_WALK_CONTINUE); } /** * @brief Get a fib_node_t struct from the index of a LISP fwd entry */ static fib_node_t * lisp_gpe_fwd_entry_get_fib_node (fib_node_index_t index) { lisp_gpe_main_t *lgm = &lisp_gpe_main; lisp_gpe_fwd_entry_t *lfe; lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, index); return (&(lfe->node)); } /** * @brief An indication from the graph that the last lock has gone */ static void lisp_gpe_fwd_entry_fib_node_last_lock_gone (fib_node_t * node) { /* We don't manage the locks of the LISP objects via the graph, since * this object has no children. so this is a no-op. */ } /** * @brief Virtual function table to register with FIB for the LISP type */ const static fib_node_vft_t lisp_fwd_vft = { .fnv_get = lisp_gpe_fwd_entry_get_fib_node, .fnv_last_lock = lisp_gpe_fwd_entry_fib_node_last_lock_gone, .fnv_back_walk = lisp_gpe_fib_node_back_walk, }; /** * @brief Forwarding entry create/remove dispatcher. * * Calls l2 or l3 forwarding entry add/del function based on input data. * * @param[in] a Forwarding entry parameters. * @param[out] hw_if_indexp NOT USED * * @return 0 on success. */ int vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, u32 * hw_if_indexp) { lisp_gpe_main_t *lgm = &lisp_gpe_main; u8 type; if (vnet_lisp_gpe_enable_disable_status () == 0) { clib_warning ("LISP is disabled!"); return VNET_API_ERROR_LISP_DISABLED; } type = gid_address_type (&a->rmt_eid); switch (type) { case GID_ADDR_IP_PREFIX: if (a->is_add) return add_ip_fwd_entry (lgm, a); else return del_ip_fwd_entry (lgm, a); break; case GID_ADDR_MAC: if (a->is_add) return add_l2_fwd_entry (lgm, a); else return del_l2_fwd_entry (lgm, a); case GID_ADDR_NSH: if (a->is_add) return add_nsh_fwd_entry (lgm, a); else return del_nsh_fwd_entry (lgm, a); default: clib_warning ("Forwarding entries for type %d not supported!", type); return -1; } } int vnet_lisp_flush_stats (void) { lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); vlib_combined_counter_main_t *cm = &lgm->counters; u32 i; for (i = 0; i < vlib_combined_counter_n_counters (cm); i++) vlib_zero_combined_counter (cm, i); return 0; } static void lisp_del_adj_stats (lisp_gpe_main_t * lgm, u32 fwd_entry_index, u32 ti) { hash_pair_t *hp; lisp_stats_key_t key; void *key_copy; uword *p; u8 *s; memset (&key, 0, sizeof (key)); key.fwd_entry_index = fwd_entry_index; key.tunnel_index = ti; p = hash_get_mem (lgm->lisp_stats_index_by_key, &key); if (p) { s = pool_elt_at_index (lgm->dummy_stats_pool, p[0]); hp = hash_get_pair (lgm->lisp_stats_index_by_key, &key); key_copy = (void *) (hp->key); hash_unset_mem (lgm->lisp_stats_index_by_key, &key); clib_mem_free (key_copy); pool_put (lgm->dummy_stats_pool, s); } } void vnet_lisp_gpe_del_fwd_counters (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, u32 fwd_entry_index) { lisp_gpe_main_t *lgm = &lisp_gpe_main; lisp_gpe_fwd_entry_key_t fe_key; lisp_gpe_fwd_entry_t *lfe; lisp_fwd_path_t *path; const lisp_gpe_adjacency_t *ladj; lfe = find_fwd_entry (lgm, a, &fe_key); if (!lfe) return; if (LISP_GPE_FWD_ENTRY_TYPE_NORMAL != lfe->type) return; vec_foreach (path, lfe->paths) { ladj = lisp_gpe_adjacency_get (path->lisp_adj); lisp_del_adj_stats (lgm, fwd_entry_index, ladj->tunnel_index); } } /** * @brief Flush all the forwrding entries */ void vnet_lisp_gpe_fwd_entry_flush (void) { lisp_gpe_main_t *lgm = &lisp_gpe_main; lisp_gpe_fwd_entry_t *lfe; /* *INDENT-OFF* */ pool_foreach (lfe, lgm->lisp_fwd_entry_pool, ({ switch (fid_addr_type(&lfe->key->rmt)) { case FID_ADDR_MAC: del_l2_fwd_entry_i (lgm, lfe); break; case FID_ADDR_IP_PREF: del_ip_fwd_entry_i (lgm, lfe); break; case FID_ADDR_NSH: del_nsh_fwd_entry_i (lgm, lfe); break; } })); /* *INDENT-ON* */ } static u8 * format_lisp_fwd_path (u8 * s, va_list ap) { lisp_fwd_path_t *lfp = va_arg (ap, lisp_fwd_path_t *); s = format (s, "weight:%d ", lfp->weight); s = format (s, "adj:[%U]\n", format_lisp_gpe_adjacency, lisp_gpe_adjacency_get (lfp->lisp_adj), LISP_GPE_ADJ_FORMAT_FLAG_NONE); return (s); } typedef enum lisp_gpe_fwd_entry_format_flag_t_ { LISP_GPE_FWD_ENTRY_FORMAT_NONE = (0 << 0), LISP_GPE_FWD_ENTRY_FORMAT_DETAIL = (1 << 1), } lisp_gpe_fwd_entry_format_flag_t; static u8 * format_lisp_gpe_fwd_entry (u8 * s, va_list ap) { lisp_gpe_main_t *lgm = &lisp_gpe_main; lisp_gpe_fwd_entry_t *lfe = va_arg (ap, lisp_gpe_fwd_entry_t *); lisp_gpe_fwd_entry_format_flag_t flags = va_arg (ap, lisp_gpe_fwd_entry_format_flag_t); s = format (s, "VNI:%d VRF:%d EID: %U -> %U [index:%d]", lfe->key->vni, lfe->eid_table_id, format_fid_address, &lfe->key->lcl, format_fid_address, &lfe->key->rmt, lfe - lgm->lisp_fwd_entry_pool); if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE == lfe->type) { s = format (s, "\n Negative - action:%U", format_negative_mapping_action, lfe->action); } else { lisp_fwd_path_t *path; s = format (s, "\n via:"); vec_foreach (path, lfe->paths) { s = format (s, "\n %U", format_lisp_fwd_path, path); } } if (flags & LISP_GPE_FWD_ENTRY_FORMAT_DETAIL) { switch (fid_addr_type (&lfe->key->rmt)) { case FID_ADDR_MAC: s = format (s, " fib-path-list:%d\n", lfe->l2.path_list_index); s = format (s, " dpo:%U\n", format_dpo_id, &lfe->l2.dpo, 0); break; case FID_ADDR_NSH: s = format (s, " fib-path-list:%d\n", lfe->nsh.path_list_index); s = format (s, " dpo:%U\n", format_dpo_id, &lfe->nsh.dpo, 0); break; case FID_ADDR_IP_PREF: break; } } return (s); } static clib_error_t * lisp_gpe_fwd_entry_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { lisp_gpe_main_t *lgm = &lisp_gpe_main; lisp_gpe_fwd_entry_t *lfe; index_t index; u32 vni = ~0; if (unformat (input, "vni %d", &vni)) ; else if (unformat (input, "%d", &index)) { if (!pool_is_free_index (lgm->lisp_fwd_entry_pool, index)) { lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, index); vlib_cli_output (vm, "[%d@] %U", index, format_lisp_gpe_fwd_entry, lfe, LISP_GPE_FWD_ENTRY_FORMAT_DETAIL); } else { vlib_cli_output (vm, "entry %d invalid", index); } return (NULL); } /* *INDENT-OFF* */ pool_foreach (lfe, lgm->lisp_fwd_entry_pool, ({ if ((vni == ~0) || (lfe->key->vni == vni)) vlib_cli_output (vm, "%U", format_lisp_gpe_fwd_entry, lfe, LISP_GPE_FWD_ENTRY_FORMAT_NONE); })); /* *INDENT-ON* */ return (NULL); } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (lisp_gpe_fwd_entry_show_command, static) = { .path = "show gpe entry", .short_help = "show gpe entry vni <vni> vrf <vrf> [leid <leid>] reid <reid>", .function = lisp_gpe_fwd_entry_show, }; /* *INDENT-ON* */ clib_error_t * lisp_gpe_fwd_entry_init (vlib_main_t * vm) { lisp_gpe_main_t *lgm = &lisp_gpe_main; clib_error_t *error = NULL; if ((error = vlib_call_init_function (vm, lisp_cp_dpo_module_init))) return (error); l2_fib_init (lgm); nsh_fib_init (lgm); fib_node_register_type (FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, &lisp_fwd_vft); return (error); } u32 * vnet_lisp_gpe_get_fwd_entry_vnis (void) { lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); lisp_gpe_fwd_entry_t *lfe; u32 *vnis = 0; /* *INDENT-OFF* */ pool_foreach (lfe, lgm->lisp_fwd_entry_pool, ({ hash_set (vnis, lfe->key->vni, 0); })); /* *INDENT-ON* */ return vnis; } lisp_api_gpe_fwd_entry_t * vnet_lisp_gpe_fwd_entries_get_by_vni (u32 vni) { lisp_gpe_main_t *lgm = &lisp_gpe_main; lisp_gpe_fwd_entry_t *lfe; lisp_api_gpe_fwd_entry_t *entries = 0, e; /* *INDENT-OFF* */ pool_foreach (lfe, lgm->lisp_fwd_entry_pool, ({ if (lfe->key->vni == vni) { memset (&e, 0, sizeof (e)); e.dp_table = lfe->eid_table_id; e.vni = lfe->key->vni; if (lfe->type == LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE) e.action = lfe->action; e.fwd_entry_index = lfe - lgm->lisp_fwd_entry_pool; memcpy (&e.reid, &lfe->key->rmt, sizeof (e.reid)); memcpy (&e.leid, &lfe->key->lcl, sizeof (e.leid)); vec_add1 (entries, e); } })); /* *INDENT-ON* */ return entries; } int vnet_lisp_gpe_get_fwd_stats (vnet_lisp_gpe_add_del_fwd_entry_args_t * a, vlib_counter_t * c) { lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main (); lisp_gpe_fwd_entry_t *lfe; lisp_gpe_fwd_entry_key_t unused; lfe = find_fwd_entry (lgm, a, &unused); if (NULL == lfe) return -1; if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE == lfe->type) return -1; if (~0 == lfe->dpoi_index) return -1; vlib_get_combined_counter (&load_balance_main.lbm_to_counters, lfe->dpoi_index, c); return 0; } VLIB_INIT_FUNCTION (lisp_gpe_fwd_entry_init); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */