| /****************************************************************************** |
| * 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; |
| } |