211 lines
6.1 KiB
C
211 lines
6.1 KiB
C
/******************************************************************************
|
|
* Copyright (c) 2013 IBM Corporation
|
|
* All rights reserved.
|
|
* This program and the accompanying materials
|
|
* are made available under the terms of the BSD License
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.opensource.org/licenses/bsd-license.php
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial implementation
|
|
*****************************************************************************/
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <sys/socket.h>
|
|
#include <time.h>
|
|
#include "ethernet.h"
|
|
#include "ipv6.h"
|
|
#include "udp.h"
|
|
#include "dhcpv6.h"
|
|
#include "tftp.h"
|
|
#include "dns.h"
|
|
|
|
static uint8_t tid[3];
|
|
static uint32_t dhcpv6_state = -1;
|
|
static filename_ip_t *my_fn_ip;
|
|
|
|
static struct ip6addr_list_entry all_dhcpv6_ll; /* All DHCPv6 servers address */
|
|
|
|
void
|
|
dhcpv6_generate_transaction_id(void)
|
|
{
|
|
/* As per RFC 3315 transaction IDs should be generated randomly */
|
|
tid[0] = rand();
|
|
tid[1] = rand();
|
|
tid[2] = rand();
|
|
}
|
|
|
|
static void
|
|
send_info_request(int fd)
|
|
{
|
|
uint8_t ether_packet[ETH_MTU_SIZE];
|
|
uint32_t payload_length;
|
|
struct dhcp_message_header *dhcph;
|
|
|
|
memset(ether_packet, 0, ETH_MTU_SIZE);
|
|
|
|
/* Get an IPv6 packet */
|
|
payload_length = sizeof(struct udphdr) + sizeof(struct dhcp_message_header);
|
|
fill_ip6hdr (ether_packet + sizeof(struct ethhdr),
|
|
payload_length, IPTYPE_UDP,
|
|
get_ipv6_address(), &(all_dhcpv6_ll.addr));
|
|
fill_udphdr ( ether_packet + sizeof(struct ethhdr) + sizeof(struct ip6hdr),
|
|
payload_length, DHCP_CLIENT_PORT, DHCP_SERVER_PORT);
|
|
dhcph = (struct dhcp_message_header *) (ether_packet +
|
|
sizeof(struct ethhdr) +
|
|
sizeof(struct ip6hdr) +
|
|
sizeof(struct udphdr));
|
|
|
|
/* Fill in DHCPv6 data */
|
|
dhcph->type = DHCP_INFORMATION_REQUEST;
|
|
memcpy( &(dhcph->transaction_id), &tid, 3);
|
|
dhcph->option.client_id.code = DHCPV6_OPTION_CLIENTID;
|
|
dhcph->option.client_id.length = 10;
|
|
dhcph->option.client_id.duid_type = DUID_LL;
|
|
dhcph->option.client_id.hardware_type = 1;
|
|
memcpy( &(dhcph->option.client_id.mac),
|
|
get_mac_address(), 6);
|
|
dhcph->option.el_time.code = DHCPV6_OPTION_ELAPSED_TIME;
|
|
dhcph->option.el_time.length = 2;
|
|
dhcph->option.el_time.time = 0x190; /* 4000 ms */
|
|
dhcph->option.option_request_option.code = DHCPV6_OPTION_ORO;
|
|
dhcph->option.option_request_option.length = DHCPV6_OPTREQUEST_NUMOPTS * 2;
|
|
dhcph->option.option_request_option.option_code[0] = DHCPV6_OPTION_DNS_SERVERS;
|
|
dhcph->option.option_request_option.option_code[1] = DHCPV6_OPTION_DOMAIN_LIST;
|
|
dhcph->option.option_request_option.option_code[2] = DHCPV6_OPTION_BOOT_URL;
|
|
|
|
send_ipv6(fd, ether_packet + sizeof(struct ethhdr),
|
|
sizeof(struct ip6hdr) + sizeof(struct udphdr)
|
|
+ sizeof(struct dhcp_message_header));
|
|
}
|
|
|
|
static int32_t
|
|
dhcpv6_attempt(int fd)
|
|
{
|
|
int sec;
|
|
|
|
// Send information request
|
|
send_info_request(fd);
|
|
|
|
dhcpv6_state = DHCPV6_STATE_SELECT;
|
|
|
|
// setting up a timer with a timeout of two seconds
|
|
for (sec = 0; sec < 2; sec++) {
|
|
set_timer(TICKS_SEC);
|
|
do {
|
|
receive_ether(fd);
|
|
|
|
// Wait until client will switch to Final state or Timeout occurs
|
|
switch (dhcpv6_state) {
|
|
case DHCP_STATUSCODE_SUCCESS:
|
|
return 1;
|
|
case DHCP_STATUSCODE_UNSPECFAIL: //FIXME
|
|
return 0;
|
|
}
|
|
} while (get_timer() > 0);
|
|
}
|
|
|
|
// timeout
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
dhcpv6 ( char *ret_buffer, void *fn_ip)
|
|
{
|
|
int fd;
|
|
|
|
all_dhcpv6_ll.addr.part.prefix = 0xff02000000000000ULL;
|
|
all_dhcpv6_ll.addr.part.interface_id = 0x10002ULL;
|
|
|
|
my_fn_ip = (filename_ip_t *) fn_ip;
|
|
fd = my_fn_ip->fd;
|
|
|
|
if( !dhcpv6_attempt(fd)) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dhcp6_process_options (uint8_t *option, int32_t option_length)
|
|
{
|
|
struct dhcp_boot_url *option_boot_url;
|
|
struct client_identifier *option_clientid;
|
|
struct server_identifier *option_serverid;
|
|
struct dhcp_dns *option_dns;
|
|
struct dhcp_dns_list *option_dns_list;
|
|
struct dhcp6_gen_option *option_gen;
|
|
char buffer[256];
|
|
|
|
while (option_length > 0) {
|
|
switch ((uint16_t) *(option+1)) {
|
|
case DHCPV6_OPTION_CLIENTID:
|
|
option_clientid = (struct client_identifier *) option;
|
|
option = option + option_clientid->length + 4;
|
|
option_length = option_length - option_clientid->length - 4;
|
|
break;
|
|
case DHCPV6_OPTION_SERVERID:
|
|
option_serverid = (struct server_identifier *) option;
|
|
option = option + option_serverid->length + 4;
|
|
option_length = option_length - option_serverid->length - 4;
|
|
break;
|
|
case DHCPV6_OPTION_DNS_SERVERS:
|
|
option_dns = (struct dhcp_dns *) option;
|
|
option = option + option_dns->length + 4;
|
|
option_length = option_length - option_dns->length - 4;
|
|
memcpy( &(my_fn_ip->dns_ip6),
|
|
option_dns->p_ip6,
|
|
IPV6_ADDR_LENGTH);
|
|
dns_init(0, option_dns->p_ip6, 6);
|
|
break;
|
|
case DHCPV6_OPTION_DOMAIN_LIST:
|
|
option_dns_list = (struct dhcp_dns_list *) option;
|
|
option = option + option_dns_list->length + 4;
|
|
option_length = option_length - option_dns_list->length - 4;
|
|
break;
|
|
case DHCPV6_OPTION_BOOT_URL:
|
|
option_boot_url = (struct dhcp_boot_url *) option;
|
|
option = option + option_boot_url->length + 4;
|
|
option_length = option_length - option_boot_url->length - 4;
|
|
strncpy((char *)buffer,
|
|
(const char *)option_boot_url->url,
|
|
(size_t)option_boot_url->length);
|
|
buffer[option_boot_url->length] = 0;
|
|
if (parse_tftp_args(buffer,
|
|
(char *)my_fn_ip->server_ip6.addr,
|
|
my_fn_ip->filename, my_fn_ip->fd,
|
|
option_boot_url->length) == -1)
|
|
return;
|
|
break;
|
|
default:
|
|
option_gen = (struct dhcp6_gen_option *) option;
|
|
option = option + option_gen->length + 4;
|
|
option_length = option_length - option_gen->length - 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
handle_dhcpv6(uint8_t * packet, int32_t packetsize)
|
|
{
|
|
|
|
uint8_t *first_option;
|
|
int32_t option_length;
|
|
struct dhcp_message_reply *reply;
|
|
reply = (struct dhcp_message_reply *) packet;
|
|
|
|
if (memcmp(reply->transaction_id, tid, 3))
|
|
return -1; /* Wrong transaction ID */
|
|
|
|
if (reply->type == 7)
|
|
dhcpv6_state = DHCP_STATUSCODE_SUCCESS;
|
|
|
|
first_option = packet + 4;
|
|
option_length = packet + packetsize - first_option;
|
|
dhcp6_process_options(first_option, option_length);
|
|
|
|
return 0;
|
|
}
|