2 * snat.c - simple nat plugin
4 * Copyright (c) 2016 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:
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <vnet/vnet.h>
19 #include <vnet/plugin/plugin.h>
20 #include <vlibapi/api.h>
21 #include <snat/snat.h>
23 #include <vlibapi/api.h>
24 #include <vlibmemory/api.h>
25 #include <vlibsocket/api.h>
27 snat_main_t snat_main;
29 /* define message IDs */
30 #include <snat/snat_msg_enum.h>
32 /* define message structures */
34 #include <snat/snat_all_api_h.h>
37 /* define generated endian-swappers */
39 #include <snat/snat_all_api_h.h>
42 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
44 /* Get the API version number */
45 #define vl_api_version(n,v) static u32 api_version=(v);
46 #include <snat/snat_all_api_h.h>
49 /* Macro to finish up custom dump fns */
52 vl_print (handle, (char *)s); \
57 * A handy macro to set up a message reply.
58 * Assumes that the following variables are available:
59 * mp - pointer to request message
60 * rmp - pointer to reply message type
64 #define REPLY_MACRO(t) \
66 unix_shared_memory_queue_t * q = \
67 vl_api_client_index_to_input_queue (mp->client_index); \
71 rmp = vl_msg_api_alloc (sizeof (*rmp)); \
72 rmp->_vl_msg_id = ntohs((t)+sm->msg_id_base); \
73 rmp->context = mp->context; \
74 rmp->retval = ntohl(rv); \
76 vl_msg_api_send_shmem (q, (u8 *)&rmp); \
80 /* Hook up input features */
81 VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_in2out, static) = {
82 .node_name = "snat-in2out",
83 .runs_before = {"snat-out2in", 0},
84 .feature_index = &snat_main.rx_feature_in2out,
86 VNET_IP4_UNICAST_FEATURE_INIT (ip4_snat_out2in, static) = {
87 .node_name = "snat-out2in",
88 .runs_before = {"ip4-lookup", 0},
89 .feature_index = &snat_main.rx_feature_out2in,
93 * This routine exists to convince the vlib plugin framework that
94 * we haven't accidentally copied a random .dll into the plugin directory.
96 * Also collects global variable pointers passed from the vpp engine
100 vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
103 snat_main_t * sm = &snat_main;
104 clib_error_t * error = 0;
107 sm->vnet_main = h->vnet_main;
108 sm->ethernet_main = h->ethernet_main;
113 /*$$$$$ move to an installed header file */
114 #if (1 || CLIB_DEBUG > 0) /* "trust, but verify" */
116 #define VALIDATE_SW_IF_INDEX(mp) \
117 do { u32 __sw_if_index = ntohl(mp->sw_if_index); \
118 vnet_main_t *__vnm = vnet_get_main(); \
119 if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
121 rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \
122 goto bad_sw_if_index; \
126 #define BAD_SW_IF_INDEX_LABEL \
132 #define VALIDATE_RX_SW_IF_INDEX(mp) \
133 do { u32 __rx_sw_if_index = ntohl(mp->rx_sw_if_index); \
134 vnet_main_t *__vnm = vnet_get_main(); \
135 if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
136 __rx_sw_if_index)) { \
137 rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \
138 goto bad_rx_sw_if_index; \
142 #define BAD_RX_SW_IF_INDEX_LABEL \
144 bad_rx_sw_if_index: \
148 #define VALIDATE_TX_SW_IF_INDEX(mp) \
149 do { u32 __tx_sw_if_index = ntohl(mp->tx_sw_if_index); \
150 vnet_main_t *__vnm = vnet_get_main(); \
151 if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
152 __tx_sw_if_index)) { \
153 rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \
154 goto bad_tx_sw_if_index; \
158 #define BAD_TX_SW_IF_INDEX_LABEL \
160 bad_tx_sw_if_index: \
166 #define VALIDATE_SW_IF_INDEX(mp)
167 #define BAD_SW_IF_INDEX_LABEL
168 #define VALIDATE_RX_SW_IF_INDEX(mp)
169 #define BAD_RX_SW_IF_INDEX_LABEL
170 #define VALIDATE_TX_SW_IF_INDEX(mp)
171 #define BAD_TX_SW_IF_INDEX_LABEL
173 #endif /* CLIB_DEBUG > 0 */
175 void snat_add_address (snat_main_t *sm, ip4_address_t *addr)
179 vec_add2 (sm->addresses, ap, 1);
184 static void increment_v4_address (ip4_address_t * a)
188 v = clib_net_to_host_u32(a->as_u32) + 1;
189 a->as_u32 = clib_host_to_net_u32(v);
193 vl_api_snat_add_address_range_t_handler
194 (vl_api_snat_add_address_range_t * mp)
196 snat_main_t * sm = &snat_main;
197 vl_api_snat_add_address_range_reply_t * rmp;
198 ip4_address_t this_addr;
199 u32 start_host_order, end_host_order;
206 rv = VNET_API_ERROR_UNIMPLEMENTED;
210 tmp = (u32 *) mp->first_ip_address;
211 start_host_order = clib_host_to_net_u32 (tmp[0]);
212 tmp = (u32 *) mp->last_ip_address;
213 end_host_order = clib_host_to_net_u32 (tmp[0]);
215 count = (end_host_order - start_host_order) + 1;
218 clib_warning ("%U - %U, %d addresses...",
219 format_ip4_address, mp->first_ip_address,
220 format_ip4_address, mp->last_ip_address,
223 memcpy (&this_addr.as_u8, mp->first_ip_address, 4);
225 for (i = 0; i < count; i++)
227 snat_add_address (sm, &this_addr);
228 increment_v4_address (&this_addr);
232 REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY);
235 static void *vl_api_snat_add_address_range_t_print
236 (vl_api_snat_add_address_range_t *mp, void * handle)
240 s = format (0, "SCRIPT: snat_add_address_range ");
241 s = format (s, "%U ", format_ip4_address, mp->first_ip_address);
242 if (memcmp (mp->first_ip_address, mp->last_ip_address, 4))
244 s = format (s, " - %U ", format_ip4_address, mp->last_ip_address);
250 vl_api_snat_interface_add_del_feature_t_handler
251 (vl_api_snat_interface_add_del_feature_t * mp)
253 snat_main_t * sm = &snat_main;
254 vl_api_snat_interface_add_del_feature_reply_t * rmp;
255 u8 is_del = mp->is_add == 0;
256 u32 sw_if_index = ntohl(mp->sw_if_index);
258 ip4_main_t * im = &ip4_main;
259 ip_lookup_main_t * lm = &im->lookup_main;
260 ip_config_main_t * rx_cm = &lm->rx_config_mains[VNET_UNICAST];
264 VALIDATE_SW_IF_INDEX(mp);
266 feature_index = mp->is_inside ? sm->rx_feature_in2out
267 : sm->rx_feature_out2in;
269 ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
271 ? vnet_config_del_feature
272 : vnet_config_add_feature)
273 (sm->vlib_main, &rx_cm->config_main,
276 0 /* config struct */,
277 0 /* sizeof config struct*/);
278 rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
280 BAD_SW_IF_INDEX_LABEL;
282 REPLY_MACRO(VL_API_SNAT_INTERFACE_ADD_DEL_FEATURE_REPLY);
285 static void *vl_api_snat_interface_add_del_feature_t_print
286 (vl_api_snat_interface_add_del_feature_t * mp, void *handle)
290 s = format (0, "SCRIPT: snat_interface_add_del_feature ");
291 s = format (s, "sw_if_index %d %s %s",
292 clib_host_to_net_u32(mp->sw_if_index),
293 mp->is_inside ? "in":"out",
294 mp->is_add ? "" : "del");
299 /* List of message types that this plugin understands */
300 #define foreach_snat_plugin_api_msg \
301 _(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range) \
302 _(SNAT_INTERFACE_ADD_DEL_FEATURE, snat_interface_add_del_feature)
304 /* Set up the API message handling tables */
305 static clib_error_t *
306 snat_plugin_api_hookup (vlib_main_t *vm)
308 snat_main_t * sm __attribute__ ((unused)) = &snat_main;
310 vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \
312 vl_api_##n##_t_handler, \
314 vl_api_##n##_t_endian, \
315 vl_api_##n##_t_print, \
316 sizeof(vl_api_##n##_t), 1);
317 foreach_snat_plugin_api_msg;
323 static void plugin_custom_dump_configure (snat_main_t * sm)
325 #define _(n,f) sm->api_main->msg_print_handlers \
326 [VL_API_##n + sm->msg_id_base] \
327 = (void *) vl_api_##f##_t_print;
328 foreach_snat_plugin_api_msg;
332 static clib_error_t * snat_init (vlib_main_t * vm)
334 snat_main_t * sm = &snat_main;
335 clib_error_t * error = 0;
336 ip4_main_t * im = &ip4_main;
337 ip_lookup_main_t * lm = &im->lookup_main;
340 name = format (0, "snat_%08x%c", api_version, 0);
342 /* Ask for a correctly-sized block of API message decode slots */
343 sm->msg_id_base = vl_msg_api_get_msg_ids
344 ((char *) name, VL_MSG_FIRST_AVAILABLE);
347 sm->vnet_main = vnet_get_main();
349 sm->ip4_lookup_main = lm;
350 sm->api_main = &api_main;
352 error = snat_plugin_api_hookup (vm);
353 plugin_custom_dump_configure (sm);
359 VLIB_INIT_FUNCTION (snat_init);
361 void snat_free_outside_address_and_port (snat_main_t * sm,
362 snat_session_key_t * k,
366 u16 port_host_byte_order = clib_net_to_host_u16 (k->port);
368 ASSERT (address_index < vec_len (sm->addresses));
370 a = sm->addresses + address_index;
372 ASSERT (clib_bitmap_get (a->busy_port_bitmap, port_host_byte_order) == 1);
374 a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap,
375 port_host_byte_order, 0);
379 int snat_alloc_outside_address_and_port (snat_main_t * sm,
380 snat_session_key_t * k,
381 u32 * address_indexp)
387 for (i = 0; i < vec_len (sm->addresses); i++)
389 if (sm->addresses[i].busy_ports < (65535-1024))
391 a = sm->addresses + i;
395 portnum = random_u32 (&sm->random_seed);
399 if (clib_bitmap_get (a->busy_port_bitmap, portnum))
401 a->busy_port_bitmap = clib_bitmap_set (a->busy_port_bitmap,
404 /* Caller sets protocol and fib index */
406 k->port = clib_host_to_net_u16(portnum);
412 /* Totally out of translations to use... */
417 static clib_error_t *
418 add_address_command_fn (vlib_main_t * vm,
419 unformat_input_t * input,
420 vlib_cli_command_t * cmd)
422 snat_main_t * sm = &snat_main;
423 ip4_address_t start_addr, end_addr, this_addr;
424 u32 start_host_order, end_host_order;
427 if (unformat (input, "%U - %U",
428 unformat_ip4_address, &start_addr,
429 unformat_ip4_address, &end_addr))
431 else if (unformat (input, "%U", unformat_ip4_address, &start_addr))
432 end_addr = start_addr;
434 start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
435 end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
437 if (end_host_order < start_host_order)
438 return clib_error_return (0, "end address less than start address");
440 count = (end_host_order - start_host_order) + 1;
443 clib_warning ("%U - %U, %d addresses...",
444 format_ip4_address, &start_addr,
445 format_ip4_address, &end_addr,
448 this_addr = start_addr;
450 for (i = 0; i < count; i++)
452 snat_add_address (sm, &this_addr);
453 increment_v4_address (&this_addr);
459 VLIB_CLI_COMMAND (add_address_command, static) = {
460 .path = "snat add address",
461 .short_help = "snat add addresses <ip4-range-start> [- <ip4-range-end>]",
462 .function = add_address_command_fn,
465 static clib_error_t *
466 snat_feature_command_fn (vlib_main_t * vm,
467 unformat_input_t * input,
468 vlib_cli_command_t * cmd)
470 vnet_main_t * vnm = vnet_get_main();
471 snat_main_t * sm = &snat_main;
472 ip4_main_t * im = &ip4_main;
473 ip_lookup_main_t * lm = &im->lookup_main;
474 ip_config_main_t * rx_cm = &lm->rx_config_mains[VNET_UNICAST];
475 clib_error_t * error = 0;
478 u32 * inside_sw_if_indices = 0;
479 u32 * outside_sw_if_indices = 0;
485 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
487 if (unformat (input, "in %U", unformat_vnet_sw_interface,
489 vec_add1 (inside_sw_if_indices, sw_if_index);
490 else if (unformat (input, "out %U", unformat_vnet_sw_interface,
492 vec_add1 (outside_sw_if_indices, sw_if_index);
493 else if (unformat (input, "del"))
499 if (vec_len (inside_sw_if_indices))
501 feature_index = sm->rx_feature_in2out;
503 for (i = 0; i < vec_len(inside_sw_if_indices); i++)
505 sw_if_index = inside_sw_if_indices[i];
506 ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
508 ? vnet_config_del_feature
509 : vnet_config_add_feature)
510 (vm, &rx_cm->config_main,
513 0 /* config struct */,
514 0 /* sizeof config struct*/);
515 rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
519 if (vec_len (outside_sw_if_indices))
521 feature_index = sm->rx_feature_out2in;
523 for (i = 0; i < vec_len(outside_sw_if_indices); i++)
525 sw_if_index = outside_sw_if_indices[i];
526 ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
528 ? vnet_config_del_feature
529 : vnet_config_add_feature)
530 (vm, &rx_cm->config_main,
533 0 /* config struct */,
534 0 /* sizeof config struct*/);
535 rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
539 vec_free (inside_sw_if_indices);
540 vec_free (outside_sw_if_indices);
545 VLIB_CLI_COMMAND (set_interface_snat_command, static) = {
546 .path = "set interface snat",
547 .function = snat_feature_command_fn,
548 .short_help = "set interface snat in <intfc> out <intfc> [del]",
551 static clib_error_t *
552 snat_config (vlib_main_t * vm, unformat_input_t * input)
554 snat_main_t * sm = &snat_main;
555 u32 translation_buckets = 1024;
556 u32 translation_memory_size = 128<<20;
557 u32 user_buckets = 128;
558 u32 user_memory_size = 64<<20;
559 u32 max_translations_per_user = 100;
560 u32 outside_vrf_id = 0;
562 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
564 if (unformat (input, "translation hash buckets %d", &translation_buckets))
566 else if (unformat (input, "translation hash memory %d",
567 &translation_memory_size));
568 else if (unformat (input, "user hash buckets %d", &user_buckets))
570 else if (unformat (input, "user hash memory %d",
573 else if (unformat (input, "max translations per user %d",
574 &max_translations_per_user))
576 else if (unformat (input, "outside VRF id %d",
580 return clib_error_return (0, "unknown input `%U'",
581 format_unformat_error, input);
584 /* for show commands, etc. */
585 sm->translation_buckets = translation_buckets;
586 sm->translation_memory_size = translation_memory_size;
587 sm->user_buckets = user_buckets;
588 sm->user_memory_size = user_memory_size;
589 sm->max_translations_per_user = max_translations_per_user;
590 sm->outside_vrf_id = outside_vrf_id;
592 clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets,
593 translation_memory_size);
595 clib_bihash_init_8_8 (&sm->out2in, "out2in", translation_buckets,
596 translation_memory_size);
598 clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets,
603 VLIB_CONFIG_FUNCTION (snat_config, "snat");
605 u8 * format_snat_key (u8 * s, va_list * args)
607 snat_session_key_t * key = va_arg (*args, snat_session_key_t *);
608 char * protocol_string = "unknown";
609 static char *protocol_strings[] = {
615 if (key->protocol < ARRAY_LEN(protocol_strings))
616 protocol_string = protocol_strings[key->protocol];
618 s = format (s, "%U proto %s port %d fib %d",
619 format_ip4_address, &key->addr, protocol_string,
620 key->port, key->fib_index);
624 u8 * format_snat_session (u8 * s, va_list * args)
626 snat_main_t * sm __attribute__((unused)) = va_arg (*args, snat_main_t *);
627 snat_session_t * sess = va_arg (*args, snat_session_t *);
629 s = format (s, " i2o %U\n", format_snat_key, &sess->in2out);
630 s = format (s, " o2i %U\n", format_snat_key, &sess->out2in);
631 s = format (s, " last heard %.2f\n", sess->last_heard);
632 s = format (s, " total pkts %d, total bytes %lld\n",
633 sess->total_pkts, sess->total_bytes);
638 u8 * format_snat_user (u8 * s, va_list * args)
640 snat_main_t * sm = va_arg (*args, snat_main_t *);
641 snat_user_t * u = va_arg (*args, snat_user_t *);
642 int verbose = va_arg (*args, int);
643 dlist_elt_t * head, * elt;
644 u32 elt_index, head_index;
646 snat_session_t * sess;
648 s = format (s, "%U: %d translations\n",
649 format_ip4_address, &u->addr, u->nsessions);
654 head_index = u->sessions_per_user_list_head_index;
655 head = pool_elt_at_index (sm->list_pool, head_index);
657 elt_index = head->next;
658 elt = pool_elt_at_index (sm->list_pool, elt_index);
659 session_index = elt->value;
661 while (session_index != ~0)
663 sess = pool_elt_at_index (sm->sessions, session_index);
665 s = format (s, " %U\n", format_snat_session, sm, sess);
667 elt_index = elt->next;
668 elt = pool_elt_at_index (sm->list_pool, elt_index);
669 session_index = elt->value;
675 static clib_error_t *
676 show_snat_command_fn (vlib_main_t * vm,
677 unformat_input_t * input,
678 vlib_cli_command_t * cmd)
681 snat_main_t * sm = &snat_main;
684 if (unformat (input, "detail"))
686 else if (unformat (input, "verbose"))
689 vlib_cli_output (vm, "%d users, %d outside addresses, %d active sessions",
690 pool_elts (sm->users),
691 vec_len (sm->addresses),
692 pool_elts (sm->sessions));
696 vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->in2out,
698 vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in,
700 vlib_cli_output (vm, "%d list pool elements",
701 pool_elts (sm->list_pool));
703 pool_foreach (u, sm->users,
705 vlib_cli_output (vm, "%U", format_snat_user, sm, u, verbose - 1);
712 VLIB_CLI_COMMAND (show_snat_command, static) = {
714 .short_help = "show snat",
715 .function = show_snat_command_fn,