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