1b4df4d02fd56492ad79b58e5ec6c29db84ed792
[vpp.git] / src / plugins / nat / nat44 / ed_inlines.h
1 /*
2  * simple nat plugin
3  *
4  * Copyright (c) 2020 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #ifndef __included_ed_inlines_h__
19 #define __included_ed_inlines_h__
20
21 #include <float.h>
22 #include <vppinfra/clib.h>
23 #include <nat/nat.h>
24 #include <nat/nat_inlines.h>
25
26 static_always_inline int
27 nat_ed_lru_insert (snat_main_per_thread_data_t * tsm,
28                    snat_session_t * s, f64 now, u8 proto)
29 {
30   dlist_elt_t *lru_list_elt;
31   pool_get (tsm->lru_pool, lru_list_elt);
32   s->lru_index = lru_list_elt - tsm->lru_pool;
33   switch (proto)
34     {
35     case IP_PROTOCOL_UDP:
36       s->lru_head_index = tsm->udp_lru_head_index;
37       break;
38     case IP_PROTOCOL_TCP:
39       s->lru_head_index = tsm->tcp_trans_lru_head_index;
40       break;
41     case IP_PROTOCOL_ICMP:
42       s->lru_head_index = tsm->icmp_lru_head_index;
43       break;
44     default:
45       s->lru_head_index = tsm->unk_proto_lru_head_index;
46       break;
47     }
48   clib_dlist_addtail (tsm->lru_pool, s->lru_head_index, s->lru_index);
49   lru_list_elt->value = s - tsm->sessions;
50   s->last_lru_update = now;
51   return 1;
52 }
53
54 always_inline void
55 nat_ed_session_delete (snat_main_t * sm, snat_session_t * ses,
56                        u32 thread_index, int lru_delete
57                        /* delete from global LRU list */ )
58 {
59   snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data,
60                                                        thread_index);
61
62   if (lru_delete)
63     {
64       clib_dlist_remove (tsm->lru_pool, ses->lru_index);
65     }
66   pool_put_index (tsm->lru_pool, ses->lru_index);
67   pool_put (tsm->sessions, ses);
68   vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
69                            pool_elts (tsm->sessions));
70
71 }
72
73 static_always_inline int
74 nat_lru_free_one_with_head (snat_main_t * sm, int thread_index,
75                             f64 now, u32 head_index)
76 {
77   snat_session_t *s = NULL;
78   dlist_elt_t *oldest_elt;
79   f64 sess_timeout_time;
80   u32 oldest_index;
81   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
82   oldest_index = clib_dlist_remove_head (tsm->lru_pool, head_index);
83   if (~0 != oldest_index)
84     {
85       oldest_elt = pool_elt_at_index (tsm->lru_pool, oldest_index);
86       s = pool_elt_at_index (tsm->sessions, oldest_elt->value);
87
88       sess_timeout_time =
89         s->last_heard + (f64) nat44_session_get_timeout (sm, s);
90       if (now >= sess_timeout_time
91           || (s->tcp_closed_timestamp && now >= s->tcp_closed_timestamp))
92         {
93           nat_free_session_data (sm, s, thread_index, 0);
94           nat_ed_session_delete (sm, s, thread_index, 0);
95           return 1;
96         }
97       else
98         {
99           clib_dlist_addhead (tsm->lru_pool, head_index, oldest_index);
100         }
101     }
102   return 0;
103 }
104
105 static_always_inline int
106 nat_lru_free_one (snat_main_t * sm, int thread_index, f64 now)
107 {
108   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
109   int rc = 0;
110 #define _(p)                                                       \
111   if ((rc = nat_lru_free_one_with_head (sm, thread_index, now,     \
112                                         tsm->p##_lru_head_index))) \
113     {                                                              \
114       return rc;                                                   \
115     }
116   _(tcp_trans);
117   _(udp);
118   _(unk_proto);
119   _(icmp);
120   _(tcp_estab);
121 #undef _
122   return 0;
123 }
124
125 static_always_inline snat_session_t *
126 nat_ed_session_alloc (snat_main_t * sm, u32 thread_index, f64 now, u8 proto)
127 {
128   snat_session_t *s;
129   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
130
131   nat_lru_free_one (sm, thread_index, now);
132
133   pool_get (tsm->sessions, s);
134   clib_memset (s, 0, sizeof (*s));
135
136   nat_ed_lru_insert (tsm, s, now, proto);
137
138   s->ha_last_refreshed = now;
139   vlib_set_simple_counter (&sm->total_sessions, thread_index, 0,
140                            pool_elts (tsm->sessions));
141   return s;
142 }
143
144 // slow path
145 static_always_inline void
146 per_vrf_sessions_cleanup (u32 thread_index)
147 {
148   snat_main_t *sm = &snat_main;
149   snat_main_per_thread_data_t *tsm =
150     vec_elt_at_index (sm->per_thread_data, thread_index);
151   per_vrf_sessions_t *per_vrf_sessions;
152   u32 *to_free = 0, *i;
153
154   vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec)
155   {
156     if (per_vrf_sessions->expired)
157       {
158         if (per_vrf_sessions->ses_count == 0)
159           {
160             vec_add1 (to_free, per_vrf_sessions - tsm->per_vrf_sessions_vec);
161           }
162       }
163   }
164
165   if (vec_len (to_free))
166     {
167       vec_foreach (i, to_free)
168       {
169         vec_del1 (tsm->per_vrf_sessions_vec, *i);
170       }
171     }
172
173   vec_free (to_free);
174 }
175
176 // slow path
177 static_always_inline void
178 per_vrf_sessions_register_session (snat_session_t * s, u32 thread_index)
179 {
180   snat_main_t *sm = &snat_main;
181   snat_main_per_thread_data_t *tsm =
182     vec_elt_at_index (sm->per_thread_data, thread_index);
183   per_vrf_sessions_t *per_vrf_sessions;
184
185   per_vrf_sessions_cleanup (thread_index);
186
187   // s->per_vrf_sessions_index == ~0 ... reuse of old session
188
189   vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec)
190   {
191     // ignore already expired registrations
192     if (per_vrf_sessions->expired)
193       continue;
194
195     if ((s->in2out.fib_index == per_vrf_sessions->rx_fib_index) &&
196         (s->out2in.fib_index == per_vrf_sessions->tx_fib_index))
197       {
198         goto done;
199       }
200     if ((s->in2out.fib_index == per_vrf_sessions->tx_fib_index) &&
201         (s->out2in.fib_index == per_vrf_sessions->rx_fib_index))
202       {
203         goto done;
204       }
205   }
206
207   // create a new registration
208   vec_add2 (tsm->per_vrf_sessions_vec, per_vrf_sessions, 1);
209   clib_memset (per_vrf_sessions, 0, sizeof (*per_vrf_sessions));
210
211   per_vrf_sessions->rx_fib_index = s->in2out.fib_index;
212   per_vrf_sessions->tx_fib_index = s->out2in.fib_index;
213
214 done:
215   s->per_vrf_sessions_index = per_vrf_sessions - tsm->per_vrf_sessions_vec;
216   per_vrf_sessions->ses_count++;
217 }
218
219 // fast path
220 static_always_inline void
221 per_vrf_sessions_unregister_session (snat_session_t * s, u32 thread_index)
222 {
223   snat_main_t *sm = &snat_main;
224   snat_main_per_thread_data_t *tsm;
225   per_vrf_sessions_t *per_vrf_sessions;
226
227   ASSERT (s->per_vrf_sessions_index != ~0);
228   
229   tsm = vec_elt_at_index (sm->per_thread_data, thread_index);
230   per_vrf_sessions = vec_elt_at_index (tsm->per_vrf_sessions_vec,
231                                        s->per_vrf_sessions_index);
232
233   ASSERT (per_vrf_sessions->ses_count != 0);
234
235   per_vrf_sessions->ses_count--;
236   s->per_vrf_sessions_index = ~0;
237 }
238
239 // fast path
240 static_always_inline u8
241 per_vrf_sessions_is_expired (snat_session_t * s, u32 thread_index)
242 {
243   snat_main_t *sm = &snat_main;
244   snat_main_per_thread_data_t *tsm;
245   per_vrf_sessions_t *per_vrf_sessions;
246
247   ASSERT (s->per_vrf_sessions_index != ~0);
248
249   tsm = vec_elt_at_index (sm->per_thread_data, thread_index);
250   per_vrf_sessions = vec_elt_at_index (tsm->per_vrf_sessions_vec,
251                                        s->per_vrf_sessions_index);
252   return per_vrf_sessions->expired;
253 }
254
255 #endif