| /* |
| * Copyright (c) 2016 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. |
| */ |
| |
| /** @file |
| udp state machine, etc. |
| */ |
| |
| #include <vnet/udp/udp.h> |
| #include <vnet/session/session.h> |
| #include <vnet/dpo/load_balance.h> |
| #include <vnet/fib/ip4_fib.h> |
| |
| udp_uri_main_t udp_uri_main; |
| |
| u32 |
| udp_session_bind_ip4 (u32 session_index, |
| ip46_address_t * ip, u16 port_number_host_byte_order) |
| { |
| udp_uri_main_t *um = vnet_get_udp_main (); |
| udp_connection_t *listener; |
| |
| pool_get (um->udp_listeners, listener); |
| memset (listener, 0, sizeof (udp_connection_t)); |
| listener->c_lcl_port = clib_host_to_net_u16 (port_number_host_byte_order); |
| listener->c_lcl_ip4.as_u32 = ip->ip4.as_u32; |
| listener->c_proto = SESSION_TYPE_IP4_UDP; |
| udp_register_dst_port (um->vlib_main, port_number_host_byte_order, |
| udp4_uri_input_node.index, 1 /* is_ipv4 */ ); |
| return 0; |
| } |
| |
| u32 |
| udp_session_bind_ip6 (u32 session_index, |
| ip46_address_t * ip, u16 port_number_host_byte_order) |
| { |
| udp_uri_main_t *um = vnet_get_udp_main (); |
| udp_connection_t *listener; |
| |
| pool_get (um->udp_listeners, listener); |
| listener->c_lcl_port = clib_host_to_net_u16 (port_number_host_byte_order); |
| clib_memcpy (&listener->c_lcl_ip6, &ip->ip6, sizeof (ip6_address_t)); |
| listener->c_proto = SESSION_TYPE_IP6_UDP; |
| udp_register_dst_port (um->vlib_main, port_number_host_byte_order, |
| udp4_uri_input_node.index, 0 /* is_ipv4 */ ); |
| return 0; |
| } |
| |
| u32 |
| udp_session_unbind_ip4 (u32 listener_index) |
| { |
| vlib_main_t *vm = vlib_get_main (); |
| udp_connection_t *listener; |
| listener = udp_listener_get (listener_index); |
| |
| /* deregister the udp_local mapping */ |
| udp_unregister_dst_port (vm, listener->c_lcl_port, 1 /* is_ipv4 */ ); |
| return 0; |
| } |
| |
| u32 |
| udp_session_unbind_ip6 (u32 listener_index) |
| { |
| vlib_main_t *vm = vlib_get_main (); |
| udp_connection_t *listener; |
| |
| listener = udp_listener_get (listener_index); |
| |
| /* deregister the udp_local mapping */ |
| udp_unregister_dst_port (vm, listener->c_lcl_port, 0 /* is_ipv4 */ ); |
| return 0; |
| } |
| |
| transport_connection_t * |
| udp_session_get_listener (u32 listener_index) |
| { |
| udp_connection_t *us; |
| |
| us = udp_listener_get (listener_index); |
| return &us->connection; |
| } |
| |
| u32 |
| udp_push_header (transport_connection_t * tconn, vlib_buffer_t * b) |
| { |
| udp_connection_t *us; |
| u8 *data; |
| udp_header_t *udp; |
| |
| us = (udp_connection_t *) tconn; |
| |
| if (tconn->is_ip4) |
| { |
| ip4_header_t *ip; |
| |
| data = vlib_buffer_get_current (b); |
| udp = (udp_header_t *) (data - sizeof (udp_header_t)); |
| ip = (ip4_header_t *) ((u8 *) udp - sizeof (ip4_header_t)); |
| |
| /* Build packet header, swap rx key src + dst fields */ |
| ip->src_address.as_u32 = us->c_lcl_ip4.as_u32; |
| ip->dst_address.as_u32 = us->c_rmt_ip4.as_u32; |
| ip->ip_version_and_header_length = 0x45; |
| ip->ttl = 254; |
| ip->protocol = IP_PROTOCOL_UDP; |
| ip->length = clib_host_to_net_u16 (b->current_length + sizeof (*udp)); |
| ip->checksum = ip4_header_checksum (ip); |
| |
| udp->src_port = us->c_lcl_port; |
| udp->dst_port = us->c_rmt_port; |
| udp->length = clib_host_to_net_u16 (b->current_length); |
| udp->checksum = 0; |
| |
| b->current_length = sizeof (*ip) + sizeof (*udp); |
| return SESSION_QUEUE_NEXT_IP4_LOOKUP; |
| } |
| else |
| { |
| vlib_main_t *vm = vlib_get_main (); |
| ip6_header_t *ip; |
| u16 payload_length; |
| int bogus = ~0; |
| |
| data = vlib_buffer_get_current (b); |
| udp = (udp_header_t *) (data - sizeof (udp_header_t)); |
| ip = (ip6_header_t *) ((u8 *) udp - sizeof (ip6_header_t)); |
| |
| /* Build packet header, swap rx key src + dst fields */ |
| clib_memcpy (&ip->src_address, &us->c_lcl_ip6, sizeof (ip6_address_t)); |
| clib_memcpy (&ip->dst_address, &us->c_rmt_ip6, sizeof (ip6_address_t)); |
| |
| ip->ip_version_traffic_class_and_flow_label = |
| clib_host_to_net_u32 (0x6 << 28); |
| |
| ip->hop_limit = 0xff; |
| ip->protocol = IP_PROTOCOL_UDP; |
| |
| payload_length = vlib_buffer_length_in_chain (vm, b); |
| payload_length -= sizeof (*ip); |
| |
| ip->payload_length = clib_host_to_net_u16 (payload_length); |
| |
| udp->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip, &bogus); |
| ASSERT (!bogus); |
| |
| udp->src_port = us->c_lcl_port; |
| udp->dst_port = us->c_rmt_port; |
| udp->length = clib_host_to_net_u16 (b->current_length); |
| udp->checksum = 0; |
| |
| b->current_length = sizeof (*ip) + sizeof (*udp); |
| |
| return SESSION_QUEUE_NEXT_IP6_LOOKUP; |
| } |
| } |
| |
| transport_connection_t * |
| udp_session_get (u32 connection_index, u32 my_thread_index) |
| { |
| udp_uri_main_t *um = vnet_get_udp_main (); |
| |
| udp_connection_t *us; |
| us = |
| pool_elt_at_index (um->udp_sessions[my_thread_index], connection_index); |
| return &us->connection; |
| } |
| |
| void |
| udp_session_close (u32 connection_index, u32 my_thread_index) |
| { |
| udp_uri_main_t *um = vnet_get_udp_main (); |
| pool_put_index (um->udp_sessions[my_thread_index], connection_index); |
| } |
| |
| u8 * |
| format_udp_session_ip4 (u8 * s, va_list * args) |
| { |
| u32 uci = va_arg (*args, u32); |
| u32 thread_index = va_arg (*args, u32); |
| udp_connection_t *u4; |
| |
| u4 = udp_connection_get (uci, thread_index); |
| |
| s = format (s, "[%s] %U:%d->%U:%d", "udp", format_ip4_address, |
| &u4->c_lcl_ip4, clib_net_to_host_u16 (u4->c_lcl_port), |
| format_ip4_address, &u4->c_rmt_ip4, |
| clib_net_to_host_u16 (u4->c_rmt_port)); |
| return s; |
| } |
| |
| u8 * |
| format_udp_session_ip6 (u8 * s, va_list * args) |
| { |
| u32 uci = va_arg (*args, u32); |
| u32 thread_index = va_arg (*args, u32); |
| udp_connection_t *tc = udp_connection_get (uci, thread_index); |
| s = format (s, "[%s] %U:%d->%U:%d", "udp", format_ip6_address, |
| &tc->c_lcl_ip6, clib_net_to_host_u16 (tc->c_lcl_port), |
| format_ip6_address, &tc->c_rmt_ip6, |
| clib_net_to_host_u16 (tc->c_rmt_port)); |
| return s; |
| } |
| |
| u8 * |
| format_udp_listener_session_ip4 (u8 * s, va_list * args) |
| { |
| u32 tci = va_arg (*args, u32); |
| udp_connection_t *tc = udp_listener_get (tci); |
| s = format (s, "[%s] %U:%d->%U:%d", "udp", format_ip4_address, |
| &tc->c_lcl_ip4, clib_net_to_host_u16 (tc->c_lcl_port), |
| format_ip4_address, &tc->c_rmt_ip4, |
| clib_net_to_host_u16 (tc->c_rmt_port)); |
| return s; |
| } |
| |
| u8 * |
| format_udp_listener_session_ip6 (u8 * s, va_list * args) |
| { |
| u32 tci = va_arg (*args, u32); |
| udp_connection_t *tc = udp_listener_get (tci); |
| s = format (s, "[%s] %U:%d->%U:%d", "udp", format_ip6_address, |
| &tc->c_lcl_ip6, clib_net_to_host_u16 (tc->c_lcl_port), |
| format_ip6_address, &tc->c_rmt_ip6, |
| clib_net_to_host_u16 (tc->c_rmt_port)); |
| return s; |
| } |
| |
| u16 |
| udp_send_mss_uri (transport_connection_t * t) |
| { |
| /* TODO figure out MTU of output interface */ |
| return 400; |
| } |
| |
| u32 |
| udp_send_space_uri (transport_connection_t * t) |
| { |
| /* No constraint on TX window */ |
| return ~0; |
| } |
| |
| int |
| udp_open_connection (ip46_address_t * addr, u16 port) |
| { |
| clib_warning ("Not implemented"); |
| return 0; |
| } |
| |
| /* *INDENT-OFF* */ |
| const static transport_proto_vft_t udp4_proto = { |
| .bind = udp_session_bind_ip4, |
| .open = udp_open_connection, |
| .unbind = udp_session_unbind_ip4, |
| .push_header = udp_push_header, |
| .get_connection = udp_session_get, |
| .get_listener = udp_session_get_listener, |
| .close = udp_session_close, |
| .send_mss = udp_send_mss_uri, |
| .send_space = udp_send_space_uri, |
| .format_connection = format_udp_session_ip4, |
| .format_listener = format_udp_listener_session_ip4 |
| }; |
| |
| const static transport_proto_vft_t udp6_proto = { |
| .bind = udp_session_bind_ip6, |
| .open = udp_open_connection, |
| .unbind = udp_session_unbind_ip6, |
| .push_header = udp_push_header, |
| .get_connection = udp_session_get, |
| .get_listener = udp_session_get_listener, |
| .close = udp_session_close, |
| .send_mss = udp_send_mss_uri, |
| .send_space = udp_send_space_uri, |
| .format_connection = format_udp_session_ip6, |
| .format_listener = format_udp_listener_session_ip6 |
| }; |
| /* *INDENT-ON* */ |
| |
| static clib_error_t * |
| udp_init (vlib_main_t * vm) |
| { |
| udp_uri_main_t *um = vnet_get_udp_main (); |
| ip_main_t *im = &ip_main; |
| vlib_thread_main_t *tm = vlib_get_thread_main (); |
| u32 num_threads; |
| clib_error_t *error = 0; |
| ip_protocol_info_t *pi; |
| |
| um->vlib_main = vm; |
| um->vnet_main = vnet_get_main (); |
| |
| if ((error = vlib_call_init_function (vm, ip_main_init))) |
| return error; |
| if ((error = vlib_call_init_function (vm, ip4_lookup_init))) |
| return error; |
| if ((error = vlib_call_init_function (vm, ip6_lookup_init))) |
| return error; |
| |
| /* |
| * Registrations |
| */ |
| |
| /* IP registration */ |
| pi = ip_get_protocol_info (im, IP_PROTOCOL_UDP); |
| if (pi == 0) |
| return clib_error_return (0, "UDP protocol info AWOL"); |
| pi->format_header = format_udp_header; |
| pi->unformat_pg_edit = unformat_pg_udp_header; |
| |
| |
| /* Register as transport with URI */ |
| session_register_transport (SESSION_TYPE_IP4_UDP, &udp4_proto); |
| session_register_transport (SESSION_TYPE_IP6_UDP, &udp6_proto); |
| |
| /* |
| * Initialize data structures |
| */ |
| |
| num_threads = 1 /* main thread */ + tm->n_threads; |
| vec_validate (um->udp_sessions, num_threads - 1); |
| |
| return error; |
| } |
| |
| VLIB_INIT_FUNCTION (udp_init); |
| |
| /* |
| * fd.io coding-style-patch-verification: ON |
| * |
| * Local Variables: |
| * eval: (c-set-style "gnu") |
| * End: |
| */ |