mpls: support fragmentation of mpls output packet
[vpp.git] / src / vnet / mpls / mpls_output.c
1 /*
2  * mpls_output.c: MPLS Adj rewrite
3  *
4  * Copyright (c) 2012-2014 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vlib/vlib.h>
19 #include <vnet/pg/pg.h>
20 #include <vnet/ip/ip.h>
21 #include <vnet/mpls/mpls.h>
22 #include <vnet/ip/ip_frag.h>
23
24 typedef struct {
25   /* Adjacency taken. */
26   u32 adj_index;
27   u32 flow_hash;
28 } mpls_output_trace_t;
29
30 typedef enum {
31   MPLS_OUTPUT_MODE,
32   MPLS_OUTPUT_MIDCHAIN_MODE
33 }mpls_output_mode_t;
34
35 #define foreach_mpls_output_next                \
36 _(DROP, "error-drop")                           \
37 _(IP4_FRAG, "ip4-frag")                         \
38 _(IP6_FRAG, "ip6-frag")
39
40 typedef enum {
41 #define _(s,n) MPLS_OUTPUT_NEXT_##s,
42   foreach_mpls_output_next
43 #undef _
44   MPLS_OUTPUT_N_NEXT,
45 } mpls_output_next_t;
46
47 static u8 *
48 format_mpls_output_trace (u8 * s, va_list * args)
49 {
50   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
51   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
52   mpls_output_trace_t * t = va_arg (*args, mpls_output_trace_t *);
53
54   s = format (s, "adj-idx %d : %U flow hash: 0x%08x",
55               t->adj_index,
56               format_ip_adjacency, t->adj_index, FORMAT_IP_ADJACENCY_NONE,
57               t->flow_hash);
58   return s;
59 }
60
61 /*
62  * Save the mpls header length and adjust the current to ip header
63  */
64 static inline u32
65 set_mpls_fragmentation(vlib_buffer_t * p0, ip_adjacency_t * adj0)
66 {
67   u32 next0;
68
69   /* advance size of (all) mpls header to ip header before fragmenting */
70   /* save the current pointing to first mpls header. */
71   vnet_buffer (p0)->mpls.mpls_hdr_length = vnet_buffer(p0)->l3_hdr_offset - p0->current_data;
72   vlib_buffer_advance (p0, vnet_buffer (p0)->mpls.mpls_hdr_length);
73
74   /* IP fragmentation */
75   ip_frag_set_vnet_buffer (p0, adj0[0].rewrite_header.max_l3_packet_bytes,
76                            IP4_FRAG_NEXT_MPLS_OUTPUT,
77                            ((vnet_buffer (p0)->mpls.pyld_proto == DPO_PROTO_IP4) ? IP_FRAG_FLAG_IP4_HEADER:IP_FRAG_FLAG_IP6_HEADER));
78
79   /* Tell ip_frag to retain certain mpls parameters after fragmentation of mpls packet */
80   vnet_buffer (p0)->ip_frag.flags = (vnet_buffer (p0)->ip_frag.flags | IP_FRAG_FLAG_MPLS_HEADER);
81   next0 = (vnet_buffer (p0)->mpls.pyld_proto == DPO_PROTO_IP4)? MPLS_OUTPUT_NEXT_IP4_FRAG:MPLS_OUTPUT_NEXT_IP6_FRAG;
82
83   return next0;
84 }
85
86 static inline uword
87 mpls_output_inline (vlib_main_t * vm,
88                     vlib_node_runtime_t * node,
89                     vlib_frame_t * from_frame,
90                     mpls_output_mode_t mode)
91 {
92   u32 n_left_from, next_index, * from, * to_next, thread_index;
93   vlib_node_runtime_t * error_node;
94   u32 n_left_to_next;
95   mpls_main_t *mm;
96
97   thread_index = vlib_get_thread_index();
98   error_node = vlib_node_get_runtime (vm, mpls_output_node.index);
99   from = vlib_frame_vector_args (from_frame);
100   n_left_from = from_frame->n_vectors;
101   next_index = node->cached_next_index;
102   mm = &mpls_main;
103
104   while (n_left_from > 0)
105     {
106       vlib_get_next_frame (vm, node, next_index,
107                            to_next, n_left_to_next);
108
109       while (n_left_from >= 4 && n_left_to_next >= 2)
110         {
111           ip_adjacency_t * adj0;
112           mpls_unicast_header_t *hdr0;
113           vlib_buffer_t * p0;
114           u32 pi0, adj_index0, next0, error0;
115           word rw_len0;
116
117           ip_adjacency_t * adj1;
118           mpls_unicast_header_t *hdr1;
119           vlib_buffer_t * p1;
120           u32 pi1, adj_index1, next1, error1;
121           word rw_len1;
122
123           /* Prefetch next iteration. */
124           {
125             vlib_buffer_t * p2, * p3;
126
127             p2 = vlib_get_buffer (vm, from[2]);
128             p3 = vlib_get_buffer (vm, from[3]);
129
130             vlib_prefetch_buffer_header (p2, STORE);
131             vlib_prefetch_buffer_header (p3, STORE);
132
133             CLIB_PREFETCH (p2->data, sizeof (hdr0[0]), STORE);
134             CLIB_PREFETCH (p3->data, sizeof (hdr1[0]), STORE);
135           }
136
137           pi0 = to_next[0] = from[0];
138           pi1 = to_next[1] = from[1];
139
140           from += 2;
141           n_left_from -= 2;
142           to_next += 2;
143           n_left_to_next -= 2;
144
145           p0 = vlib_get_buffer (vm, pi0);
146           p1 = vlib_get_buffer (vm, pi1);
147
148           adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
149           adj_index1 = vnet_buffer (p1)->ip.adj_index[VLIB_TX];
150
151           adj0 = adj_get(adj_index0);
152           adj1 = adj_get(adj_index1);
153           hdr0 = vlib_buffer_get_current (p0);
154           hdr1 = vlib_buffer_get_current (p1);
155
156           /* Guess we are only writing on simple Ethernet header. */
157           vnet_rewrite_two_headers (adj0[0], adj1[0], hdr0, hdr1,
158                                    sizeof (ethernet_header_t));
159
160           /* Update packet buffer attributes/set output interface. */
161           rw_len0 = adj0[0].rewrite_header.data_bytes;
162           rw_len1 = adj1[0].rewrite_header.data_bytes;
163           vnet_buffer (p0)->mpls.save_rewrite_length = rw_len0;
164           vnet_buffer (p1)->mpls.save_rewrite_length = rw_len1;
165
166           /* Bump the adj counters for packet and bytes */
167           vlib_increment_combined_counter
168               (&adjacency_counters,
169                thread_index,
170                adj_index0,
171                1,
172                vlib_buffer_length_in_chain (vm, p0) + rw_len0);
173           vlib_increment_combined_counter
174               (&adjacency_counters,
175                thread_index,
176                adj_index1,
177                1,
178                vlib_buffer_length_in_chain (vm, p1) + rw_len1);
179
180           /* Check MTU of outgoing interface. */
181           if (PREDICT_TRUE(vlib_buffer_length_in_chain (vm, p0) <=
182                            adj0[0].rewrite_header.max_l3_packet_bytes))
183             {
184               vlib_buffer_advance(p0, -rw_len0);
185
186               vnet_buffer (p0)->sw_if_index[VLIB_TX] =
187                   adj0[0].rewrite_header.sw_if_index;
188               next0 = adj0[0].rewrite_header.next_index;
189               error0 = IP4_ERROR_NONE;
190
191               if (PREDICT_FALSE(adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
192                 vnet_feature_arc_start (mm->output_feature_arc_index,
193                                         adj0[0].rewrite_header.sw_if_index,
194                                         &next0, p0);
195             }
196           else
197             {
198               error0 = IP4_ERROR_MTU_EXCEEDED;
199               next0 = set_mpls_fragmentation (p0, adj0);
200               vlib_node_increment_counter (vm, mpls_output_node.index,
201                                            MPLS_ERROR_PKTS_NEED_FRAG,
202                                            1);
203             }
204           if (PREDICT_TRUE(vlib_buffer_length_in_chain (vm, p1) <=
205                            adj1[0].rewrite_header.max_l3_packet_bytes))
206             {
207               vlib_buffer_advance(p1, -rw_len1);
208
209               vnet_buffer (p1)->sw_if_index[VLIB_TX] =
210                   adj1[0].rewrite_header.sw_if_index;
211               next1 = adj1[0].rewrite_header.next_index;
212               error1 = IP4_ERROR_NONE;
213
214               if (PREDICT_FALSE(adj1[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
215                 vnet_feature_arc_start (mm->output_feature_arc_index,
216                                         adj1[0].rewrite_header.sw_if_index,
217                                         &next1, p1);
218             }
219           else
220             {
221               error1 = IP4_ERROR_MTU_EXCEEDED;
222               next1 = set_mpls_fragmentation (p1, adj1);
223               vlib_node_increment_counter (vm, mpls_output_node.index,
224                                            MPLS_ERROR_PKTS_NEED_FRAG,
225                                            1);
226             }
227           if (mode == MPLS_OUTPUT_MIDCHAIN_MODE)
228           {
229               adj0->sub_type.midchain.fixup_func
230                 (vm, adj0, p0,
231                  adj0->sub_type.midchain.fixup_data);
232               adj1->sub_type.midchain.fixup_func
233                 (vm, adj1, p1,
234                  adj1->sub_type.midchain.fixup_data);
235           }
236
237           p0->error = error_node->errors[error0];
238           p1->error = error_node->errors[error1];
239
240           if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED))
241             {
242               mpls_output_trace_t *tr = vlib_add_trace (vm, node,
243                                                         p0, sizeof (*tr));
244               tr->adj_index = vnet_buffer(p0)->ip.adj_index[VLIB_TX];
245               tr->flow_hash = vnet_buffer(p0)->ip.flow_hash;
246             }
247           if (PREDICT_FALSE(p1->flags & VLIB_BUFFER_IS_TRACED))
248             {
249               mpls_output_trace_t *tr = vlib_add_trace (vm, node,
250                                                         p1, sizeof (*tr));
251               tr->adj_index = vnet_buffer(p1)->ip.adj_index[VLIB_TX];
252               tr->flow_hash = vnet_buffer(p1)->ip.flow_hash;
253             }
254
255           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
256                                            to_next, n_left_to_next,
257                                            pi0, pi1, next0, next1);
258         }
259
260       while (n_left_from > 0 && n_left_to_next > 0)
261         {
262           ip_adjacency_t * adj0;
263           mpls_unicast_header_t *hdr0;
264           vlib_buffer_t * p0;
265           u32 pi0, adj_index0, next0, error0;
266           word rw_len0;
267
268           pi0 = to_next[0] = from[0];
269
270           p0 = vlib_get_buffer (vm, pi0);
271
272           adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
273
274           adj0 = adj_get(adj_index0);
275           hdr0 = vlib_buffer_get_current (p0);
276
277           /* Guess we are only writing on simple Ethernet header. */
278           vnet_rewrite_one_header (adj0[0], hdr0, 
279                                    sizeof (ethernet_header_t));
280           
281           /* Update packet buffer attributes/set output interface. */
282           rw_len0 = adj0[0].rewrite_header.data_bytes;
283           vnet_buffer (p0)->mpls.save_rewrite_length = rw_len0;
284
285           vlib_increment_combined_counter
286               (&adjacency_counters,
287                thread_index,
288                adj_index0,
289                1,
290                vlib_buffer_length_in_chain (vm, p0) + rw_len0);
291
292           /* Check MTU of outgoing interface. */
293           if (PREDICT_TRUE(vlib_buffer_length_in_chain (vm, p0) <=
294                            adj0[0].rewrite_header.max_l3_packet_bytes))
295             {
296               vlib_buffer_advance(p0, -rw_len0);
297
298               vnet_buffer (p0)->sw_if_index[VLIB_TX] =
299                   adj0[0].rewrite_header.sw_if_index;
300               next0 = adj0[0].rewrite_header.next_index;
301               error0 = IP4_ERROR_NONE;
302
303               if (PREDICT_FALSE(adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
304                 vnet_feature_arc_start (mm->output_feature_arc_index,
305                                         adj0[0].rewrite_header.sw_if_index,
306                                         &next0, p0);
307             }
308           else
309             {
310               error0 = IP4_ERROR_MTU_EXCEEDED;
311               next0 = set_mpls_fragmentation (p0, adj0);
312               vlib_node_increment_counter (vm, mpls_output_node.index,
313                                            MPLS_ERROR_PKTS_NEED_FRAG,
314                                            1);
315             }
316           if (mode == MPLS_OUTPUT_MIDCHAIN_MODE)
317           {
318               adj0->sub_type.midchain.fixup_func
319                 (vm, adj0, p0,
320                  adj0->sub_type.midchain.fixup_data);
321           }
322
323           p0->error = error_node->errors[error0];
324
325           from += 1;
326           n_left_from -= 1;
327           to_next += 1;
328           n_left_to_next -= 1;
329       
330           if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) 
331             {
332               mpls_output_trace_t *tr = vlib_add_trace (vm, node, 
333                                                         p0, sizeof (*tr));
334               tr->adj_index = vnet_buffer(p0)->ip.adj_index[VLIB_TX];
335               tr->flow_hash = vnet_buffer(p0)->ip.flow_hash;
336             }
337
338           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
339                                            to_next, n_left_to_next,
340                                            pi0, next0);
341         }
342
343       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
344     }
345   vlib_node_increment_counter (vm, mpls_output_node.index,
346                                MPLS_ERROR_PKTS_ENCAP,
347                                from_frame->n_vectors);
348
349   return from_frame->n_vectors;
350 }
351
352 static char * mpls_error_strings[] = {
353 #define mpls_error(n,s) s,
354 #include "error.def"
355 #undef mpls_error
356 };
357
358 VLIB_NODE_FN (mpls_output_node) (vlib_main_t * vm,
359              vlib_node_runtime_t * node,
360              vlib_frame_t * from_frame)
361 {
362     return (mpls_output_inline(vm, node, from_frame, MPLS_OUTPUT_MODE));
363 }
364
365 VLIB_REGISTER_NODE (mpls_output_node) = {
366   .name = "mpls-output",
367   /* Takes a vector of packets. */
368   .vector_size = sizeof (u32),
369   .n_errors = MPLS_N_ERROR,
370   .error_strings = mpls_error_strings,
371
372   .n_next_nodes = MPLS_OUTPUT_N_NEXT,
373   .next_nodes = {
374 #define _(s,n) [MPLS_OUTPUT_NEXT_##s] = n,
375     foreach_mpls_output_next
376 #undef _
377   },
378
379   .format_trace = format_mpls_output_trace,
380 };
381
382 VLIB_NODE_FN (mpls_midchain_node) (vlib_main_t * vm,
383                vlib_node_runtime_t * node,
384                vlib_frame_t * from_frame)
385 {
386     return (mpls_output_inline(vm, node, from_frame, MPLS_OUTPUT_MIDCHAIN_MODE));
387 }
388
389 VLIB_REGISTER_NODE (mpls_midchain_node) = {
390   .name = "mpls-midchain",
391   .vector_size = sizeof (u32),
392
393   .format_trace = format_mpls_output_trace,
394
395   .sibling_of = "mpls-output",
396 };
397
398 /**
399  * @brief Next index values from the MPLS incomplete adj node
400  */
401 #define foreach_mpls_adj_incomplete_next        \
402 _(DROP, "error-drop")                   \
403 _(IP4,  "ip4-arp")                      \
404 _(IP6,  "ip6-discover-neighbor")
405
406 typedef enum {
407 #define _(s,n) MPLS_ADJ_INCOMPLETE_NEXT_##s,
408   foreach_mpls_adj_incomplete_next
409 #undef _
410   MPLS_ADJ_INCOMPLETE_N_NEXT,
411 } mpls_adj_incomplete_next_t;
412
413 /**
414  * @brief A struct to hold tracing information for the MPLS label imposition
415  * node.
416  */
417 typedef struct mpls_adj_incomplete_trace_t_
418 {
419     u32 next;
420 } mpls_adj_incomplete_trace_t;
421
422
423 /**
424  * @brief Graph node for incomplete MPLS adjacency.
425  * This node will push traffic to either the v4-arp or v6-nd node
426  * based on the next-hop proto of the adj.
427  * We pay a cost for this 'routing' node, but an incomplete adj is the
428  * exception case.
429  */
430 VLIB_NODE_FN (mpls_adj_incomplete_node) (vlib_main_t * vm,
431                      vlib_node_runtime_t * node,
432                      vlib_frame_t * from_frame)
433 {
434     u32 n_left_from, next_index, * from, * to_next;
435
436   from = vlib_frame_vector_args (from_frame);
437   n_left_from = from_frame->n_vectors;
438   next_index = node->cached_next_index;
439
440   while (n_left_from > 0)
441     {
442       u32 n_left_to_next;
443
444       vlib_get_next_frame (vm, node, next_index,
445                            to_next, n_left_to_next);
446
447       while (n_left_from > 0 && n_left_to_next > 0)
448         {
449           u32 pi0, next0, adj_index0;
450           ip_adjacency_t * adj0;
451           vlib_buffer_t * p0;
452
453           pi0 = to_next[0] = from[0];
454           p0 = vlib_get_buffer (vm, pi0);
455           from += 1;
456           n_left_from -= 1;
457           to_next += 1;
458           n_left_to_next -= 1;
459
460           adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
461
462           adj0 = adj_get(adj_index0);
463
464           if (PREDICT_TRUE(FIB_PROTOCOL_IP4 == adj0->ia_nh_proto))
465           {
466               next0 = MPLS_ADJ_INCOMPLETE_NEXT_IP4;
467           }
468           else
469           {
470               next0 = MPLS_ADJ_INCOMPLETE_NEXT_IP6;
471           }              
472
473           if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) 
474           {
475               mpls_adj_incomplete_trace_t *tr =
476                   vlib_add_trace (vm, node, p0, sizeof (*tr));
477               tr->next = next0;
478           }
479
480           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
481                                            to_next, n_left_to_next,
482                                            pi0, next0);
483         }
484
485       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
486     }
487
488   return from_frame->n_vectors;
489 }
490
491 static u8 *
492 format_mpls_adj_incomplete_trace (u8 * s, va_list * args)
493 {
494     CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
495     CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
496     mpls_adj_incomplete_trace_t * t;
497     u32 indent;
498
499     t = va_arg (*args, mpls_adj_incomplete_trace_t *);
500     indent = format_get_indent (s);
501
502     s = format (s, "%Unext:%d",
503                 format_white_space, indent,
504                 t->next);
505     return (s);
506 }
507
508 VLIB_REGISTER_NODE (mpls_adj_incomplete_node) = {
509   .name = "mpls-adj-incomplete",
510   .format_trace = format_mpls_adj_incomplete_trace,
511   /* Takes a vector of packets. */
512   .vector_size = sizeof (u32),
513   .n_errors = MPLS_N_ERROR,
514   .error_strings = mpls_error_strings,
515
516   .n_next_nodes = MPLS_ADJ_INCOMPLETE_N_NEXT,
517   .next_nodes = {
518 #define _(s,n) [MPLS_ADJ_INCOMPLETE_NEXT_##s] = n,
519     foreach_mpls_adj_incomplete_next
520 #undef _
521   },
522 };
523