/* *------------------------------------------------------------------ * socket_api.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 #include #include #include #include #include #include #include #include #include #define vl_typedefs /* define message structures */ #include #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 #undef vl_printfun /* instantiate all the endian swap functions we know about */ #define vl_endianfun #include #undef vl_endianfun socket_main_t socket_main; #define SOCK_API_REG_HANDLE_BIT (1<<31) static u32 sock_api_registration_handle (vl_api_registration_t * regp) { ASSERT (regp->vl_api_registration_pool_index < SOCK_API_REG_HANDLE_BIT); return regp->vl_api_registration_pool_index | SOCK_API_REG_HANDLE_BIT; } static u32 socket_api_registration_handle_to_index (u32 reg_index) { return (reg_index & ~SOCK_API_REG_HANDLE_BIT); } u8 vl_socket_api_registration_handle_is_valid (u32 reg_handle) { return ((reg_handle & SOCK_API_REG_HANDLE_BIT) != 0); } void vl_sock_api_dump_clients (vlib_main_t * vm, api_main_t * am) { vl_api_registration_t *reg; socket_main_t *sm = &socket_main; clib_file_t *f; /* * Must have at least one active client, not counting the * REGISTRATION_TYPE_SOCKET_LISTEN bind/accept socket */ if (pool_elts (sm->registration_pool) < 2) return; vlib_cli_output (vm, "Socket clients"); vlib_cli_output (vm, "%20s %8s", "Name", "Fildesc"); /* *INDENT-OFF* */ pool_foreach (reg, sm->registration_pool, ({ if (reg->registration_type == REGISTRATION_TYPE_SOCKET_SERVER) { f = vl_api_registration_file (reg); vlib_cli_output (vm, "%20s %8d", reg->name, f->file_descriptor); } })); /* *INDENT-ON* */ } vl_api_registration_t * vl_socket_api_client_handle_to_registration (u32 handle) { socket_main_t *sm = &socket_main; u32 index = socket_api_registration_handle_to_index (handle); if (pool_is_free_index (sm->registration_pool, index)) { #if DEBUG > 2 clib_warning ("Invalid index %d\n", index); #endif return 0; } return pool_elt_at_index (sm->registration_pool, index); } void vl_socket_api_send (vl_api_registration_t * rp, u8 * elem) { #if CLIB_DEBUG > 1 u32 output_length; #endif socket_main_t *sm = &socket_main; u16 msg_id = ntohs (*(u16 *) elem); api_main_t *am = &api_main; msgbuf_t *mb = (msgbuf_t *) (elem - offsetof (msgbuf_t, data)); vl_api_registration_t *sock_rp; clib_file_main_t *fm = &file_main; clib_error_t *error; clib_file_t *cf; cf = vl_api_registration_file (rp); ASSERT (rp->registration_type > REGISTRATION_TYPE_SHMEM); if (msg_id >= vec_len (am->api_trace_cfg)) { clib_warning ("id out of range: %d", msg_id); vl_msg_api_free ((void *) elem); return; } sock_rp = pool_elt_at_index (sm->registration_pool, rp->vl_api_registration_pool_index); ASSERT (sock_rp); /* Add the msgbuf_t to the output vector */ vec_add (sock_rp->output_vector, (u8 *) mb, sizeof (*mb)); /* Try to send the message and save any error like * we do in the input epoll loop */ vec_add (sock_rp->output_vector, elem, ntohl (mb->data_len)); error = clib_file_write (cf); unix_save_error (&unix_main, error); /* If we didn't finish sending everything, wait for tx space */ if (vec_len (sock_rp->output_vector) > 0 && !(cf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE)) { cf->flags |= UNIX_FILE_DATA_AVAILABLE_TO_WRITE; fm->file_update (cf, UNIX_FILE_UPDATE_MODIFY); } #if CLIB_DEBUG > 1 output_length = sizeof (*mb) + ntohl (mb->data_len); clib_warning ("wrote %u bytes to fd %d", output_length, cf->file_descriptor); #endif vl_msg_api_free ((void *) elem); } void vl_socket_free_registration_index (u32 pool_index) { int i; vl_api_registration_t *rp; if (pool_is_free_index (socket_main.registration_pool, pool_index)) { clib_warning ("main pool index %d already free", pool_index); return; } rp = pool_elt_at_index (socket_main.registration_pool, pool_index); ASSERT (rp->registration_type != REGISTRATION_TYPE_FREE); for (i = 0; i < vec_len (rp->additional_fds_to_close); i++) if (close (rp->additional_fds_to_close[i]) < 0) clib_unix_warning ("close"); vec_free (rp->additional_fds_to_close); vec_free (rp->name); vec_free (rp->unprocessed_input); vec_free (rp->output_vector); rp->registration_type = REGISTRATION_TYPE_FREE; pool_put (socket_main.registration_pool, rp); } void vl_socket_process_api_msg (clib_file_t * uf, vl_api_registration_t * rp, i8 * input_v) { msgbuf_t *mbp = (msgbuf_t *) input_v; u8 *the_msg = (u8 *) (mbp->data); socket_main.current_uf = uf; socket_main.current_rp = rp; vl_msg_api_socket_handler (the_msg); socket_main.current_uf = 0; socket_main.current_rp = 0; } clib_error_t * vl_socket_read_ready (clib_file_t * uf) { clib_file_main_t *fm = &file_main; vlib_main_t *vm = vlib_get_main (); vl_api_registration_t *rp; int n; i8 *msg_buffer = 0; u8 *data_for_process; u32 msg_len; u32 save_input_buffer_length = vec_len (socket_main.input_buffer); vl_socket_args_for_process_t *a; msgbuf_t *mbp; int mbp_set = 0; rp = pool_elt_at_index (socket_main.registration_pool, uf->private_data); n = read (uf->file_descriptor, socket_main.input_buffer, vec_len (socket_main.input_buffer)); if (n <= 0 && errno != EAGAIN) { clib_file_del (fm, uf); if (!pool_is_free (socket_main.registration_pool, rp)) { u32 index = rp - socket_main.registration_pool; vl_socket_free_registration_index (index); } else { clib_warning ("client index %d already free?", rp->vl_api_registration_pool_index); } return 0; } _vec_len (socket_main.input_buffer) = n; /* * Look for bugs here. This code is tricky because * data read from a stream socket does not honor message * boundaries. In the case of a long message (>4K bytes) * we have to do (at least) 2 reads, etc. */ do { if (vec_len (rp->unprocessed_input)) { vec_append (rp->unprocessed_input, socket_main.input_buffer); msg_buffer = rp->unprocessed_input; } else { msg_buffer = socket_main.input_buffer; mbp_set = 0; } if (mbp_set == 0) { /* Any chance that we have a complete message? */ if (vec_len (msg_buffer) <= sizeof (msgbuf_t)) goto save_and_split; mbp = (msgbuf_t *) msg_buffer; msg_len = ntohl (mbp->data_len); mbp_set = 1; } /* We don't have the entire message yet. */ if (mbp_set == 0 || (msg_len + sizeof (msgbuf_t)) > vec_len (msg_buffer)) { save_and_split: /* if we were using the input buffer save the fragment */ if (msg_buffer == socket_main.input_buffer) { ASSERT (vec_len (rp->unprocessed_input) == 0); vec_validate (rp->unprocessed_input, vec_len (msg_buffer) - 1); clib_memcpy_fast (rp->unprocessed_input, msg_buffer, vec_len (msg_buffer)); _vec_len (rp->unprocessed_input) = vec_len (msg_buffer); } _vec_len (socket_main.input_buffer) = save_input_buffer_length; return 0; } data_for_process = (u8 *) vec_dup (msg_buffer); _vec_len (data_for_process) = (msg_len + sizeof (msgbuf_t)); pool_get (socket_main.process_args, a); a->clib_file = uf; a->regp = rp; a->data = data_for_process; vlib_process_signal_event (vm, vl_api_clnt_node.index, SOCKET_READ_EVENT, a - socket_main.process_args); if (n > (msg_len + sizeof (*mbp))) vec_delete (msg_buffer, msg_len + sizeof (*mbp), 0); else _vec_len (msg_buffer) = 0; n -= msg_len + sizeof (msgbuf_t); msg_len = 0; mbp_set = 0; } while (n > 0); _vec_len (socket_main.input_buffer) = save_input_buffer_length; return 0; } clib_error_t * vl_socket_write_ready (clib_file_t * uf) { clib_file_main_t *fm = &file_main; vl_api_registration_t *rp; int n; rp = pool_elt_at_index (socket_main.registration_pool, uf->private_data); /* Flush output vector. */ size_t total_bytes = vec_len (rp->output_vector); size_t bytes_to_send, remaining_bytes = total_bytes; void *p = rp->output_vector; while (remaining_bytes > 0) { bytes_to_send = remaining_bytes > 4096 ? 4096 : remaining_bytes; n = write (uf->file_descriptor, p, bytes_to_send); if (n < 0) { if (errno == EAGAIN) { break; } #if DEBUG > 2 clib_warning ("write error, close the file...\n"); #endif clib_file_del (fm, uf); vl_socket_free_registration_index (rp - socket_main.registration_pool); return 0; } remaining_bytes -= bytes_to_send; p += bytes_to_send; } vec_delete (rp->output_vector, total_bytes - remaining_bytes, 0); if (vec_len (rp->output_vector) <= 0 && (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE)) { uf->flags &= ~UNIX_FILE_DATA_AVAILABLE_TO_WRITE; fm->file_update (uf, UNIX_FILE_UPDATE_MODIFY); } return 0; } clib_error_t * vl_socket_error_ready (clib_file_t * uf) { vl_api_registration_t *rp; clib_file_main_t *fm = &file_main; rp = pool_elt_at_index (socket_main.registration_pool, uf->private_data); clib_file_del (fm, uf); vl_socket_free_registration_index (rp - socket_main.registration_pool); return 0; } void socksvr_file_add (clib_file_main_t * fm, int fd) { vl_api_registration_t *rp; clib_file_t template = { 0 }; pool_get (socket_main.registration_pool, rp); clib_memset (rp, 0, sizeof (*rp)); template.read_function = vl_socket_read_ready; template.write_function = vl_socket_write_ready; template.error_function = vl_socket_error_ready; template.file_descriptor = fd; template.private_data = rp - socket_main.registration_pool; rp->registration_type = REGISTRATION_TYPE_SOCKET_SERVER; rp->vl_api_registration_pool_index = rp - socket_main.registration_pool; rp->clib_file_index = clib_file_add (fm, &template); } static clib_error_t * socksvr_accept_ready (clib_file_t * uf) { clib_file_main_t *fm = &file_main; socket_main_t *sm = &sock
dhcp_client_config {interface} {hostname}
thing wipes out early */ if (sm->registration_pool) { u32 index; /* *INDENT-OFF* */ pool_foreach (rp, sm->registration_pool, ({ vl_api_registration_del_file (rp); index = rp->vl_api_registration_pool_index; vl_socket_free_registration_index (index); })); /* *INDENT-ON* */ } return 0; } VLIB_MAIN_LOOP_EXIT_FUNCTION (socket_exit); static clib_error_t * socksvr_config (vlib_main_t * vm, unformat_input_t * input) { socket_main_t *sm = &socket_main; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "socket-name %s", &sm->socket_name)) ; else if (unformat (input, "default")) { sm->socket_name = format (0, "%s%c", API_SOCKET_FILE, 0); } else { return clib_error_return (0, "unknown input '%U'", format_unformat_error, input); } } return 0; } VLIB_CONFIG_FUNCTION (socksvr_config, "socksvr"); void vlibsocket_reference () { } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */