diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/SLOF/lib/libnet/dhcpv6.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/SLOF/lib/libnet/dhcpv6.c')
-rw-r--r-- | roms/SLOF/lib/libnet/dhcpv6.c | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/roms/SLOF/lib/libnet/dhcpv6.c b/roms/SLOF/lib/libnet/dhcpv6.c new file mode 100644 index 000000000..e92fc0f41 --- /dev/null +++ b/roms/SLOF/lib/libnet/dhcpv6.c @@ -0,0 +1,211 @@ +/****************************************************************************** + * 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; +} |