aboutsummaryrefslogtreecommitdiffstats
path: root/vnet/vnet/map/ip4_map.c
diff options
context:
space:
mode:
Diffstat (limited to 'vnet/vnet/map/ip4_map.c')
-rw-r--r--vnet/vnet/map/ip4_map.c591
1 files changed, 591 insertions, 0 deletions
diff --git a/vnet/vnet/map/ip4_map.c b/vnet/vnet/map/ip4_map.c
new file mode 100644
index 00000000000..cf53ef4918c
--- /dev/null
+++ b/vnet/vnet/map/ip4_map.c
@@ -0,0 +1,591 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Defines used for testing various optimisation schemes
+ */
+#define MAP_ENCAP_DUAL 0
+
+#include "map.h"
+#include "../ip/ip_frag.h"
+
+vlib_node_registration_t ip4_map_reass_node;
+
+enum ip4_map_next_e {
+ IP4_MAP_NEXT_IP6_LOOKUP,
+#ifdef MAP_SKIP_IP6_LOOKUP
+ IP4_MAP_NEXT_IP6_REWRITE,
+#endif
+ IP4_MAP_NEXT_FRAGMENT,
+ IP4_MAP_NEXT_REASS,
+ IP4_MAP_NEXT_DROP,
+ IP4_MAP_N_NEXT,
+};
+
+enum ip4_map_reass_next_t {
+ IP4_MAP_REASS_NEXT_IP6_LOOKUP,
+ IP4_MAP_REASS_NEXT_IP4_FRAGMENT,
+ IP4_MAP_REASS_NEXT_DROP,
+ IP4_MAP_REASS_N_NEXT,
+};
+
+typedef struct {
+ u32 map_domain_index;
+ u16 port;
+ u8 cached;
+} map_ip4_map_reass_trace_t;
+
+u8 *
+format_ip4_map_reass_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 *);
+ map_ip4_map_reass_trace_t *t = va_arg (*args, map_ip4_map_reass_trace_t *);
+ return format(s, "MAP domain index: %d L4 port: %u Status: %s", t->map_domain_index,
+ t->port, t->cached?"cached":"forwarded");
+}
+
+/*
+ * ip4_map_get_port
+ */
+u16
+ip4_map_get_port (ip4_header_t *ip, map_dir_e dir)
+{
+ /* Find port information */
+ if (PREDICT_TRUE((ip->protocol == IP_PROTOCOL_TCP) ||
+ (ip->protocol == IP_PROTOCOL_UDP))) {
+ udp_header_t *udp = (void *)(ip + 1);
+ return (dir == MAP_SENDER ? udp->src_port : udp->dst_port);
+ } else if (ip->protocol == IP_PROTOCOL_ICMP) {
+ /*
+ * 1) ICMP Echo request or Echo reply
+ * 2) ICMP Error with inner packet being UDP or TCP
+ * 3) ICMP Error with inner packet being ICMP Echo request or Echo reply
+ */
+ icmp46_header_t *icmp = (void *)(ip + 1);
+ if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply) {
+ return *((u16 *)(icmp + 1));
+ } else if (clib_net_to_host_u16(ip->length) >= 64) { // IP + ICMP + IP + L4 header
+ ip4_header_t *icmp_ip = (ip4_header_t *)(icmp + 2);
+ if (PREDICT_TRUE((icmp_ip->protocol == IP_PROTOCOL_TCP) ||
+ (icmp_ip->protocol == IP_PROTOCOL_UDP))) {
+ udp_header_t *udp = (void *)(icmp_ip + 1);
+ return (dir == MAP_SENDER ? udp->dst_port : udp->src_port);
+ } else if (icmp_ip->protocol == IP_PROTOCOL_ICMP) {
+ icmp46_header_t *inner_icmp = (void *)(icmp_ip + 1);
+ if (inner_icmp->type == ICMP4_echo_request || inner_icmp->type == ICMP4_echo_reply)
+ return (*((u16 *)(inner_icmp + 1)));
+ }
+ }
+ }
+ return (0);
+}
+
+static_always_inline u16
+ip4_map_port_and_security_check (map_domain_t *d, ip4_header_t *ip, u32 *next, u8 *error)
+{
+ u16 port = 0;
+
+ if (d->psid_length > 0) {
+ if (!ip4_is_fragment(ip)) {
+ if (PREDICT_FALSE((ip->ip_version_and_header_length != 0x45) || clib_host_to_net_u16(ip->length) < 28)) {
+ return 0;
+ }
+ port = ip4_map_get_port(ip, MAP_RECEIVER);
+ if (port) {
+ /* Verify that port is not among the well-known ports */
+ if ((d->psid_offset > 0) && (clib_net_to_host_u16(port) < (0x1 << (16 - d->psid_offset)))) {
+ *error = MAP_ERROR_ENCAP_SEC_CHECK;
+ } else {
+ return (port);
+ }
+ } else {
+ *error = MAP_ERROR_BAD_PROTOCOL;
+ }
+ } else {
+ *next = IP4_MAP_NEXT_REASS;
+ }
+ }
+ return (0);
+}
+
+/*
+ * ip4_map_vtcfl
+ */
+static_always_inline u32
+ip4_map_vtcfl (ip4_header_t *ip4, vlib_buffer_t *p)
+{
+ map_main_t *mm = &map_main;
+ u8 tc = mm->tc_copy ? ip4->tos : mm->tc;
+ u32 vtcfl = 0x6 << 28;
+ vtcfl |= tc << 20;
+ vtcfl |= vnet_buffer(p)->ip.flow_hash && 0x000fffff;
+
+ return (clib_host_to_net_u32(vtcfl));
+}
+
+static_always_inline bool
+ip4_map_ip6_lookup_bypass (vlib_buffer_t *p0, ip4_header_t *ip)
+{
+#ifdef MAP_SKIP_IP6_LOOKUP
+ map_main_t *mm = &map_main;
+ u32 adj_index0 = mm->adj6_index;
+ if (adj_index0 > 0) {
+ ip_lookup_main_t *lm6 = &ip6_main.lookup_main;
+ ip_adjacency_t *adj = ip_get_adjacency(lm6, mm->adj6_index);
+ if (adj->n_adj > 1) {
+ u32 hash_c0 = ip4_compute_flow_hash(ip, IP_FLOW_HASH_DEFAULT);
+ adj_index0 += (hash_c0 & (adj->n_adj - 1));
+ }
+ vnet_buffer(p0)->ip.adj_index[VLIB_TX] = adj_index0;
+ return (true);
+ }
+#endif
+ return (false);
+}
+
+/*
+ * ip4_map
+ */
+static uword
+ip4_map (vlib_main_t *vm,
+ vlib_node_runtime_t *node,
+ vlib_frame_t *frame)
+{
+ u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
+ vlib_node_runtime_t *error_node = vlib_node_get_runtime(vm, ip4_map_node.index);
+ from = vlib_frame_vector_args(frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+ map_main_t *mm = &map_main;
+ vlib_combined_counter_main_t *cm = mm->domain_counters;
+ u32 cpu_index = os_get_cpu_number();
+
+ while (n_left_from > 0) {
+ vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
+
+ /* Dual loop */
+ while (n_left_from > 4 && n_left_to_next > 2) {
+ u32 pi0, pi1;
+ vlib_buffer_t *p0, *p1;
+ map_domain_t *d0, *d1;
+ u8 error0 = MAP_ERROR_NONE, error1 = MAP_ERROR_NONE;
+ ip4_header_t *ip40, *ip41;
+ u16 port0 = 0, port1 = 0;
+ ip6_header_t *ip6h0, *ip6h1;
+ u32 map_domain_index0 = ~0, map_domain_index1 = ~0;
+ u32 next0 = IP4_MAP_NEXT_IP6_LOOKUP, next1 = IP4_MAP_NEXT_IP6_LOOKUP;
+
+ /* 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, STORE);
+ vlib_prefetch_buffer_header(p3, STORE);
+ /* IPv4 + 8 = 28. possibly plus -40 */
+ CLIB_PREFETCH (p2->data-40, 68, STORE);
+ CLIB_PREFETCH (p3->data-40, 68, STORE);
+ }
+
+ pi0 = to_next[0] = from[0];
+ pi1 = to_next[1] = from[1];
+ from += 2;
+ n_left_from -= 2;
+ to_next +=2;
+ n_left_to_next -= 2;
+
+ p0 = vlib_get_buffer(vm, pi0);
+ p1 = vlib_get_buffer(vm, pi1);
+ ip40 = vlib_buffer_get_current(p0);
+ ip41 = vlib_buffer_get_current(p1);
+ p0->current_length = clib_net_to_host_u16(ip40->length);
+ p1->current_length = clib_net_to_host_u16(ip41->length);
+ d0 = ip4_map_get_domain(vnet_buffer(p0)->ip.adj_index[VLIB_TX], &map_domain_index0);
+ d1 = ip4_map_get_domain(vnet_buffer(p1)->ip.adj_index[VLIB_TX], &map_domain_index1);
+ ASSERT(d0);
+ ASSERT(d1);
+
+ /*
+ * Shared IPv4 address
+ */
+ port0 = ip4_map_port_and_security_check(d0, ip40, &next0, &error0);
+ port1 = ip4_map_port_and_security_check(d1, ip41, &next1, &error1);
+
+ /* MAP calc */
+ u32 da40 = clib_net_to_host_u32(ip40->dst_address.as_u32);
+ u32 da41 = clib_net_to_host_u32(ip41->dst_address.as_u32);
+ u16 dp40 = clib_net_to_host_u16(port0);
+ u16 dp41 = clib_net_to_host_u16(port1);
+ u64 dal60 = map_get_pfx(d0, da40, dp40);
+ u64 dal61 = map_get_pfx(d1, da41, dp41);
+ u64 dar60 = map_get_sfx(d0, da40, dp40);
+ u64 dar61 = map_get_sfx(d1, da41, dp41);
+ if (dal60 == 0 && dar60 == 0) error0 = MAP_ERROR_UNKNOWN;
+ if (dal61 == 0 && dar61 == 0) error1 = MAP_ERROR_UNKNOWN;
+
+ /* construct ipv6 header */
+ vlib_buffer_advance(p0, - sizeof(ip6_header_t));
+ vlib_buffer_advance(p1, - sizeof(ip6_header_t));
+ ip6h0 = vlib_buffer_get_current(p0);
+ ip6h1 = vlib_buffer_get_current(p1);
+ vnet_buffer(p0)->sw_if_index[VLIB_TX] = (u32)~0;
+ vnet_buffer(p1)->sw_if_index[VLIB_TX] = (u32)~0;
+
+ ip6h0->ip_version_traffic_class_and_flow_label = ip4_map_vtcfl(ip40, p0);
+ ip6h1->ip_version_traffic_class_and_flow_label = ip4_map_vtcfl(ip41, p1);
+ ip6h0->payload_length = ip40->length;
+ ip6h1->payload_length = ip41->length;
+ ip6h0->protocol = IP_PROTOCOL_IP_IN_IP;
+ ip6h1->protocol = IP_PROTOCOL_IP_IN_IP;
+ ip6h0->hop_limit = 0x40;
+ ip6h1->hop_limit = 0x40;
+ ip6h0->src_address = d0->ip6_src;
+ ip6h1->src_address = d1->ip6_src;
+ ip6h0->dst_address.as_u64[0] = clib_host_to_net_u64(dal60);
+ ip6h0->dst_address.as_u64[1] = clib_host_to_net_u64(dar60);
+ ip6h1->dst_address.as_u64[0] = clib_host_to_net_u64(dal61);
+ ip6h1->dst_address.as_u64[1] = clib_host_to_net_u64(dar61);
+
+ /*
+ * Determine next node. Can be one of:
+ * ip6-lookup, ip6-rewrite, ip4-fragment, ip4-virtreass, error-drop
+ */
+ if (PREDICT_TRUE(error0 == MAP_ERROR_NONE)) {
+ if (PREDICT_FALSE(d0->mtu && (clib_net_to_host_u16(ip6h0->payload_length) + sizeof(*ip6h0) > d0->mtu))) {
+ vnet_buffer(p0)->ip_frag.header_offset = sizeof(*ip6h0);
+ vnet_buffer(p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
+ vnet_buffer(p0)->ip_frag.mtu = d0->mtu;
+ vnet_buffer(p0)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
+ next0 = IP4_MAP_NEXT_FRAGMENT;
+ } else {
+ next0 = ip4_map_ip6_lookup_bypass(p0, ip40) ? IP4_MAP_NEXT_IP6_REWRITE : next0;
+ vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index0, 1,
+ clib_net_to_host_u16(ip6h0->payload_length) + 40);
+ }
+ } else {
+ next0 = IP4_MAP_NEXT_DROP;
+ }
+
+ /*
+ * Determine next node. Can be one of:
+ * ip6-lookup, ip6-rewrite, ip4-fragment, ip4-virtreass, error-drop
+ */
+ if (PREDICT_TRUE(error1 == MAP_ERROR_NONE)) {
+ if (PREDICT_FALSE(d1->mtu && (clib_net_to_host_u16(ip6h1->payload_length) + sizeof(*ip6h1) > d1->mtu))) {
+ vnet_buffer(p1)->ip_frag.header_offset = sizeof(*ip6h1);
+ vnet_buffer(p1)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
+ vnet_buffer(p1)->ip_frag.mtu = d1->mtu;
+ vnet_buffer(p1)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
+ next1 = IP4_MAP_NEXT_FRAGMENT;
+ } else {
+ next1 = ip4_map_ip6_lookup_bypass(p1, ip41) ? IP4_MAP_NEXT_IP6_REWRITE : next1;
+ vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index1, 1,
+ clib_net_to_host_u16(ip6h1->payload_length) + 40);
+ }
+ } else {
+ next1 = IP4_MAP_NEXT_DROP;
+ }
+
+ if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
+ map_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof(*tr));
+ tr->map_domain_index = map_domain_index0;
+ tr->port = port0;
+ }
+ if (PREDICT_FALSE(p1->flags & VLIB_BUFFER_IS_TRACED)) {
+ map_trace_t *tr = vlib_add_trace(vm, node, p1, sizeof(*tr));
+ tr->map_domain_index = map_domain_index1;
+ tr->port = port1;
+ }
+
+ p0->error = error_node->errors[error0];
+ p1->error = error_node->errors[error1];
+
+ vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next, n_left_to_next, pi0, pi1, next0, next1);
+ }
+
+ while (n_left_from > 0 && n_left_to_next > 0) {
+ u32 pi0;
+ vlib_buffer_t *p0;
+ map_domain_t *d0;
+ u8 error0 = MAP_ERROR_NONE;
+ ip4_header_t *ip40;
+ u16 port0 = 0;
+ ip6_header_t *ip6h0;
+ u32 next0 = IP4_MAP_NEXT_IP6_LOOKUP;
+ u32 map_domain_index0 = ~0;
+
+ pi0 = to_next[0] = from[0];
+ from += 1;
+ n_left_from -= 1;
+ to_next +=1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer(vm, pi0);
+ ip40 = vlib_buffer_get_current(p0);
+ p0->current_length = clib_net_to_host_u16(ip40->length);
+ d0 = ip4_map_get_domain(vnet_buffer(p0)->ip.adj_index[VLIB_TX], &map_domain_index0);
+ ASSERT(d0);
+
+ /*
+ * Shared IPv4 address
+ */
+ port0 = ip4_map_port_and_security_check(d0, ip40, &next0, &error0);
+
+ /* MAP calc */
+ u32 da40 = clib_net_to_host_u32(ip40->dst_address.as_u32);
+ u16 dp40 = clib_net_to_host_u16(port0);
+ u64 dal60 = map_get_pfx(d0, da40, dp40);
+ u64 dar60 = map_get_sfx(d0, da40, dp40);
+ if (dal60 == 0 && dar60 == 0 && error0 == MAP_ERROR_NONE) error0 = MAP_ERROR_UNKNOWN;
+
+ /* construct ipv6 header */
+ vlib_buffer_advance(p0, - (sizeof(ip6_header_t)));
+ ip6h0 = vlib_buffer_get_current(p0);
+ vnet_buffer(p0)->sw_if_index[VLIB_TX] = (u32)~0;
+
+ ip6h0->ip_version_traffic_class_and_flow_label = ip4_map_vtcfl(ip40, p0);
+ ip6h0->payload_length = ip40->length;
+ ip6h0->protocol = IP_PROTOCOL_IP_IN_IP;
+ ip6h0->hop_limit = 0x40;
+ ip6h0->src_address = d0->ip6_src;
+ ip6h0->dst_address.as_u64[0] = clib_host_to_net_u64(dal60);
+ ip6h0->dst_address.as_u64[1] = clib_host_to_net_u64(dar60);
+
+ /*
+ * Determine next node. Can be one of:
+ * ip6-lookup, ip6-rewrite, ip4-fragment, ip4-virtreass, error-drop
+ */
+ if (PREDICT_TRUE(error0 == MAP_ERROR_NONE)) {
+ if (PREDICT_FALSE(d0->mtu && (clib_net_to_host_u16(ip6h0->payload_length) + sizeof(*ip6h0) > d0->mtu))) {
+ vnet_buffer(p0)->ip_frag.header_offset = sizeof(*ip6h0);
+ vnet_buffer(p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
+ vnet_buffer(p0)->ip_frag.mtu = d0->mtu;
+ vnet_buffer(p0)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
+ next0 = IP4_MAP_NEXT_FRAGMENT;
+ } else {
+ next0 = ip4_map_ip6_lookup_bypass(p0, ip40) ? IP4_MAP_NEXT_IP6_REWRITE : next0;
+ vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index0, 1,
+ clib_net_to_host_u16(ip6h0->payload_length) + 40);
+ }
+ } else {
+ next0 = IP4_MAP_NEXT_DROP;
+ }
+
+ if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
+ map_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof(*tr));
+ tr->map_domain_index = map_domain_index0;
+ tr->port = port0;
+ }
+
+ p0->error = error_node->errors[error0];
+ vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
+ }
+ vlib_put_next_frame(vm, node, next_index, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+/*
+ * ip4_map_reass
+ */
+static uword
+ip4_map_reass (vlib_main_t *vm,
+ vlib_node_runtime_t *node,
+ vlib_frame_t *frame)
+{
+ u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
+ vlib_node_runtime_t *error_node = vlib_node_get_runtime(vm, ip4_map_reass_node.index);
+ from = vlib_frame_vector_args(frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+ map_main_t *mm = &map_main;
+ vlib_combined_counter_main_t *cm = mm->domain_counters;
+ u32 cpu_index = os_get_cpu_number();
+ u32 *fragments_to_drop = NULL;
+ u32 *fragments_to_loopback = NULL;
+
+ while (n_left_from > 0) {
+ vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0) {
+ u32 pi0;
+ vlib_buffer_t *p0;
+ map_domain_t *d0;
+ u8 error0 = MAP_ERROR_NONE;
+ ip4_header_t *ip40;
+ i32 port0 = 0;
+ ip6_header_t *ip60;
+ u32 next0 = IP4_MAP_REASS_NEXT_IP6_LOOKUP;
+ u32 map_domain_index0;
+ u8 cached = 0;
+
+ pi0 = to_next[0] = from[0];
+ from += 1;
+ n_left_from -= 1;
+ to_next +=1;
+ n_left_to_next -= 1;
+
+ p0 = vlib_get_buffer(vm, pi0);
+ ip60 = vlib_buffer_get_current(p0);
+ ip40 = (ip4_header_t *)(ip60 + 1);
+ d0 = ip4_map_get_domain(vnet_buffer(p0)->ip.adj_index[VLIB_TX], &map_domain_index0);
+
+ map_ip4_reass_lock();
+ map_ip4_reass_t *r = map_ip4_reass_get(ip40->src_address.as_u32, ip40->dst_address.as_u32,
+ ip40->fragment_id, ip40->protocol, &fragments_to_drop);
+ if (PREDICT_FALSE(!r)) {
+ // Could not create a caching entry
+ error0 = MAP_ERROR_FRAGMENT_MEMORY;
+ } else if (PREDICT_TRUE(ip4_get_fragment_offset(ip40))) {
+ if (r->port >= 0) {
+ // We know the port already
+ port0 = r->port;
+ } else if (map_ip4_reass_add_fragment(r, pi0)) {
+ // Not enough space for caching
+ error0 = MAP_ERROR_FRAGMENT_MEMORY;
+ map_ip4_reass_free(r, &fragments_to_drop);
+ } else {
+ cached = 1;
+ }
+ } else if ((port0 = ip4_get_port(ip40, MAP_RECEIVER, p0->current_length)) < 0) {
+ // Could not find port. We'll free the reassembly.
+ error0 = MAP_ERROR_BAD_PROTOCOL;
+ port0 = 0;
+ map_ip4_reass_free(r, &fragments_to_drop);
+ } else {
+ r->port = port0;
+ map_ip4_reass_get_fragments(r, &fragments_to_loopback);
+ }
+
+#ifdef MAP_IP4_REASS_COUNT_BYTES
+ if (!cached && r) {
+ r->forwarded += clib_host_to_net_u16(ip40->length) - 20;
+ if (!ip4_get_fragment_more(ip40))
+ r->expected_total = ip4_get_fragment_offset(ip40) * 8 + clib_host_to_net_u16(ip40->length) - 20;
+ if(r->forwarded >= r->expected_total)
+ map_ip4_reass_free(r, &fragments_to_drop);
+ }
+#endif
+
+ map_ip4_reass_unlock();
+
+ // NOTE: Most operations have already been performed by ip4_map
+ // All we need is the right destination address
+ ip60->dst_address.as_u64[0] = map_get_pfx_net(d0, ip40->dst_address.as_u32, port0);
+ ip60->dst_address.as_u64[1] = map_get_sfx_net(d0, ip40->dst_address.as_u32, port0);
+
+ if (PREDICT_FALSE(d0->mtu && (clib_net_to_host_u16(ip60->payload_length) + sizeof(*ip60) > d0->mtu))) {
+ vnet_buffer(p0)->ip_frag.header_offset = sizeof(*ip60);
+ vnet_buffer(p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
+ vnet_buffer(p0)->ip_frag.mtu = d0->mtu;
+ vnet_buffer(p0)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
+ next0 = IP4_MAP_REASS_NEXT_IP4_FRAGMENT;
+ }
+
+ if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
+ map_ip4_map_reass_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof(*tr));
+ tr->map_domain_index = map_domain_index0;
+ tr->port = port0;
+ tr->cached = cached;
+ }
+
+ if(cached) {
+ //Dequeue the packet
+ n_left_to_next++;
+ to_next--;
+ } else {
+ if (error0 == MAP_ERROR_NONE)
+ vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index0, 1,
+ clib_net_to_host_u16(ip60->payload_length) + 40);
+ next0 = (error0 == MAP_ERROR_NONE) ? next0 : IP4_MAP_REASS_NEXT_DROP;
+ p0->error = error_node->errors[error0];
+ vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
+ }
+
+ //Loopback when we reach the end of the inpu vector
+ if(n_left_from == 0 && vec_len(fragments_to_loopback)) {
+ from = vlib_frame_vector_args(frame);
+ u32 len = vec_len(fragments_to_loopback);
+ if(len <= VLIB_FRAME_SIZE) {
+ memcpy(from, fragments_to_loopback, sizeof(u32)*len);
+ n_left_from = len;
+ vec_reset_length(fragments_to_loopback);
+ } else {
+ memcpy(from, fragments_to_loopback + (len - VLIB_FRAME_SIZE), sizeof(u32)*VLIB_FRAME_SIZE);
+ n_left_from = VLIB_FRAME_SIZE;
+ _vec_len(fragments_to_loopback) = len - VLIB_FRAME_SIZE;
+ }
+ }
+ }
+ vlib_put_next_frame(vm, node, next_index, n_left_to_next);
+ }
+
+ map_send_all_to_node(vm, fragments_to_drop, node,
+ &error_node->errors[MAP_ERROR_FRAGMENT_DROPPED],
+ IP4_MAP_REASS_NEXT_DROP);
+
+ vec_free(fragments_to_drop);
+ vec_free(fragments_to_loopback);
+ return frame->n_vectors;
+}
+
+static char *map_error_strings[] = {
+#define _(sym,string) string,
+ foreach_map_error
+#undef _
+};
+
+VLIB_REGISTER_NODE(ip4_map_node) = {
+ .function = ip4_map,
+ .name = "ip4-map",
+ .vector_size = sizeof(u32),
+ .format_trace = format_map_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = MAP_N_ERROR,
+ .error_strings = map_error_strings,
+
+ .n_next_nodes = IP4_MAP_N_NEXT,
+ .next_nodes = {
+ [IP4_MAP_NEXT_IP6_LOOKUP] = "ip6-lookup",
+#ifdef MAP_SKIP_IP6_LOOKUP
+ [IP4_MAP_NEXT_IP6_REWRITE] = "ip6-rewrite",
+#endif
+ [IP4_MAP_NEXT_FRAGMENT] = "ip4-frag",
+ [IP4_MAP_NEXT_REASS] = "ip4-map-reass",
+ [IP4_MAP_NEXT_DROP] = "error-drop",
+ },
+};
+
+VLIB_REGISTER_NODE(ip4_map_reass_node) = {
+ .function = ip4_map_reass,
+ .name = "ip4-map-reass",
+ .vector_size = sizeof(u32),
+ .format_trace = format_ip4_map_reass_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = MAP_N_ERROR,
+ .error_strings = map_error_strings,
+
+ .n_next_nodes = IP4_MAP_REASS_N_NEXT,
+ .next_nodes = {
+ [IP4_MAP_REASS_NEXT_IP6_LOOKUP] = "ip6-lookup",
+ [IP4_MAP_REASS_NEXT_IP4_FRAGMENT] = "ip4-frag",
+ [IP4_MAP_REASS_NEXT_DROP] = "error-drop",
+ },
+};