aboutsummaryrefslogtreecommitdiffstats
path: root/docs/content/methodology/vpp_forwarding_modes.md
blob: 85284a3ec4a94f405de1dfd048101ab31eb75cab (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
---
bookToc: false
title: "VPP Forwarding Modes"
weight: 3
---

# VPP Forwarding Modes

VPP is tested in a number of L2, IPv4 and IPv6 packet lookup and
forwarding modes. Within each mode baseline and scale tests are
executed, the latter with varying number of FIB entries.

## L2 Ethernet Switching

VPP is tested in three L2 forwarding modes:

- *l2patch*: L2 patch, the fastest point-to-point L2 path that loops
  packets between two interfaces without any Ethernet frame checks or
  lookups.
- *l2xc*: L2 cross-connect, point-to-point L2 path with all Ethernet
  frame checks, but no MAC learning and no MAC lookup.
- *l2bd*: L2 bridge-domain, multipoint-to-multipoint L2 path with all
  Ethernet frame checks, with MAC learning (unless static MACs are used)
  and MAC lookup.

l2bd tests are executed in baseline and scale configurations:

- *l2bdbase*: Two MAC FIB entries are learned by VPP to enable packet
  switching between two interfaces in two directions. VPP L2 switching
  is tested with 254 IPv4 unique flows per direction, varying IPv4
  source address per flow in order to invoke RSS based packet
  distribution across VPP workers. The same source and destination MAC
  address is used for all flows per direction. IPv4 source address is
  incremented for every packet.

- *l2bdscale*: A high number of MAC FIB entries are learned by VPP to
  enable packet switching between two interfaces in two directions.
  Tested MAC FIB sizes include: i) 10k with 5k unique flows per
  direction, ii) 100k with 2 x 50k flows and iii) 1M with 2 x 500k
  flows. Unique flows are created by using distinct source and
  destination MAC addresses that are changed for every packet using
  incremental ordering, making VPP learn (or refresh) distinct src MAC
  entries and look up distinct dst MAC entries for every packet. For
  details, see
  [Packet Flow Ordering]({{< ref "packet_flow_ordering#Packet Flow Ordering" >}}).

Ethernet wire encapsulations tested include: untagged, dot1q, dot1ad.

## IPv4 Routing

IPv4 routing tests are executed in baseline and scale configurations:

- *ip4base*: Two /32 IPv4 FIB entries are configured in VPP to enable
  packet routing between two interfaces in two directions. VPP routing
  is tested with 253 IPv4 unique flows per direction, varying IPv4
  source address per flow in order to invoke RSS based packet
  distribution across VPP workers. IPv4 source address is incremented
  for every packet.

- *ip4scale*: A high number of /32 IPv4 FIB entries are configured in
  VPP. Tested IPv4 FIB sizes include: i) 20k with 10k unique flows per
  direction, ii) 200k with 2 * 100k flows and iii) 2M with 2 * 1M
  flows. Unique flows are created by using distinct IPv4 destination
  addresses that are changed for every packet, using incremental or
  random ordering. For details, see
  [Packet Flow Ordering]({{< ref "packet_flow_ordering#Packet Flow Ordering" >}}).

## IPv6 Routing

Similarly to IPv4, IPv6 routing tests are executed in baseline and scale
configurations:

- *ip6base*: Two /128 IPv4 FIB entries are configured in VPP to enable
  packet routing between two interfaces in two directions. VPP routing
  is tested with 253 IPv6 unique flows per direction, varying IPv6
  source address per flow in order to invoke RSS based packet
  distribution across VPP workers. IPv6 source address is incremented
  for every packet.

- *ip4scale*: A high number of /128 IPv6 FIB entries are configured in
  VPP. Tested IPv6 FIB sizes include: i) 20k with 10k unique flows per
  direction, ii) 200k with 2 * 100k flows and iii) 2M with 2 * 1M
  flows. Unique flows are created by using distinct IPv6 destination
  addresses that are changed for every packet, using incremental or
  random ordering. For details, see
  [Packet Flow Ordering]({{< ref "packet_flow_ordering#Packet Flow Ordering" >}}).

## SRv6 Routing

SRv6 routing tests are executed in a number of baseline configurations,
in each case SR policy and steering policy are configured for one
direction and one (or two) SR behaviours (functions) in the other
directions:

- *srv6enc1sid*: One SID (no SRH present), one SR function - End.
- *srv6enc2sids*: Two SIDs (SRH present), two SR functions - End and
  End.DX6.
- *srv6enc2sids-nodecaps*: Two SIDs (SRH present) without decapsulation,
  one SR function - End.
- *srv6proxy-dyn*: Dynamic SRv6 proxy, one SR function - End.AD.
- *srv6proxy-masq*: Masquerading SRv6 proxy, one SR function - End.AM.
- *srv6proxy-stat*: Static SRv6 proxy, one SR function - End.AS.

In all listed cases low number of IPv6 flows (253 per direction) is
routed by VPP.
="cp"> #include <vnet/bier/bier_update.h> #include <vnet/fib/fib_path_list.h> #include <vnet/bier/bier_fmask_db.h> #include <vnet/bier/bier_fmask.h> #include <vnet/bier/bier_table.h> bier_entry_t *bier_entry_pool; static index_t bier_entry_get_index (const bier_entry_t *be) { return (be - bier_entry_pool); } static fib_path_list_walk_rc_t bier_entry_link_walk (fib_node_index_t pl_index, fib_node_index_t path_index, void *arg) { bier_entry_t *be = arg; index_t bfmi; bfmi = fib_path_get_resolving_index(path_index); bier_fmask_link(bfmi, be->be_bp); return (FIB_PATH_LIST_WALK_CONTINUE); } static fib_path_list_walk_rc_t bier_entry_unlink_walk (fib_node_index_t pl_index, fib_node_index_t path_index, void *arg) { bier_entry_t *be = arg; index_t bfmi; bfmi = fib_path_get_resolving_index(path_index); bier_fmask_unlink(bfmi, be->be_bp); return (FIB_PATH_LIST_WALK_CONTINUE); } index_t bier_entry_create (index_t bti, bier_bp_t bp) { bier_entry_t *be; pool_get(bier_entry_pool, be); be->be_bp = bp; be->be_bti = bti; be->be_path_list = FIB_NODE_INDEX_INVALID; return (bier_entry_get_index(be)); } static void bier_entry_table_ecmp_walk_add_fmask (index_t btei, void *arg) { bier_entry_t *be = arg;; /* * choose a fmask from the entry's resolved set to add * to ECMP table's lookup table */ if (FIB_NODE_INDEX_INVALID != be->be_path_list) { const bier_table_id_t *btid; dpo_id_t dpo = DPO_INVALID; const dpo_id_t *choice; load_balance_t *lb; btid = bier_table_get_id(btei); fib_path_list_contribute_forwarding(be->be_path_list, FIB_FORW_CHAIN_TYPE_BIER, FIB_PATH_LIST_FWD_FLAG_COLLAPSE, &dpo); /* * select the appropriate bucket from the LB */ if (dpo.dpoi_type == DPO_LOAD_BALANCE) { lb = load_balance_get(dpo.dpoi_index); choice = load_balance_get_bucket_i(lb, btid->bti_ecmp & (lb->lb_n_buckets_minus_1)); } else { choice = &dpo; } if (choice->dpoi_type == DPO_BIER_FMASK) { bier_table_ecmp_set_fmask(btei, be->be_bp, choice->dpoi_index); } else { /* * any other type results in a drop, which we represent * with an empty bucket */ bier_table_ecmp_set_fmask(btei, be->be_bp, INDEX_INVALID); } dpo_reset(&dpo); } else { /* * no fmasks left. insert a drop */ bier_table_ecmp_set_fmask(btei, be->be_bp, INDEX_INVALID); } } void bier_entry_delete (index_t bei) { bier_entry_t *be; be = bier_entry_get(bei); /* * if we still have a path-list, unlink from it */ if (FIB_NODE_INDEX_INVALID != be->be_path_list) { fib_path_list_walk(be->be_path_list, bier_entry_unlink_walk, be); fib_path_list_child_remove(be->be_path_list, be->be_sibling_index); be->be_path_list = FIB_NODE_INDEX_INVALID; bier_table_ecmp_walk(be->be_bti, bier_entry_table_ecmp_walk_add_fmask, be); } pool_put(bier_entry_pool, be); } void bier_entry_path_add (index_t bei, const fib_route_path_t *rpaths) { fib_node_index_t old_pl_index; bier_entry_t *be; be = bier_entry_get(bei); old_pl_index = be->be_path_list; /* * lock the path-list so it does not go away before we unlink * from its resolved fmasks */ fib_path_list_lock(old_pl_index); if (FIB_NODE_INDEX_INVALID == be->be_path_list) { old_pl_index = FIB_NODE_INDEX_INVALID; be->be_path_list = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF), rpaths); be->be_sibling_index = fib_path_list_child_add(be->be_path_list, FIB_NODE_TYPE_BIER_ENTRY, bier_entry_get_index(be)); } else { old_pl_index = be->be_path_list; be->be_path_list = fib_path_list_copy_and_path_add(old_pl_index, (FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF), rpaths); fib_path_list_child_remove(old_pl_index, be->be_sibling_index); be->be_sibling_index = fib_path_list_child_add(be->be_path_list, FIB_NODE_TYPE_BIER_ENTRY, bier_entry_get_index(be)); } /* * link the entry's bit-position to each fmask in the new path-list * then unlink from the old. */ fib_path_list_walk(be->be_path_list, bier_entry_link_walk, be); if (FIB_NODE_INDEX_INVALID != old_pl_index) { fib_path_list_walk(old_pl_index, bier_entry_unlink_walk, be); } /* * update the ECMP tables with the new choice */ bier_table_ecmp_walk(be->be_bti, bier_entry_table_ecmp_walk_add_fmask, be); /* * symmetric unlock. The old path-list may not exist hereinafter */ fib_path_list_unlock(old_pl_index); } void bier_entry_path_update (index_t bei, const fib_route_path_t *rpaths) { fib_node_index_t old_pl_index; bier_entry_t *be; be = bier_entry_get(bei); old_pl_index = be->be_path_list; /* * lock the path-list so it does not go away before we unlink * from its resolved fmasks */ fib_path_list_lock(old_pl_index); if (FIB_NODE_INDEX_INVALID != old_pl_index) { fib_path_list_child_remove(old_pl_index, be->be_sibling_index); } be->be_path_list = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF), rpaths); be->be_sibling_index = fib_path_list_child_add(be->be_path_list, FIB_NODE_TYPE_BIER_ENTRY, bier_entry_get_index(be)); /* * link the entry's bit-position to each fmask in the new path-list * then unlink from the old. */ fib_path_list_walk(be->be_path_list, bier_entry_link_walk, be); if (FIB_NODE_INDEX_INVALID != old_pl_index) { fib_path_list_walk(old_pl_index, bier_entry_unlink_walk, be); } /* * update the ECMP tables with the new choice */ bier_table_ecmp_walk(be->be_bti, bier_entry_table_ecmp_walk_add_fmask, be); /* * symmetric unlock. The old path-list may not exist hereinafter */ fib_path_list_unlock(old_pl_index); } int bier_entry_path_remove (index_t bei, const fib_route_path_t *rpaths) { fib_node_index_t old_pl_index; bier_entry_t *be; be = bier_entry_get(bei); old_pl_index = be->be_path_list; fib_path_list_lock(old_pl_index); ASSERT (FIB_NODE_INDEX_INVALID != be->be_path_list); be->be_path_list = fib_path_list_copy_and_path_remove(old_pl_index, (FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF), rpaths); if (be->be_path_list != old_pl_index) { /* * a path was removed */ fib_path_list_child_remove(old_pl_index, be->be_sibling_index); if (FIB_NODE_INDEX_INVALID != be->be_path_list) { /* * link the entry's bit-position to each fmask in the new path-list * then unlink from the old. */ fib_path_list_walk(be->be_path_list, bier_entry_link_walk, be); be->be_sibling_index = fib_path_list_child_add(be->be_path_list, FIB_NODE_TYPE_BIER_ENTRY, bier_entry_get_index(be)); } fib_path_list_walk(old_pl_index, bier_entry_unlink_walk, be); } fib_path_list_unlock(old_pl_index); /* * update the ECMP tables with the new choice */ bier_table_ecmp_walk(be->be_bti, bier_entry_table_ecmp_walk_add_fmask, be); return (fib_path_list_get_n_paths(be->be_path_list)); } void bier_entry_contribute_forwarding(index_t bei, dpo_id_t *dpo) { bier_entry_t *be = bier_entry_get(bei); fib_path_list_contribute_forwarding(be->be_path_list, FIB_FORW_CHAIN_TYPE_BIER, FIB_PATH_LIST_FWD_FLAG_COLLAPSE, dpo); } u8* format_bier_entry (u8* s, va_list *ap) { index_t bei = va_arg(*ap, index_t); bier_show_flags_t flags = va_arg(*ap, bier_show_flags_t); bier_entry_t *be = bier_entry_get(bei); s = format(s, " bp:%d\n", be->be_bp); s = fib_path_list_format(be->be_path_list, s); if (flags & BIER_SHOW_DETAIL) { dpo_id_t dpo = DPO_INVALID; bier_entry_contribute_forwarding(bei, &dpo); s = format(s, " forwarding:\n"); s = format(s, " %U", format_dpo_id, &dpo, 2); s = format(s, "\n"); } return (s); } static fib_node_t * bier_entry_get_node (fib_node_index_t index) { bier_entry_t *be = bier_entry_get(index); return (&(be->be_node)); } static bier_entry_t* bier_entry_get_from_node (fib_node_t *node) { return ((bier_entry_t*)(((char*)node) - STRUCT_OFFSET_OF(bier_entry_t, be_node))); } static void bier_entry_last_lock_gone (fib_node_t *node) { /* * the lifetime of the entry is managed by the table. */ ASSERT(0); } /* * A back walk has reached this BIER entry */ static fib_node_back_walk_rc_t bier_entry_back_walk_notify (fib_node_t *node, fib_node_back_walk_ctx_t *ctx) { /* * re-populate the ECMP tables with new choices */ bier_entry_t *be = bier_entry_get_from_node(node); bier_table_ecmp_walk(be->be_bti, bier_entry_table_ecmp_walk_add_fmask, be); /* * no need to propagate further up the graph. */ return (FIB_NODE_BACK_WALK_CONTINUE); } /* * The BIER fmask's graph node virtual function table */ static const fib_node_vft_t bier_entry_vft = { .fnv_get = bier_entry_get_node, .fnv_last_lock = bier_entry_last_lock_gone, .fnv_back_walk = bier_entry_back_walk_notify, }; clib_error_t * bier_entry_module_init (vlib_main_t * vm) { fib_node_register_type (FIB_NODE_TYPE_BIER_ENTRY, &bier_entry_vft); return (NULL); } VLIB_INIT_FUNCTION (bier_entry_module_init);