Document more nodes
[vpp.git] / vnet / 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   u8 packet_data[64];
47 } ip4_input_trace_t;
48
49 static u8 * format_ip4_input_trace (u8 * s, va_list * va)
50 {
51   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
52   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
53   ip4_input_trace_t * t = va_arg (*va, ip4_input_trace_t *);
54
55   s = format (s, "%U",
56               format_ip4_header,
57               t->packet_data, sizeof (t->packet_data));
58
59   return s;
60 }
61
62 typedef enum {
63   IP4_INPUT_NEXT_DROP,
64   IP4_INPUT_NEXT_PUNT,
65   IP4_INPUT_NEXT_LOOKUP,
66   IP4_INPUT_NEXT_LOOKUP_MULTICAST,
67   IP4_INPUT_NEXT_ICMP_ERROR,
68   IP4_INPUT_N_NEXT,
69 } ip4_input_next_t;
70
71 /* Validate IP v4 packets and pass them either to forwarding code
72    or drop/punt exception packets. */
73 always_inline uword
74 ip4_input_inline (vlib_main_t * vm,
75                   vlib_node_runtime_t * node,
76                   vlib_frame_t * frame,
77                   int verify_checksum)
78 {
79   ip4_main_t * im = &ip4_main;
80   vnet_main_t * vnm = vnet_get_main();
81   ip_lookup_main_t * lm = &im->lookup_main;
82   u32 n_left_from, * from, * to_next;
83   ip4_input_next_t next_index;
84   vlib_node_runtime_t * error_node = vlib_node_get_runtime (vm, ip4_input_node.index);
85   vlib_simple_counter_main_t * cm;
86   u32 cpu_index = os_get_cpu_number();
87
88   from = vlib_frame_vector_args (frame);
89   n_left_from = frame->n_vectors;
90   next_index = node->cached_next_index;
91
92   if (node->flags & VLIB_NODE_FLAG_TRACE)
93     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
94                                    /* stride */ 1,
95                                    sizeof (ip4_input_trace_t));
96
97   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
98                          VNET_INTERFACE_COUNTER_IP4);
99
100   while (n_left_from > 0)
101     {
102       u32 n_left_to_next;
103
104       vlib_get_next_frame (vm, node, next_index,
105                            to_next, n_left_to_next);
106
107       while (n_left_from >= 4 && n_left_to_next >= 2)
108         {
109           vlib_buffer_t * p0, * p1;
110           ip4_header_t * ip0, * ip1;
111           ip_config_main_t * cm0, * cm1;
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, cast0, cast1;
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           cast0 = ip4_address_is_multicast (&ip0->dst_address) ? VNET_MULTICAST : VNET_UNICAST;
148           cast1 = ip4_address_is_multicast (&ip1->dst_address) ? VNET_MULTICAST : VNET_UNICAST;
149
150           cm0 = lm->rx_config_mains + cast0;
151           cm1 = lm->rx_config_mains + cast1;
152
153           p0->current_config_index = vec_elt (cm0->config_index_by_sw_if_index, sw_if_index0);
154           p1->current_config_index = vec_elt (cm1->config_index_by_sw_if_index, sw_if_index1);
155
156           vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
157           vnet_buffer (p1)->ip.adj_index[VLIB_RX] = ~0;
158
159           vnet_get_config_data (&cm0->config_main,
160                                 &p0->current_config_index,
161                                 &next0,
162                                 /* # bytes of config data */ 0);
163           vnet_get_config_data (&cm1->config_main,
164                                 &p1->current_config_index,
165                                 &next1,
166                                 /* # bytes of config data */ 0);
167
168           vlib_increment_simple_counter (cm, cpu_index, sw_if_index0, 1);
169           vlib_increment_simple_counter (cm, cpu_index, sw_if_index1, 1);
170
171           error0 = error1 = IP4_ERROR_NONE;
172
173           /* Punt packets with options. */
174           error0 = (ip0->ip_version_and_header_length & 0xf) != 5 ? IP4_ERROR_OPTIONS : error0;
175           error1 = (ip1->ip_version_and_header_length & 0xf) != 5 ? IP4_ERROR_OPTIONS : error1;
176
177           /* Version != 4?  Drop it. */
178           error0 = (ip0->ip_version_and_header_length >> 4) != 4 ? IP4_ERROR_VERSION : error0;
179           error1 = (ip1->ip_version_and_header_length >> 4) != 4 ? IP4_ERROR_VERSION : error1;
180
181           /* Verify header checksum. */
182           if (verify_checksum)
183             {
184               ip_csum_t sum0, sum1;
185
186               ip4_partial_header_checksum_x1 (ip0, sum0);
187               ip4_partial_header_checksum_x1 (ip1, sum1);
188
189               error0 = 0xffff != ip_csum_fold (sum0) ? IP4_ERROR_BAD_CHECKSUM : error0;
190               error1 = 0xffff != ip_csum_fold (sum1) ? IP4_ERROR_BAD_CHECKSUM : error1;
191             }
192
193           /* Drop fragmentation offset 1 packets. */
194           error0 = ip4_get_fragment_offset (ip0) == 1 ? IP4_ERROR_FRAGMENT_OFFSET_ONE : error0;
195           error1 = ip4_get_fragment_offset (ip1) == 1 ? IP4_ERROR_FRAGMENT_OFFSET_ONE : error1;
196
197           /* TTL < 1? Drop it. */
198           error0 = (ip0->ttl < 1 && cast0 == VNET_UNICAST) ? IP4_ERROR_TIME_EXPIRED : error0;
199           error1 = (ip1->ttl < 1 && cast1 == VNET_UNICAST) ? IP4_ERROR_TIME_EXPIRED : error1;
200
201           /* Verify lengths. */
202           ip_len0 = clib_net_to_host_u16 (ip0->length);
203           ip_len1 = clib_net_to_host_u16 (ip1->length);
204
205           /* IP length must be at least minimal IP header. */
206           error0 = ip_len0 < sizeof (ip0[0]) ? IP4_ERROR_TOO_SHORT : error0;
207           error1 = ip_len1 < sizeof (ip1[0]) ? IP4_ERROR_TOO_SHORT : error1;
208
209           cur_len0 = vlib_buffer_length_in_chain (vm, p0);
210           cur_len1 = vlib_buffer_length_in_chain (vm, p1);
211
212           len_diff0 = cur_len0 - ip_len0;
213           len_diff1 = cur_len1 - ip_len1;
214
215           error0 = len_diff0 < 0 ? IP4_ERROR_BAD_LENGTH : error0;
216           error1 = len_diff1 < 0 ? IP4_ERROR_BAD_LENGTH : error1;
217
218           p0->error = error_node->errors[error0];
219           p1->error = error_node->errors[error1];
220
221       if (PREDICT_FALSE(error0 != IP4_ERROR_NONE))
222         {
223           if (error0 == IP4_ERROR_TIME_EXPIRED) {
224             icmp4_error_set_vnet_buffer(p0, ICMP4_time_exceeded,
225                                         ICMP4_time_exceeded_ttl_exceeded_in_transit, 0);
226             next0 = IP4_INPUT_NEXT_ICMP_ERROR;
227           } else
228             next0 = error0 != IP4_ERROR_OPTIONS ? IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
229         }
230       if (PREDICT_FALSE(error1 != IP4_ERROR_NONE))
231         {
232           if (error1 == IP4_ERROR_TIME_EXPIRED) {
233             icmp4_error_set_vnet_buffer(p1, ICMP4_time_exceeded,
234                                         ICMP4_time_exceeded_ttl_exceeded_in_transit, 0);
235             next1 = IP4_INPUT_NEXT_ICMP_ERROR;
236           } else
237             next1 = error1 != IP4_ERROR_OPTIONS ? IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
238         }
239
240           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
241                                            to_next, n_left_to_next,
242                                            pi0, pi1, next0, next1);
243         }    
244       while (n_left_from > 0 && n_left_to_next > 0)
245         {
246           vlib_buffer_t * p0;
247           ip4_header_t * ip0;
248           ip_config_main_t * cm0;
249           u32 sw_if_index0, pi0, ip_len0, cur_len0, next0;
250           i32 len_diff0;
251           u8 error0, cast0;
252
253           pi0 = from[0];
254           to_next[0] = pi0;
255           from += 1;
256           to_next += 1;
257           n_left_from -= 1;
258           n_left_to_next -= 1;
259
260           p0 = vlib_get_buffer (vm, pi0);
261           ip0 = vlib_buffer_get_current (p0);
262
263           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
264
265           cast0 = ip4_address_is_multicast (&ip0->dst_address) ? VNET_MULTICAST : VNET_UNICAST;
266           cm0 = lm->rx_config_mains + cast0;
267           p0->current_config_index = vec_elt (cm0->config_index_by_sw_if_index, sw_if_index0);
268           vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
269           vnet_get_config_data (&cm0->config_main,
270                                 &p0->current_config_index,
271                                 &next0,
272                                 /* # bytes of config data */ 0);
273
274           vlib_increment_simple_counter (cm, cpu_index, sw_if_index0, 1);
275
276           error0 = IP4_ERROR_NONE;
277
278           /* Punt packets with options. */
279           error0 = (ip0->ip_version_and_header_length & 0xf) != 5 ? IP4_ERROR_OPTIONS : error0;
280
281           /* Version != 4?  Drop it. */
282           error0 = (ip0->ip_version_and_header_length >> 4) != 4 ? IP4_ERROR_VERSION : error0;
283
284           /* Verify header checksum. */
285           if (verify_checksum)
286             {
287               ip_csum_t sum0;
288
289               ip4_partial_header_checksum_x1 (ip0, sum0);
290               error0 = 0xffff != ip_csum_fold (sum0) ? IP4_ERROR_BAD_CHECKSUM : error0;
291             }
292
293           /* Drop fragmentation offset 1 packets. */
294           error0 = ip4_get_fragment_offset (ip0) == 1 ? IP4_ERROR_FRAGMENT_OFFSET_ONE : error0;
295
296           /* TTL < 1? Drop it. */
297           error0 = (ip0->ttl < 1 && cast0 == VNET_UNICAST) ? IP4_ERROR_TIME_EXPIRED : error0;
298
299           /* Verify lengths. */
300           ip_len0 = clib_net_to_host_u16 (ip0->length);
301
302           /* IP length must be at least minimal IP header. */
303           error0 = ip_len0 < sizeof (ip0[0]) ? IP4_ERROR_TOO_SHORT : error0;
304
305           cur_len0 = vlib_buffer_length_in_chain (vm, p0);
306           len_diff0 = cur_len0 - ip_len0;
307           error0 = len_diff0 < 0 ? IP4_ERROR_BAD_LENGTH : error0;
308
309           p0->error = error_node->errors[error0];
310       if (PREDICT_FALSE(error0 != IP4_ERROR_NONE))
311         {
312           if (error0 == IP4_ERROR_TIME_EXPIRED) {
313             icmp4_error_set_vnet_buffer(p0, ICMP4_time_exceeded,
314                                         ICMP4_time_exceeded_ttl_exceeded_in_transit, 0);
315             next0 = IP4_INPUT_NEXT_ICMP_ERROR;
316           } else
317             next0 = error0 != IP4_ERROR_OPTIONS ? IP4_INPUT_NEXT_DROP : IP4_INPUT_NEXT_PUNT;
318         }
319
320           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
321                                            to_next, n_left_to_next,
322                                            pi0, next0);
323         }
324
325       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
326     }
327
328   return frame->n_vectors;
329 }
330
331 /** \brief IPv4 input node.
332     @node ip4-input
333
334     This is the IPv4 input node: validates ip4 header checksums,
335     verifies ip header lengths, discards pkts with expired TTLs,
336     and sends pkts to the set of ip feature nodes configured on
337     the rx interface.
338
339     @param vm vlib_main_t corresponding to the current thread
340     @param node vlib_node_runtime_t
341     @param frame vlib_frame_t whose contents should be dispatched
342
343     @par Graph mechanics: buffer metadata, next index usage
344
345     @em Uses:
346     - ip_config_main_t cm corresponding to each pkt's dst address unicast / 
347       multicast status.
348     - <code>b->current_config_index</code> corresponding to each pkt's
349       rx sw_if_index. 
350          - This sets the per-packet graph trajectory, ensuring that
351            each packet visits the per-interface features in order.
352
353     - <code>vnet_buffer(b)->sw_if_index[VLIB_RX]</code>
354         - Indicates the @c sw_if_index value of the interface that the
355           packet was received on.
356
357     @em Sets:
358     - <code>vnet_buffer(b)->ip.adj_index[VLIB_TX]</code>
359         - The lookup result adjacency index.
360
361     <em>Next Indices:</em>
362     - Dispatches pkts to the (first) feature node:
363       <code> vnet_get_config_data (... &next0 ...); </code>
364       or @c error-drop 
365 */
366 static uword
367 ip4_input (vlib_main_t * vm,
368            vlib_node_runtime_t * node,
369            vlib_frame_t * frame)
370 {
371   return ip4_input_inline (vm, node, frame, /* verify_checksum */ 1);
372 }
373
374 static uword
375 ip4_input_no_checksum (vlib_main_t * vm,
376                        vlib_node_runtime_t * node,
377                        vlib_frame_t * frame)
378 {
379   return ip4_input_inline (vm, node, frame, /* verify_checksum */ 0);
380 }
381
382 static char * ip4_error_strings[] = {
383 #define _(sym,string) string,
384   foreach_ip4_error
385 #undef _
386 };
387
388 VLIB_REGISTER_NODE (ip4_input_node) = {
389   .function = ip4_input,
390   .name = "ip4-input",
391   .vector_size = sizeof (u32),
392
393   .n_errors = IP4_N_ERROR,
394   .error_strings = ip4_error_strings,
395
396   .n_next_nodes = IP4_INPUT_N_NEXT,
397   .next_nodes = {
398     [IP4_INPUT_NEXT_DROP] = "error-drop",
399     [IP4_INPUT_NEXT_PUNT] = "error-punt",
400     [IP4_INPUT_NEXT_LOOKUP] = "ip4-lookup",
401     [IP4_INPUT_NEXT_LOOKUP_MULTICAST] = "ip4-lookup-multicast",
402     [IP4_INPUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
403   },
404
405   .format_buffer = format_ip4_header,
406   .format_trace = format_ip4_input_trace,
407 };
408
409 VLIB_NODE_FUNCTION_MULTIARCH (ip4_input_node, ip4_input)
410
411 VLIB_REGISTER_NODE (ip4_input_no_checksum_node,static) = {
412   .function = ip4_input_no_checksum,
413   .name = "ip4-input-no-checksum",
414   .vector_size = sizeof (u32),
415
416   .n_next_nodes = IP4_INPUT_N_NEXT,
417   .next_nodes = {
418     [IP4_INPUT_NEXT_DROP] = "error-drop",
419     [IP4_INPUT_NEXT_PUNT] = "error-punt",
420     [IP4_INPUT_NEXT_LOOKUP] = "ip4-lookup",
421     [IP4_INPUT_NEXT_LOOKUP_MULTICAST] = "ip4-lookup-multicast",
422     [IP4_INPUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
423   },
424
425   .format_buffer = format_ip4_header,
426   .format_trace = format_ip4_input_trace,
427 };
428
429 VLIB_NODE_FUNCTION_MULTIARCH (ip4_input_no_checksum_node, ip4_input_no_checksum)
430
431 static clib_error_t * ip4_init (vlib_main_t * vm)
432 {
433   clib_error_t * error;
434
435   ethernet_register_input_type (vm, ETHERNET_TYPE_IP4,
436                                 ip4_input_node.index);
437   ppp_register_input_protocol (vm, PPP_PROTOCOL_ip4,
438                                ip4_input_node.index);
439   hdlc_register_input_protocol (vm, HDLC_PROTOCOL_ip4,
440                                 ip4_input_node.index);
441
442   {
443     pg_node_t * pn;
444     pn = pg_get_node (ip4_input_node.index);
445     pn->unformat_edit = unformat_pg_ip4_header;
446     pn = pg_get_node (ip4_input_no_checksum_node.index);
447     pn->unformat_edit = unformat_pg_ip4_header;
448   }
449
450   if ((error = vlib_call_init_function (vm, ip4_cli_init)))
451     return error;
452
453   if ((error = vlib_call_init_function (vm, ip4_source_check_init)))
454     return error;
455
456   /* Set flow hash to something non-zero. */
457   ip4_main.flow_hash_seed = 0xdeadbeef;
458
459   /* Default TTL for packets we generate. */
460   ip4_main.host_config.ttl = 64;
461
462   return error;
463 }
464
465 VLIB_INIT_FUNCTION (ip4_init);