aboutsummaryrefslogtreecommitdiffstats
path: root/test
AgeCommit message (Collapse)AuthorFilesLines
2018-07-20QoS: marking and recording for MPLS and VLANNeale Ranns1-4/+210
Change-Id: Icec79aa9039d5d7835d311fde0b7c1a0c76c9eb1 Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-07-19test_jvpp: improve error message when JVpp JARS are missingMarek Gradzki1-0/+7
The java command fails with missing class error, when some of the JARs given by -cp are missing, which may be missleading. This patch fixes that by adding os.path.isfile check to test_jvpp.py. Change-Id: I3a0b6ef338c7f70cfd0ba78ee0888efe0b0957b3 Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
2018-07-19gbp: Add support for ACLMohsin Kazmi1-10/+78
Change-Id: I7513c41307e62068ab5d9739cac393675c6066f8 Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
2018-07-18make_test: dhcp - add lease time test and wrong XID in OFFER testJan Gelety1-7/+102
- negative test with wrong XID in OFFER not implemented as issue reported in Jira ticket VPP-99 still not fixed Jira: CSIT-1147 Change-Id: I4ebc622bd81fed421919bc0397a12e32cb26fa1d Signed-off-by: Jan Gelety <jgelety@cisco.com>
2018-07-17VOM: support for pipesNeale Ranns2-6/+82
Change-Id: I5c381dfe2f926f94a34ee8ed8f1b9ec6038d5fe2 Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
2018-07-13srv6-as: Adding support for L2 trafficFrancois Clad1-0/+199
Change-Id: I72978c5957cb1acf154c9de7ad153092bac37785 Signed-off-by: Francois Clad <fclad@cisco.com>
2018-07-13srv6-as: Adding test casesFrancois Clad1-0/+692
Change-Id: I597a88d255c2f7cff552b1fa12ed4a7c4579b91c Signed-off-by: Francois Clad <fclad@cisco.com>
2018-07-11srv6: Fixing SRH parsing bug in Scapy 2.4Francois Clad1-0/+28
Change-Id: Ib2cb345d07665735697bf54ad48d353ba4112eda Signed-off-by: Francois Clad <fclad@cisco.com>
2018-07-11make_test: Add missing classifier testsJan Gelety3-126/+1294
Jira: CSIT-1172 Change-Id: I04e726aab97efb96ed835d2a6db293a2acab0add Signed-off-by: Jan Gelety <jgelety@cisco.com>
2018-07-10NAT44: multiple outside FIB tables (VPP-1314)Matus Fabian1-21/+442
Change-Id: I56eb15f8fd2d3049845287dc3df7870582764f8b Signed-off-by: Matus Fabian <matfabia@cisco.com>
2018-07-10Do not translate packets destined for NAT64 inside interface (VPP-1331)Juraj Sloboda1-2/+60
Change-Id: Ieb8020f57ed5ad20daf552cd62ae3fdd8c573926 Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
2018-07-09IGMP improvementsNeale Ranns5-243/+642
- Enable/Disable an interface for IGMP - improve logging - refactor common code - no orphaned timers - IGMP state changes in main thread only - Large groups split over multiple state-change reports - SSM range configuration API. - more tests Change-Id: If5674f1044e7e97274a711f47807c9ba689d7b9a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-07-09vxlan:use bihash_16_8 for ipv4 lookupEyal Bari1-5/+5
Change-Id: I0d4630c88d6caacffcd073ebaa12766dfc893f70 Signed-off-by: Eyal Bari <ebari@cisco.com>
2018-07-07PipesNeale Ranns2-0/+267
A pipe resembles a unix pipe. Each end of the pipe is a full VPP interface. pipes can be used for e.g. packet recirculation, inter-BD, etc. Change-Id: I185bb9fb43dd233ff45da63ac1b85ae2e1ceca16 Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
2018-07-05VPP-1335 vapi crash when memclnt_keepalive receivedKlement Sekera1-4/+4
Change-Id: If33a7cc6c76147fd3ea9d8118370e7a508819b81 Signed-off-by: Klement Sekera <ksekera@cisco.com>
2018-07-04fix VPP-1312 Invalid UDP packet length in ipsecKlement Sekera1-26/+22
Change-Id: Ibfd0a2e7010e6e74c32244c538f60e0713bea03f Signed-off-by: Klement Sekera <ksekera@cisco.com>
2018-07-03vom: Add cross connect supportMohsin Kazmi1-0/+64
Change-Id: Ia316730d8f9fe9836200aa96e0b5fd827dc71c98 Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
2018-07-02make_test: add icmp packet size sweep and icmp echo testsJan Gelety2-47/+201
Jira: CSIT-1141 Change-Id: I162bb4e718bff188abefc7b2f33501de9c55bb03 Signed-off-by: Jan Gelety <jgelety@cisco.com>
2018-06-29IP4 Router Alert option handling for IGMPNeale Ranns1-1/+5
and a new ip4-options node, inserted between ip4-input and ip4-punt, that checks for IP-router-alert option + IGMP combination and sends the packet to the ip4-local. This is required because some IGMP packets are sent to the group address and not the all-routers address. All IGMP packets are sent with the router alert option. Change-Id: I01f478d4d98ac9f806e0bcba0f6da6e4e7d26e2a Signed-off-by: Neale Ranns <nranns@cisco.com>
2018-06-28make test: fix profilingKlement Sekera1-1/+1
Change-Id: Iad45ca8c167a017029b20ddd0a0b59087fa69498 Signed-off-by: Klement Sekera <ksekera@cisco.com>
2018-06-27make test: easy profiling via make test PROFILE=1Klement Sekera2-2/+25
Change-Id: Ib845578485f523b7f14e98c83d05f78db382ecde Signed-off-by: Klement Sekera <ksekera@cisco.com>
2018-06-26NAT44: fix nat44_ed_not_translate_output_feature (VPP-1329)Matus Fabian1-0/+25
Change-Id: Iddb0b848c53da03116524e203c7112c82b401ac5 Signed-off-by: Matus Fabian <matfabia@cisco.com>
2018-06-26L3DSR fix ip checksum issue and add testHongjun Ni1-0/+6
Change-Id: Iedebbac71d3e694b915d6a126c80ecc3b5473a4a Signed-off-by: Hongjun Ni <hongjun.ni@intel.com>
2018-06-26Add negative tests for leaking across different VRFs - ip4/6Jan Gelety2-31/+172
Jira: CSIT-1140 Change-Id: I5c6dd44d3efb298f203e61b14345a2f13680bd34 Signed-off-by: Jan Gelety <jgelety@cisco.com>
2018-06-25make test: fix RETRIES when setUpClass throwsKlement Sekera2-2/+25
This change adds handling of special case when setUpClass throws. In this case TestResults receives a mock object called _ErrorHolder. By parsing its description, we find test class name and use it to lookup the test class in test suite to be able to add it to the list of failures for re-running. Change-Id: I656f21e38aa450fc567cdcbcf6e586967f947a64 Signed-off-by: Klement Sekera <ksekera@cisco.com>
2018-06-25make test: fix broken interfaces #2Klement Sekera5-86/+84
Change-Id: I9d5b5d925fd2c09a1113fc51e433a16d729a241b Signed-off-by: Klement Sekera <ksekera@cisco.com>
2018-06-24Revert "Revert "make test: fix broken interfaces""Klement Sekera23-86/+61
This reverts commit c8efa29b6f9a91381897b54f1147daf922ed7164. Change-Id: I1d5c5773d5f86a63073e255336bd9de628e26179 Signed-off-by: Klement Sekera <ksekera@cisco.com>
2018-06-24Revert "Revert "ipsec: VPP-1316 calculate IP/TCP/UDP inner checksums""Klement Sekera13-574/+634
This reverts commit e0d2bd6bd7fc59c0c6ac48195d7f825dc99bfd91. Change-Id: If491e16f9ea66b2493a6a7c7f3c684ed585f8f51 Signed-off-by: Klement Sekera <ksekera@cisco.com>
2018-06-22Revert "ipsec: VPP-1316 calculate IP/TCP/UDP inner checksums"Ole Troan13-634/+574
This reverts commit a98346f664aae148d26a8e158008b773d73db96f. Change-Id: Iee5b3a5ddff0e8fd3a30fe5973cee24de434fe12 Signed-off-by: Ole Troan <ot@cisco.com>
2018-06-22Revert "make test: fix broken interfaces"Ole Troan23-61/+86
This reverts commit d5c60b96a3fd93916fc4af5c8d6d25625c28242e. Change-Id: I3632b9c3f76c615aee897f28f76d094e7031e689 Signed-off-by: Ole Troan <ot@cisco.com>
2018-06-22make test: fix broken interfacesKlement Sekera23-86/+61
Change-Id: I2e092774f81503e04b53cc6c6b5d357fe3fc52ab Signed-off-by: Klement Sekera <ksekera@cisco.com>
2018-06-22Python API: Add enum and union support.Ole Troan2-487/+1
As well as a rewrite of the encoders/decoders to make it more readable and extensible. (Re-commit after fix to verify build.) Change-Id: Ic244d3cebe070bb2570491f8a24f4a1e203f889a Signed-off-by: Ole Troan <ot@cisco.com>
2018-06-21test:vxlan over ipv6 testsEyal Bari6-6/+213
Change-Id: Id910db0e3a07ecc6f469e2f0d1e97f39ba48cc60 Signed-off-by: Eyal Bari <ebari@cisco.com>
2018-06-21ipsec: VPP-1316 calculate IP/TCP/UDP inner checksumsKlement Sekera13-574/+634
Calculate IP/TCP/UDP checksums in software before adding authentication. Change-Id: I3e121cb00aeba667764f39ade8d62170f18f8b6b Signed-off-by: Klement Sekera <ksekera@cisco.com>
2018-06-21Implement DHCPv6 IA NA client (VPP-1094)Juraj Sloboda2-32/+388
Change-Id: I682a47d6cf9975aca6136188d28ee93eaadf4fe3 Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
2018-06-19Fixed bugs in SRv6 APIPablo Camarillo3-45/+50
Jira ticket VPP-1196 Jira ticket VPP-1081 Jira ticket VPP-1078 Jira ticket VPP-1217 Change-Id: Id7e85229cae1017acb0aa4ca63ced334e6dafb8d Signed-off-by: pcamaril <pcamaril@cisco.com> Signed-off-by: Pablo Camarillo <pcamaril@cisco.com> Signed-off-by: pcamaril <pcamaril@cisco.com> Signed-off-by: Michal Cmarada <michal.cmarada@pantheon.tech>
2018-06-18Revert "Python API: Add enum and union support."Ole Trøan2-3/+451
This reverts commit a5ee900fb75201bbfceaf13c8bc57a13ed094988. Some of the unit tests breaks. Backing out until fixed. Change-Id: I1846fb417db44a2a772f7b59cda8bcfe6d39f8c3 Signed-off-by: Ole Troan <ot@cisco.com>
2018-06-17Python API: Add enum and union support.Ole Troan2-451/+3
As well as a rewrite of the encoders/decoders to make it more readable and extensible. Change-Id: I253369ac76303922bf9c11377622c8974fa92f19 Signed-off-by: Ole Troan <ot@cisco.com>
2018-06-15Update DHCPv6 DUID code and fix coverity warningsJuraj Sloboda1-12/+9
- Generate client DUID only when DHCPv6 PD feature is enabled - Change client DUID type from DUID-LLT to DUID-LL - Fix coverity warnings Change-Id: I20e518fc9a1c5f3f7ea9add7e7e03a487c99e978 Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
2018-06-15NAT44: endpoint dependent mode (VPP-1273)Matus Fabian1-936/+1105
To enable NAT plugin endpoint dependent mode add following to statrup config: nat { endpoint-dependent } Enable endpoint dependent filtering and mapping for all sessions. Move some existing functionality such as service load balancing, twice nat, out2in-only static mappings and unknown protocol dynamic translations, which use endpoint dependent lookup hash tables before. Basically split to vanilla NAT44 and extra features NAT44. Change-Id: I3925eb5ddcc8f1ec4cf6af4e2a618a7ec7aa9735 Signed-off-by: Matus Fabian <matfabia@cisco.com>
2018-06-14MTU: IP fragmentation added to ip4-rewrite and ip6-rewriteOle Troan1-4/+3
Change-Id: Ibae260273f25a319153be37470aed49ff73e957a Signed-off-by: Ole Troan <ot@cisco.com>
2018-06-11udp: fix for multiple workers and add testFlorin Coras1-0/+74
Since the main thread is not used for session polling anymore, when vpp is started with multiple wokers, allocate connections on the first. Also add a simple udp make test. Change-Id: Id869f5d89e0fced51048f0384fa86a5022258b7c Signed-off-by: Florin Coras <fcoras@cisco.com>
2018-06-11MTU: Software interface / Per-protocol MTU supportOle Troan3-21/+13
This patch separates setting of hardware interfaec and software interface MTU. Software MTU is L2 payload MTU (i.e. not including L2 header). Per-protocol MTU for IPv4, IPv6 and MPLS can also be set. Currently only IP4, IP6 are enabled in adjacency / rewrite code. Documentation in src/vnet/MTU.md Change-Id: Iee2fd6f0bbc8210748dd8e073ab9fab87d323690 Signed-off-by: Ole Troan <ot@cisco.com>
2018-06-08Implement DHCPv6 PD client (VPP-718, VPP-1050)Juraj Sloboda2-0/+493
Change-Id: I72a1ccdfdd5573335ef78fc01d5268934c73bd31 Signed-off-by: Juraj Sloboda <jsloboda@cisco.com>
2018-06-08Gratuitous ARP packet handlingNeale Ranns2-2/+104
only learn from a GARP packet if it is an update to an existing entry. Change-Id: I4c1b59cfedb911466e5e4c9756cf53a6676e1909 Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
2018-06-07DHCP Client DumpNeale Ranns2-6/+33
- use types on the DHCP API so that the same data is sent in comfing messages and in dumps - add the DHCP client dump API - update VOM to refelct API changes - rename VOM class dhcp_config* dhcp_client* - the VOM dhcp_client class maintains the lease data (which it reads on a dump) for clients to read Change-Id: I2a43463937cbd80c01d45798e74b21288d8b8ead Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
2018-05-30make test: VPP-1288 fix bogus sw_if_index valuesKlement Sekera1-8/+6
Change-Id: I9a61538f4c832f09269c6ddfcd55f909003b8694 Signed-off-by: Klement Sekera <ksekera@cisco.com>
2018-05-28NAT44: code cleanup and refactor (VPP-1285)Matus Fabian1-136/+150
Change-Id: I088163f10ae5515d7a9115781cc13ef563fafed5 Signed-off-by: Matus Fabian <matfabia@cisco.com>
2018-05-27Fixes make test errors with clang compiler on aarch64Sirshak Das1-3/+3
(VAPI_*BIN).d targets didnt have fake.api.vapi.h* as dependencies this causes the compilation to proceed before the python script generates the header files. Explicit linking of stdc++ is required for clang as errors like undefined reference to 'new operator' pop up. Change-Id: I3ca0ef048f392c4a032160ce0e4f7ae759f4c79d Signed-off-by: Sirshak Das <sirshak.das@arm.com> Reviewed-by: Brian Brooks <brian.brooks@arm.com> Reviewed-by: Honnappa Nagarahalli <honnappa.nagarahalli@arm.com>
2018-05-25bond: performance harvestingSteven1-11/+3
- hash is great. But it is a bit too slow for the DP. Use direct array indexing to quickly retrieve the slave interface. - the algorithm used by flow hash is great. But it is a bit too slow for the DP. Use l2_hash_hash() extracted from lb_hash.h which ECMP is using. It makes use of intrinsic crc32 instruction set. - shortcut modulo arithmetic when the operand is 2**x (where x up to 4) to avoid division instruction. - special case for link count == 1 in bond_tx_fn() - use clib_mem_unaligned to access data for the packet to avoid alignment error - Fix some typos for packet tracing. Change-Id: I8eae3ad497061c5473aa675ba894ee0211120d25 Signed-off-by: Steven <sluong@cisco.com>
or: #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 */ }
/*
 * decap.c - decapsulate VXLAN GPE
 *
 * Copyright (c) 2013 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.
 */
/**
 *  @file
 *  @brief Functions for decapsulating VXLAN GPE tunnels
 *
*/

#include <vlib/vlib.h>
#include <vnet/pg/pg.h>
#include <vnet/vxlan-gpe/vxlan_gpe.h>

/**
 * @brief Struct for VXLAN GPE decap packet tracing
 *
 */
typedef struct
{
  u32 next_index;
  u32 tunnel_index;
  u32 error;
} vxlan_gpe_rx_trace_t;

/**
 * @brief Tracing function for VXLAN GPE packet decapsulation
 *
 * @param *s
 * @param *args
 *
 * @return *s
 *
 */
static u8 *
format_vxlan_gpe_rx_trace (u8 * s, va_list * args)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
  vxlan_gpe_rx_trace_t *t = va_arg (*args, vxlan_gpe_rx_trace_t *);

  if (t->tunnel_index != ~0)
    {
      s = format (s, "VXLAN-GPE: tunnel %d next %d error %d", t->tunnel_index,
		  t->next_index, t->error);
    }
  else
    {
      s = format (s, "VXLAN-GPE: no tunnel next %d error %d\n", t->next_index,
		  t->error);
    }
  return s;
}

/**
 * @brief Tracing function for VXLAN GPE packet decapsulation including length
 *
 * @param *s
 * @param *args
 *
 * @return *s
 *
 */
static u8 *
format_vxlan_gpe_with_length (u8 * s, va_list * args)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);


  return s;
}

/**
 * @brief Common processing for IPv4 and IPv6 VXLAN GPE decap dispatch functions
 *
 * It is worth noting that other than trivial UDP forwarding (transit), VXLAN GPE
 * tunnels are "terminate local". This means that there is no "TX" interface for this
 * decap case, so that field in the buffer_metadata can be "used for something else".
 * The something else in this case is, for the IPv4/IPv6 inner-packet type case, the
 * FIB index used to look up the inner-packet's adjacency.
 *
 *      vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index;
 *
 * @param *vm
 * @param *node
 * @param *from_frame
 * @param is_ip4
 *
 * @return from_frame->n_vectors
 *
 */
always_inline uword
vxlan_gpe_input (vlib_main_t * vm,
		 vlib_node_runtime_t * node,
		 vlib_frame_t * from_frame, u8 is_ip4)
{
  u32 n_left_from, next_index, *from, *to_next;
  vxlan_gpe_main_t *nngm = &vxlan_gpe_main;
  vnet_main_t *vnm = nngm->vnet_main;
  vnet_interface_main_t *im = &vnm->interface_main;
  u32 last_tunnel_index = ~0;
  vxlan4_gpe_tunnel_key_t last_key4;
  vxlan6_gpe_tunnel_key_t last_key6;
  u32 pkts_decapsulated = 0;
  u32 thread_index = vm->thread_index;
  u32 stats_sw_if_index, stats_n_packets, stats_n_bytes;

  if (is_ip4)
    clib_memset (&last_key4, 0xff, sizeof (last_key4));
  else
    clib_memset (&last_key6, 0xff, sizeof (last_key6));

  from = vlib_frame_vector_args (from_frame);
  n_left_from = from_frame->n_vectors;

  next_index = node->cached_next_index;
  stats_sw_if_index = node->runtime_data[0];
  stats_n_packets = stats_n_bytes = 0;

  while (n_left_from > 0)
    {
      u32 n_left_to_next;

      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);

      while (n_left_from >= 4 && n_left_to_next >= 2)
	{
	  u32 bi0, bi1;
	  vlib_buffer_t *b0, *b1;
	  u32 next0, next1;
	  ip4_vxlan_gpe_header_t *iuvn4_0, *iuvn4_1;
	  ip6_vxlan_gpe_header_t *iuvn6_0, *iuvn6_1;
	  uword *p0, *p1;
	  u32 tunnel_index0, tunnel_index1;
	  vxlan_gpe_tunnel_t *t0, *t1;
	  vxlan4_gpe_tunnel_key_t key4_0, key4_1;
	  vxlan6_gpe_tunnel_key_t key6_0, key6_1;
	  u32 error0, error1;
	  u32 sw_if_index0, sw_if_index1, len0, len1;

	  /* Prefetch next iteration. */
	  {
	    vlib_buffer_t *p2, *p3;

	    p2 = vlib_get_buffer (vm, from[2]);
	    p3 = vlib_get_buffer (vm, from[3]);

	    vlib_prefetch_buffer_header (p2, LOAD);
	    vlib_prefetch_buffer_header (p3, LOAD);

	    CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
	    CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
	  }

	  bi0 = from[0];
	  bi1 = from[1];
	  to_next[0] = bi0;
	  to_next[1] = bi1;
	  from += 2;
	  to_next += 2;
	  n_left_to_next -= 2;
	  n_left_from -= 2;

	  b0 = vlib_get_buffer (vm, bi0);
	  b1 = vlib_get_buffer (vm, bi1);

	  if (is_ip4)
	    {
	      /* udp leaves current_data pointing at the vxlan-gpe header */
	      vlib_buffer_advance (b0,
				   -(word) (sizeof (udp_header_t) +
					    sizeof (ip4_header_t)));
	      vlib_buffer_advance (b1,
				   -(word) (sizeof (udp_header_t) +
					    sizeof (ip4_header_t)));

	      iuvn4_0 = vlib_buffer_get_current (b0);
	      iuvn4_1 = vlib_buffer_get_current (b1);

	      /* pop (ip, udp, vxlan) */
	      vlib_buffer_advance (b0, sizeof (*iuvn4_0));
	      vlib_buffer_advance (b1, sizeof (*iuvn4_1));
	    }
	  else
	    {
	      /* udp leaves current_data pointing at the vxlan-gpe header */
	      vlib_buffer_advance (b0,
				   -(word) (sizeof (udp_header_t) +
					    sizeof (ip6_header_t)));
	      vlib_buffer_advance (b1,
				   -(word) (sizeof (udp_header_t) +
					    sizeof (ip6_header_t)));

	      iuvn6_0 = vlib_buffer_get_current (b0);
	      iuvn6_1 = vlib_buffer_get_current (b1);

	      /* pop (ip, udp, vxlan) */
	      vlib_buffer_advance (b0, sizeof (*iuvn6_0));
	      vlib_buffer_advance (b1, sizeof (*iuvn6_1));
	    }

	  tunnel_index0 = ~0;
	  tunnel_index1 = ~0;
	  error0 = 0;
	  error1 = 0;

	  if (is_ip4)
	    {
	      next0 =
		(iuvn4_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
		nngm->decap_next_node_list[iuvn4_0->vxlan.protocol] :
		VXLAN_GPE_INPUT_NEXT_DROP;
	      next1 =
		(iuvn4_1->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
		nngm->decap_next_node_list[iuvn4_1->vxlan.protocol] :
		VXLAN_GPE_INPUT_NEXT_DROP;

	      key4_0.local = iuvn4_0->ip4.dst_address.as_u32;
	      key4_1.local = iuvn4_1->ip4.dst_address.as_u32;

	      key4_0.remote = iuvn4_0->ip4.src_address.as_u32;
	      key4_1.remote = iuvn4_1->ip4.src_address.as_u32;

	      key4_0.vni = iuvn4_0->vxlan.vni_res;
	      key4_1.vni = iuvn4_1->vxlan.vni_res;

	      key4_0.pad = 0;
	      key4_1.pad = 0;
	    }
	  else			/* is_ip6 */
	    {
	      next0 = (iuvn6_0->vxlan.protocol < node->n_next_nodes) ?
		iuvn6_0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP;
	      next1 = (iuvn6_1->vxlan.protocol < node->n_next_nodes) ?
		iuvn6_1->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP;

	      key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0];
	      key6_0.local.as_u64[1] = iuvn6_0->ip6.dst_address.as_u64[1];
	      key6_1.local.as_u64[0] = iuvn6_1->ip6.dst_address.as_u64[0];
	      key6_1.local.as_u64[1] = iuvn6_1->ip6.dst_address.as_u64[1];

	      key6_0.remote.as_u64[0] = iuvn6_0->ip6.src_address.as_u64[0];
	      key6_0.remote.as_u64[1] = iuvn6_0->ip6.src_address.as_u64[1];
	      key6_1.remote.as_u64[0] = iuvn6_1->ip6.src_address.as_u64[0];
	      key6_1.remote.as_u64[1] = iuvn6_1->ip6.src_address.as_u64[1];

	      key6_0.vni = iuvn6_0->vxlan.vni_res;
	      key6_1.vni = iuvn6_1->vxlan.vni_res;
	    }

	  /* Processing packet 0 */
	  if (is_ip4)
	    {
	      /* Processing for key4_0 */
	      if (PREDICT_FALSE ((key4_0.as_u64[0] != last_key4.as_u64[0])
				 || (key4_0.as_u64[1] !=
				     last_key4.as_u64[1])))
		{
		  p0 = hash_get_mem (nngm->vxlan4_gpe_tunnel_by_key, &key4_0);

		  if (p0 == 0)
		    {
		      error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
		      goto trace0;
		    }

		  last_key4.as_u64[0] = key4_0.as_u64[0];
		  last_key4.as_u64[1] = key4_0.as_u64[1];
		  tunnel_index0 = last_tunnel_index = p0[0];
		}
	      else
		tunnel_index0 = last_tunnel_index;
	    }
	  else			/* is_ip6 */
	    {
	      next0 =
		(iuvn6_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
		nngm->decap_next_node_list[iuvn6_0->vxlan.protocol] :
		VXLAN_GPE_INPUT_NEXT_DROP;
	      next1 =
		(iuvn6_1->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
		nngm->decap_next_node_list[iuvn6_1->vxlan.protocol] :
		VXLAN_GPE_INPUT_NEXT_DROP;

	      key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0];
	      key6_0.local.as_u64[1] = iuvn6_0->ip6.dst_address.as_u64[1];
	      key6_1.local.as_u64[0] = iuvn6_1->ip6.dst_address.as_u64[0];
	      key6_1.local.as_u64[1] = iuvn6_1->ip6.dst_address.as_u64[1];

	      key6_0.remote.as_u64[0] = iuvn6_0->ip6.src_address.as_u64[0];
	      key6_0.remote.as_u64[1] = iuvn6_0->ip6.src_address.as_u64[1];
	      key6_1.remote.as_u64[0] = iuvn6_1->ip6.src_address.as_u64[0];
	      key6_1.remote.as_u64[1] = iuvn6_1->ip6.src_address.as_u64[1];

	      key6_0.vni = iuvn6_0->vxlan.vni_res;
	      key6_1.vni = iuvn6_1->vxlan.vni_res;

	      /* Processing for key6_0 */
	      if (PREDICT_FALSE
		  (memcmp (&key6_0, &last_key6, sizeof (last_key6)) != 0))
		{
		  p0 = hash_get_mem (nngm->vxlan6_gpe_tunnel_by_key, &key6_0);

		  if (p0 == 0)
		    {
		      error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
		      goto trace0;
		    }

		  memcpy (&last_key6, &key6_0, sizeof (key6_0));
		  tunnel_index0 = last_tunnel_index = p0[0];
		}
	      else
		tunnel_index0 = last_tunnel_index;
	    }

	  t0 = pool_elt_at_index (nngm->tunnels, tunnel_index0);


	  sw_if_index0 = t0->sw_if_index;
	  len0 = vlib_buffer_length_in_chain (vm, b0);

	  /* Required to make the l2 tag push / pop code work on l2 subifs */
	  vnet_update_l2_len (b0);

	  /* Set packet input sw_if_index to unicast VXLAN tunnel for learning */
	  vnet_buffer (b0)->sw_if_index[VLIB_RX] = t0->sw_if_index;

      /**
       * ip[46] lookup in the configured FIB
       */
	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index;

	  pkts_decapsulated++;
	  stats_n_packets += 1;
	  stats_n_bytes += len0;

	  if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index))
	    {
	      stats_n_packets -= 1;
	      stats_n_bytes -= len0;
	      if (stats_n_packets)
		vlib_increment_combined_counter (im->combined_sw_if_counters +
						 VNET_INTERFACE_COUNTER_RX,
						 thread_index,
						 stats_sw_if_index,
						 stats_n_packets,
						 stats_n_bytes);
	      stats_n_packets = 1;
	      stats_n_bytes = len0;
	      stats_sw_if_index = sw_if_index0;
	    }

	trace0:b0->error = error0 ? node->errors[error0] : 0;

	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
	    {
	      vxlan_gpe_rx_trace_t *tr =
		vlib_add_trace (vm, node, b0, sizeof (*tr));
	      tr->next_index = next0;
	      tr->error = error0;
	      tr->tunnel_index = tunnel_index0;
	    }

	  /* Process packet 1 */
	  if (is_ip4)
	    {
	      /* Processing for key4_1 */
	      if (PREDICT_FALSE ((key4_1.as_u64[0] != last_key4.as_u64[0])
				 || (key4_1.as_u64[1] !=
				     last_key4.as_u64[1])))
		{
		  p1 = hash_get_mem (nngm->vxlan4_gpe_tunnel_by_key, &key4_1);

		  if (p1 == 0)
		    {
		      error1 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
		      goto trace1;
		    }

		  last_key4.as_u64[0] = key4_1.as_u64[0];
		  last_key4.as_u64[1] = key4_1.as_u64[1];
		  tunnel_index1 = last_tunnel_index = p1[0];
		}
	      else
		tunnel_index1 = last_tunnel_index;
	    }
	  else			/* is_ip6 */
	    {
	      /* Processing for key6_1 */
	      if (PREDICT_FALSE
		  (memcmp (&key6_1, &last_key6, sizeof (last_key6)) != 0))
		{
		  p1 = hash_get_mem (nngm->vxlan6_gpe_tunnel_by_key, &key6_1);

		  if (p1 == 0)
		    {
		      error1 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
		      goto trace1;
		    }

		  memcpy (&last_key6, &key6_1, sizeof (key6_1));
		  tunnel_index1 = last_tunnel_index = p1[0];
		}
	      else
		tunnel_index1 = last_tunnel_index;
	    }

	  t1 = pool_elt_at_index (nngm->tunnels, tunnel_index1);

	  sw_if_index1 = t1->sw_if_index;
	  len1 = vlib_buffer_length_in_chain (vm, b1);

	  /* Required to make the l2 tag push / pop code work on l2 subifs */
	  vnet_update_l2_len (b1);

	  /* Set packet input sw_if_index to unicast VXLAN tunnel for learning */
	  vnet_buffer (b1)->sw_if_index[VLIB_RX] = t1->sw_if_index;

	  /*
	   * ip[46] lookup in the configured FIB
	   */
	  vnet_buffer (b1)->sw_if_index[VLIB_TX] = t1->decap_fib_index;

	  pkts_decapsulated++;
	  stats_n_packets += 1;
	  stats_n_bytes += len1;

	  /* Batch stats increment on the same vxlan tunnel so counter
	     is not incremented per packet */
	  if (PREDICT_FALSE (sw_if_index1 != stats_sw_if_index))
	    {
	      stats_n_packets -= 1;
	      stats_n_bytes -= len1;
	      if (stats_n_packets)
		vlib_increment_combined_counter (im->combined_sw_if_counters +
						 VNET_INTERFACE_COUNTER_RX,
						 thread_index,
						 stats_sw_if_index,
						 stats_n_packets,
						 stats_n_bytes);
	      stats_n_packets = 1;
	      stats_n_bytes = len1;
	      stats_sw_if_index = sw_if_index1;
	    }
	  vnet_buffer (b1)->sw_if_index[VLIB_TX] = t1->decap_fib_index;

	trace1:b1->error = error1 ? node->errors[error1] : 0;

	  if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
	    {
	      vxlan_gpe_rx_trace_t *tr =
		vlib_add_trace (vm, node, b1, sizeof (*tr));
	      tr->next_index = next1;
	      tr->error = error1;
	      tr->tunnel_index = tunnel_index1;
	    }

	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
					   n_left_to_next, bi0, bi1, next0,
					   next1);
	}

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  u32 bi0;
	  vlib_buffer_t *b0;
	  u32 next0;
	  ip4_vxlan_gpe_header_t *iuvn4_0;
	  ip6_vxlan_gpe_header_t *iuvn6_0;
	  uword *p0;
	  u32 tunnel_index0;
	  vxlan_gpe_tunnel_t *t0;
	  vxlan4_gpe_tunnel_key_t key4_0;
	  vxlan6_gpe_tunnel_key_t key6_0;
	  u32 error0;
	  u32 sw_if_index0, len0;

	  bi0 = from[0];
	  to_next[0] = bi0;
	  from += 1;
	  to_next += 1;
	  n_left_from -= 1;
	  n_left_to_next -= 1;

	  b0 = vlib_get_buffer (vm, bi0);

	  if (is_ip4)
	    {
	      /* udp leaves current_data pointing at the vxlan-gpe header */
	      vlib_buffer_advance (b0,
				   -(word) (sizeof (udp_header_t) +
					    sizeof (ip4_header_t)));

	      iuvn4_0 = vlib_buffer_get_current (b0);

	      /* pop (ip, udp, vxlan) */
	      vlib_buffer_advance (b0, sizeof (*iuvn4_0));
	    }
	  else
	    {
	      /* udp leaves current_data pointing at the vxlan-gpe header */
	      vlib_buffer_advance (b0,
				   -(word) (sizeof (udp_header_t) +
					    sizeof (ip6_header_t)));

	      iuvn6_0 = vlib_buffer_get_current (b0);

	      /* pop (ip, udp, vxlan) */
	      vlib_buffer_advance (b0, sizeof (*iuvn6_0));
	    }

	  tunnel_index0 = ~0;
	  error0 = 0;

	  if (is_ip4)
	    {
	      next0 =
		(iuvn4_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
		nngm->decap_next_node_list[iuvn4_0->vxlan.protocol] :
		VXLAN_GPE_INPUT_NEXT_DROP;

	      key4_0.local = iuvn4_0->ip4.dst_address.as_u32;
	      key4_0.remote = iuvn4_0->ip4.src_address.as_u32;
	      key4_0.vni = iuvn4_0->vxlan.vni_res;
	      key4_0.pad = 0;

	      /* Processing for key4_0 */
	      if (PREDICT_FALSE ((key4_0.as_u64[0] != last_key4.as_u64[0])
				 || (key4_0.as_u64[1] !=
				     last_key4.as_u64[1])))
		{
		  p0 = hash_get_mem (nngm->vxlan4_gpe_tunnel_by_key, &key4_0);

		  if (p0 == 0)
		    {
		      error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
		      goto trace00;
		    }

		  last_key4.as_u64[0] = key4_0.as_u64[0];
		  last_key4.as_u64[1] = key4_0.as_u64[1];
		  tunnel_index0 = last_tunnel_index = p0[0];
		}
	      else
		tunnel_index0 = last_tunnel_index;
	    }
	  else			/* is_ip6 */
	    {
	      next0 =
		(iuvn6_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
		nngm->decap_next_node_list[iuvn6_0->vxlan.protocol] :
		VXLAN_GPE_INPUT_NEXT_DROP;

	      key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0];
	      key6_0.local.as_u64[1] = iuvn6_0->ip6.dst_address.as_u64[1];
	      key6_0.remote.as_u64[0] = iuvn6_0->ip6.src_address.as_u64[0];
	      key6_0.remote.as_u64[1] = iuvn6_0->ip6.src_address.as_u64[1];
	      key6_0.vni = iuvn6_0->vxlan.vni_res;

	      /* Processing for key6_0 */
	      if (PREDICT_FALSE
		  (memcmp (&key6_0, &last_key6, sizeof (last_key6)) != 0))
		{
		  p0 = hash_get_mem (nngm->vxlan6_gpe_tunnel_by_key, &key6_0);

		  if (p0 == 0)
		    {
		      error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
		      goto trace00;
		    }

		  memcpy (&last_key6, &key6_0, sizeof (key6_0));
		  tunnel_index0 = last_tunnel_index = p0[0];
		}
	      else
		tunnel_index0 = last_tunnel_index;
	    }

	  t0 = pool_elt_at_index (nngm->tunnels, tunnel_index0);


	  sw_if_index0 = t0->sw_if_index;
	  len0 = vlib_buffer_length_in_chain (vm, b0);

	  /* Required to make the l2 tag push / pop code work on l2 subifs */
	  vnet_update_l2_len (b0);

	  /* Set packet input sw_if_index to unicast VXLAN tunnel for learning */
	  vnet_buffer (b0)->sw_if_index[VLIB_RX] = t0->sw_if_index;

	  /*
	   * ip[46] lookup in the configured FIB
	   */
	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = t0->decap_fib_index;

	  pkts_decapsulated++;
	  stats_n_packets += 1;
	  stats_n_bytes += len0;

	  /* Batch stats increment on the same vxlan-gpe tunnel so counter
	     is not incremented per packet */
	  if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index))
	    {
	      stats_n_packets -= 1;
	      stats_n_bytes -= len0;
	      if (stats_n_packets)
		vlib_increment_combined_counter (im->combined_sw_if_counters +
						 VNET_INTERFACE_COUNTER_RX,
						 thread_index,
						 stats_sw_if_index,
						 stats_n_packets,
						 stats_n_bytes);
	      stats_n_packets = 1;
	      stats_n_bytes = len0;
	      stats_sw_if_index = sw_if_index0;
	    }

	trace00:b0->error = error0 ? node->errors[error0] : 0;

	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
	    {
	      vxlan_gpe_rx_trace_t *tr =
		vlib_add_trace (vm, node, b0, sizeof (*tr));
	      tr->next_index = next0;
	      tr->error = error0;
	      tr->tunnel_index = tunnel_index0;
	    }
	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
					   n_left_to_next, bi0, next0);
	}

      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }

  vlib_node_increment_counter (vm,
			       is_ip4 ? vxlan4_gpe_input_node.index :
			       vxlan6_gpe_input_node.index,
			       VXLAN_GPE_ERROR_DECAPSULATED,
			       pkts_decapsulated);

  /* Increment any remaining batch stats */
  if (stats_n_packets)
    {
      vlib_increment_combined_counter (im->combined_sw_if_counters +
				       VNET_INTERFACE_COUNTER_RX,
				       thread_index, stats_sw_if_index,
				       stats_n_packets, stats_n_bytes);
      node->runtime_data[0] = stats_sw_if_index;
    }
  return from_frame->n_vectors;
}

/**
 * @brief Graph processing dispatch function for IPv4 VXLAN GPE
 *
 * @node vxlan4-gpe-input
 * @param *vm
 * @param *node
 * @param *from_frame
 *
 * @return from_frame->n_vectors
 *
 */
VLIB_NODE_FN (vxlan4_gpe_input_node) (vlib_main_t * vm,
				      vlib_node_runtime_t * node,
				      vlib_frame_t * from_frame)
{
  return vxlan_gpe_input (vm, node, from_frame, /* is_ip4 */ 1);
}

#ifndef CLIB_MARCH_VARIANT
void
vxlan_gpe_register_decap_protocol (u8 protocol_id, uword next_node_index)
{
  vxlan_gpe_main_t *hm = &vxlan_gpe_main;
  hm->decap_next_node_list[protocol_id] = next_node_index;
  return;
}

void
vxlan_gpe_unregister_decap_protocol (u8 protocol_id, uword next_node_index)
{
  vxlan_gpe_main_t *hm = &vxlan_gpe_main;
  hm->decap_next_node_list[protocol_id] = VXLAN_GPE_INPUT_NEXT_DROP;
  return;
}
#endif /* CLIB_MARCH_VARIANT */

/**
 * @brief Graph processing dispatch function for IPv6 VXLAN GPE
 *
 * @node vxlan6-gpe-input
 * @param *vm
 * @param *node
 * @param *from_frame
 *
 * @return from_frame->n_vectors - uword
 *
 */
VLIB_NODE_FN (vxlan6_gpe_input_node) (vlib_main_t * vm,
				      vlib_node_runtime_t * node,
				      vlib_frame_t * from_frame)
{
  return vxlan_gpe_input (vm, node, from_frame, /* is_ip4 */ 0);
}

/**
 * @brief VXLAN GPE error strings
 */
static char *vxlan_gpe_error_strings[] = {
#define vxlan_gpe_error(n,s) s,
#include <vnet/vxlan-gpe/vxlan_gpe_error.def>
#undef vxlan_gpe_error
#undef _
};

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (vxlan4_gpe_input_node) = {
  .name = "vxlan4-gpe-input",
  /* Takes a vector of packets. */
  .vector_size = sizeof (u32),
  .type = VLIB_NODE_TYPE_INTERNAL,
  .n_errors = ARRAY_LEN(vxlan_gpe_error_strings),
  .error_strings = vxlan_gpe_error_strings,

  .n_next_nodes = VXLAN_GPE_INPUT_N_NEXT,
  .next_nodes = {
#define _(s,n) [VXLAN_GPE_INPUT_NEXT_##s] = n,
    foreach_vxlan_gpe_input_next
#undef _
  },

  .format_buffer = format_vxlan_gpe_with_length,
  .format_trace = format_vxlan_gpe_rx_trace,
  // $$$$ .unformat_buffer = unformat_vxlan_gpe_header,
};
/* *INDENT-ON* */

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (vxlan6_gpe_input_node) = {
  .name = "vxlan6-gpe-input",
  /* Takes a vector of packets. */
  .vector_size = sizeof (u32),
  .type = VLIB_NODE_TYPE_INTERNAL,
  .n_errors = ARRAY_LEN(vxlan_gpe_error_strings),
  .error_strings = vxlan_gpe_error_strings,

  .n_next_nodes = VXLAN_GPE_INPUT_N_NEXT,
  .next_nodes = {
#define _(s,n) [VXLAN_GPE_INPUT_NEXT_##s] = n,
    foreach_vxlan_gpe_input_next
#undef _
  },

  .format_buffer = format_vxlan_gpe_with_length,
  .format_trace = format_vxlan_gpe_rx_trace,
  // $$$$ .unformat_buffer = unformat_vxlan_gpe_header,
};
/* *INDENT-ON* */

typedef enum
{
  IP_VXLAN_BYPASS_NEXT_DROP,
  IP_VXLAN_BYPASS_NEXT_VXLAN,
  IP_VXLAN_BYPASS_N_NEXT,
} ip_vxlan_bypass_next_t;

always_inline uword
ip_vxlan_gpe_bypass_inline (vlib_main_t * vm,
			    vlib_node_runtime_t * node,
			    vlib_frame_t * frame, u32 is_ip4)
{
  vxlan_gpe_main_t *ngm = &vxlan_gpe_main;
  u32 *from, *to_next, n_left_from, n_left_to_next, next_index;
  vlib_node_runtime_t *error_node =
    vlib_node_get_runtime (vm, ip4_input_node.index);
  ip4_address_t addr4;		/* last IPv4 address matching a local VTEP address */
  ip6_address_t addr6;		/* last IPv6 address matching a local VTEP address */

  from = vlib_frame_vector_args (frame);
  n_left_from = frame->n_vectors;
  next_index = node->cached_next_index;

  if (node->flags & VLIB_NODE_FLAG_TRACE)
    ip4_forward_next_trace (vm, node, frame, VLIB_TX);

  if (is_ip4)
    addr4.data_u32 = ~0;
  else
    ip6_address_set_zero (&addr6);

  while (n_left_from > 0)
    {
      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);

      while (n_left_from >= 4 && n_left_to_next >= 2)
	{
	  vlib_buffer_t *b0, *b1;
	  ip4_header_t *ip40, *ip41;
	  ip6_header_t *ip60, *ip61;
	  udp_header_t *udp0, *udp1;
	  u32 bi0, ip_len0, udp_len0, flags0, next0;
	  u32 bi1, ip_len1, udp_len1, flags1, next1;
	  i32 len_diff0, len_diff1;
	  u8 error0, good_udp0, proto0;
	  u8 error1, good_udp1, proto1;

	  /* Prefetch next iteration. */
	  {
	    vlib_buffer_t *p2, *p3;

	    p2 = vlib_get_buffer (vm, from[2]);
	    p3 = vlib_get_buffer (vm, from[3]);

	    vlib_prefetch_buffer_header (p2, LOAD);
	    vlib_prefetch_buffer_header (p3, LOAD);

	    CLIB_PREFETCH (p2->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
	    CLIB_PREFETCH (p3->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
	  }

	  bi0 = to_next[0] = from[0];
	  bi1 = to_next[1] = from[1];
	  from += 2;
	  n_left_from -= 2;
	  to_next += 2;
	  n_left_to_next -= 2;

	  b0 = vlib_get_buffer (vm, bi0);
	  b1 = vlib_get_buffer (vm, bi1);
	  if (is_ip4)
	    {
	      ip40 = vlib_buffer_get_current (b0);
	      ip41 = vlib_buffer_get_current (b1);
	    }
	  else
	    {
	      ip60 = vlib_buffer_get_current (b0);
	      ip61 = vlib_buffer_get_current (b1);
	    }

	  /* Setup packet for next IP feature */
	  vnet_feature_next (&next0, b0);
	  vnet_feature_next (&next1, b1);

	  if (is_ip4)
	    {
	      proto0 = ip40->protocol;
	      proto1 = ip41->protocol;
	    }
	  else
	    {
	      proto0 = ip60->protocol;
	      proto1 = ip61->protocol;
	    }

	  /* Process packet 0 */
	  if (proto0 != IP_PROTOCOL_UDP)
	    goto exit0;		/* not UDP packet */

	  if (is_ip4)
	    udp0 = ip4_next_header (ip40);
	  else
	    udp0 = ip6_next_header (ip60);

	  if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE))
	    goto exit0;		/* not VXLAN packet */

	  /* Validate DIP against VTEPs */
	  if (is_ip4)
	    {
	      if (addr4.as_u32 != ip40->dst_address.as_u32)
		{
		  if (!hash_get (ngm->vtep4, ip40->dst_address.as_u32))
		    goto exit0;	/* no local VTEP for VXLAN packet */
		  addr4 = ip40->dst_address;
		}
	    }
	  else
	    {
	      if (!ip6_address_is_equal (&addr6, &ip60->dst_address))
		{
		  if (!hash_get_mem (ngm->vtep6, &ip60->dst_address))
		    goto exit0;	/* no local VTEP for VXLAN packet */
		  addr6 = ip60->dst_address;
		}
	    }

	  flags0 = b0->flags;
	  good_udp0 = (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;

	  /* Don't verify UDP checksum for packets with explicit zero checksum. */
	  good_udp0 |= udp0->checksum == 0;

	  /* Verify UDP length */
	  if (is_ip4)
	    ip_len0 = clib_net_to_host_u16 (ip40->length);
	  else
	    ip_len0 = clib_net_to_host_u16 (ip60->payload_length);
	  udp_len0 = clib_net_to_host_u16 (udp0->length);
	  len_diff0 = ip_len0 - udp_len0;

	  /* Verify UDP checksum */
	  if (PREDICT_FALSE (!good_udp0))
	    {
	      if ((flags0 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0)
		{
		  if (is_ip4)
		    flags0 = ip4_tcp_udp_validate_checksum (vm, b0);
		  else
		    flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0);
		  good_udp0 =
		    (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
		}
	    }

	  if (is_ip4)
	    {
	      error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM;
	      error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH;
	    }
	  else
	    {
	      error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM;
	      error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH;
	    }

	  next0 = error0 ?
	    IP_VXLAN_BYPASS_NEXT_DROP : IP_VXLAN_BYPASS_NEXT_VXLAN;
	  b0->error = error0 ? error_node->errors[error0] : 0;

	  /* vxlan_gpe-input node expect current at VXLAN header */
	  if (is_ip4)
	    vlib_buffer_advance (b0,
				 sizeof (ip4_header_t) +
				 sizeof (udp_header_t));
	  else
	    vlib_buffer_advance (b0,
				 sizeof (ip6_header_t) +
				 sizeof (udp_header_t));

	exit0:
	  /* Process packet 1 */
	  if (proto1 != IP_PROTOCOL_UDP)
	    goto exit1;		/* not UDP packet */

	  if (is_ip4)
	    udp1 = ip4_next_header (ip41);
	  else
	    udp1 = ip6_next_header (ip61);

	  if (udp1->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE))
	    goto exit1;		/* not VXLAN packet */

	  /* Validate DIP against VTEPs */
	  if (is_ip4)
	    {
	      if (addr4.as_u32 != ip41->dst_address.as_u32)
		{
		  if (!hash_get (ngm->vtep4, ip41->dst_address.as_u32))
		    goto exit1;	/* no local VTEP for VXLAN packet */
		  addr4 = ip41->dst_address;
		}
	    }
	  else
	    {
	      if (!ip6_address_is_equal (&addr6, &ip61->dst_address))
		{
		  if (!hash_get_mem (ngm->vtep6, &ip61->dst_address))
		    goto exit1;	/* no local VTEP for VXLAN packet */
		  addr6 = ip61->dst_address;
		}
	    }

	  flags1 = b1->flags;
	  good_udp1 = (flags1 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;

	  /* Don't verify UDP checksum for packets with explicit zero checksum. */
	  good_udp1 |= udp1->checksum == 0;

	  /* Verify UDP length */
	  if (is_ip4)
	    ip_len1 = clib_net_to_host_u16 (ip41->length);
	  else
	    ip_len1 = clib_net_to_host_u16 (ip61->payload_length);
	  udp_len1 = clib_net_to_host_u16 (udp1->length);
	  len_diff1 = ip_len1 - udp_len1;

	  /* Verify UDP checksum */
	  if (PREDICT_FALSE (!good_udp1))
	    {
	      if ((flags1 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0)
		{
		  if (is_ip4)
		    flags1 = ip4_tcp_udp_validate_checksum (vm, b1);
		  else
		    flags1 = ip6_tcp_udp_icmp_validate_checksum (vm, b1);
		  good_udp1 =
		    (flags1 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
		}
	    }

	  if (is_ip4)
	    {
	      error1 = good_udp1 ? 0 : IP4_ERROR_UDP_CHECKSUM;
	      error1 = (len_diff1 >= 0) ? error1 : IP4_ERROR_UDP_LENGTH;
	    }
	  else
	    {
	      error1 = good_udp1 ? 0 : IP6_ERROR_UDP_CHECKSUM;
	      error1 = (len_diff1 >= 0) ? error1 : IP6_ERROR_UDP_LENGTH;
	    }

	  next1 = error1 ?
	    IP_VXLAN_BYPASS_NEXT_DROP : IP_VXLAN_BYPASS_NEXT_VXLAN;
	  b1->error = error1 ? error_node->errors[error1] : 0;

	  /* vxlan_gpe-input node expect current at VXLAN header */
	  if (is_ip4)
	    vlib_buffer_advance (b1,
				 sizeof (ip4_header_t) +
				 sizeof (udp_header_t));
	  else
	    vlib_buffer_advance (b1,
				 sizeof (ip6_header_t) +
				 sizeof (udp_header_t));

	exit1:
	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, bi1, next0, next1);
	}

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  vlib_buffer_t *b0;
	  ip4_header_t *ip40;
	  ip6_header_t *ip60;
	  udp_header_t *udp0;
	  u32 bi0, ip_len0, udp_len0, flags0, next0;
	  i32 len_diff0;
	  u8 error0, good_udp0, proto0;

	  bi0 = to_next[0] = from[0];
	  from += 1;
	  n_left_from -= 1;
	  to_next += 1;
	  n_left_to_next -= 1;

	  b0 = vlib_get_buffer (vm, bi0);
	  if (is_ip4)
	    ip40 = vlib_buffer_get_current (b0);
	  else
	    ip60 = vlib_buffer_get_current (b0);

	  /* Setup packet for next IP feature */
	  vnet_feature_next (&next0, b0);

	  if (is_ip4)
	    proto0 = ip40->protocol;
	  else
	    proto0 = ip60->protocol;

	  if (proto0 != IP_PROTOCOL_UDP)
	    goto exit;		/* not UDP packet */

	  if (is_ip4)
	    udp0 = ip4_next_header (ip40);
	  else
	    udp0 = ip6_next_header (ip60);

	  if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE))
	    goto exit;		/* not VXLAN packet */

	  /* Validate DIP against VTEPs */
	  if (is_ip4)
	    {
	      if (addr4.as_u32 != ip40->dst_address.as_u32)
		{
		  if (!hash_get (ngm->vtep4, ip40->dst_address.as_u32))
		    goto exit;	/* no local VTEP for VXLAN packet */
		  addr4 = ip40->dst_address;
		}
	    }
	  else
	    {
	      if (!ip6_address_is_equal (&addr6, &ip60->dst_address))
		{
		  if (!hash_get_mem (ngm->vtep6, &ip60->dst_address))
		    goto exit;	/* no local VTEP for VXLAN packet */
		  addr6 = ip60->dst_address;
		}
	    }

	  flags0 = b0->flags;
	  good_udp0 = (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;

	  /* Don't verify UDP checksum for packets with explicit zero checksum. */
	  good_udp0 |= udp0->checksum == 0;

	  /* Verify UDP length */
	  if (is_ip4)
	    ip_len0 = clib_net_to_host_u16 (ip40->length);
	  else
	    ip_len0 = clib_net_to_host_u16 (ip60->payload_length);
	  udp_len0 = clib_net_to_host_u16 (udp0->length);
	  len_diff0 = ip_len0 - udp_len0;

	  /* Verify UDP checksum */
	  if (PREDICT_FALSE (!good_udp0))
	    {
	      if ((flags0 & VNET_BUFFER_F_L4_CHECKSUM_COMPUTED) == 0)
		{
		  if (is_ip4)
		    flags0 = ip4_tcp_udp_validate_checksum (vm, b0);
		  else
		    flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0);
		  good_udp0 =
		    (flags0 & VNET_BUFFER_F_L4_CHECKSUM_CORRECT) != 0;
		}
	    }

	  if (is_ip4)
	    {
	      error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM;
	      error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH;
	    }
	  else
	    {
	      error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM;
	      error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH;
	    }

	  next0 = error0 ?
	    IP_VXLAN_BYPASS_NEXT_DROP : IP_VXLAN_BYPASS_NEXT_VXLAN;
	  b0->error = error0 ? error_node->errors[error0] : 0;

	  /* vxlan_gpe-input node expect current at VXLAN header */
	  if (is_ip4)
	    vlib_buffer_advance (b0,
				 sizeof (ip4_header_t) +
				 sizeof (udp_header_t));
	  else
	    vlib_buffer_advance (b0,
				 sizeof (ip6_header_t) +
				 sizeof (udp_header_t));

	exit:
	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, next0);
	}

      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }

  return frame->n_vectors;
}

VLIB_NODE_FN (ip4_vxlan_gpe_bypass_node) (vlib_main_t * vm,
					  vlib_node_runtime_t * node,
					  vlib_frame_t * frame)
{
  return ip_vxlan_gpe_bypass_inline (vm, node, frame, /* is_ip4 */ 1);
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (ip4_vxlan_gpe_bypass_node) = {
  .name = "ip4-vxlan-gpe-bypass",
  .vector_size = sizeof (u32),

  .n_next_nodes = IP_VXLAN_BYPASS_N_NEXT,
  .next_nodes = {
    [IP_VXLAN_BYPASS_NEXT_DROP] = "error-drop",
    [IP_VXLAN_BYPASS_NEXT_VXLAN] = "vxlan4-gpe-input",
  },

  .format_buffer = format_ip4_header,
  .format_trace = format_ip4_forward_next_trace,
};
/* *INDENT-ON* */

#ifndef CLIB_MARCH_VARIANT
/* Dummy init function to get us linked in. */
clib_error_t *
ip4_vxlan_gpe_bypass_init (vlib_main_t * vm)
{
  return 0;
}

VLIB_INIT_FUNCTION (ip4_vxlan_gpe_bypass_init);
#endif /* CLIB_MARCH_VARIANT */

VLIB_NODE_FN (ip6_vxlan_gpe_bypass_node) (vlib_main_t * vm,
					  vlib_node_runtime_t * node,
					  vlib_frame_t * frame)
{
  return ip_vxlan_gpe_bypass_inline (vm, node, frame, /* is_ip4 */ 0);
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (ip6_vxlan_gpe_bypass_node) = {
  .name = "ip6-vxlan-gpe-bypass",
  .vector_size = sizeof (u32),

  .n_next_nodes = IP_VXLAN_BYPASS_N_NEXT,
  .next_nodes = {
    [IP_VXLAN_BYPASS_NEXT_DROP] = "error-drop",
    [IP_VXLAN_BYPASS_NEXT_VXLAN] = "vxlan6-gpe-input",
  },

  .format_buffer = format_ip6_header,
  .format_trace = format_ip6_forward_next_trace,
};
/* *INDENT-ON* */

#ifndef CLIB_MARCH_VARIANT
/* Dummy init function to get us linked in. */
clib_error_t *
ip6_vxlan_gpe_bypass_init (vlib_main_t * vm)
{
  return 0;
}

VLIB_INIT_FUNCTION (ip6_vxlan_gpe_bypass_init);
#endif /* CLIB_MARCH_VARIANT */

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