fc722e45668f4adec421516b331263f65e1a52a3
[vpp.git] / src / vnet / session / transport.c
1 /*
2  * Copyright (c) 2017 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/session/transport_interface.h>
17 #include <vnet/session/session.h>
18 #include <vnet/fib/fib.h>
19
20 /**
21  * Per-type vector of transport protocol virtual function tables
22  */
23 transport_proto_vft_t *tp_vfts;
24
25 /*
26  * Port allocator seed
27  */
28 static u32 port_allocator_seed;
29
30 /*
31  * Local endpoints table
32  */
33 static transport_endpoint_table_t local_endpoints_table;
34
35 /*
36  * Pool of local endpoints
37  */
38 static transport_endpoint_t *local_endpoints;
39
40 /*
41  * Local endpoints pool lock
42  */
43 static clib_spinlock_t local_endpoints_lock;
44
45
46 u32
47 transport_endpoint_lookup (transport_endpoint_table_t * ht, u8 proto,
48                            ip46_address_t * ip, u16 port)
49 {
50   clib_bihash_kv_24_8_t kv;
51   int rv;
52
53   kv.key[0] = ip->as_u64[0];
54   kv.key[1] = ip->as_u64[1];
55   kv.key[2] = (u64) port << 8 | (u64) proto;
56
57   rv = clib_bihash_search_inline_24_8 (ht, &kv);
58   if (rv == 0)
59     return kv.value;
60
61   return ENDPOINT_INVALID_INDEX;
62 }
63
64 void
65 transport_endpoint_table_add (transport_endpoint_table_t * ht, u8 proto,
66                               transport_endpoint_t * te, u32 value)
67 {
68   clib_bihash_kv_24_8_t kv;
69
70   kv.key[0] = te->ip.as_u64[0];
71   kv.key[1] = te->ip.as_u64[1];
72   kv.key[2] = (u64) te->port << 8 | (u64) proto;
73   kv.value = value;
74
75   clib_bihash_add_del_24_8 (ht, &kv, 1);
76 }
77
78 void
79 transport_endpoint_table_del (transport_endpoint_table_t * ht, u8 proto,
80                               transport_endpoint_t * te)
81 {
82   clib_bihash_kv_24_8_t kv;
83
84   kv.key[0] = te->ip.as_u64[0];
85   kv.key[1] = te->ip.as_u64[1];
86   kv.key[2] = (u64) te->port << 8 | (u64) proto;
87
88   clib_bihash_add_del_24_8 (ht, &kv, 0);
89 }
90
91 /**
92  * Register transport virtual function table.
93  *
94  * @param type - session type (not protocol type)
95  * @param vft - virtual function table
96  */
97 void
98 transport_register_protocol (transport_proto_t transport_proto, u8 is_ip4,
99                              const transport_proto_vft_t * vft)
100 {
101   u8 session_type;
102   session_type = session_type_from_proto_and_ip (transport_proto, is_ip4);
103
104   vec_validate (tp_vfts, session_type);
105   tp_vfts[session_type] = *vft;
106
107   /* If an offset function is provided, then peek instead of dequeue */
108   session_manager_set_transport_rx_fn (session_type,
109                                        vft->tx_fifo_offset != 0);
110 }
111
112 /**
113  * Get transport virtual function table
114  *
115  * @param type - session type (not protocol type)
116  */
117 transport_proto_vft_t *
118 transport_protocol_get_vft (u8 session_type)
119 {
120   if (session_type >= vec_len (tp_vfts))
121     return 0;
122   return &tp_vfts[session_type];
123 }
124
125 #define PORT_MASK ((1 << 16)- 1)
126
127 void
128 transport_endpoint_del (u32 tepi)
129 {
130   clib_spinlock_lock_if_init (&local_endpoints_lock);
131   pool_put_index (local_endpoints, tepi);
132   clib_spinlock_unlock_if_init (&local_endpoints_lock);
133 }
134
135 always_inline transport_endpoint_t *
136 transport_endpoint_new (void)
137 {
138   transport_endpoint_t *tep;
139   pool_get (local_endpoints, tep);
140   return tep;
141 }
142
143 void
144 transport_endpoint_cleanup (u8 proto, ip46_address_t * lcl_ip, u16 port)
145 {
146   u32 tepi;
147   transport_endpoint_t *tep;
148
149   /* Cleanup local endpoint if this was an active connect */
150   tepi = transport_endpoint_lookup (&local_endpoints_table, proto, lcl_ip,
151                                     clib_net_to_host_u16 (port));
152   if (tepi != ENDPOINT_INVALID_INDEX)
153     {
154       tep = pool_elt_at_index (local_endpoints, tepi);
155       transport_endpoint_table_del (&local_endpoints_table, proto, tep);
156       transport_endpoint_del (tepi);
157     }
158 }
159
160 /**
161  * Allocate local port and add if successful add entry to local endpoint
162  * table to mark the pair as used.
163  */
164 int
165 transport_alloc_local_port (u8 proto, ip46_address_t * ip)
166 {
167   transport_endpoint_t *tep;
168   u32 tei;
169   u16 min = 1024, max = 65535;  /* XXX configurable ? */
170   int tries, limit;
171
172   limit = max - min;
173
174   /* Only support active opens from thread 0 */
175   ASSERT (vlib_get_thread_index () == 0);
176
177   /* Search for first free slot */
178   for (tries = 0; tries < limit; tries++)
179     {
180       u16 port = 0;
181
182       /* Find a port in the specified range */
183       while (1)
184         {
185           port = random_u32 (&port_allocator_seed) & PORT_MASK;
186           if (PREDICT_TRUE (port >= min && port < max))
187             break;
188         }
189
190       /* Look it up. If not found, we're done */
191       tei = transport_endpoint_lookup (&local_endpoints_table, proto, ip,
192                                        port);
193       if (tei == ENDPOINT_INVALID_INDEX)
194         {
195           clib_spinlock_lock_if_init (&local_endpoints_lock);
196           tep = transport_endpoint_new ();
197           clib_memcpy (&tep->ip, ip, sizeof (*ip));
198           tep->port = port;
199           transport_endpoint_table_add (&local_endpoints_table, proto, tep,
200                                         tep - local_endpoints);
201           clib_spinlock_unlock_if_init (&local_endpoints_lock);
202
203           return tep->port;
204         }
205     }
206   return -1;
207 }
208
209 int
210 transport_alloc_local_endpoint (u8 proto, transport_endpoint_t * rmt,
211                                 ip46_address_t * lcl_addr, u16 * lcl_port)
212 {
213   fib_prefix_t prefix;
214   fib_node_index_t fei;
215   u32 sw_if_index;
216   int port;
217
218   /*
219    * Find the local address and allocate port
220    */
221
222   /* Find a FIB path to the destination */
223   clib_memcpy (&prefix.fp_addr, &rmt->ip, sizeof (rmt->ip));
224   prefix.fp_proto = rmt->is_ip4 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
225   prefix.fp_len = rmt->is_ip4 ? 32 : 128;
226
227   ASSERT (rmt->fib_index != ENDPOINT_INVALID_INDEX);
228   fei = fib_table_lookup (rmt->fib_index, &prefix);
229
230   /* Couldn't find route to destination. Bail out. */
231   if (fei == FIB_NODE_INDEX_INVALID)
232     {
233       clib_warning ("no route to destination");
234       return -1;
235     }
236
237   sw_if_index = rmt->sw_if_index;
238   if (sw_if_index == ENDPOINT_INVALID_INDEX)
239     sw_if_index = fib_entry_get_resolving_interface (fei);
240
241   if (sw_if_index == ENDPOINT_INVALID_INDEX)
242     {
243       clib_warning ("no resolving interface for %U", format_ip46_address,
244                     &rmt->ip, (rmt->is_ip4 == 0) + 1);
245       return -1;
246     }
247
248   memset (lcl_addr, 0, sizeof (*lcl_addr));
249
250   if (rmt->is_ip4)
251     {
252       ip4_address_t *ip4;
253       ip4 = ip_interface_get_first_ip (sw_if_index, 1);
254       lcl_addr->ip4.as_u32 = ip4->as_u32;
255     }
256   else
257     {
258       ip6_address_t *ip6;
259       ip6 = ip_interface_get_first_ip (sw_if_index, 0);
260       if (ip6 == 0)
261         {
262           clib_warning ("no routable ip6 addresses on %U",
263                         format_vnet_sw_if_index_name, vnet_get_main (),
264                         sw_if_index);
265           return -1;
266         }
267       clib_memcpy (&lcl_addr->ip6, ip6, sizeof (*ip6));
268     }
269
270   /* Allocate source port */
271   port = transport_alloc_local_port (proto, lcl_addr);
272   if (port < 1)
273     {
274       clib_warning ("Failed to allocate src port");
275       return -1;
276     }
277   *lcl_port = port;
278   return 0;
279 }
280
281 void
282 transport_init (void)
283 {
284   vlib_thread_main_t *vtm = vlib_get_thread_main ();
285   u32 local_endpoints_table_buckets = 250000;
286   u32 local_endpoints_table_memory = 512 << 20;
287   u32 num_threads;
288
289   /* Initialize [port-allocator] random number seed */
290   port_allocator_seed = (u32) clib_cpu_time_now ();
291
292   clib_bihash_init_24_8 (&local_endpoints_table, "local endpoints table",
293                          local_endpoints_table_buckets,
294                          local_endpoints_table_memory);
295   num_threads = 1 /* main thread */  + vtm->n_threads;
296   if (num_threads > 1)
297     clib_spinlock_init (&local_endpoints_lock);
298 }
299
300 /*
301  * fd.io coding-style-patch-verification: ON
302  *
303  * Local Variables:
304  * eval: (c-set-style "gnu")
305  * End:
306  */