Dave Barach | 68b0fb0 | 2017-02-28 15:15:56 -0500 | [diff] [blame] | 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 |
Florin Coras | 04e5344 | 2017-07-16 17:12:15 -0700 | [diff] [blame] | 28 | udp_session_bind_ip4 (u32 session_index, transport_endpoint_t * lcl) |
Dave Barach | 68b0fb0 | 2017-02-28 15:15:56 -0500 | [diff] [blame] | 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)); |
Florin Coras | 04e5344 | 2017-07-16 17:12:15 -0700 | [diff] [blame] | 35 | listener->c_lcl_port = clib_host_to_net_u16 (lcl->port); |
| 36 | listener->c_lcl_ip4.as_u32 = lcl->ip.ip4.as_u32; |
Florin Coras | 6881062 | 2017-07-24 17:40:28 -0700 | [diff] [blame] | 37 | listener->c_transport_proto = TRANSPORT_PROTO_UDP; |
Florin Coras | 04e5344 | 2017-07-16 17:12:15 -0700 | [diff] [blame] | 38 | udp_register_dst_port (um->vlib_main, lcl->port, udp4_uri_input_node.index, |
| 39 | 1 /* is_ipv4 */ ); |
Dave Barach | 68b0fb0 | 2017-02-28 15:15:56 -0500 | [diff] [blame] | 40 | return 0; |
| 41 | } |
| 42 | |
| 43 | u32 |
Florin Coras | 04e5344 | 2017-07-16 17:12:15 -0700 | [diff] [blame] | 44 | udp_session_bind_ip6 (u32 session_index, transport_endpoint_t * lcl) |
Dave Barach | 68b0fb0 | 2017-02-28 15:15:56 -0500 | [diff] [blame] | 45 | { |
| 46 | udp_uri_main_t *um = vnet_get_udp_main (); |
| 47 | udp_connection_t *listener; |
| 48 | |
| 49 | pool_get (um->udp_listeners, listener); |
Florin Coras | 04e5344 | 2017-07-16 17:12:15 -0700 | [diff] [blame] | 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)); |
Florin Coras | 6881062 | 2017-07-24 17:40:28 -0700 | [diff] [blame] | 52 | listener->c_transport_proto = TRANSPORT_PROTO_UDP; |
Florin Coras | 04e5344 | 2017-07-16 17:12:15 -0700 | [diff] [blame] | 53 | udp_register_dst_port (um->vlib_main, lcl->port, |
Dave Barach | 68b0fb0 | 2017-02-28 15:15:56 -0500 | [diff] [blame] | 54 | udp4_uri_input_node.index, 0 /* is_ipv4 */ ); |
| 55 | return 0; |
| 56 | } |
| 57 | |
| 58 | u32 |
Florin Coras | e69f495 | 2017-03-07 10:06:24 -0800 | [diff] [blame] | 59 | udp_session_unbind_ip4 (u32 listener_index) |
Dave Barach | 68b0fb0 | 2017-02-28 15:15:56 -0500 | [diff] [blame] | 60 | { |
Florin Coras | e69f495 | 2017-03-07 10:06:24 -0800 | [diff] [blame] | 61 | vlib_main_t *vm = vlib_get_main (); |
Dave Barach | 68b0fb0 | 2017-02-28 15:15:56 -0500 | [diff] [blame] | 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 |
Florin Coras | e69f495 | 2017-03-07 10:06:24 -0800 | [diff] [blame] | 71 | udp_session_unbind_ip6 (u32 listener_index) |
Dave Barach | 68b0fb0 | 2017-02-28 15:15:56 -0500 | [diff] [blame] | 72 | { |
Florin Coras | e69f495 | 2017-03-07 10:06:24 -0800 | [diff] [blame] | 73 | vlib_main_t *vm = vlib_get_main (); |
Dave Barach | 68b0fb0 | 2017-02-28 15:15:56 -0500 | [diff] [blame] | 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 |
Florin Coras | 04e5344 | 2017-07-16 17:12:15 -0700 | [diff] [blame] | 252 | udp_open_connection (transport_endpoint_t * tep) |
Dave Barach | 68b0fb0 | 2017-02-28 15:15:56 -0500 | [diff] [blame] | 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 */ |
Florin Coras | 6881062 | 2017-07-24 17:40:28 -0700 | [diff] [blame] | 321 | session_register_transport (TRANSPORT_PROTO_UDP, 1, &udp4_proto); |
| 322 | session_register_transport (TRANSPORT_PROTO_UDP, 0, &udp6_proto); |
Dave Barach | 68b0fb0 | 2017-02-28 15:15:56 -0500 | [diff] [blame] | 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 | */ |