Cleanup/refactor session layer code
[vpp.git] / src / vnet / udp / udp.c
1 /*
2  * Copyright (c) 2016 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 /** @file
17     udp state machine, etc.
18 */
19
20 #include <vnet/udp/udp.h>
21 #include <vnet/session/session.h>
22 #include <vnet/dpo/load_balance.h>
23 #include <vnet/fib/ip4_fib.h>
24
25 udp_uri_main_t udp_uri_main;
26
27 u32
28 udp_session_bind_ip4 (u32 session_index, transport_endpoint_t * lcl)
29 {
30   udp_uri_main_t *um = vnet_get_udp_main ();
31   udp_connection_t *listener;
32
33   pool_get (um->udp_listeners, listener);
34   memset (listener, 0, sizeof (udp_connection_t));
35   listener->c_lcl_port = clib_host_to_net_u16 (lcl->port);
36   listener->c_lcl_ip4.as_u32 = lcl->ip.ip4.as_u32;
37   listener->c_proto = SESSION_TYPE_IP4_UDP;
38   udp_register_dst_port (um->vlib_main, lcl->port, udp4_uri_input_node.index,
39                          1 /* is_ipv4 */ );
40   return 0;
41 }
42
43 u32
44 udp_session_bind_ip6 (u32 session_index, transport_endpoint_t * lcl)
45 {
46   udp_uri_main_t *um = vnet_get_udp_main ();
47   udp_connection_t *listener;
48
49   pool_get (um->udp_listeners, listener);
50   listener->c_lcl_port = clib_host_to_net_u16 (lcl->port);
51   clib_memcpy (&listener->c_lcl_ip6, &lcl->ip.ip6, sizeof (ip6_address_t));
52   listener->c_proto = SESSION_TYPE_IP6_UDP;
53   udp_register_dst_port (um->vlib_main, lcl->port,
54                          udp4_uri_input_node.index, 0 /* is_ipv4 */ );
55   return 0;
56 }
57
58 u32
59 udp_session_unbind_ip4 (u32 listener_index)
60 {
61   vlib_main_t *vm = vlib_get_main ();
62   udp_connection_t *listener;
63   listener = udp_listener_get (listener_index);
64
65   /* deregister the udp_local mapping */
66   udp_unregister_dst_port (vm, listener->c_lcl_port, 1 /* is_ipv4 */ );
67   return 0;
68 }
69
70 u32
71 udp_session_unbind_ip6 (u32 listener_index)
72 {
73   vlib_main_t *vm = vlib_get_main ();
74   udp_connection_t *listener;
75
76   listener = udp_listener_get (listener_index);
77
78   /* deregister the udp_local mapping */
79   udp_unregister_dst_port (vm, listener->c_lcl_port, 0 /* is_ipv4 */ );
80   return 0;
81 }
82
83 transport_connection_t *
84 udp_session_get_listener (u32 listener_index)
85 {
86   udp_connection_t *us;
87
88   us = udp_listener_get (listener_index);
89   return &us->connection;
90 }
91
92 u32
93 udp_push_header (transport_connection_t * tconn, vlib_buffer_t * b)
94 {
95   udp_connection_t *us;
96   u8 *data;
97   udp_header_t *udp;
98
99   us = (udp_connection_t *) tconn;
100
101   if (tconn->is_ip4)
102     {
103       ip4_header_t *ip;
104
105       data = vlib_buffer_get_current (b);
106       udp = (udp_header_t *) (data - sizeof (udp_header_t));
107       ip = (ip4_header_t *) ((u8 *) udp - sizeof (ip4_header_t));
108
109       /* Build packet header, swap rx key src + dst fields */
110       ip->src_address.as_u32 = us->c_lcl_ip4.as_u32;
111       ip->dst_address.as_u32 = us->c_rmt_ip4.as_u32;
112       ip->ip_version_and_header_length = 0x45;
113       ip->ttl = 254;
114       ip->protocol = IP_PROTOCOL_UDP;
115       ip->length = clib_host_to_net_u16 (b->current_length + sizeof (*udp));
116       ip->checksum = ip4_header_checksum (ip);
117
118       udp->src_port = us->c_lcl_port;
119       udp->dst_port = us->c_rmt_port;
120       udp->length = clib_host_to_net_u16 (b->current_length);
121       udp->checksum = 0;
122
123       b->current_length = sizeof (*ip) + sizeof (*udp);
124       return SESSION_QUEUE_NEXT_IP4_LOOKUP;
125     }
126   else
127     {
128       vlib_main_t *vm = vlib_get_main ();
129       ip6_header_t *ip;
130       u16 payload_length;
131       int bogus = ~0;
132
133       data = vlib_buffer_get_current (b);
134       udp = (udp_header_t *) (data - sizeof (udp_header_t));
135       ip = (ip6_header_t *) ((u8 *) udp - sizeof (ip6_header_t));
136
137       /* Build packet header, swap rx key src + dst fields */
138       clib_memcpy (&ip->src_address, &us->c_lcl_ip6, sizeof (ip6_address_t));
139       clib_memcpy (&ip->dst_address, &us->c_rmt_ip6, sizeof (ip6_address_t));
140
141       ip->ip_version_traffic_class_and_flow_label =
142         clib_host_to_net_u32 (0x6 << 28);
143
144       ip->hop_limit = 0xff;
145       ip->protocol = IP_PROTOCOL_UDP;
146
147       payload_length = vlib_buffer_length_in_chain (vm, b);
148       payload_length -= sizeof (*ip);
149
150       ip->payload_length = clib_host_to_net_u16 (payload_length);
151
152       udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip, &bogus);
153       ASSERT (!bogus);
154
155       udp->src_port = us->c_lcl_port;
156       udp->dst_port = us->c_rmt_port;
157       udp->length = clib_host_to_net_u16 (b->current_length);
158       udp->checksum = 0;
159
160       b->current_length = sizeof (*ip) + sizeof (*udp);
161
162       return SESSION_QUEUE_NEXT_IP6_LOOKUP;
163     }
164 }
165
166 transport_connection_t *
167 udp_session_get (u32 connection_index, u32 my_thread_index)
168 {
169   udp_uri_main_t *um = vnet_get_udp_main ();
170
171   udp_connection_t *us;
172   us =
173     pool_elt_at_index (um->udp_sessions[my_thread_index], connection_index);
174   return &us->connection;
175 }
176
177 void
178 udp_session_close (u32 connection_index, u32 my_thread_index)
179 {
180   udp_uri_main_t *um = vnet_get_udp_main ();
181   pool_put_index (um->udp_sessions[my_thread_index], connection_index);
182 }
183
184 u8 *
185 format_udp_session_ip4 (u8 * s, va_list * args)
186 {
187   u32 uci = va_arg (*args, u32);
188   u32 thread_index = va_arg (*args, u32);
189   udp_connection_t *u4;
190
191   u4 = udp_connection_get (uci, thread_index);
192
193   s = format (s, "[%s] %U:%d->%U:%d", "udp", format_ip4_address,
194               &u4->c_lcl_ip4, clib_net_to_host_u16 (u4->c_lcl_port),
195               format_ip4_address, &u4->c_rmt_ip4,
196               clib_net_to_host_u16 (u4->c_rmt_port));
197   return s;
198 }
199
200 u8 *
201 format_udp_session_ip6 (u8 * s, va_list * args)
202 {
203   u32 uci = va_arg (*args, u32);
204   u32 thread_index = va_arg (*args, u32);
205   udp_connection_t *tc = udp_connection_get (uci, thread_index);
206   s = format (s, "[%s] %U:%d->%U:%d", "udp", format_ip6_address,
207               &tc->c_lcl_ip6, clib_net_to_host_u16 (tc->c_lcl_port),
208               format_ip6_address, &tc->c_rmt_ip6,
209               clib_net_to_host_u16 (tc->c_rmt_port));
210   return s;
211 }
212
213 u8 *
214 format_udp_listener_session_ip4 (u8 * s, va_list * args)
215 {
216   u32 tci = va_arg (*args, u32);
217   udp_connection_t *tc = udp_listener_get (tci);
218   s = format (s, "[%s] %U:%d->%U:%d", "udp", format_ip4_address,
219               &tc->c_lcl_ip4, clib_net_to_host_u16 (tc->c_lcl_port),
220               format_ip4_address, &tc->c_rmt_ip4,
221               clib_net_to_host_u16 (tc->c_rmt_port));
222   return s;
223 }
224
225 u8 *
226 format_udp_listener_session_ip6 (u8 * s, va_list * args)
227 {
228   u32 tci = va_arg (*args, u32);
229   udp_connection_t *tc = udp_listener_get (tci);
230   s = format (s, "[%s] %U:%d->%U:%d", "udp", format_ip6_address,
231               &tc->c_lcl_ip6, clib_net_to_host_u16 (tc->c_lcl_port),
232               format_ip6_address, &tc->c_rmt_ip6,
233               clib_net_to_host_u16 (tc->c_rmt_port));
234   return s;
235 }
236
237 u16
238 udp_send_mss_uri (transport_connection_t * t)
239 {
240   /* TODO figure out MTU of output interface */
241   return 400;
242 }
243
244 u32
245 udp_send_space_uri (transport_connection_t * t)
246 {
247   /* No constraint on TX window */
248   return ~0;
249 }
250
251 int
252 udp_open_connection (transport_endpoint_t * tep)
253 {
254   clib_warning ("Not implemented");
255   return 0;
256 }
257
258 /* *INDENT-OFF* */
259 const static transport_proto_vft_t udp4_proto = {
260   .bind = udp_session_bind_ip4,
261   .open = udp_open_connection,
262   .unbind = udp_session_unbind_ip4,
263   .push_header = udp_push_header,
264   .get_connection = udp_session_get,
265   .get_listener = udp_session_get_listener,
266   .close = udp_session_close,
267   .send_mss = udp_send_mss_uri,
268   .send_space = udp_send_space_uri,
269   .format_connection = format_udp_session_ip4,
270   .format_listener = format_udp_listener_session_ip4
271 };
272
273 const static transport_proto_vft_t udp6_proto = {
274   .bind = udp_session_bind_ip6,
275   .open = udp_open_connection,
276   .unbind = udp_session_unbind_ip6,
277   .push_header = udp_push_header,
278   .get_connection = udp_session_get,
279   .get_listener = udp_session_get_listener,
280   .close = udp_session_close,
281   .send_mss = udp_send_mss_uri,
282   .send_space = udp_send_space_uri,
283   .format_connection = format_udp_session_ip6,
284   .format_listener = format_udp_listener_session_ip6
285 };
286 /* *INDENT-ON* */
287
288 static clib_error_t *
289 udp_init (vlib_main_t * vm)
290 {
291   udp_uri_main_t *um = vnet_get_udp_main ();
292   ip_main_t *im = &ip_main;
293   vlib_thread_main_t *tm = vlib_get_thread_main ();
294   u32 num_threads;
295   clib_error_t *error = 0;
296   ip_protocol_info_t *pi;
297
298   um->vlib_main = vm;
299   um->vnet_main = vnet_get_main ();
300
301   if ((error = vlib_call_init_function (vm, ip_main_init)))
302     return error;
303   if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
304     return error;
305   if ((error = vlib_call_init_function (vm, ip6_lookup_init)))
306     return error;
307
308   /*
309    * Registrations
310    */
311
312   /* IP registration */
313   pi = ip_get_protocol_info (im, IP_PROTOCOL_UDP);
314   if (pi == 0)
315     return clib_error_return (0, "UDP protocol info AWOL");
316   pi->format_header = format_udp_header;
317   pi->unformat_pg_edit = unformat_pg_udp_header;
318
319
320   /* Register as transport with URI */
321   session_register_transport (SESSION_TYPE_IP4_UDP, &udp4_proto);
322   session_register_transport (SESSION_TYPE_IP6_UDP, &udp6_proto);
323
324   /*
325    * Initialize data structures
326    */
327
328   num_threads = 1 /* main thread */  + tm->n_threads;
329   vec_validate (um->udp_sessions, num_threads - 1);
330
331   return error;
332 }
333
334 VLIB_INIT_FUNCTION (udp_init);
335
336 /*
337  * fd.io coding-style-patch-verification: ON
338  *
339  * Local Variables:
340  * eval: (c-set-style "gnu")
341  * End:
342  */