aboutsummaryrefslogtreecommitdiffstats
AgeCommit message (Collapse)AuthorFilesLines
2023-02-10hs-test: refactor test cases from no-topo suiteMaros Ondrejicka19-649/+207
This converts remaining tests to configation of VPP from test context. Type: test Change-Id: I386714f6b290e03d1757c2a033a25fae0340f5d6 Signed-off-by: Maros Ondrejicka <mondreji@cisco.com>
2023-02-10hs-test: refactor test cases from ns suiteMaros Ondrejicka20-524/+333
This converts more tests to configure VPP from test context. Type: test Signed-off-by: Maros Ondrejicka <maros.ondrejicka@pantheon.tech> Change-Id: Idf26b0c16f87e87c97b198412af39b99d947ced6
2023-02-10tests: use existing pip compiled req file for building the run.py venvNaveen Joy1-18/+1
pip compiled requirements file named requirements-3.txt exists in the test directory. No need to auto-generate it again Type: improvement Change-Id: Ib2b51c983af8d0e4b000e4544012b6cd94405519 Signed-off-by: Naveen Joy <najoy@cisco.com>
2023-02-10tests: use iperf3 for running interface tests on the hostNaveen Joy1-1/+20
Type: improvement Change-Id: I7123591932d51ce0c5b372893454945bbd3913b2 Signed-off-by: Naveen Joy <najoy@cisco.com>
2023-02-09hs-test: configure VPP from test contextMaros Ondrejicka16-417/+891
Instead of configuring VPP instances running inside of a container, now the configuration is going to be done from within the test context by using binary API and shared volume that exposes api socket. This converts just some of the test cases, rest is to follow. Type: test Signed-off-by: Maros Ondrejicka <maros.ondrejicka@pantheon.tech> Change-Id: I87e4ab15de488f0eebb01ff514596265fc2a787f
2023-02-08session: accept lcl ip updates on cl sessionsFlorin Coras1-0/+2
Allow apps/vcl to provide updated local ips for dgrams. In particular, allow sessions bound to 0/0 to send data with valid local ips. Type: improvement Signed-off-by: Florin Coras <fcoras@cisco.com> Change-Id: I50a086b1c252731a32a15b6a181ad3dba0c687e0
2023-02-08build: allow skipping external-depsMohammed Hawari2-0/+22
Change-Id: I0e5090ec6978af0dc4baecc7654918cf40663f42 Signed-off-by: Mohammed Hawari <mohammed@hawari.fr> Type: feature
2023-02-08avf dpdk: fix incorrect handling of IPv6 src address in flowTing Xu2-3/+4
In current flow creating process in native avf and dpdk-plugins, when parsing the input arguments, it does not copy IPv6 src address correctly, so that IPv6 src address will not be configured in any flow rule, and any packet with the same address will not be matched. Type: fix Signed-off-by: Ting Xu <ting.xu@intel.com> Change-Id: Ic957c57e3e1488b74e6281f4ed1df7fd491af35c
2023-02-08avf: fix incorrect flag for flow directorTing Xu1-2/+1
When parsing flow action type in avf, there is an incorrect flag for flow director, which makes flow director rule created unexpectedly. Type: fix Signed-off-by: Ting Xu <ting.xu@intel.com> Change-Id: Id9fed5db8ccacd5cc6c2f4833183364d763188c1
2023-02-08avf: fix checksum offload configurationTing Xu2-3/+1
Fix some configurations of avf checksum offload to get the correct udp and tcp checksum. Change Tx checksum offload capability since avf supports ipv4, tcp and udp offload all. Remove the operation to swap bit of checksum. Type: fix Signed-off-by: Ting Xu <ting.xu@intel.com> Change-Id: I55a916cc9ee6bef5b2074b5b6bb5f517fc2c178d
2023-02-08avf: fix bit calculation function fls_u32Ting Xu1-1/+1
In avf the function fls_u32 is used to calculate the power of 2. Fix the expression of this function. Type: fix Signed-off-by: Ting Xu <ting.xu@intel.com> Change-Id: I27160de8588a5efb3f24306597a5a240deb3ab74
2023-02-08ip6-nd: support dump/details for IPv6 RAAlexander Chernavin6-98/+549
Type: improvement With this change, add support for dumping IPv6 Router Advertisements details on a per-interface basis (or all). Also, cover that with a test. Signed-off-by: Alexander Chernavin <achernavin@netgate.com> Change-Id: I89fa93439d33cc36252377f27187b18b3d30a1d4
2023-02-08ipsec: fix AES CBC IV generation (CVE-2022-46397)Benoît Ganne3-29/+65
For AES-CBC, the IV must be unpredictable (see NIST SP800-38a Appendix C). Chaining IVs like is done by ipsecmb and native backends for the VNET_CRYPTO_OP_FLAG_INIT_IV is fully predictable. Encrypt a counter as part of the message, making the (predictable) counter-generated IV unpredictable. Fixes: VPP-2037 Type: fix Change-Id: If4f192d62bf97dda553e7573331c75efa11822ae Signed-off-by: Benoît Ganne <bganne@cisco.com>
2023-02-07vcl: drop lock on segment attach failureFlorin Coras1-0/+1
Type: fix Signed-off-by: Florin Coras <fcoras@cisco.com> Change-Id: I3bc2c7986f492b7b7dfbc84e4893202354223790
2023-02-07vcl: add ldp implementation for recvmmsgFlorin Coras1-31/+39
Type: improvement Signed-off-by: Florin Coras <fcoras@cisco.com> Change-Id: I7322abc3d3b0aa81399667bf02b03786fc62c958
2023-02-07vcl: better handlig of ldp apis that rely on gnu sourceFlorin Coras6-91/+139
Control use of apis that rely on _GNU_SOURCE being defined with compile time macro. Also fixes sendmmsg and recvmmsg which were not probably wrapped. Type: improvement Signed-off-by: Florin Coras <fcoras@cisco.com> Change-Id: I207de23210d4b9dc960bb4289159502760c5614d
2023-02-07packetforge: fix lack of edge for ipv6 after gtppscTing Xu1-0/+5
Add one new edge for ipv6 after gtppsc so that packetforge can parse this protocol combination. Type: fix Signed-off-by: Ting Xu <ting.xu@intel.com> Change-Id: I1bae1ec617c4867de2e0b3de27eda77b89e5580c
2023-02-06hs-test: add nginx perf testsFilip Tehlar2-1/+74
Type: test Signed-off-by: Filip Tehlar <ftehlar@cisco.com> Change-Id: Ic609cf70c1d381afa78f393700359434c8bd0452
2023-02-06vppinfra: refactor clib_socket_init, add linux netns supportDamjan Marion8-347/+494
Type: improvement Change-Id: Ida2d044bccf0bc8914b4fe7d383f827400fa6a52 Signed-off-by: Damjan Marion <dmarion@me.com>
2023-02-06ipsec: fix SA names consistency in testsArthur de Kerhor5-127/+127
In some IPsec tests, the SA called scapy_sa designs the SA that encrypts Scapy packets and decrypts them in VPP, and the one called vpp_sa the SA that encrypts VPP packets and decrypts them with Scapy. However, this pattern is not consistent across all tests. Some tests use the opposite logic. Others even mix both correlating scapy_tra_spi with vpp_tra_sa_id and vice-versa. Because of that, sometimes, the SA called vpp_sa_in is used as an outbound SA and vpp_sa_out as an inbound one. This patch forces all the tests to follow the same following logic: - scapy_sa is the SA used to encrypt Scapy packets and decrypt them in VPP. It matches the VPP inbound SA. - vpp_sa is the SA used to encrypt VPP packets and decrypt them in Scapy. It matches the VPP outbound SA. Type: fix Signed-off-by: Arthur de Kerhor <arthurdekerhor@gmail.com> Change-Id: Iadccdccbf98e834add13b5f4ad87af57e2ea3c2a
2023-02-06ipsec: fix async crypto linked keys memory leakBenoît Ganne1-1/+6
Type: fix Change-Id: I7bd2696541c8b3824837e187de096fdde19b2c44 Signed-off-by: Benoît Ganne <bganne@cisco.com>
2023-02-03session: fix out of bounds event memcpyFlorin Coras1-3/+1
Type: fix Signed-off-by: Florin Coras <fcoras@cisco.com> Change-Id: If5300653edd2dad470985f4591959d00cad2a43b
2023-02-03nat: fix accidental o2i deletion/reuseDmitry Valter2-2/+79
Nat session is allocated before the port allocation. During port allocation candidate address+port are set to o2i 6-tuple and tested against the flow hash. If insertion fails, the port is busy and rejected. When all N attempts are unsuccessful, "out-of-ports" error is recorded and the session is to be deleted. During session deletion o2i and i2o tuples are deleted from the flow hash. In case of "out-of-ports" i2o tuple is not valid, however o2i is and it refers to **some other** session that's known to be allocated. By backing match tuple up session should be invalidated well enough not to collide with any valid one. Type: fix Signed-off-by: Dmitry Valter <d-valter@yandex-team.ru> Change-Id: Id30be6f26ecce7a5a63135fb971bb65ce318af82
2023-02-03vpp-swan: allow SAs to be used to the route-based IPsecAtzm Watanabe1-1/+17
This patch adds a "charon.plugins.kernel-vpp.use_tunnel_mode_sa" key into strongswan.conf. If this is turned off, SAs will be installed without tunnel information and can be used to "ipsec tunnel protect". For the route-based IPsec, it will be used with turning "policies" off in swanctl.conf. Type: feature Signed-off-by: Atzm Watanabe <atzmism@gmail.com> Change-Id: I58fb94bfe56627fa7002d9b95c48930a32993d2d
2023-02-03vppapigen: fix incorrect comments in jsonOndrej Fabry2-1/+4
Type: fix Signed-off-by: Ondrej Fabry <ofabry@cisco.com> Change-Id: I241cefbbce98cf6fef83f36bd87ae2c1f4b067f0
2023-02-02tls: openssl: fix SSL_read partial read scenarioOfer Heifetz1-8/+10
When application performs SSL_read from the app rx-fifo, it can pre-allocate multiple segments, but there is an issue if the OpenSSL manages to partially fill in the first segment, in this case, since data is assumed to be copied over by OpenSSL to the pre-allocated segments(s), vpp uses svm_fifo_enqueue_nocopy API which performs zero copy by passing the pre-allocated segment to SSL_read. If the decrypted data size is smaller than the pre-allocated fifo segment buffer size, application will fetch buffers including zero in the area not filled in by SSL_read. Type: fix Signed-off-by: Ofer Heifetz <oferh@marvell.com> Change-Id: I941a89b17d567d86e5bd2c35785f1df043c33f38
2023-02-02linux-cp: fix auto-sub-intStanislav Zaikin2-1/+5
lcp_itf_pair_pool could grew during sub-interface creation. Type: fix Signed-off-by: Stanislav Zaikin <zstaseg@gmail.com> Change-Id: Ideafe392f9bb2b418ce9d6faa4f08dfe26f4a273
2023-02-02ip: fix ip ACL tracesBenoît Ganne1-6/+9
If we match a next table, we must save its index in the trace instead of the index of the 1st table. Type: fix Change-Id: Idd862242e7fc200eb3ab29b17a26131b844af2c0 Signed-off-by: Benoît Ganne <bganne@cisco.com>
2023-02-02af_xdp: update custom XDP program exampleYulong Pei2-53/+65
Update custom XDP program example to work with libbpf 0.8.0 and libxdp 1.2.9. Type: fix Signed-off-by: Yulong Pei <yulong.pei@intel.com> Change-Id: Ib8d03f0be7f71fe996dfb7da0cfe35165711ebb0 Signed-off-by: Yulong Pei <yulong.pei@intel.com>
2023-02-02packetforge: fix order of dst/src address of macTing Xu1-2/+2
In the defination of mac node, the order of dst and src address is reversed. Swap their order in this patch. Type: fix Signed-off-by: Ting Xu <ting.xu@intel.com> Change-Id: I039accc0a881eef12f13c75c5becf8b7df97d525
2023-02-02af_xdp: fix default xdp program unload failYulong Pei1-20/+45
Change to get ad->linux_ifindex in af_xdp_create_if() instead of in af_xdp_load_program(), previous if did not load custom XDP program, ad->linux_ifindex will be none, but bpf_xdp_detach() need it, so default xdp program will be not unloaded when delete af_xdp interface. Type: fix Signed-off-by: Yulong Pei <yulong.pei@intel.com> Change-Id: Id8a640204e8d29152f03349a0b58104b275635aa
2023-02-02policer: API policer selection by indexMaxime Peim10-279/+917
Policer API calls were only by policer name. It is now possible to select a policer by its index. Some functionalities are also added to allow updating a policer configuration and to refill its token buckets. Some dead codes are being removed, and small fixes made. Type: improvement Signed-off-by: Maxime Peim <mpeim@cisco.com> Change-Id: I4cc8fda0fc7c635a4110da3e757356b150f9b606
2023-02-02fib: keep AddressSanitizer happyBenoît Ganne1-3/+2
adj_delegate_remove() makes 'ad' invalid, invalidate it only after its use. Type: fix Change-Id: I6908d3dd2962ebd3fdf37e946cb19dae727bda09 Signed-off-by: Benoît Ganne <bganne@cisco.com>
2023-02-01memif: improve error reportingDamjan Marion5-122/+64
Type: improvement Change-Id: I12b120d988347cced3df82810e86dc2fd5cfca80 Signed-off-by: Damjan Marion <dmarion@me.com>
2023-02-01wireguard: update ESTABLISHED flagArtem Glazychev2-6/+32
We cannot confidently say that if we have received and processed the handshake_initiation message, then the connection has been established. Because we also send a response. The fact that the connection is established can only be considered if a keepalive packet was received. Type: fix Signed-off-by: Artem Glazychev <artem.glazychev@xored.com> Change-Id: I61731916071990f28cdebcd1d0e4d302fa1dee15
2023-01-31tests: refactor quic tests to use app-socket-apiDave Wallace1-25/+40
- clean up nomenclature & use f-strings where applicable Type: test Signed-off-by: Dave Wallace <dwallacelf@gmail.com> Change-Id: I561b7808cfc3fbfa463f7698732d19759d9ddcd4
2023-01-30vppinfra: keep AddressSanitizer happyBenoît Ganne1-2/+3
The vector size must be increased before setting the element so that AddressSanitizer can keep track of the accessible memory. Type: fix Change-Id: I7b13ce98ff29d98e643f399ec1ecb4681d3cec92 Signed-off-by: Benoît Ganne <bganne@cisco.com>
2023-01-30vlib: chdir to runtime_dirDamjan Marion1-0/+3
Type: improvement Change-Id: Id8ab75ef4384a1029ab7ee84048f347708307830 Signed-off-by: Damjan Marion <dmarion@me.com>
2023-01-27api: keep AddressSanitizer happyBenoît Ganne1-10/+11
Playing with vector length prevents AddressSanitizer to track accessible memory. Make sure we update the size of the vector once we received the data. Type: fix Change-Id: If7808254d46d7ab37d516e3de49e3583d07bb9ff Signed-off-by: Benoît Ganne <bganne@cisco.com>
2023-01-27api: keep AddressSanitizer happyBenoît Ganne2-6/+8
socket_tx_buffer is a vector, update its length accordingly so that AddressSanitizer can keep track of the allowed memory area. By doing so we can get rid of socket_tx_nbytes which becomes redundant with the vector length. Type: fix Change-Id: Ied7cb430b5dd40d5ed1390aa15bd5f455a0dba62 Signed-off-by: Benoît Ganne <bganne@cisco.com>
2023-01-27api: keep AddressSanitizer happyBenoît Ganne1-0/+1
Type: fix Change-Id: I793206068b8dca15b2f7f525ae1049139333c5b8 Signed-off-by: Benoît Ganne <bganne@cisco.com>
2023-01-26dns: keep AddressSanitizer happyBenoît Ganne2-16/+23
Type: fix Change-Id: I0ae4071ee317f38daa882fec17087a55afe75d1d Signed-off-by: Benoît Ganne <bganne@cisco.com>
2023-01-26dpdk: add intf tag to dev{} subinputNathan Skrzypczak2-0/+6
This patch allows to pass a tag when specifying the dpdk `dev { }` interface configuration. It allows a control plane generating a vpp.conf file to retreive the resulting mapping between dpdk interfaces & sw_if_indices in VPP without having to change the interface name exposed to the user. Type: feature Change-Id: I55907417de0083b82d4a127172816cec3459acf3 Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
2023-01-26wireguard: sending the first handshakeArtem Glazychev4-10/+46
After creating a peer, we send a handshake request. But it's not quite right to call wg_send_keepalive() directly. According to documentation, handshake initiation is sent after (REKEY_TIMEOUT + jitter) ms. Since it's the first one - we don't need to take REKEY_TIMEOUT into account, but we still have jitter. It also makes no sense to immediately send keepalives, because the connection is not created yet. Type: fix Signed-off-by: Artem Glazychev <artem.glazychev@xored.com> Change-Id: I61707e4be79be65abc3396b5f1dbd48ecbf7ba60
2023-01-25hs-test: handle error in config serializationFilip Tehlar1-0/+3
Type: test Signed-off-by: Filip Tehlar <ftehlar@cisco.com> Change-Id: If5bbf390df08acd1f67d31428b763f246dbcedf2
2023-01-25api: pcap capture api updateMaxime Peim6-5/+188
Allow enabling and disabling pcap capture via the API. A little bug is fixed along the way in vl_api_classify_pcap_set_table_t_handler. Type: improvement Signed-off-by: Maxime Peim <mpeim@cisco.com> Change-Id: I096129c82aecdc82bee5dbfb5e19c76a51d80aab
2023-01-24af_xdp: fix xdp socket create failChen Yahui1-2/+18
In libbpf code, xsk_socket__create will call xsk_link_lookup to get the xdp_sock bpf prog. But xsk_link_lookup can't get any bpf prog. This will cause Libbpf not to insert the fd into xsks_map and return ERROR. The solution to this problem is to insert fd into xsks_map ourselves instead of libbpf. Type: fix Change-Id: Ic5d279c6ddc02d67371262d6106a5b53b70e7913 Signed-off-by: Chen Yahui <goodluckwillcomesoon@gmail.com>
2023-01-23vppapigen: enable codegen for stream message typesStanislav Zaikin4-25/+51
Enable codegen for C type from 'rpc A returns B stream C' notation Type: improvement Change-Id: I05cfce71c385d414d7b177a080009628bc8c8fad Signed-off-by: Stanislav Zaikin <zstaseg@gmail.com>
2023-01-22vppinfra: fix random buffer OOB crash with ASANDmitry Valter1-1/+9
Don't truncate with vec_set_len bytes before they can be used. When built with ASAN, it these bytes are poisoned and trigger SIGSEGV when read. Type: fix Signed-off-by: Dmitry Valter <d-valter@yandex-team.ru> Change-Id: I912dbbd83822b884f214b3ddcde02e3527848592
2023-01-21vlib: make pending_interrupts valid for AddressSanitizerBenoît Ganne1-1/+1
vec_alloc_aligned() pre-allocates the vector memory but does not update its size, making ASan unhappy when trying to access it. Type: fix Change-Id: I80e753cf2458cf516d1180a24cfaca4f382339d5 Signed-off-by: Benoît Ganne <bganne@cisco.com>
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) 2016 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file
 * @brief NAT44 EI outside to inside network translation
 */

#include <vlib/vlib.h>

#include <vnet/vnet.h>
#include <vnet/ip/ip.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/udp/udp_local.h>
#include <vnet/fib/ip4_fib.h>

#include <vppinfra/hash.h>
#include <vppinfra/error.h>

#include <nat/lib/log.h>
#include <nat/lib/nat_syslog.h>
#include <nat/lib/ipfix_logging.h>
#include <nat/nat44-ei/nat44_ei_inlines.h>
#include <nat/nat44-ei/nat44_ei.h>

typedef struct
{
  u32 sw_if_index;
  u32 next_index;
  u32 session_index;
} nat44_ei_out2in_trace_t;

/* packet trace format function */
static u8 *
format_nat44_ei_out2in_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 *);
  nat44_ei_out2in_trace_t *t = va_arg (*args, nat44_ei_out2in_trace_t *);

  s =
    format (s,
	    "NAT44_OUT2IN: sw_if_index %d, next index %d, session index %d",
	    t->sw_if_index, t->next_index, t->session_index);
  return s;
}

#define foreach_nat44_ei_out2in_error                                         \
  _ (UNSUPPORTED_PROTOCOL, "unsupported protocol")                            \
  _ (OUT_OF_PORTS, "out of ports")                                            \
  _ (BAD_ICMP_TYPE, "unsupported ICMP type")                                  \
  _ (NO_TRANSLATION, "no translation")                                        \
  _ (MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded")                      \
  _ (CANNOT_CREATE_USER, "cannot create NAT user")

typedef enum
{
#define _(sym, str) NAT44_EI_OUT2IN_ERROR_##sym,
  foreach_nat44_ei_out2in_error
#undef _
    NAT44_EI_OUT2IN_N_ERROR,
} nat44_ei_out2in_error_t;

static char *nat44_ei_out2in_error_strings[] = {
#define _(sym,string) string,
  foreach_nat44_ei_out2in_error
#undef _
};

typedef enum
{
  NAT44_EI_OUT2IN_NEXT_DROP,
  NAT44_EI_OUT2IN_NEXT_LOOKUP,
  NAT44_EI_OUT2IN_NEXT_ICMP_ERROR,
  NAT44_EI_OUT2IN_N_NEXT,
} nat44_ei_out2in_next_t;

#ifndef CLIB_MARCH_VARIANT
int
nat44_o2i_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void *arg)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  nat44_ei_is_idle_session_ctx_t *ctx = arg;
  nat44_ei_session_t *s;
  u64 sess_timeout_time;
  nat44_ei_main_per_thread_data_t *tnm =
    vec_elt_at_index (nm->per_thread_data, ctx->thread_index);
  clib_bihash_kv_8_8_t s_kv;

  if (ctx->thread_index != nat_value_get_thread_index (kv))
    {
      return 0;
    }

  s = pool_elt_at_index (tnm->sessions, nat_value_get_session_index (kv));
  sess_timeout_time = s->last_heard + (f64) nat_session_get_timeout (
					&nm->timeouts, s->nat_proto, s->state);
  if (ctx->now >= sess_timeout_time)
    {
      init_nat_i2o_k (&s_kv, s);
      if (clib_bihash_add_del_8_8 (&nm->in2out, &s_kv, 0))
	nat_elog_warn (nm, "out2in key del failed");

      nat_ipfix_logging_nat44_ses_delete (
	ctx->thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32,
	nat_proto_to_ip_proto (s->nat_proto), s->in2out.port, s->out2in.port,
	s->in2out.fib_index);

      nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index,
			       &s->in2out.addr, s->in2out.port,
			       &s->out2in.addr, s->out2in.port, s->nat_proto);

      nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
		   s->ext_host_port, s->nat_proto, s->out2in.fib_index,
		   ctx->thread_index);

      if (!nat44_ei_is_session_static (s))
	nat44_ei_free_outside_address_and_port (
	  nm->addresses, ctx->thread_index, &s->out2in.addr, s->out2in.port,
	  s->nat_proto);

      nat44_ei_delete_session (nm, s, ctx->thread_index);
      return 1;
    }

  return 0;
}
#endif

/**
 * @brief Create session for static mapping.
 *
 * Create NAT session initiated by host from external network with static
 * mapping.
 *
 * @param nm     NAT main.
 * @param b0     Vlib buffer.
 * @param in2out In2out NAT44 session key.
 * @param out2in Out2in NAT44 session key.
 * @param node   Vlib node.
 *
 * @returns NAT44_EI session if successfully created otherwise 0.
 */
static inline nat44_ei_session_t *
create_session_for_static_mapping (
  nat44_ei_main_t *nm, vlib_buffer_t *b0, ip4_address_t i2o_addr, u16 i2o_port,
  u32 i2o_fib_index, ip4_address_t o2i_addr, u16 o2i_port, u32 o2i_fib_index,
  nat_protocol_t proto, vlib_node_runtime_t *node, u32 thread_index, f64 now)
{
  nat44_ei_user_t *u;
  nat44_ei_session_t *s;
  clib_bihash_kv_8_8_t kv0;
  ip4_header_t *ip0;
  udp_header_t *udp0;
  nat44_ei_is_idle_session_ctx_t ctx0;

  if (PREDICT_FALSE (nat44_ei_maximum_sessions_exceeded (nm, thread_index)))
    {
      b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED];
      nat_elog_notice (nm, "maximum sessions exceeded");
      return 0;
    }

  ip0 = vlib_buffer_get_current (b0);
  udp0 = ip4_next_header (ip0);

  u = nat44_ei_user_get_or_create (nm, &i2o_addr, i2o_fib_index, thread_index);
  if (!u)
    {
      b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_CANNOT_CREATE_USER];
      return 0;
    }

  s = nat44_ei_session_alloc_or_recycle (nm, u, thread_index, now);
  if (!s)
    {
      nat44_ei_delete_user_with_no_session (nm, u, thread_index);
      nat_elog_warn (nm, "create NAT session failed");
      return 0;
    }

  s->flags |= NAT44_EI_SESSION_FLAG_STATIC_MAPPING;
  s->ext_host_addr.as_u32 = ip0->src_address.as_u32;
  s->ext_host_port = udp0->src_port;
  nat44_ei_user_session_increment (nm, u, 1 /* static */);
  s->in2out.addr = i2o_addr;
  s->in2out.port = i2o_port;
  s->in2out.fib_index = i2o_fib_index;
  s->out2in.addr = o2i_addr;
  s->out2in.port = o2i_port;
  s->out2in.fib_index = o2i_fib_index;
  s->nat_proto = proto;

  /* Add to translation hashes */
  ctx0.now = now;
  ctx0.thread_index = thread_index;
  init_nat_i2o_kv (&kv0, s, thread_index,
		   s - nm->per_thread_data[thread_index].sessions);
  if (clib_bihash_add_or_overwrite_stale_8_8 (
	&nm->in2out, &kv0, nat44_i2o_is_idle_session_cb, &ctx0))
    nat_elog_notice (nm, "in2out key add failed");

  init_nat_o2i_kv (&kv0, s, thread_index,
		   s - nm->per_thread_data[thread_index].sessions);
  if (clib_bihash_add_or_overwrite_stale_8_8 (
	&nm->out2in, &kv0, nat44_o2i_is_idle_session_cb, &ctx0))
    nat_elog_notice (nm, "out2in key add failed");

  /* log NAT event */
  nat_ipfix_logging_nat44_ses_create (
    thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32,
    nat_proto_to_ip_proto (s->nat_proto), s->in2out.port, s->out2in.port,
    s->in2out.fib_index);

  nat_syslog_nat44_apmadd (s->user_index, s->in2out.fib_index,
			   &s->in2out.addr, s->in2out.port, &s->out2in.addr,
			   s->out2in.port, s->nat_proto);

  nat_ha_sadd (&s->in2out.addr, s->in2out.port, &s->out2in.addr,
	       s->out2in.port, &s->ext_host_addr, s->ext_host_port,
	       &s->ext_host_nat_addr, s->ext_host_nat_port,
	       s->nat_proto, s->in2out.fib_index, s->flags, thread_index, 0);

  return s;
}

#ifndef CLIB_MARCH_VARIANT
static_always_inline nat44_ei_out2in_error_t
icmp_get_key (vlib_buffer_t *b, ip4_header_t *ip0, ip4_address_t *addr,
	      u16 *port, nat_protocol_t *nat_proto)
{
  icmp46_header_t *icmp0;
  icmp_echo_header_t *echo0, *inner_echo0 = 0;
  ip4_header_t *inner_ip0;
  void *l4_header = 0;
  icmp46_header_t *inner_icmp0;

  icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
  echo0 = (icmp_echo_header_t *) (icmp0 + 1);

  if (!icmp_type_is_error_message
      (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))
    {
      *nat_proto = NAT_PROTOCOL_ICMP;
      *addr = ip0->dst_address;
      *port = vnet_buffer (b)->ip.reass.l4_src_port;
    }
  else
    {
      inner_ip0 = (ip4_header_t *) (echo0 + 1);
      l4_header = ip4_next_header (inner_ip0);
      *nat_proto = ip_proto_to_nat_proto (inner_ip0->protocol);
      *addr = inner_ip0->src_address;
      switch (*nat_proto)
	{
	case NAT_PROTOCOL_ICMP:
	  inner_icmp0 = (icmp46_header_t *) l4_header;
	  inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1);
	  *port = inner_echo0->identifier;
	  break;
	case NAT_PROTOCOL_UDP:
	case NAT_PROTOCOL_TCP:
	  *port = ((tcp_udp_header_t *) l4_header)->src_port;
	  break;
	default:
	  return NAT44_EI_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL;
	}
    }
  return -1;			/* success */
}

/**
 * Get address and port values to be used for ICMP packet translation
 * and create session if needed
 *
 * @param[in,out] nm             NAT main
 * @param[in,out] node           NAT node runtime
 * @param[in] thread_index       thread index
 * @param[in,out] b0             buffer containing packet to be translated
 * @param[in,out] ip0            ip header
 * @param[out] p_proto           protocol used for matching
 * @param[out] p_value           address and port after NAT translation
 * @param[out] p_dont_translate  if packet should not be translated
 * @param d                      optional parameter
 * @param e                      optional parameter
 */
u32
nat44_ei_icmp_match_out2in_slow (vlib_node_runtime_t *node, u32 thread_index,
				 vlib_buffer_t *b0, ip4_header_t *ip0,
				 ip4_address_t *addr, u16 *port,
				 u32 *fib_index, nat_protocol_t *proto,
				 nat44_ei_session_t **p_s0, u8 *dont_translate)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index];
  u32 sw_if_index0;
  nat44_ei_session_t *s0 = 0;
  clib_bihash_kv_8_8_t kv0, value0;
  u8 is_addr_only;
  u32 next0 = ~0;
  int err;
  u8 identity_nat;
  vlib_main_t *vm = vlib_get_main ();
  *dont_translate = 0;

  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
  *fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);

  *proto = 0;

  err = icmp_get_key (b0, ip0, addr, port, proto);
  if (err != -1)
    {
      b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL];
      next0 = NAT44_EI_OUT2IN_NEXT_DROP;
      goto out;
    }

  ip4_address_t mapping_addr;
  u16 mapping_port;
  u32 mapping_fib_index;

  init_nat_k (&kv0, *addr, *port, *fib_index, *proto);
  if (clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0))
    {
      /* Try to match static mapping by external address and port,
         destination address and port in packet */
      if (nat44_ei_static_mapping_match (
	    *addr, *port, *fib_index, *proto, &mapping_addr, &mapping_port,
	    &mapping_fib_index, 1, &is_addr_only, &identity_nat))
	{
	  if (!nm->forwarding_enabled)
	    {
	      /* Don't NAT packet aimed at the intfc address */
	      if (PREDICT_FALSE (nat44_ei_is_interface_addr (
		    nm->ip4_main, node, sw_if_index0,
		    ip0->dst_address.as_u32)))
		{
		  *dont_translate = 1;
		  goto out;
		}
	      b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_NO_TRANSLATION];
	      next0 = NAT44_EI_OUT2IN_NEXT_DROP;
	      goto out;
	    }
	  else
	    {
	      *dont_translate = 1;
	      goto out;
	    }
	}

      if (PREDICT_FALSE
	  (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags !=
	   ICMP4_echo_reply
	   && (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags !=
	       ICMP4_echo_request || !is_addr_only)))
	{
	  b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_BAD_ICMP_TYPE];
	  next0 = NAT44_EI_OUT2IN_NEXT_DROP;
	  goto out;
	}

      if (PREDICT_FALSE (identity_nat))
	{
	  *dont_translate = 1;
	  goto out;
	}
      /* Create session initiated by host from external network */
      s0 = create_session_for_static_mapping (
	nm, b0, mapping_addr, mapping_port, mapping_fib_index, *addr, *port,
	*fib_index, *proto, node, thread_index, vlib_time_now (vm));

      if (!s0)
	{
	  next0 = NAT44_EI_OUT2IN_NEXT_DROP;
	  goto out;
	}
    }
  else
    {
      if (PREDICT_FALSE
	  (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags !=
	   ICMP4_echo_reply
	   && vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags !=
	   ICMP4_echo_request
	   && !icmp_type_is_error_message (vnet_buffer (b0)->ip.
					   reass.icmp_type_or_tcp_flags)))
	{
	  b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_BAD_ICMP_TYPE];
	  next0 = NAT44_EI_OUT2IN_NEXT_DROP;
	  goto out;
	}

      s0 = pool_elt_at_index (tnm->sessions,
			      nat_value_get_session_index (&value0));
    }

out:
  if (s0)
    {
      *addr = s0->in2out.addr;
      *port = s0->in2out.port;
      *fib_index = s0->in2out.fib_index;
    }
  if (p_s0)
    *p_s0 = s0;
  return next0;
}
#endif

#ifndef CLIB_MARCH_VARIANT
u32
nat44_ei_icmp_match_out2in_fast (vlib_node_runtime_t *node, u32 thread_index,
				 vlib_buffer_t *b0, ip4_header_t *ip0,
				 ip4_address_t *mapping_addr,
				 u16 *mapping_port, u32 *mapping_fib_index,
				 nat_protocol_t *proto,
				 nat44_ei_session_t **p_s0, u8 *dont_translate)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  u32 sw_if_index0;
  u32 rx_fib_index0;
  u8 is_addr_only;
  u32 next0 = ~0;
  int err;
  ip4_address_t addr;
  u16 port;
  *dont_translate = 0;

  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
  rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);

  err = icmp_get_key (b0, ip0, &addr, &port, proto);
  if (err != -1)
    {
      b0->error = node->errors[err];
      next0 = NAT44_EI_OUT2IN_NEXT_DROP;
      goto out;
    }
  if (nat44_ei_static_mapping_match (addr, port, rx_fib_index0, *proto,
				     mapping_addr, mapping_port,
				     mapping_fib_index, 1, &is_addr_only, 0))
    {
      /* Don't NAT packet aimed at the intfc address */
      if (nat44_ei_is_interface_addr (nm->ip4_main, node, sw_if_index0,
				      ip0->dst_address.as_u32))
	{
	  *dont_translate = 1;
	  goto out;
	}
      b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_NO_TRANSLATION];
      next0 = NAT44_EI_OUT2IN_NEXT_DROP;
      goto out;
    }

  if (PREDICT_FALSE
      (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != ICMP4_echo_reply
       && (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags !=
	   ICMP4_echo_request || !is_addr_only)
       && !icmp_type_is_error_message (vnet_buffer (b0)->ip.
				       reass.icmp_type_or_tcp_flags)))
    {
      b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_BAD_ICMP_TYPE];
      next0 = NAT44_EI_OUT2IN_NEXT_DROP;
      goto out;
    }

out:
  return next0;
}
#endif

u32 nat44_ei_icmp_out2in (vlib_buffer_t *b0, ip4_header_t *ip0,
			  icmp46_header_t *icmp0, u32 sw_if_index0,
			  u32 rx_fib_index0, vlib_node_runtime_t *node,
			  u32 next0, u32 thread_index,
			  nat44_ei_session_t **p_s0);

#ifndef CLIB_MARCH_VARIANT
u32
nat44_ei_icmp_out2in (vlib_buffer_t *b0, ip4_header_t *ip0,
		      icmp46_header_t *icmp0, u32 sw_if_index0,
		      u32 rx_fib_index0, vlib_node_runtime_t *node, u32 next0,
		      u32 thread_index, nat44_ei_session_t **p_s0)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  icmp_echo_header_t *echo0, *inner_echo0 = 0;
  ip4_header_t *inner_ip0 = 0;
  void *l4_header = 0;
  icmp46_header_t *inner_icmp0;
  u8 dont_translate;
  u32 new_addr0, old_addr0;
  u16 old_id0, new_id0;
  ip_csum_t sum0;
  u16 checksum0;
  u32 next0_tmp;
  vlib_main_t *vm = vlib_get_main ();
  ip4_address_t addr;
  u16 port;
  u32 fib_index;
  nat_protocol_t proto;

  echo0 = (icmp_echo_header_t *) (icmp0 + 1);

  if (PREDICT_TRUE (nm->pat))
    {
      next0_tmp = nat44_ei_icmp_match_out2in_slow (
	node, thread_index, b0, ip0, &addr, &port, &fib_index, &proto, p_s0,
	&dont_translate);
    }
  else
    {
      next0_tmp = nat44_ei_icmp_match_out2in_fast (
	node, thread_index, b0, ip0, &addr, &port, &fib_index, &proto, p_s0,
	&dont_translate);
    }

  if (next0_tmp != ~0)
    next0 = next0_tmp;
  if (next0 == NAT44_EI_OUT2IN_NEXT_DROP || dont_translate)
    goto out;

  if (PREDICT_TRUE (!ip4_is_fragment (ip0)))
    {
      sum0 =
	ip_incremental_checksum_buffer (vm, b0,
					(u8 *) icmp0 -
					(u8 *) vlib_buffer_get_current (b0),
					ntohs (ip0->length) -
					ip4_header_bytes (ip0), 0);
      checksum0 = ~ip_csum_fold (sum0);
      if (checksum0 != 0 && checksum0 != 0xffff)
	{
	  next0 = NAT44_EI_OUT2IN_NEXT_DROP;
	  goto out;
	}
    }

  old_addr0 = ip0->dst_address.as_u32;
  new_addr0 = ip0->dst_address.as_u32 = addr.as_u32;
  vnet_buffer (b0)->sw_if_index[VLIB_TX] = fib_index;

  sum0 = ip0->checksum;
  sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
			 dst_address /* changed member */ );
  ip0->checksum = ip_csum_fold (sum0);


  if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment)
    {
      if (icmp0->checksum == 0)
	icmp0->checksum = 0xffff;

      if (!icmp_type_is_error_message (icmp0->type))
	{
	  new_id0 = port;
	  if (PREDICT_FALSE (new_id0 != echo0->identifier))
	    {
	      old_id0 = echo0->identifier;
	      new_id0 = port;
	      echo0->identifier = new_id0;

	      sum0 = icmp0->checksum;
	      sum0 =
		ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
				identifier /* changed member */ );
	      icmp0->checksum = ip_csum_fold (sum0);
	    }
	}
      else
	{
	  inner_ip0 = (ip4_header_t *) (echo0 + 1);
	  l4_header = ip4_next_header (inner_ip0);

	  if (!ip4_header_checksum_is_valid (inner_ip0))
	    {
	      next0 = NAT44_EI_OUT2IN_NEXT_DROP;
	      goto out;
	    }

	  old_addr0 = inner_ip0->src_address.as_u32;
	  inner_ip0->src_address = addr;
	  new_addr0 = inner_ip0->src_address.as_u32;

	  sum0 = icmp0->checksum;
	  sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
				 src_address /* changed member */ );
	  icmp0->checksum = ip_csum_fold (sum0);

	  switch (proto)
	    {
	    case NAT_PROTOCOL_ICMP:
	      inner_icmp0 = (icmp46_header_t *) l4_header;
	      inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1);

	      old_id0 = inner_echo0->identifier;
	      new_id0 = port;
	      inner_echo0->identifier = new_id0;

	      sum0 = icmp0->checksum;
	      sum0 =
		ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
				identifier);
	      icmp0->checksum = ip_csum_fold (sum0);
	      break;
	    case NAT_PROTOCOL_UDP:
	    case NAT_PROTOCOL_TCP:
	      old_id0 = ((tcp_udp_header_t *) l4_header)->src_port;
	      new_id0 = port;
	      ((tcp_udp_header_t *) l4_header)->src_port = new_id0;

	      sum0 = icmp0->checksum;
	      sum0 = ip_csum_update (sum0, old_id0, new_id0, tcp_udp_header_t,
				     src_port);
	      icmp0->checksum = ip_csum_fold (sum0);
	      break;
	    default:
	      ASSERT (0);
	    }
	}
    }

out:
  return next0;
}
#endif

static inline u32
nat44_ei_icmp_out2in_slow_path (nat44_ei_main_t *nm, vlib_buffer_t *b0,
				ip4_header_t *ip0, icmp46_header_t *icmp0,
				u32 sw_if_index0, u32 rx_fib_index0,
				vlib_node_runtime_t *node, u32 next0, f64 now,
				u32 thread_index, nat44_ei_session_t **p_s0)
{
  vlib_main_t *vm = vlib_get_main ();

  next0 = nat44_ei_icmp_out2in (b0, ip0, icmp0, sw_if_index0, rx_fib_index0,
				node, next0, thread_index, p_s0);
  nat44_ei_session_t *s0 = *p_s0;
  if (PREDICT_TRUE (next0 != NAT44_EI_OUT2IN_NEXT_DROP && s0))
    {
      /* Accounting */
      nat44_ei_session_update_counters (
	s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index);
      /* Per-user LRU list maintenance */
      nat44_ei_session_update_lru (nm, s0, thread_index);
    }
  return next0;
}

static int
nat_out2in_sm_unknown_proto (nat44_ei_main_t *nm, vlib_buffer_t *b,
			     ip4_header_t *ip, u32 rx_fib_index)
{
  clib_bihash_kv_8_8_t kv, value;
  nat44_ei_static_mapping_t *m;
  u32 old_addr, new_addr;
  ip_csum_t sum;

  init_nat_k (&kv, ip->dst_address, 0, 0, 0);
  if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value))
    return 1;

  m = pool_elt_at_index (nm->static_mappings, value.value);

  old_addr = ip->dst_address.as_u32;
  new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32;
  sum = ip->checksum;
  sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
  ip->checksum = ip_csum_fold (sum);

  vnet_buffer (b)->sw_if_index[VLIB_TX] = m->fib_index;
  return 0;
}

VLIB_NODE_FN (nat44_ei_out2in_node)
(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
{
  u32 n_left_from, *from;
  nat44_ei_main_t *nm = &nat44_ei_main;
  f64 now = vlib_time_now (vm);
  u32 thread_index = vm->thread_index;
  nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index];

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

  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
  u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
  vlib_get_buffers (vm, from, b, n_left_from);

  while (n_left_from >= 2)
    {
      vlib_buffer_t *b0, *b1;
      u32 next0 = NAT44_EI_OUT2IN_NEXT_LOOKUP;
      u32 next1 = NAT44_EI_OUT2IN_NEXT_LOOKUP;
      u32 sw_if_index0, sw_if_index1;
      ip4_header_t *ip0, *ip1;
      ip_csum_t sum0, sum1;
      u32 new_addr0, old_addr0;
      u16 new_port0, old_port0;
      u32 new_addr1, old_addr1;
      u16 new_port1, old_port1;
      udp_header_t *udp0, *udp1;
      tcp_header_t *tcp0, *tcp1;
      icmp46_header_t *icmp0, *icmp1;
      u32 rx_fib_index0, rx_fib_index1;
      u32 proto0, proto1;
      nat44_ei_session_t *s0 = 0, *s1 = 0;
      clib_bihash_kv_8_8_t kv0, kv1, value0, value1;
      u8 identity_nat0, identity_nat1;
      ip4_address_t sm_addr0, sm_addr1;
      u16 sm_port0, sm_port1;
      u32 sm_fib_index0, sm_fib_index1;

      b0 = *b;
      b++;
      b1 = *b;
      b++;

      /* Prefetch next iteration. */
      if (PREDICT_TRUE (n_left_from >= 4))
	{
	  vlib_buffer_t *p2, *p3;

	  p2 = *b;
	  p3 = *(b + 1);

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

	  clib_prefetch_load (p2->data);
	  clib_prefetch_load (p3->data);
	}

      vnet_buffer (b0)->snat.flags = 0;
      vnet_buffer (b1)->snat.flags = 0;

      ip0 = vlib_buffer_get_current (b0);
      udp0 = ip4_next_header (ip0);
      tcp0 = (tcp_header_t *) udp0;
      icmp0 = (icmp46_header_t *) udp0;

      sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
      rx_fib_index0 =
	vec_elt (nm->ip4_main->fib_index_by_sw_if_index, sw_if_index0);

      if (PREDICT_FALSE (ip0->ttl == 1))
	{
	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
	  icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
				       ICMP4_time_exceeded_ttl_exceeded_in_transit,
				       0);
	  next0 = NAT44_EI_OUT2IN_NEXT_ICMP_ERROR;
	  goto trace0;
	}

      proto0 = ip_proto_to_nat_proto (ip0->protocol);

      if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER))
	{
	  if (nat_out2in_sm_unknown_proto (nm, b0, ip0, rx_fib_index0))
	    {
	      if (!nm->forwarding_enabled)
		{
		  b0->error =
		    node->errors[NAT44_EI_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL];
		  next0 = NAT44_EI_OUT2IN_NEXT_DROP;
		}
	    }
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.other,
					 thread_index, sw_if_index0, 1);

	  goto trace0;
	}

      if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
	{
	  next0 = nat44_ei_icmp_out2in_slow_path (
	    nm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, next0, now,
	    thread_index, &s0);
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.icmp,
					 thread_index, sw_if_index0, 1);
	  goto trace0;
	}

      init_nat_k (&kv0, ip0->dst_address,
		  vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0,
		  proto0);
      if (clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0))
	{
	  /* Try to match static mapping by external address and port,
	     destination address and port in packet */
	  if (nat44_ei_static_mapping_match (
		ip0->dst_address, vnet_buffer (b0)->ip.reass.l4_dst_port,
		rx_fib_index0, proto0, &sm_addr0, &sm_port0, &sm_fib_index0, 1,
		0, &identity_nat0))
	    {
	      /*
	       * Send DHCP packets to the ipv4 stack, or we won't
	       * be able to use dhcp client on the outside interface
	       */
	      if (PREDICT_FALSE
		  (proto0 == NAT_PROTOCOL_UDP
		   && (vnet_buffer (b0)->ip.reass.l4_dst_port ==
		       clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client))))
		{
		  vnet_feature_next (&next0, b0);
		  goto trace0;
		}

	      if (!nm->forwarding_enabled)
		{
		  b0->error =
		    node->errors[NAT44_EI_OUT2IN_ERROR_NO_TRANSLATION];
		  next0 = NAT44_EI_OUT2IN_NEXT_DROP;
		}
	      goto trace0;
	    }

	  if (PREDICT_FALSE (identity_nat0))
	    goto trace0;

	  /* Create session initiated by host from external network */
	  s0 = create_session_for_static_mapping (
	    nm, b0, sm_addr0, sm_port0, sm_fib_index0, ip0->dst_address,
	    vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, proto0,
	    node, thread_index, now);
	  if (!s0)
	    {
	      next0 = NAT44_EI_OUT2IN_NEXT_DROP;
	      goto trace0;
	    }
	}
      else
	s0 = pool_elt_at_index (tnm->sessions,
				nat_value_get_session_index (&value0));

      old_addr0 = ip0->dst_address.as_u32;
      ip0->dst_address = s0->in2out.addr;
      new_addr0 = ip0->dst_address.as_u32;
      vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;

      sum0 = ip0->checksum;
      sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
			     ip4_header_t, dst_address /* changed member */ );
      ip0->checksum = ip_csum_fold (sum0);

      if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
	{
	  if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment)
	    {
	      old_port0 = vnet_buffer (b0)->ip.reass.l4_dst_port;
	      new_port0 = udp0->dst_port = s0->in2out.port;
	      sum0 = tcp0->checksum;
	      sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
				     ip4_header_t,
				     dst_address /* changed member */ );

	      sum0 = ip_csum_update (sum0, old_port0, new_port0,
				     ip4_header_t /* cheat */ ,
				     length /* changed member */ );
	      tcp0->checksum = ip_csum_fold (sum0);
	    }
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.tcp,
					 thread_index, sw_if_index0, 1);
	}
      else
	{
	  if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment)
	    {
	      old_port0 = vnet_buffer (b0)->ip.reass.l4_dst_port;
	      new_port0 = udp0->dst_port = s0->in2out.port;
	      if (PREDICT_FALSE (udp0->checksum))
		{
		  sum0 = udp0->checksum;
		  sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, dst_address	/* changed member */
		    );
		  sum0 =
		    ip_csum_update (sum0, old_port0, new_port0,
				    ip4_header_t /* cheat */ ,
				    length /* changed member */ );
		  udp0->checksum = ip_csum_fold (sum0);
		}
	    }
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.udp,
					 thread_index, sw_if_index0, 1);
	}

      /* Accounting */
      nat44_ei_session_update_counters (
	s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index);
      /* Per-user LRU list maintenance */
      nat44_ei_session_update_lru (nm, s0, thread_index);
    trace0:

      if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
			 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
	{
	  nat44_ei_out2in_trace_t *t =
	    vlib_add_trace (vm, node, b0, sizeof (*t));
	  t->sw_if_index = sw_if_index0;
	  t->next_index = next0;
	  t->session_index = ~0;
	  if (s0)
	    t->session_index = s0 - nm->per_thread_data[thread_index].sessions;
	}

      if (next0 == NAT44_EI_OUT2IN_NEXT_DROP)
	{
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.drops,
					 thread_index, sw_if_index0, 1);
	}


      ip1 = vlib_buffer_get_current (b1);
      udp1 = ip4_next_header (ip1);
      tcp1 = (tcp_header_t *) udp1;
      icmp1 = (icmp46_header_t *) udp1;

      sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
      rx_fib_index1 =
	vec_elt (nm->ip4_main->fib_index_by_sw_if_index, sw_if_index1);

      if (PREDICT_FALSE (ip1->ttl == 1))
	{
	  vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
	  icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded,
				       ICMP4_time_exceeded_ttl_exceeded_in_transit,
				       0);
	  next1 = NAT44_EI_OUT2IN_NEXT_ICMP_ERROR;
	  goto trace1;
	}

      proto1 = ip_proto_to_nat_proto (ip1->protocol);

      if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_OTHER))
	{
	  if (nat_out2in_sm_unknown_proto (nm, b1, ip1, rx_fib_index1))
	    {
	      if (!nm->forwarding_enabled)
		{
		  b1->error =
		    node->errors[NAT44_EI_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL];
		  next1 = NAT44_EI_OUT2IN_NEXT_DROP;
		}
	    }
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.other,
					 thread_index, sw_if_index1, 1);
	  goto trace1;
	}

      if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_ICMP))
	{
	  next1 = nat44_ei_icmp_out2in_slow_path (
	    nm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, next1, now,
	    thread_index, &s1);
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.icmp,
					 thread_index, sw_if_index1, 1);
	  goto trace1;
	}

      init_nat_k (&kv1, ip1->dst_address,
		  vnet_buffer (b1)->ip.reass.l4_dst_port, rx_fib_index1,
		  proto1);
      if (clib_bihash_search_8_8 (&nm->out2in, &kv1, &value1))
	{
	  /* Try to match static mapping by external address and port,
	     destination address and port in packet */
	  if (nat44_ei_static_mapping_match (
		ip1->dst_address, vnet_buffer (b1)->ip.reass.l4_dst_port,
		rx_fib_index1, proto1, &sm_addr1, &sm_port1, &sm_fib_index1, 1,
		0, &identity_nat1))
	    {
	      /*
	       * Send DHCP packets to the ipv4 stack, or we won't
	       * be able to use dhcp client on the outside interface
	       */
	      if (PREDICT_FALSE
		  (proto1 == NAT_PROTOCOL_UDP
		   && (vnet_buffer (b1)->ip.reass.l4_dst_port ==
		       clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client))))
		{
		  vnet_feature_next (&next1, b1);
		  goto trace1;
		}

	      if (!nm->forwarding_enabled)
		{
		  b1->error =
		    node->errors[NAT44_EI_OUT2IN_ERROR_NO_TRANSLATION];
		  next1 = NAT44_EI_OUT2IN_NEXT_DROP;
		}
	      goto trace1;
	    }

	  if (PREDICT_FALSE (identity_nat1))
	    goto trace1;

	  /* Create session initiated by host from external network */
	  s1 = create_session_for_static_mapping (
	    nm, b1, sm_addr1, sm_port1, sm_fib_index1, ip1->dst_address,
	    vnet_buffer (b1)->ip.reass.l4_dst_port, rx_fib_index1, proto1,
	    node, thread_index, now);
	  if (!s1)
	    {
	      next1 = NAT44_EI_OUT2IN_NEXT_DROP;
	      goto trace1;
	    }
	}
      else
	s1 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions,
				nat_value_get_session_index (&value1));

      old_addr1 = ip1->dst_address.as_u32;
      ip1->dst_address = s1->in2out.addr;
      new_addr1 = ip1->dst_address.as_u32;
      vnet_buffer (b1)->sw_if_index[VLIB_TX] = s1->in2out.fib_index;

      sum1 = ip1->checksum;
      sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
			     ip4_header_t, dst_address /* changed member */ );
      ip1->checksum = ip_csum_fold (sum1);

      if (PREDICT_TRUE (proto1 == NAT_PROTOCOL_TCP))
	{
	  if (!vnet_buffer (b1)->ip.reass.is_non_first_fragment)
	    {
	      old_port1 = vnet_buffer (b1)->ip.reass.l4_dst_port;
	      new_port1 = udp1->dst_port = s1->in2out.port;

	      sum1 = tcp1->checksum;
	      sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
				     ip4_header_t,
				     dst_address /* changed member */ );

	      sum1 = ip_csum_update (sum1, old_port1, new_port1,
				     ip4_header_t /* cheat */ ,
				     length /* changed member */ );
	      tcp1->checksum = ip_csum_fold (sum1);
	    }
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.tcp,
					 thread_index, sw_if_index1, 1);
	}
      else
	{
	  if (!vnet_buffer (b1)->ip.reass.is_non_first_fragment)
	    {
	      old_port1 = vnet_buffer (b1)->ip.reass.l4_dst_port;
	      new_port1 = udp1->dst_port = s1->in2out.port;
	      if (PREDICT_FALSE (udp1->checksum))
		{

		  sum1 = udp1->checksum;
		  sum1 =
		    ip_csum_update (sum1, old_addr1, new_addr1,
				    ip4_header_t,
				    dst_address /* changed member */ );
		  sum1 =
		    ip_csum_update (sum1, old_port1, new_port1,
				    ip4_header_t /* cheat */ ,
				    length /* changed member */ );
		  udp1->checksum = ip_csum_fold (sum1);
		}
	    }
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.udp,
					 thread_index, sw_if_index1, 1);
	}

      /* Accounting */
      nat44_ei_session_update_counters (
	s1, now, vlib_buffer_length_in_chain (vm, b1), thread_index);
      /* Per-user LRU list maintenance */
      nat44_ei_session_update_lru (nm, s1, thread_index);
    trace1:

      if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
			 && (b1->flags & VLIB_BUFFER_IS_TRACED)))
	{
	  nat44_ei_out2in_trace_t *t =
	    vlib_add_trace (vm, node, b1, sizeof (*t));
	  t->sw_if_index = sw_if_index1;
	  t->next_index = next1;
	  t->session_index = ~0;
	  if (s1)
	    t->session_index = s1 - nm->per_thread_data[thread_index].sessions;
	}

      if (next1 == NAT44_EI_OUT2IN_NEXT_DROP)
	{
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.drops,
					 thread_index, sw_if_index1, 1);
	}

      n_left_from -= 2;
      next[0] = next0;
      next[1] = next1;
      next += 2;
    }

  while (n_left_from > 0)
    {
      vlib_buffer_t *b0;
      u32 next0 = NAT44_EI_OUT2IN_NEXT_LOOKUP;
      u32 sw_if_index0;
      ip4_header_t *ip0;
      ip_csum_t sum0;
      u32 new_addr0, old_addr0;
      u16 new_port0, old_port0;
      udp_header_t *udp0;
      tcp_header_t *tcp0;
      icmp46_header_t *icmp0;
      u32 rx_fib_index0;
      u32 proto0;
      nat44_ei_session_t *s0 = 0;
      clib_bihash_kv_8_8_t kv0, value0;
      u8 identity_nat0;
      ip4_address_t sm_addr0;
      u16 sm_port0;
      u32 sm_fib_index0;

      b0 = *b;
      ++b;

      vnet_buffer (b0)->snat.flags = 0;

      ip0 = vlib_buffer_get_current (b0);
      udp0 = ip4_next_header (ip0);
      tcp0 = (tcp_header_t *) udp0;
      icmp0 = (icmp46_header_t *) udp0;

      sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
      rx_fib_index0 =
	vec_elt (nm->ip4_main->fib_index_by_sw_if_index, sw_if_index0);

      proto0 = ip_proto_to_nat_proto (ip0->protocol);

      if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER))
	{
	  if (nat_out2in_sm_unknown_proto (nm, b0, ip0, rx_fib_index0))
	    {
	      if (!nm->forwarding_enabled)
		{
		  b0->error =
		    node->errors[NAT44_EI_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL];
		  next0 = NAT44_EI_OUT2IN_NEXT_DROP;
		}
	    }
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.other,
					 thread_index, sw_if_index0, 1);
	  goto trace00;
	}

      if (PREDICT_FALSE (ip0->ttl == 1))
	{
	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
	  icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
				       ICMP4_time_exceeded_ttl_exceeded_in_transit,
				       0);
	  next0 = NAT44_EI_OUT2IN_NEXT_ICMP_ERROR;
	  goto trace00;
	}

      if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
	{
	  next0 = nat44_ei_icmp_out2in_slow_path (
	    nm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, next0, now,
	    thread_index, &s0);
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.icmp,
					 thread_index, sw_if_index0, 1);
	  goto trace00;
	}

      init_nat_k (&kv0, ip0->dst_address,
		  vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0,
		  proto0);

      if (clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0))
	{
	  /* Try to match static mapping by external address and port,
	     destination address and port in packet */
	  if (nat44_ei_static_mapping_match (
		ip0->dst_address, vnet_buffer (b0)->ip.reass.l4_dst_port,
		rx_fib_index0, proto0, &sm_addr0, &sm_port0, &sm_fib_index0, 1,
		0, &identity_nat0))
	    {
	      /*
	       * Send DHCP packets to the ipv4 stack, or we won't
	       * be able to use dhcp client on the outside interface
	       */
	      if (PREDICT_FALSE
		  (proto0 == NAT_PROTOCOL_UDP
		   && (vnet_buffer (b0)->ip.reass.l4_dst_port ==
		       clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client))))
		{
		  vnet_feature_next (&next0, b0);
		  goto trace00;
		}

	      if (!nm->forwarding_enabled)
		{
		  b0->error =
		    node->errors[NAT44_EI_OUT2IN_ERROR_NO_TRANSLATION];
		  next0 = NAT44_EI_OUT2IN_NEXT_DROP;
		}
	      goto trace00;
	    }

	  if (PREDICT_FALSE (identity_nat0))
	    goto trace00;

	  /* Create session initiated by host from external network */
	  s0 = create_session_for_static_mapping (
	    nm, b0, sm_addr0, sm_port0, sm_fib_index0, ip0->dst_address,
	    vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, proto0,
	    node, thread_index, now);
	  if (!s0)
	    {
	      next0 = NAT44_EI_OUT2IN_NEXT_DROP;
	      goto trace00;
	    }
	}
      else
	s0 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions,
				nat_value_get_session_index (&value0));

      old_addr0 = ip0->dst_address.as_u32;
      ip0->dst_address = s0->in2out.addr;
      new_addr0 = ip0->dst_address.as_u32;
      vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;

      sum0 = ip0->checksum;
      sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
			     ip4_header_t, dst_address /* changed member */ );
      ip0->checksum = ip_csum_fold (sum0);

      if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
	{
	  if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment)
	    {
	      old_port0 = vnet_buffer (b0)->ip.reass.l4_dst_port;
	      new_port0 = udp0->dst_port = s0->in2out.port;

	      sum0 = tcp0->checksum;
	      sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
				     ip4_header_t,
				     dst_address /* changed member */ );

	      sum0 = ip_csum_update (sum0, old_port0, new_port0,
				     ip4_header_t /* cheat */ ,
				     length /* changed member */ );
	      tcp0->checksum = ip_csum_fold (sum0);
	    }
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.tcp,
					 thread_index, sw_if_index0, 1);
	}
      else
	{
	  if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment)
	    {
	      old_port0 = vnet_buffer (b0)->ip.reass.l4_dst_port;
	      new_port0 = udp0->dst_port = s0->in2out.port;
	      if (PREDICT_FALSE (udp0->checksum))
		{
		  sum0 = udp0->checksum;
		  sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, dst_address	/* changed member */
		    );
		  sum0 =
		    ip_csum_update (sum0, old_port0, new_port0,
				    ip4_header_t /* cheat */ ,
				    length /* changed member */ );
		  udp0->checksum = ip_csum_fold (sum0);
		}
	    }
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.udp,
					 thread_index, sw_if_index0, 1);
	}

      /* Accounting */
      nat44_ei_session_update_counters (
	s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index);
      /* Per-user LRU list maintenance */
      nat44_ei_session_update_lru (nm, s0, thread_index);
    trace00:

      if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
			 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
	{
	  nat44_ei_out2in_trace_t *t =
	    vlib_add_trace (vm, node, b0, sizeof (*t));
	  t->sw_if_index = sw_if_index0;
	  t->next_index = next0;
	  t->session_index = ~0;
	  if (s0)
	    t->session_index = s0 - nm->per_thread_data[thread_index].sessions;
	}

      if (next0 == NAT44_EI_OUT2IN_NEXT_DROP)
	{
	  vlib_increment_simple_counter (&nm->counters.slowpath.out2in.drops,
					 thread_index, sw_if_index0, 1);
	}

      n_left_from--;
      next[0] = next0;
      next++;
    }

  vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts,
			       frame->n_vectors);

  return frame->n_vectors;
}

VLIB_REGISTER_NODE (nat44_ei_out2in_node) = {
  .name = "nat44-ei-out2in",
  .vector_size = sizeof (u32),
  .format_trace = format_nat44_ei_out2in_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = ARRAY_LEN(nat44_ei_out2in_error_strings),
  .error_strings = nat44_ei_out2in_error_strings,

  .runtime_data_bytes = sizeof (nat44_ei_runtime_t),

  .n_next_nodes = NAT44_EI_OUT2IN_N_NEXT,

  /* edit / add dispositions here */
  .next_nodes = {
    [NAT44_EI_OUT2IN_NEXT_DROP] = "error-drop",
    [NAT44_EI_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
    [NAT44_EI_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error",
  },
};

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