Line data Source code
1 : /*
2 : * Copyright (c) 2020 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 : #include <vnet/fib/fib_table.h>
17 : #include <vnet/dpo/drop_dpo.h>
18 :
19 : #include <cnat/cnat_client.h>
20 : #include <cnat/cnat_translation.h>
21 :
22 : cnat_client_t *cnat_client_pool;
23 :
24 : cnat_client_db_t cnat_client_db;
25 :
26 : dpo_type_t cnat_client_dpo;
27 :
28 : static_always_inline u8
29 57 : cnat_client_is_clone (cnat_client_t * cc)
30 : {
31 57 : return (FIB_NODE_INDEX_INVALID == cc->cc_fei);
32 : }
33 :
34 : static void
35 28 : cnat_client_db_remove (cnat_client_t * cc)
36 : {
37 28 : if (ip_addr_version (&cc->cc_ip) == AF_IP4)
38 14 : hash_unset (cnat_client_db.crd_cip4, ip_addr_v4 (&cc->cc_ip).as_u32);
39 : else
40 14 : hash_unset_mem_free (&cnat_client_db.crd_cip6, &ip_addr_v6 (&cc->cc_ip));
41 28 : }
42 :
43 : static void
44 28 : cnat_client_destroy (cnat_client_t * cc)
45 : {
46 28 : ASSERT (!cnat_client_is_clone (cc));
47 :
48 28 : ASSERT (fib_entry_is_sourced (cc->cc_fei, cnat_fib_source));
49 28 : fib_table_entry_delete_index (cc->cc_fei, cnat_fib_source);
50 :
51 28 : cnat_client_db_remove (cc);
52 28 : dpo_reset (&cc->cc_parent);
53 28 : pool_put (cnat_client_pool, cc);
54 28 : }
55 :
56 : void
57 176 : cnat_client_free_by_ip (ip46_address_t * ip, u8 af)
58 : {
59 : cnat_client_t *cc;
60 176 : cc = (AF_IP4 == af ?
61 176 : cnat_client_ip4_find (&ip->ip4) : cnat_client_ip6_find (&ip->ip6));
62 176 : ASSERT (NULL != cc);
63 :
64 176 : if (0 == cnat_client_uncnt_session (cc) && 0 == cc->tr_refcnt)
65 28 : cnat_client_destroy (cc);
66 176 : }
67 :
68 : void
69 84 : cnat_client_throttle_pool_process ()
70 : {
71 : /* This processes ips stored in the throttle pool
72 : to update session refcounts
73 : and should be called before cnat_client_free_by_ip */
74 : cnat_client_t *cc;
75 84 : ip_address_t *addr, *del_vec = NULL;
76 : u32 refcnt;
77 :
78 84 : vec_reset_length (del_vec);
79 84 : clib_spinlock_lock (&cnat_client_db.throttle_lock);
80 5484 : hash_foreach_mem (addr, refcnt, cnat_client_db.throttle_mem, {
81 : cc = (AF_IP4 == addr->version ? cnat_client_ip4_find (&ip_addr_v4 (addr)) :
82 : cnat_client_ip6_find (&ip_addr_v6 (addr)));
83 : /* Client might not already be created */
84 : if (NULL != cc)
85 : {
86 : cnat_client_t *ccp = cnat_client_get (cc->parent_cci);
87 : clib_atomic_add_fetch (&ccp->session_refcnt, refcnt);
88 : vec_add1 (del_vec, *addr);
89 : }
90 : });
91 108 : vec_foreach (addr, del_vec)
92 24 : hash_unset_mem_free (&cnat_client_db.throttle_mem, addr);
93 84 : clib_spinlock_unlock (&cnat_client_db.throttle_lock);
94 84 : }
95 :
96 : void
97 18 : cnat_client_translation_added (index_t cci)
98 : {
99 : cnat_client_t *cc;
100 18 : if (INDEX_INVALID == cci)
101 2 : return;
102 :
103 16 : cc = cnat_client_get (cci);
104 16 : cc->tr_refcnt++;
105 : }
106 :
107 : void
108 18 : cnat_client_translation_deleted (index_t cci)
109 : {
110 : cnat_client_t *cc;
111 18 : if (INDEX_INVALID == cci)
112 6 : return;
113 :
114 12 : cc = cnat_client_get (cci);
115 12 : cc->tr_refcnt--;
116 :
117 12 : if (0 == cc->tr_refcnt && 0 == cc->session_refcnt)
118 0 : cnat_client_destroy (cc);
119 : }
120 :
121 : static void
122 34 : cnat_client_db_add (cnat_client_t * cc)
123 : {
124 : index_t cci;
125 :
126 34 : cci = cc - cnat_client_pool;
127 :
128 34 : if (ip_addr_version (&cc->cc_ip) == AF_IP4)
129 17 : hash_set (cnat_client_db.crd_cip4, ip_addr_v4 (&cc->cc_ip).as_u32, cci);
130 : else
131 17 : hash_set_mem_alloc (&cnat_client_db.crd_cip6,
132 17 : &ip_addr_v6 (&cc->cc_ip), cci);
133 34 : }
134 :
135 :
136 : index_t
137 46 : cnat_client_add (const ip_address_t * ip, u8 flags)
138 : {
139 : cnat_client_t *cc;
140 46 : dpo_id_t tmp = DPO_INVALID;
141 : fib_node_index_t fei;
142 : dpo_proto_t dproto;
143 : fib_prefix_t pfx;
144 : index_t cci;
145 : u32 fib_flags;
146 :
147 : /* check again if we need this client */
148 92 : cc = (AF_IP4 == ip->version ?
149 46 : cnat_client_ip4_find (&ip->ip.ip4) :
150 23 : cnat_client_ip6_find (&ip->ip.ip6));
151 :
152 46 : if (NULL != cc)
153 12 : return (cc - cnat_client_pool);
154 :
155 :
156 34 : pool_get_aligned (cnat_client_pool, cc, CLIB_CACHE_LINE_BYTES);
157 34 : cc->cc_locks = 1;
158 34 : cci = cc - cnat_client_pool;
159 34 : cc->parent_cci = cci;
160 34 : cc->flags = flags;
161 34 : cc->tr_refcnt = 0;
162 34 : cc->session_refcnt = 0;
163 :
164 34 : ip_address_copy (&cc->cc_ip, ip);
165 34 : cnat_client_db_add (cc);
166 :
167 34 : ip_address_to_fib_prefix (&cc->cc_ip, &pfx);
168 :
169 34 : dproto = fib_proto_to_dpo (pfx.fp_proto);
170 34 : dpo_set (&tmp, cnat_client_dpo, dproto, cci);
171 34 : dpo_stack (cnat_client_dpo, dproto, &cc->cc_parent, drop_dpo_get (dproto));
172 :
173 34 : fib_flags = FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT;
174 68 : fib_flags |= (flags & CNAT_FLAG_EXCLUSIVE) ?
175 34 : FIB_ENTRY_FLAG_EXCLUSIVE : FIB_ENTRY_FLAG_INTERPOSE;
176 :
177 34 : fei = fib_table_entry_special_dpo_add (CNAT_FIB_TABLE,
178 : &pfx, cnat_fib_source, fib_flags,
179 : &tmp);
180 :
181 34 : cc = pool_elt_at_index (cnat_client_pool, cci);
182 34 : cc->cc_fei = fei;
183 :
184 34 : return (cci);
185 : }
186 :
187 : void
188 24 : cnat_client_learn (const ip_address_t *addr)
189 : {
190 : /* RPC call to add a client from the dataplane */
191 : index_t cci;
192 : cnat_client_t *cc;
193 24 : cci = cnat_client_add (addr, 0 /* flags */);
194 24 : cc = pool_elt_at_index (cnat_client_pool, cci);
195 24 : cnat_client_cnt_session (cc);
196 : /* Process throttled calls if any */
197 24 : cnat_client_throttle_pool_process ();
198 24 : }
199 :
200 : /**
201 : * Interpose a policy DPO
202 : */
203 : static void
204 24 : cnat_client_dpo_interpose (const dpo_id_t * original,
205 : const dpo_id_t * parent, dpo_id_t * clone)
206 : {
207 : cnat_client_t *cc, *cc_clone;
208 :
209 24 : pool_get_zero (cnat_client_pool, cc_clone);
210 24 : cc = cnat_client_get (original->dpoi_index);
211 :
212 24 : cc_clone->cc_fei = FIB_NODE_INDEX_INVALID;
213 24 : cc_clone->parent_cci = cc->parent_cci;
214 24 : cc_clone->flags = cc->flags;
215 24 : ip_address_copy (&cc_clone->cc_ip, &cc->cc_ip);
216 :
217 : /* stack the clone on the FIB provided parent */
218 24 : dpo_stack (cnat_client_dpo, original->dpoi_proto, &cc_clone->cc_parent,
219 : parent);
220 :
221 : /* return the clone */
222 24 : dpo_set (clone,
223 : cnat_client_dpo,
224 24 : original->dpoi_proto, cc_clone - cnat_client_pool);
225 24 : }
226 :
227 : int
228 0 : cnat_client_purge (void)
229 : {
230 0 : int rv = 0, rrv = 0;
231 0 : if ((rv = hash_elts (cnat_client_db.crd_cip6)))
232 0 : clib_warning ("len(crd_cip6) isnt 0 but %d", rv);
233 0 : rrv |= rv;
234 0 : if ((rv = hash_elts (cnat_client_db.crd_cip4)))
235 0 : clib_warning ("len(crd_cip4) isnt 0 but %d", rv);
236 0 : rrv |= rv;
237 0 : if ((rv = pool_elts (cnat_client_pool)))
238 0 : clib_warning ("len(cnat_client_pool) isnt 0 but %d", rv);
239 0 : rrv |= rv;
240 0 : if ((rv = hash_elts (cnat_client_db.throttle_mem)))
241 0 : clib_warning ("len(throttle_mem) isnt 0 but %d", rv);
242 0 : rrv |= rv;
243 0 : return (rrv);
244 : }
245 :
246 : u8 *
247 5 : format_cnat_client (u8 * s, va_list * args)
248 : {
249 5 : index_t cci = va_arg (*args, index_t);
250 5 : u32 indent = va_arg (*args, u32);
251 :
252 5 : cnat_client_t *cc = pool_elt_at_index (cnat_client_pool, cci);
253 :
254 5 : s = format (s, "[%d] cnat-client:[%U] tr:%d sess:%d", cci,
255 : format_ip_address, &cc->cc_ip,
256 : cc->tr_refcnt, cc->session_refcnt);
257 :
258 5 : if (cc->flags & CNAT_FLAG_EXCLUSIVE)
259 5 : s = format (s, " exclusive");
260 :
261 5 : if (cnat_client_is_clone (cc))
262 0 : s = format (s, "\n%Uclone of [%d]\n%U%U",
263 : format_white_space, indent + 2, cc->parent_cci,
264 : format_white_space, indent + 2,
265 : format_dpo_id, &cc->cc_parent, indent + 4);
266 :
267 5 : return (s);
268 : }
269 :
270 :
271 : static clib_error_t *
272 2 : cnat_client_show (vlib_main_t * vm,
273 : unformat_input_t * input, vlib_cli_command_t * cmd)
274 : {
275 : index_t cci;
276 :
277 2 : cci = INDEX_INVALID;
278 :
279 2 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
280 : {
281 0 : if (unformat (input, "%d", &cci))
282 : ;
283 : else
284 0 : return (clib_error_return (0, "unknown input '%U'",
285 : format_unformat_error, input));
286 : }
287 :
288 2 : if (INDEX_INVALID == cci)
289 : {
290 7 : pool_foreach_index (cci, cnat_client_pool)
291 5 : vlib_cli_output(vm, "%U", format_cnat_client, cci, 0);
292 :
293 2 : vlib_cli_output (vm, "%d clients", pool_elts (cnat_client_pool));
294 2 : vlib_cli_output (vm, "%d timestamps", pool_elts (cnat_timestamps));
295 : }
296 : else
297 : {
298 0 : vlib_cli_output (vm, "Invalid policy ID:%d", cci);
299 : }
300 :
301 2 : return (NULL);
302 : }
303 :
304 245447 : VLIB_CLI_COMMAND (cnat_client_show_cmd_node, static) = {
305 : .path = "show cnat client",
306 : .function = cnat_client_show,
307 : .short_help = "show cnat client",
308 : .is_mp_safe = 1,
309 : };
310 :
311 : const static char *const cnat_client_dpo_ip4_nodes[] = {
312 : "ip4-cnat-tx",
313 : NULL,
314 : };
315 :
316 : const static char *const cnat_client_dpo_ip6_nodes[] = {
317 : "ip6-cnat-tx",
318 : NULL,
319 : };
320 :
321 : const static char *const *const cnat_client_dpo_nodes[DPO_PROTO_NUM] = {
322 : [DPO_PROTO_IP4] = cnat_client_dpo_ip4_nodes,
323 : [DPO_PROTO_IP6] = cnat_client_dpo_ip6_nodes,
324 : };
325 :
326 : static void
327 228 : cnat_client_dpo_lock (dpo_id_t * dpo)
328 : {
329 : cnat_client_t *cc;
330 :
331 228 : cc = cnat_client_get (dpo->dpoi_index);
332 :
333 228 : cc->cc_locks++;
334 228 : }
335 :
336 : static void
337 176 : cnat_client_dpo_unlock (dpo_id_t * dpo)
338 : {
339 : cnat_client_t *cc;
340 :
341 176 : cc = cnat_client_get (dpo->dpoi_index);
342 :
343 176 : cc->cc_locks--;
344 :
345 176 : if (0 == cc->cc_locks)
346 : {
347 24 : ASSERT (cnat_client_is_clone (cc));
348 24 : dpo_reset (&cc->cc_parent);
349 24 : pool_put (cnat_client_pool, cc);
350 : }
351 176 : }
352 :
353 : u8 *
354 0 : format_cnat_client_dpo (u8 * s, va_list * ap)
355 : {
356 0 : index_t cci = va_arg (*ap, index_t);
357 0 : u32 indent = va_arg (*ap, u32);
358 :
359 0 : s = format (s, "%U", format_cnat_client, cci, indent);
360 :
361 0 : return (s);
362 : }
363 :
364 : const static dpo_vft_t cnat_client_dpo_vft = {
365 : .dv_lock = cnat_client_dpo_lock,
366 : .dv_unlock = cnat_client_dpo_unlock,
367 : .dv_format = format_cnat_client_dpo,
368 : .dv_mk_interpose = cnat_client_dpo_interpose,
369 : };
370 :
371 : static clib_error_t *
372 559 : cnat_client_init (vlib_main_t * vm)
373 : {
374 559 : cnat_client_dpo = dpo_register_new_type (&cnat_client_dpo_vft,
375 : cnat_client_dpo_nodes);
376 :
377 559 : cnat_client_db.crd_cip6 = hash_create_mem (0,
378 : sizeof (ip6_address_t),
379 : sizeof (uword));
380 :
381 559 : clib_spinlock_init (&cnat_client_db.throttle_lock);
382 559 : cnat_client_db.throttle_mem =
383 559 : hash_create_mem (0, sizeof (ip_address_t), sizeof (uword));
384 :
385 559 : return (NULL);
386 : }
387 :
388 1119 : VLIB_INIT_FUNCTION (cnat_client_init);
389 :
390 : /*
391 : * fd.io coding-style-patch-verification: ON
392 : *
393 : * Local Variables:
394 : * eval: (c-set-style "gnu")
395 : * End:
396 : */
|