summaryrefslogtreecommitdiffstats
path: root/src/vnet/ip/lookup.c
blob: 597de06b5e35f97e450fe01476055a36da0588af (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

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
# Copyright (c) 2018 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.

*** Settings ***
| Resource | resources/libraries/robot/performance/performance_setup.robot
| Library | resources.libraries.python.QemuUtils
| ...
| Force Tags | 3_NODE_SINGLE_LINK_TOPO | PERFTEST | HW_ENV | MRR
| ... | NIC_Intel-X710 | DOT1Q | L2BDMACLRN | BASE | VHOST | 1VM
| ... | VHOST_1024 | LBOND | LBOND_DPDK | LBOND_MODE_XOR | LBOND_LB_L34
| ... | LBOND_1L
| ...
| Suite Setup | Set up 3-node performance topology with DUT's NIC model
| ... | L2 | Intel-X710
| Suite Teardown | Tear down 3-node performance topology
| ...
| Test Setup | Set up performance test
| Test Teardown | Tear down performance mrr test with vhost and VM with dpdk-testpmd
| ... | dut1_node=${dut1} | dut1_vm_refs=${dut1_vm_refs}
| ... | dut2_node=${dut2} | dut2_vm_refs=${dut2_vm_refs}
| ...
| Test Template | Local Template
| ...
| Documentation | *Raw results L2BD test cases with vhost and dpdk link bonding*
| ...
| ... | *[Top] Network Topologies:* TG-DUT1-DUT2-TG 3-node circular topology
| ... | with single links between nodes.
| ... | *[Enc] Packet Encapsulations:* Eth-IPv4 for L2 switching of IPv4. 802.1q
| ... | tagging is applied on link between DUT1 and DUT2.
| ... | *[Cfg] DUT configuration:* DUT1 and DUT2 are configured with DPDK
| ... | link bonding (mode XOR, transmit policy l34) on link between DUT1 and
| ... | DUT2 and L2 bridge-domain with MAC learning enabled. Qemu Guest is
| ... | connected to VPP via vhost-user interfaces. Guest is running DPDK
| ... | testpmd interconnecting vhost-user interfaces using 5 cores pinned to
| ... | cpus 5-9 and 2048M memory. Testpmd is using socket-mem=1024M (512x2M
| ... | hugepages), 5 cores (1 main core and 4 cores dedicated for io),
| ... | forwarding mode is set to io, rxd/txd=1024, burst=64. DUT1, DUT2 are
| ... | tested with 2p10GE NIC X710 Fortville by Intel.
| ... | *[Ver] TG verification:* In MaxReceivedRate test TG sends traffic
| ... | at line rate and reports total received/sent packets over trial period.
| ... | Test packets are generated by TG on links to DUTs. TG traffic profile
| ... | contains two L3 flow-groups (flow-group per direction, 254 flows per
| ... | flow-group) with all packets containing Ethernet header, IPv4 header
| ... | with IP protocol=61 and static payload. MAC addresses are matching MAC
| ... | addresses of the TG node interfaces.

*** Variables ***
| ${dut1_eth_bond_if1}= | eth_bond1
| ${dut1_eth_bond_if1_name}= | BondEthernet0
| ${dut2_eth_bond_if1}= | eth_bond1
| ${dut2_eth_bond_if1_name}= | BondEthernet0
| ${subid}= | 10
| ${tag_rewrite}= | pop-1
| ${overhead}= | ${4}
# Socket names
| ${bd_id1}= | 1
| ${bd_id2}= | 2
| ${sock1}= | /tmp/sock-1-${bd_id1}
| ${sock2}= | /tmp/sock-1-${bd_id2}
# X710 bandwidth limit
| ${s_limit}= | ${10000000000}
# Traffic profile:
| ${traffic_profile}= | trex-sl-3n-ethip4-ip4src254

*** Keywords ***
| Local Template
| | [Documentation]
| | ... | [Cfg] DUT runs L2BD switching config.
| | ... | Each DUT uses ${phy_cores} physical core(s) for worker threads.
| | ... | [Ver] Measure MaxReceivedRate for ${framesize}B frames using single\
| | ... | trial throughput test.
| | ...
| | ... | *Arguments:*
| | ... | - framesize - Framesize in Bytes in integer or string (IMIX_v4_1).
| | ... | Type: integer, string
| | ... | - phy_cores - Number of physical cores. Type: integer
| | ... | - rxq - Number of RX queues, default value: ${None}. Type: integer
| | ...
| | [Arguments] | ${framesize} | ${phy_cores} | ${rxq}=${None}
| | ...
| | ${dut1_vm_refs}= | Create Dictionary
| | ${dut2_vm_refs}= | Create Dictionary
| | Set Test Variable | ${dut1_vm_refs}
| | Set Test Variable | ${dut2_vm_refs}
| | ...
| | Given Add worker threads and rxqueues to all DUTs | ${phy_cores} | ${rxq}
| | And Add PCI devices to all DUTs
| | And Add VLAN Strip Offload switch off between DUTs in 3-node single link topology
| | And Add DPDK bonded Ethernet interfaces to DUTs in 3-node single link topology
| | ${max_rate} | ${jumbo} = | Get Max Rate And Jumbo And Handle Multi Seg
| | ... | ${s_limit} | ${framesize} | overhead=${overhead}
| | And Apply startup configuration on all VPP DUTs
| | When Initialize L2 bridge domains with Vhost-User and VLAN with DPDK link bonding in a 3-node circular topology
| | ... | ${bd_id1} | ${bd_id2} | ${sock1} | ${sock2} | ${subid}
| | ... | ${tag_rewrite}
| | And Configure guest VMs with dpdk-testpmd connected via vhost-user
| | ... | vm_count=${1} | jumbo=${jumbo} | perf_qemu_qsz=${1024}
| | ... | use_tuned_cfs=${False}
| | Then Traffic should pass with maximum rate
| | ... | ${max_rate}pps | ${framesize} | ${traffic_profile}

*** Test Cases ***
| tc01-64B-1c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | 64B | 1C
| | framesize=${64} | phy_cores=${1}

| tc02-64B-2c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | 64B | 2C
| | framesize=${64} | phy_cores=${2}

| tc03-64B-4c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | 64B | 4C
| | framesize=${64} | phy_cores=${4}

| tc04-1518B-1c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | 1518B | 1C
| | framesize=${1518} | phy_cores=${1}

| tc05-1518B-2c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | 1518B | 2C
| | framesize=${1518} | phy_cores=${2}

| tc06-1518B-4c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | 1518B | 4C
| | framesize=${1518} | phy_cores=${4}

| tc07-9000B-1c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | 9000B | 1C
| | framesize=${9000} | phy_cores=${1}

| tc08-9000B-2c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | 9000B | 2C
| | framesize=${9000} | phy_cores=${2}

| tc09-9000B-4c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | 9000B | 4C
| | framesize=${9000} | phy_cores=${4}

| tc10-IMIX-1c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | IMIX | 1C
| | framesize=IMIX_v4_1 | phy_cores=${1}

| tc11-IMIX-2c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | IMIX | 2C
| | framesize=IMIX_v4_1 | phy_cores=${2}

| tc12-IMIX-4c-1lbdpdk-dot1q-l2bdbasemaclrn-eth-2vhostvr1024-1vm-mrr
| | [Tags] | IMIX | 4C
| | framesize=IMIX_v4_1
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
 * Copyright (c) 2015 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.
 */
/*
 * ip/ip_lookup.c: ip4/6 adjacency and lookup table managment
 *
 * Copyright (c) 2008 Eliot Dresselhaus
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <vnet/ip/ip.h>
#include <vnet/adj/adj.h>
#include <vnet/fib/fib_table.h>
#include <vnet/fib/ip4_fib.h>
#include <vnet/fib/ip6_fib.h>
#include <vnet/mpls/mpls.h>
#include <vnet/mfib/mfib_table.h>
#include <vnet/dpo/drop_dpo.h>
#include <vnet/dpo/classify_dpo.h>
#include <vnet/dpo/punt_dpo.h>
#include <vnet/dpo/receive_dpo.h>
#include <vnet/dpo/ip_null_dpo.h>
#include <vnet/ip/ip6_neighbor.h>

/**
 * @file
 * @brief IPv4 and IPv6 adjacency and lookup table managment.
 *
 */

clib_error_t *
ip_interface_address_add_del (ip_lookup_main_t * lm,
			      u32 sw_if_index,
			      void *addr_fib,
			      u32 address_length,
			      u32 is_del, u32 * result_if_address_index)
{
  vnet_main_t *vnm = vnet_get_main ();
  ip_interface_address_t *a, *prev, *next;
  uword *p = mhash_get (&lm->address_to_if_address_index, addr_fib);

  vec_validate_init_empty (lm->if_address_pool_index_by_sw_if_index,
			   sw_if_index, ~0);
  a = p ? pool_elt_at_index (lm->if_address_pool, p[0]) : 0;

  /* Verify given length. */
  if ((a && (address_length != a->address_length)) ||
      (address_length == 0) ||
      (lm->is_ip6 && address_length > 128) ||
      (!lm->is_ip6 && address_length > 32))
    {
      vnm->api_errno = VNET_API_ERROR_ADDRESS_LENGTH_MISMATCH;
      return clib_error_create
	("%U wrong length (expected %d) for interface %U",
	 lm->format_address_and_length, addr_fib,
	 address_length, a ? a->address_length : -1,
	 format_vnet_sw_if_index_name, vnm, sw_if_index);
    }

  if (is_del)
    {
      if (!a)
	{
	  vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
	  vnm->api_errno = VNET_API_ERROR_ADDRESS_NOT_FOUND_FOR_INTERFACE;
	  return clib_error_create ("%U not found for interface %U",
				    lm->format_address_and_length,
				    addr_fib, address_length,
				    format_vnet_sw_interface_name, vnm, si);
	}

      if (a->prev_this_sw_interface != ~0)
	{
	  prev =
	    pool_elt_at_index (lm->if_address_pool,
			       a->prev_this_sw_interface);
	  prev->next_this_sw_interface = a->next_this_sw_interface;
	}
      if (a->next_this_sw_interface != ~0)
	{
	  next =
	    pool_elt_at_index (lm->if_address_pool,
			       a->next_this_sw_interface);
	  next->prev_this_sw_interface = a->prev_this_sw_interface;

	  if (a->prev_this_sw_interface == ~0)
	    lm->if_address_pool_index_by_sw_if_index[sw_if_index] =
	      a->next_this_sw_interface;
	}

      if ((a->next_this_sw_interface == ~0)
	  && (a->prev_this_sw_interface == ~0))
	lm->if_address_pool_index_by_sw_if_index[sw_if_index] = ~0;

      mhash_unset (&lm->address_to_if_address_index, addr_fib,
		   /* old_value */ 0);
      pool_put (lm->if_address_pool, a);

      if (result_if_address_index)
	*result_if_address_index = ~0;
    }

  else if (!a)
    {
      u32 pi;			/* previous index */
      u32 ai;
      u32 hi;			/* head index */

      pool_get (lm->if_address_pool, a);
      memset (a, ~0, sizeof (a[0]));
      ai = a - lm->if_address_pool;

      hi = pi = lm->if_address_pool_index_by_sw_if_index[sw_if_index];
      prev = 0;
      while (pi != (u32) ~ 0)
	{
	  prev = pool_elt_at_index (lm->if_address_pool, pi);
	  pi = prev->next_this_sw_interface;
	}
      pi = prev ? prev - lm->if_address_pool : (u32) ~ 0;

      a->address_key = mhash_set (&lm->address_to_if_address_index,
				  addr_fib, ai, /* old_value */ 0);
      a->address_length = address_length;
      a->sw_if_index = sw_if_index;
      a->flags = 0;
      a->prev_this_sw_interface = pi;
      a->next_this_sw_interface = ~0;
      if (prev)
	prev->next_this_sw_interface = ai;

      lm->if_address_pool_index_by_sw_if_index[sw_if_index] =
	(hi != ~0) ? hi : ai;
      if (result_if_address_index)
	*result_if_address_index = ai;
    }
  else
    {
      if (result_if_address_index)
	*result_if_address_index = a - lm->if_address_pool;
    }


  return /* no error */ 0;
}

static clib_error_t *
ip_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add)
{
  vec_validate_init_empty (ip4_main.
			   lookup_main.if_address_pool_index_by_sw_if_index,
			   sw_if_index, ~0);
  vec_validate_init_empty (ip6_main.
			   lookup_main.if_address_pool_index_by_sw_if_index,
			   sw_if_index, ~0);

  return (NULL);
}

VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip_sw_interface_add_del);

void
ip_lookup_init (ip_lookup_main_t * lm, u32 is_ip6)
{
  /* Preallocate three "special" adjacencies */
  lm->adjacency_heap = adj_pool;

  if (!lm->fib_result_n_bytes)
    lm->fib_result_n_bytes = sizeof (uword);

  lm->is_ip6 = is_ip6;
  if (is_ip6)
    {
      lm->format_address_and_length = format_ip6_address_and_length;
      mhash_init (&lm->address_to_if_address_index, sizeof (uword),
		  sizeof (ip6_address_fib_t));
    }
  else
    {
      lm->format_address_and_length = format_ip4_address_and_length;
      mhash_init (&lm->address_to_if_address_index, sizeof (uword),
		  sizeof (ip4_address_fib_t));
    }

  {
    int i;

    /* Setup all IP protocols to be punted and builtin-unknown. */
    for (i = 0; i < 256; i++)
      {
	lm->local_next_by_ip_protocol[i] = IP_LOCAL_NEXT_PUNT;
	lm->builtin_protocol_by_ip_protocol[i] = IP_BUILTIN_PROTOCOL_UNKNOWN;
      }

    lm->local_next_by_ip_protocol[IP_PROTOCOL_UDP] = IP_LOCAL_NEXT_UDP_LOOKUP;
    lm->local_next_by_ip_protocol[is_ip6 ? IP_PROTOCOL_ICMP6 :
				  IP_PROTOCOL_ICMP] = IP_LOCAL_NEXT_ICMP;
    lm->builtin_protocol_by_ip_protocol[IP_PROTOCOL_UDP] =
      IP_BUILTIN_PROTOCOL_UDP;
    lm->builtin_protocol_by_ip_protocol[is_ip6 ? IP_PROTOCOL_ICMP6 :
					IP_PROTOCOL_ICMP] =
      IP_BUILTIN_PROTOCOL_ICMP;
  }
}

u8 *
format_ip_flow_hash_config (u8 * s, va_list * args)
{
  flow_hash_config_t flow_hash_config = va_arg (*args, u32);

#define _(n,v) if (flow_hash_config & v) s = format (s, "%s ", #n);
  foreach_flow_hash_bit;
#undef _

  return s;
}

u8 *
format_ip_lookup_next (u8 * s, va_list * args)
{
  /* int promotion of ip_lookup_next_t */
  ip_lookup_next_t n = va_arg (*args, int);
  char *t = 0;

  switch (n)
    {
    default:
      s = format (s, "unknown %d", n);
      return s;

    case IP_LOOKUP_NEXT_DROP:
      t = "drop";
      break;
    case IP_LOOKUP_NEXT_PUNT:
      t = "punt";
      break;
    case IP_LOOKUP_NEXT_ARP:
      t = "arp";
      break;
    case IP_LOOKUP_NEXT_MIDCHAIN:
      t = "midchain";
      break;
    case IP_LOOKUP_NEXT_GLEAN:
      t = "glean";
      break;
    case IP_LOOKUP_NEXT_MCAST:
      t = "mcast";
      break;
    case IP_LOOKUP_NEXT_REWRITE:
      break;
    }

  if (t)
    vec_add (s, t, strlen (t));

  return s;
}

u8 *
format_ip_adjacency_packet_data (u8 * s, va_list * args)
{
  u32 adj_index = va_arg (*args, u32);
  u8 *packet_data = va_arg (*args, u8 *);
  u32 n_packet_data_bytes = va_arg (*args, u32);
  ip_adjacency_t *adj = adj_get (adj_index);

  switch (adj->lookup_next_index)
    {
    case IP_LOOKUP_NEXT_REWRITE:
    case IP_LOOKUP_NEXT_MCAST:
      s =
	format (s, "%U", format_hex_bytes, packet_data, n_packet_data_bytes);
      break;

    default:
      break;
    }

  return s;
}

static uword
unformat_dpo (unformat_input_t * input, va_list * args)
{
  dpo_id_t *dpo = va_arg (*args, dpo_id_t *);
  fib_protocol_t fp = va_arg (*args, int);
  dpo_proto_t proto;

  proto = fib_proto_to_dpo (fp);

  if (unformat (input, "drop"))
    dpo_copy (dpo, drop_dpo_get (proto));
  else if (unformat (input, "punt"))
    dpo_copy (dpo, punt_dpo_get (proto));
  else if (unformat (input, "local"))
    receive_dpo_add_or_lock (proto, ~0, NULL, dpo);
  else if (unformat (input, "null-send-unreach"))
    ip_null_dpo_add_and_lock (proto, IP_NULL_ACTION_SEND_ICMP_UNREACH, dpo);
  else if (unformat (input, "null-send-prohibit"))
    ip_null_dpo_add_and_lock (proto, IP_NULL_ACTION_SEND_ICMP_PROHIBIT, dpo);
  else if (unformat (input, "null"))
    ip_null_dpo_add_and_lock (proto, IP_NULL_ACTION_NONE, dpo);
  else if (unformat (input, "classify"))
    {
      u32 classify_table_index;

      if (!unformat (input, "%d", &classify_table_index))
	{
	  clib_warning ("classify adj must specify table index");
	  return 0;
	}

      dpo_set (dpo, DPO_CLASSIFY, proto,
	       classify_dpo_create (proto, classify_table_index));
    }
  else
    return 0;

  return 1;
}

const ip46_address_t zero_addr = {
  .as_u64 = {
	     0, 0},
};

u32
fib_table_id_find_fib_index (fib_protocol_t proto, u32 table_id)
{
  ip4_main_t *im4 = &ip4_main;
  ip6_main_t *im6 = &ip6_main;
  uword *p;

  switch (proto)
    {
    case FIB_PROTOCOL_IP4:
      p = hash_get (im4->fib_index_by_table_id, table_id);
      break;
    case FIB_PROTOCOL_IP6:
      p = hash_get (im6->fib_index_by_table_id, table_id);
      break;
    default:
      p = NULL;
      break;
    }
  if (NULL != p)
    {
      return (p[0]);
    }
  return (~0);
}

clib_error_t *
vnet_ip_route_cmd (vlib_main_t * vm,
		   unformat_input_t * main_input, vlib_cli_command_t * cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  fib_route_path_t *rpaths = NULL, rpath;
  dpo_id_t dpo = DPO_INVALID, *dpos = NULL;
  fib_prefix_t *prefixs = NULL, pfx;
  mpls_label_t out_label, via_label;
  clib_error_t *error = NULL;
  u32 table_id, is_del;
  vnet_main_t *vnm;
  u32 fib_index;
  f64 count;
  int i;

  vnm = vnet_get_main ();
  is_del = 0;
  table_id = 0;
  count = 1;
  memset (&pfx, 0, sizeof (pfx));
  out_label = via_label = MPLS_LABEL_INVALID;

  /* Get a line of input. */
  if (!unformat_user (main_input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      memset (&rpath, 0, sizeof (rpath));

      if (unformat (line_input, "table %d", &table_id))
	;
      else if (unformat (line_input, "del"))
	is_del = 1;
      else if (unformat (line_input, "add"))
	is_del = 0;
      else if (unformat (line_input, "resolve-via-host"))
	{
	  if (vec_len (rpaths) == 0)
	    {
	      error = clib_error_return (0, "Paths then flags");
	      goto done;
	    }
	  rpaths[vec_len (rpaths) - 1].frp_flags |=
	    FIB_ROUTE_PATH_RESOLVE_VIA_HOST;
	}
      else if (unformat (line_input, "resolve-via-attached"))
	{
	  if (vec_len (rpaths) == 0)
	    {
	      error = clib_error_return (0, "Paths then flags");
	      goto done;
	    }
	  rpaths[vec_len (rpaths) - 1].frp_flags |=
	    FIB_ROUTE_PATH_RESOLVE_VIA_ATTACHED;
	}
      else if (unformat (line_input, "out-label %U",
			 unformat_mpls_unicast_label, &out_label))
	{
	  if (vec_len (rpaths) == 0)
	    {
	      error = clib_error_return (0, "Paths then labels");
	      goto done;
	    }
	  vec_add1 (rpaths[vec_len (rpaths) - 1].frp_label_stack, out_label);
	}
      else if (unformat (line_input, "via-label %U",
			 unformat_mpls_unicast_label, &rpath.frp_local_label))
	{
	  rpath.frp_weight = 1;
	  rpath.frp_eos = MPLS_NON_EOS;
	  rpath.frp_proto = FIB_PROTOCOL_MPLS;
	  rpath.frp_sw_if_index = ~0;
	  vec_add1 (rpaths, rpath);
	}
      else if (unformat (line_input, "count %f", &count))
	;

      else if (unformat (line_input, "%U/%d",
			 unformat_ip4_address, &pfx.fp_addr.ip4, &pfx.fp_len))
	{
	  pfx.fp_proto = FIB_PROTOCOL_IP4;
	  vec_add1 (prefixs, pfx);
	}
      else if (unformat (line_input, "%U/%d",
			 unformat_ip6_address, &pfx.fp_addr.ip6, &pfx.fp_len))
	{
	  pfx.fp_proto = FIB_PROTOCOL_IP6;
	  vec_add1 (prefixs, pfx);
	}
      else if (unformat (line_input, "via %U %U weight %u",
			 unformat_ip4_address,
			 &rpath.frp_addr.ip4,
			 unformat_vnet_sw_interface, vnm,
			 &rpath.frp_sw_if_index, &rpath.frp_weight))
	{
	  rpath.frp_proto = FIB_PROTOCOL_IP4;
	  vec_add1 (rpaths, rpath);
	}

      else if (unformat (line_input, "via %U %U weight %u",
			 unformat_ip6_address,
			 &rpath.frp_addr.ip6,
			 unformat_vnet_sw_interface, vnm,
			 &rpath.frp_sw_if_index, &rpath.frp_weight))
	{
	  rpath.frp_proto = FIB_PROTOCOL_IP6;
	  vec_add1 (rpaths, rpath);
	}

      else if (unformat (line_input, "via %U %U",
			 unformat_ip4_address,
			 &rpath.frp_addr.ip4,
			 unformat_vnet_sw_interface, vnm,
			 &rpath.frp_sw_if_index))
	{
	  rpath.frp_weight = 1;
	  rpath.frp_proto = FIB_PROTOCOL_IP4;
	  vec_add1 (rpaths, rpath);
	}

      else if (unformat (line_input, "via %U %U",
			 unformat_ip6_address,
			 &rpath.frp_addr.ip6,
			 unformat_vnet_sw_interface, vnm,
			 &rpath.frp_sw_if_index))
	{
	  rpath.frp_weight = 1;
	  rpath.frp_proto = FIB_PROTOCOL_IP6;
	  vec_add1 (rpaths, rpath);
	}
      else if (unformat (line_input, "via %U next-hop-table %d",
			 unformat_ip4_address,
			 &rpath.frp_addr.ip4, &rpath.frp_fib_index))
	{
	  rpath.frp_weight = 1;
	  rpath.frp_sw_if_index = ~0;
	  rpath.frp_proto = FIB_PROTOCOL_IP4;
	  vec_add1 (rpaths, rpath);
	}
      else if (unformat (line_input, "via %U next-hop-table %d",
			 unformat_ip6_address,
			 &rpath.frp_addr.ip6, &rpath.frp_fib_index))
	{
	  rpath.frp_weight = 1;
	  rpath.frp_sw_if_index = ~0;
	  rpath.frp_proto = FIB_PROTOCOL_IP6;
	  vec_add1 (rpaths, rpath);
	}
      else if (unformat (line_input, "via %U",
			 unformat_ip4_address, &rpath.frp_addr.ip4))
	{
	  /*
	   * the recursive next-hops are by default in the same table
	   * as the prefix
	   */
	  rpath.frp_fib_index = table_id;
	  rpath.frp_weight = 1;
	  rpath.frp_sw_if_index = ~0;
	  rpath.frp_proto = FIB_PROTOCOL_IP4;
	  vec_add1 (rpaths, rpath);
	}
      else if (unformat (line_input, "via %U",
			 unformat_ip6_address, &rpath.frp_addr.ip6))
	{
	  rpath.frp_fib_index = table_id;
	  rpath.frp_weight = 1;
	  rpath.frp_sw_if_index = ~0;
	  rpath.frp_proto = FIB_PROTOCOL_IP6;
	  vec_add1 (rpaths, rpath);
	}
      else if (unformat (line_input,
			 "lookup in table %d", &rpath.frp_fib_index))
	{
	  rpath.frp_proto = pfx.fp_proto;
	  rpath.frp_sw_if_index = ~0;
	  vec_add1 (rpaths, rpath);
	}
      else if (vec_len (prefixs) > 0 &&
	       unformat (line_input, "via %U",
			 unformat_vnet_sw_interface, vnm,
			 &rpath.frp_sw_if_index))
	{
	  rpath.frp_weight = 1;
	  rpath.frp_proto = prefixs[0].fp_proto;
	  vec_add1 (rpaths, rpath);
	}
      else if (vec_len (prefixs) > 0 &&
	       unformat (line_input, "via %U",
			 unformat_dpo, &dpo, prefixs[0].fp_proto))
	{
	  vec_add1 (dpos, dpo);
	}
      else
	{
	  error = unformat_parse_error (line_input);
	  goto done;
	}
    }

  if (vec_len (prefixs) == 0)
    {
      error =
	clib_error_return (0, "expected ip4/ip6 destination address/length.");
      goto done;
    }

  if (!is_del && vec_len (rpaths) + vec_len (dpos) == 0)
    {
      error = clib_error_return (0, "expected paths.");
      goto done;
    }

  if (~0 == table_id)
    {
      /*
       * if no table_id is passed we will manipulate the default
       */
      fib_index = 0;
    }
  else
    {
      fib_index = fib_table_id_find_fib_index (prefixs[0].fp_proto, table_id);

      if (~0 == fib_index)
	{
	  error = clib_error_return (0, "Nonexistent table id %d", table_id);
	  goto done;
	}
    }

  for (i = 0; i < vec_len (prefixs); i++)
    {
      if (is_del && 0 == vec_len (rpaths))
	{
	  fib_table_entry_delete (fib_index, &prefixs[i], FIB_SOURCE_CLI);
	}
      else if (!is_del && 1 == vec_len (dpos))
	{
	  fib_table_entry_special_dpo_add (fib_index,
					   &prefixs[i],
					   FIB_SOURCE_CLI,
					   FIB_ENTRY_FLAG_EXCLUSIVE,
					   &dpos[0]);
	  dpo_reset (&dpos[0]);
	}
      else if (vec_len (dpos) > 0)
	{
	  error =
	    clib_error_return (0,
			       "Load-balancing over multiple special adjacencies is unsupported");
	  goto done;
	}
      else if (0 < vec_len (rpaths))
	{
	  u32 k, j, n, incr;
	  ip46_address_t dst = prefixs[i].fp_addr;
	  f64 t[2];
	  n = count;
	  t[0] = vlib_time_now (vm);
	  incr = 1 << ((FIB_PROTOCOL_IP4 == prefixs[0].fp_proto ? 32 : 128) -
		       prefixs[i].fp_len);

	  for (k = 0; k < n; k++)
	    {
	      for (j = 0; j < vec_len (rpaths); j++)
		{
		  u32 fi;
		  /*
		   * the CLI parsing stored table Ids, swap to FIB indicies
		   */
		  fi = fib_table_id_find_fib_index (prefixs[i].fp_proto,
						    rpaths[i].frp_fib_index);

		  if (~0 == fi)
		    {
		      error =
			clib_error_return (0, "Via table %d does not exist",
					   rpaths[i].frp_fib_index);
		      goto done;
		    }
		  rpaths[i].frp_fib_index = fi;

		  fib_prefix_t rpfx = {
		    .fp_len = prefixs[i].fp_len,
		    .fp_proto = prefixs[i].fp_proto,
		    .fp_addr = dst,
		  };

		  if (is_del)
		    fib_table_entry_path_remove2 (fib_index,
						  &rpfx,
						  FIB_SOURCE_CLI, &rpaths[j]);
		  else
		    fib_table_entry_path_add2 (fib_index,
					       &rpfx,
					       FIB_SOURCE_CLI,
					       FIB_ENTRY_FLAG_NONE,
					       &rpaths[j]);
		}

	      if (FIB_PROTOCOL_IP4 == prefixs[0].fp_proto)
		{
		  dst.ip4.as_u32 =
		    clib_host_to_net_u32 (incr +
					  clib_net_to_host_u32 (dst.
								ip4.as_u32));
		}
	      else
		{
		  int bucket = (incr < 64 ? 0 : 1);
		  dst.ip6.as_u64[bucket] =
		    clib_host_to_net_u64 (incr +
					  clib_net_to_host_u64 (dst.ip6.as_u64
								[bucket]));

		}
	    }
	  t[1] = vlib_time_now (vm);
	  if (count > 1)
	    vlib_cli_output (vm, "%.6e routes/sec", count / (t[1] - t[0]));
	}
      else
	{
	  error = clib_error_return (0, "Don't understand what you want...");
	  goto done;
	}
    }


done:
  vec_free (dpos);
  vec_free (prefixs);
  vec_free (rpaths);
  unformat_free (line_input);
  return error;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (vlib_cli_ip_command, static) = {
  .path = "ip",
  .short_help = "Internet protocol (IP) commands",
};
/* *INDENT-ON* */

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (vlib_cli_ip6_command, static) = {
  .path = "ip6",
  .short_help = "Internet protocol version 6 (IPv6) commands",
};
/* *INDENT-ON* */

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (vlib_cli_show_ip_command, static) = {
  .path = "show ip",
  .short_help = "Internet protocol (IP) show commands",
};
/* *INDENT-ON* */

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (vlib_cli_show_ip6_command, static) = {
  .path = "show ip6",
  .short_help = "Internet protocol version 6 (IPv6) show commands",
};
/* *INDENT-ON* */

/*?
 * This command is used to add or delete IPv4 or IPv6 routes. All
 * IP Addresses ('<em><dst-ip-addr>/<width></em>',
 * '<em><next-hop-ip-addr></em>' and '<em><adj-hop-ip-addr></em>')
 * can be IPv4 or IPv6, but all must be of the same form in a single
 * command. To display the current set of routes, use the commands
 * '<em>show ip fib</em>' and '<em>show ip6 fib</em>'.
 *
 * @cliexpar
 * Example of how to add a straight forward static route:
 * @cliexcmd{ip route add 6.0.1.2/32 via 6.0.0.1 GigabitEthernet2/0/0}
 * Example of how to delete a straight forward static route:
 * @cliexcmd{ip route del 6.0.1.2/32 via 6.0.0.1 GigabitEthernet2/0/0}
 * Mainly for route add/del performance testing, one can add or delete
 * multiple routes by adding 'count N' to the previous item:
 * @cliexcmd{ip route add count 10 7.0.0.0/24 via 6.0.0.1 GigabitEthernet2/0/0}
 * Add multiple routes for the same destination to create equal-cost multipath:
 * @cliexcmd{ip route add 7.0.0.1/32 via 6.0.0.1 GigabitEthernet2/0/0}
 * @cliexcmd{ip route add 7.0.0.1/32 via 6.0.0.2 GigabitEthernet2/0/0}
 * For unequal-cost multipath, specify the desired weights. This
 * combination of weights results in 3/4 of the traffic following the
 * second path, 1/4 following the first path:
 * @cliexcmd{ip route add 7.0.0.1/32 via 6.0.0.1 GigabitEthernet2/0/0 weight 1}
 * @cliexcmd{ip route add 7.0.0.1/32 via 6.0.0.2 GigabitEthernet2/0/0 weight 3}
 * To add a route to a particular FIB table (VRF), use:
 * @cliexcmd{ip route add 172.16.24.0/24 table 7 via GigabitEthernet2/0/0}
 ?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (ip_route_command, static) = {
  .path = "ip route",
  .short_help = "ip route [add|del] [count <n>] <dst-ip-addr>/<width> [table <table-id>] [via <next-hop-ip-addr> [<interface>] [weight <weight>]] | [via arp <interface> <adj-hop-ip-addr>] | [via drop|punt|local<id>|arp|classify <classify-idx>] [lookup in table <out-table-id>]",
  .function = vnet_ip_route_cmd,
  .is_mp_safe = 1,
};
/* *INDENT-ON* */

clib_error_t *
vnet_ip_mroute_cmd (vlib_main_t * vm,
		    unformat_input_t * main_input, vlib_cli_command_t * cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  clib_error_t *error = NULL;
  fib_route_path_t rpath;
  u32 table_id, is_del;
  vnet_main_t *vnm;
  mfib_prefix_t pfx;
  u32 fib_index;
  mfib_itf_flags_t iflags = 0;
  mfib_entry_flags_t eflags = 0;
  u32 gcount, scount, ss, gg, incr;
  f64 timet[2];

  gcount = scount = 1;
  vnm = vnet_get_main ();
  is_del = 0;
  table_id = 0;
  memset (&pfx, 0, sizeof (pfx));
  memset (&rpath, 0, sizeof (rpath));
  rpath.frp_sw_if_index = ~0;

  /* Get a line of input. */
  if (!unformat_user (main_input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "table %d", &table_id))
	;
      else if (unformat (line_input, "del"))
	is_del = 1;
      else if (unformat (line_input, "add"))
	is_del = 0;
      else if (unformat (line_input, "scount %d", &scount))
	;
      else if (unformat (line_input, "gcount %d", &gcount))
	;
      else if (unformat (line_input, "%U %U",
			 unformat_ip4_address,
			 &pfx.fp_src_addr.ip4,
			 unformat_ip4_address, &pfx.fp_grp_addr.ip4))
	{
	  pfx.fp_proto = FIB_PROTOCOL_IP4;
	  pfx.fp_len = 64;
	}
      else if (unformat (line_input, "%U %U",
			 unformat_ip6_address,
			 &pfx.fp_src_addr.ip6,
			 unformat_ip6_address, &pfx.fp_grp_addr.ip6))
	{
	  pfx.fp_proto = FIB_PROTOCOL_IP6;
	  pfx.fp_len = 256;
	}
      else if (unformat (line_input, "%U/%d",
			 unformat_ip4_address,
			 &pfx.fp_grp_addr.ip4, &pfx.fp_len))
	{
	  pfx.fp_proto = FIB_PROTOCOL_IP4;
	}
      else if (unformat (line_input, "%U/%d",
			 unformat_ip6_address,
			 &pfx.fp_grp_addr.ip6, &pfx.fp_len))
	{
	  pfx.fp_proto = FIB_PROTOCOL_IP6;
	}
      else if (unformat (line_input, "%U",
			 unformat_ip4_address, &pfx.fp_grp_addr.ip4))
	{
	  memset (&pfx.fp_src_addr.ip4, 0, sizeof (pfx.fp_src_addr.ip4));
	  pfx.fp_proto = FIB_PROTOCOL_IP4;
	  pfx.fp_len = 32;
	}
      else if (unformat (line_input, "%U",
			 unformat_ip6_address, &pfx.fp_grp_addr.ip6))
	{
	  memset (&pfx.fp_src_addr.ip6, 0, sizeof (pfx.fp_src_addr.ip6));
	  pfx.fp_proto = FIB_PROTOCOL_IP6;
	  pfx.fp_len = 128;
	}
      else if (unformat (line_input, "via %U",
			 unformat_vnet_sw_interface, vnm,
			 &rpath.frp_sw_if_index))
	{
	  rpath.frp_weight = 1;
	  rpath.frp_proto = FIB_PROTOCOL_IP4;
	}
      else if (unformat (line_input, "%U", unformat_mfib_itf_flags, &iflags))
	;
      else if (unformat (line_input, "%U",
			 unformat_mfib_entry_flags, &eflags))
	;
      else
	{
	  error = unformat_parse_error (line_input);
	  goto done;
	}
    }

  if (~0 == table_id)
    {
      /*
       * if no table_id is passed we will manipulate the default
       */
      fib_index = 0;
    }
  else
    {
      fib_index = mfib_table_find (pfx.fp_proto, table_id);

      if (~0 == fib_index)
	{
	  error = clib_error_return (0, "Nonexistent table id %d", table_id);
	  goto done;
	}
    }

  timet[0] = vlib_time_now (vm);

  if (FIB_PROTOCOL_IP4 == pfx.fp_proto)
    {
      incr = 1 << (32 - (pfx.fp_len % 32));
    }
  else
    {
      incr = 1 << (128 - (pfx.fp_len % 128));
    }

  for (ss = 0; ss < scount; ss++)
    {
      for (gg = 0; gg < gcount; gg++)
	{
	  if (is_del && 0 == rpath.frp_weight)
	    {
	      /* no path provided => route delete */
	      mfib_table_entry_delete (fib_index, &pfx, MFIB_SOURCE_CLI);
	    }
	  else if (eflags)
	    {
	      mfib_table_entry_update (fib_index, &pfx, MFIB_SOURCE_CLI,
				       MFIB_RPF_ID_NONE, eflags);
	    }
	  else
	    {
	      if (is_del)
		mfib_table_entry_path_remove (fib_index,
					      &pfx, MFIB_SOURCE_CLI, &rpath);
	      else
		mfib_table_entry_path_update (fib_index,
					      &pfx, MFIB_SOURCE_CLI, &rpath,
					      iflags);
	    }

	  if (FIB_PROTOCOL_IP4 == pfx.fp_proto)
	    {
	      pfx.fp_grp_addr.ip4.as_u32 =
		clib_host_to_net_u32 (incr +
				      clib_net_to_host_u32 (pfx.
							    fp_grp_addr.ip4.
							    as_u32));
	    }
	  else
	    {
	      int bucket = (incr < 64 ? 0 : 1);
	      pfx.fp_grp_addr.ip6.as_u64[bucket] =
		clib_host_to_net_u64 (incr +
				      clib_net_to_host_u64 (pfx.
							    fp_grp_addr.ip6.as_u64
							    [bucket]));

	    }
	}
      if (FIB_PROTOCOL_IP4 == pfx.fp_proto)
	{
	  pfx.fp_src_addr.ip4.as_u32 =
	    clib_host_to_net_u32 (1 +
				  clib_net_to_host_u32 (pfx.fp_src_addr.
							ip4.as_u32));
	}
      else
	{
	  pfx.fp_src_addr.ip6.as_u64[1] =
	    clib_host_to_net_u64 (1 +
				  clib_net_to_host_u64 (pfx.fp_src_addr.
							ip6.as_u64[1]));
	}
    }

  timet[1] = vlib_time_now (vm);

  if (scount > 1 || gcount > 1)
    vlib_cli_output (vm, "%.6e routes/sec",
		     (scount * gcount) / (timet[1] - timet[0]));

done:
  unformat_free (line_input);

  return error;
}

/*?
 * This command is used to add or delete IPv4 or IPv6  multicastroutes. All
 * IP Addresses ('<em><dst-ip-addr>/<width></em>',
 * '<em><next-hop-ip-addr></em>' and '<em><adj-hop-ip-addr></em>')
 * can be IPv4 or IPv6, but all must be of the same form in a single
 * command. To display the current set of routes, use the commands
 * '<em>show ip mfib</em>' and '<em>show ip6 mfib</em>'.
 * The full set of support flags for interfaces and route is shown via;
 * '<em>show mfib route flags</em>' and '<em>show mfib itf flags</em>'
 * respectively.
 * @cliexpar
 * Example of how to add a forwarding interface to a route (and create the
 * route if it does not exist)
 * @cliexcmd{ip mroute add 232.1.1.1 via GigabitEthernet2/0/0 Forward}
 * Example of how to add an accepting interface to a route (and create the
 * route if it does not exist)
 * @cliexcmd{ip mroute add 232.1.1.1 via GigabitEthernet2/0/1 Accept}
 * Example of changing the route's flags to send signals via the API
 * @cliexcmd{ip mroute add 232.1.1.1 Signal}

 ?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (ip_mroute_command, static) =
{
  .path = "ip mroute",
  .short_help = "ip mroute [add|del] <dst-ip-addr>/<width> [table <table-id>] [via <next-hop-ip-addr> [<interface>],",
  .function = vnet_ip_mroute_cmd,
  .is_mp_safe = 1,
};
/* *INDENT-ON* */

/*
 * The next two routines address a longstanding script hemorrhoid.
 * Probing a v4 or v6 neighbor needs to appear to be synchronous,
 * or dependent route-adds will simply fail.
 */
static clib_error_t *
ip6_probe_neighbor_wait (vlib_main_t * vm, ip6_address_t * a, u32 sw_if_index,
			 int retry_count)
{
  vnet_main_t *vnm = vnet_get_main ();
  clib_error_t *e;
  int i;
  int resolved = 0;
  uword event_type;
  uword *event_data = 0;

  ASSERT (vlib_in_process_context (vm));

  if (retry_count > 0)
    vnet_register_ip6_neighbor_resolution_event
      (vnm, a, vlib_get_current_process (vm)->node_runtime.node_index,
       1 /* event */ , 0 /* data */ );

  for (i = 0; i < retry_count; i++)
    {
      /* The interface may be down, etc. */
      e = ip6_probe_neighbor (vm, a, sw_if_index);

      if (e)
	return e;

      vlib_process_wait_for_event_or_clock (vm, 1.0);
      event_type = vlib_process_get_events (vm, &event_data);
      switch (event_type)
	{
	case 1:		/* resolved... */
	  vlib_cli_output (vm, "Resolved %U", format_ip6_address, a);
	  resolved = 1;
	  goto done;

	case ~0:		/* timeout */
	  break;

	default:
	  clib_warning ("unknown event_type %d", event_type);
	}
      vec_reset_length (event_data);
    }

done:

  if (!resolved)
    return clib_error_return (0, "Resolution failed for %U",
			      format_ip6_address, a);
  return 0;
}

static clib_error_t *
ip4_probe_neighbor_wait (vlib_main_t * vm, ip4_address_t * a, u32 sw_if_index,
			 int retry_count)
{
  vnet_main_t *vnm = vnet_get_main ();
  clib_error_t *e;
  int i;
  int resolved = 0;
  uword event_type;
  uword *event_data = 0;

  ASSERT (vlib_in_process_context (vm));

  if (retry_count > 0)
    vnet_register_ip4_arp_resolution_event
      (vnm, a, vlib_get_current_process (vm)->node_runtime.node_index,
       1 /* event */ , 0 /* data */ );

  for (i = 0; i < retry_count; i++)
    {
      /* The interface may be down, etc. */
      e = ip4_probe_neighbor (vm, a, sw_if_index);

      if (e)
	return e;

      vlib_process_wait_for_event_or_clock (vm, 1.0);
      event_type = vlib_process_get_events (vm, &event_data);
      switch (event_type)
	{
	case 1:		/* resolved... */
	  vlib_cli_output (vm, "Resolved %U", format_ip4_address, a);
	  resolved = 1;
	  goto done;

	case ~0:		/* timeout */
	  break;

	default:
	  clib_warning ("unknown event_type %d", event_type);
	}
      vec_reset_length (event_data);
    }

done:

  vec_reset_length (event_data);

  if (!resolved)
    return clib_error_return (0, "Resolution failed for %U",
			      format_ip4_address, a);
  return 0;
}

static clib_error_t *
probe_neighbor_address (vlib_main_t * vm,
			unformat_input_t * input, vlib_cli_command_t * cmd)
{
  vnet_main_t *vnm = vnet_get_main ();
  unformat_input_t _line_input, *line_input = &_line_input;
  ip4_address_t a4;
  ip6_address_t a6;
  clib_error_t *error = 0;
  u32 sw_if_index = ~0;
  int retry_count = 3;
  int is_ip4 = 1;
  int address_set = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat_user (line_input, unformat_vnet_sw_interface, vnm,
			 &sw_if_index))
	;
      else if (unformat (line_input, "retry %d", &retry_count))
	;

      else if (unformat (line_input, "%U", unformat_ip4_address, &a4))
	address_set++;
      else if (unformat (line_input, "%U", unformat_ip6_address, &a6))
	{
	  address_set++;
	  is_ip4 = 0;
	}
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (sw_if_index == ~0)
    {
      error = clib_error_return (0, "Interface required, not set.");
      goto done;
    }
  if (address_set == 0)
    {
      error = clib_error_return (0, "ip address required, not set.");
      goto done;
    }
  if (address_set > 1)
    {
      error = clib_error_return (0, "Multiple ip addresses not supported.");
      goto done;
    }

  if (is_ip4)
    error = ip4_probe_neighbor_wait (vm, &a4, sw_if_index, retry_count);
  else
    error = ip6_probe_neighbor_wait (vm, &a6, sw_if_index, retry_count);

done:
  unformat_free (line_input);

  return error;
}

/*?
 * The '<em>ip probe-neighbor</em>' command ARPs for IPv4 addresses or
 * attempts IPv6 neighbor discovery depending on the supplied IP address
 * format.
 *
 * @note This command will not immediately affect the indicated FIB; it
 * is not suitable for use in establishing a FIB entry prior to adding
 * recursive FIB entries. As in: don't use it in a script to probe a
 * gateway prior to adding a default route. It won't work. Instead,
 * configure a static ARP cache entry [see '<em>set ip arp</em>'], or
 * a static IPv6 neighbor [see '<em>set ip6 neighbor</em>'].
 *
 * @cliexpar
 * Example of probe for an IPv4 address:
 * @cliexcmd{ip probe-neighbor GigabitEthernet2/0/0 172.16.1.2}
?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (ip_probe_neighbor_command, static) = {
  .path = "ip probe-neighbor",
  .function = probe_neighbor_address,
  .short_help = "ip probe-neighbor <interface> <ip4-addr> | <ip6-addr> [retry nn]",
  .is_mp_safe = 1,
};
/* *INDENT-ON* */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */