Dual loop decrement TTL.
[vpp.git] / vnet / vnet / ip / ip_frag.c
1 /*---------------------------------------------------------------------------
2  * Copyright (c) 2009-2014 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 /*
17  * IPv4 Fragmentation Node
18  *
19  *
20  */
21
22 #include "ip_frag.h"
23
24 #include <vnet/ip/ip.h>
25
26
27 typedef struct {
28   u8 ipv6;
29   u16 header_offset;
30   u16 mtu;
31   u8 next;
32   u16 n_fragments;
33 } ip_frag_trace_t;
34
35 static u8 * format_ip_frag_trace (u8 * s, va_list * args)
36 {
37   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
38   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
39   ip_frag_trace_t * t = va_arg (*args, ip_frag_trace_t *);
40   s = format(s, "IPv%s offset: %u mtu: %u fragments: %u",
41              t->ipv6?"6":"4", t->header_offset, t->mtu, t->n_fragments);
42   return s;
43 }
44
45 static u32 running_fragment_id;
46
47 static void
48 ip4_frag_do_fragment(vlib_main_t *vm, u32 pi, u32 **buffer, ip_frag_error_t *error)
49 {
50   vlib_buffer_t *p;
51   ip4_header_t *ip4;
52   u16 mtu, ptr, len, max, rem,
53     offset, ip_frag_id, ip_frag_offset;
54   u8 *packet, more;
55
56   vec_add1(*buffer, pi);
57   p = vlib_get_buffer(vm, pi);
58   offset = vnet_buffer(p)->ip_frag.header_offset;
59   mtu = vnet_buffer(p)->ip_frag.mtu;
60   packet = (u8 *)vlib_buffer_get_current(p);
61   ip4 = (ip4_header_t *)(packet + offset);
62
63   rem = clib_net_to_host_u16(ip4->length) - sizeof(*ip4);
64   ptr = 0;
65   max = (mtu - sizeof(*ip4) - vnet_buffer(p)->ip_frag.header_offset) & ~0x7;
66
67   if (rem < (p->current_length - offset - sizeof(*ip4))) {
68     *error = IP_FRAG_ERROR_MALFORMED;
69     return;
70   }
71
72   if (mtu < sizeof(*ip4)) {
73     *error = IP_FRAG_ERROR_CANT_FRAGMENT_HEADER;
74     return;
75   }
76
77   if (ip4->flags_and_fragment_offset &
78       clib_host_to_net_u16(IP4_HEADER_FLAG_DONT_FRAGMENT)) {
79     *error = IP_FRAG_ERROR_DONT_FRAGMENT_SET;
80     return;
81   }
82
83   if (ip4_is_fragment(ip4)) {
84     ip_frag_id = ip4->fragment_id;
85     ip_frag_offset = ip4_get_fragment_offset(ip4);
86     more = !!(ip4->flags_and_fragment_offset & clib_host_to_net_u16(IP4_HEADER_FLAG_MORE_FRAGMENTS));
87   } else {
88     ip_frag_id = (++running_fragment_id);
89     ip_frag_offset = 0;
90     more = 0;
91   }
92
93   //Do the actual fragmentation
94   while (rem) {
95     u32 bi;
96     vlib_buffer_t *b;
97     ip4_header_t *fip4;
98
99     len = (rem > (mtu - sizeof(*ip4) - vnet_buffer(p)->ip_frag.header_offset)) ? max : rem;
100
101     if (ptr == 0) {
102       bi = pi;
103       b = p;
104       fip4 = (ip4_header_t *)(vlib_buffer_get_current(b) + offset);
105     } else {
106       if (!vlib_buffer_alloc(vm, &bi, 1)) {
107         *error = IP_FRAG_ERROR_MEMORY;
108         return;
109       }
110       vec_add1(*buffer, bi);
111       b = vlib_get_buffer(vm, bi);
112       vnet_buffer(b)->sw_if_index[VLIB_RX] = vnet_buffer(p)->sw_if_index[VLIB_RX];
113       vnet_buffer(b)->sw_if_index[VLIB_TX] = vnet_buffer(p)->sw_if_index[VLIB_TX];
114       fip4 = (ip4_header_t *)(vlib_buffer_get_current(b) + offset);
115
116       //Copy offset and ip4 header
117       memcpy(b->data, packet, offset + sizeof(*ip4));
118       //Copy data
119       memcpy(((u8*)(fip4)) + sizeof(*fip4),
120              packet + offset + sizeof(*fip4) + ptr, len);
121     }
122     b->current_length = offset + len + sizeof(*fip4);
123
124     fip4->fragment_id = ip_frag_id;
125     fip4->flags_and_fragment_offset = clib_host_to_net_u16((ptr >> 3) + ip_frag_offset);
126     fip4->flags_and_fragment_offset |= clib_host_to_net_u16(((len != rem) || more) << 13);
127     // ((len0 != rem0) || more0) << 13 is optimization for
128     // ((len0 != rem0) || more0) ? IP4_HEADER_FLAG_MORE_FRAGMENTS : 0
129     fip4->length = clib_host_to_net_u16(len + sizeof(*fip4));
130     fip4->checksum = ip4_header_checksum(fip4);
131
132     if(vnet_buffer(p)->ip_frag.flags & IP_FRAG_FLAG_IP4_HEADER) {
133       //Encapsulating ipv4 header
134       ip4_header_t *encap_header4 = (ip4_header_t *)vlib_buffer_get_current(b);
135       encap_header4->length = clib_host_to_net_u16(b->current_length);
136       encap_header4->checksum = ip4_header_checksum(encap_header4);
137     } else if (vnet_buffer(p)->ip_frag.flags & IP_FRAG_FLAG_IP6_HEADER) {
138       //Encapsulating ipv6 header
139       ip6_header_t *encap_header6 = (ip6_header_t *)vlib_buffer_get_current(b);
140       encap_header6->payload_length = clib_host_to_net_u16(b->current_length - sizeof(*encap_header6));
141     }
142
143     rem -= len;
144     ptr += len;
145   }
146 }
147
148 void
149 ip_frag_set_vnet_buffer (vlib_buffer_t *b, u16 offset, u16 mtu, u8 next_index, u8 flags)
150 {
151   vnet_buffer(b)->ip_frag.header_offset = offset;
152   vnet_buffer(b)->ip_frag.mtu = mtu;
153   vnet_buffer(b)->ip_frag.next_index = next_index;
154   vnet_buffer(b)->ip_frag.flags = flags;
155 }
156
157 static uword
158 ip4_frag (vlib_main_t *vm,
159           vlib_node_runtime_t *node,
160           vlib_frame_t *frame)
161 {
162   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
163   vlib_node_runtime_t * error_node = vlib_node_get_runtime(vm, ip4_frag_node.index);
164   from = vlib_frame_vector_args(frame);
165   n_left_from = frame->n_vectors;
166   next_index = node->cached_next_index;
167   u32 frag_sent = 0, small_packets = 0;
168   u32 *buffer = 0;
169
170   while (n_left_from > 0) {
171     vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
172
173     while (n_left_from > 0 && n_left_to_next > 0) {
174       u32 pi0, *frag_from, frag_left;
175       vlib_buffer_t *p0;
176       ip_frag_error_t error0;
177       ip4_frag_next_t next0;
178
179       //Note: The packet is not enqueued now.
180       //It is instead put in a vector where other fragments
181       //will be put as well.
182       pi0 = from[0];
183       from += 1;
184       n_left_from -= 1;
185       error0 = IP_FRAG_ERROR_NONE;
186
187       p0 = vlib_get_buffer(vm, pi0);
188       ip4_frag_do_fragment(vm, pi0, &buffer, &error0);
189
190       if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
191         ip_frag_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof (*tr));
192         tr->header_offset = vnet_buffer(p0)->ip_frag.header_offset;
193         tr->mtu = vnet_buffer(p0)->ip_frag.mtu;
194         tr->ipv6 = 0;
195         tr->n_fragments = vec_len(buffer);
196         tr->next = vnet_buffer(p0)->ip_frag.next_index;
197       }
198
199       if (error0 == IP_FRAG_ERROR_DONT_FRAGMENT_SET) {
200         icmp4_error_set_vnet_buffer(p0, ICMP4_destination_unreachable,
201                                     ICMP4_destination_unreachable_fragmentation_needed_and_dont_fragment_set,
202                                     vnet_buffer(p0)->ip_frag.mtu);
203         vlib_buffer_advance(p0, vnet_buffer(p0)->ip_frag.header_offset);
204         next0 = IP4_FRAG_NEXT_ICMP_ERROR;
205       } else
206         next0 = (error0 == IP_FRAG_ERROR_NONE) ? vnet_buffer(p0)->ip_frag.next_index : IP4_FRAG_NEXT_DROP;
207
208       if (error0 == IP_FRAG_ERROR_NONE) {
209         frag_sent += vec_len(buffer);
210         small_packets += (vec_len(buffer) == 1);
211       } else
212         vlib_error_count(vm, ip4_frag_node.index, error0, 1);
213
214       //Send fragments that were added in the frame
215       frag_from = buffer;
216       frag_left = vec_len(buffer);
217
218       while (frag_left > 0) {
219         while (frag_left > 0 && n_left_to_next > 0) {
220           u32 i;
221           i = to_next[0] = frag_from[0];
222           frag_from += 1;
223           frag_left -= 1;
224           to_next += 1;
225           n_left_to_next -= 1;
226
227           vlib_get_buffer(vm, i)->error = error_node->errors[error0];
228           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
229                                            to_next, n_left_to_next, i,
230                                            next0);
231         }
232         vlib_put_next_frame(vm, node, next_index, n_left_to_next);
233         vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
234       }
235       vec_reset_length(buffer);
236     }
237     vlib_put_next_frame(vm, node, next_index, n_left_to_next);
238   }
239   vec_free(buffer);
240
241   vlib_node_increment_counter(vm, ip4_frag_node.index, IP_FRAG_ERROR_FRAGMENT_SENT, frag_sent);
242   vlib_node_increment_counter(vm, ip4_frag_node.index, IP_FRAG_ERROR_SMALL_PACKET, small_packets);
243
244   return frame->n_vectors;
245 }
246
247
248 static void
249 ip6_frag_do_fragment(vlib_main_t *vm, u32 pi, u32 **buffer, ip_frag_error_t *error)
250 {
251   vlib_buffer_t *p;
252   ip6_header_t *ip6_hdr;
253   ip6_frag_hdr_t *frag_hdr;
254   u8 *payload, *next_header;
255
256   p = vlib_get_buffer(vm, pi);
257
258   //Parsing the IPv6 headers
259   ip6_hdr = vlib_buffer_get_current(p) + vnet_buffer(p)->ip_frag.header_offset;
260   payload = (u8 *)(ip6_hdr + 1);
261   next_header = &ip6_hdr->protocol;
262   if (*next_header == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS) {
263     next_header = payload;
264     payload += payload[1] * 8;
265   }
266
267   if (*next_header == IP_PROTOCOL_IP6_DESTINATION_OPTIONS) {
268     next_header = payload;
269     payload += payload[1] * 8;
270   }
271
272   if (*next_header == IP_PROTOCOL_IPV6_ROUTE) {
273     next_header = payload;
274     payload += payload[1] * 8;
275   }
276
277   u8 has_more;
278   u16 initial_offset;
279   if (*next_header == IP_PROTOCOL_IPV6_FRAGMENTATION) {
280     //The fragmentation header is already there
281     frag_hdr = (ip6_frag_hdr_t *)payload;
282     has_more = ip6_frag_hdr_more(frag_hdr);
283     initial_offset = ip6_frag_hdr_offset(frag_hdr);
284   } else {
285     //Insert a fragmentation header in the packet
286     u8 nh = *next_header;
287     *next_header = IP_PROTOCOL_IPV6_FRAGMENTATION;
288     vlib_buffer_advance(p, -sizeof(*frag_hdr));
289     u8 *start = vlib_buffer_get_current(p);
290     memmove(start, start + sizeof(*frag_hdr), payload - (start + sizeof(*frag_hdr)));
291     frag_hdr = (ip6_frag_hdr_t *)(payload - sizeof(*frag_hdr));
292     frag_hdr->identification = ++running_fragment_id;
293     frag_hdr->next_hdr = nh;
294     frag_hdr->rsv = 0;
295     has_more = 0;
296     initial_offset = 0;
297   }
298   payload = (u8 *)(frag_hdr + 1);
299
300   u16 headers_len = payload - (u8 *)vlib_buffer_get_current(p);
301   u16 max_payload = vnet_buffer(p)->ip_frag.mtu - headers_len;
302   u16 rem = p->current_length - headers_len;
303   u16 ptr = 0;
304
305   if(max_payload < 8) {
306     *error = IP_FRAG_ERROR_CANT_FRAGMENT_HEADER;
307     return;
308   }
309
310   while (rem) {
311     u32 bi;
312     vlib_buffer_t *b;
313     u16 len = (rem > max_payload)?(max_payload & ~0x7):rem;
314     rem -= len;
315
316     if (ptr != 0) {
317       if (!vlib_buffer_alloc(vm, &bi, 1)) {
318         *error = IP_FRAG_ERROR_MEMORY;
319         return;
320       }
321       b = vlib_get_buffer(vm, bi);
322       vnet_buffer(b)->sw_if_index[VLIB_RX] = vnet_buffer(p)->sw_if_index[VLIB_RX];
323       vnet_buffer(b)->sw_if_index[VLIB_TX] = vnet_buffer(p)->sw_if_index[VLIB_TX];
324       memcpy(vlib_buffer_get_current(b), vlib_buffer_get_current(p), headers_len);
325       memcpy(vlib_buffer_get_current(b) + headers_len, payload + ptr, len);
326       frag_hdr = vlib_buffer_get_current(b) + headers_len - sizeof(*frag_hdr);
327     } else {
328       bi = pi;
329       b = vlib_get_buffer(vm, bi);
330       //frag_hdr already set here
331     }
332
333     ip6_hdr = vlib_buffer_get_current(b) +  vnet_buffer(p)->ip_frag.header_offset;
334     frag_hdr->fragment_offset_and_more = ip6_frag_hdr_offset_and_more(initial_offset + (ptr >> 3), (rem || has_more));
335     b->current_length = headers_len + len;
336     ip6_hdr->payload_length = clib_host_to_net_u16(b->current_length - vnet_buffer(p)->ip_frag.header_offset - sizeof(*ip6_hdr));
337
338     if(vnet_buffer(p)->ip_frag.flags & IP_FRAG_FLAG_IP4_HEADER) {
339       //Encapsulating ipv4 header
340       ip4_header_t *encap_header4 = (ip4_header_t *)vlib_buffer_get_current(b);
341       encap_header4->length = clib_host_to_net_u16(b->current_length);
342       encap_header4->checksum = ip4_header_checksum(encap_header4);
343     } else if (vnet_buffer(p)->ip_frag.flags & IP_FRAG_FLAG_IP6_HEADER) {
344       //Encapsulating ipv6 header
345       ip6_header_t *encap_header6 = (ip6_header_t *)vlib_buffer_get_current(b);
346       encap_header6->payload_length = clib_host_to_net_u16(b->current_length - sizeof(*encap_header6));
347     }
348
349     vec_add1(*buffer, bi);
350
351     ptr += len;
352   }
353 }
354
355 static uword
356 ip6_frag (vlib_main_t * vm,
357           vlib_node_runtime_t * node,
358           vlib_frame_t * frame)
359 {
360   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
361   vlib_node_runtime_t * error_node = vlib_node_get_runtime (vm, ip6_frag_node.index);
362   from = vlib_frame_vector_args (frame);
363   n_left_from = frame->n_vectors;
364   next_index = node->cached_next_index;
365   u32 frag_sent = 0, small_packets = 0;
366   u32 *buffer = 0;
367
368   while (n_left_from > 0) {
369     vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
370
371     while (n_left_from > 0 && n_left_to_next > 0) {
372       u32 pi0, *frag_from, frag_left;
373       vlib_buffer_t * p0;
374       ip_frag_error_t error0;
375       ip6_frag_next_t next0;
376
377       pi0 = from[0];
378       from += 1;
379       n_left_from -= 1;
380       error0 = IP_FRAG_ERROR_NONE;
381
382       p0 = vlib_get_buffer(vm, pi0);
383       ip6_frag_do_fragment(vm, pi0, &buffer, &error0);
384
385       if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
386         ip_frag_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof (*tr));
387         tr->header_offset = vnet_buffer(p0)->ip_frag.header_offset;
388         tr->mtu = vnet_buffer(p0)->ip_frag.mtu;
389         tr->ipv6 = 1;
390         tr->n_fragments = vec_len(buffer);
391         tr->next = vnet_buffer(p0)->ip_frag.next_index;
392       }
393
394       next0 = (error0 == IP_FRAG_ERROR_NONE) ? vnet_buffer(p0)->ip_frag.next_index : IP6_FRAG_NEXT_DROP;
395       frag_sent += vec_len(buffer);
396       small_packets += (vec_len(buffer) == 1);
397
398       //Send fragments that were added in the frame
399       frag_from = buffer;
400       frag_left = vec_len(buffer);
401       while (frag_left > 0) {
402         while (frag_left > 0 && n_left_to_next > 0) {
403           u32 i;
404           i = to_next[0] = frag_from[0];
405           frag_from += 1;
406           frag_left -= 1;
407           to_next += 1;
408           n_left_to_next -= 1;
409
410           vlib_get_buffer(vm, i)->error = error_node->errors[error0];
411           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
412                                            to_next, n_left_to_next, i,
413                                            next0);
414         }
415         vlib_put_next_frame(vm, node, next_index, n_left_to_next);
416         vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
417       }
418       vec_reset_length(buffer);
419     }
420     vlib_put_next_frame(vm, node, next_index, n_left_to_next);
421   }
422   vec_free(buffer);
423   vlib_node_increment_counter(vm, ip6_frag_node.index, IP_FRAG_ERROR_FRAGMENT_SENT, frag_sent);
424   vlib_node_increment_counter(vm, ip6_frag_node.index, IP_FRAG_ERROR_SMALL_PACKET, small_packets);
425
426   return frame->n_vectors;
427 }
428
429 static char * ip4_frag_error_strings[] = {
430 #define _(sym,string) string,
431   foreach_ip_frag_error
432 #undef _
433 };
434
435 VLIB_REGISTER_NODE (ip4_frag_node) = {
436   .function = ip4_frag,
437   .name = IP4_FRAG_NODE_NAME,
438   .vector_size = sizeof (u32),
439   .format_trace = format_ip_frag_trace,
440   .type = VLIB_NODE_TYPE_INTERNAL,
441
442   .n_errors = IP_FRAG_N_ERROR,
443   .error_strings = ip4_frag_error_strings,
444
445   .n_next_nodes = IP4_FRAG_N_NEXT,
446   .next_nodes = {
447     [IP4_FRAG_NEXT_IP4_LOOKUP] = "ip4-lookup",
448     [IP4_FRAG_NEXT_IP6_LOOKUP] = "ip6-lookup",
449     [IP4_FRAG_NEXT_ICMP_ERROR] = "ip4-icmp-error",
450     [IP4_FRAG_NEXT_DROP] = "error-drop"
451   },
452 };
453
454 VLIB_REGISTER_NODE (ip6_frag_node) = {
455   .function = ip6_frag,
456   .name = IP6_FRAG_NODE_NAME,
457   .vector_size = sizeof (u32),
458   .format_trace = format_ip_frag_trace,
459   .type = VLIB_NODE_TYPE_INTERNAL,
460
461   .n_errors = IP_FRAG_N_ERROR,
462   .error_strings = ip4_frag_error_strings,
463
464   .n_next_nodes = IP6_FRAG_N_NEXT,
465   .next_nodes = {
466     [IP6_FRAG_NEXT_IP4_LOOKUP] = "ip4-lookup",
467     [IP6_FRAG_NEXT_IP6_LOOKUP] = "ip6-lookup",
468     [IP6_FRAG_NEXT_DROP] = "error-drop"
469   },
470 };