|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* Copyright (c) 2011, Broadcom Corporation
|
|
Packit |
eace71 |
* Copyright (c) 2014, QLogic Corporation
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* Written by: Eddie Wai <eddie.wai@broadcom.com>
|
|
Packit |
eace71 |
* Based on code from Kevin Tran's iSCSI boot code
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* All rights reserved.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* Redistribution and use in source and binary forms, with or without
|
|
Packit |
eace71 |
* modification, are permitted provided that the following conditions
|
|
Packit |
eace71 |
* are met:
|
|
Packit |
eace71 |
* 1. Redistributions of source code must retain the above copyright
|
|
Packit |
eace71 |
* notice, this list of conditions and the following disclaimer.
|
|
Packit |
eace71 |
* 2. Redistributions in binary form must reproduce the above copyright
|
|
Packit |
eace71 |
* notice, this list of conditions and the following disclaimer in the
|
|
Packit |
eace71 |
* documentation and/or other materials provided with the distribution.
|
|
Packit |
eace71 |
* 3. All advertising materials mentioning features or use of this software
|
|
Packit |
eace71 |
* must display the following acknowledgement:
|
|
Packit |
eace71 |
* This product includes software developed by Adam Dunkels.
|
|
Packit |
eace71 |
* 4. The name of the author may not be used to endorse or promote
|
|
Packit |
eace71 |
* products derived from this software without specific prior
|
|
Packit |
eace71 |
* written permission.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
Packit |
eace71 |
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
Packit |
eace71 |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
Packit |
eace71 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
Packit |
eace71 |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
Packit |
eace71 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
Packit |
eace71 |
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
Packit |
eace71 |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
Packit |
eace71 |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
Packit |
eace71 |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
Packit |
eace71 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* dhcpv6.c - DHCPv6 engine
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
#include <stdio.h>
|
|
Packit |
eace71 |
#include <string.h>
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
#include "ipv6.h"
|
|
Packit |
eace71 |
#include "ipv6_pkt.h"
|
|
Packit |
eace71 |
#include "dhcpv6.h"
|
|
Packit |
eace71 |
#include "logger.h"
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Local function prototypes */
|
|
Packit |
eace71 |
static int dhcpv6_send_solicit_packet(struct dhcpv6_context *context);
|
|
Packit |
eace71 |
static int dhcpv6_send_request_packet(struct dhcpv6_context *context);
|
|
Packit |
eace71 |
static u16_t dhcpv6_init_packet(struct dhcpv6_context *context, u8_t type);
|
|
Packit |
eace71 |
static void dhcpv6_init_dhcpv6_server_addr(struct ipv6_addr *addr);
|
|
Packit |
eace71 |
static void dhcpv6_handle_advertise(struct dhcpv6_context *context,
|
|
Packit |
eace71 |
u16_t dhcpv6_len);
|
|
Packit |
eace71 |
static void dhcpv6_handle_reply(struct dhcpv6_context *context,
|
|
Packit |
eace71 |
u16_t dhcpv6_len);
|
|
Packit |
eace71 |
static int dhcpv6_process_opt_ia_na(struct dhcpv6_context *context,
|
|
Packit |
eace71 |
struct dhcpv6_opt_hdr *opt_hdr);
|
|
Packit |
eace71 |
static void dhcpv6_process_opt_dns_servers(struct dhcpv6_context *context,
|
|
Packit |
eace71 |
struct dhcpv6_opt_hdr *opt_hdr);
|
|
Packit |
eace71 |
static void dhcpv6_parse_vendor_option(struct dhcpv6_context *context,
|
|
Packit |
eace71 |
u8_t *option, int len);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
void dhcpv6_init(struct dhcpv6_context *context)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
context->seconds = 0;
|
|
Packit |
eace71 |
context->our_mac_addr =
|
|
Packit |
eace71 |
ipv6_get_link_addr(context->ipv6_context);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Use the last four bytes of MAC address as base of the transaction
|
|
Packit |
eace71 |
ID */
|
|
Packit |
eace71 |
context->dhcpv6_transaction_id = context->our_mac_addr->last_4_bytes;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
context->dhcpv6_done = FALSE;
|
|
Packit |
eace71 |
strcpy(context->dhcp_vendor_id, "BRCM ISAN");
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
int dhcpv6_do_discovery(struct dhcpv6_context *context)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
int retc = ISCSI_FAILURE;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
context->eth =
|
|
Packit |
eace71 |
(struct eth_hdr *)context->ipv6_context->ustack->data_link_layer;
|
|
Packit |
eace71 |
context->ipv6 =
|
|
Packit |
eace71 |
(struct ipv6_hdr *)context->ipv6_context->ustack->network_layer;
|
|
Packit |
eace71 |
context->udp =
|
|
Packit |
eace71 |
(struct udp_hdr *)((u8_t *)context->ipv6 + sizeof(struct ipv6_hdr));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Send out DHCPv6 Solicit packet. */
|
|
Packit |
eace71 |
dhcpv6_send_solicit_packet(context);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return retc;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int dhcpv6_send_solicit_packet(struct dhcpv6_context *context)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
u16_t packet_len;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG("DHCPV6: Send solicit");
|
|
Packit |
eace71 |
packet_len = dhcpv6_init_packet(context, DHCPV6_SOLICIT);
|
|
Packit |
eace71 |
context->dhcpv6_state = DHCPV6_STATE_SOLICIT_SENT;
|
|
Packit |
eace71 |
ipv6_send_udp_packet(context->ipv6_context, packet_len);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int dhcpv6_send_request_packet(struct dhcpv6_context *context)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
u16_t packet_len;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG("DHCPV6: Send request");
|
|
Packit |
eace71 |
packet_len = dhcpv6_init_packet(context, DHCPV6_REQUEST);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
context->dhcpv6_state = DHCPV6_STATE_REQ_SENT;
|
|
Packit |
eace71 |
ipv6_send_udp_packet(context->ipv6_context, packet_len);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return 0;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static u16_t dhcpv6_init_packet(struct dhcpv6_context *context, u8_t type)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
u16_t pkt_len;
|
|
Packit |
eace71 |
struct udp_hdr *udp = context->udp;
|
|
Packit |
eace71 |
union dhcpv6_hdr *dhcpv6;
|
|
Packit |
eace71 |
struct dhcpv6_option *opt;
|
|
Packit |
eace71 |
u16_t len;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Initialize dest IP with well-known DHCP server address */
|
|
Packit |
eace71 |
dhcpv6_init_dhcpv6_server_addr(&context->ipv6->ipv6_dst);
|
|
Packit |
eace71 |
/* Initialize dest MAC based on MC dest IP */
|
|
Packit |
eace71 |
ipv6_mc_init_dest_mac(context->eth, context->ipv6);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Initialize UDP header */
|
|
Packit |
eace71 |
udp->src_port = HOST_TO_NET16(DHCPV6_CLIENT_PORT);
|
|
Packit |
eace71 |
udp->dest_port = HOST_TO_NET16(DHCPV6_SERVER_PORT);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* DHCPv6 section has the following format per RFC 3315
|
|
Packit |
eace71 |
*
|
|
Packit |
eace71 |
* 0 1 2 3
|
|
Packit |
eace71 |
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
Packit |
eace71 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
Packit |
eace71 |
* | msg-type | transaction-id |
|
|
Packit |
eace71 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
Packit |
eace71 |
* | |
|
|
Packit |
eace71 |
* . options .
|
|
Packit |
eace71 |
* . (variable) .
|
|
Packit |
eace71 |
* | |
|
|
Packit |
eace71 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
dhcpv6 = (union dhcpv6_hdr *)((u8_t *)udp + sizeof(struct udp_hdr));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (dhcpv6->dhcpv6_type != type)
|
|
Packit |
eace71 |
context->dhcpv6_transaction_id++;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
dhcpv6->dhcpv6_trans_id = HOST_TO_NET16(context->dhcpv6_transaction_id);
|
|
Packit |
eace71 |
dhcpv6->dhcpv6_type = type;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Keep track of length of all DHCP options. */
|
|
Packit |
eace71 |
pkt_len = sizeof(union dhcpv6_hdr);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (dhcpv6->dhcpv6_type == DHCPV6_REQUEST) {
|
|
Packit |
eace71 |
/* We will send back whatever DHCPv6 sent us */
|
|
Packit |
eace71 |
return ((u8_t *)udp - (u8_t *)context->eth +
|
|
Packit |
eace71 |
NET_TO_HOST16(udp->length));
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
opt = (struct dhcpv6_option *)((u8_t *)dhcpv6 +
|
|
Packit |
eace71 |
sizeof(union dhcpv6_hdr));
|
|
Packit |
eace71 |
/* Add client ID option */
|
|
Packit |
eace71 |
opt->hdr.type = HOST_TO_NET16(DHCPV6_OPT_CLIENTID);
|
|
Packit |
eace71 |
opt->hdr.length = HOST_TO_NET16(sizeof(struct dhcpv6_opt_client_id));
|
|
Packit |
eace71 |
opt->type.client_id.duid_type =
|
|
Packit |
eace71 |
HOST_TO_NET16(DHCPV6_DUID_TYPE_LINK_LAYER_AND_TIME);
|
|
Packit |
eace71 |
opt->type.client_id.hw_type = HOST_TO_NET16(DHCPV6_HW_TYPE_ETHERNET);
|
|
Packit |
eace71 |
opt->type.client_id.time = HOST_TO_NET32(clock_time()/1000 -
|
|
Packit |
eace71 |
0x3A4FC880);
|
|
Packit |
eace71 |
memcpy((char *)&opt->type.client_id.link_layer_addr,
|
|
Packit |
eace71 |
(char *)context->our_mac_addr, sizeof(struct mac_address));
|
|
Packit |
eace71 |
pkt_len += sizeof(struct dhcpv6_opt_client_id) +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_hdr);
|
|
Packit |
eace71 |
opt = (struct dhcpv6_option *)((u8_t *)opt +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_client_id) +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_hdr));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Add Vendor Class option if it's configured */
|
|
Packit |
eace71 |
len = strlen(context->dhcp_vendor_id);
|
|
Packit |
eace71 |
if (len > 0) {
|
|
Packit |
eace71 |
opt->hdr.type = HOST_TO_NET16(DHCPV6_OPT_VENDOR_CLASS);
|
|
Packit |
eace71 |
opt->hdr.length =
|
|
Packit |
eace71 |
HOST_TO_NET16(sizeof(struct dhcpv6_vendor_class)
|
|
Packit |
eace71 |
+ len - 1);
|
|
Packit |
eace71 |
opt->type.vendor_class.enterprise_number =
|
|
Packit |
eace71 |
HOST_TO_NET32(IANA_ENTERPRISE_NUM_BROADCOM);
|
|
Packit |
eace71 |
opt->type.vendor_class.vendor_class_length = HOST_TO_NET16(len);
|
|
Packit |
eace71 |
memcpy((char *)&opt->type.vendor_class.
|
|
Packit |
eace71 |
vendor_class_data[0],
|
|
Packit |
eace71 |
(char *)context->dhcp_vendor_id, len);
|
|
Packit |
eace71 |
pkt_len +=
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_vendor_class) - 1 + len +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_hdr);
|
|
Packit |
eace71 |
opt =
|
|
Packit |
eace71 |
(struct dhcpv6_option *)((u8_t *)opt +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_vendor_class) - 1 + len +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_hdr));
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Add IA_NA option */
|
|
Packit |
eace71 |
opt->hdr.type = HOST_TO_NET16(DHCPV6_OPT_IA_NA);
|
|
Packit |
eace71 |
opt->hdr.length = HOST_TO_NET16(sizeof(struct dhcpv6_opt_id_assoc_na));
|
|
Packit |
eace71 |
opt->type.ida_na.iaid = htonl(context->our_mac_addr->last_4_bytes);
|
|
Packit |
eace71 |
opt->type.ida_na.t1 = 0;
|
|
Packit |
eace71 |
opt->type.ida_na.t2 = 0;
|
|
Packit |
eace71 |
pkt_len += sizeof(struct dhcpv6_opt_id_assoc_na) +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_hdr);
|
|
Packit |
eace71 |
opt = (struct dhcpv6_option *)((u8_t *)opt +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_id_assoc_na) +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_hdr));
|
|
Packit |
eace71 |
/* Add Elapsed Time option */
|
|
Packit |
eace71 |
opt->hdr.type = HOST_TO_NET16(DHCPV6_OPT_ELAPSED_TIME);
|
|
Packit |
eace71 |
opt->hdr.length = HOST_TO_NET16(sizeof(struct dhcpv6_opt_elapse_time));
|
|
Packit |
eace71 |
opt->type.elapsed_time.time = HOST_TO_NET16(context->seconds);
|
|
Packit |
eace71 |
pkt_len += sizeof(struct dhcpv6_opt_elapse_time) +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_hdr);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Add Option Request List */
|
|
Packit |
eace71 |
opt = (struct dhcpv6_option *)((u8_t *)opt +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_elapse_time) +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_hdr));
|
|
Packit |
eace71 |
opt->hdr.type = HOST_TO_NET16(DHCPV6_OPT_ORO);
|
|
Packit |
eace71 |
opt->hdr.length = HOST_TO_NET16(3 *
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_request_list));
|
|
Packit |
eace71 |
opt->type.list.request_code[0] = HOST_TO_NET16(DHCPV6_OPT_VENDOR_CLASS);
|
|
Packit |
eace71 |
opt->type.list.request_code[1] = HOST_TO_NET16(DHCPV6_OPT_VENDOR_OPTS);
|
|
Packit |
eace71 |
opt->type.list.request_code[2] = HOST_TO_NET16(DHCPV6_OPT_DNS_SERVERS);
|
|
Packit |
eace71 |
pkt_len += 3 * sizeof(struct dhcpv6_opt_request_list) +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_hdr);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
udp->length = HOST_TO_NET16(sizeof(struct udp_hdr) + pkt_len);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
pkt_len +=
|
|
Packit |
eace71 |
((u8_t *)udp - (u8_t *)context->eth) + sizeof(struct udp_hdr);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return pkt_len;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static void dhcpv6_init_dhcpv6_server_addr(struct ipv6_addr *addr)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
/* Well-known DHCPv6 server address is ff02::1:2 */
|
|
Packit |
eace71 |
memset((char *)addr, 0, sizeof(struct ipv6_addr));
|
|
Packit |
eace71 |
addr->addr8[0] = 0xff;
|
|
Packit |
eace71 |
addr->addr8[1] = 0x02;
|
|
Packit |
eace71 |
addr->addr8[13] = 0x01;
|
|
Packit |
eace71 |
addr->addr8[15] = 0x02;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
void ipv6_udp_handle_dhcp(struct dhcpv6_context *context)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
union dhcpv6_hdr *dhcpv6;
|
|
Packit |
eace71 |
u16_t dhcpv6_len;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (context->dhcpv6_done == TRUE)
|
|
Packit |
eace71 |
return;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
dhcpv6 = (union dhcpv6_hdr *)((u8_t *)context->udp +
|
|
Packit |
eace71 |
sizeof(struct udp_hdr));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (dhcpv6->dhcpv6_trans_id !=
|
|
Packit |
eace71 |
HOST_TO_NET16(context->dhcpv6_transaction_id)) {
|
|
Packit |
eace71 |
LOG_ERR("DHCPv6 transaction-id error, sent %x, received %x",
|
|
Packit |
eace71 |
HOST_TO_NET16(context->dhcpv6_transaction_id),
|
|
Packit |
eace71 |
dhcpv6->dhcpv6_trans_id);
|
|
Packit |
eace71 |
return;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
dhcpv6_len =
|
|
Packit |
eace71 |
NET_TO_HOST16(context->udp->length) - sizeof(struct udp_hdr);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
switch (dhcpv6->dhcpv6_type) {
|
|
Packit |
eace71 |
case DHCPV6_ADVERTISE:
|
|
Packit |
eace71 |
dhcpv6_handle_advertise(context, dhcpv6_len);
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
case DHCPV6_REPLY:
|
|
Packit |
eace71 |
dhcpv6_handle_reply(context, dhcpv6_len);
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
default:
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static void dhcpv6_handle_advertise(struct dhcpv6_context *context,
|
|
Packit |
eace71 |
u16_t dhcpv6_len)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
union dhcpv6_hdr *dhcpv6 =
|
|
Packit |
eace71 |
(union dhcpv6_hdr *)((u8_t *)context->udp +
|
|
Packit |
eace71 |
sizeof(struct udp_hdr));
|
|
Packit |
eace71 |
struct dhcpv6_opt_hdr *opt;
|
|
Packit |
eace71 |
u16_t type;
|
|
Packit |
eace71 |
int i;
|
|
Packit |
eace71 |
int opt_len;
|
|
Packit |
eace71 |
u8_t *vendor_id = NULL;
|
|
Packit |
eace71 |
u16_t vendor_id_len = 0;
|
|
Packit |
eace71 |
u8_t *vendor_opt_data = NULL;
|
|
Packit |
eace71 |
int vendor_opt_len = 0;
|
|
Packit |
eace71 |
int addr_cnt = 0;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* We only handle DHCPv6 advertise if we recently sent DHCPv6 solicit */
|
|
Packit |
eace71 |
if (context->dhcpv6_state != DHCPV6_STATE_SOLICIT_SENT)
|
|
Packit |
eace71 |
return;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
LOG_DEBUG("DHCPV6: handle advertise");
|
|
Packit |
eace71 |
context->dhcpv6_state = DHCPV6_STATE_ADV_RCVD;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
i = 0;
|
|
Packit |
eace71 |
while (i < (dhcpv6_len - sizeof(union dhcpv6_hdr))) {
|
|
Packit |
eace71 |
opt = (struct dhcpv6_opt_hdr *)((u8_t *)dhcpv6 +
|
|
Packit |
eace71 |
sizeof(union dhcpv6_hdr) + i);
|
|
Packit |
eace71 |
opt_len = NET_TO_HOST16(opt->length);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
type = NET_TO_HOST16(opt->type);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* We only care about some of the options */
|
|
Packit |
eace71 |
switch (type) {
|
|
Packit |
eace71 |
case DHCPV6_OPT_IA_NA:
|
|
Packit |
eace71 |
if (context->
|
|
Packit |
eace71 |
dhcpv6_task & DHCPV6_TASK_GET_IP_ADDRESS) {
|
|
Packit |
eace71 |
addr_cnt +=
|
|
Packit |
eace71 |
dhcpv6_process_opt_ia_na(context, opt);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
case DHCPV6_OPT_VENDOR_CLASS:
|
|
Packit |
eace71 |
vendor_id_len =
|
|
Packit |
eace71 |
NET_TO_HOST16(((struct dhcpv6_option *)opt)->type.
|
|
Packit |
eace71 |
vendor_class.vendor_class_length);
|
|
Packit |
eace71 |
vendor_id =
|
|
Packit |
eace71 |
&((struct dhcpv6_option *)opt)->type.vendor_class.
|
|
Packit |
eace71 |
vendor_class_data[0];
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
case DHCPV6_OPT_VENDOR_OPTS:
|
|
Packit |
eace71 |
vendor_opt_len = opt_len - 4;
|
|
Packit |
eace71 |
vendor_opt_data =
|
|
Packit |
eace71 |
&((struct dhcpv6_option *)opt)->type.vendor_opts.
|
|
Packit |
eace71 |
vendor_opt_data[0];
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
case DHCPV6_OPT_DNS_SERVERS:
|
|
Packit |
eace71 |
if (context->dhcpv6_task & DHCPV6_TASK_GET_OTHER_PARAMS)
|
|
Packit |
eace71 |
dhcpv6_process_opt_dns_servers(context, opt);
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
default:
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
i += NET_TO_HOST16(opt->length) + sizeof(struct dhcpv6_opt_hdr);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (context->dhcpv6_task & DHCPV6_TASK_GET_OTHER_PARAMS) {
|
|
Packit |
eace71 |
if ((vendor_id_len > 0) &&
|
|
Packit |
eace71 |
(strncmp((char *)vendor_id,
|
|
Packit |
eace71 |
(char *)context->dhcp_vendor_id,
|
|
Packit |
eace71 |
vendor_id_len) == 0)) {
|
|
Packit |
eace71 |
dhcpv6_parse_vendor_option(context,
|
|
Packit |
eace71 |
vendor_opt_data,
|
|
Packit |
eace71 |
vendor_opt_len);
|
|
Packit |
eace71 |
context->dhcpv6_done = TRUE;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (context->dhcpv6_task & DHCPV6_TASK_GET_IP_ADDRESS) {
|
|
Packit |
eace71 |
if (addr_cnt > 0) {
|
|
Packit |
eace71 |
/*
|
|
Packit |
eace71 |
* If we need to acquire IP address from the server,
|
|
Packit |
eace71 |
* we need to send Request to server to confirm.
|
|
Packit |
eace71 |
*/
|
|
Packit |
eace71 |
dhcpv6_send_request_packet(context);
|
|
Packit |
eace71 |
context->dhcpv6_done = TRUE;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (context->dhcpv6_done) {
|
|
Packit |
eace71 |
/* Keep track of IPv6 address of DHCHv6 server */
|
|
Packit |
eace71 |
memcpy((char *)&context->dhcp_server,
|
|
Packit |
eace71 |
(char *)&context->ipv6->ipv6_src,
|
|
Packit |
eace71 |
sizeof(struct ipv6_addr));
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static int dhcpv6_process_opt_ia_na(struct dhcpv6_context *context,
|
|
Packit |
eace71 |
struct dhcpv6_opt_hdr *opt_hdr)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
int i;
|
|
Packit |
eace71 |
int opt_len;
|
|
Packit |
eace71 |
struct dhcpv6_option *opt;
|
|
Packit |
eace71 |
int len;
|
|
Packit |
eace71 |
int addr_cnt;
|
|
Packit |
eace71 |
opt_len = NET_TO_HOST16(opt_hdr->length) -
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_id_assoc_na);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
i = 0;
|
|
Packit |
eace71 |
addr_cnt = 0;
|
|
Packit |
eace71 |
while (i < opt_len) {
|
|
Packit |
eace71 |
opt =
|
|
Packit |
eace71 |
(struct dhcpv6_option *)((u8_t *)opt_hdr +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_hdr) +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_id_assoc_na) + i);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
len = NET_TO_HOST16(opt->hdr.length);
|
|
Packit |
eace71 |
switch (NET_TO_HOST16(opt->hdr.type)) {
|
|
Packit |
eace71 |
case DHCPV6_OPT_IAADDR:
|
|
Packit |
eace71 |
if (len >
|
|
Packit |
eace71 |
(sizeof(struct dhcpv6_opt_hdr) +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_iaa_addr))) {
|
|
Packit |
eace71 |
struct dhcpv6_option *in_opt;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
in_opt = (struct dhcpv6_option *)((u8_t *)opt +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_hdr) +
|
|
Packit |
eace71 |
sizeof(struct dhcpv6_opt_iaa_addr));
|
|
Packit |
eace71 |
if (in_opt->hdr.type ==
|
|
Packit |
eace71 |
HOST_TO_NET16(DHCPV6_OPT_STATUS_CODE)) {
|
|
Packit |
eace71 |
/* This entry has error! */
|
|
Packit |
eace71 |
if (in_opt->type.sts.status != 0)
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
LOG_INFO("DHCPv6: Got IP Addr");
|
|
Packit |
eace71 |
/* Status is OK, let's add this addr to our address
|
|
Packit |
eace71 |
list */
|
|
Packit |
eace71 |
ipv6_add_prefix_entry(context->ipv6_context,
|
|
Packit |
eace71 |
&opt->type.iaa_addr.addr, 64);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
/* Add multicast address for this address */
|
|
Packit |
eace71 |
ipv6_add_solit_node_address(context->
|
|
Packit |
eace71 |
ipv6_context,
|
|
Packit |
eace71 |
&opt->type.iaa_addr.addr);
|
|
Packit |
eace71 |
addr_cnt++;
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
default:
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
i += len + sizeof(struct dhcpv6_opt_hdr);
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
return addr_cnt;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static void dhcpv6_process_opt_dns_servers(struct dhcpv6_context *context,
|
|
Packit |
eace71 |
struct dhcpv6_opt_hdr *opt_hdr)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
int opt_len;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
opt_len = NET_TO_HOST16(opt_hdr->length);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (opt_len >= sizeof(struct ipv6_addr))
|
|
Packit |
eace71 |
memcpy((char *)&context->primary_dns_server,
|
|
Packit |
eace71 |
(char *)&((struct dhcpv6_option *)opt_hdr)->type.dns.
|
|
Packit |
eace71 |
primary_addr, sizeof(struct ipv6_addr));
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
if (opt_len >= 2 * sizeof(struct ipv6_addr))
|
|
Packit |
eace71 |
memcpy((char *)&context->secondary_dns_server,
|
|
Packit |
eace71 |
(char *)&((struct dhcpv6_option *)opt_hdr)->type.dns.
|
|
Packit |
eace71 |
secondary_addr, sizeof(struct ipv6_addr));
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static void dhcpv6_handle_reply(struct dhcpv6_context *context,
|
|
Packit |
eace71 |
u16_t dhcpv6_len)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
if (context->dhcpv6_state != DHCPV6_STATE_REQ_SENT)
|
|
Packit |
eace71 |
return;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
context->dhcpv6_done = TRUE;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
static void dhcpv6_parse_vendor_option(struct dhcpv6_context *context,
|
|
Packit |
eace71 |
u8_t *option, int len)
|
|
Packit |
eace71 |
{
|
|
Packit |
eace71 |
struct dhcpv6_option *opt;
|
|
Packit |
eace71 |
u16_t type;
|
|
Packit |
eace71 |
int opt_len;
|
|
Packit |
eace71 |
int data_len;
|
|
Packit |
eace71 |
int i;
|
|
Packit |
eace71 |
u8_t *data;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
for (i = 0; i < len; i += opt_len + sizeof(struct dhcpv6_opt_hdr)) {
|
|
Packit |
eace71 |
opt = (struct dhcpv6_option *)((u8_t *)option + i);
|
|
Packit |
eace71 |
type = HOST_TO_NET16(opt->hdr.type);
|
|
Packit |
eace71 |
opt_len = HOST_TO_NET16(opt->hdr.length);
|
|
Packit |
eace71 |
data = &opt->type.data[0];
|
|
Packit |
eace71 |
data_len = strlen((char *)data);
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
switch (type) {
|
|
Packit |
eace71 |
case 201:
|
|
Packit |
eace71 |
/* iSCSI target 1 */
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
case 202:
|
|
Packit |
eace71 |
/* iSCSI target 2 */
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
case 203:
|
|
Packit |
eace71 |
if (data_len > ISCSI_MAX_ISCSI_NAME_LENGTH)
|
|
Packit |
eace71 |
data_len = ISCSI_MAX_ISCSI_NAME_LENGTH;
|
|
Packit |
eace71 |
data[data_len] = '\0';
|
|
Packit |
eace71 |
strcpy(context->initiatorName, (char *)data);
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
|
|
Packit |
eace71 |
default:
|
|
Packit |
eace71 |
break;
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|
|
Packit |
eace71 |
}
|