dpdk: Add support for Mellanox ConnectX-4 devices
[vpp.git] / src / vnet / ip / ip4_input.c
1 /*
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  * ip/ip4_input.c: IP v4 input node
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #include <vnet/ip/ip.h>
41 #include <vnet/ethernet/ethernet.h>
42 #include <vnet/ppp/ppp.h>
43 #include <vnet/hdlc/hdlc.h>
44
45 typedef struct
46 {
47   u8 packet_data[64];
48 } ip4_input_trace_t;
49
50 static u8 *
51 format_ip4_input_trace (u8 * s, va_list * va)
52 {
53   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
54   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
55   ip4_input_trace_t *t = va_arg (*va, ip4_input_trace_t *);
56
57   s = format (s, "%U",
58               format_ip4_header, t->packet_data, sizeof (t->packet_data));
59
60   return s;
61 }
62
63 typedef enum
64 {
65   IP4_INPUT_NEXT_DROP,
66   IP4_INPUT_NEXT_PUNT,
67   IP4_INPUT_NEXT_LOOKUP,
68   IP4_INPUT_NEXT_LOOKUP_MULTICAST,
69   IP4_INPUT_NEXT_ICMP_ERROR,
70   IP4_INPUT_N_NEXT,
71 } ip4_input_next_t;
72
73 /* Validate IP v4 packets and pass them either to forwarding code
74    or drop/punt exception packets. */
75 always_inline uword
76 ip4_input_inline (vlib_main_t * vm,
77                   vlib_node_runtime_t * node,
78                   vlib_frame_t * frame, int verify_checksum)
79 {
80   ip4_main_t *im = &ip4_main;
81   vnet_main_t *vnm = vnet_get_main ();
82   ip_lookup_main_t *lm = &im->lookup_main;
83   u32 n_left_from, *from, *to_next;
84   ip4_input_next_t next_index;
85   vlib_node_runtime_t *error_node =
86     vlib_node_get_runtime (vm, ip4_input_node.index);
87   vlib_simple_counter_main_t *cm;
88   u32 cpu_index = os_get_cpu_number ();
89
90   from = vlib_frame_vector_args (frame);
91   n_left_from = frame->n_vectors;
92   next_index = node->cached_next_index;
93
94   if (node->flags & VLIB_NODE_FLAG_TRACE)
95     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
96                                    /* stride */ 1,
97                                    sizeof (ip4_input_trace_t));
98
99   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
100                          VNET_INTERFACE_COUNTER_IP4);
101
102   while (n_left_from > 0)
103     {
104       u32 n_left_to_next;
105
106       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
107
108       while (n_left_from >= 4 && n_left_to_next >= 2)
109         {
110           vlib_buffer_t *p0, *p1;
111           ip4_header_t *ip0, *ip1;
112           u32 sw_if_index0, pi0, ip_len0, cur_len0, next0;
113           u32 sw_if_index1, pi1, ip_len1, cur_len1, next1;
114           i32 len_diff0, len_diff1;
115           u8 error0, error1, arc0, arc1;
116
117           /* Prefetch next iteration. */
118           {
119             vlib_buffer_t *p2, *p3;
120
121             p2 = vlib_get_buffer (vm, from[2]);
122             p3 = vlib_get_buffer (vm, from[3]);
123
124             vlib_prefetch_buffer_header (p2, LOAD);
125             vlib_prefetch_buffer_header (p3, LOAD);
126
127             CLIB_PREFETCH (p2->data, sizeof (ip0[0]), LOAD);
128             CLIB_PREFETCH (p3->data, sizeof (ip1[0]), LOAD);
129           }
130
131           to_next[0] = pi0 = from[0];
132           to_next[1] = pi1 = from[1];
133           from += 2;
134           to_next += 2;
135           n_left_from -= 2;
136           n_left_to_next -= 2;
137
138           p0 = vlib_get_buffer (vm, pi0);
139           p1 = vlib_get_buffer (vm, pi1);
140
141           ip0 = vlib_buffer_get_current (p0);
142           ip1 = vlib_buffer_get_current (p1);
143
144           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
145           sw_if_index1 = vnet_buffer (p1)->sw_if_index[VLIB_RX];
146
147           error0 = error1 = IP4_ERROR_NONE;
148
149           if (PREDICT_FALSE (ip4_address_is_multicast (&ip0->dst_address)))
150             {
151               arc0 = lm->mcast_feature_arc_index;
152               next0 = IP4_INPUT_NEXT_LOOKUP_MULTICAST;
153             }
154           else
155             {
156               arc0 = lm->ucast_feature_arc_index;
157               next0 = IP4_INPUT_NEXT_LOOKUP;
158               if (PREDICT_FALSE (ip0->ttl < 1))
159                 error0 = IP4_ERROR_TIME_EXPIRED;
160             }
161
162           if (PREDICT_FALSE (ip4_address_is_multicast (&ip1->dst_address)))
163             {
164               arc1 = lm->mcast_feature_arc_index;
165               next1 = IP4_INPUT_NEXT_LOOKUP_MULTICAST;
166             }
167           else
168             {
169               arc1 = lm->ucast_feature_arc_index;
170               next1 = IP4_INPUT_NEXT_LOOKUP;
171               if (PREDICT_FALSE (ip1->ttl < 1))
172                 error1 = IP4_ERROR_TIME_EXPIRED;
173             }
174
175           vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
176           vnet_buffer (p1)->ip.adj_index[VLIB_RX] = ~0;
177
178           vnet_feature_arc_start (arc0, sw_if_index0, &next0, p0);
179           vnet_feature_arc_start (arc1, sw_if_index1, &next1, p1);
180
181           vlib_increment_simple_counter (cm, cpu_index, sw_if_index0, 1);
182           vlib_increment_simple_counter (cm, cpu_index, sw_if_index1, 1);
183
184           /* Punt packets with options or wrong version. */
185           if (PREDICT_FALSE (ip0->ip_version_and_header_length != 0x45))
186             error0 = (ip0->ip_version_and_header_length & 0xf) != 5 ?
187               IP4_ERROR_OPTIONS : IP4_ERROR_VERSION;
188
189           if (PREDICT_FALSE (ip1->ip_version_and_header_length != 0x45))
190             error1 = (ip1->ip_version_and_header_length & 0xf) != 5 ?
191               IP4_ERROR_OPTIONS : IP4_ERROR_VERSION;
192
193           /* Verify header checksum. */
194           if (verify_checksum)
195             {
196               ip_csum_t sum0, sum1;
197
198               ip4_partial_header_checksum_x1 (ip0, sum0);
199               ip4_partial_header_checksum_x1 (ip1, sum1);
200
201               error0 = 0xffff != ip_csum_fold (sum0) ?
202                 IP4_ERROR_BAD_CHECKSUM : error0;
203               error1 = 0xffff != ip_csum_fold (sum1) ?
204                 IP4_ERROR_BAD_CHECKSUM : error1;
205             }
206
207           /* Drop fragmentation offset 1 packets. */
208           error0 = ip4_get_fragment_offset (ip0) == 1 ?
209             IP4_ERROR_FRAGMENT_OFFSET_ONE : error0;
210           error1 = ip4_get_fragment_offset (ip1) == 1 ?
211             IP4_ERROR_FRAGMENT_OFFSET_ONE : error1;
212
213           /* Verify lengths. */
214           ip_len0 = clib_net_to_host_u16 (ip0->length);
215           ip_len1 = clib_net_to_host_u16 (ip1->length);
216
217           /* IP length must be at least minimal IP header. */
218           error0 = ip_len0 < sizeof (ip0[0]) ? IP4_ERROR_TOO_SHORT : error0;
219           error1 = ip_len1 < sizeof (ip1[0]) ? IP4_ERROR_TOO_SHORT : error1;
220
221           cur_len0 = vlib_buffer_length_in_chain (vm, p0);
222           cur_len1 = vlib_buffer_length_in_chain (vm, p1);
223
224           len_diff0 = cur_len0 - ip_len0;
225           len_diff1 = cur_len1 - ip_len1;
226
227           error0 = len_diff0 < 0 ? IP4_ERROR_BAD_LENGTH : error0;
228           error1 = len_diff1 < 0 ? IP4_ERROR_BAD_LENGTH : error1;
229
230           p0->error = error_node->errors[error0];
231           p1->error = error_node->errors[error1];
232
233           if (PREDICT_FALSE (error0 != IP4_ERROR_NONE))
234             {
235               if (error0 == IP4_ERROR_TIME_EXPIRED)
236                 {
237                   icmp4_error_set_vnet_buffer (p0, ICMP4_time_exceeded,
238                                                ICMP4_time_exceeded_ttl_exceeded_in_transit,
239                                                0);
240                   next0 = IP4_INPUT_NEXT_ICMP_ERROR;
241                 }
242               else
243                 next0 = error0 != IP4_ERROR_OPTIONS ?
244                   IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
245             }
246           if (PREDICT_FALSE (error1 != IP4_ERROR_NONE))
247             {
248               if (error1 == IP4_ERROR_TIME_EXPIRED)
249                 {
250                   icmp4_error_set_vnet_buffer (p1, ICMP4_time_exceeded,
251                                                ICMP4_time_exceeded_ttl_exceeded_in_transit,
252                                                0);
253                   next1 = IP4_INPUT_NEXT_ICMP_ERROR;
254                 }
255               else
256                 next1 = error1 != IP4_ERROR_OPTIONS ?
257                   IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
258             }
259
260           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
261                                            to_next, n_left_to_next,
262                                            pi0, pi1, next0, next1);
263         }
264       while (n_left_from > 0 && n_left_to_next > 0)
265         {
266           vlib_buffer_t *p0;
267           ip4_header_t *ip0;
268           u32 sw_if_index0, pi0, ip_len0, cur_len0, next0;
269           i32 len_diff0;
270           u8 error0, arc0;
271
272           pi0 = from[0];
273           to_next[0] = pi0;
274           from += 1;
275           to_next += 1;
276           n_left_from -= 1;
277           n_left_to_next -= 1;
278
279           p0 = vlib_get_buffer (vm, pi0);
280           ip0 = vlib_buffer_get_current (p0);
281
282           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
283
284           error0 = IP4_ERROR_NONE;
285
286           if (PREDICT_FALSE (ip4_address_is_multicast (&ip0->dst_address)))
287             {
288               arc0 = lm->mcast_feature_arc_index;
289               next0 = IP4_INPUT_NEXT_LOOKUP_MULTICAST;
290             }
291           else
292             {
293               arc0 = lm->ucast_feature_arc_index;
294               next0 = IP4_INPUT_NEXT_LOOKUP;
295               if (PREDICT_FALSE (ip0->ttl < 1))
296                 error0 = IP4_ERROR_TIME_EXPIRED;
297             }
298
299           vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
300           vnet_feature_arc_start (arc0, sw_if_index0, &next0, p0);
301
302           vlib_increment_simple_counter (cm, cpu_index, sw_if_index0, 1);
303
304           /* Punt packets with options or wrong version. */
305           if (PREDICT_FALSE (ip0->ip_version_and_header_length != 0x45))
306             error0 = (ip0->ip_version_and_header_length & 0xf) != 5 ?
307               IP4_ERROR_OPTIONS : IP4_ERROR_VERSION;
308
309           /* Verify header checksum. */
310           if (verify_checksum)
311             {
312               ip_csum_t sum0;
313
314               ip4_partial_header_checksum_x1 (ip0, sum0);
315               error0 =
316                 0xffff !=
317                 ip_csum_fold (sum0) ? IP4_ERROR_BAD_CHECKSUM : error0;
318             }
319
320           /* Drop fragmentation offset 1 packets. */
321           error0 =
322             ip4_get_fragment_offset (ip0) ==
323             1 ? IP4_ERROR_FRAGMENT_OFFSET_ONE : error0;
324
325           /* Verify lengths. */
326           ip_len0 = clib_net_to_host_u16 (ip0->length);
327
328           /* IP length must be at least minimal IP header. */
329           error0 = ip_len0 < sizeof (ip0[0]) ? IP4_ERROR_TOO_SHORT : error0;
330
331           cur_len0 = vlib_buffer_length_in_chain (vm, p0);
332           len_diff0 = cur_len0 - ip_len0;
333           error0 = len_diff0 < 0 ? IP4_ERROR_BAD_LENGTH : error0;
334
335           p0->error = error_node->errors[error0];
336           if (PREDICT_FALSE (error0 != IP4_ERROR_NONE))
337             {
338               if (error0 == IP4_ERROR_TIME_EXPIRED)
339                 {
340                   icmp4_error_set_vnet_buffer (p0, ICMP4_time_exceeded,
341                                                ICMP4_time_exceeded_ttl_exceeded_in_transit,
342                                                0);
343                   next0 = IP4_INPUT_NEXT_ICMP_ERROR;
344                 }
345               else
346                 next0 = error0 != IP4_ERROR_OPTIONS ?
347                   IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
348             }
349
350           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
351                                            to_next, n_left_to_next,
352                                            pi0, next0);
353         }
354
355       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
356     }
357
358   return frame->n_vectors;
359 }
360
361 /** \brief IPv4 input node.
362     @node ip4-input
363
364     This is the IPv4 input node: validates ip4 header checksums,
365     verifies ip header lengths, discards pkts with expired TTLs,
366     and sends pkts to the set of ip feature nodes configured on
367     the rx interface.
368
369     @param vm vlib_main_t corresponding to the current thread
370     @param node vlib_node_runtime_t
371     @param frame vlib_frame_t whose contents should be dispatched
372
373     @par Graph mechanics: buffer metadata, next index usage
374
375     @em Uses:
376     - vnet_feature_config_main_t cm corresponding to each pkt's dst address unicast /
377       multicast status.
378     - <code>b->current_config_index</code> corresponding to each pkt's
379       rx sw_if_index.
380          - This sets the per-packet graph trajectory, ensuring that
381            each packet visits the per-interface features in order.
382
383     - <code>vnet_buffer(b)->sw_if_index[VLIB_RX]</code>
384         - Indicates the @c sw_if_index value of the interface that the
385           packet was received on.
386
387     @em Sets:
388     - <code>vnet_buffer(b)->ip.adj_index[VLIB_TX]</code>
389         - The lookup result adjacency index.
390
391     <em>Next Indices:</em>
392     - Dispatches pkts to the (first) feature node:
393       <code> vnet_get_config_data (... &next0 ...); </code>
394       or @c error-drop
395 */
396 static uword
397 ip4_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
398 {
399   return ip4_input_inline (vm, node, frame, /* verify_checksum */ 1);
400 }
401
402 static uword
403 ip4_input_no_checksum (vlib_main_t * vm,
404                        vlib_node_runtime_t * node, vlib_frame_t * frame)
405 {
406   return ip4_input_inline (vm, node, frame, /* verify_checksum */ 0);
407 }
408
409 static char *ip4_error_strings[] = {
410 #define _(sym,string) string,
411   foreach_ip4_error
412 #undef _
413 };
414
415 /* *INDENT-OFF* */
416 VLIB_REGISTER_NODE (ip4_input_node) = {
417   .function = ip4_input,
418   .name = "ip4-input",
419   .vector_size = sizeof (u32),
420
421   .n_errors = IP4_N_ERROR,
422   .error_strings = ip4_error_strings,
423
424   .n_next_nodes = IP4_INPUT_N_NEXT,
425   .next_nodes = {
426     [IP4_INPUT_NEXT_DROP] = "error-drop",
427     [IP4_INPUT_NEXT_PUNT] = "error-punt",
428     [IP4_INPUT_NEXT_LOOKUP] = "ip4-lookup",
429     [IP4_INPUT_NEXT_LOOKUP_MULTICAST] = "ip4-lookup-multicast",
430     [IP4_INPUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
431   },
432
433   .format_buffer = format_ip4_header,
434   .format_trace = format_ip4_input_trace,
435 };
436 /* *INDENT-ON* */
437
438 VLIB_NODE_FUNCTION_MULTIARCH (ip4_input_node, ip4_input);
439
440 /* *INDENT-OFF* */
441 VLIB_REGISTER_NODE (ip4_input_no_checksum_node,static) = {
442   .function = ip4_input_no_checksum,
443   .name = "ip4-input-no-checksum",
444   .vector_size = sizeof (u32),
445
446   .n_next_nodes = IP4_INPUT_N_NEXT,
447   .next_nodes = {
448     [IP4_INPUT_NEXT_DROP] = "error-drop",
449     [IP4_INPUT_NEXT_PUNT] = "error-punt",
450     [IP4_INPUT_NEXT_LOOKUP] = "ip4-lookup",
451     [IP4_INPUT_NEXT_LOOKUP_MULTICAST] = "ip4-lookup-multicast",
452     [IP4_INPUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
453   },
454
455   .format_buffer = format_ip4_header,
456   .format_trace = format_ip4_input_trace,
457 };
458 /* *INDENT-ON* */
459
460 VLIB_NODE_FUNCTION_MULTIARCH (ip4_input_no_checksum_node,
461                               ip4_input_no_checksum);
462
463 static clib_error_t *
464 ip4_init (vlib_main_t * vm)
465 {
466   clib_error_t *error;
467
468   ethernet_register_input_type (vm, ETHERNET_TYPE_IP4, ip4_input_node.index);
469   ppp_register_input_protocol (vm, PPP_PROTOCOL_ip4, ip4_input_node.index);
470   hdlc_register_input_protocol (vm, HDLC_PROTOCOL_ip4, ip4_input_node.index);
471
472   {
473     pg_node_t *pn;
474     pn = pg_get_node (ip4_input_node.index);
475     pn->unformat_edit = unformat_pg_ip4_header;
476     pn = pg_get_node (ip4_input_no_checksum_node.index);
477     pn->unformat_edit = unformat_pg_ip4_header;
478   }
479
480   if ((error = vlib_call_init_function (vm, ip4_cli_init)))
481     return error;
482
483   if ((error = vlib_call_init_function (vm, ip4_source_check_init)))
484     return error;
485
486   if ((error = vlib_call_init_function
487        (vm, ip4_source_and_port_range_check_init)))
488     return error;
489
490   /* Set flow hash to something non-zero. */
491   ip4_main.flow_hash_seed = 0xdeadbeef;
492
493   /* Default TTL for packets we generate. */
494   ip4_main.host_config.ttl = 64;
495
496   return error;
497 }
498
499 VLIB_INIT_FUNCTION (ip4_init);
500
501 /*
502  * fd.io coding-style-patch-verification: ON
503  *
504  * Local Variables:
505  * eval: (c-set-style "gnu")
506  * End:
507  */