summaryrefslogtreecommitdiffstats
path: root/src/vlibsocket/sockclnt_vlib.c
blob: e16adfeb503d810df4a1c190b2ccc3094518eac1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
 *------------------------------------------------------------------
 * sockclnt_vlib.c
 *
 * Copyright (c) 2009 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *------------------------------------------------------------------
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <vppinfra/byte_order.h>
#include <netdb.h>

#include <fcntl.h>
#include <sys/stat.h>

#include <vlibmemory/api.h>
#include <vlibsocket/api.h>

#include <vlibsocket/vl_socket_msg_enum.h>

#define vl_typedefs		/* define message structures */
#include <vlibsocket/vl_socket_api_h.h>
#undef vl_typedefs

/* instantiate all the print functions we know about */
#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
#define vl_printfun
#include <vlibsocket/vl_socket_api_h.h>
#undef vl_printfun

/* instantiate all the endian swap functions we know about */
#define vl_endianfun
#include <vlibsocket/vl_socket_api_h.h>
#undef vl_endianfun

static void
vl_api_sockclnt_create_reply_t_handler (vl_api_sockclnt_create_reply_t * mp)
{
  vl_api_registration_t *rp = socket_main.current_rp;

  rp->server_handle = mp->handle;
  rp->server_index = mp->index;
}

static void
vl_api_sockclnt_delete_reply_t_handler (vl_api_sockclnt_delete_reply_t * mp)
{
  unix_main_t *um = &unix_main;
  unix_file_t *uf = socket_main.current_uf;
  vl_api_registration_t *rp = socket_main.current_rp;

  unix_file_del (um, uf);
  vl_free_socket_registration_index (rp->vl_api_registration_pool_index);
}

u32
sockclnt_open_index (char *client_name, char *hostname, int port)
{
  vl_api_registration_t *rp;
  unix_main_t *um = &unix_main;
  unix_file_t template = { 0 };
  int sockfd;
  int one = 1;
  int rv;
  struct sockaddr_in serv_addr;
  struct hostent *server;
  vl_api_sockclnt_create_t *mp;
  char my_hostname[64];

  server = gethostbyname (hostname);
  if (server == NULL)
    {
      clib_warning ("Couldn't translate server name %s", hostname);
      return ~0;
    }

  /* Set up non-blocking server socket on CLIENT_API_SERVER_PORT */
  sockfd = socket (AF_INET, SOCK_STREAM, 0);

  if (sockfd < 0)
    {
      clib_unix_warning ("socket");
      return ~0;
    }

  bzero ((char *) &serv_addr, sizeof (serv_addr));
  serv_addr.sin_family = AF_INET;
  bcopy ((char *) server->h_addr,
	 (char *) &serv_addr.sin_addr.s_addr, server->h_length);
  serv_addr.sin_port = htons (port);

  if (connect (sockfd, (const void *) &serv_addr, sizeof (serv_addr)) < 0)
    {
      clib_unix_warning ("Connect failure to (%s, %d)", hostname, port);
      close (sockfd);
      return ~0;
    }

  rv = ioctl (sockfd, FIONBIO, &one);
  if (rv < 0)
    {
      clib_unix_warning ("FIONBIO");
      close (sockfd);
      return ~0;
    }

  pool_get (socket_main.registration_pool, rp);
  memset (rp, 0, sizeof (*rp));
  rp->registration_type = REGISTRATION_TYPE_SOCKET_CLIENT;
  rp->vl_api_registration_pool_index = rp - socket_main.registration_pool;

  template.read_function = vl_socket_read_ready;
  template.write_function = vl_socket_write_ready;
  template.file_descriptor = sockfd;
  template.private_data = rp - socket_main.registration_pool;

  rp->unix_file_index = unix_file_add (um, &template);
  rp->name = format (0, "%s:%d", hostname, port);

  mp = vl_msg_api_alloc (sizeof (*mp));
  mp->_vl_msg_id = ntohs (VL_API_SOCKCLNT_CREATE);
  mp->context = rp - socket_main.registration_pool;

  if (gethostname (my_hostname, sizeof (my_hostname)) < 0)
    {
      clib_unix_warning ("gethostname");
      strncpy (my_hostname, "unknown!", sizeof (my_hostname) - 1);
    }
  strncpy ((char *) mp->name, my_hostname, sizeof (mp->name) - 1);

  vl_msg_api_send (rp, (u8 *) mp);
  return rp - socket_main.registration_pool;
}

void
sockclnt_close_index (u32 index)
{
  vl_api_sockclnt_delete_t *mp;
  vl_api_registration_t *rp;

  /* Don't crash / assert if fed garbage */
  if (pool_is_free_index (socket_main.registration_pool, index))
    {
      clib_warning ("registration_pool index %d already free", index);
      return;
    }
  rp = pool_elt_at_index (socket_main.registration_pool, index);

  mp = vl_msg_api_alloc (sizeof (*mp));
  mp->_vl_msg_id = ntohs (VL_API_SOCKCLNT_DELETE);
  mp->handle = rp->server_handle;
  mp->index = rp->server_index;
  vl_msg_api_send (rp, (u8 *) mp);
}

vl_api_registration_t *
sockclnt_get_registration (u32 index)
{
  return pool_elt_at_index (socket_main.registration_pool, index);
}

/*
 * Both rx and tx msgs MUST be initialized, or we'll have
 * precisely no idea how many bytes to write into the API trace...
 */
#define foreach_sockclnt_api_msg                        \
_(SOCKCLNT_CREATE_REPLY, sockclnt_create_reply)         \
_(SOCKCLNT_DELETE_REPLY, sockclnt_delete_reply)


static clib_error_t *
sockclnt_vlib_api_init (vlib_main_t * vm)
{
#define _(N,n)                                                  \
    vl_msg_api_set_handlers(VL_API_##N, #n,                     \
                           vl_api_##n##_t_handler,              \
                           vl_noop_handler,                     \
                           vl_api_##n##_t_endian,               \
                           vl_api_##n##_t_print,                \
                           sizeof(vl_api_##n##_t), 1);
  foreach_sockclnt_api_msg;
#undef _
  return 0;
}

VLIB_API_INIT_FUNCTION (sockclnt_vlib_api_init);

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
>~0) return clib_error_return (0, "Please specify inside or outside"); if (sw_if_index == ~0) return clib_error_return (0, "Please specify an interface..."); if (inside == 1) rv = ct6_in2out_enable_disable (cmp, sw_if_index, enable_disable); else rv = ct6_out2in_enable_disable (cmp, sw_if_index, enable_disable); switch (rv) { case 0: break; case VNET_API_ERROR_INVALID_SW_IF_INDEX: return clib_error_return (0, "Invalid interface, only works on physical ports"); break; default: return clib_error_return (0, "ct6_enable_disable returned %d", rv); } return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (set_ct6_command, static) = { .path = "set ct6", .short_help = "set ct6 [inside|outside] <interface-name> [disable]", .function = set_ct6_enable_disable_command_fn, }; /* *INDENT-ON* */ /* API message handler */ static void vl_api_ct6_enable_disable_t_handler (vl_api_ct6_enable_disable_t * mp) { vl_api_ct6_enable_disable_reply_t *rmp; ct6_main_t *cmp = &ct6_main; int rv; VALIDATE_SW_IF_INDEX (mp); if (mp->is_inside) rv = ct6_in2out_enable_disable (cmp, ntohl (mp->sw_if_index), (int) (mp->enable_disable)); else rv = ct6_out2in_enable_disable (cmp, ntohl (mp->sw_if_index), (int) (mp->enable_disable)); BAD_SW_IF_INDEX_LABEL; REPLY_MACRO (VL_API_CT6_ENABLE_DISABLE_REPLY); } #include <ct6/ct6.api.c> static clib_error_t * ct6_init (vlib_main_t * vm) { ct6_main_t *cmp = &ct6_main; clib_error_t *error = 0; cmp->vlib_main = vm; cmp->vnet_main = vnet_get_main (); /* Ask for a correctly-sized block of API message decode slots */ cmp->msg_id_base = setup_message_id_table (); /* * Set default parameters... * 256K sessions * 64K buckets * 2 minute inactivity timer * 10000 concurrent sessions */ cmp->session_hash_memory = 16ULL << 20; cmp->session_hash_buckets = 64 << 10; cmp->session_timeout_interval = 120.0; cmp->max_sessions_per_worker = 10000; /* ... so the packet generator can feed the in2out node ... */ ethernet_setup_node (vm, ct6_in2out_node.index); return error; } VLIB_INIT_FUNCTION (ct6_init); /* *INDENT-OFF* */ VNET_FEATURE_INIT (ct6out2in, static) = { .arc_name = "ip6-unicast", .node_name = "ct6-out2in", .runs_before = VNET_FEATURES ("ip6-lookup"), }; /* *INDENT-ON */ /* *INDENT-OFF* */ VNET_FEATURE_INIT (ct6in2out, static) = { .arc_name = "interface-output", .node_name = "ct6-in2out", .runs_before = VNET_FEATURES ("interface-tx"), }; /* *INDENT-ON */ /* *INDENT-OFF* */ VLIB_PLUGIN_REGISTER () = { .version = VPP_BUILD_VER, .description = "IPv6 Connection Tracker", }; /* *INDENT-ON* */ u8 * format_ct6_session (u8 * s, va_list * args) { ct6_main_t *cmp = va_arg (*args, ct6_main_t *); int i = va_arg (*args, int); ct6_session_t *s0 = va_arg (*args, ct6_session_t *); int verbose = va_arg (*args, int); clib_bihash_kv_48_8_t kvp0; if (s0 == 0) { s = format (s, "\n%6s%6s%40s%6s%40s%6s", "Sess", "Prot", "Src", "Sport", "Dst", "Dport"); return s; } s = format (s, "\n%6d%6d%40U%6u%40U%6u", s0 - cmp->sessions[i], s0->key.proto, format_ip6_address, &s0->key.src, clib_net_to_host_u16 (s0->key.sport), format_ip6_address, &s0->key.dst, clib_net_to_host_u16 (s0->key.dport)); clib_memcpy_fast (&kvp0, s0, sizeof (ct6_session_key_t)); if (clib_bihash_search_48_8 (&cmp->session_hash, &kvp0, &kvp0) < 0) { s = format (s, " LOOKUP FAIL!"); } else { if (kvp0.value == s0 - cmp->sessions[s0->thread_index]) { s = format (s, " OK"); if (verbose > 1) { s = format (s, " next %d prev %d", s0->next_index, s0->prev_index); s = format (s, " hits %d expires %.2f", s0->hits, s0->expires); } } else s = format (s, " BOGUS LOOKUP RESULT!"); } return s; } static clib_error_t * show_ct6_command_fn_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { ct6_main_t *cmp = &ct6_main; ct6_session_t *s0; int verbose = 0; u8 *s = 0; int i; if (!cmp->feature_initialized) return clib_error_return (0, "ip6 connection tracking not enabled..."); if (unformat (input, "verbose %d", &verbose)) ; else if (unformat (input, "verbose")) verbose = 1; for (i = 0; i < vec_len (cmp->sessions); i++) { s = format (s, "Thread %d: %d sessions\n", i, pool_elts (cmp->sessions[i])); if (verbose == 0) continue; s = format (s, "%U", format_ct6_session, cmp, 0 /* pool */ , 0 /* header */ , verbose); /* *INDENT-OFF* */ pool_foreach (s0, cmp->sessions[i]) { s = format (s, "%U", format_ct6_session, cmp, i, s0, verbose); } /* *INDENT-ON* */ } vlib_cli_output (cmp->vlib_main, "%v", s); vec_free (s); return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_ct6_command_fn_command, static) = { .path = "show ip6 connection-tracker", .short_help = "show ip6 connection-tracker", .function = show_ct6_command_fn_command_fn, }; /* *INDENT-ON* */ static void increment_v6_address (ip6_address_t * a) { u64 v0, v1; v0 = clib_net_to_host_u64 (a->as_u64[0]); v1 = clib_net_to_host_u64 (a->as_u64[1]); v1 += 1; if (v1 == 0) v0 += 1; a->as_u64[0] = clib_net_to_host_u64 (v0); a->as_u64[1] = clib_net_to_host_u64 (v1); } static clib_error_t * test_ct6_command_fn_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { ct6_main_t *cmp = &ct6_main; clib_bihash_kv_48_8_t kvp0; ct6_session_key_t *key0; ct6_session_t *s0; u8 src[16], dst[16]; u32 recycled = 0, created = 0; int i, num_sessions = 5; u32 midpt_index; u8 *s = 0; cmp->max_sessions_per_worker = 4; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "num-sessions %d", &num_sessions)) ; else if (unformat (input, "max-sessions %d", &cmp->max_sessions_per_worker)) ; else break; } ct6_feature_init (cmp); /* Set up starting src/dst addresses */ memset (src, 0, sizeof (src)); memset (dst, 0, sizeof (dst)); src[0] = 0xdb; dst[0] = 0xbe; src[15] = 1; dst[15] = 1; /* * See if we know about this flow. * Key set up for the out2in path, the performant case */ key0 = (ct6_session_key_t *) & kvp0; memset (&kvp0, 0, sizeof (kvp0)); for (i = 0; i < num_sessions; i++) { clib_memcpy_fast (&key0->src, src, sizeof (src)); clib_memcpy_fast (&key0->dst, dst, sizeof (dst)); key0->as_u64[4] = 0; key0->as_u64[5] = 0; key0->sport = clib_host_to_net_u16 (1234); key0->dport = clib_host_to_net_u16 (4321); key0->proto = 17; /* udp, fwiw */ s0 = ct6_create_or_recycle_session (cmp, &kvp0, 3.0 /* now */ , 0 /* thread index */ , &recycled, &created); s = format (s, "%U (%d, %d)", format_ct6_session, cmp, 0 /* thread index */ , s0, 1 /* verbose */ , recycled, created); vlib_cli_output (vm, "%v", s); vec_free (s); increment_v6_address ((ip6_address_t *) src); recycled = 0; created = 0; } /* *INDENT-OFF* */ pool_foreach (s0, cmp->sessions[0]) { s = format (s, "%U", format_ct6_session, cmp, 0, s0, 1 /* verbose */); } /* *INDENT-ON* */ vlib_cli_output (vm, "\nEnd state: first index %d last index %d\n%v", cmp->first_index[0], cmp->last_index[0], s); vec_free (s); midpt_index = cmp->max_sessions_per_worker / 3; s0 = pool_elt_at_index (cmp->sessions[0], midpt_index); vlib_cli_output (vm, "\nSimulate LRU hit on session %d", s0 - cmp->sessions[0]); ct6_update_session_hit (cmp, s0, 234.0); /* *INDENT-OFF* */ pool_foreach (s0, cmp->sessions[0]) { s = format (s, "%U", format_ct6_session, cmp, 0, s0, 1 /* verbose */); } /* *INDENT-ON* */ vlib_cli_output (vm, "\nEnd state: first index %d last index %d\n%v", cmp->first_index[0], cmp->last_index[0], s); vec_free (s); return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (test_ct6_command_fn_command, static) = { .path = "test ip6 connection-tracker", .short_help = "test ip6 connection-tracker", .function = test_ct6_command_fn_command_fn, }; /* *INDENT-ON* */ static clib_error_t * ct6_config (vlib_main_t * vm, unformat_input_t * input) { ct6_main_t *cmp = &ct6_main; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "session-hash-buckets %u", &cmp->session_hash_buckets)) ; else if (unformat (input, "session-hash-memory %U", unformat_memory_size, &cmp->session_hash_memory)) ; else if (unformat (input, "session-timeout %f", &cmp->session_timeout_interval)) ; else { return clib_error_return (0, "unknown input '%U'", format_unformat_error, input); } } return 0; } VLIB_CONFIG_FUNCTION (ct6_config, "ct6"); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */