blob: c8ee2764f2365c6b2fd2528ee583952525dea39b [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/*
16 * Defines used for testing various optimisation schemes
17 */
18#define MAP_ENCAP_DUAL 0
19
20#include "map.h"
21#include "../ip/ip_frag.h"
22
23vlib_node_registration_t ip4_map_reass_node;
24
25enum ip4_map_next_e {
26 IP4_MAP_NEXT_IP6_LOOKUP,
27#ifdef MAP_SKIP_IP6_LOOKUP
28 IP4_MAP_NEXT_IP6_REWRITE,
29#endif
30 IP4_MAP_NEXT_FRAGMENT,
31 IP4_MAP_NEXT_REASS,
32 IP4_MAP_NEXT_DROP,
33 IP4_MAP_N_NEXT,
34};
35
36enum ip4_map_reass_next_t {
37 IP4_MAP_REASS_NEXT_IP6_LOOKUP,
38 IP4_MAP_REASS_NEXT_IP4_FRAGMENT,
39 IP4_MAP_REASS_NEXT_DROP,
40 IP4_MAP_REASS_N_NEXT,
41};
42
43typedef struct {
44 u32 map_domain_index;
45 u16 port;
46 u8 cached;
47} map_ip4_map_reass_trace_t;
48
49u8 *
50format_ip4_map_reass_trace (u8 *s, va_list *args)
51{
52 CLIB_UNUSED(vlib_main_t *vm) = va_arg (*args, vlib_main_t *);
53 CLIB_UNUSED(vlib_node_t *node) = va_arg (*args, vlib_node_t *);
54 map_ip4_map_reass_trace_t *t = va_arg (*args, map_ip4_map_reass_trace_t *);
55 return format(s, "MAP domain index: %d L4 port: %u Status: %s", t->map_domain_index,
56 t->port, t->cached?"cached":"forwarded");
57}
58
59/*
60 * ip4_map_get_port
61 */
62u16
63ip4_map_get_port (ip4_header_t *ip, map_dir_e dir)
64{
65 /* Find port information */
66 if (PREDICT_TRUE((ip->protocol == IP_PROTOCOL_TCP) ||
67 (ip->protocol == IP_PROTOCOL_UDP))) {
68 udp_header_t *udp = (void *)(ip + 1);
69 return (dir == MAP_SENDER ? udp->src_port : udp->dst_port);
70 } else if (ip->protocol == IP_PROTOCOL_ICMP) {
71 /*
72 * 1) ICMP Echo request or Echo reply
73 * 2) ICMP Error with inner packet being UDP or TCP
74 * 3) ICMP Error with inner packet being ICMP Echo request or Echo reply
75 */
76 icmp46_header_t *icmp = (void *)(ip + 1);
77 if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply) {
78 return *((u16 *)(icmp + 1));
79 } else if (clib_net_to_host_u16(ip->length) >= 64) { // IP + ICMP + IP + L4 header
80 ip4_header_t *icmp_ip = (ip4_header_t *)(icmp + 2);
81 if (PREDICT_TRUE((icmp_ip->protocol == IP_PROTOCOL_TCP) ||
82 (icmp_ip->protocol == IP_PROTOCOL_UDP))) {
83 udp_header_t *udp = (void *)(icmp_ip + 1);
84 return (dir == MAP_SENDER ? udp->dst_port : udp->src_port);
85 } else if (icmp_ip->protocol == IP_PROTOCOL_ICMP) {
86 icmp46_header_t *inner_icmp = (void *)(icmp_ip + 1);
87 if (inner_icmp->type == ICMP4_echo_request || inner_icmp->type == ICMP4_echo_reply)
88 return (*((u16 *)(inner_icmp + 1)));
89 }
90 }
91 }
92 return (0);
93}
94
95static_always_inline u16
96ip4_map_port_and_security_check (map_domain_t *d, ip4_header_t *ip, u32 *next, u8 *error)
97{
98 u16 port = 0;
99
100 if (d->psid_length > 0) {
101 if (!ip4_is_fragment(ip)) {
102 if (PREDICT_FALSE((ip->ip_version_and_header_length != 0x45) || clib_host_to_net_u16(ip->length) < 28)) {
103 return 0;
104 }
105 port = ip4_map_get_port(ip, MAP_RECEIVER);
106 if (port) {
107 /* Verify that port is not among the well-known ports */
108 if ((d->psid_offset > 0) && (clib_net_to_host_u16(port) < (0x1 << (16 - d->psid_offset)))) {
109 *error = MAP_ERROR_ENCAP_SEC_CHECK;
110 } else {
111 return (port);
112 }
113 } else {
114 *error = MAP_ERROR_BAD_PROTOCOL;
115 }
116 } else {
117 *next = IP4_MAP_NEXT_REASS;
118 }
119 }
120 return (0);
121}
122
123/*
124 * ip4_map_vtcfl
125 */
126static_always_inline u32
127ip4_map_vtcfl (ip4_header_t *ip4, vlib_buffer_t *p)
128{
129 map_main_t *mm = &map_main;
130 u8 tc = mm->tc_copy ? ip4->tos : mm->tc;
131 u32 vtcfl = 0x6 << 28;
132 vtcfl |= tc << 20;
Damjan Marion2c29d752015-12-18 10:26:56 +0100133 vtcfl |= vnet_buffer(p)->ip.flow_hash & 0x000fffff;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700134
135 return (clib_host_to_net_u32(vtcfl));
136}
137
138static_always_inline bool
139ip4_map_ip6_lookup_bypass (vlib_buffer_t *p0, ip4_header_t *ip)
140{
141#ifdef MAP_SKIP_IP6_LOOKUP
142 map_main_t *mm = &map_main;
143 u32 adj_index0 = mm->adj6_index;
144 if (adj_index0 > 0) {
145 ip_lookup_main_t *lm6 = &ip6_main.lookup_main;
146 ip_adjacency_t *adj = ip_get_adjacency(lm6, mm->adj6_index);
147 if (adj->n_adj > 1) {
148 u32 hash_c0 = ip4_compute_flow_hash(ip, IP_FLOW_HASH_DEFAULT);
149 adj_index0 += (hash_c0 & (adj->n_adj - 1));
150 }
151 vnet_buffer(p0)->ip.adj_index[VLIB_TX] = adj_index0;
152 return (true);
153 }
154#endif
155 return (false);
156}
157
158/*
Ole Troan366ac6e2016-01-06 12:40:28 +0100159 * ip4_map_ttl
160 */
161static inline void
162ip4_map_decrement_ttl (ip4_header_t *ip, u8 *error)
163{
164 i32 ttl = ip->ttl;
165
166 /* Input node should have reject packets with ttl 0. */
167 ASSERT (ip->ttl > 0);
168
169 u32 checksum = ip->checksum + clib_host_to_net_u16(0x0100);
170 checksum += checksum >= 0xffff;
171 ip->checksum = checksum;
172 ttl -= 1;
173 ip->ttl = ttl;
174 *error = ttl <= 0 ? IP4_ERROR_TIME_EXPIRED : *error;
175
176 /* Verify checksum. */
177 ASSERT (ip->checksum == ip4_header_checksum(ip));
178}
179
180/*
Ed Warnickecb9cada2015-12-08 15:45:58 -0700181 * ip4_map
182 */
183static uword
184ip4_map (vlib_main_t *vm,
185 vlib_node_runtime_t *node,
186 vlib_frame_t *frame)
187{
188 u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
189 vlib_node_runtime_t *error_node = vlib_node_get_runtime(vm, ip4_map_node.index);
190 from = vlib_frame_vector_args(frame);
191 n_left_from = frame->n_vectors;
192 next_index = node->cached_next_index;
193 map_main_t *mm = &map_main;
194 vlib_combined_counter_main_t *cm = mm->domain_counters;
195 u32 cpu_index = os_get_cpu_number();
196
197 while (n_left_from > 0) {
198 vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
199
200 /* Dual loop */
Ole Troana5d2c702016-01-05 21:26:17 +0100201 while (n_left_from >= 4 && n_left_to_next >= 2) {
Ed Warnickecb9cada2015-12-08 15:45:58 -0700202 u32 pi0, pi1;
203 vlib_buffer_t *p0, *p1;
204 map_domain_t *d0, *d1;
205 u8 error0 = MAP_ERROR_NONE, error1 = MAP_ERROR_NONE;
206 ip4_header_t *ip40, *ip41;
207 u16 port0 = 0, port1 = 0;
208 ip6_header_t *ip6h0, *ip6h1;
209 u32 map_domain_index0 = ~0, map_domain_index1 = ~0;
210 u32 next0 = IP4_MAP_NEXT_IP6_LOOKUP, next1 = IP4_MAP_NEXT_IP6_LOOKUP;
211
212 /* Prefetch next iteration. */
213 {
214 vlib_buffer_t *p2, *p3;
215
216 p2 = vlib_get_buffer(vm, from[2]);
217 p3 = vlib_get_buffer(vm, from[3]);
218
219 vlib_prefetch_buffer_header(p2, STORE);
220 vlib_prefetch_buffer_header(p3, STORE);
221 /* IPv4 + 8 = 28. possibly plus -40 */
222 CLIB_PREFETCH (p2->data-40, 68, STORE);
223 CLIB_PREFETCH (p3->data-40, 68, STORE);
224 }
225
226 pi0 = to_next[0] = from[0];
227 pi1 = to_next[1] = from[1];
228 from += 2;
229 n_left_from -= 2;
230 to_next +=2;
231 n_left_to_next -= 2;
232
233 p0 = vlib_get_buffer(vm, pi0);
234 p1 = vlib_get_buffer(vm, pi1);
235 ip40 = vlib_buffer_get_current(p0);
236 ip41 = vlib_buffer_get_current(p1);
237 p0->current_length = clib_net_to_host_u16(ip40->length);
238 p1->current_length = clib_net_to_host_u16(ip41->length);
239 d0 = ip4_map_get_domain(vnet_buffer(p0)->ip.adj_index[VLIB_TX], &map_domain_index0);
240 d1 = ip4_map_get_domain(vnet_buffer(p1)->ip.adj_index[VLIB_TX], &map_domain_index1);
241 ASSERT(d0);
242 ASSERT(d1);
243
244 /*
245 * Shared IPv4 address
246 */
247 port0 = ip4_map_port_and_security_check(d0, ip40, &next0, &error0);
248 port1 = ip4_map_port_and_security_check(d1, ip41, &next1, &error1);
249
250 /* MAP calc */
251 u32 da40 = clib_net_to_host_u32(ip40->dst_address.as_u32);
252 u32 da41 = clib_net_to_host_u32(ip41->dst_address.as_u32);
253 u16 dp40 = clib_net_to_host_u16(port0);
254 u16 dp41 = clib_net_to_host_u16(port1);
255 u64 dal60 = map_get_pfx(d0, da40, dp40);
256 u64 dal61 = map_get_pfx(d1, da41, dp41);
257 u64 dar60 = map_get_sfx(d0, da40, dp40);
258 u64 dar61 = map_get_sfx(d1, da41, dp41);
259 if (dal60 == 0 && dar60 == 0) error0 = MAP_ERROR_UNKNOWN;
260 if (dal61 == 0 && dar61 == 0) error1 = MAP_ERROR_UNKNOWN;
261
262 /* construct ipv6 header */
263 vlib_buffer_advance(p0, - sizeof(ip6_header_t));
264 vlib_buffer_advance(p1, - sizeof(ip6_header_t));
265 ip6h0 = vlib_buffer_get_current(p0);
266 ip6h1 = vlib_buffer_get_current(p1);
267 vnet_buffer(p0)->sw_if_index[VLIB_TX] = (u32)~0;
268 vnet_buffer(p1)->sw_if_index[VLIB_TX] = (u32)~0;
269
270 ip6h0->ip_version_traffic_class_and_flow_label = ip4_map_vtcfl(ip40, p0);
271 ip6h1->ip_version_traffic_class_and_flow_label = ip4_map_vtcfl(ip41, p1);
272 ip6h0->payload_length = ip40->length;
273 ip6h1->payload_length = ip41->length;
274 ip6h0->protocol = IP_PROTOCOL_IP_IN_IP;
275 ip6h1->protocol = IP_PROTOCOL_IP_IN_IP;
276 ip6h0->hop_limit = 0x40;
277 ip6h1->hop_limit = 0x40;
278 ip6h0->src_address = d0->ip6_src;
279 ip6h1->src_address = d1->ip6_src;
280 ip6h0->dst_address.as_u64[0] = clib_host_to_net_u64(dal60);
281 ip6h0->dst_address.as_u64[1] = clib_host_to_net_u64(dar60);
282 ip6h1->dst_address.as_u64[0] = clib_host_to_net_u64(dal61);
283 ip6h1->dst_address.as_u64[1] = clib_host_to_net_u64(dar61);
284
285 /*
286 * Determine next node. Can be one of:
287 * ip6-lookup, ip6-rewrite, ip4-fragment, ip4-virtreass, error-drop
288 */
289 if (PREDICT_TRUE(error0 == MAP_ERROR_NONE)) {
290 if (PREDICT_FALSE(d0->mtu && (clib_net_to_host_u16(ip6h0->payload_length) + sizeof(*ip6h0) > d0->mtu))) {
291 vnet_buffer(p0)->ip_frag.header_offset = sizeof(*ip6h0);
292 vnet_buffer(p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
293 vnet_buffer(p0)->ip_frag.mtu = d0->mtu;
294 vnet_buffer(p0)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
295 next0 = IP4_MAP_NEXT_FRAGMENT;
296 } else {
297 next0 = ip4_map_ip6_lookup_bypass(p0, ip40) ? IP4_MAP_NEXT_IP6_REWRITE : next0;
298 vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index0, 1,
299 clib_net_to_host_u16(ip6h0->payload_length) + 40);
300 }
301 } else {
302 next0 = IP4_MAP_NEXT_DROP;
303 }
304
305 /*
306 * Determine next node. Can be one of:
307 * ip6-lookup, ip6-rewrite, ip4-fragment, ip4-virtreass, error-drop
308 */
309 if (PREDICT_TRUE(error1 == MAP_ERROR_NONE)) {
310 if (PREDICT_FALSE(d1->mtu && (clib_net_to_host_u16(ip6h1->payload_length) + sizeof(*ip6h1) > d1->mtu))) {
311 vnet_buffer(p1)->ip_frag.header_offset = sizeof(*ip6h1);
312 vnet_buffer(p1)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
313 vnet_buffer(p1)->ip_frag.mtu = d1->mtu;
314 vnet_buffer(p1)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
315 next1 = IP4_MAP_NEXT_FRAGMENT;
316 } else {
317 next1 = ip4_map_ip6_lookup_bypass(p1, ip41) ? IP4_MAP_NEXT_IP6_REWRITE : next1;
318 vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index1, 1,
319 clib_net_to_host_u16(ip6h1->payload_length) + 40);
320 }
321 } else {
322 next1 = IP4_MAP_NEXT_DROP;
323 }
324
325 if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
326 map_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof(*tr));
327 tr->map_domain_index = map_domain_index0;
328 tr->port = port0;
329 }
330 if (PREDICT_FALSE(p1->flags & VLIB_BUFFER_IS_TRACED)) {
331 map_trace_t *tr = vlib_add_trace(vm, node, p1, sizeof(*tr));
332 tr->map_domain_index = map_domain_index1;
333 tr->port = port1;
334 }
335
336 p0->error = error_node->errors[error0];
337 p1->error = error_node->errors[error1];
338
339 vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next, n_left_to_next, pi0, pi1, next0, next1);
340 }
341
342 while (n_left_from > 0 && n_left_to_next > 0) {
343 u32 pi0;
344 vlib_buffer_t *p0;
345 map_domain_t *d0;
346 u8 error0 = MAP_ERROR_NONE;
347 ip4_header_t *ip40;
348 u16 port0 = 0;
349 ip6_header_t *ip6h0;
350 u32 next0 = IP4_MAP_NEXT_IP6_LOOKUP;
351 u32 map_domain_index0 = ~0;
352
353 pi0 = to_next[0] = from[0];
354 from += 1;
355 n_left_from -= 1;
356 to_next +=1;
357 n_left_to_next -= 1;
358
359 p0 = vlib_get_buffer(vm, pi0);
360 ip40 = vlib_buffer_get_current(p0);
361 p0->current_length = clib_net_to_host_u16(ip40->length);
362 d0 = ip4_map_get_domain(vnet_buffer(p0)->ip.adj_index[VLIB_TX], &map_domain_index0);
363 ASSERT(d0);
364
365 /*
366 * Shared IPv4 address
367 */
368 port0 = ip4_map_port_and_security_check(d0, ip40, &next0, &error0);
369
Ole Troan366ac6e2016-01-06 12:40:28 +0100370 /* Decrement IPv4 TTL */
371 ip4_map_decrement_ttl(ip40, &error0);
372
Ed Warnickecb9cada2015-12-08 15:45:58 -0700373 /* MAP calc */
374 u32 da40 = clib_net_to_host_u32(ip40->dst_address.as_u32);
375 u16 dp40 = clib_net_to_host_u16(port0);
376 u64 dal60 = map_get_pfx(d0, da40, dp40);
377 u64 dar60 = map_get_sfx(d0, da40, dp40);
378 if (dal60 == 0 && dar60 == 0 && error0 == MAP_ERROR_NONE) error0 = MAP_ERROR_UNKNOWN;
379
380 /* construct ipv6 header */
381 vlib_buffer_advance(p0, - (sizeof(ip6_header_t)));
382 ip6h0 = vlib_buffer_get_current(p0);
383 vnet_buffer(p0)->sw_if_index[VLIB_TX] = (u32)~0;
384
385 ip6h0->ip_version_traffic_class_and_flow_label = ip4_map_vtcfl(ip40, p0);
386 ip6h0->payload_length = ip40->length;
387 ip6h0->protocol = IP_PROTOCOL_IP_IN_IP;
388 ip6h0->hop_limit = 0x40;
389 ip6h0->src_address = d0->ip6_src;
390 ip6h0->dst_address.as_u64[0] = clib_host_to_net_u64(dal60);
391 ip6h0->dst_address.as_u64[1] = clib_host_to_net_u64(dar60);
392
393 /*
394 * Determine next node. Can be one of:
395 * ip6-lookup, ip6-rewrite, ip4-fragment, ip4-virtreass, error-drop
396 */
397 if (PREDICT_TRUE(error0 == MAP_ERROR_NONE)) {
398 if (PREDICT_FALSE(d0->mtu && (clib_net_to_host_u16(ip6h0->payload_length) + sizeof(*ip6h0) > d0->mtu))) {
399 vnet_buffer(p0)->ip_frag.header_offset = sizeof(*ip6h0);
400 vnet_buffer(p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
401 vnet_buffer(p0)->ip_frag.mtu = d0->mtu;
402 vnet_buffer(p0)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
403 next0 = IP4_MAP_NEXT_FRAGMENT;
404 } else {
405 next0 = ip4_map_ip6_lookup_bypass(p0, ip40) ? IP4_MAP_NEXT_IP6_REWRITE : next0;
406 vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index0, 1,
407 clib_net_to_host_u16(ip6h0->payload_length) + 40);
408 }
409 } else {
410 next0 = IP4_MAP_NEXT_DROP;
411 }
412
413 if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
414 map_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof(*tr));
415 tr->map_domain_index = map_domain_index0;
416 tr->port = port0;
417 }
418
419 p0->error = error_node->errors[error0];
420 vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
421 }
422 vlib_put_next_frame(vm, node, next_index, n_left_to_next);
423 }
424
425 return frame->n_vectors;
426}
427
428/*
429 * ip4_map_reass
430 */
431static uword
432ip4_map_reass (vlib_main_t *vm,
433 vlib_node_runtime_t *node,
434 vlib_frame_t *frame)
435{
436 u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
437 vlib_node_runtime_t *error_node = vlib_node_get_runtime(vm, ip4_map_reass_node.index);
438 from = vlib_frame_vector_args(frame);
439 n_left_from = frame->n_vectors;
440 next_index = node->cached_next_index;
441 map_main_t *mm = &map_main;
442 vlib_combined_counter_main_t *cm = mm->domain_counters;
443 u32 cpu_index = os_get_cpu_number();
444 u32 *fragments_to_drop = NULL;
445 u32 *fragments_to_loopback = NULL;
446
447 while (n_left_from > 0) {
448 vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
449
450 while (n_left_from > 0 && n_left_to_next > 0) {
451 u32 pi0;
452 vlib_buffer_t *p0;
453 map_domain_t *d0;
454 u8 error0 = MAP_ERROR_NONE;
455 ip4_header_t *ip40;
456 i32 port0 = 0;
457 ip6_header_t *ip60;
458 u32 next0 = IP4_MAP_REASS_NEXT_IP6_LOOKUP;
459 u32 map_domain_index0;
460 u8 cached = 0;
461
462 pi0 = to_next[0] = from[0];
463 from += 1;
464 n_left_from -= 1;
465 to_next +=1;
466 n_left_to_next -= 1;
467
468 p0 = vlib_get_buffer(vm, pi0);
469 ip60 = vlib_buffer_get_current(p0);
470 ip40 = (ip4_header_t *)(ip60 + 1);
471 d0 = ip4_map_get_domain(vnet_buffer(p0)->ip.adj_index[VLIB_TX], &map_domain_index0);
472
473 map_ip4_reass_lock();
474 map_ip4_reass_t *r = map_ip4_reass_get(ip40->src_address.as_u32, ip40->dst_address.as_u32,
475 ip40->fragment_id, ip40->protocol, &fragments_to_drop);
476 if (PREDICT_FALSE(!r)) {
477 // Could not create a caching entry
478 error0 = MAP_ERROR_FRAGMENT_MEMORY;
479 } else if (PREDICT_TRUE(ip4_get_fragment_offset(ip40))) {
480 if (r->port >= 0) {
481 // We know the port already
482 port0 = r->port;
483 } else if (map_ip4_reass_add_fragment(r, pi0)) {
484 // Not enough space for caching
485 error0 = MAP_ERROR_FRAGMENT_MEMORY;
486 map_ip4_reass_free(r, &fragments_to_drop);
487 } else {
488 cached = 1;
489 }
490 } else if ((port0 = ip4_get_port(ip40, MAP_RECEIVER, p0->current_length)) < 0) {
491 // Could not find port. We'll free the reassembly.
492 error0 = MAP_ERROR_BAD_PROTOCOL;
493 port0 = 0;
494 map_ip4_reass_free(r, &fragments_to_drop);
495 } else {
496 r->port = port0;
497 map_ip4_reass_get_fragments(r, &fragments_to_loopback);
498 }
499
500#ifdef MAP_IP4_REASS_COUNT_BYTES
501 if (!cached && r) {
502 r->forwarded += clib_host_to_net_u16(ip40->length) - 20;
503 if (!ip4_get_fragment_more(ip40))
504 r->expected_total = ip4_get_fragment_offset(ip40) * 8 + clib_host_to_net_u16(ip40->length) - 20;
505 if(r->forwarded >= r->expected_total)
506 map_ip4_reass_free(r, &fragments_to_drop);
507 }
508#endif
509
510 map_ip4_reass_unlock();
511
512 // NOTE: Most operations have already been performed by ip4_map
513 // All we need is the right destination address
514 ip60->dst_address.as_u64[0] = map_get_pfx_net(d0, ip40->dst_address.as_u32, port0);
515 ip60->dst_address.as_u64[1] = map_get_sfx_net(d0, ip40->dst_address.as_u32, port0);
516
517 if (PREDICT_FALSE(d0->mtu && (clib_net_to_host_u16(ip60->payload_length) + sizeof(*ip60) > d0->mtu))) {
518 vnet_buffer(p0)->ip_frag.header_offset = sizeof(*ip60);
519 vnet_buffer(p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
520 vnet_buffer(p0)->ip_frag.mtu = d0->mtu;
521 vnet_buffer(p0)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
522 next0 = IP4_MAP_REASS_NEXT_IP4_FRAGMENT;
523 }
524
525 if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
526 map_ip4_map_reass_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof(*tr));
527 tr->map_domain_index = map_domain_index0;
528 tr->port = port0;
529 tr->cached = cached;
530 }
531
532 if(cached) {
533 //Dequeue the packet
534 n_left_to_next++;
535 to_next--;
536 } else {
537 if (error0 == MAP_ERROR_NONE)
538 vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index0, 1,
539 clib_net_to_host_u16(ip60->payload_length) + 40);
540 next0 = (error0 == MAP_ERROR_NONE) ? next0 : IP4_MAP_REASS_NEXT_DROP;
541 p0->error = error_node->errors[error0];
542 vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
543 }
544
545 //Loopback when we reach the end of the inpu vector
546 if(n_left_from == 0 && vec_len(fragments_to_loopback)) {
547 from = vlib_frame_vector_args(frame);
548 u32 len = vec_len(fragments_to_loopback);
549 if(len <= VLIB_FRAME_SIZE) {
550 memcpy(from, fragments_to_loopback, sizeof(u32)*len);
551 n_left_from = len;
552 vec_reset_length(fragments_to_loopback);
553 } else {
554 memcpy(from, fragments_to_loopback + (len - VLIB_FRAME_SIZE), sizeof(u32)*VLIB_FRAME_SIZE);
555 n_left_from = VLIB_FRAME_SIZE;
556 _vec_len(fragments_to_loopback) = len - VLIB_FRAME_SIZE;
557 }
558 }
559 }
560 vlib_put_next_frame(vm, node, next_index, n_left_to_next);
561 }
562
563 map_send_all_to_node(vm, fragments_to_drop, node,
564 &error_node->errors[MAP_ERROR_FRAGMENT_DROPPED],
565 IP4_MAP_REASS_NEXT_DROP);
566
567 vec_free(fragments_to_drop);
568 vec_free(fragments_to_loopback);
569 return frame->n_vectors;
570}
571
572static char *map_error_strings[] = {
573#define _(sym,string) string,
574 foreach_map_error
575#undef _
576};
577
578VLIB_REGISTER_NODE(ip4_map_node) = {
579 .function = ip4_map,
580 .name = "ip4-map",
581 .vector_size = sizeof(u32),
582 .format_trace = format_map_trace,
583 .type = VLIB_NODE_TYPE_INTERNAL,
584
585 .n_errors = MAP_N_ERROR,
586 .error_strings = map_error_strings,
587
588 .n_next_nodes = IP4_MAP_N_NEXT,
589 .next_nodes = {
590 [IP4_MAP_NEXT_IP6_LOOKUP] = "ip6-lookup",
591#ifdef MAP_SKIP_IP6_LOOKUP
592 [IP4_MAP_NEXT_IP6_REWRITE] = "ip6-rewrite",
593#endif
594 [IP4_MAP_NEXT_FRAGMENT] = "ip4-frag",
595 [IP4_MAP_NEXT_REASS] = "ip4-map-reass",
596 [IP4_MAP_NEXT_DROP] = "error-drop",
597 },
598};
599
600VLIB_REGISTER_NODE(ip4_map_reass_node) = {
601 .function = ip4_map_reass,
602 .name = "ip4-map-reass",
603 .vector_size = sizeof(u32),
604 .format_trace = format_ip4_map_reass_trace,
605 .type = VLIB_NODE_TYPE_INTERNAL,
606
607 .n_errors = MAP_N_ERROR,
608 .error_strings = map_error_strings,
609
610 .n_next_nodes = IP4_MAP_REASS_N_NEXT,
611 .next_nodes = {
612 [IP4_MAP_REASS_NEXT_IP6_LOOKUP] = "ip6-lookup",
613 [IP4_MAP_REASS_NEXT_IP4_FRAGMENT] = "ip4-frag",
614 [IP4_MAP_REASS_NEXT_DROP] = "error-drop",
615 },
616};