nat: optimize prefetching
[vpp.git] / src / plugins / nat / nat_inlines.h
1 /*
2  * Copyright (c) 2018 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  * @brief The NAT inline functions
17  */
18
19 #ifndef __included_nat_inlines_h__
20 #define __included_nat_inlines_h__
21
22 #include <vnet/fib/ip4_fib.h>
23 #include <nat/nat.h>
24 #include <nat/nat_ha.h>
25
26 always_inline u64
27 calc_nat_key (ip4_address_t addr, u16 port, u32 fib_index, u8 proto)
28 {
29   ASSERT (fib_index <= (1 << 14) - 1);
30   ASSERT (proto <= (1 << 3) - 1);
31   return (u64) addr.as_u32 << 32 | (u64) port << 16 | fib_index << 3 |
32     (proto & 0x7);
33 }
34
35 always_inline void
36 split_nat_key (u64 key, ip4_address_t * addr, u16 * port,
37                u32 * fib_index, nat_protocol_t * proto)
38 {
39   if (addr)
40     {
41       addr->as_u32 = key >> 32;
42     }
43   if (port)
44     {
45       *port = (key >> 16) & (u16) ~ 0;
46     }
47   if (fib_index)
48     {
49       *fib_index = key >> 3 & ((1 << 13) - 1);
50     }
51   if (proto)
52     {
53       *proto = key & 0x7;
54     }
55 }
56
57 always_inline void
58 init_nat_k (clib_bihash_kv_8_8_t * kv, ip4_address_t addr, u16 port,
59             u32 fib_index, nat_protocol_t proto)
60 {
61   kv->key = calc_nat_key (addr, port, fib_index, proto);
62   kv->value = ~0ULL;
63 }
64
65 always_inline void
66 init_nat_kv (clib_bihash_kv_8_8_t * kv, ip4_address_t addr, u16 port,
67              u32 fib_index, nat_protocol_t proto, u64 value)
68 {
69   init_nat_k (kv, addr, port, fib_index, proto);
70   kv->value = value;
71 }
72
73 always_inline void
74 init_nat_i2o_k (clib_bihash_kv_8_8_t * kv, snat_session_t * s)
75 {
76   return init_nat_k (kv, s->in2out.addr, s->in2out.port, s->in2out.fib_index,
77                      s->nat_proto);
78 }
79
80 always_inline void
81 init_nat_i2o_kv (clib_bihash_kv_8_8_t * kv, snat_session_t * s, u64 value)
82 {
83   init_nat_k (kv, s->in2out.addr, s->in2out.port, s->in2out.fib_index,
84               s->nat_proto);
85   kv->value = value;
86 }
87
88 always_inline void
89 init_nat_o2i_k (clib_bihash_kv_8_8_t * kv, snat_session_t * s)
90 {
91   return init_nat_k (kv, s->out2in.addr, s->out2in.port, s->out2in.fib_index,
92                      s->nat_proto);
93 }
94
95 always_inline void
96 init_nat_o2i_kv (clib_bihash_kv_8_8_t * kv, snat_session_t * s, u64 value)
97 {
98   init_nat_k (kv, s->out2in.addr, s->out2in.port, s->out2in.fib_index,
99               s->nat_proto);
100   kv->value = value;
101 }
102
103 static inline uword
104 nat_pre_node_fn_inline (vlib_main_t * vm,
105                         vlib_node_runtime_t * node,
106                         vlib_frame_t * frame, u32 def_next)
107 {
108   u32 n_left_from, *from, *to_next;
109   u16 next_index;
110
111   from = vlib_frame_vector_args (frame);
112   n_left_from = frame->n_vectors;
113   next_index = node->cached_next_index;
114
115   while (n_left_from > 0)
116     {
117       u32 n_left_to_next;
118
119       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
120
121       while (n_left_from >= 4 && n_left_to_next >= 2)
122         {
123           u32 next0, next1;
124           u32 arc_next0, arc_next1;
125           u32 bi0, bi1;
126           vlib_buffer_t *b0, *b1;
127
128           /* Prefetch next iteration. */
129           {
130             vlib_buffer_t *p2, *p3;
131
132             p2 = vlib_get_buffer (vm, from[2]);
133             p3 = vlib_get_buffer (vm, from[3]);
134
135             vlib_prefetch_buffer_header (p2, LOAD);
136             vlib_prefetch_buffer_header (p3, LOAD);
137
138             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
139             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, LOAD);
140           }
141
142           /* speculatively enqueue b0 and b1 to the current next frame */
143           to_next[0] = bi0 = from[0];
144           to_next[1] = bi1 = from[1];
145           from += 2;
146           to_next += 2;
147           n_left_from -= 2;
148           n_left_to_next -= 2;
149
150           b0 = vlib_get_buffer (vm, bi0);
151           b1 = vlib_get_buffer (vm, bi1);
152
153           next0 = def_next;
154           next1 = def_next;
155
156           vnet_feature_next (&arc_next0, b0);
157           vnet_feature_next (&arc_next1, b1);
158
159           vnet_buffer2 (b0)->nat.arc_next = arc_next0;
160           vnet_buffer2 (b1)->nat.arc_next = arc_next1;
161
162           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
163             {
164               if (b0->flags & VLIB_BUFFER_IS_TRACED)
165                 {
166                   nat_pre_trace_t *t =
167                     vlib_add_trace (vm, node, b0, sizeof (*t));
168                   t->next_index = next0;
169                   t->arc_next_index = arc_next0;
170                 }
171               if (b1->flags & VLIB_BUFFER_IS_TRACED)
172                 {
173                   nat_pre_trace_t *t =
174                     vlib_add_trace (vm, node, b0, sizeof (*t));
175                   t->next_index = next1;
176                   t->arc_next_index = arc_next1;
177                 }
178             }
179
180           /* verify speculative enqueues, maybe switch current next frame */
181           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
182                                            to_next, n_left_to_next,
183                                            bi0, bi1, next0, next1);
184         }
185
186       while (n_left_from > 0 && n_left_to_next > 0)
187         {
188           u32 next0;
189           u32 arc_next0;
190           u32 bi0;
191           vlib_buffer_t *b0;
192
193           /* speculatively enqueue b0 to the current next frame */
194           bi0 = from[0];
195           to_next[0] = bi0;
196           from += 1;
197           to_next += 1;
198           n_left_from -= 1;
199           n_left_to_next -= 1;
200
201           b0 = vlib_get_buffer (vm, bi0);
202           next0 = def_next;
203           vnet_feature_next (&arc_next0, b0);
204           vnet_buffer2 (b0)->nat.arc_next = arc_next0;
205
206           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
207                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
208             {
209               nat_pre_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
210               t->next_index = next0;
211               t->arc_next_index = arc_next0;
212             }
213
214           /* verify speculative enqueue, maybe switch current next frame */
215           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
216                                            to_next, n_left_to_next,
217                                            bi0, next0);
218         }
219
220       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
221     }
222
223   return frame->n_vectors;
224 }
225
226 always_inline u8
227 is_interface_addr (snat_main_t * sm, vlib_node_runtime_t * node,
228                    u32 sw_if_index0, u32 ip4_addr)
229 {
230   snat_runtime_t *rt = (snat_runtime_t *) node->runtime_data;
231   ip4_address_t *first_int_addr;
232
233   if (PREDICT_FALSE (rt->cached_sw_if_index != sw_if_index0))
234     {
235       first_int_addr =
236         ip4_interface_first_address (sm->ip4_main, sw_if_index0,
237                                      0 /* just want the address */ );
238       rt->cached_sw_if_index = sw_if_index0;
239       if (first_int_addr)
240         rt->cached_ip4_address = first_int_addr->as_u32;
241       else
242         rt->cached_ip4_address = 0;
243     }
244
245   if (PREDICT_FALSE (ip4_addr == rt->cached_ip4_address))
246     return 1;
247   else
248     return 0;
249 }
250
251 always_inline u8
252 maximum_sessions_exceeded (snat_main_t * sm, u32 thread_index)
253 {
254   if (pool_elts (sm->per_thread_data[thread_index].sessions) >=
255       sm->max_translations)
256     return 1;
257
258   return 0;
259 }
260
261 always_inline void
262 nat_send_all_to_node (vlib_main_t * vm, u32 * bi_vector,
263                       vlib_node_runtime_t * node, vlib_error_t * error,
264                       u32 next)
265 {
266   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
267
268   from = bi_vector;
269   n_left_from = vec_len (bi_vector);
270   next_index = node->cached_next_index;
271   while (n_left_from > 0)
272     {
273       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
274       while (n_left_from > 0 && n_left_to_next > 0)
275         {
276           u32 bi0 = to_next[0] = from[0];
277           from += 1;
278           n_left_from -= 1;
279           to_next += 1;
280           n_left_to_next -= 1;
281           vlib_buffer_t *p0 = vlib_get_buffer (vm, bi0);
282           if (error)
283             p0->error = *error;
284           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
285                                            n_left_to_next, bi0, next);
286         }
287       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
288     }
289 }
290
291 always_inline void
292 user_session_increment (snat_main_t * sm, snat_user_t * u, u8 is_static)
293 {
294   if (u->nsessions + u->nstaticsessions < sm->max_translations_per_user)
295     {
296       if (is_static)
297         u->nstaticsessions++;
298       else
299         u->nsessions++;
300     }
301 }
302
303 always_inline void
304 nat44_delete_user_with_no_session (snat_main_t * sm, snat_user_t * u,
305                                    u32 thread_index)
306 {
307   clib_bihash_kv_8_8_t kv;
308   snat_user_key_t u_key;
309   snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data,
310                                                        thread_index);
311
312   if (u->nstaticsessions == 0 && u->nsessions == 0)
313     {
314       u_key.addr.as_u32 = u->addr.as_u32;
315       u_key.fib_index = u->fib_index;
316       kv.key = u_key.as_u64;
317       pool_put_index (tsm->list_pool, u->sessions_per_user_list_head_index);
318       pool_put (tsm->users, u);
319       clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 0);
320       vlib_set_simple_counter (&sm->total_users, thread_index, 0,
321                                pool_elts (tsm->users));
322     }
323 }
324
325 always_inline void
326 nat44_delete_session (snat_main_t * sm, snat_session_t * ses,
327                       u32 thread_index)
328 {
329   snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data,
330                                                        thread_index);
331   clib_bihash_kv_8_8_t kv, value;
332   snat_user_t *u;
333   const snat_user_key_t u_key = {
334     .addr = ses->in2out.addr,
335     .fib_index = ses->in2out.fib_index
336   };
337   const u8 u_static = snat_is_session_static (ses);
338
339   clib_dlist_remove (tsm->list_pool, ses->per_user_index);
340   pool_put_index (tsm->list_pool, ses->per_user_index);
341   if (sm->endpoint_dependent)
342     {
343       clib_dlist_remove (tsm->lru_pool, ses->lru_index);
344       pool_put_index (tsm->lru_pool, ses->lru_index);
345     }
346   pool_put (tsm->sessions, ses);
347   vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
348                            pool_elts (tsm->sessions));
349
350   kv.key = u_key.as_u64;
351   if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
352     {
353       u = pool_elt_at_index (tsm->users, value.value);
354       if (u_static)
355         u->nstaticsessions--;
356       else
357         u->nsessions--;
358
359       nat44_delete_user_with_no_session (sm, u, thread_index);
360     }
361 }
362
363 /** \brief Set TCP session state.
364     @return 1 if session was closed, otherwise 0
365 */
366 always_inline int
367 nat44_set_tcp_session_state_i2o (snat_main_t * sm, f64 now,
368                                  snat_session_t * ses, vlib_buffer_t * b,
369                                  u32 thread_index)
370 {
371   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
372   u8 tcp_flags = vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags;
373   u32 tcp_ack_number = vnet_buffer (b)->ip.reass.tcp_ack_number;
374   u32 tcp_seq_number = vnet_buffer (b)->ip.reass.tcp_seq_number;
375   if ((ses->state == 0) && (tcp_flags & TCP_FLAG_RST))
376     ses->state = NAT44_SES_RST;
377   if ((ses->state == NAT44_SES_RST) && !(tcp_flags & TCP_FLAG_RST))
378     ses->state = 0;
379   if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_SYN) &&
380       (ses->state & NAT44_SES_O2I_SYN))
381     ses->state = 0;
382   if (tcp_flags & TCP_FLAG_SYN)
383     ses->state |= NAT44_SES_I2O_SYN;
384   if (tcp_flags & TCP_FLAG_FIN)
385     {
386       ses->i2o_fin_seq = clib_net_to_host_u32 (tcp_seq_number);
387       ses->state |= NAT44_SES_I2O_FIN;
388     }
389   if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_O2I_FIN))
390     {
391       if (clib_net_to_host_u32 (tcp_ack_number) > ses->o2i_fin_seq)
392         {
393           ses->state |= NAT44_SES_O2I_FIN_ACK;
394           if (nat44_is_ses_closed (ses))
395             {                   // if session is now closed, save the timestamp
396               ses->tcp_closed_timestamp = now + sm->tcp_transitory_timeout;
397               ses->last_lru_update = now;
398             }
399         }
400     }
401
402   // move the session to proper LRU
403   if (ses->state)
404     {
405       ses->lru_head_index = tsm->tcp_trans_lru_head_index;
406     }
407   else
408     {
409       ses->lru_head_index = tsm->tcp_estab_lru_head_index;
410     }
411   clib_dlist_remove (tsm->lru_pool, ses->lru_index);
412   clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index);
413   return 0;
414 }
415
416 always_inline int
417 nat44_set_tcp_session_state_o2i (snat_main_t * sm, f64 now,
418                                  snat_session_t * ses, u8 tcp_flags,
419                                  u32 tcp_ack_number, u32 tcp_seq_number,
420                                  u32 thread_index)
421 {
422   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
423   if ((ses->state == 0) && (tcp_flags & TCP_FLAG_RST))
424     ses->state = NAT44_SES_RST;
425   if ((ses->state == NAT44_SES_RST) && !(tcp_flags & TCP_FLAG_RST))
426     ses->state = 0;
427   if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_SYN) &&
428       (ses->state & NAT44_SES_O2I_SYN))
429     ses->state = 0;
430   if (tcp_flags & TCP_FLAG_SYN)
431     ses->state |= NAT44_SES_O2I_SYN;
432   if (tcp_flags & TCP_FLAG_FIN)
433     {
434       ses->o2i_fin_seq = clib_net_to_host_u32 (tcp_seq_number);
435       ses->state |= NAT44_SES_O2I_FIN;
436     }
437   if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_FIN))
438     {
439       if (clib_net_to_host_u32 (tcp_ack_number) > ses->i2o_fin_seq)
440         ses->state |= NAT44_SES_I2O_FIN_ACK;
441       if (nat44_is_ses_closed (ses))
442         {                       // if session is now closed, save the timestamp
443           ses->tcp_closed_timestamp = now + sm->tcp_transitory_timeout;
444           ses->last_lru_update = now;
445         }
446     }
447   // move the session to proper LRU
448   if (ses->state)
449     {
450       ses->lru_head_index = tsm->tcp_trans_lru_head_index;
451     }
452   else
453     {
454       ses->lru_head_index = tsm->tcp_estab_lru_head_index;
455     }
456   clib_dlist_remove (tsm->lru_pool, ses->lru_index);
457   clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index);
458   return 0;
459 }
460
461 always_inline u32
462 nat44_session_get_timeout (snat_main_t * sm, snat_session_t * s)
463 {
464   switch (s->nat_proto)
465     {
466     case NAT_PROTOCOL_ICMP:
467       return sm->icmp_timeout;
468     case NAT_PROTOCOL_UDP:
469       return sm->udp_timeout;
470     case NAT_PROTOCOL_TCP:
471       {
472         if (s->state)
473           return sm->tcp_transitory_timeout;
474         else
475           return sm->tcp_established_timeout;
476       }
477     default:
478       return sm->udp_timeout;
479     }
480
481   return 0;
482 }
483
484 always_inline void
485 nat44_session_update_counters (snat_session_t * s, f64 now, uword bytes,
486                                u32 thread_index)
487 {
488   s->last_heard = now;
489   s->total_pkts++;
490   s->total_bytes += bytes;
491   nat_ha_sref (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
492                s->ext_host_port, s->nat_proto, s->out2in.fib_index,
493                s->total_pkts, s->total_bytes, thread_index,
494                &s->ha_last_refreshed, now);
495 }
496
497 /** \brief Per-user LRU list maintenance */
498 always_inline void
499 nat44_session_update_lru (snat_main_t * sm, snat_session_t * s,
500                           u32 thread_index)
501 {
502   /* don't update too often - timeout is in magnitude of seconds anyway */
503   if (s->last_heard > s->last_lru_update + 1)
504     {
505       if (!sm->endpoint_dependent)
506         {
507           clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
508                              s->per_user_index);
509           clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
510                               s->per_user_list_head_index, s->per_user_index);
511         }
512       else
513         {
514           clib_dlist_remove (sm->per_thread_data[thread_index].lru_pool,
515                              s->lru_index);
516           clib_dlist_addtail (sm->per_thread_data[thread_index].lru_pool,
517                               s->lru_head_index, s->lru_index);
518         }
519       s->last_lru_update = s->last_heard;
520     }
521 }
522
523 always_inline void
524 init_ed_k (clib_bihash_kv_16_8_t * kv, ip4_address_t l_addr, u16 l_port,
525            ip4_address_t r_addr, u16 r_port, u32 fib_index, u8 proto)
526 {
527   kv->key[0] = (u64) r_addr.as_u32 << 32 | l_addr.as_u32;
528   kv->key[1] =
529     (u64) r_port << 48 | (u64) l_port << 32 | fib_index << 8 | proto;
530 }
531
532 always_inline void
533 init_ed_kv (clib_bihash_kv_16_8_t * kv, ip4_address_t l_addr, u16 l_port,
534             ip4_address_t r_addr, u16 r_port, u32 fib_index, u8 proto,
535             u32 thread_index, u32 session_index)
536 {
537   init_ed_k (kv, l_addr, l_port, r_addr, r_port, fib_index, proto);
538   kv->value = (u64) thread_index << 32 | session_index;
539 }
540
541 always_inline u32
542 ed_value_get_thread_index (clib_bihash_kv_16_8_t * value)
543 {
544   return value->value >> 32;
545 }
546
547 always_inline u32
548 ed_value_get_session_index (clib_bihash_kv_16_8_t * value)
549 {
550   return value->value & ~(u32) 0;
551 }
552
553 always_inline void
554 split_ed_value (clib_bihash_kv_16_8_t * value, u32 * thread_index,
555                 u32 * session_index)
556 {
557   if (thread_index)
558     {
559       *thread_index = ed_value_get_thread_index (value);
560     }
561   if (session_index)
562     {
563       *session_index = ed_value_get_session_index (value);
564     }
565 }
566
567 always_inline void
568 split_ed_kv (clib_bihash_kv_16_8_t * kv,
569              ip4_address_t * l_addr, ip4_address_t * r_addr, u8 * proto,
570              u32 * fib_index, u16 * l_port, u16 * r_port)
571 {
572   if (l_addr)
573     {
574       l_addr->as_u32 = kv->key[0] & (u32) ~ 0;
575     }
576   if (r_addr)
577     {
578       r_addr->as_u32 = kv->key[0] >> 32;
579     }
580   if (r_port)
581     {
582       *r_port = kv->key[1] >> 48;
583     }
584   if (l_port)
585     {
586       *l_port = (kv->key[1] >> 32) & (u16) ~ 0;
587     }
588   if (fib_index)
589     {
590       *fib_index = (kv->key[1] >> 8) & ((1 << 24) - 1);
591     }
592   if (proto)
593     {
594       *proto = kv->key[1] & (u8) ~ 0;
595     }
596 }
597
598 static_always_inline int
599 get_icmp_i2o_ed_key (vlib_buffer_t * b, ip4_header_t * ip0, u32 rx_fib_index,
600                      u32 thread_index, u32 session_index,
601                      nat_protocol_t * nat_proto, u16 * l_port, u16 * r_port,
602                      clib_bihash_kv_16_8_t * kv)
603 {
604   u8 proto;
605   u16 _l_port, _r_port;
606   ip4_address_t *l_addr, *r_addr;
607
608   icmp46_header_t *icmp0;
609   icmp_echo_header_t *echo0, *inner_echo0 = 0;
610   ip4_header_t *inner_ip0 = 0;
611   void *l4_header = 0;
612   icmp46_header_t *inner_icmp0;
613
614   icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
615   echo0 = (icmp_echo_header_t *) (icmp0 + 1);
616
617   if (!icmp_type_is_error_message
618       (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))
619     {
620       proto = IP_PROTOCOL_ICMP;
621       l_addr = &ip0->src_address;
622       r_addr = &ip0->dst_address;
623       _l_port = vnet_buffer (b)->ip.reass.l4_src_port;
624       _r_port = 0;
625     }
626   else
627     {
628       inner_ip0 = (ip4_header_t *) (echo0 + 1);
629       l4_header = ip4_next_header (inner_ip0);
630       proto = inner_ip0->protocol;
631       r_addr = &inner_ip0->src_address;
632       l_addr = &inner_ip0->dst_address;
633       switch (ip_proto_to_nat_proto (inner_ip0->protocol))
634         {
635         case NAT_PROTOCOL_ICMP:
636           inner_icmp0 = (icmp46_header_t *) l4_header;
637           inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1);
638           _r_port = 0;
639           _l_port = inner_echo0->identifier;
640           break;
641         case NAT_PROTOCOL_UDP:
642         case NAT_PROTOCOL_TCP:
643           _l_port = ((tcp_udp_header_t *) l4_header)->dst_port;
644           _r_port = ((tcp_udp_header_t *) l4_header)->src_port;
645           break;
646         default:
647           return NAT_IN2OUT_ED_ERROR_UNSUPPORTED_PROTOCOL;
648         }
649     }
650   init_ed_kv (kv, *l_addr, _l_port, *r_addr, _r_port, rx_fib_index, proto,
651               thread_index, session_index);
652   if (nat_proto)
653     {
654       *nat_proto = ip_proto_to_nat_proto (proto);
655     }
656   if (l_port)
657     {
658       *l_port = _l_port;
659     }
660   if (r_port)
661     {
662       *r_port = _r_port;
663     }
664   return 0;
665 }
666
667 static_always_inline int
668 get_icmp_o2i_ed_key (vlib_buffer_t * b, ip4_header_t * ip0, u32 rx_fib_index,
669                      u32 thread_index, u32 session_index,
670                      nat_protocol_t * nat_proto, u16 * l_port, u16 * r_port,
671                      clib_bihash_kv_16_8_t * kv)
672 {
673   icmp46_header_t *icmp0;
674   u8 proto;
675   ip4_address_t *l_addr, *r_addr;
676   u16 _l_port, _r_port;
677   icmp_echo_header_t *echo0, *inner_echo0 = 0;
678   ip4_header_t *inner_ip0;
679   void *l4_header = 0;
680   icmp46_header_t *inner_icmp0;
681
682   icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
683   echo0 = (icmp_echo_header_t *) (icmp0 + 1);
684
685   if (!icmp_type_is_error_message
686       (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))
687     {
688       proto = IP_PROTOCOL_ICMP;
689       l_addr = &ip0->dst_address;
690       r_addr = &ip0->src_address;
691       _l_port = vnet_buffer (b)->ip.reass.l4_src_port;
692       _r_port = 0;
693     }
694   else
695     {
696       inner_ip0 = (ip4_header_t *) (echo0 + 1);
697       l4_header = ip4_next_header (inner_ip0);
698       proto = inner_ip0->protocol;
699       l_addr = &inner_ip0->src_address;
700       r_addr = &inner_ip0->dst_address;
701       switch (ip_proto_to_nat_proto (inner_ip0->protocol))
702         {
703         case NAT_PROTOCOL_ICMP:
704           inner_icmp0 = (icmp46_header_t *) l4_header;
705           inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1);
706           _l_port = inner_echo0->identifier;
707           _r_port = 0;
708           break;
709         case NAT_PROTOCOL_UDP:
710         case NAT_PROTOCOL_TCP:
711           _l_port = ((tcp_udp_header_t *) l4_header)->src_port;
712           _r_port = ((tcp_udp_header_t *) l4_header)->dst_port;
713           break;
714         default:
715           return -1;
716         }
717     }
718   init_ed_kv (kv, *l_addr, _l_port, *r_addr, _r_port, rx_fib_index, proto,
719               thread_index, session_index);
720   if (nat_proto)
721     {
722       *nat_proto = ip_proto_to_nat_proto (proto);
723     }
724   if (l_port)
725     {
726       *l_port = _l_port;
727     }
728   if (r_port)
729     {
730       *r_port = _r_port;
731     }
732   return 0;
733 }
734
735 /**
736  * @brief Check if packet should be translated
737  *
738  * Packets aimed at outside interface and external address with active session
739  * should be translated.
740  *
741  * @param sm            NAT main
742  * @param rt            NAT runtime data
743  * @param sw_if_index0  index of the inside interface
744  * @param ip0           IPv4 header
745  * @param proto0        NAT protocol
746  * @param rx_fib_index0 RX FIB index
747  *
748  * @returns 0 if packet should be translated otherwise 1
749  */
750 static inline int
751 snat_not_translate_fast (snat_main_t * sm, vlib_node_runtime_t * node,
752                          u32 sw_if_index0, ip4_header_t * ip0, u32 proto0,
753                          u32 rx_fib_index0)
754 {
755   if (sm->out2in_dpo)
756     return 0;
757
758   fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
759   nat_outside_fib_t *outside_fib;
760   fib_prefix_t pfx = {
761     .fp_proto = FIB_PROTOCOL_IP4,
762     .fp_len = 32,
763     .fp_addr = {
764                 .ip4.as_u32 = ip0->dst_address.as_u32,
765                 }
766     ,
767   };
768
769   /* Don't NAT packet aimed at the intfc address */
770   if (PREDICT_FALSE (is_interface_addr (sm, node, sw_if_index0,
771                                         ip0->dst_address.as_u32)))
772     return 1;
773
774   fei = fib_table_lookup (rx_fib_index0, &pfx);
775   if (FIB_NODE_INDEX_INVALID != fei)
776     {
777       u32 sw_if_index = fib_entry_get_resolving_interface (fei);
778       if (sw_if_index == ~0)
779         {
780           vec_foreach (outside_fib, sm->outside_fibs)
781           {
782             fei = fib_table_lookup (outside_fib->fib_index, &pfx);
783             if (FIB_NODE_INDEX_INVALID != fei)
784               {
785                 sw_if_index = fib_entry_get_resolving_interface (fei);
786                 if (sw_if_index != ~0)
787                   break;
788               }
789           }
790         }
791       if (sw_if_index == ~0)
792         return 1;
793
794       snat_interface_t *i;
795       /* *INDENT-OFF* */
796       pool_foreach (i, sm->interfaces, ({
797         /* NAT packet aimed at outside interface */
798         if ((nat_interface_is_outside (i)) && (sw_if_index == i->sw_if_index))
799           return 0;
800       }));
801       /* *INDENT-ON* */
802     }
803
804   return 1;
805 }
806
807 static inline void
808 increment_v4_address (ip4_address_t * a)
809 {
810   u32 v;
811
812   v = clib_net_to_host_u32 (a->as_u32) + 1;
813   a->as_u32 = clib_host_to_net_u32 (v);
814 }
815
816 #endif /* __included_nat_inlines_h__ */
817
818 /*
819  * fd.io coding-style-patch-verification: ON
820  *
821  * Local Variables:
822  * eval: (c-set-style "gnu")
823  * End:
824  */