static vlib_buffer_t * create_buffer_for_client_message (vlib_main_t * vm, u32 sw_if_index, dhcp6_pd_client_state_t * client_state, u32 type) { dhcp6_client_common_main_t *ccm = &dhcp6_client_common_main; vnet_main_t *vnm = vnet_get_main (); vlib_buffer_t *b; u32 bi; ip6_header_t *ip; udp_header_t *udp; dhcpv6_header_t *dhcp; ip6_address_t src_addr; u32 dhcp_opt_len = 0; client_state->transaction_start = vlib_time_now (vm); u32 n_prefixes; u32 i; vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index); vnet_sw_interface_t *sup_sw = vnet_get_sup_sw_interface (vnm, sw_if_index); vnet_sw_interface_t *sw = vnet_get_sw_interface (vnm, sw_if_index); /* Interface(s) down? */ if ((hw->flags & VNET_HW_INTERFACE_FLAG_LINK_UP) == 0) return NULL; if ((sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0) return NULL; if ((sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0) return NULL; /* Get a link-local address */ src_addr = ip6_neighbor_get_link_local_address (sw_if_index); if (src_addr.as_u8[0] != 0xfe) { clib_warning ("Could not find source address to send DHCPv6 packet"); return NULL; } if (vlib_buffer_alloc (vm, &bi, 1) != 1) { clib_warning ("Buffer allocation failed"); return NULL; } b = vlib_get_buffer (vm, bi); vnet_buffer (b)->sw_if_index[VLIB_RX] = sw_if_index; vnet_buffer (b)->sw_if_index[VLIB_TX] = sw_if_index; client_state->adj_index = adj_mcast_add_or_lock (FIB_PROTOCOL_IP6, VNET_LINK_IP6, sw_if_index); vnet_buffer (b)->ip.adj_index[VLIB_TX] = client_state->adj_index; b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; ip = (ip6_header_t *) vlib_buffer_get_current (b); udp = (udp_header_t *) (ip + 1); dhcp = (dhcpv6_header_t *) (udp + 1); ip->src_address = src_addr; ip->hop_limit = 255; ip->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32 (0x6 << 28); ip->payload_length = 0; ip->protocol = IP_PROTOCOL_UDP; udp->src_port = clib_host_to_net_u16 (DHCPV6_CLIENT_PORT); udp->dst_port = clib_host_to_net_u16 (DHCPV6_SERVER_PORT); udp->checksum = 0; udp->length = 0; dhcp->msg_type = type; dhcp->xid[0] = (client_state->transaction_id & 0x00ff0000) >> 16; dhcp->xid[1] = (client_state->transaction_id & 0x0000ff00) >> 8; dhcp->xid[2] = (client_state->transaction_id & 0x000000ff) >> 0; void *d = (void *) dhcp->data; dhcpv6_option_t *duid; dhcpv6_elapsed_t *elapsed; dhcpv6_ia_header_t *ia_hdr; dhcpv6_ia_opt_pd_t *opt_pd; if (type == DHCPV6_MSG_SOLICIT || type == DHCPV6_MSG_REQUEST || type == DHCPV6_MSG_RENEW || type == DHCPV6_MSG_REBIND || type == DHCPV6_MSG_RELEASE) { duid = (dhcpv6_option_t *) d; duid->option = clib_host_to_net_u16 (DHCPV6_OPTION_CLIENTID); duid->length = clib_host_to_net_u16 (CLIENT_DUID_LENGTH); clib_memcpy (duid + 1, client_duid.bin_string, CLIENT_DUID_LENGTH); d += sizeof (*duid) + CLIENT_DUID_LENGTH; if (client_state->params.server_index != ~0) { server_id_t *se = &ccm->server_ids[client_state->params.server_index]; duid = (dhcpv6_option_t *) d; duid->option = clib_host_to_net_u16 (DHCPV6_OPTION_SERVERID); duid->length = clib_host_to_net_u16 (se->len); clib_memcpy (duid + 1, se->data, se->len); d += sizeof (*duid) + se->len; } elapsed = (dhcpv6_elapsed_t *) d; elapsed->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_ELAPSED_TIME); elapsed->opt.length = clib_host_to_net_u16 (sizeof (*elapsed) - sizeof (elapsed->opt)); elapsed->elapsed_10ms = 0; client_state->elapsed_pos = (char *) &elapsed->elapsed_10ms - (char *) vlib_buffer_get_current (b); d += sizeof (*elapsed); ia_hdr = (dhcpv6_ia_header_t *) d; ia_hdr->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_IA_PD); ia_hdr->iaid = clib_host_to_net_u32 (DHCPV6_CLIENT_IAID); ia_hdr->t1 = clib_host_to_net_u32 (client_state->params.T1); ia_hdr->t2 = clib_host_to_net_u32 (client_state->params.T2); d += sizeof (*ia_hdr); n_prefixes = vec_len (client_state->params.prefixes); ia_hdr->opt.length = clib_host_to_net_u16 (sizeof (*ia_hdr) + n_prefixes * sizeof (*opt_pd) - sizeof (ia_hdr->opt)); for (i = 0; i < n_prefixes; i++) { dhcp6_pd_send_client_message_params_prefix_t *pref = &client_state->params.prefixes[i]; opt_pd = (dhcpv6_ia_opt_pd_t *) d; opt_pd->opt.option = clib_host_to_net_u16 (DHCPV6_OPTION_IAPREFIX); opt_pd->opt.length = clib_host_to_net_u16 (sizeof (*opt_pd) - sizeof (opt_pd->opt)); opt_pd->addr = pref->prefix; opt_pd->prefix = pref->prefix_length; opt_pd->valid = clib_host_to_net_u32 (pref->valid_lt); opt_pd->preferred = clib_host_to_net_u32 (pref->preferred_lt); d += sizeof (*opt_pd); } } else { clib_warning ("State not implemented"); } dhcp_opt_len = ((u8 *) d) - dhcp->data; udp->length = clib_host_to_net_u16 (sizeof (*udp) + sizeof (*dhcp) + dhcp_opt_len); ip->payload_length = udp->length; b->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*dhcp) + dhcp_opt_len; ip->dst_address = all_dhcp6_relay_agents_and_servers; return b; }
static clib_error_t * recvmsg_helper (mc_socket_main_t * msm, int socket, struct sockaddr_in * rx_addr, u32 * buffer_index, u32 drop_message) { vlib_main_t * vm = msm->mc_main.vlib_main; vlib_buffer_t * b; uword n_left, n_alloc, n_mtu, i, i_rx; const uword buffer_size = VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES; word n_bytes_left; /* Make sure we have at least a MTU worth of buffers. */ n_mtu = msm->rx_mtu_n_buffers; n_left = vec_len (msm->rx_buffers); if (n_left < n_mtu) { uword max_alloc = 8 * n_mtu; vec_validate (msm->rx_buffers, max_alloc - 1); n_alloc = vlib_buffer_alloc (vm, msm->rx_buffers + n_left, max_alloc - n_left); _vec_len (msm->rx_buffers) = n_left + n_alloc; } ASSERT (vec_len (msm->rx_buffers) >= n_mtu); vec_validate (msm->iovecs, n_mtu - 1); /* Allocate RX buffers from end of rx_buffers. Turn them into iovecs to pass to readv. */ i_rx = vec_len (msm->rx_buffers) - 1; for (i = 0; i < n_mtu; i++) { b = vlib_get_buffer (vm, msm->rx_buffers[i_rx - i]); msm->iovecs[i].iov_base = b->data; msm->iovecs[i].iov_len = buffer_size; } _vec_len (msm->iovecs) = n_mtu; { struct msghdr h; memset (&h, 0, sizeof (h)); if (rx_addr) { h.msg_name = rx_addr; h.msg_namelen = sizeof (rx_addr[0]); } h.msg_iov = msm->iovecs; h.msg_iovlen = vec_len (msm->iovecs); n_bytes_left = recvmsg (socket, &h, 0); if (n_bytes_left < 0) return clib_error_return_unix (0, "recvmsg"); } if (drop_message) { *buffer_index = ~0; return 0; } *buffer_index = msm->rx_buffers[i_rx]; while (1) { b = vlib_get_buffer (vm, msm->rx_buffers[i_rx]); b->flags = 0; b->current_data = 0; b->current_length = n_bytes_left < buffer_size ? n_bytes_left : buffer_size; n_bytes_left -= buffer_size; if (n_bytes_left <= 0) break; i_rx--; b->flags |= VLIB_BUFFER_NEXT_PRESENT; b->next_buffer = msm->rx_buffers[i_rx]; } _vec_len (msm->rx_buffers) = i_rx; return 0 /* no error */; }