IPv4/6 reassembly
[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/ip4_input.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 /* Validate IP v4 packets and pass them either to forwarding code
64    or drop/punt exception packets. */
65 always_inline uword
66 ip4_input_inline (vlib_main_t * vm,
67                   vlib_node_runtime_t * node,
68                   vlib_frame_t * frame, int verify_checksum)
69 {
70   ip4_main_t *im = &ip4_main;
71   vnet_main_t *vnm = vnet_get_main ();
72   ip_lookup_main_t *lm = &im->lookup_main;
73   u32 n_left_from, *from, *to_next;
74   ip4_input_next_t next_index;
75   vlib_node_runtime_t *error_node =
76     vlib_node_get_runtime (vm, ip4_input_node.index);
77   vlib_simple_counter_main_t *cm;
78   u32 thread_index = vlib_get_thread_index ();
79
80   from = vlib_frame_vector_args (frame);
81   n_left_from = frame->n_vectors;
82   next_index = node->cached_next_index;
83
84   if (node->flags & VLIB_NODE_FLAG_TRACE)
85     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
86                                    /* stride */ 1,
87                                    sizeof (ip4_input_trace_t));
88
89   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
90                          VNET_INTERFACE_COUNTER_IP4);
91
92   while (n_left_from > 0)
93     {
94       u32 n_left_to_next;
95
96       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
97
98       while (n_left_from >= 4 && n_left_to_next >= 2)
99         {
100           vlib_buffer_t *p0, *p1;
101           ip4_header_t *ip0, *ip1;
102           u32 sw_if_index0, pi0, next0;
103           u32 sw_if_index1, pi1, next1;
104           u8 arc0, arc1;
105
106           /* Prefetch next iteration. */
107           {
108             vlib_buffer_t *p2, *p3;
109
110             p2 = vlib_get_buffer (vm, from[2]);
111             p3 = vlib_get_buffer (vm, from[3]);
112
113             vlib_prefetch_buffer_header (p2, LOAD);
114             vlib_prefetch_buffer_header (p3, LOAD);
115
116             CLIB_PREFETCH (p2->data, sizeof (ip0[0]), LOAD);
117             CLIB_PREFETCH (p3->data, sizeof (ip1[0]), LOAD);
118           }
119
120           to_next[0] = pi0 = from[0];
121           to_next[1] = pi1 = from[1];
122           from += 2;
123           to_next += 2;
124           n_left_from -= 2;
125           n_left_to_next -= 2;
126
127           p0 = vlib_get_buffer (vm, pi0);
128           p1 = vlib_get_buffer (vm, pi1);
129
130           ip0 = vlib_buffer_get_current (p0);
131           ip1 = vlib_buffer_get_current (p1);
132
133           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
134           sw_if_index1 = vnet_buffer (p1)->sw_if_index[VLIB_RX];
135
136           if (PREDICT_FALSE (ip4_address_is_multicast (&ip0->dst_address)))
137             {
138               arc0 = lm->mcast_feature_arc_index;
139               next0 = IP4_INPUT_NEXT_LOOKUP_MULTICAST;
140             }
141           else
142             {
143               arc0 = lm->ucast_feature_arc_index;
144               next0 = IP4_INPUT_NEXT_LOOKUP;
145             }
146
147           if (PREDICT_FALSE (ip4_address_is_multicast (&ip1->dst_address)))
148             {
149               arc1 = lm->mcast_feature_arc_index;
150               next1 = IP4_INPUT_NEXT_LOOKUP_MULTICAST;
151             }
152           else
153             {
154               arc1 = lm->ucast_feature_arc_index;
155               next1 = IP4_INPUT_NEXT_LOOKUP;
156             }
157
158           vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
159           vnet_buffer (p1)->ip.adj_index[VLIB_RX] = ~0;
160
161           vnet_feature_arc_start (arc0, sw_if_index0, &next0, p0);
162           vnet_feature_arc_start (arc1, sw_if_index1, &next1, p1);
163
164           vlib_increment_simple_counter (cm, thread_index, sw_if_index0, 1);
165           vlib_increment_simple_counter (cm, thread_index, sw_if_index1, 1);
166           ip4_input_check_x2 (vm, error_node,
167                               p0, p1, ip0, ip1,
168                               &next0, &next1, verify_checksum);
169
170           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
171                                            to_next, n_left_to_next,
172                                            pi0, pi1, next0, next1);
173         }
174       while (n_left_from > 0 && n_left_to_next > 0)
175         {
176           vlib_buffer_t *p0;
177           ip4_header_t *ip0;
178           u32 sw_if_index0, pi0, next0;
179           u8 arc0;
180
181           pi0 = from[0];
182           to_next[0] = pi0;
183           from += 1;
184           to_next += 1;
185           n_left_from -= 1;
186           n_left_to_next -= 1;
187
188           p0 = vlib_get_buffer (vm, pi0);
189           ip0 = vlib_buffer_get_current (p0);
190
191           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
192
193           if (PREDICT_FALSE (ip4_address_is_multicast (&ip0->dst_address)))
194             {
195               arc0 = lm->mcast_feature_arc_index;
196               next0 = IP4_INPUT_NEXT_LOOKUP_MULTICAST;
197             }
198           else
199             {
200               arc0 = lm->ucast_feature_arc_index;
201               next0 = IP4_INPUT_NEXT_LOOKUP;
202             }
203
204           vnet_buffer (p0)->ip.adj_index[VLIB_RX] = ~0;
205           vnet_feature_arc_start (arc0, sw_if_index0, &next0, p0);
206
207           vlib_increment_simple_counter (cm, thread_index, sw_if_index0, 1);
208           ip4_input_check_x1 (vm, error_node, p0, ip0, &next0,
209                               verify_checksum);
210
211           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
212                                            to_next, n_left_to_next,
213                                            pi0, next0);
214         }
215
216       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
217     }
218
219   return frame->n_vectors;
220 }
221
222 /** \brief IPv4 input node.
223     @node ip4-input
224
225     This is the IPv4 input node: validates ip4 header checksums,
226     verifies ip header lengths, discards pkts with expired TTLs,
227     and sends pkts to the set of ip feature nodes configured on
228     the rx interface.
229
230     @param vm vlib_main_t corresponding to the current thread
231     @param node vlib_node_runtime_t
232     @param frame vlib_frame_t whose contents should be dispatched
233
234     @par Graph mechanics: buffer metadata, next index usage
235
236     @em Uses:
237     - vnet_feature_config_main_t cm corresponding to each pkt's dst address unicast /
238       multicast status.
239     - <code>b->current_config_index</code> corresponding to each pkt's
240       rx sw_if_index.
241          - This sets the per-packet graph trajectory, ensuring that
242            each packet visits the per-interface features in order.
243
244     - <code>vnet_buffer(b)->sw_if_index[VLIB_RX]</code>
245         - Indicates the @c sw_if_index value of the interface that the
246           packet was received on.
247
248     @em Sets:
249     - <code>vnet_buffer(b)->ip.adj_index[VLIB_TX]</code>
250         - The lookup result adjacency index.
251
252     <em>Next Indices:</em>
253     - Dispatches pkts to the (first) feature node:
254       <code> vnet_get_config_data (... &next0 ...); </code>
255       or @c error-drop
256 */
257 static uword
258 ip4_input (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
259 {
260   return ip4_input_inline (vm, node, frame, /* verify_checksum */ 1);
261 }
262
263 static uword
264 ip4_input_no_checksum (vlib_main_t * vm,
265                        vlib_node_runtime_t * node, vlib_frame_t * frame)
266 {
267   return ip4_input_inline (vm, node, frame, /* verify_checksum */ 0);
268 }
269
270 char *ip4_error_strings[] = {
271 #define _(sym,string) string,
272   foreach_ip4_error
273 #undef _
274 };
275
276 /* *INDENT-OFF* */
277 VLIB_REGISTER_NODE (ip4_input_node) = {
278   .function = ip4_input,
279   .name = "ip4-input",
280   .vector_size = sizeof (u32),
281
282   .n_errors = IP4_N_ERROR,
283   .error_strings = ip4_error_strings,
284
285   .n_next_nodes = IP4_INPUT_N_NEXT,
286   .next_nodes = {
287     [IP4_INPUT_NEXT_DROP] = "error-drop",
288     [IP4_INPUT_NEXT_PUNT] = "error-punt",
289     [IP4_INPUT_NEXT_LOOKUP] = "ip4-lookup",
290     [IP4_INPUT_NEXT_LOOKUP_MULTICAST] = "ip4-mfib-forward-lookup",
291     [IP4_INPUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
292     [IP4_INPUT_NEXT_REASSEMBLY] = "ip4-reassembly",
293   },
294
295   .format_buffer = format_ip4_header,
296   .format_trace = format_ip4_input_trace,
297 };
298 /* *INDENT-ON* */
299
300 VLIB_NODE_FUNCTION_MULTIARCH (ip4_input_node, ip4_input);
301
302 /* *INDENT-OFF* */
303 VLIB_REGISTER_NODE (ip4_input_no_checksum_node,static) = {
304   .function = ip4_input_no_checksum,
305   .name = "ip4-input-no-checksum",
306   .vector_size = sizeof (u32),
307
308   .n_next_nodes = IP4_INPUT_N_NEXT,
309   .next_nodes = {
310     [IP4_INPUT_NEXT_DROP] = "error-drop",
311     [IP4_INPUT_NEXT_PUNT] = "error-punt",
312     [IP4_INPUT_NEXT_LOOKUP] = "ip4-lookup",
313     [IP4_INPUT_NEXT_LOOKUP_MULTICAST] = "ip4-mfib-forward-lookup",
314     [IP4_INPUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
315     [IP4_INPUT_NEXT_REASSEMBLY] = "ip4-reassembly",
316   },
317
318   .format_buffer = format_ip4_header,
319   .format_trace = format_ip4_input_trace,
320 };
321 /* *INDENT-ON* */
322
323 VLIB_NODE_FUNCTION_MULTIARCH (ip4_input_no_checksum_node,
324                               ip4_input_no_checksum);
325
326 static clib_error_t *
327 ip4_init (vlib_main_t * vm)
328 {
329   clib_error_t *error;
330
331   ethernet_register_input_type (vm, ETHERNET_TYPE_IP4, ip4_input_node.index);
332   ppp_register_input_protocol (vm, PPP_PROTOCOL_ip4, ip4_input_node.index);
333   hdlc_register_input_protocol (vm, HDLC_PROTOCOL_ip4, ip4_input_node.index);
334
335   {
336     pg_node_t *pn;
337     pn = pg_get_node (ip4_input_node.index);
338     pn->unformat_edit = unformat_pg_ip4_header;
339     pn = pg_get_node (ip4_input_no_checksum_node.index);
340     pn->unformat_edit = unformat_pg_ip4_header;
341   }
342
343   if ((error = vlib_call_init_function (vm, ip4_cli_init)))
344     return error;
345
346   if ((error = vlib_call_init_function (vm, ip4_source_check_init)))
347     return error;
348
349   if ((error = vlib_call_init_function
350        (vm, ip4_source_and_port_range_check_init)))
351     return error;
352
353   /* Set flow hash to something non-zero. */
354   ip4_main.flow_hash_seed = 0xdeadbeef;
355
356   /* Default TTL for packets we generate. */
357   ip4_main.host_config.ttl = 64;
358
359   return error;
360 }
361
362 VLIB_INIT_FUNCTION (ip4_init);
363
364 /*
365  * fd.io coding-style-patch-verification: ON
366  *
367  * Local Variables:
368  * eval: (c-set-style "gnu")
369  * End:
370  */