diff options
Diffstat (limited to 'lib/libnet')
-rw-r--r-- | lib/libnet/Makefile | 51 | ||||
-rw-r--r-- | lib/libnet/bootp.c | 255 | ||||
-rw-r--r-- | lib/libnet/dhcp.c | 955 | ||||
-rw-r--r-- | lib/libnet/dhcp.h | 54 | ||||
-rw-r--r-- | lib/libnet/dhcpv6.c | 212 | ||||
-rw-r--r-- | lib/libnet/dhcpv6.h | 154 | ||||
-rw-r--r-- | lib/libnet/dns.c | 526 | ||||
-rw-r--r-- | lib/libnet/dns.h | 28 | ||||
-rw-r--r-- | lib/libnet/ethernet.c | 189 | ||||
-rw-r--r-- | lib/libnet/ethernet.h | 47 | ||||
-rw-r--r-- | lib/libnet/icmpv6.c | 389 | ||||
-rw-r--r-- | lib/libnet/icmpv6.h | 135 | ||||
-rw-r--r-- | lib/libnet/ipv4.c | 898 | ||||
-rw-r--r-- | lib/libnet/ipv4.h | 97 | ||||
-rw-r--r-- | lib/libnet/ipv6.c | 775 | ||||
-rw-r--r-- | lib/libnet/ipv6.h | 188 | ||||
-rw-r--r-- | lib/libnet/ndp.c | 184 | ||||
-rw-r--r-- | lib/libnet/ndp.h | 72 | ||||
-rw-r--r-- | lib/libnet/tcp.c | 46 | ||||
-rw-r--r-- | lib/libnet/tcp.h | 27 | ||||
-rw-r--r-- | lib/libnet/tftp.c | 586 | ||||
-rw-r--r-- | lib/libnet/tftp.h | 51 | ||||
-rw-r--r-- | lib/libnet/udp.c | 153 | ||||
-rw-r--r-- | lib/libnet/udp.h | 58 |
24 files changed, 6130 insertions, 0 deletions
diff --git a/lib/libnet/Makefile b/lib/libnet/Makefile new file mode 100644 index 0000000..14c29d3 --- /dev/null +++ b/lib/libnet/Makefile @@ -0,0 +1,51 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 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 +# ****************************************************************************/ + +ifndef TOP + TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd) + export TOP +endif +include $(TOP)/make.rules + +CFLAGS += -I. -I.. -I../libc/include +CFLAGS += -I../../clients/net-snk/app/netapps -I../../clients/net-snk/include + +SRCS = ethernet.c ipv4.c udp.c tcp.c dns.c bootp.c dhcp.c tftp.c \ + ipv6.c dhcpv6.c icmpv6.c ndp.c + +OBJS = $(SRCS:%.c=%.o) + +TARGET = ../libnet.a + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep diff --git a/lib/libnet/bootp.c b/lib/libnet/bootp.c new file mode 100644 index 0000000..6d58cef --- /dev/null +++ b/lib/libnet/bootp.c @@ -0,0 +1,255 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 <stdio.h> +#include <string.h> +#include <sys/socket.h> +#include <time.h> + +#include <ethernet.h> +#include <ipv4.h> +#include <udp.h> +#include <dhcp.h> + +#define DEBUG 0 + +static char * response_buffer; + +#if DEBUG +static void +print_ip(char *ip) +{ + printf("%d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); +} +#endif + +/* IP header checksum calculation */ +static unsigned short +checksum(unsigned short *packet, int words) +{ + unsigned long checksum; + for (checksum = 0; words > 0; words--) + checksum += *packet++; + checksum = (checksum >> 16) + (checksum & 0xffff); + checksum += (checksum >> 16); + return ~checksum; +} + + +static int +send_bootp(filename_ip_t * fn_ip) +{ +#if DEBUG + int i; +#endif + unsigned int packetsize = + sizeof(struct iphdr) + sizeof(struct ethhdr) + + sizeof(struct udphdr) + sizeof(struct btphdr); + unsigned char packet[packetsize]; + struct iphdr *iph; + struct udphdr *udph; + struct btphdr *btph; + + iph = (struct iphdr *) packet; + udph = (struct udphdr *) (iph + 1); + btph = (struct btphdr *) (udph + 1); + + memset(packet, 0, packetsize); + + fill_iphdr((uint8_t *) iph, htons(packetsize - sizeof(struct ethhdr)), + IPTYPE_UDP, 0, fn_ip->server_ip); + fill_udphdr((uint8_t *) udph, + htons(sizeof(struct udphdr) + sizeof(struct btphdr)), + htons(UDPPORT_BOOTPC), htons(UDPPORT_BOOTPS)); + btph->op = 1; + btph->htype = 1; + btph->hlen = 6; + strcpy((char *) btph->file, "bla"); + memcpy(btph->chaddr, get_mac_address(), 6); + +#if DEBUG + printf("Sending packet\n"); + printf("Packet is "); + for (i = 0; i < packetsize; i++) + printf(" %02x", packet[i]); + printf(".\n"); +#endif + + send_ipv4(fn_ip->fd, packet, iph->ip_len); +#if DEBUG + printf("%d bytes transmitted over socket.\n", i); +#endif + + return 0; +} + + +static int +receive_bootp(filename_ip_t * fn_ip) +{ + int len, old_sum; + unsigned int packetsize = 2000; + unsigned char packet[packetsize]; + struct iphdr *iph; + struct udphdr *udph; + struct btphdr *btph; + +#if DEBUG + struct ethhdr *ethh; + ethh = (struct ethhdr *) packet; +#endif + + iph = (struct iphdr *) (packet + sizeof(struct ethhdr)); + udph = (struct udphdr *) (iph + 1); + btph = (struct btphdr *) (udph + 1); + + memset(packet, 0, packetsize); + + /* setting up a timer with a timeout of one second */ + set_timer(TICKS_SEC); + + do { + + /* let's receive a packet */ + len = recv(fn_ip->fd, packet, packetsize, 0); + +#if DEBUG + int j; + printf("%d bytes received, %d expected \n", len, packetsize); + if (len == 346) { + printf("Rec packet\n"); + printf("Packet is "); + for (j = 0; j < len; j++) { + if (j % 16 == 0) + printf("\n"); + printf(" %02x", packet[j]); + } + printf(".\n"); + } +#endif + if (len == 0) + continue; + + /* check if the ip checksum is correct */ + old_sum = iph->ip_sum; + iph->ip_sum = 0x00; + if (old_sum != + checksum((unsigned short *) iph, sizeof(struct iphdr) >> 1)) + /* checksum failed */ + continue; + /* is it a udp packet */ + if (iph->ip_p != IPTYPE_UDP) + continue; + /* check if the source port and destination port and the packet + * say that it is a bootp answer */ + if (udph->uh_dport != htons(UDPPORT_BOOTPC) || udph->uh_sport != htons(UDPPORT_BOOTPS)) + continue; + /* check if it is a Boot Reply */ + if (btph->op != 2) + continue; + /* Comparing our mac address with the one in the bootp reply */ + if (memcmp(get_mac_address(), btph->chaddr, ETH_ALEN)) + continue; + + if(response_buffer) + memcpy(response_buffer, btph, 1720); + + fn_ip->own_ip = btph->yiaddr; + fn_ip->server_ip = btph->siaddr; + strcpy((char *) fn_ip->filename, (char *) btph->file); + +#if DEBUG + printf("\nThese are the details of the bootp reply:\n"); + printf("Our IP address: "); + print_ip((char*) &fn_ip->own_ip); + printf("Next server IP address: "); + print_ip((char*) &fn_ip->server_ip); + printf("Boot file name: %s\n", btph->file); + printf("Packet is: %s\n", btph->file); + for (j = 0; j < len; j++) { + if (j % 16 == 0) + printf("\n"); + printf(" %02x", packet[j]); + } + printf(".\n"); + printf("fn_ip->own_mac: %02x:%02x:%02x:%02x:%02x:%02x\n", + get_mac_address()[0], get_mac_address()[1], + get_mac_address()[2], get_mac_address()[3], + get_mac_address()[4], get_mac_address()[5]); + printf("Header ethh->dest_mac: %02x:%02x:%02x:%02x:%02x:%02x\n", + ethh->dest_mac[0], ethh->dest_mac[1], ethh->dest_mac[2], + ethh->dest_mac[3], ethh->dest_mac[4], ethh->dest_mac[5]); + printf("Header ethh->src_mac: %02x:%02x:%02x:%02x:%02x:%02x\n", + ethh->src_mac[0], ethh->src_mac[1], ethh->src_mac[2], + ethh->src_mac[3], ethh->src_mac[4], ethh->src_mac[5]); + printf("Header ethh->typ: %x\n",ethh->type); + printf("Header iph->ip_hlv: %x\n",iph->ip_hlv); + printf("Header iph->ip_len: %x\n",iph->ip_len); + printf("Header iph->ip_id: %x\n",iph->ip_id); + printf("Header iph->ip_off: %x\n",iph->ip_off); + printf("Header iph->ip_ttl: %x\n",iph->ip_ttl); + printf("Header iph->ip_p: %x\n",iph->ip_p); + printf("Header iph->ip_sum: %x\n",iph->ip_sum); + printf("Header iph->ip_src: %x\n",iph->ip_src); + printf("Header iph->ip_dst: %x\n",iph->ip_dst); + + printf("Header btph->op: %x\n",btph->op); + printf("Header btph->htype: %x\n",btph->htype); + printf("Header btph->hlen: %x\n",btph->hlen); + printf("Header btph->hops: %x\n",btph->hops); + printf("Header btph->xid: %x\n",btph->xid); + printf("Header btph->secs: %x\n",btph->secs); + printf("Header btph->ciaddr: %x\n",btph->ciaddr); + printf("Header btph->yiaddr: %x\n",btph->yiaddr); + printf("Header btph->siaddr: %x\n",btph->siaddr); + printf("Header btph->giaddr: %x\n",btph->giaddr); + + printf("Header btph->chaddr: %02x:%02x:%02x:%02x:%02x:%02x:\n", + btph->chaddr[0], btph->chaddr[1], btph->chaddr[2], + btph->chaddr[3], btph->chaddr[4], btph->chaddr[5]); +#endif + return 0; + + /* only do this for the time specified during set_timer() */ + } while (get_timer() > 0); + return -1; +} + + +int +bootp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries) +{ + int i = (int) retries+1; + fn_ip->own_ip = 0; + + printf(" Requesting IP address via BOOTP: "); + + response_buffer = ret_buffer; + + do { + printf("\b\b%02d", i); + if (!i--) { + printf("\nGiving up after %d bootp requests\n", + retries+1); + return -1; + } + send_bootp(fn_ip); + /* if the timer in receive_bootp expired it will return + * -1 and we will just send another bootp request just + * in case the previous one was lost. And because we don't + * trust the network cable we keep on doing this 30 times */ + } while (receive_bootp(fn_ip) != 0); + + printf("\b\b\bdone\n"); + return 0; +} diff --git a/lib/libnet/dhcp.c b/lib/libnet/dhcp.c new file mode 100644 index 0000000..0cb4fa4 --- /dev/null +++ b/lib/libnet/dhcp.c @@ -0,0 +1,955 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + + +/******************************* ALGORITHMS ******************************/ + +/** \file dhcp.c <pre> + * **************** State-transition diagram for DHCP client ************* + * + * +---------+ Note: DHCP-server msg / DHCP-client msg + * | INIT | + * +---------+ + * | + * | - / Discover + * V + * +---------+ + * | SELECT | Timeout + * +---------+ | + * | | + * | Offer / Request | + * | | + * V V + * +---------+ NACK / - *********** + * | REQUEST | ----------------> * FAULT * + * +---------+ *********** + * | + * | ACK / - *********** + * +----------------------> * SUCCESS * + * *********** + * + * ************************************************************************ + * </pre> */ + + +/********************** DEFINITIONS & DECLARATIONS ***********************/ + +#include <dhcp.h> +#include <ethernet.h> +#include <ipv4.h> +#include <udp.h> +#include <dns.h> +#include <args.h> + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/socket.h> +#include <ctype.h> +#include <stdlib.h> + +/* DHCP Message Types */ +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNACK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +/* DHCP Option Codes */ +#define DHCP_MASK 1 +#define DHCP_ROUTER 3 +#define DHCP_DNS 6 +#define DHCP_REQUESTED_IP 50 +#define DHCP_OVERLOAD 52 +#define DHCP_MSG_TYPE 53 +#define DHCP_SERVER_ID 54 +#define DHCP_REQUEST_LIST 55 +#define DHCP_TFTP_SERVER 66 +#define DHCP_BOOTFILE 67 +#define DHCP_CLIENT_ARCH 93 +#define DHCP_ENDOPT 0xFF +#define DHCP_PADOPT 0x00 + +/* "file/sname" overload option values */ +#define DHCP_OVERLOAD_FILE 1 +#define DHCP_OVERLOAD_SNAME 2 +#define DHCP_OVERLOAD_BOTH 3 + +/* DHCP states codes */ +#define DHCP_STATE_SELECT 1 +#define DHCP_STATE_REQUEST 2 +#define DHCP_STATE_SUCCESS 3 +#define DHCP_STATE_FAULT 4 + +/* DHCP Client Architecture */ +#ifndef DHCPARCH +#define USE_DHCPARCH 0 +#define DHCPARCH 0 +#else +#define USE_DHCPARCH 1 +#endif + +static uint8_t dhcp_magic[] = {0x63, 0x82, 0x53, 0x63}; +/**< DHCP_magic is a cookie, that identifies DHCP options (see RFC 2132) */ + +/** \struct dhcp_options_t + * This structure is used to fill options in DHCP-msg during transmitting + * or to retrieve options from DHCP-msg during receiving. + * <p> + * If flag[i] == TRUE then field for i-th option retains valid value and + * information from this field may retrived (in case of receiving) or will + * be transmitted (in case of transmitting). + * + */ +typedef struct { + uint8_t flag[256]; /**< Show if corresponding opt. is valid */ + uint8_t request_list[256]; /**< o.55 If i-th member is TRUE, then i-th + option will be requested from server */ + uint32_t server_ID; /**< o.54 Identifies DHCP-server */ + uint32_t requested_IP; /**< o.50 Must be filled in DHCP-Request */ + uint32_t dns_IP; /**< o. 6 DNS IP */ + uint32_t router_IP; /**< o. 3 Router IP */ + uint32_t subnet_mask; /**< o. 1 Subnet mask */ + uint8_t msg_type; /**< o.53 DHCP-message type */ + uint8_t overload; /**< o.52 Overload sname/file fields */ + int8_t tftp_server[256]; /**< o.66 TFTP server name */ + int8_t bootfile[256]; /**< o.67 Boot file name */ + uint16_t client_arch; /**< o.93 Client architecture type */ +} dhcp_options_t; + +/** Stores state of DHCP-client (refer to State-transition diagram) */ +static uint8_t dhcp_state; + + +/***************************** PROTOTYPES ********************************/ + +static int32_t dhcp_attempt(int fd); + +static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct); + +static int32_t dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, + dhcp_options_t * opt_struct); + +static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, + uint8_t src_options[], uint32_t src_len); + +static int8_t dhcp_find_option(uint8_t options[], uint32_t len, + uint8_t op_code, uint32_t * op_offset); + +static void dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len, + uint8_t * new_option); + +static void dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, + uint32_t dst_offset, uint8_t * new_option); + +static void dhcp_send_discover(int fd); + +static void dhcp_send_request(int fd); + +/***************************** LOCAL VARIABLES ***************************/ + +static uint8_t ether_packet[ETH_MTU_SIZE]; +static uint32_t dhcp_own_ip = 0; +static uint32_t dhcp_server_ip = 0; +static uint32_t dhcp_siaddr_ip = 0; +static char dhcp_filename[256]; +static char dhcp_tftp_name[256]; +static uint32_t dhcp_xid; + +static char * response_buffer; + +/***************************** IMPLEMENTATION ****************************/ + +void dhcpv4_generate_transaction_id(void) +{ + dhcp_xid = (rand() << 16) ^ rand(); +} + +int32_t dhcpv4(char *ret_buffer, filename_ip_t *fn_ip) +{ + uint32_t dhcp_tftp_ip = 0; + int fd = fn_ip->fd; + + strcpy(dhcp_filename, ""); + strcpy(dhcp_tftp_name, ""); + + response_buffer = ret_buffer; + + if (dhcp_attempt(fd) == 0) + return -1; + + if (fn_ip->own_ip) { + dhcp_own_ip = fn_ip->own_ip; + } + if (fn_ip->server_ip) { + dhcp_siaddr_ip = fn_ip->server_ip; + } + if(fn_ip->filename[0] != 0) { + strcpy(dhcp_filename, (char *) fn_ip->filename); + } + + // TFTP SERVER + if (!strlen(dhcp_tftp_name)) { + if (!dhcp_siaddr_ip) { + // ERROR: TFTP name is not presented + return -3; + } + + // take TFTP-ip from siaddr field + dhcp_tftp_ip = dhcp_siaddr_ip; + } + else { + // TFTP server defined by its name + if (!strtoip(dhcp_tftp_name, (char *)&dhcp_tftp_ip)) { + if (!dns_get_ip(fd, dhcp_tftp_name, (uint8_t *)&dhcp_tftp_ip, 4)) { + // DNS error - can't obtain TFTP-server name + // Use TFTP-ip from siaddr field, if presented + if (dhcp_siaddr_ip) { + dhcp_tftp_ip = dhcp_siaddr_ip; + } + else { + // ERROR: Can't obtain TFTP server IP + return -4; + } + } + } + } + + // Store configuration info into filename_ip strucutre + fn_ip -> own_ip = dhcp_own_ip; + fn_ip -> server_ip = dhcp_tftp_ip; + strcpy((char *) fn_ip -> filename, dhcp_filename); + + return 0; +} + +/** + * DHCP: Tries o obtain DHCP parameters, refer to state-transition diagram + */ +static int32_t dhcp_attempt(int fd) +{ + int sec; + + // Send DISCOVER message and switch DHCP-client to SELECT state + dhcp_send_discover(fd); + + dhcp_state = DHCP_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 (dhcp_state) { + case DHCP_STATE_SUCCESS : + return 1; + case DHCP_STATE_FAULT : + return 0; + } + } while (get_timer() > 0); + } + + // timeout + return 0; +} + +/** + * DHCP: Supplements DHCP-message with options stored in structure. + * For more information about option coding see dhcp_options_t. + * + * @param opt_field Points to the "vend" field of DHCP-message + * (destination) + * @param opt_struct this structure stores info about the options which + * will be added to DHCP-message (source) + * @return TRUE - options packed; + * FALSE - error condition occurs. + * @see dhcp_options_t + */ +static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct) +{ + uint8_t * options = opt_field; + uint16_t i, sum; // used to define is any options set + + // magic + memcpy(options, dhcp_magic, 4); + options += 4; + + // fill message type + switch (opt_struct -> msg_type) { + case DHCPDISCOVER : + case DHCPREQUEST : + case DHCPDECLINE : + case DHCPINFORM : + case DHCPRELEASE : + options[0] = DHCP_MSG_TYPE; + options[1] = 1; + options[2] = opt_struct -> msg_type; + options += 3; + break; + default : + return 0; // Unsupported DHCP-message + } + + if (opt_struct -> overload) { + options[0] = DHCP_OVERLOAD; + options[1] = 0x01; + options[2] = opt_struct -> overload; + options +=3; + } + + if (opt_struct -> flag[DHCP_REQUESTED_IP]) { + options[0] = DHCP_REQUESTED_IP; + options[1] = 0x04; + * (uint32_t *) (options + 2) = htonl (opt_struct -> requested_IP); + options +=6; + } + + if (opt_struct -> flag[DHCP_SERVER_ID]) { + options[0] = DHCP_SERVER_ID; + options[1] = 0x04; + * (uint32_t *) (options + 2) = htonl (opt_struct -> server_ID); + options +=6; + } + + sum = 0; + for (i = 0; i < 256; i++) + sum += opt_struct -> request_list[i]; + + if (sum) { + options[0] = DHCP_REQUEST_LIST; + options[1] = sum; + options += 2; + for (i = 0; i < 256; i++) { + if (opt_struct -> request_list[i]) { + options[0] = i; options++; + } + } + } + + if (opt_struct -> flag[DHCP_TFTP_SERVER]) { + options[0] = DHCP_TFTP_SERVER; + options[1] = strlen((char *) opt_struct -> tftp_server) + 1; + memcpy(options + 2, opt_struct -> tftp_server, options[1]); + options += options[1] + 2; + } + + if (opt_struct -> flag[DHCP_BOOTFILE]) { + options[0] = DHCP_BOOTFILE; + options[1] = strlen((char *) opt_struct -> bootfile) + 1; + memcpy(options + 2, opt_struct -> bootfile, options[1]); + options += options[1] + 2; + } + + if (opt_struct -> flag[DHCP_CLIENT_ARCH]) { + options[0] = DHCP_CLIENT_ARCH; + options[1] = 2; + options[2] = (DHCPARCH >> 8); + options[3] = DHCPARCH & 0xff; + options += 4; + } + + // end options + options[0] = 0xFF; + options++; + + return 1; +} + +/** + * DHCP: Extracts encoded options from DHCP-message into the structure. + * For more information about option coding see dhcp_options_t. + * + * @param opt_field Points to the "options" field of DHCP-message + * (source). + * @param opt_len Length of "options" field. + * @param opt_struct this structure stores info about the options which + * was extracted from DHCP-message (destination). + * @return TRUE - options extracted; + * FALSE - error condition occurs. + * @see dhcp_options_t + */ +static int32_t dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, + dhcp_options_t * opt_struct) +{ + uint32_t offset = 0; + + memset(opt_struct, 0, sizeof(dhcp_options_t)); + + // magic + if (memcmp(opt_field, dhcp_magic, 4)) { + return 0; + } + + offset += 4; + while (offset < opt_len) { + opt_struct -> flag[opt_field[offset]] = 1; + switch(opt_field[offset]) { + case DHCP_OVERLOAD : + opt_struct -> overload = opt_field[offset + 2]; + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_REQUESTED_IP : + opt_struct -> requested_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_MASK : + opt_struct -> flag[DHCP_MASK] = 1; + opt_struct -> subnet_mask = htonl(* (uint32_t *) (opt_field + offset + 2)); + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_DNS : + opt_struct -> flag[DHCP_DNS] = 1; + opt_struct -> dns_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_ROUTER : + opt_struct -> flag[DHCP_ROUTER] = 1; + opt_struct -> router_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_MSG_TYPE : + if ((opt_field[offset + 2] > 0) && (opt_field[offset + 2] < 9)) + opt_struct -> msg_type = opt_field[offset + 2]; + else + return 0; + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_SERVER_ID : + opt_struct -> server_ID = htonl(* (uint32_t *) (opt_field + offset + 2)); + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_TFTP_SERVER : + memcpy(opt_struct -> tftp_server, opt_field + offset + 2, opt_field[offset + 1]); + (opt_struct -> tftp_server)[opt_field[offset + 1]] = 0; + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_BOOTFILE : + memcpy(opt_struct -> bootfile, opt_field + offset + 2, opt_field[offset + 1]); + (opt_struct -> bootfile)[opt_field[offset + 1]] = 0; + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_CLIENT_ARCH : + opt_struct -> client_arch = ((opt_field[offset + 2] << 8) & 0xFF00) | (opt_field[offset + 3] & 0xFF); + offset += 4; + break; + + case DHCP_PADOPT : + offset++; + break; + + case DHCP_ENDOPT : // End of options + return 1; + + default : + offset += 2 + opt_field[offset + 1]; // Unsupported opt. - do nothing + } + } + if (offset == opt_len) + return 1; // options finished without 0xFF + + return 0; +} + +/** + * DHCP: Appends information from source "options" into dest "options". + * This function is used to support "file/sname" overloading. + * + * @param dst_options destanation "options" field + * @param dst_len size of dst_options (modified by this function) + * @param src_options source "options" field + * @param src_len size of src_options + * @return TRUE - options merged; + * FALSE - error condition occurs. + */ +static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, + uint8_t src_options[], uint32_t src_len) +{ + uint32_t dst_offset, src_offset = 0; + + // remove ENDOPT if presented + if (dhcp_find_option(dst_options, * dst_len, DHCP_ENDOPT, &dst_offset)) + * dst_len = dst_offset; + + while (src_offset < src_len) { + switch(src_options[src_offset]) { + case DHCP_PADOPT: + src_offset++; + break; + case DHCP_ENDOPT: + return 1; + default: + if (dhcp_find_option(dst_options, * dst_len, + src_options[src_offset], + &dst_offset)) { + dhcp_combine_option(dst_options, dst_len, + dst_offset, + (uint8_t *) src_options + + src_offset); + } + else { + dhcp_append_option(dst_options, dst_len, src_options + src_offset); + } + src_offset += 2 + src_options[src_offset + 1]; + } + } + + if (src_offset == src_len) + return 1; + return 0; +} + +/** + * DHCP: Finds given occurrence of the option with the given code (op_code) + * in "options" field of DHCP-message. + * + * @param options "options" field of DHCP-message + * @param len length of the "options" field + * @param op_code code of the option to find + * @param op_offset SUCCESS - offset to an option occurrence; + * FAULT - offset is set to zero. + * @return TRUE - option was find; + * FALSE - option wasn't find. + */ +static int8_t dhcp_find_option(uint8_t options[], uint32_t len, + uint8_t op_code, uint32_t * op_offset) +{ + uint32_t srch_offset = 0; + * op_offset = 0; + + while (srch_offset < len) { + if (options[srch_offset] == op_code) { + * op_offset = srch_offset; + return 1; + } + if (options[srch_offset] == DHCP_ENDOPT) + return 0; + + if (options[srch_offset] == DHCP_PADOPT) + srch_offset++; + else + srch_offset += 2 + options[srch_offset + 1]; + } + return 0; +} + +/** + * DHCP: Appends new option from one list (src) into the tail + * of another option list (dst) + * + * @param dst_options "options" field of DHCP-message + * @param dst_len length of the "options" field (modified) + * @param new_option points to an option in another list (src) + */ +static void dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len, + uint8_t * new_option) +{ + memcpy(dst_options + ( * dst_len), new_option, 2 + (* (new_option + 1))); + * dst_len += 2 + *(new_option + 1); +} + +/** + * DHCP: This function is used when options with the same code are + * presented in both merged lists. In this case information + * about the option from one list (src) is combined (complemented) + * with information about the option in another list (dst). + * + * @param dst_options "options" field of DHCP-message + * @param dst_len length of the "options" field (modified) + * @param dst_offset offset of the option from beginning of the list + * @param new_option points to an option in another list (src) + */ +static void dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, + uint32_t dst_offset, uint8_t * new_option) +{ + uint8_t tmp_buffer[1024]; // use to provide safe memcpy + uint32_t tail_len; + + // move all subsequent options (allocate size for additional info) + tail_len = (* dst_len) - dst_offset - 2 - dst_options[dst_offset + 1]; + + memcpy(tmp_buffer, dst_options + (* dst_len) - tail_len, tail_len); + memcpy(dst_options + (* dst_len) - tail_len + (* (new_option + 1)), + tmp_buffer, tail_len); + + // add new_content to option + memcpy(dst_options + (* dst_len) - tail_len, new_option + 2, + * (new_option + 1)); + dst_options[dst_offset + 1] += * (new_option + 1); + + // correct dst_len + * dst_len += * (new_option + 1); +} + +/** + * DHCP: Sends DHCP-Discover message. Looks for DHCP servers. + */ +static void dhcp_send_discover(int fd) +{ + uint32_t packetsize = sizeof(struct iphdr) + + sizeof(struct udphdr) + sizeof(struct btphdr); + struct btphdr *btph; + dhcp_options_t opt; + + memset(ether_packet, 0, packetsize); + + btph = (struct btphdr *) (ðer_packet[ + sizeof(struct iphdr) + sizeof(struct udphdr)]); + + btph -> op = 1; + btph -> htype = 1; + btph -> hlen = 6; + btph -> xid = dhcp_xid; + memcpy(btph -> chaddr, get_mac_address(), 6); + + memset(&opt, 0, sizeof(dhcp_options_t)); + + opt.msg_type = DHCPDISCOVER; + + opt.request_list[DHCP_MASK] = 1; + opt.request_list[DHCP_DNS] = 1; + opt.request_list[DHCP_ROUTER] = 1; + opt.request_list[DHCP_TFTP_SERVER] = 1; + opt.request_list[DHCP_BOOTFILE] = 1; + opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH; + + dhcp_encode_options(btph -> vend, &opt); + + fill_udphdr(ðer_packet[sizeof(struct iphdr)], + sizeof(struct btphdr) + sizeof(struct udphdr), + UDPPORT_BOOTPC, UDPPORT_BOOTPS); + fill_iphdr(ether_packet, sizeof(struct btphdr) + + sizeof(struct udphdr) + sizeof(struct iphdr), + IPTYPE_UDP, dhcp_own_ip, 0xFFFFFFFF); + + send_ipv4(fd, ether_packet, packetsize); +} + +/** + * DHCP: Sends DHCP-Request message. Asks for acknowledgment to occupy IP. + */ +static void dhcp_send_request(int fd) +{ + uint32_t packetsize = sizeof(struct iphdr) + + sizeof(struct udphdr) + sizeof(struct btphdr); + struct btphdr *btph; + dhcp_options_t opt; + + memset(ether_packet, 0, packetsize); + + btph = (struct btphdr *) (ðer_packet[ + sizeof(struct iphdr) + sizeof(struct udphdr)]); + + btph -> op = 1; + btph -> htype = 1; + btph -> hlen = 6; + btph -> xid = dhcp_xid; + memcpy(btph -> chaddr, get_mac_address(), 6); + + memset(&opt, 0, sizeof(dhcp_options_t)); + + opt.msg_type = DHCPREQUEST; + memcpy(&(opt.requested_IP), &dhcp_own_ip, 4); + opt.flag[DHCP_REQUESTED_IP] = 1; + memcpy(&(opt.server_ID), &dhcp_server_ip, 4); + opt.flag[DHCP_SERVER_ID] = 1; + + opt.request_list[DHCP_MASK] = 1; + opt.request_list[DHCP_DNS] = 1; + opt.request_list[DHCP_ROUTER] = 1; + opt.request_list[DHCP_TFTP_SERVER] = 1; + opt.request_list[DHCP_BOOTFILE] = 1; + opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH; + opt.flag[DHCP_CLIENT_ARCH] = USE_DHCPARCH; + + dhcp_encode_options(btph -> vend, &opt); + + fill_udphdr(ðer_packet[sizeof(struct iphdr)], + sizeof(struct btphdr) + sizeof(struct udphdr), + UDPPORT_BOOTPC, UDPPORT_BOOTPS); + fill_iphdr(ether_packet, sizeof(struct btphdr) + + sizeof(struct udphdr) + sizeof(struct iphdr), + IPTYPE_UDP, 0, 0xFFFFFFFF); + + send_ipv4(fd, ether_packet, packetsize); +} + + +/** + * DHCP: Sends DHCP-Release message. Releases occupied IP. + */ +void dhcp_send_release(int fd) +{ + uint32_t packetsize = sizeof(struct iphdr) + + sizeof(struct udphdr) + sizeof(struct btphdr); + struct btphdr *btph; + dhcp_options_t opt; + + btph = (struct btphdr *) (ðer_packet[ + sizeof(struct iphdr) + sizeof(struct udphdr)]); + + memset(ether_packet, 0, packetsize); + + btph -> op = 1; + btph -> htype = 1; + btph -> hlen = 6; + btph -> xid = dhcp_xid; + strcpy((char *) btph -> file, ""); + memcpy(btph -> chaddr, get_mac_address(), 6); + btph -> ciaddr = htonl(dhcp_own_ip); + + memset(&opt, 0, sizeof(dhcp_options_t)); + + opt.msg_type = DHCPRELEASE; + opt.server_ID = dhcp_server_ip; + opt.flag[DHCP_SERVER_ID] = 1; + + dhcp_encode_options(btph -> vend, &opt); + + fill_udphdr(ðer_packet[sizeof(struct iphdr)], + sizeof(struct btphdr) + sizeof(struct udphdr), + UDPPORT_BOOTPC, UDPPORT_BOOTPS); + fill_iphdr(ether_packet, sizeof(struct btphdr) + + sizeof(struct udphdr) + sizeof(struct iphdr), IPTYPE_UDP, + dhcp_own_ip, dhcp_server_ip); + + send_ipv4(fd, ether_packet, packetsize); +} + +/** + * DHCP: Handles DHCP-messages according to Receive-handle diagram. + * Changes the state of DHCP-client. + * + * @param fd socket descriptor + * @param packet BootP/DHCP-packet to be handled + * @param packetsize length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see receive_ether + * @see btphdr + */ + +int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) +{ + struct btphdr * btph; + struct iphdr * iph; + dhcp_options_t opt; + + memset(&opt, 0, sizeof(dhcp_options_t)); + btph = (struct btphdr *) packet; + iph = (struct iphdr *) packet - sizeof(struct udphdr) - + sizeof(struct iphdr); + + if (btph->op != 2) + return -1; /* It is not a Bootp/DHCP reply */ + if (btph->xid != dhcp_xid) + return -1; /* The transaction ID does not match */ + + if (memcmp(btph -> vend, dhcp_magic, 4)) { + // It is BootP - RFC 951 + dhcp_own_ip = htonl(btph -> yiaddr); + dhcp_siaddr_ip = htonl(btph -> siaddr); + dhcp_server_ip = htonl(iph -> ip_src); + + if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) { + strncpy((char *) dhcp_tftp_name, (char *) btph -> sname, + sizeof(btph -> sname)); + dhcp_tftp_name[sizeof(btph -> sname)] = 0; + } + + if (strlen((char *) btph -> file)) { + strncpy((char *) dhcp_filename, (char *) btph -> file, sizeof(btph -> file)); + dhcp_filename[sizeof(btph -> file)] = 0; + } + + dhcp_state = DHCP_STATE_SUCCESS; + return 0; + } + + + // decode options + if (!dhcp_decode_options(btph -> vend, packetsize - + sizeof(struct btphdr) + sizeof(btph -> vend), + &opt)) { + return -1; // can't decode options + } + + if (opt.overload) { + int16_t decode_res = 0; + uint8_t options[1024]; // buffer for merged options + uint32_t opt_len; + + // move 1-st part of options from vend field into buffer + opt_len = packetsize - sizeof(struct btphdr) + + sizeof(btph -> vend) - 4; + memcpy(options, btph -> vend, opt_len + 4); + + // add other parts + switch (opt.overload) { + case DHCP_OVERLOAD_FILE: + decode_res = dhcp_merge_options(options + 4, &opt_len, + btph -> file, + sizeof(btph -> file)); + break; + case DHCP_OVERLOAD_SNAME: + decode_res = dhcp_merge_options(options + 4, &opt_len, + btph -> sname, + sizeof(btph -> sname)); + break; + case DHCP_OVERLOAD_BOTH: + decode_res = dhcp_merge_options(options + 4, &opt_len, + btph -> file, + sizeof(btph -> file)); + if (!decode_res) + break; + decode_res = dhcp_merge_options(options + 4, &opt_len, + btph -> sname, + sizeof(btph -> sname)); + break; + } + + if (!decode_res) + return -1; // bad options in sname/file fields + + // decode merged options + if (!dhcp_decode_options(options, opt_len + 4, &opt)) { + return -1; // can't decode options + } + } + + if (!opt.msg_type) { + // It is BootP with Extensions - RFC 1497 + // retrieve conf. settings from BootP - reply + dhcp_own_ip = htonl(btph -> yiaddr); + dhcp_siaddr_ip = htonl(btph -> siaddr); + if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) { + strncpy((char *) dhcp_tftp_name, (char *) btph -> sname, sizeof(btph -> sname)); + dhcp_tftp_name[sizeof(btph -> sname)] = 0; + } + + if (strlen((char *) btph -> file)) { + strncpy((char *) dhcp_filename, (char *) btph -> file, sizeof(btph -> file)); + dhcp_filename[sizeof(btph -> file)] = 0; + } + + // retrieve DHCP-server IP from IP-header + dhcp_server_ip = iph -> htonl(ip_src); + + dhcp_state = DHCP_STATE_SUCCESS; + } + else { + // It is DHCP - RFC 2131 & RFC 2132 + // opt contains parameters from server + switch (dhcp_state) { + case DHCP_STATE_SELECT : + if (opt.msg_type == DHCPOFFER) { + dhcp_own_ip = htonl(btph -> yiaddr); + dhcp_server_ip = opt.server_ID; + dhcp_send_request(fd); + dhcp_state = DHCP_STATE_REQUEST; + } + return 0; + case DHCP_STATE_REQUEST : + switch (opt.msg_type) { + case DHCPNACK : + dhcp_own_ip = 0; + dhcp_server_ip = 0; + dhcp_state = DHCP_STATE_FAULT; + break; + case DHCPACK : + dhcp_own_ip = htonl(btph -> yiaddr); + dhcp_server_ip = opt.server_ID; + dhcp_siaddr_ip = htonl(btph -> siaddr); + if (opt.flag[DHCP_TFTP_SERVER]) { + strcpy((char *) dhcp_tftp_name, (char *) opt.tftp_server); + } + else { + strcpy((char *) dhcp_tftp_name, ""); + if ((opt.overload != DHCP_OVERLOAD_SNAME && + opt.overload != DHCP_OVERLOAD_BOTH) && + !dhcp_siaddr_ip) { + strncpy((char *) dhcp_tftp_name, + (char *) btph->sname, + sizeof(btph -> sname)); + dhcp_tftp_name[sizeof(btph->sname)] = 0; + } + } + + if (opt.flag[DHCP_BOOTFILE]) { + strcpy((char *) dhcp_filename, (char *) opt.bootfile); + } + else { + strcpy((char *) dhcp_filename, ""); + if (opt.overload != DHCP_OVERLOAD_FILE && + opt.overload != DHCP_OVERLOAD_BOTH && + strlen((char *) btph -> file)) { + strncpy((char *) dhcp_filename, + (char *) btph->file, + sizeof(btph->file)); + dhcp_filename[sizeof(btph -> file)] = 0; + } + } + + dhcp_state = DHCP_STATE_SUCCESS; + break; + default: + break; // Unused DHCP-message - do nothing + } + break; + default : + return -1; // Illegal DHCP-client state + } + } + + if (dhcp_state == DHCP_STATE_SUCCESS) { + + // initialize network entity with real own_ip + // to be able to answer for foreign requests + set_ipv4_address(dhcp_own_ip); + + if(response_buffer) { + if(packetsize <= 1720) + memcpy(response_buffer, packet, packetsize); + else + memcpy(response_buffer, packet, 1720); + } + + /* Subnet mask */ + if (opt.flag[DHCP_MASK]) { + /* Router */ + if (opt.flag[DHCP_ROUTER]) { + set_ipv4_router(opt.router_IP); + set_ipv4_netmask(opt.subnet_mask); + } + } + + /* DNS-server */ + if (opt.flag[DHCP_DNS]) { + dns_init(opt.dns_IP, 0, 4); + } + } + + return 0; +} diff --git a/lib/libnet/dhcp.h b/lib/libnet/dhcp.h new file mode 100644 index 0000000..645fdd1 --- /dev/null +++ b/lib/libnet/dhcp.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + +#ifndef _DHCP_H_ +#define _DHCP_H_ + +#include <stdint.h> + +#ifdef USE_MTFTP +#include <mtftp.h> +#else +#include <tftp.h> +#endif + +/** \struct btphdr + * A header for BootP/DHCP-messages. + * For more information see RFC 951 / RFC 2131. + */ +struct btphdr { + uint8_t op; /**< Identifies is it request (1) or reply (2) */ + uint8_t htype; /**< HW address type (ethernet usually) */ + uint8_t hlen; /**< HW address length */ + uint8_t hops; /**< This info used by relay agents (not used) */ + uint32_t xid; /**< This ID is used to match queries and replies */ + uint16_t secs; /**< Unused */ + uint16_t unused; /**< Unused */ + uint32_t ciaddr; /**< Client IP address (if client knows it) */ + uint32_t yiaddr; /**< "Your" (client) IP address */ + uint32_t siaddr; /**< Next server IP address (TFTP server IP) */ + uint32_t giaddr; /**< Gateway IP address (used by relay agents) */ + uint8_t chaddr[16]; /**< Client HW address */ + uint8_t sname[64]; /**< Server host name (TFTP server name) */ + uint8_t file[128]; /**< Boot file name */ + uint8_t vend[64]; /**< Optional parameters field (DHCP-options) */ +}; + +void dhcpv4_generate_transaction_id(void); +int bootp(char *ret_buffer, filename_ip_t *, unsigned int); +int dhcpv4(char *ret_buffer, filename_ip_t *); +void dhcp_send_release(int fd); + +/* Handles DHCP-packets, which are detected by receive_ether. */ +extern int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize); + +#endif diff --git a/lib/libnet/dhcpv6.c b/lib/libnet/dhcpv6.c new file mode 100644 index 0000000..491d540 --- /dev/null +++ b/lib/libnet/dhcpv6.c @@ -0,0 +1,212 @@ +/****************************************************************************** + * 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, + (char *)my_fn_ip->filename, + (int)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; +} diff --git a/lib/libnet/dhcpv6.h b/lib/libnet/dhcpv6.h new file mode 100644 index 0000000..02747a0 --- /dev/null +++ b/lib/libnet/dhcpv6.h @@ -0,0 +1,154 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#ifndef _DHCPV6_H_ +#define _DHCPV6_H_ + +#include <stdint.h> +#include "ethernet.h" + +#define DHCPV6_STATELESS 0 +#define DHCPV6_STATEFUL 1 + +/* DHCP port numbers */ +#define DHCP_CLIENT_PORT 546 +#define DHCP_SERVER_PORT 547 + +/* DHCPv6 message types */ +#define DHCP_SOLICIT 1 +#define DHCP_ADVERTISE 2 +#define DHCP_REQUEST 3 +#define DHCP_CONFIRM 4 +#define DHCP_RENEW 5 +#define DHCP_REBIND 6 +#define DHCP_REPLY 7 +#define DHCP_RELEASE 8 +#define DHCP_DECLINE 9 +#define DHCP_RECONFIGURE 10 +#define DHCP_INFORMATION_REQUEST 11 +#define RELAY_FORW 12 +#define RELAY_REPL 13 + +/* DHCPv6 option types */ +#define DHCPV6_OPTION_CLIENTID 0x0001 +#define DHCPV6_OPTION_SERVERID 0x0002 +#define DHCPV6_OPTION_IA_NA 3 +#define DHCPV6_OPTION_IA_TA 4 +#define DHCPV6_OPTION_IAADDR 5 +#define DHCPV6_OPTION_ORO 6 +#define DHCPV6_OPTION_PREFEREN 7 +#define DHCPV6_OPTION_ELAPSED_TIME 8 +#define DHCPV6_OPTION_RELAY_MS 9 +#define DHCPV6_OPTION_AUTH 11 +#define DHCPV6_OPTION_UNICAST 12 +#define DHCPV6_OPTION_STATUS_C 13 +#define DHCPV6_OPTION_RAPID_CO 14 +#define DHCPV6_OPTION_USER_CLA 15 +#define DHCPV6_OPTION_VENDOR_C 16 +#define DHCPV6_OPTION_VENDOR_O 17 +#define DHCPV6_OPTION_INTERFAC 18 +#define DHCPV6_OPTION_RECONF_M 19 +#define DHCPV6_OPTION_RECONF_A 20 +#define DHCPV6_OPTION_DNS_SERVERS 23 +#define DHCPV6_OPTION_DOMAIN_LIST 24 +#define DHCPV6_OPTION_BOOT_URL 59 + +/* DHCPv6 status codes */ +#define DHCP_STATUSCODE_SUCCESS 0 +#define DHCP_STATUSCODE_UNSPECFAIL 1 +#define DHCP_STATUSCODE_NOADDRAVAIL 2 +#define DHCP_STATUSCODE_NOBINDING 3 +#define DHCP_STATUSCODE_NOTONLINK 4 +#define DHCP_STATUSCODE_USEMULTICAST 5 +#define DHCPV6_STATE_SELECT 6 + +/* DUID types */ +#define DUID_LLT 1 /* DUID based on Link-layer Address Plus Time */ +#define DUID_EN 2 /* DUID based on Assigned by Vendor Based on Enterprise Number */ +#define DUID_LL 3 /* DUID based on Link-layer Address */ + +/* Prototypes */ +void dhcpv6_generate_transaction_id(void); +int32_t dhcpv6 ( char *ret_buffer, void *fn_ip); +uint32_t handle_dhcpv6(uint8_t * , int32_t); + +struct dhcp6_gen_option { + uint16_t code; + uint16_t length; +}; + +struct client_identifier { + uint16_t code; + uint16_t length; + uint16_t duid_type; + uint16_t hardware_type; + uint8_t mac[6]; +}; + +struct server_identifier { + uint16_t code; + uint16_t length; + uint16_t duid_type; + uint16_t hardware_type; + uint32_t time; + uint8_t mac[6]; +}; + +#define DHCPV6_OPTREQUEST_NUMOPTS 3 + +struct dhcp_info_request { + struct client_identifier client_id; + struct elapsed_time { + uint16_t code; + uint16_t length; + uint16_t time; + } el_time; + struct option_request { + uint16_t code; + uint16_t length; + uint16_t option_code[DHCPV6_OPTREQUEST_NUMOPTS]; + } option_request_option; +}; + +struct dhcp_message_header { + uint8_t type; /* Message type */ + uint8_t transaction_id[3]; /* Transaction id */ + struct dhcp_info_request option; +}; + +struct dhcp_dns { + uint16_t code; + uint16_t length; + uint8_t p_ip6[16]; + uint8_t s_ip6[16]; +}__attribute((packed)); + +struct dhcp_dns_list { + uint16_t code; + uint16_t length; + uint8_t domain[256]; +}__attribute((packed)); + +struct dhcp_boot_url { + uint16_t type; + uint16_t length; + uint8_t url[256]; +}; + +struct dhcp_message_reply { + uint8_t type; /* Message type */ + uint8_t transaction_id[3]; /* Transaction id */ + struct client_identifier client_id; + struct server_identifier server_id; +}; + +#endif diff --git a/lib/libnet/dns.c b/lib/libnet/dns.c new file mode 100644 index 0000000..a7313a9 --- /dev/null +++ b/lib/libnet/dns.c @@ -0,0 +1,526 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + +/********************** DEFINITIONS & DECLARATIONS ***********************/ + +#include <dns.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/socket.h> + +#include <ethernet.h> +#include <ipv4.h> +#include <ipv6.h> +#include <udp.h> + +#define DNS_FLAG_MSGTYPE 0xF800 /**< Message type mask (opcode) */ +#define DNS_FLAG_SQUERY 0x0000 /**< Standard query type */ +#define DNS_FLAG_SRESPONSE 0x8000 /**< Standard response type */ +#define DNS_FLAG_RD 0x0100 /**< Recursion desired flag */ +#define DNS_FLAG_RCODE 0x000F /**< Response code mask + (stores err.cond.) code */ +#define DNS_RCODE_NERROR 0 /**< "No errors" code */ + +#define DNS_QTYPE_A 1 /**< A 32-bit IP record type */ +#define DNS_QTYPE_AAAA 0x1c /**< 128-bit IPv6 record type */ +#define DNS_QTYPE_CNAME 5 /**< Canonical name record type */ + +#define DNS_QCLASS_IN 1 /**< Query class for internet msgs */ + +/** \struct dnshdr + * A header for DNS-messages (see RFC 1035, paragraph 4.1.1). + * <p> + * DNS-message consist of DNS-header and 4 optional sections, + * arranged in the following order:<ul> + * <li> DNS-header + * <li> question section + * <li> answer section + * <li> authority section + * <li> additional section + * </ul> + */ +struct dnshdr { + uint16_t id; /**< an identifier used to match up replies */ + uint16_t flags; /**< contains op_code, err_code, etc. */ + uint16_t qdcount; /**< specifies the number of entries in the + question section */ + uint16_t ancount; /**< specifies the number of entries in the + answer section */ + uint16_t nscount; /**< specifies the number of entries in the + authority section */ + uint16_t arcount; /**< specifies the number of entries in the + additional section */ +}; + + +/***************************** PROTOTYPES ********************************/ + +static void +dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version); + +static void +fill_dnshdr(uint8_t * packet, int8_t * domain_name, uint8_t ip_version); + +static uint8_t * +dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name); + +static int8_t +urltohost(char * url, char * host_name); + +static int8_t +hosttodomain(char * host_name, char * domain_name); + +/**************************** LOCAL VARIABLES ****************************/ + +static uint8_t ether_packet[ETH_MTU_SIZE]; +static int32_t dns_server_ip = 0; +static uint8_t dns_server_ipv6[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static int32_t dns_result_ip = 0; +static uint8_t dns_result_ipv6[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static int8_t dns_error = 0; /**< Stores error code or 0 */ +static int8_t dns_domain_name[0x100]; /**< Raw domain name */ +static int8_t dns_domain_cname[0x100]; /**< Canonical domain name */ + +/**************************** IMPLEMENTATION *****************************/ + +/** + * DNS: Initialize the environment for DNS client. + * To perfrom DNS-queries use the function dns_get_ip. + * + * @param device_socket a socket number used to send and receive packets + * @param server_ip DNS-server IPv4 address (e.g. 127.0.0.1) + * @return TRUE in case of successful initialization; + * FALSE in case of fault (e.g. can't obtain MAC). + * @see dns_get_ip + */ +int8_t +dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_version) +{ + if(ip_version == 6) + memcpy(dns_server_ipv6, _dns_server_ipv6, 16); + else + dns_server_ip = _dns_server_ip; + return 0; +} + +/** + * DNS: For given URL retrieves IPv4/IPv6 from DNS-server. + * <p> + * URL can be given in one of the following form: <ul> + * <li> scheme with full path with (without) user and password + * <br>(e.g. "http://user:pass@www.host.org/url-path"); + * <li> host name with url-path + * <br>(e.g. "www.host.org/url-path"); + * <li> nothing but host name + * <br>(e.g. "www.host.org"); + * </ul> + * + * @param fd socket descriptor + * @param url the URL to be resolved + * @param domain_ip In case of SUCCESS stores extracted IP. + * In case of FAULT stores zeros (0.0.0.0). + * @return TRUE - IP successfuly retrieved; + * FALSE - error condition occurs. + */ +int8_t +dns_get_ip(int fd, char* url, uint8_t * domain_ip, uint8_t ip_version) +{ + /* this counter is used so that we abort after 30 DNS request */ + int32_t i; + /* this buffer stores host name retrieved from url */ + static int8_t host_name[0x100]; + + (* domain_ip) = 0; + + // Retrieve host name from URL + if (!urltohost(url, (char *) host_name)) { + printf("\nERROR:\t\t\tBad URL!\n"); + return 0; + } + + // Reformat host name into a series of labels + if (!hosttodomain((char *) host_name, (char *) dns_domain_name)) { + printf("\nERROR:\t\t\tBad host name!\n"); + return 0; + } + + // Check if DNS server is presented and accessible + if (dns_server_ip == 0) { + printf("\nERROR:\t\t\tCan't resolve domain name " + "(DNS server is not presented)!\n"); + return 0; + } + + // Use DNS-server to obtain IP + if (ip_version == 6) + memset(dns_result_ipv6, 0, 16); + else + dns_result_ip = 0; + dns_error = 0; + strcpy((char *) dns_domain_cname, ""); + + for(i = 0; i < 30; ++i) { + // Use canonical name in case we obtained it + if (strlen((char *) dns_domain_cname)) + dns_send_query(fd, dns_domain_cname, ip_version); + else + dns_send_query(fd, dns_domain_name, ip_version); + + // setting up a timer with a timeout of one seconds + set_timer(TICKS_SEC); + do { + receive_ether(fd); + if (dns_error) + return 0; // FALSE - error + if ((dns_result_ip != 0) && (ip_version == 4)) { + memcpy(domain_ip, &dns_result_ip, 4); + return 1; // TRUE - success (domain IP retrieved) + } + else if ((dns_result_ipv6[0] != 0) && (ip_version == 6)) { + memcpy(domain_ip, dns_result_ipv6, 16); + return 1; // TRUE - success (domain IP retrieved) + } + } while (get_timer() > 0); + } + + printf("\nGiving up after %d DNS requests\n", i); + return 0; // FALSE - domain name wasn't retrieved +} + +/** + * DNS: Handles DNS-messages according to Receive-handle diagram. + * Sets dns_result_ip for given dns_domain_name (see dns_get_ip) + * or signals error condition occurs during DNS-resolving process + * by setting dns_error flag. + * + * @param packet DNS-packet to be handled + * @param packetsize length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see dns_get_ip + * @see receive_ether + * @see dnshdr + */ +int32_t +handle_dns(uint8_t * packet, int32_t packetsize) +{ + struct dnshdr * dnsh = (struct dnshdr *) packet; + uint8_t * resp_section = packet + sizeof(struct dnshdr); + /* This string stores domain name from DNS-packets */ + static int8_t handle_domain_name[0x100]; + int i; + + // verify ID - is it response for our query? + if (dnsh -> id != htons(0x1234)) + return 0; + + // Is it DNS response? + if ((dnsh -> flags & htons(DNS_FLAG_MSGTYPE)) != htons(DNS_FLAG_SRESPONSE)) + return 0; + + // Is error condition occurs? (check error field in incoming packet) + if ((dnsh -> flags & htons(DNS_FLAG_RCODE)) != DNS_RCODE_NERROR) { + dns_error = 1; + return 0; + } + + /* Pass all (qdcount) records in question section */ + + for (i = 0; i < htons(dnsh -> qdcount); i++) { + // pass QNAME + resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section, + handle_domain_name); + if (resp_section == NULL) { + return -1; // incorrect domain name (bad packet) + } + // pass QTYPE & QCLASS + resp_section += 4; + } + + /* Handle all (ancount) records in answer section */ + + for (i = 0; i < htons(dnsh -> ancount); i++) { + // retrieve domain name from the packet + resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section, + handle_domain_name); + + if (resp_section == NULL) { + return -1; // incorrect domain name (bad packet) + } + + // Check the class of the query (should be IN for Internet) + if (* (uint16_t *) (resp_section + 2) == htons(DNS_QCLASS_IN)) { + // check if retrieved name fit raw or canonical domain name + if (!strcmp((char *) handle_domain_name, (char *) dns_domain_name) || + !strcmp((char *) handle_domain_name, (char *) dns_domain_cname)) { + switch (htons(* (uint16_t *) resp_section)) { + + case DNS_QTYPE_A : + // rdata contains IP + dns_result_ip = htonl(* (uint32_t *) (resp_section + 10)); + return 0; // IP successfully obtained + + case DNS_QTYPE_CNAME : + // rdata contains canonical name, store it for further requests + if (dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section + 10, + dns_domain_cname) == NULL) { + // incorrect domain name (bad packet) + return -1; + } + break; + case DNS_QTYPE_AAAA : + memcpy(dns_result_ipv6, (resp_section + 10), 16); + return 0; // IP successfully obtained + } + } + // continue with next record in answer section + resp_section += htons(* (uint16_t *) (resp_section + 8)) + 10; + } + } + return 0; // Packet successfully handled but IP wasn't obtained +} + +/** + * DNS: Sends a standard DNS-query (read request package) to a DNS-server. + * DNS-server respones with host IP or signals some error condition. + * Responses from the server are handled by handle_dns function. + * + * @param fd socket descriptor + * @param domain_name the domain name given as series of labels preceded + * with length(label) and terminated with 0 + * <br>(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0") + * @see handle_dns + */ +static void +dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version) +{ + int qry_len = strlen((char *) domain_name) + 5; + int iphdr_len = (ip_version == 4) ? sizeof(struct iphdr) : sizeof(struct ip6hdr); + ip6_addr_t server_ipv6; + + uint32_t packetsize = iphdr_len + + sizeof(struct udphdr) + sizeof(struct dnshdr) + + qry_len; + + memset(ether_packet, 0, packetsize); + fill_dnshdr(ðer_packet[ + iphdr_len + sizeof(struct udphdr)], + domain_name, + ip_version); + fill_udphdr(ðer_packet[iphdr_len], + sizeof(struct dnshdr) + + sizeof(struct udphdr) + qry_len, + UDPPORT_DNSC, UDPPORT_DNSS); + if (ip_version == 4) { + fill_iphdr(ether_packet, + sizeof(struct dnshdr) + sizeof(struct udphdr) + + iphdr_len + qry_len, + IPTYPE_UDP, 0, dns_server_ip); + } else { + memcpy(server_ipv6.addr, dns_server_ipv6, 16); + fill_ip6hdr(ether_packet, + sizeof(struct dnshdr) + sizeof(struct udphdr) + qry_len, + IPTYPE_UDP, get_ipv6_address(), + &server_ipv6); + } + + send_ip(fd, ether_packet, packetsize); +} + +/** + * DNS: Creates standard DNS-query package. Places DNS-header + * and question section in a packet and fills it with + * corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_udphdr, fill_iphdr, fill_ethhdr). + * + * @param packet Points to the place where ARP-header must be placed. + * @param domain_name the domain name given as series of labels preceded + * with length(label) and terminated with 0 + * <br>(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0") + * @see fill_udphdr + * @see fill_iphdr + * @see fill_ethhdr + */ +static void +fill_dnshdr(uint8_t * packet, int8_t * domain_name, uint8_t ip_version) +{ + struct dnshdr * dnsh = (struct dnshdr *) packet; + uint8_t * qry_section = packet + sizeof(struct dnshdr); + + dnsh -> id = htons(0x1234); + dnsh -> flags = htons(DNS_FLAG_SQUERY) | htons(DNS_FLAG_RD); + dnsh -> qdcount = htons(1); + + strcpy((char *) qry_section, (char *) domain_name); + qry_section += strlen((char *) domain_name) + 1; + + // fill QTYPE (ask for IP) + if (ip_version == 4) + * (uint16_t *) qry_section = htons(DNS_QTYPE_A); + else + * (uint16_t *) qry_section = htons(DNS_QTYPE_AAAA); + qry_section += 2; + // fill QCLASS (IN is a standard class for Internet) + * (uint16_t *) qry_section = htons(DNS_QCLASS_IN); +} + +/** + * DNS: Extracts domain name from the question or answer section of + * the DNS-message. This function is need to support message + * compression requirement (see RFC 1035, paragraph 4.1.4). + * + * @param dnsh Points at the DNS-header. + * @param head Points at the beginning of the domain_name + * which has to be extracted. + * @param domain_name In case of SUCCESS this string stores extracted name. + * In case of FAULT this string is empty. + * @return NULL in case of FAULT (domain name > 255 octets); + * otherwise pointer to the data following the name. + * @see dnshdr + */ +static uint8_t * +dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name) +{ + int8_t * tail = domain_name; + int8_t * ptr = head; + int8_t * next_section = NULL; + + while (1) { + if ((ptr[0] & 0xC0) == 0xC0) { + // message compressed (reference is used) + next_section = ptr + 2; + ptr = (int8_t *) dnsh + (htons(* (uint16_t *) ptr) & 0x3FFF); + continue; + } + if (ptr[0] == 0) { + // message termination + tail[0] = 0; + ptr += 1; + break; + } + // maximum length for domain name is 255 octets w/o termination sym + if (tail - domain_name + ptr[0] + 1 > 255) { + strcpy((char *) domain_name, ""); + return NULL; + } + memcpy(tail, ptr, ptr[0] + 1); + tail += ptr[0] + 1; + ptr += ptr[0] + 1; + } + + if (next_section == NULL) + next_section = ptr; + + return (uint8_t *) next_section; +} + +/** + * DNS: Parses URL and returns host name. + * Input string can be given as: <ul> + * <li> scheme with full path with (without) user and password + * <br>(e.g. "http://user:pass@www.host.org/url-path"); + * <li> host name with url-path + * <br>(e.g. "www.host.org/url-path"); + * <li> nothing but host name + * <br>(e.g. "www.host.org"); + * </ul> + * + * @param url string that stores incoming URL + * @param host_name In case of SUCCESS this string stores the host name, + * In case of FAULT this string is empty. + * @return TRUE - host name retrieved, + * FALSE - host name > 255 octets or empty. + */ +static int8_t +urltohost(char * url, char * host_name) +{ + uint16_t length1; + uint16_t length2; + + strcpy(host_name, ""); + + if (strstr(url, "://") != NULL) + url = strstr(url, "//") + 2; // URL + + if (strstr(url, "@") != NULL) // truncate user & password + url = strstr(url, "@") + 1; + + if (strstr(url, "/") != NULL) // truncate url path + length1 = strstr(url, "/") - url; + else + length1 = strlen(url); + + if (strstr(url, ":") != NULL) // truncate port path + length2 = strstr(url, ":") - url; + else + length2 = strlen(url); + + if(length1 > length2) + length1 = length2; + + if (length1 == 0) + return 0; // string is empty + if(length1 >= 256) + return 0; // host name is too big + + strncpy(host_name, url, length1); + host_name[length1] = 0; + + return 1; // Host name is retrieved +} + +/** + * DNS: Transforms host name string into a series of labels + * each of them preceded with length(label). 0 is a terminator. + * "www.domain.dom" -> "\3,w,w,w,\6,d,o,m,a,i,n,\3,c,o,m,\0" + * <p> + * This format is used in DNS-messages. + * + * @param host_name incoming string with the host name + * @param domain_name resulting string with series of labels + * or empty string in case of FAULT + * @return TRUE - host name transformed, + * FALSE - host name > 255 octets or label > 63 octets. + */ +static int8_t +hosttodomain(char * host_name, char * domain_name) +{ + char * domain_iter = domain_name; + char * host_iter = host_name; + + strcpy(domain_name, ""); + + if(strlen(host_name) > 255) + return 0; // invalid host name (refer to RFC 1035) + + for(; 1; ++host_iter) { + if(*host_iter != '.' && *host_iter != 0) + continue; + *domain_iter = host_iter - host_name; + if (*domain_iter > 63) { + strcpy(domain_name, ""); + return 0; // invalid host name (refer to RFC 1035) + } + ++domain_iter; + strncpy(domain_iter, host_name, host_iter - host_name); + domain_iter += (host_iter - host_name); + if(*host_iter == 0) { + *domain_iter = 0; + break; + } + host_name = host_iter + 1; + } + return 1; // ok +} diff --git a/lib/libnet/dns.h b/lib/libnet/dns.h new file mode 100644 index 0000000..b8756af --- /dev/null +++ b/lib/libnet/dns.h @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + + +#ifndef _DNS_H_ +#define _DNS_H_ + +#include <stdint.h> + +/* Initialize the environment for DNS client. */ +extern int8_t dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_version); + +/* For given URL retrieves IPv4 from DNS-server. */ +extern int8_t dns_get_ip(int fd, char * url, uint8_t * domain_ip, uint8_t ip_version); + +/* Handles DNS-packets, which are detected by receive_ether. */ +extern int32_t handle_dns(uint8_t * packet, int32_t packetsize); + +#endif diff --git a/lib/libnet/ethernet.c b/lib/libnet/ethernet.c new file mode 100644 index 0000000..1e03a0b --- /dev/null +++ b/lib/libnet/ethernet.c @@ -0,0 +1,189 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + + +/******************************* ALGORITHMS ******************************/ + +/** \file netbase.c <pre> + * *********************** Receive-handle diagram ************************* + * + * Note: Every layer calls out required upper layer + * + * lower + * | MAC/LLC Receive packet (receive_ether) + * | | + * | NETWORK +-----------+---------+ + * | | | + * | IPv4 (handle_ipv4) IPv6 (handle_ipv4) + * | ARP (handle_arp) ICMP & NDP + * | ICMP | + * | | | + * | +---------+---------+ + * | | + * | TRANSPORT +---------+---------+ + * | | | + * | TCP (handle_tcp) UDP (handle_udp) + * | | + * | APPLICATION +----------------+-----------+ + * V | | + * upper DNS (handle_dns) BootP / DHCP (handle_bootp_client) + * + * ************************************************************************ + * </pre> */ + + +/************************ DEFINITIONS & DECLARATIONS *********************/ + +#include <ethernet.h> +#include <string.h> +#include <sys/socket.h> +#include <ipv4.h> +#include <ipv6.h> + + +/****************************** LOCAL VARIABLES **************************/ + +static uint8_t ether_packet[ETH_MTU_SIZE]; +static uint8_t own_mac[6] = {0, 0, 0, 0, 0, 0}; +static uint8_t multicast_mac[] = {0x01, 0x00, 0x5E}; +static const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +/****************************** IMPLEMENTATION ***************************/ + +/** + * Ethernet: Set the own MAC address to initializes ethernet layer. + * + * @param own_mac own hardware-address (MAC) + */ +void set_mac_address(const uint8_t * _own_mac) +{ + if (_own_mac) + memcpy(own_mac, _own_mac, 6); + else + memset(own_mac, 0, 6); +} + +/** + * Ethernet: Set the own MAC address to initializes ethernet layer. + * + * @return own hardware-address (MAC) + */ +const uint8_t *get_mac_address(void) +{ + return own_mac; +} + +/** + * Ethernet: Check if given multicast address is a multicast MAC address + * starting with 0x3333 + * + * @return true or false + */ +static uint8_t is_multicast_mac(uint8_t * mac) +{ + + uint16_t mc = 0x3333; + if (memcmp(mac, &mc, 2) == 0) + return 1; + + return 0; +} + +/** + * Ethernet: Receives an ethernet-packet and handles it according to + * Receive-handle diagram. + * + * @param fd socket fd + * @return ZERO - packet was handled or no packets received; + * NON ZERO - error condition occurs. + */ +int32_t receive_ether(int fd) +{ + int32_t bytes_received; + struct ethhdr * ethh; + + memset(ether_packet, 0, ETH_MTU_SIZE); + bytes_received = recv(fd, ether_packet, ETH_MTU_SIZE, 0); + + if (!bytes_received) // No messages + return 0; + + if (bytes_received < 0) + return -1; /* recv() failed */ + + if ((size_t) bytes_received < sizeof(struct ethhdr)) + return -1; // packet is too small + + ethh = (struct ethhdr *) ether_packet; + + if(memcmp(ethh->dest_mac, broadcast_mac, 6) != 0 + && memcmp(ethh->dest_mac, multicast_mac, 3) != 0 + && memcmp(ethh->dest_mac, own_mac, 6 ) != 0 + && !is_multicast_mac(ethh->dest_mac)) + return -1; // packet is too small + + switch (htons(ethh -> type)) { + case ETHERTYPE_IP: + return handle_ipv4(fd, (uint8_t*) (ethh + 1), + bytes_received - sizeof(struct ethhdr)); + + case ETHERTYPE_IPv6: + return handle_ipv6(fd, ether_packet + sizeof(struct ethhdr), + bytes_received - sizeof(struct ethhdr)); + + case ETHERTYPE_ARP: + return handle_arp(fd, (uint8_t*) (ethh + 1), + bytes_received - sizeof(struct ethhdr)); + default: + break; + } + return -1; // unknown protocol +} + +/** + * Ethernet: Sends an ethernet frame via the initialized file descriptor. + * + * @return number of transmitted bytes + */ +int +send_ether(int fd, void* buffer, int len) +{ + return send(fd, buffer, len, 0); +} + +/** + * Ethernet: Creates Ethernet-packet. Places Ethernet-header in a packet and + * fills it with corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_arphdr, fill_iphdr, fill_udphdr, fill_dnshdr, fill_btphdr). + * + * @param packet Points to the place where eth-header must be placed. + * @param eth_type Type of the next level protocol (e.g. IP or ARP). + * @param src_mac Sender MAC address + * @param dest_mac Receiver MAC address + * @see ethhdr + * @see fill_arphdr + * @see fill_iphdr + * @see fill_udphdr + * @see fill_dnshdr + * @see fill_btphdr + */ +void fill_ethhdr(uint8_t * packet, uint16_t eth_type, + const uint8_t * src_mac, const uint8_t * dest_mac) +{ + struct ethhdr * ethh = (struct ethhdr *) packet; + + ethh -> type = htons(eth_type); + memcpy(ethh -> src_mac, src_mac, 6); + memcpy(ethh -> dest_mac, dest_mac, 6); +} diff --git a/lib/libnet/ethernet.h b/lib/libnet/ethernet.h new file mode 100644 index 0000000..e541c8f --- /dev/null +++ b/lib/libnet/ethernet.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + +#ifndef _ETHERNET_H +#define _ETHERNET_H + +#include <stdint.h> + +#define ETH_MTU_SIZE 1518 /**< Maximum Transfer Unit */ +#define ETH_ALEN 6 /**< HW address length */ +#define ETHERTYPE_IP 0x0800 +#define ETHERTYPE_IPv6 0x86DD +#define ETHERTYPE_ARP 0x0806 + +/** \struct ethhdr + * A header for Ethernet-packets. + */ +struct ethhdr { + uint8_t dest_mac[ETH_ALEN]; /**< Destination HW address */ + uint8_t src_mac[ETH_ALEN]; /**< Source HW address */ + uint16_t type; /**< Next level protocol type */ +}; + +/* Initializes ethernet layer */ +extern void set_mac_address(const uint8_t * own_mac); +extern const uint8_t * get_mac_address(void); + +/* Receives and handles packets, according to Receive-handle diagram */ +extern int32_t receive_ether(int fd); + +/* Sends an ethernet frame. */ +extern int send_ether(int fd, void* buffer, int len); + +/* fills ethernet header */ +extern void fill_ethhdr(uint8_t * packet, uint16_t eth_type, + const uint8_t * src_mac, const uint8_t * dest_mac); + +#endif diff --git a/lib/libnet/icmpv6.c b/lib/libnet/icmpv6.c new file mode 100644 index 0000000..e897588 --- /dev/null +++ b/lib/libnet/icmpv6.c @@ -0,0 +1,389 @@ +/****************************************************************************** + * 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 <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/socket.h> +#include "ethernet.h" +#include "ipv6.h" +#include "icmpv6.h" +#include "ndp.h" +#include "dhcpv6.h" + +static int ra_received = 0; + +/** + * NET: + * @param fd socket fd + */ +void +send_router_solicitation (int fd) +{ + ip6_addr_t dest_addr; + uint8_t ether_packet[ETH_MTU_SIZE]; + struct packeth headers; + + headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr)); + headers.icmp6h = (struct icmp6hdr *) (ether_packet + + sizeof(struct ethhdr) + + sizeof(struct ip6hdr)); + + /* Destination is "All routers multicast address" (link-local) */ + dest_addr.part.prefix = 0xff02000000000000ULL; + dest_addr.part.interface_id = 2; + + /* Fill IPv6 header */ + fill_ip6hdr (ether_packet + sizeof(struct ethhdr), + ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation), + 0x3a, //ICMPV6 + get_ipv6_address(), &dest_addr); + + /* Fill ICMPv6 message */ + headers.icmp6h->type = ICMPV6_ROUTER_SOLICITATION; + headers.icmp6h->code = 0; + headers.icmp6h->icmp6body.router_solicit.lladdr.type = 1; + headers.icmp6h->icmp6body.router_solicit.lladdr.length = 1; + memcpy( &(headers.icmp6h->icmp6body.router_solicit.lladdr.mac), + get_mac_address(), 6); + + send_ip (fd, headers.ip6h, sizeof(struct ip6hdr) + + ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation)); +} + +/** + * NET: Process prefix option in Router Advertisements + * + * @param ip6_packet pointer to an IPv6 packet + */ +static void +handle_prefixoption (uint8_t *option) +{ + ip6_addr_t prefix; + struct ip6addr_list_entry *new_address; + struct option_prefix *prefix_option; + struct prefix_info *prfx_info; + + prefix_option = (struct option_prefix *) option; + memcpy( &(prefix.addr), &(prefix_option->prefix.addr), IPV6_ADDR_LENGTH); + + /* Link-local adresses in RAs are nonsense */ + if (ip6_is_linklocal(&prefix)) + return; + + if (prefix_option->preferred_lifetime > prefix_option->valid_lifetime) + return; + + /* Add address created from prefix to IPv6 address list */ + new_address = ip6_prefix2addr (prefix); + if (!new_address) + return; + + /* Process only prefixes we don't already have an adress from */ + if (!unknown_prefix (&new_address->addr)) { + return; + } + + /* Fill struct prefix_info from data in RA and store it in new_address */ + prfx_info = ip6_create_prefix_info(); + if (!prfx_info) + return; + memcpy (&(new_address->prfx_info), prfx_info, sizeof(struct prefix_info)); + + /* Add prefix received in RA to list of known prefixes */ + ip6addr_add (new_address); +} + +/** + * NET: Process source link layer addresses in Router Advertisements + * + * @param ip6_packet pointer to an IPv6 packet + */ +static void +handle_source_lladdr ( struct option_ll_address *option, struct router *rtr) +{ + memcpy (&(rtr->mac), &(option->mac), 6); +} + +/** + * NET: Process ICMPv6 options in Router Advertisements + * + * @param ip6_packet pointer to an IPv6 packet + */ +static void +process_ra_options (uint8_t *option, int32_t option_length, struct router *r) +{ + while (option_length > 0) { + switch (*option) { + case ND_OPTION_SOURCE_LL_ADDR: + handle_source_lladdr ((struct option_ll_address *) option, r); + break; + case ND_OPTION_PREFIX_INFO: + handle_prefixoption(option); + break; + default: + break; + } + //option+1 is the length field. length is in units of 8 bytes + option_length = option_length - (*(option+1) * 8); + option = option + (*(option+1) * 8); + } + + return; +} + +/** + * NET: Process Router Advertisements + * + * @param ip6_packet pointer to an IPv6 packet + */ +static void +handle_ra (struct icmp6hdr *icmp6h, uint8_t *ip6_packet) +{ + uint8_t *first_option; + int32_t option_length; + struct ip6hdr *ip6h; + struct router_advertisement *ra; + struct router *rtr; + ip6_addr_t *rtr_ip; + uint8_t rtr_mac[] = {0, 0, 0, 0, 0, 0}; + + ip6h = (struct ip6hdr *) ip6_packet; + ra = (struct router_advertisement *) &icmp6h->icmp6body.ra; + rtr_ip = (ip6_addr_t *) &ip6h->src; + + rtr = find_router (&(ip6h->src)); + if (!rtr) { + rtr = router_create (rtr_mac, rtr_ip); + router_add (rtr); + } + + /* store info from router advertisement in router struct */ + rtr->lifetime = ra->router_lifetime; + rtr->reachable_time = ra->reachable_time; + rtr->retrans_timer = ra->retrans_timer; + + /* save flags concerning address (auto-) configuration */ + ip6_state.managed_mode = ra->flags.managed; + ip6_state.other_config = ra->flags.other; + + /* Process ICMPv6 options in Router Advertisement */ + first_option = (uint8_t *) icmp6h + ICMPv6_HEADER_SIZE + 12; + option_length = (uint8_t *) icmp6h + ip6h->pl - first_option; + process_ra_options( (uint8_t *) first_option, option_length, rtr); + + ra_received = 1; +} + +int is_ra_received(void) +{ + return ra_received; +} + +/** + * NET: + * + * @param fd socket fd + * @param ip6_addr_t *dest_ip6 + */ +void +send_neighbour_solicitation (int fd, ip6_addr_t *dest_ip6) +{ + ip6_addr_t snma; + + uint8_t ether_packet[ETH_MTU_SIZE]; + struct packeth headers; + + memset(ether_packet, 0, ETH_MTU_SIZE); + headers.ethh = (struct ethhdr *) ether_packet; + headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr)); + headers.icmp6h = (struct icmp6hdr *) (ether_packet + + sizeof(struct ethhdr) + + sizeof(struct ip6hdr)); + + /* Fill IPv6 header */ + snma.part.prefix = IPV6_SOLIC_NODE_PREFIX; + snma.part.interface_id = IPV6_SOLIC_NODE_IFACE_ID; + snma.addr[13] = dest_ip6->addr[13]; + snma.addr[14] = dest_ip6->addr[14]; + snma.addr[15] = dest_ip6->addr[15]; + fill_ip6hdr((uint8_t *) headers.ip6h, + ICMPv6_HEADER_SIZE + + sizeof(struct neighbour_solicitation), + 0x3a, //ICMPv6 + get_ipv6_address(), &snma); + + /* Fill ICMPv6 message */ + headers.icmp6h->type = ICMPV6_NEIGHBOUR_SOLICITATION; + headers.icmp6h->code = 0; + memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.target), + dest_ip6, IPV6_ADDR_LENGTH ); + headers.icmp6h->icmp6body.nghb_solicit.lladdr.type = 1; + headers.icmp6h->icmp6body.nghb_solicit.lladdr.length = 1; + memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.lladdr.mac), + get_mac_address(), 6); + + send_ip (fd, ether_packet + sizeof(struct ethhdr), + sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE + + sizeof(struct neighbour_solicitation)); +} + +/** + * NET: + * + * @param fd socket fd + * @param ip6_packet pointer to an IPv6 packet + * @param icmp6hdr pointer to the icmp6 header in ip6_packet + * @param na_flags Neighbour advertisment flags + */ +static void +send_neighbour_advertisement (int fd, struct neighbor *target) +{ + struct na_flags na_adv_flags; + uint8_t ether_packet[ETH_MTU_SIZE]; + struct packeth headers; + + + headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr)); + headers.icmp6h = (struct icmp6hdr *) (ether_packet + + sizeof(struct ethhdr) + + sizeof(struct ip6hdr)); + + /* Fill IPv6 header */ + fill_ip6hdr(ether_packet + sizeof(struct ethhdr), + ICMPv6_HEADER_SIZE + + sizeof(struct neighbour_advertisement), + 0x3a, //ICMPv6 + get_ipv6_address(), (ip6_addr_t *) &(target->ip.addr)); + + /* Fill ICMPv6 message */ + memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target), + &(target->ip.addr), IPV6_ADDR_LENGTH ); + headers.icmp6h->icmp6body.nghb_adv.lladdr.type = 1; + headers.icmp6h->icmp6body.nghb_adv.lladdr.length = 1; + memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac), + get_mac_address(), 6); + + na_adv_flags.is_router = 0; + na_adv_flags.na_is_solicited = 1; + na_adv_flags.override = 1; + + headers.icmp6h->type = ICMPV6_NEIGHBOUR_ADVERTISEMENT; + headers.icmp6h->code = 0; + headers.icmp6h->icmp6body.nghb_adv.router = na_adv_flags.is_router; + + headers.icmp6h->icmp6body.nghb_adv.solicited = na_adv_flags.na_is_solicited; + headers.icmp6h->icmp6body.nghb_adv.override = na_adv_flags.override; + headers.icmp6h->icmp6body.nghb_adv.lladdr.type = 2; + headers.icmp6h->icmp6body.nghb_adv.lladdr.length = 1; + + memset( &(headers.icmp6h->icmp6body.nghb_adv.target), 0, + IPV6_ADDR_LENGTH ); + + if( na_adv_flags.na_is_solicited ) { + memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target), + get_ipv6_address(), IPV6_ADDR_LENGTH); + } + + memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac), + get_mac_address(), 6); + + send_ip (fd, ether_packet + sizeof(struct ethhdr), + sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE + + sizeof(struct neighbour_advertisement)); +} + +/** + * NET: + * + * @param fd socket fd + * @param ip6_packet pointer to an IPv6 packet + */ +static int8_t +handle_na (int fd, uint8_t *packet) +{ + struct neighbor *n = NULL; + struct packeth headers; + ip6_addr_t ip; + + headers.ethh = (struct ethhdr *) packet; + headers.ip6h = (struct ip6hdr *) (packet + sizeof(struct ethhdr)); + headers.icmp6h = (struct icmp6hdr *) (packet + + sizeof(struct ethhdr) + + sizeof(struct ip6hdr)); + + memcpy(&(ip.addr), &(headers.ip6h->src), IPV6_ADDR_LENGTH); + + n = find_neighbor (&ip); + + if (!n) { + n= (struct neighbor *) + neighbor_create( packet, &headers ); + if (!n) + return 0; + if (!neighbor_add(n)) + return 0; + } else { + memcpy (&(n->mac), &(headers.ethh->src_mac[0]), 6); + n->status = NB_REACHABLE; + if (n->eth_len > 0) { + struct ethhdr * ethh = (struct ethhdr *) &(n->eth_frame); + memcpy(ethh->dest_mac, &(n->mac), 6); + send_ether (fd, &(n->eth_frame), n->eth_len + sizeof(struct ethhdr)); + n->eth_len = 0; + } + } + + return 1; +} + +/** + * NET: Handles ICMPv6 messages + * + * @param fd socket fd + * @param ip6_packet pointer to an IPv6 packet + * @param packetsize size of ipv6_packet + */ +int8_t +handle_icmpv6 (int fd, struct ethhdr *etherhdr, + uint8_t *ip6_packet) +{ + + struct icmp6hdr *received_icmp6 = NULL; + struct ip6hdr *received_ip6 = NULL; + struct neighbor target; + + received_ip6 = (struct ip6hdr *) ip6_packet; + received_icmp6 = (struct icmp6hdr *) (ip6_packet + + sizeof(struct ip6hdr)); + memcpy( &(target.ip.addr), &(received_ip6->src), + IPV6_ADDR_LENGTH ); + memcpy( &(target.mac), etherhdr->src_mac, 6); + + /* process ICMPv6 types */ + switch(received_icmp6->type) { + case ICMPV6_NEIGHBOUR_SOLICITATION: + send_neighbour_advertisement(fd, &target); + break; + case ICMPV6_NEIGHBOUR_ADVERTISEMENT: + handle_na(fd, (uint8_t *) ip6_packet - sizeof(struct ethhdr)); + break; + case ICMPV6_ROUTER_ADVERTISEMENT: + handle_ra(received_icmp6, (uint8_t *) received_ip6); + break; + default: + return -1; + } + + return 1; +} diff --git a/lib/libnet/icmpv6.h b/lib/libnet/icmpv6.h new file mode 100644 index 0000000..41b0c70 --- /dev/null +++ b/lib/libnet/icmpv6.h @@ -0,0 +1,135 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#ifndef _ICMPV6_H_ +#define _ICMPV6_H_ + +#include <stdint.h> +#include "ethernet.h" +#include "ipv6.h" + +#define __ICMPV6_DEBUG__ + +#ifdef __ICMPV6_DEBUG__ +#define ICMPV6_DEBUG_PRINT(format, ...) printf(format, ## __VA_ARGS__) +#else +#define ICMPV6_DEBUG_PRINT(format, ...) +#endif + +#define ICMPv6_HEADER_SIZE 4 /* Size of common fields */ +#define IPTYPE_ICMPV6 0x3a + +/* Error message types */ +#define ICMPV6_DEST_UNREACHABLE 1 /* Destination unreachable */ +#define ICMPV6_PACKET_TOO_BIG 2 /* Packet too big */ +#define ICMPV6_TIME_EXCEEDED 3 /* Time exceeded */ +#define ICMPV6_PARAM_PROBLEM 4 /* Parameter problem */ + +/* Informational message types */ +#define ICMPV6_ECHO_REQUEST 128 /* Echo request */ +#define ICMPV6_ECHO_REPLY 129 /* Echo reply */ +#define ICMPV6_MCAST_LISTENER_QUERY 130 /* Multicast listener query */ +#define ICMPV6_MCAST_LISTENER_REPORT 131 /* Multicast listener report */ +#define ICMPv6 MCAST_LISTENER_DONE 132 /* Multicast listener done */ +#define ICMPV6_ROUTER_SOLICITATION 133 /* Router solicitation */ +#define ICMPV6_ROUTER_ADVERTISEMENT 134 /* Router advertisement */ +#define ICMPV6_NEIGHBOUR_SOLICITATION 135 /* Neighbor solicitation */ +#define ICMPV6_NEIGHBOUR_ADVERTISEMENT 136 /* Neighbor advertisement */ +#define ICMPV6_REDIRECT_MSG 137 /* Redirect message */ + +/******** Functions *******************/ +int8_t handle_icmpv6 (int fd, struct ethhdr *etherhdr, uint8_t *ip6_packet); +void send_neighbour_solicitation(int fd, ip6_addr_t *target_ip6); +void send_router_solicitation(int fd); +int is_ra_received(void); + +/* Prefix information */ +struct option_prefix { + uint8_t type; + uint8_t length; + uint8_t prefix_length; + uint8_t onlink:1, + autom:1, + not_router:1, + not_site_prefix:1, + reserved:4; + uint32_t valid_lifetime; + uint32_t preferred_lifetime; + uint32_t reserved2; + ip6_addr_t prefix; +} __attribute((packed)); + +/* Neighbour advertisement/solicitation flags */ +struct na_flags { + uint8_t is_router:1, /* sender (we) is a router */ + na_is_solicited:1, /* this NA was solicited (asked for) */ + override:1, /* receiver shall override its cache entries */ + unused:5; +}__attribute((packed)); + +/* Source/Target Link-layer address */ +struct option_ll_address{ + uint8_t type; + uint8_t length; + uint8_t mac[ETH_ALEN]; +} __attribute((packed)); + +struct neighbour_solicitation { + uint32_t router:1, + solicited:1, + override:1, + reserved:29; + ip6_addr_t target; + struct option_ll_address lladdr; +} __attribute((packed)); + +struct neighbour_advertisement { + uint32_t router:1, + solicited:1, + override:1, + reserved:29; + ip6_addr_t target; + struct option_ll_address lladdr; +} __attribute((packed)); + +struct router_solicitation { + uint32_t reserved; + struct option_ll_address lladdr; +} __attribute((packed)); + +struct router_advertisement { + uint8_t curr_hop_limit; + struct raflags { + uint8_t managed:1, + other:1, + reserved:6; + } flags; + uint16_t router_lifetime; + uint32_t reachable_time; + uint32_t retrans_timer; + struct option_prefix prefix; + struct option_ll_address ll_addr; +} __attribute((packed)); + +struct icmp6hdr { + uint8_t type; + uint8_t code; + uint16_t checksum; + union { + struct neighbour_solicitation nghb_solicit; + struct neighbour_advertisement nghb_adv; + struct router_solicitation router_solicit; + struct router_advertisement ra; + } icmp6body; +} __attribute((packed)); + +#endif diff --git a/lib/libnet/ipv4.c b/lib/libnet/ipv4.c new file mode 100644 index 0000000..3a1a789 --- /dev/null +++ b/lib/libnet/ipv4.c @@ -0,0 +1,898 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + + +/********************** DEFINITIONS & DECLARATIONS ***********************/ + +#include <ipv4.h> +#include <udp.h> +#include <tcp.h> +#include <ethernet.h> +#include <time.h> +#include <sys/socket.h> +#include <string.h> + +/* ARP Message types */ +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +/* ARP talbe size (+1) */ +#define ARP_ENTRIES 10 + +/* ICMP Message types */ +#define ICMP_ECHO_REPLY 0 +#define ICMP_DST_UNREACHABLE 3 +#define ICMP_SRC_QUENCH 4 +#define ICMP_REDIRECT 5 +#define ICMP_ECHO_REQUEST 8 +#define ICMP_TIME_EXCEEDED 11 +#define ICMP_PARAMETER_PROBLEM 12 +#define ICMP_TIMESTAMP_REQUEST 13 +#define ICMP_TIMESTAMP_REPLY 14 +#define ICMP_INFORMATION_REQUEST 15 +#define ICMP_INFORMATION_REPLY 16 + +/** \struct arp_entry + * A entry that describes a mapping between IPv4- and MAC-address. + */ +typedef struct arp_entry arp_entry_t; +struct arp_entry { + uint32_t ipv4_addr; + uint8_t mac_addr[6]; + uint8_t eth_frame[ETH_MTU_SIZE]; + int eth_len; + int pkt_pending; +}; + +/** \struct icmphdr + * ICMP packet + */ +struct icmphdr { + unsigned char type; + unsigned char code; + unsigned short int checksum; + union { + /* for type 3 "Destination Unreachable" */ + unsigned int unused; + /* for type 0 and 8 */ + struct echo { + unsigned short int id; + unsigned short int seq; + } echo; + } options; + union { + /* payload for destination unreachable */ + struct dun { + unsigned char iphdr[20]; + unsigned char data[64]; + } dun; + /* payload for echo or echo reply */ + /* maximum size supported is 84 */ + unsigned char data[84]; + } payload; +}; + +/****************************** PROTOTYPES *******************************/ + +static unsigned short checksum(unsigned short *packet, int words); + +static void arp_send_request(int fd, uint32_t dest_ip); + +static void arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac); + +static void fill_arphdr(uint8_t * packet, uint8_t opcode, + const uint8_t * src_mac, uint32_t src_ip, + const uint8_t * dest_mac, uint32_t dest_ip); + +static arp_entry_t *lookup_mac_addr(uint32_t ipv4_addr); + +static void fill_udp_checksum(struct iphdr *ipv4_hdr); + +static int8_t handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, + int32_t packetsize); + +/****************************** LOCAL VARIABLES **************************/ + +/* Routing parameters */ +static uint32_t own_ip = 0; +static uint32_t multicast_ip = 0; +static uint32_t router_ip = 0; +static uint32_t subnet_mask = 0; + +/* helper variables */ +static uint32_t ping_dst_ip; +static const uint8_t null_mac_addr[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +static uint8_t multicast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +/* There are only (ARP_ENTRIES-1) effective entries because + * the entry that is pointed by arp_producer is never used. + */ +static unsigned int arp_consumer = 0; +static unsigned int arp_producer = 0; +static arp_entry_t arp_table[ARP_ENTRIES]; + +static uint8_t pending_pkt_frame[ETH_MTU_SIZE]; +static int pending_pkt_len; + +/* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */ +int (*send_ip) (int fd, void *, int); + +/***************************** IMPLEMENTATION ****************************/ + +/** + * IPv4: Initialize the environment for the IPv4 layer. + */ +static void ipv4_init(void) +{ + int i; + + ping_dst_ip = 0; + + // clear ARP table + arp_consumer = 0; + arp_producer = 0; + for(i=0; i<ARP_ENTRIES; ++i) { + arp_table[i].ipv4_addr = 0; + memset(arp_table[i].mac_addr, 0, 6); + arp_table[i].eth_len = 0; + arp_table[i].pkt_pending = 0; + } + + /* Set IP send function to send_ipv4() */ + send_ip = &send_ipv4; +} + +/** + * IPv4: Set the own IPv4 address. + * + * @param _own_ip client IPv4 address (e.g. 127.0.0.1) + */ +void set_ipv4_address(uint32_t _own_ip) +{ + own_ip = _own_ip; + ipv4_init(); +} + +/** + * IPv4: Get the own IPv4 address. + * + * @return client IPv4 address (e.g. 127.0.0.1) + */ +uint32_t get_ipv4_address(void) +{ + return own_ip; +} + +/** + * IPv4: Set the IPv4 multicast address. + * + * @param _own_ip multicast IPv4 address (224.0.0.0 - 239.255.255.255) + */ +void set_ipv4_multicast(uint32_t _multicast_ip) +{ + // is this IP Multicast out of range (224.0.0.0 - 239.255.255.255) + if((htonl(_multicast_ip) < 0xE0000000) + || (htonl(_multicast_ip) > 0xEFFFFFFF)) { + multicast_ip = 0; + memset(multicast_mac, 0xFF, 6); + return; + } + + multicast_ip = _multicast_ip; + multicast_mac[0] = 0x01; + multicast_mac[1] = 0x00; + multicast_mac[2] = 0x5E; + multicast_mac[3] = (uint8_t) 0x7F & (multicast_ip >> 16); + multicast_mac[4] = (uint8_t) 0xFF & (multicast_ip >> 8); + multicast_mac[5] = (uint8_t) 0xFF & (multicast_ip >> 0); +} + +/** + * IPv4: Get the IPv4 multicast address. + * + * @return multicast IPv4 address (224.0.0.0 - 239.255.255.255 or 0 if not set) + */ +uint32_t get_ipv4_multicast(void) +{ + return multicast_ip; +} + +/** + * IPv4: Set the routers IPv4 address. + * + * @param _router_ip router IPv4 address + */ +void set_ipv4_router(uint32_t _router_ip) +{ + router_ip = _router_ip; + ipv4_init(); +} + +/** + * IPv4: Get the routers IPv4 address. + * + * @return router IPv4 address + */ +uint32_t get_ipv4_router(void) +{ + return router_ip; +} + +/** + * IPv4: Set the subnet mask. + * + * @param _subnet_mask netmask of the own IPv4 address + */ +void set_ipv4_netmask(uint32_t _subnet_mask) +{ + subnet_mask = _subnet_mask; + ipv4_init(); +} + +/** + * IPv4: Get the subnet mask. + * + * @return netmask of the own IPv4 address + */ +uint32_t get_ipv4_netmask(void) +{ + return subnet_mask; +} + +/** + * IPv4: Get the default subnet mask according to the IP class + * + * @param ip_addr IPv4 address + * @return default netmask according to the IP class + */ +uint32_t get_default_ipv4_netmask(char *ip_addr) +{ + unsigned char top; + + top = ip_addr[0]; + if (top > 0 && top < 128) + return 0xFF000000; /* Class A: 255.0.0.0 */ + else if (top >= 128 && top < 192) + return 0xFFFF0000; /* Class B: 255.255.0.0 */ + else if (top >= 192 && top < 224) + return 0xFFFFFF00; /* Class C: 255.255.255.0 */ + else + return 0; +} + +/** + * IPv4: Creates IP-packet. Places IP-header in a packet and fills it + * with corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_ethhdr, fill_udphdr, fill_dnshdr, fill_btphdr). + * + * @param packet Points to the place where IP-header must be placed. + * @param packetsize Size of the packet in bytes incl. this hdr and data. + * @param ip_proto Type of the next level protocol (e.g. UDP). + * @param ip_src Sender IP address + * @param ip_dst Receiver IP address + * @see iphdr + * @see fill_ethhdr + * @see fill_udphdr + * @see fill_dnshdr + * @see fill_btphdr + */ +void fill_iphdr(uint8_t * packet, uint16_t packetsize, + uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst) +{ + struct iphdr * iph = (struct iphdr *) packet; + + iph -> ip_hlv = 0x45; + iph -> ip_tos = 0x10; + iph -> ip_len = htons(packetsize); + iph -> ip_id = htons(0); + iph -> ip_off = 0; + iph -> ip_ttl = 0xFF; + iph -> ip_p = ip_proto; + iph -> ip_src = htonl(ip_src); + iph -> ip_dst = htonl(ip_dst); + iph -> ip_sum = 0; +} + +/** + * IPv4: Handles IPv4-packets according to Receive-handle diagram. + * + * @param fd socket fd + * @param ip_packet IP-packet to be handled + * @param packetsize Length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see receive_ether + * @see iphdr + */ +int8_t handle_ipv4(int fd, uint8_t * ip_packet, uint32_t packetsize) +{ + struct iphdr * iph; + int32_t old_sum; + static uint8_t ip_heap[65536 + ETH_MTU_SIZE]; + + if (packetsize < sizeof(struct iphdr)) + return -1; // packet is too small + + iph = (struct iphdr * ) ip_packet; + + /* Drop it if destination IPv4 address is no IPv4 Broadcast, no + * registered IPv4 Multicast and not our Unicast address + */ + if((multicast_ip == 0 && iph->ip_dst >= 0xE0000000 && iph->ip_dst <= 0xEFFFFFFF) + || (multicast_ip != iph->ip_dst && iph->ip_dst != 0xFFFFFFFF && + own_ip != 0 && iph->ip_dst != own_ip)) { + return -1; + } + + old_sum = iph -> ip_sum; + iph -> ip_sum = 0; + if (old_sum != checksum((uint16_t *) iph, sizeof (struct iphdr) >> 1)) + return -1; // Wrong IP checksum + + // is it the first fragment in a packet? + if (((iph -> ip_off) & 0x1FFF) == 0) { + // is it part of more fragments? + if (((iph -> ip_off) & 0x2000) == 0x2000) { + memcpy(ip_heap, ip_packet, iph->ip_len); + return 0; + } + } + // it's not the first fragment + else { + // get the first fragment + struct iphdr * iph_first = (struct iphdr * ) ip_heap; + + // is this fragment not part of the first one, then exit + if ((iph_first->ip_id != iph->ip_id ) || + (iph_first->ip_p != iph->ip_p ) || + (iph_first->ip_src != iph->ip_src) || + (iph_first->ip_dst != iph->ip_dst)) { + return 0; + } + + // this fragment is part of the first one! + memcpy(ip_heap + sizeof(struct iphdr) + + ((iph -> ip_off) & 0x1FFF) * 8, + ip_packet + sizeof(struct iphdr), + iph -> ip_len - sizeof(struct iphdr)); + + // is it part of more fragments? Then return. + if (((iph -> ip_off) & 0x2000) == 0x2000) { + return 0; + } + + // packet is completly reassambled now! + + // recalculate ip_len and set iph and ip_packet to the + iph_first->ip_len = iph->ip_len + ((iph->ip_off) & 0x1FFF) * 8; + + // set iph and ip_packet to the resulting packet. + ip_packet = ip_heap; + iph = (struct iphdr * ) ip_packet; + } + + switch (iph -> ip_p) { + case IPTYPE_ICMP: + return handle_icmp(fd, iph, ip_packet + sizeof(struct iphdr), + iph -> ip_len - sizeof(struct iphdr)); + case IPTYPE_UDP: + return handle_udp(fd, ip_packet + sizeof(struct iphdr), + iph -> ip_len - sizeof(struct iphdr)); + case IPTYPE_TCP: + return handle_tcp(ip_packet + sizeof(struct iphdr), + iph -> ip_len - sizeof(struct iphdr)); + default: + break; + } + return -1; // Unknown protocol +} + +/** + * IPv4: Send IPv4-packets. + * + * Before the packet is sent there are some patcches performed: + * - IPv4 source address is replaced by our unicast IPV4 address + * if it is set to 0 or 1 + * - IPv4 destination address is replaced by our multicast IPV4 address + * if it is set to 1 + * - IPv4 checksum is calculaded. + * - If payload type is UDP, then the UDP checksum is calculated also. + * + * We send an ARP request first, if this is the first packet sent to + * the declared IPv4 destination address. In this case we store the + * the packet and send it later if we receive the ARP response. + * If the MAC address is known already, then we send the packet immediately. + * If there is already an ARP request pending, then we drop this packet + * and send again an ARP request. + * + * @param fd socket fd + * @param ip_packet IP-packet to be handled + * @param packetsize Length of the packet + * @return -2 - packet dropped (MAC address not resolved - ARP request pending) + * -1 - packet dropped (bad format) + * 0 - packet stored (ARP request sent - packet will be sent if + * ARP response is received) + * >0 - packet send (number of transmitted bytes is returned) + * + * @see receive_ether + * @see iphdr + */ +int send_ipv4(int fd, void* buffer, int len) +{ + arp_entry_t *arp_entry = 0; + struct iphdr *ip; + const uint8_t *mac_addr = 0; + uint32_t ip_dst = 0; + + if(len + sizeof(struct ethhdr) > ETH_MTU_SIZE) + return -1; + + ip = (struct iphdr *) buffer; + + /* Replace source IPv4 address with our own unicast IPv4 address + * if it's 0 (= own unicast source address not specified). + */ + if(ip->ip_src == 0) { + ip->ip_src = htonl( own_ip ); + } + /* Replace source IPv4 address with our unicast IPv4 address and + * replace destination IPv4 address with our multicast IPv4 address + * if source address is set to 1. + */ + else if(ip->ip_src == 1) { + ip->ip_src = htonl( own_ip ); + ip->ip_dst = htonl( multicast_ip ); + } + + // Calculate the IPv4 checksum + ip->ip_sum = 0; + ip->ip_sum = checksum((uint16_t *) ip, sizeof (struct iphdr) >> 1); + + // if payload type is UDP, then we need to calculate the + // UDP checksum that depends on the IP header + if(ip->ip_p == IPTYPE_UDP) { + fill_udp_checksum(ip); + } + + ip_dst = ip->ip_dst; + // Check if the MAC address is already cached + if(~ip->ip_dst == 0 + || ( ((~subnet_mask) & ip->ip_dst) == ~subnet_mask && + ( subnet_mask & ip->ip_dst) == (subnet_mask & own_ip))) { + arp_entry = &arp_table[arp_producer]; + mac_addr = broadcast_mac; + } + else if(ip->ip_dst == multicast_ip) { + arp_entry = &arp_table[arp_producer]; + mac_addr = multicast_mac; + } + else { + // Check if IP address is in the same subnet as we are + if((subnet_mask & own_ip) == (subnet_mask & ip->ip_dst)) + arp_entry = lookup_mac_addr(ip->ip_dst); + // if not then we need to know the router's IP address + else { + ip_dst = router_ip; + arp_entry = lookup_mac_addr(router_ip); + } + if(arp_entry && memcmp(arp_entry->mac_addr, null_mac_addr, 6) != 0) + mac_addr = arp_entry->mac_addr; + } + + // If we could not resolv the MAC address by our own... + if(!mac_addr) { + // send the ARP request + arp_send_request(fd, ip_dst); + + // drop the current packet if there is already a ARP request pending + if(arp_entry) + return -2; + + // take the next entry in the ARP table to prepare a the new ARP entry. + arp_entry = &arp_table[arp_producer]; + arp_producer = (arp_producer+1)%ARP_ENTRIES; + + // if ARP table is full then we must drop the oldes entry. + if(arp_consumer == arp_producer) + arp_consumer = (arp_consumer+1)%ARP_ENTRIES; + + // store the packet to be send if the ARP reply is received + arp_entry->pkt_pending = 1; + arp_entry->ipv4_addr = ip_dst; + memset(arp_entry->mac_addr, 0, 6); + fill_ethhdr (pending_pkt_frame, htons(ETHERTYPE_IP), + get_mac_address(), null_mac_addr); + memcpy(&pending_pkt_frame[sizeof(struct ethhdr)], + buffer, len); + pending_pkt_len = len + sizeof(struct ethhdr); + + set_timer(TICKS_SEC); + do { + receive_ether(fd); + if (!arp_entry->eth_len) + break; + } while (get_timer() > 0); + + return 0; + } + + // Send the packet with the known MAC address + fill_ethhdr(arp_entry->eth_frame, htons(ETHERTYPE_IP), + get_mac_address(), mac_addr); + memcpy(&arp_entry->eth_frame[sizeof(struct ethhdr)], buffer, len); + return send_ether(fd, arp_entry->eth_frame, len + sizeof(struct ethhdr)); +} + +/** + * IPv4: Calculate UDP checksum. Places the result into the UDP-header. + * <p> + * Use this function after filling the UDP payload. + * + * @param ipv4_hdr Points to the place where IPv4-header starts. + */ +static void fill_udp_checksum(struct iphdr *ipv4_hdr) +{ + unsigned i; + unsigned long checksum = 0; + struct iphdr ip_hdr; + char *ptr; + udp_hdr_t *udp_hdr; + + udp_hdr = (udp_hdr_t *) (ipv4_hdr + 1); + udp_hdr->uh_sum = 0; + + memset(&ip_hdr, 0, sizeof(struct iphdr)); + ip_hdr.ip_src = ipv4_hdr->ip_src; + ip_hdr.ip_dst = ipv4_hdr->ip_dst; + ip_hdr.ip_len = udp_hdr->uh_ulen; + ip_hdr.ip_p = ipv4_hdr->ip_p; + + ptr = (char*) udp_hdr; + for (i = 0; i < udp_hdr->uh_ulen; i+=2) + checksum += *((uint16_t*) &ptr[i]); + + ptr = (char*) &ip_hdr; + for (i = 0; i < sizeof(struct iphdr); i+=2) + checksum += *((uint16_t*) &ptr[i]); + + checksum = (checksum >> 16) + (checksum & 0xffff); + checksum += (checksum >> 16); + udp_hdr->uh_sum = ~checksum; + + /* As per RFC 768, if the computed checksum is zero, + * it is transmitted as all ones (the equivalent in + * one's complement arithmetic). + */ + if (udp_hdr->uh_sum == 0) + udp_hdr->uh_sum = ~udp_hdr->uh_sum; +} + +/** + * IPv4: Calculates checksum for IP header. + * + * @param packet Points to the IP-header + * @param words Size of the packet in words incl. IP-header and data. + * @return Checksum + * @see iphdr + */ +static unsigned short checksum(unsigned short * packet, int words) +{ + unsigned long checksum; + + for (checksum = 0; words > 0; words--) + checksum += *packet++; + checksum = (checksum >> 16) + (checksum & 0xffff); + checksum += (checksum >> 16); + + return ~checksum; +} + +static arp_entry_t* lookup_mac_addr(uint32_t ipv4_addr) +{ + unsigned int i; + + for(i=arp_consumer; i != arp_producer; i = ((i+1)%ARP_ENTRIES) ) { + if(arp_table[i].ipv4_addr == ipv4_addr) + return &arp_table[i]; + } + return 0; +} + + +/** + * ARP: Sends an ARP-request package. + * For given IPv4 retrieves MAC via ARP (makes several attempts) + * + * @param fd socket fd + * @param dest_ip IP of the host which MAC should be obtained + */ +static void arp_send_request(int fd, uint32_t dest_ip) +{ + arp_entry_t *arp_entry = &arp_table[arp_producer]; + + memset(arp_entry->eth_frame, 0, sizeof(struct ethhdr) + sizeof(struct arphdr)); + fill_arphdr(&arp_entry->eth_frame[sizeof(struct ethhdr)], ARP_REQUEST, + get_mac_address(), own_ip, broadcast_mac, dest_ip); + fill_ethhdr(arp_entry->eth_frame, ETHERTYPE_ARP, + get_mac_address(), broadcast_mac); + + send_ether(fd, arp_entry->eth_frame, + sizeof(struct ethhdr) + sizeof(struct arphdr)); +} + +/** + * ARP: Sends an ARP-reply package. + * This package is used to serve foreign requests (in case IP in + * foreign request matches our host IP). + * + * @param fd socket fd + * @param src_ip requester IP address (foreign IP) + * @param src_mac requester MAC address (foreign MAC) + */ +static void arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac) +{ + arp_entry_t *arp_entry = &arp_table[arp_producer]; + + memset(arp_entry->eth_frame, 0, sizeof(struct ethhdr) + sizeof(struct arphdr)); + fill_ethhdr(arp_entry->eth_frame, ETHERTYPE_ARP, + get_mac_address(), src_mac); + fill_arphdr(&arp_entry->eth_frame[sizeof(struct ethhdr)], ARP_REPLY, + get_mac_address(), own_ip, src_mac, src_ip); + + send_ether(fd, arp_entry->eth_frame, + sizeof(struct ethhdr) + sizeof(struct arphdr)); +} + +/** + * ARP: Creates ARP package. Places ARP-header in a packet and fills it + * with corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_ethhdr). + * + * @param packet Points to the place where ARP-header must be placed. + * @param opcode Identifies is it request (ARP_REQUEST) + * or reply (ARP_REPLY) package. + * @param src_mac sender MAC address + * @param src_ip sender IP address + * @param dest_mac receiver MAC address + * @param dest_ip receiver IP address + * @see arphdr + * @see fill_ethhdr + */ +static void fill_arphdr(uint8_t * packet, uint8_t opcode, + const uint8_t * src_mac, uint32_t src_ip, + const uint8_t * dest_mac, uint32_t dest_ip) +{ + struct arphdr * arph = (struct arphdr *) packet; + + arph -> hw_type = htons(1); + arph -> proto_type = htons(ETHERTYPE_IP); + arph -> hw_len = 6; + arph -> proto_len = 4; + arph -> opcode = htons(opcode); + + memcpy(arph->src_mac, src_mac, 6); + arph->src_ip = htonl(src_ip); + memcpy(arph->dest_mac, dest_mac, 6); + arph->dest_ip = htonl(dest_ip); +} + +/** + * ARP: Handles ARP-messages according to Receive-handle diagram. + * Updates arp_table for outstanding ARP requests (see arp_getmac). + * + * @param fd socket fd + * @param packet ARP-packet to be handled + * @param packetsize length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see arp_getmac + * @see receive_ether + * @see arphdr + */ +int8_t handle_arp(int fd, uint8_t * packet, uint32_t packetsize) +{ + struct arphdr * arph = (struct arphdr *) packet; + + if (packetsize < sizeof(struct arphdr)) + return -1; // Packet is too small + + if (arph -> hw_type != htons(1) || arph -> proto_type != htons(ETHERTYPE_IP)) + return -1; // Unknown hardware or unsupported protocol + + if (arph -> dest_ip != htonl(own_ip)) + return -1; // receiver IP doesn't match our IP + + switch(htons(arph -> opcode)) { + case ARP_REQUEST: + // foreign request + if(own_ip != 0) + arp_send_reply(fd, htonl(arph->src_ip), arph -> src_mac); + return 0; // no error + case ARP_REPLY: { + unsigned int i; + // if it is not for us -> return immediately + if(memcmp(get_mac_address(), arph->dest_mac, 6)) { + return 0; // no error + } + + if(arph->src_ip == 0) { + // we are not interested for a MAC address if + // the IPv4 address is 0.0.0.0 or ff.ff.ff.ff + return -1; + } + + // now let's find the corresponding entry in the ARP table + + for(i=arp_consumer; i != arp_producer; i = ((i+1)%ARP_ENTRIES) ) { + if(arp_table[i].ipv4_addr == arph->src_ip) + break; + } + if(i == arp_producer || memcmp(arp_table[i].mac_addr, null_mac_addr, 6) != 0) { + // we have not asked to resolve this IPv4 address ! + return -1; + } + + memcpy(arp_table[i].mac_addr, arph->src_mac, 6); + + // do we have something to send + if (arp_table[i].pkt_pending) { + struct ethhdr * ethh = (struct ethhdr *) pending_pkt_frame; + memcpy(ethh -> dest_mac, arp_table[i].mac_addr, 6); + + send_ether(fd, pending_pkt_frame, pending_pkt_len); + arp_table[i].pkt_pending = 0; + arp_table[i].eth_len = 0; + } + return 0; // no error + } + default: + break; + } + return -1; // Invalid message type +} + +/** + * ICMP: Send an ICMP Echo request to destination IPv4 address. + * This function does also set a global variable to the + * destination IPv4 address. If there is an ICMP Echo Reply + * received later then the variable is set back to 0. + * In other words, reading a value of 0 form this variable + * means that an answer to the request has been arrived. + * + * @param fd socket descriptor + * @param _ping_dst_ip destination IPv4 address + */ +void ping_ipv4(int fd, uint32_t _ping_dst_ip) +{ + unsigned char packet[sizeof(struct iphdr) + sizeof(struct icmphdr)]; + struct icmphdr *icmp; + + ping_dst_ip = _ping_dst_ip; + + if(ping_dst_ip == 0) + return; + + fill_iphdr(packet, sizeof(struct iphdr) + sizeof(struct icmphdr), IPTYPE_ICMP, + 0, ping_dst_ip); + icmp = (struct icmphdr *) (packet + sizeof(struct iphdr)); + icmp->type = ICMP_ECHO_REQUEST; + icmp->code = 0; + icmp->checksum = 0; + icmp->options.echo.id = 0xd476; + icmp->options.echo.seq = 1; + + memset(icmp->payload.data, '*', sizeof(icmp->payload.data)); + + icmp->checksum = + checksum((unsigned short *) icmp, sizeof(struct icmphdr) >> 1); + send_ipv4(fd, packet, sizeof(struct iphdr) + sizeof(struct icmphdr)); +} + +/** + * ICMP: Return host IPv4 address that we are waiting for a + * ICMP Echo reply message. If this value is 0 then we have + * received an reply. + * + * @return ping_dst_ip host IPv4 address + */ +uint32_t pong_ipv4(void) +{ + return ping_dst_ip; +} + +/** + * ICMP: Handles ICMP-packets according to Receive-handle diagram. + * + * @param fd socket fd + * @param icmp_packet ICMP-packet to be handled + * @param packetsize Length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see handle_ipv4 + */ +static int8_t handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, + int32_t packetsize) +{ + struct icmphdr *icmp = (struct icmphdr *) packet; + + switch(icmp->type) { + case ICMP_ECHO_REPLY: + if (icmp->options.echo.id != 0xd476) + return -1; + if (icmp->options.echo.seq != 1) + return -1; + if(ping_dst_ip != iph->ip_src + || ping_dst_ip == 0) + return -1; + ping_dst_ip = 0; + break; + case ICMP_DST_UNREACHABLE: { + // We've got Destination Unreachable msg + // Inform corresponding upper network layers + struct iphdr * bad_iph = (struct iphdr * ) &icmp->payload; + + switch(bad_iph->ip_p) { + case IPTYPE_TCP: + handle_tcp_dun((uint8_t *) (bad_iph + 1), packetsize + - sizeof(struct icmphdr) + - sizeof(struct iphdr), icmp->code); + break; + case IPTYPE_UDP: + handle_udp_dun((uint8_t *) (bad_iph + 1), packetsize + - sizeof(struct icmphdr) + - sizeof(struct iphdr), icmp->code); + break; + } + break; + } + case ICMP_SRC_QUENCH: + break; + case ICMP_REDIRECT: + break; + case ICMP_ECHO_REQUEST: { + // We've got an Echo Request - answer with Echo Replay msg + unsigned char reply_packet[sizeof(struct iphdr) + packetsize]; + struct icmphdr *reply_icmph; + + fill_iphdr(reply_packet, sizeof(struct iphdr) + packetsize, + IPTYPE_ICMP, 0, iph->ip_src); + + reply_icmph = (struct icmphdr *) &reply_packet[sizeof(struct iphdr)]; + memcpy(reply_icmph, packet, packetsize); + reply_icmph -> type = ICMP_ECHO_REPLY; + reply_icmph -> checksum = 0; + reply_icmph->checksum = checksum((unsigned short *) reply_icmph, + sizeof(struct icmphdr) >> 1); + + send_ipv4(fd, reply_packet, sizeof(struct iphdr) + packetsize); + break; + } + case ICMP_TIME_EXCEEDED: + break; + case ICMP_PARAMETER_PROBLEM: + break; + case ICMP_TIMESTAMP_REQUEST: + break; + case ICMP_TIMESTAMP_REPLY: + break; + case ICMP_INFORMATION_REQUEST: + break; + case ICMP_INFORMATION_REPLY: + break; + } + return 0; +} diff --git a/lib/libnet/ipv4.h b/lib/libnet/ipv4.h new file mode 100644 index 0000000..5717c9a --- /dev/null +++ b/lib/libnet/ipv4.h @@ -0,0 +1,97 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + + +#ifndef _IPV4_H_ +#define _IPV4_H_ + +#include <stdint.h> + +#define IPTYPE_ICMP 1 + +/** \struct iphdr + * A header for IP-packets. + * For more information see RFC 791. + */ +struct iphdr { + uint8_t ip_hlv; /**< Header length and version of the header */ + uint8_t ip_tos; /**< Type of Service */ + uint16_t ip_len; /**< Length in octets, inlc. this header and data */ + uint16_t ip_id; /**< ID is used to aid in assembling framents */ + uint16_t ip_off; /**< Info about fragmentation (control, offset) */ + uint8_t ip_ttl; /**< Time to Live */ + uint8_t ip_p; /**< Next level protocol type */ + uint16_t ip_sum; /**< Header checksum */ + uint32_t ip_src; /**< Source IP address */ + uint32_t ip_dst; /**< Destination IP address */ +}; +typedef struct iphdr ipv4_hdr_t; + +/* ICMP Error Codes */ +#define ICMP_NET_UNREACHABLE 0 +#define ICMP_HOST_UNREACHABLE 1 +#define ICMP_PROTOCOL_UNREACHABLE 2 +#define ICMP_PORT_UNREACHABLE 3 +#define ICMP_FRAGMENTATION_NEEDED 4 +#define ICMP_SOURCE_ROUTE_FAILED 5 + +/** \struct arphdr + * A header for ARP-messages, retains info about HW and proto addresses. + * For more information see RFC 826. + */ +struct arphdr { + uint16_t hw_type; /**< HW address space (1 for Ethernet) */ + uint16_t proto_type; /**< Protocol address space */ + uint8_t hw_len; /**< Byte length of each HW address */ + uint8_t proto_len; /**< Byte length of each proto address */ + uint16_t opcode; /**< Identifies is it request (1) or reply (2) */ + uint8_t src_mac[6]; /**< HW address of sender of this packet */ + uint32_t src_ip; /**< Proto address of sender of this packet */ + uint8_t dest_mac[6]; /**< HW address of target of this packet */ + uint32_t dest_ip; /**< Proto address of target of this packet */ +} __attribute((packed)); + +/************** Initialization of the IPv4 network layer. **************/ +extern void set_ipv4_address(uint32_t own_ip); +extern uint32_t get_ipv4_address(void); +extern void set_ipv4_multicast(uint32_t multicast_ip); +extern uint32_t get_ipv4_multicast(void); +extern void set_ipv4_router(uint32_t router_ip); +extern uint32_t get_ipv4_router(void); +extern void set_ipv4_netmask(uint32_t subnet_mask); +extern uint32_t get_ipv4_netmask(void); +extern uint32_t get_default_ipv4_netmask(char *ip_addr); + +extern int (*send_ip) (int fd, void *, int); + +/* fills ip header */ +extern void fill_iphdr(uint8_t * packet, uint16_t packetsize, + uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst); + +/* Send a IPv4 packet. Adding the Ethernet-Header and resolving the + * MAC address is done transparent in the background if necessary. + */ +extern int send_ipv4(int fd, void* buffer, int len); + +/* Sends an ICMP Echo request to destination IPv4 address */ +extern void ping_ipv4(int fd, uint32_t _ping_dst_ip); + +/* Returns host IPv4 address that we are waiting for a response */ +extern uint32_t pong_ipv4(void); + +/* Handles IPv4-packets that are detected by receive_ether. */ +extern int8_t handle_ipv4(int fd, uint8_t * packet, uint32_t packetsize); + +/* Handles ARP-packets that are detected by receive_ether. */ +extern int8_t handle_arp(int fd, uint8_t * packet, uint32_t packetsize); + +#endif diff --git a/lib/libnet/ipv6.c b/lib/libnet/ipv6.c new file mode 100644 index 0000000..49de5c7 --- /dev/null +++ b/lib/libnet/ipv6.c @@ -0,0 +1,775 @@ +/****************************************************************************** + * 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 <stdlib.h> +#include <stdio.h> +#include <stdbool.h> +#include <time.h> +#include <ctype.h> +#include <sys/socket.h> +#include "ethernet.h" +#include "ipv6.h" +#include "icmpv6.h" +#include "ndp.h" +#include "udp.h" + +#undef IPV6_DEBUG +//#define IPV6_DEBUG +#ifdef IPV6_DEBUG +#define dprintf(_x ...) do { printf(_x); } while (0) +#else +#define dprintf(_x ...) +#endif + +/****************************** PROTOTYPES *******************************/ +int8_t ip6addr_add (struct ip6addr_list_entry *new_address); +static void ipv6_init(int fd); +static int ip6_is_multicast (ip6_addr_t * ip); + +/****************************** LOCAL VARIABLES **************************/ + +/* List of Ipv6 Addresses */ +static struct ip6addr_list_entry *first_ip6; +static struct ip6addr_list_entry *last_ip6; + +/* Own IPv6 address */ +static struct ip6addr_list_entry *own_ip6; + +/* All nodes link-local address */ +struct ip6addr_list_entry all_nodes_ll; + +/* Null IPv6 address */ +static ip6_addr_t null_ip6; + +/* helper variables */ +static uint8_t null_mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +struct ip6_config ip6_state; + +/****************************** IMPLEMENTATION ***************************/ + +/** + * IPv6: Set the own IPv6 address. + * + * @param fd Socket descriptor + * @param _own_ip client IPv6 address (e.g. ::1) + */ +void set_ipv6_address(int fd, ip6_addr_t *_own_ip6) +{ + struct ip6addr_list_entry *ile; + + ile = malloc(sizeof(struct ip6addr_list_entry)); + if (!ile) + return; + memset(ile, 0, sizeof(struct ip6addr_list_entry)); + own_ip6 = ile; + + /* If no address was passed as a parameter generate a link-local + * address from our MAC address.*/ + if (_own_ip6 == NULL) + ip6_create_ll_address(get_mac_address(), &own_ip6->addr); + else + memcpy (&(own_ip6->addr.addr), _own_ip6, 16); + + /* Add to our list of IPv6 addresses */ + ip6addr_add (own_ip6); + + ipv6_init(fd); + + /* + * Check whether we've got a non-link-local address during + * ipv6_init() and use that as preferred address if possible + */ + if (_own_ip6 == NULL) { + for (ile = first_ip6; ile != NULL ; ile = ile->next) { + if (!ip6_is_multicast(&ile->addr) && + !ip6_is_linklocal(&ile->addr)) { + own_ip6 = ile; + break; + } + } + } +} + +/** + * IPv6: Get pointer to own IPv6 address. + * + * @return pointer to client IPv6 address (e.g. ::1) + */ +ip6_addr_t *get_ipv6_address(void) +{ + return (ip6_addr_t *) &(own_ip6->addr); +} + +/** + * IPv6: Search for IPv6 address in list + * + * @return 0 - IPv6 address is not in list + * 1 - IPv6 address is in list + */ +static int8_t find_ip6addr(ip6_addr_t *ip) +{ + struct ip6addr_list_entry *n = NULL; + + if (ip == NULL) + return 0; + + for (n = first_ip6; n != NULL ; n=n->next) + if (ip6_cmp (&(n->addr), ip)) + return 1; /* IPv6 address is in our list*/ + + return 0; /* not one of our IPv6 addresses*/ +} + +/** + * NET: Handles IPv6-packets + * + * @param fd - Socket descriptor + * @param ip6_packet - Pointer to IPv6 header + * @param packetsize - Size of Ipv6 packet + * @return ERROR - -1 if packet is too small or unknown protocol + * return value of handle_udp + * + * @see handle_udp + * @see ip6hdr + */ +int8_t handle_ipv6(int fd, uint8_t * ip6_packet, uint32_t packetsize) +{ + + struct ip6hdr *ip6 = NULL; + ip6 = (struct ip6hdr *) ip6_packet; + + /* Only handle packets which are for us */ + if (! find_ip6addr(&(ip6->dst))) + return -1; + + if (packetsize < sizeof(struct ip6hdr)) + return -1; // packet is too small + + switch (ip6->nh) { + case IPTYPE_UDP: + return handle_udp (fd, ip6_packet + sizeof (struct ip6hdr), + ip6->pl); + case IPTYPE_ICMPV6: + return handle_icmpv6 (fd, (struct ethhdr *) ip6_packet - sizeof(struct ethhdr), + ip6_packet); + } + + return -1; // unknown protocol +} + + /** + * NET: Creates IPv6-packet. Places IPv6-header in a packet and fills it + * with corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_ethhdr, fill_udphdr, fill_dnshdr, fill_btphdr). + * + * @param packet Points to the place where IPv6-header must be placed. + * @param packetsize Size of payload (i.e. excluding ethhdr and ip6hdr) + * @param ip_proto Type of the next level protocol (e.g. UDP). + * @param ip6_src Sender IPv6 address + * @param ip6_dst Receiver IPv6 address + * @see ip6hdr + * @see fill_iphdr + * @see fill_ethhdr + * @see fill_udphdr + * @see fill_dnshdr + * @see fill_btphdr + */ +void fill_ip6hdr(uint8_t * packet, uint16_t packetsize, + uint8_t ip_proto, ip6_addr_t *ip6_src, ip6_addr_t *ip6_dst) +{ + struct ip6hdr * ip6h = (struct ip6hdr *) packet; + + ip6h->ver_tc_fl = 6 << 28; // set version to 6 + ip6h->pl = packetsize; // IPv6 payload size + ip6h->nh = ip_proto; + ip6h->hl = 255; + memcpy (&(ip6h->src), ip6_src, IPV6_ADDR_LENGTH); + memcpy (&(ip6h->dst), ip6_dst, IPV6_ADDR_LENGTH); +} + +/** + * NET: For a given MAC calculates EUI64-Identifier. + * See RFC 4291 "IP Version 6 Addressing Architecture" + * + */ +uint64_t mac2eui64(const uint8_t *mac) +{ + uint8_t eui64id[8]; + uint64_t retid; + + memcpy (eui64id, mac, 3); + memcpy (eui64id + 5, mac + 3, 3); + eui64id[3] = 0xff; + eui64id[4] = 0xfe; + + memcpy(&retid, eui64id, 8); + return retid; +} + +/** + * NET: create link-local IPv6 address + * + * @param own_mac MAC of NIC + * @param ll_addr pointer to link-local address which should be created + */ +void ip6_create_ll_address(const uint8_t *own_mac, ip6_addr_t *ll_addr) +{ + ll_addr->part.prefix = IPV6_LL_PREFIX; + ll_addr->part.interface_id = mac2eui64((uint8_t *) own_mac); +} + +/* + * NET: check if we already have an address with the same prefix. + * @param struct ip6_addr_list_entry *ip6 + * @return true or false + */ +int8_t unknown_prefix(ip6_addr_t *ip) +{ + struct ip6addr_list_entry *node; + + for( node = first_ip6; node != NULL; node=node->next ) + if( node->addr.part.prefix == ip->part.prefix ) + return 0; /* address is one of ours */ + + return 1; /* prefix not yet in our list */ +} + +/* + * NET: Create empty element for prefix list and return a pointer to it; + * @return NULL - malloc failed + * ! NULL - pointer to new prefix_info + */ +struct prefix_info *ip6_create_prefix_info() +{ + struct prefix_info *prfx_info; + + prfx_info = malloc (sizeof(struct prefix_info)); + if (!prfx_info) + return NULL; + memset(prfx_info, 0, sizeof(struct prefix_info)); + + return prfx_info; +} + +/* + * NET: create a new IPv6 address with a given network prefix + * and add it to our IPv6 address list + * + * @param ip6_addr prefix (as received in RA) + * @return NULL - pointer to new ip6addr_list entry + */ +void *ip6_prefix2addr(ip6_addr_t prefix) +{ + struct ip6addr_list_entry *new_address; + uint64_t interface_id; + + new_address = malloc (sizeof(struct ip6addr_list_entry)); + if( !new_address ) + return NULL; + memset(new_address, 0, sizeof(struct ip6addr_list_entry)); + + /* fill new addr struct */ + /* extract prefix from Router Advertisement */ + memcpy (&(new_address->addr.part.prefix), &prefix, 8 ); + + /* interface id is generated from MAC address */ + interface_id = mac2eui64 (get_mac_address()); + memcpy (&(new_address->addr.part.interface_id), &interface_id, 8); + + return new_address; +} + +/** + * NET: add new IPv6 adress to list + * + * @param ip6_addr *new_address + * @return 0 - passed pointer = NULL; + * 1 - ok + */ +int8_t ip6addr_add(struct ip6addr_list_entry *new_address) +{ + struct ip6addr_list_entry *solicited_node; + + + if (new_address == NULL) + return 0; + + /* Don't add the same address twice */ + if (find_ip6addr (&(new_address->addr))) + return 0; + + /* If address is a unicast address, we also have to process packets + * for its solicited-node multicast address. + * See RFC 2373 - IP Version 6 Adressing Architecture */ + if (! ip6_is_multicast(&(new_address->addr))) { + solicited_node = malloc(sizeof(struct ip6addr_list_entry)); + if (! solicited_node) + return 0; + memset(solicited_node, 0, sizeof(struct ip6addr_list_entry)); + + solicited_node->addr.part.prefix = IPV6_SOLIC_NODE_PREFIX; + solicited_node->addr.part.interface_id = IPV6_SOLIC_NODE_IFACE_ID; + solicited_node->addr.addr[13] = new_address->addr.addr[13]; + solicited_node->addr.addr[14] = new_address->addr.addr[14]; + solicited_node->addr.addr[15] = new_address->addr.addr[15]; + ip6addr_add (solicited_node); + } + + if (first_ip6 == NULL) + first_ip6 = new_address; + else + last_ip6->next = new_address; + last_ip6 = new_address; + last_ip6->next = NULL; + + return 1; /* no error */ +} + +/** + * NET: Initialize IPv6 + * + * @param fd socket fd + */ +static void ipv6_init(int fd) +{ + int i = 0; + + send_ip = &send_ipv6; + + /* Address configuration parameters */ + ip6_state.managed_mode = 0; + + /* Null IPv6 address */ + null_ip6.part.prefix = 0; + null_ip6.part.interface_id = 0; + + /* Multicast addresses */ + all_nodes_ll.addr.part.prefix = 0xff02000000000000; + all_nodes_ll.addr.part.interface_id = 1; + ip6addr_add(&all_nodes_ll); + + ndp_init(); + + send_router_solicitation (fd); + for(i=0; i < 4 && !is_ra_received(); i++) { + set_timer(TICKS_SEC); + do { + receive_ether(fd); + if (is_ra_received()) + break; + } while (get_timer() > 0); + } +} + +/** + * NET: compare IPv6 adresses + * + * @param ip6_addr ip_1 + * @param ip6_addr ip_2 + */ +int8_t ip6_cmp(ip6_addr_t *ip_1, ip6_addr_t *ip_2) +{ + return ((int8_t) !memcmp( &(ip_1->addr[0]), &(ip_2->addr[0]), + IPV6_ADDR_LENGTH )); +} + +/** + * NET: Calculate checksum over IPv6 header and upper-layer protocol + * (e.g. UDP or ICMPv6) + * + * @param *ip - pointer to IPv6 address + * @return true or false + */ +int ip6_is_multicast(ip6_addr_t * ip) +{ + return ip->addr[0] == 0xFF; +} + +/** + * NET: Generate multicast MAC address from IPv6 address + * (e.g. UDP or ICMPv6) + * + * @param *ip - pointer to IPv6 address + * @param *mc_mac pointer to an array with 6 bytes (for the MAC address) + * @return pointer to Multicast MAC address + */ +static uint8_t *ip6_to_multicast_mac(ip6_addr_t * ip, uint8_t *mc_mac) +{ + mc_mac[0] = 0x33; + mc_mac[1] = 0x33; + memcpy (mc_mac+2, (uint8_t *) &(ip->addr)+12, 4); + + return mc_mac; +} + +/** + * Check whether an IPv6 address is on the same network as we are + */ +static bool is_ip6addr_in_my_net(ip6_addr_t *ip) +{ + struct ip6addr_list_entry *n = NULL; + + for (n = first_ip6; n != NULL; n = n->next) { + if (n->addr.part.prefix == ip->part.prefix) + return true; /* IPv6 address is in our neighborhood */ + } + + return false; /* not in our neighborhood */ +} + +/** + * NET: calculate checksum over IPv6 header and upper-layer protocol + * (e.g. UDP or ICMPv6) + * + * @param struct ip6hdr *ip6h - pointer to IPv6 header + * @param unsigned short *packet - pointer to header of upper-layer + * protocol + * @param int words - number of words (as in 2 bytes) + * starting from *packet + * @return checksum + */ +static unsigned short ip6_checksum(struct ip6hdr *ip6h, unsigned short *packet, + int words) +{ + int i=0; + unsigned long checksum; + struct ip6hdr pseudo_ip6h; + unsigned short *pip6h; + + memcpy (&pseudo_ip6h, ip6h, sizeof(struct ip6hdr)); + pseudo_ip6h.hl = ip6h->nh; + pseudo_ip6h.ver_tc_fl = 0; + pseudo_ip6h.nh = 0; + pip6h = (unsigned short *) &pseudo_ip6h; + + for (checksum = 0; words > 0; words--) { + checksum += *packet++; + i++; + } + + for (i = 0; i < 20; i++) { + checksum += *pip6h++; + } + + checksum = (checksum >> 16) + (checksum & 0xffff); + checksum += (checksum >> 16); + + return ~checksum; +} + +/** + * NET: Handles IPv6-packets + * + * @param fd socket fd + * @param ip6_packet Pointer to IPv6 header in packet + * @param packetsize Size of IPv6 packet + * @return -1 : Some error occured + * 0 : packet stored (NDP request sent - packet will be sent if + * NDP response is received) + * >0 : packet sent (number of transmitted bytes is returned) + * + * @see receive_ether + * @see ip6hdr + */ +int send_ipv6(int fd, void* buffer, int len) +{ + struct ip6hdr *ip6h; + struct udphdr *udph; + struct icmp6hdr *icmp6h; + ip6_addr_t ip_dst; + uint8_t *mac_addr, mc_mac[6]; + static uint8_t ethframe[ETH_MTU_SIZE]; + + mac_addr = null_mac; + + ip6h = (struct ip6hdr *) buffer; + udph = (struct udphdr *) ((uint8_t *) ip6h + sizeof (struct ip6hdr)); + icmp6h = (struct icmp6hdr *) ((uint8_t *) ip6h + sizeof (struct ip6hdr)); + + memcpy(&ip_dst, &ip6h->dst, 16); + + if(len + sizeof(struct ethhdr) > ETH_MTU_SIZE) + return -1; + + if ( ip6_cmp (&ip6h->src, &null_ip6)) + memcpy (&(ip6h->src), get_ipv6_address(), IPV6_ADDR_LENGTH); + + if (ip6h->nh == 17) {//UDP + udph->uh_sum = ip6_checksum (ip6h, (unsigned short *) udph , + ip6h->pl >> 1); + /* As per RFC 768, if the computed checksum is zero, + * it is transmitted as all ones (the equivalent in + * one's complement arithmetic). + */ + if (udph->uh_sum == 0) + udph->uh_sum = ~udph->uh_sum; + } + else if (ip6h->nh == 0x3a) //ICMPv6 + icmp6h->checksum = ip6_checksum (ip6h, + (unsigned short *) icmp6h, + ip6h->pl >> 1); + + if (ip6_is_multicast (&ip_dst)) { + /* If multicast, then create a proper multicast mac address */ + mac_addr = ip6_to_multicast_mac (&ip_dst, mc_mac); + } else if (!is_ip6addr_in_my_net(&ip_dst)) { + /* If server is not in same subnet, user MAC of the router */ + struct router *gw; + gw = ipv6_get_default_router(&ip6h->src); + mac_addr = gw ? gw->mac : null_mac; + } else { + /* Normal unicast, so use neighbor cache to look up MAC */ + struct neighbor *n = find_neighbor (&ip_dst); + if (n) { /* Already cached ? */ + if (memcmp(n->mac, null_mac, ETH_ALEN) != 0) + mac_addr = n->mac; /* found it */ + } else { + mac_addr = null_mac; + n = malloc(sizeof(struct neighbor)); + if (!n) + return -1; + memset(n, 0, sizeof(struct neighbor)); + memcpy(&(n->ip.addr[0]), &ip_dst, 16); + n->status = NB_PROBE; + n->times_asked += 1; + neighbor_add(n); + } + + if (! memcmp (mac_addr, &null_mac, 6)) { + if (n->eth_len == 0) { + send_neighbour_solicitation (fd, &ip_dst); + + // Store the packet until we know the MAC address + fill_ethhdr (n->eth_frame, + htons(ETHERTYPE_IPv6), + get_mac_address(), + mac_addr); + memcpy (&(n->eth_frame[sizeof(struct ethhdr)]), + buffer, len); + n->eth_len = len; + set_timer(TICKS_SEC); + do { + receive_ether(fd); + if (n->status == NB_REACHABLE) + return len; + } while (get_timer() > 0); + return 0; + } + } + } + + if (mac_addr == null_mac) + return -1; + + fill_ethhdr(ethframe, htons(ETHERTYPE_IPv6), get_mac_address(), + mac_addr); + memcpy(ðframe[sizeof(struct ethhdr)], buffer, len); + + return send_ether(fd, ethframe, len + sizeof(struct ethhdr)); +} + +static int check_colons(const char *str) +{ + char *pch, *prv; + int col = 0; + int dcol = 0; + + dprintf("str : %s\n",str); + pch = strchr(str, ':'); + while(pch != NULL){ + prv = pch; + pch = strchr(pch+1, ':'); + if((pch-prv) != 1) { + col++; + } else { + col--; /* Its part of double colon */ + dcol++; + } + } + + dprintf("The number of col : %d \n",col); + dprintf("The number of dcol : %d \n",dcol); + + if((dcol > 1) || /* Cannot have 2 "::" */ + ((dcol == 1) && (col > 5)) || /* Too many ':'s */ + ((dcol == 0) && (col != 7)) ) { /* Too few ':'s */ + dprintf(" exiting for check_colons \n"); + return 0; + } + + return (col+dcol); +} + +static int ipv6str_to_bytes(const char *str, char *ip) +{ + char block[5]; + int res; + char *pos; + uint32_t cnt = 0, len; + + dprintf("str : %s \n",str); + + while (*str != 0) { + if (cnt > 15 || !isxdigit(*str)){ + return 0; + } + if ((pos = strchr(str, ':')) != NULL) { + len = (int16_t) (pos - str); + dprintf("\t len is : %d \n",len); + if (len > 4) + return 0; + strncpy(block, str, len); + block[len] = 0; + dprintf("\t str : %s \n",str); + dprintf("\t block : %s \n",block); + str += len; + } else { + strncpy(block, str, 4); + block[4] = 0; + dprintf("\t str : %s \n",str); + dprintf("\t block : %s \n",block); + str += strlen(block); + } + res = strtol(block, NULL, 16); + dprintf("\t res : %x \n",res); + if ((res > 0xFFFF) || (res < 0)) + return 0; + ip[cnt++] = (res & 0xFF00) >> 8; + ip[cnt++] = (res & 0x00FF); + if (*str == ':'){ + str++; + } + } + + dprintf("cnt : %d\n",cnt); + return cnt; +} + +int str_to_ipv6(const char *str, uint8_t *ip) +{ + int i, k; + uint16_t len; + char *ptr; + char tmp[30], buf[16]; + + memset(ip,0,16); + + if(!check_colons(str)) + return 0; + + if ((ptr = strstr(str, "::")) != NULL) { + /* Handle the ::1 IPv6 loopback */ + if(!strcmp(str,"::1")) { + ip[15] = 1; + return 16; + } + len = (ptr-str); + dprintf(" len : %d \n",len); + if (len >= sizeof(tmp)) + return 0; + strncpy(tmp, str, len); + tmp[len] = 0; + ptr += 2; + + i = ipv6str_to_bytes(ptr, buf); + if(i == 0) + return i; + + #if defined(ARGS_DEBUG) + int j; + dprintf("=========== bottom part i : %d \n",i); + for(j=0; j<i; j++) + dprintf("%02x \t",buf[j]); + #endif + + /* Copy the bottom part i.e bytes following "::" */ + memcpy(ip+(16-i), buf, i); + + k = ipv6str_to_bytes(tmp, buf); + if(k == 0) + return k; + + #if defined(ARGS_DEBUG) + dprintf("=========== top part k : %d \n",k); + for(j=0; j<k; j++) + printf("%02x \t",buf[j]); + #endif + + /* Copy the top part i.e bytes before "::" */ + memcpy(ip, buf, k); + #if defined(ARGS_DEBUG) + dprintf("\n"); + for(j=0; j<16; j++) + dprintf("%02x \t",ip[j]); + #endif + + } else { + i = ipv6str_to_bytes(str, (char *)ip); + } + return i; +} + +void ipv6_to_str(const uint8_t *ip, char *str) +{ + int i, len; + uint8_t byte_even, byte_odd; + char *consec_zero, *strptr; + + *str = 0; + for (i = 0; i < 16; i+=2) { + byte_even = ip[i]; + byte_odd = ip[i+1]; + if (byte_even) + sprintf(str, "%s%x%02x", str, byte_even, byte_odd); + else if (byte_odd) + sprintf(str, "%s%x", str, byte_odd); + else + strcat(str, "0"); + if (i != 14) + strcat(str, ":"); + } + strptr = str; + do { + consec_zero = strstr(strptr, "0:0:"); + if (consec_zero) { + len = consec_zero - strptr; + if (!len) + break; + else if (strptr[len-1] == ':') + break; + else + strptr = consec_zero + 2; + } + } while (consec_zero); + if (consec_zero) { + len = consec_zero - str; + str[len] = 0; + if (len) + strcat(str, ":"); + else + strcat(str, "::"); + strptr = consec_zero + 4; + while (*strptr) { + if (!strncmp(strptr, "0:", 2)) + strptr += 2; + else + break; + } + strcat(str, strptr); + if (!strcmp(str, "::0")) + strcpy(str, "::"); + } +} diff --git a/lib/libnet/ipv6.h b/lib/libnet/ipv6.h new file mode 100644 index 0000000..6f783b3 --- /dev/null +++ b/lib/libnet/ipv6.h @@ -0,0 +1,188 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#ifndef _IPV6_H_ +#define _IPV6_H_ + +#include <stdint.h> +#include "ethernet.h" + +#define __IPV6_DEBUG__ + +#ifdef __IPV6_DEBUG__ +#define IPV6_DEBUG_PRINT(format, ...) do { printf(format, ## __VA_ARGS__); } while (0) +#else +#define IPV6_DEBUG_PRINT(format, ...) +#endif + +#define IPV6_ADDR_LENGTH 16 /* Size of IPv6 adress in bytes */ +#define IPV6_LL_PREFIX 0xFE80000000000000ULL +#define IPV6_LL_PREFIX_MASK 0xFFC0000000000000ULL +#define IPV6_SOLIC_NODE_PREFIX 0xFF02000000000000ULL +#define IPV6_SOLIC_NODE_IFACE_ID 0x00000001FF000000ULL + +/** + * An IPv6 Address + */ +typedef union { + uint8_t addr[IPV6_ADDR_LENGTH]; + struct { + uint64_t prefix; + uint64_t interface_id; + } part; +} ip6_addr_t; + +typedef struct { + uint8_t type; + uint8_t pad[7]; + union { + ip6_addr_t v6; + char v4[4]; + } addr; +} netaddr_t; + +/** \struct prefix_info + * + * List of Prefixes we have adresses from + * Used for internal purposes, information derived from prefix option + * in Router Advertisements + * See RFC 4861 section 4.6.2 + */ +struct prefix_info { + uint64_t prefix; + uint8_t on_link:1, /* When set prefix can be used for on-link + * determination */ + autoconf:1, /* Prefix can be used for stateless address + * configuration */ + reserved1:6; + uint32_t valid_lifetime; /* Time until prefix expires */ + uint32_t preferred_lifetime; /* Time until prefix becomes deprecated */ + uint32_t start_time; /* Time when received */ + uint32_t reserved2; + struct prefix_info *next; +}; + + +/* List of IPv6 addresses */ +struct ip6addr_list_entry { + ip6_addr_t addr; + struct prefix_info prfx_info; + struct ip6addr_list_entry *next; +}; + +/** \struct ip6hdr + * A header for IPv6 packets. + * For more information see RFC 2460 + */ +struct ip6hdr { + uint32_t ver_tc_fl; /**< Version, Traffic class, Flow label */ + uint16_t pl; /**< Payload length */ + uint8_t nh; /**< Next header */ + uint8_t hl; /**< Hop limit */ + ip6_addr_t src; /**< IPv6 source address */ + ip6_addr_t dst; /**< IPv6 destination address */ +} __attribute((packed)); + +/** \struct packeth + * Struct with pointers to headers within a packet + */ +struct packeth { + struct ethhdr *ethh; + struct ip6hdr *ip6h; + struct icmp6hdr *icmp6h; + struct udphdr *udph; + /* ... */ +}; + +/** \struct parseip6_state + * Stores information about state of IPv6 address parser + */ +struct parseip6_state { + char *lookahead; + char *ptr; + const char *addr; + int state; + int s1ctr; + int s2ctr; + int blocknr; + int zeroblocks; + int i; + int done; + int errorcode; +}; + +/** \struct ip6_config + * Stores flags wheter we use Stateless- or Stateful Autoconfiguration or DHCPv6 + */ +struct ip6_config { + uint8_t managed_mode:1, + other_config:1, + reserved:6; +}; + +/******************** VARIABLES **********************************************/ +/* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */ +extern int (*send_ip) (int fd, void *, int); + +extern struct ip6_config ip6_state; + +/******************** FUNCTIONS *********************************************/ +/* Handles IPv6-packets that are detected by receive_ether. */ +int8_t handle_ipv6(int fd, uint8_t * ip6_packet, uint32_t packetsize); + +/* Fill IPv6 header */ +void fill_ip6hdr(uint8_t * packet, uint16_t packetsize, + uint8_t ip_proto, ip6_addr_t *ip6_src, ip6_addr_t *ip6_dst); + +/* Set own IPv6 address */ +void set_ipv6_address(int fd, ip6_addr_t *own_ip6); + +/* Get own IPv6 address */ +ip6_addr_t *get_ipv6_address(void); + +/* Create link-local address from a given Mac Address */ +void ip6_create_ll_address (const uint8_t *own_mac, ip6_addr_t *ll_addr); + +/* For a given MAC calculates EUI64-Identifier.*/ +uint64_t mac2eui64 (const uint8_t *mac); + +/* Create empty element for prefix list and return a pointer to it */ +struct prefix_info * ip6_create_prefix_info(void); + +/* Create a new IPv6 address with a given network prefix + * and add it to our IPv6 address list */ +void * ip6_prefix2addr (ip6_addr_t prefix); + +/* Compare IPv6 adresses */ +int8_t ip6_cmp( ip6_addr_t *ip_1, ip6_addr_t *ip_2 ); + +/* Check if it is a link-local address */ +static inline int ip6_is_linklocal(ip6_addr_t *ip) +{ + return (ip->part.prefix & IPV6_LL_PREFIX_MASK) == IPV6_LL_PREFIX; +} + +/* Check if prefix is already in our list */ +int8_t unknown_prefix (ip6_addr_t *ip); + +/* Send IPv6 packet */ +int send_ipv6 (int fd, void* buffer, int len); + +/* Add IPv6 address to list */ +int8_t ip6addr_add (struct ip6addr_list_entry *new_address); + +/* Parse an IPv6 address */ +int parseip6(const char *addr, uint8_t *parsedaddr); +int str_to_ipv6(const char *str, uint8_t *ip); +void ipv6_to_str(const uint8_t *ip, char *str); + +#endif diff --git a/lib/libnet/ndp.c b/lib/libnet/ndp.c new file mode 100644 index 0000000..f1f51c7 --- /dev/null +++ b/lib/libnet/ndp.c @@ -0,0 +1,184 @@ +/****************************************************************************** + * 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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "ipv6.h" +#include "icmpv6.h" +#include "ndp.h" + +/* Neighbor cache */ +static struct neighbor *first_neighbor; +static struct neighbor *last_neighbor; + +/* Router list */ +static struct router *first_router; +static struct router *last_router; + +/* + * NET: add new router to list + * @param struct router nghb - new router + * @return true or false + */ +int8_t +router_add (struct router *nghb ) +{ + if (nghb == NULL) + return -1; + + if (first_router == NULL) { + first_router= nghb; + last_router = first_router; + last_router->next = NULL; + } else { + last_router->next = nghb; + last_router = nghb; + last_router->next = NULL; + } + return 1; /* no error */ +} + +/* + * NET: create a new router + * @param uint8_t *packet - received packet (Ethernet/IPv6/ICMPv6/ND_NghSlct) + * @param struct packeth - pointers to headers in packet + * @return pointer to new router + */ +void * +router_create (uint8_t *mac, ip6_addr_t *ip) +{ + struct router *new_router; + + new_router = malloc (sizeof(struct router)); + if( !new_router) { + return 0; + } + memset (new_router, 0, sizeof(struct router)); + + /* fill neighbor struct */ + memcpy (new_router->mac, mac, 6); + memcpy (&(new_router->ip.addr[0]), &(ip->addr[0]), IPV6_ADDR_LENGTH); + + return new_router; +} + +struct router * +find_router( ip6_addr_t *ip ) +{ + struct router *n = NULL; + + for (n = first_router; n != NULL ; n=n->next) + if (ip6_cmp (&(n->ip), ip)) + return n; /* router is already in list*/ + + return NULL; /* router is unknown */ +} + +/** + * Find a router for a given host address + * @param ip - IPv6 address with the prefered prefix + * @return pointer to router, or NULL if none is available + */ +struct router *ipv6_get_default_router(ip6_addr_t *ip) +{ + struct router *n = NULL; + + for (n = first_router; n != NULL; n = n->next) { + if (n->ip.part.prefix == ip->part.prefix) + return n; + } + + return first_router; +} + +/* + * NET: add new neighbor to list + * @param struct neighbor nghb - new neighbor + * @return true or false + */ +int8_t +neighbor_add (struct neighbor *nghb) +{ + if (nghb == NULL) + return -1; + + if (first_neighbor == NULL) { + first_neighbor = nghb; + last_neighbor = first_neighbor; + last_neighbor->next = NULL; + } else { + last_neighbor->next = nghb; + last_neighbor = nghb; + last_neighbor->next = NULL; + } + + return 1; /* no error */ +} + +/* + * NET: create a new neighbor + * @param uint8_t *packet - received packet (Ethernet/IPv6/ICMPv6/ND_NghSlct) + * @param struct packeth - pointers to headers in packet + * @return pointer - pointer to new neighbor + * NULL - malloc failed + */ +void * +neighbor_create (uint8_t *packet, struct packeth *headers) +{ + struct neighbor *new_neighbor; + + new_neighbor = malloc (sizeof(struct neighbor)); + if( !new_neighbor ) + return NULL; + memset(new_neighbor, 0, sizeof(struct neighbor)); + + /* fill neighbor struct */ + memcpy (&(new_neighbor->mac), + &(headers->ethh->src_mac[0]), 6); + memcpy (&(new_neighbor->ip.addr), &(headers->ip6h->src), IPV6_ADDR_LENGTH); + new_neighbor->status = NB_INCOMPLETE; + + return new_neighbor; +} + +/** + * NET: Find neighbor with given IPv6 address in Neighbor Cache + * + * @param ip - Pointer to IPv6 address + * @return pointer - pointer to client IPv6 address (e.g. ::1) + * NULL - Neighbor not found + */ +struct neighbor * +find_neighbor (ip6_addr_t *ip) +{ + struct neighbor *n = NULL; + + for (n = first_neighbor; n != NULL ; n=n->next) { + if (ip6_cmp (&(n->ip), ip)) { + return n; /* neighbor is already in cache */ + } + } + + return NULL; /* neighbor is unknown */ +} + +void ndp_init(void) +{ + /* Router list */ + first_router = NULL; + last_router = first_router; + + /* Init Neighbour cache */ + first_neighbor = NULL; + last_neighbor = first_neighbor; +} diff --git a/lib/libnet/ndp.h b/lib/libnet/ndp.h new file mode 100644 index 0000000..cd18158 --- /dev/null +++ b/lib/libnet/ndp.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#ifndef _NDP_H_ +#define _NDP_H_ + +#include "ipv6.h" + +#define __NDP_DEBUG__ + +#ifdef __NDP_DEBUG__ +#define NDP_DEBUG_PRINT(format, ...) do { printf(format, ## __VA_ARGS__); } while (0) +#else +#define NDP_DEBUG_PRINT(format, ...) +#endif + +#define ND_OPTION_SOURCE_LL_ADDR 1 +#define ND_OPTION_TARGET_LL_ADDR 2 +#define ND_OPTION_PREFIX_INFO 3 +#define ND_OPTION_REDIRECT_HDR 4 +#define ND_OPTION_MTU 5 + +/* Default Router List */ +struct router { + uint8_t mac[6]; + ip6_addr_t ip; + uint32_t lifetime; + uint32_t reachable_time; + uint32_t retrans_timer; + struct router *next; +}; + +/* Neighbor cache */ +struct neighbor { + uint8_t mac[6]; + ip6_addr_t ip; + uint8_t is_router; + uint8_t status; + uint8_t times_asked; + /* ... */ + struct neighbor *next; + uint8_t eth_frame[ETH_MTU_SIZE]; + uint32_t eth_len; + +#define NB_INCOMPLETE 1 +#define NB_REACHABLE 2 +#define NB_STALE 3 +#define NB_DELAY 4 +#define NB_PROBE 5 +}; + +/******************** FUNCTIONS *********************************************/ +void ndp_init(void); +int8_t neighbor_add (struct neighbor *); +void * neighbor_create (uint8_t *packet, struct packeth *headers); +struct neighbor * find_neighbor (ip6_addr_t *); + +int8_t router_add(struct router*); +void * router_create(uint8_t *mac, ip6_addr_t *ip); +struct router * find_router(ip6_addr_t *); +struct router *ipv6_get_default_router(ip6_addr_t *ip); + +#endif //_NDP_H_ diff --git a/lib/libnet/tcp.c b/lib/libnet/tcp.c new file mode 100644 index 0000000..faa0b83 --- /dev/null +++ b/lib/libnet/tcp.c @@ -0,0 +1,46 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + +/************************ DEFINITIONS & DECLARATIONS *********************/ + +#include <tcp.h> +#include <sys/socket.h> + +/****************************** LOCAL VARIABLES **************************/ + +/****************************** IMPLEMENTATION ***************************/ + +/** + * TCP: Handles TCP-packets according to Receive-handle diagram. + * + * @param tcp_packet TCP-packet to be handled + * @param packetsize Length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + */ +int8_t handle_tcp(uint8_t * tcp_packet, int32_t packetsize) +{ + return -1; +} + +/** + * NET: This function handles situation when "Destination unreachable" + * ICMP-error occurs during sending TCP-packet. + * + * @param err_code Error Code (e.g. "Host unreachable") + * @param packet original TCP-packet + * @param packetsize length of the packet + * @see handle_icmp + */ +void handle_tcp_dun(uint8_t * tcp_packet, uint32_t packetsize, uint8_t err_code) +{ +} diff --git a/lib/libnet/tcp.h b/lib/libnet/tcp.h new file mode 100644 index 0000000..375afd7 --- /dev/null +++ b/lib/libnet/tcp.h @@ -0,0 +1,27 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + +#ifndef _TCP_H +#define _TCP_H + +#include <stdint.h> + +#define IPTYPE_TCP 6 + +/* Handles TCP-packets that are detected by any network layer. */ +extern int8_t handle_tcp(uint8_t * udp_packet, int32_t packetsize); + +/* Handles TCP related ICMP-Dest.Unreachable packets that are detected by + * the network layers. */ +extern void handle_tcp_dun(uint8_t * tcp_packet, uint32_t packetsize, uint8_t err_code); + +#endif diff --git a/lib/libnet/tftp.c b/lib/libnet/tftp.c new file mode 100644 index 0000000..c1197cf --- /dev/null +++ b/lib/libnet/tftp.c @@ -0,0 +1,586 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 <tftp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/socket.h> + +#include <ethernet.h> +#include <ipv4.h> +#include <ipv6.h> +#include <udp.h> + +//#define __DEBUG__ + +#define MAX_BLOCKSIZE 1428 +#define BUFFER_LEN 256 + +#define ENOTFOUND 1 +#define EACCESS 2 +#define EBADOP 4 +#define EBADID 5 +#define ENOUSER 7 +//#define EUNDEF 0 +//#define ENOSPACE 3 +//#define EEXISTS 6 + +#define RRQ 1 +#define WRQ 2 +#define DATA 3 +#define ACK 4 +#define ERROR 5 +#define OACK 6 + +/* Local variables */ +static unsigned char packet[BUFFER_LEN]; +static unsigned char *buffer = NULL; +static unsigned short block = 0; +static unsigned short blocksize; +static char blocksize_str[6]; /* Blocksize string for read request */ +static int received_len = 0; +static unsigned int retries = 0; +static int huge_load; +static int len; +static int tftp_finished = 0; +static int lost_packets = 0; +static int tftp_errno = 0; +static int ip_version = 0; +static short port_number = -1; +static tftp_err_t *tftp_err; +static filename_ip_t *fn_ip; + +/** + * dump_package - Prints a package. + * + * @package: package which is to print + * @len: length of the package + */ +#ifdef __DEBUG__ + +static void dump_package(unsigned char *buffer, unsigned int len) +{ + int i; + + for (i = 1; i <= len; i++) { + printf("%02x%02x ", buffer[i - 1], buffer[i]); + i++; + if ((i % 16) == 0) + printf("\n"); + } + printf("\n"); +} +#endif + +/** + * send_rrq - Sends a read request package. + * + * @fd: Socket Descriptor + */ +static void send_rrq(int fd) +{ + int ip_len = 0; + int ip6_payload_len = 0; + unsigned short udp_len = 0; + unsigned char mode[] = "octet"; + char *ptr = NULL; + struct iphdr *ip = NULL; + struct ip6hdr *ip6 = NULL; + struct udphdr *udph = NULL; + struct tftphdr *tftp = NULL; + + memset(packet, 0, BUFFER_LEN); + + if (4 == ip_version) { + ip = (struct iphdr *) packet; + udph = (struct udphdr *) (ip + 1); + ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 + + strlen("blksize") + strlen(blocksize_str) + 2; + fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, + fn_ip->server_ip); + } + else if (6 == ip_version) { + ip6 = (struct ip6hdr *) packet; + udph = (struct udphdr *) (ip6 + 1); + ip6_payload_len = sizeof(struct udphdr) + + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 + + strlen("blksize") + strlen(blocksize_str) + 2; + ip_len = sizeof(struct ip6hdr) + ip6_payload_len; + fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), + &(fn_ip->server_ip6)); + + } + udp_len = htons(sizeof(struct udphdr) + + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 + + strlen("blksize") + strlen(blocksize_str) + 2); + fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(69)); + + tftp = (struct tftphdr *) (udph + 1); + tftp->th_opcode = htons(RRQ); + + ptr = (char *) &tftp->th_data; + memcpy(ptr, fn_ip->filename, strlen((char *) fn_ip->filename) + 1); + + ptr += strlen((char *) fn_ip->filename) + 1; + memcpy(ptr, mode, strlen((char *) mode) + 1); + + ptr += strlen((char *) mode) + 1; + memcpy(ptr, "blksize", strlen("blksize") + 1); + + ptr += strlen("blksize") + 1; + memcpy(ptr, blocksize_str, strlen(blocksize_str) + 1); + + send_ip (fd, packet, ip_len); + +#ifdef __DEBUG__ + printf("tftp RRQ with %d bytes transmitted.\n", ip_len); +#endif + return; +} + +/** + * send_ack - Sends a acknowlege package. + * + * @blckno: block number + * @dport: UDP destination port + */ +static void send_ack(int fd, int blckno, unsigned short dport) +{ + int ip_len = 0; + int ip6_payload_len = 0; + unsigned short udp_len = 0; + struct iphdr *ip = NULL; + struct ip6hdr *ip6 = NULL; + struct udphdr *udph = NULL; + struct tftphdr *tftp = NULL; + + memset(packet, 0, BUFFER_LEN); + + if (4 == ip_version) { + ip = (struct iphdr *) packet; + udph = (struct udphdr *) (ip + 1); + ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 4; + fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, + fn_ip->server_ip); + } + else if (6 == ip_version) { + ip6 = (struct ip6hdr *) packet; + udph = (struct udphdr *) (ip6 + 1); + ip6_payload_len = sizeof(struct udphdr) + 4; + ip_len = sizeof(struct ip6hdr) + ip6_payload_len; + fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), + &(fn_ip->server_ip6)); + } + udp_len = htons(sizeof(struct udphdr) + 4); + fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport)); + + tftp = (struct tftphdr *) (udph + 1); + tftp->th_opcode = htons(ACK); + tftp->th_data = htons(blckno); + + send_ip(fd, packet, ip_len); + +#ifdef __DEBUG__ + printf("tftp ACK %d bytes transmitted.\n", ip_len); +#endif + + return; +} + +/** + * send_error - Sends an error package. + * + * @fd: Socket Descriptor + * @error_code: Used sub code for error packet + * @dport: UDP destination port + */ +static void send_error(int fd, int error_code, unsigned short dport) +{ + int ip_len = 0; + int ip6_payload_len = 0; + unsigned short udp_len = 0; + struct ip6hdr *ip6 = NULL; + struct iphdr *ip = NULL; + struct udphdr *udph = NULL; + struct tftphdr *tftp = NULL; + + memset(packet, 0, BUFFER_LEN); + + if (4 == ip_version) { + ip = (struct iphdr *) packet; + udph = (struct udphdr *) (ip + 1); + ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 5; + fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, + fn_ip->server_ip); + } + else if (6 == ip_version) { + ip6 = (struct ip6hdr *) packet; + udph = (struct udphdr *) (ip6 + 1); + ip6_payload_len = sizeof(struct udphdr) + 5; + ip_len = sizeof(struct ip6hdr) + ip6_payload_len; + fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), + &(fn_ip->server_ip6)); + } + udp_len = htons(sizeof(struct udphdr) + 5); + fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport)); + + tftp = (struct tftphdr *) (udph + 1); + tftp->th_opcode = htons(ERROR); + tftp->th_data = htons(error_code); + ((char *) &tftp->th_data)[2] = 0; + + send_ip(fd, packet, ip_len); + +#ifdef __DEBUG__ + printf("tftp ERROR %d bytes transmitted.\n", ip_len); +#endif + + return; +} + +static void print_progress(int urgent, int received_bytes) +{ + static unsigned int i = 1; + static int first = -1; + static int last_bytes = 0; + char buffer[100]; + char *ptr; + + // 1MB steps or 0x400 times or urgent + if(((received_bytes - last_bytes) >> 20) > 0 + || (i & 0x3FF) == 0 || urgent) { + if(!first) { + sprintf(buffer, "%d KBytes", (last_bytes >> 10)); + for(ptr = buffer; *ptr != 0; ++ptr) + *ptr = '\b'; + printf(buffer); + } + printf("%d KBytes", (received_bytes >> 10)); + i = 1; + first = 0; + last_bytes = received_bytes; + } + ++i; +} + +/** + * get_blksize tries to extract the blksize from the OACK package + * the TFTP returned. From RFC 1782 + * The OACK packet has the following format: + * + * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ + * | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | + * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ + * + * @param buffer the network packet + * @param len the length of the network packet + * @return the blocksize the server supports or 0 for error + */ +static int get_blksize(unsigned char *buffer, unsigned int len) +{ + unsigned char *orig = buffer; + /* skip all headers until tftp has been reached */ + buffer += sizeof(struct udphdr); + /* skip opc */ + buffer += 2; + while (buffer < orig + len) { + if (!memcmp(buffer, "blksize", strlen("blksize") + 1)) + return (unsigned short) strtoul((char *) (buffer + + strlen("blksize") + 1), + (char **) NULL, 10); + else { + /* skip the option name */ + buffer = (unsigned char *) strchr((char *) buffer, 0); + if (!buffer) + return 0; + buffer++; + /* skip the option value */ + buffer = (unsigned char *) strchr((char *) buffer, 0); + if (!buffer) + return 0; + buffer++; + } + } + return 0; +} + +/** + * Handle incoming tftp packets after read request was sent + * + * this function also prints out some status characters + * \|-/ for each packet received + * A for an arp packet + * I for an ICMP packet + * #+* for different unexpected TFTP packets (not very good) + * + * @param fd socket descriptor + * @param packet points to the UDP header of the packet + * @param len the length of the network packet + * @return ZERO if packet was handled successfully + * ERRORCODE if error occurred + */ +int32_t handle_tftp(int fd, uint8_t *pkt, int32_t packetsize) +{ + struct udphdr *udph; + struct tftphdr *tftp; + + /* buffer is only set if we are handling TFTP */ + if (buffer == NULL ) + return 0; + +#ifndef __DEBUG__ + print_progress(0, received_len); +#endif + udph = (struct udphdr *) pkt; + tftp = (struct tftphdr *) ((void *) udph + sizeof(struct udphdr)); + set_timer(TICKS_SEC); + +#ifdef __DEBUG__ + dump_package(pkt, packetsize); +#endif + + port_number = udph->uh_sport; + if (tftp->th_opcode == htons(OACK)) { + /* an OACK means that the server answers our blocksize request */ + blocksize = get_blksize(pkt, packetsize); + if (!blocksize || blocksize > MAX_BLOCKSIZE) { + send_error(fd, 8, port_number); + tftp_errno = -8; + goto error; + } + send_ack(fd, 0, port_number); + } else if (tftp->th_opcode == htons(ACK)) { + /* an ACK means that the server did not answers + * our blocksize request, therefore we will set the blocksize + * to the default value of 512 */ + blocksize = 512; + send_ack(fd, 0, port_number); + } else if ((unsigned char) tftp->th_opcode == ERROR) { +#ifdef __DEBUG__ + printf("tftp->th_opcode : %x\n", tftp->th_opcode); + printf("tftp->th_data : %x\n", tftp->th_data); +#endif + switch ( (uint8_t) tftp->th_data) { + case ENOTFOUND: + tftp_errno = -3; // ERROR: file not found + break; + case EACCESS: + tftp_errno = -4; // ERROR: access violation + break; + case EBADOP: + tftp_errno = -5; // ERROR: illegal TFTP operation + break; + case EBADID: + tftp_errno = -6; // ERROR: unknown transfer ID + break; + case ENOUSER: + tftp_errno = -7; // ERROR: no such user + break; + default: + tftp_errno = -1; // ERROR: unknown error + } + goto error; + } else if (tftp->th_opcode == DATA) { + /* DATA PACKAGE */ + if (block + 1 == tftp->th_data) { + ++block; + } + else if( block == 0xffff && huge_load != 0 + && (tftp->th_data == 0 || tftp->th_data == 1) ) { + block = tftp->th_data; + } + else if (tftp->th_data == block) { +#ifdef __DEBUG__ + printf + ("\nTFTP: Received block %x, expected block was %x\n", + tftp->th_data, block + 1); + printf("\b+ "); +#endif + send_ack(fd, tftp->th_data, port_number); + lost_packets++; + tftp_err->bad_tftp_packets++; + return 0; + } else if (tftp->th_data < block) { +#ifdef __DEBUG__ + printf + ("\nTFTP: Received block %x, expected block was %x\n", + tftp->th_data, block + 1); + printf("\b* "); +#endif + /* This means that an old data packet appears (again); + * this happens sometimes if we don't answer fast enough + * and a timeout is generated on the server side; + * as we already have this packet we just ignore it */ + tftp_err->bad_tftp_packets++; + return 0; + } else { + tftp_err->blocks_missed = block + 1; + tftp_err->blocks_received = tftp->th_data; + tftp_errno = -42; + goto error; + } + tftp_err->bad_tftp_packets = 0; + /* check if our buffer is large enough */ + if (received_len + udph->uh_ulen - 12 > len) { + tftp_errno = -2; + goto error; + } + memcpy(buffer + received_len, &tftp->th_data + 1, + udph->uh_ulen - 12); + send_ack(fd, tftp->th_data, port_number); + received_len += udph->uh_ulen - 12; + /* Last packet reached if the payload of the UDP packet + * is smaller than blocksize + 12 + * 12 = UDP header (8) + 4 bytes TFTP payload */ + if (udph->uh_ulen < blocksize + 12) { + tftp_finished = 1; + return 0; + } + /* 0xffff is the highest block number possible + * see the TFTP RFCs */ + + if (block >= 0xffff && huge_load == 0) { + tftp_errno = -9; + goto error; + } + } else { +#ifdef __DEBUG__ + printf("Unknown packet %x\n", tftp->th_opcode); + printf("\b# "); +#endif + tftp_err->bad_tftp_packets++; + return 0; + } + + return 0; + +error: +#ifdef __DEBUG__ + printf("\nTFTP errno: %d\n", tftp_errno); +#endif + tftp_finished = 1; + return tftp_errno; +} + +/** + * TFTP: This function handles situation when "Destination unreachable" + * ICMP-error occurs during sending TFTP-packet. + * + * @param err_code Error Code (e.g. "Host unreachable") + */ +void handle_tftp_dun(uint8_t err_code) +{ + tftp_errno = - err_code - 10; + tftp_finished = 1; +} + +/** + * TFTP: Interface function to load files via TFTP. + * + * @param _fn_ip contains the following configuration information: + * client IP, TFTP-server IP, filename to be loaded + * @param _buffer destination buffer for the file + * @param _len size of destination buffer + * @param _retries max number of retries + * @param _tftp_err contains info about TFTP-errors (e.g. lost packets) + * @param _mode NON ZERO - multicast, ZERO - unicast + * @param _blocksize blocksize for DATA-packets + * @return ZERO - error condition occurs + * NON ZERO - size of received file + */ +int tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len, + unsigned int _retries, tftp_err_t * _tftp_err, + int32_t _mode, int32_t _blocksize, int _ip_version) +{ + retries = _retries; + fn_ip = _fn_ip; + len = _len; + huge_load = _mode; + ip_version = _ip_version; + tftp_errno = 0; + tftp_err = _tftp_err; + tftp_err->bad_tftp_packets = 0; + tftp_err->no_packets = 0; + + /* Default blocksize must be 512 for TFTP servers + * which do not support the RRQ blocksize option */ + blocksize = 512; + + /* Preferred blocksize - used as option for the read request */ + if (_blocksize < 8) + _blocksize = 8; + else if (_blocksize > MAX_BLOCKSIZE) + _blocksize = MAX_BLOCKSIZE; + sprintf(blocksize_str, "%d", _blocksize); + + printf(" Receiving data: "); + print_progress(-1, 0); + + // Setting buffer to a non-zero address enabled handling of received TFTP packets. + buffer = _buffer; + + set_timer(TICKS_SEC); + send_rrq(fn_ip->fd); + + while (! tftp_finished) { + /* if timeout (no packet received) */ + if(get_timer() <= 0) { + /* the server doesn't seem to retry let's help out a bit */ + if (tftp_err->no_packets > 4 && port_number != -1 + && block > 1) { + send_ack(fn_ip->fd, block, port_number); + } + else if (port_number == -1 && block == 0 + && (tftp_err->no_packets&3) == 3) { + printf("\nRepeating TFTP read request...\n"); + send_rrq(fn_ip->fd); + } + tftp_err->no_packets++; + set_timer(TICKS_SEC); + } + + /* handle received packets */ + receive_ether(fn_ip->fd); + + /* bad_tftp_packets are counted whenever we receive a TFTP packet + * which was not expected; if this gets larger than 'retries' + * we just exit */ + if (tftp_err->bad_tftp_packets > retries) { + tftp_errno = -40; + break; + } + + /* no_packets counts the times we have returned from receive_ether() + * without any packet received; if this gets larger than 'retries' + * we also just exit */ + if (tftp_err->no_packets > retries) { + tftp_errno = -41; + break; + } + } + + // Setting buffer to NULL disables handling of received TFTP packets. + buffer = NULL; + + if (tftp_errno) + return tftp_errno; + + print_progress(-1, received_len); + printf("\n"); + if (lost_packets) + printf("Lost ACK packets: %d\n", lost_packets); + + return received_len; +} diff --git a/lib/libnet/tftp.h b/lib/libnet/tftp.h new file mode 100644 index 0000000..6b5996c --- /dev/null +++ b/lib/libnet/tftp.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + + +#ifndef _TFTP_H_ +#define _TFTP_H_ + +#include <stdint.h> +#include "ipv6.h" + +struct tftphdr { + int16_t th_opcode; + uint16_t th_data; +}; + +typedef struct { + uint32_t own_ip; + ip6_addr_t own_ip6; + uint32_t server_ip; + ip6_addr_t server_ip6; + ip6_addr_t dns_ip6; + int8_t filename[256]; + int fd; +} __attribute__ ((packed)) filename_ip_t ; + +typedef struct { + uint32_t bad_tftp_packets; + uint32_t no_packets; + uint32_t blocks_missed; + uint32_t blocks_received; +} tftp_err_t; + +int tftp(filename_ip_t *, unsigned char *, int, unsigned int, + tftp_err_t *, int32_t mode, int32_t blocksize, int ip_version); +int tftp_netsave(filename_ip_t *, uint8_t * buffer, int len, + int use_ci, unsigned int retries, tftp_err_t * tftp_err); + +int32_t handle_tftp(int fd, uint8_t *, int32_t); +void handle_tftp_dun(uint8_t err_code); +int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd, int len); + +#endif diff --git a/lib/libnet/udp.c b/lib/libnet/udp.c new file mode 100644 index 0000000..1203947 --- /dev/null +++ b/lib/libnet/udp.c @@ -0,0 +1,153 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + +/************************ DEFINITIONS & DECLARATIONS *********************/ + +#include <udp.h> +#include <sys/socket.h> +#include <dhcp.h> +#include <dhcpv6.h> +#include <dns.h> +#ifdef USE_MTFTP +#include <mtftp.h> +#else +#include <tftp.h> +#endif + + + +/****************************** LOCAL VARIABLES **************************/ + + +#ifdef USE_MTFTP + +uint16_t net_tftp_uport; +uint16_t net_mtftp_uport; + +void net_set_tftp_port(uint16_t tftp_port) +{ + net_tftp_uport = tftp_port; +} + +void net_set_mtftp_port(uint16_t tftp_port) +{ + net_mtftp_uport = tftp_port; +} + +#endif + +/****************************** IMPLEMENTATION ***************************/ + + +/** + * NET: Handles UDP-packets according to Receive-handle diagram. + * + * @param udp_packet UDP-packet to be handled + * @param packetsize Length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see receive_ether + * @see udphdr + */ +int8_t handle_udp(int fd, uint8_t * udp_packet, uint32_t packetsize) +{ + struct udphdr * udph = (struct udphdr *) udp_packet; + + if (packetsize < sizeof(struct udphdr)) + return -1; // packet is too small + + switch (htons(udph -> uh_dport)) { + case UDPPORT_BOOTPC: + if (udph -> uh_sport == htons(UDPPORT_BOOTPS)) + return handle_dhcp(fd, udp_packet + sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); + else + return -1; + case UDPPORT_DNSC: + if (udph -> uh_sport == htons(UDPPORT_DNSS)) + return handle_dns(udp_packet + sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); + else + return -1; + case UDPPORT_DHCPV6C: + return handle_dhcpv6(udp_packet+sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); + case UDPPORT_TFTPC: +#ifdef USE_MTFTP + return handle_tftp(fd, udp_packet + sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); +#else + return handle_tftp(fd, udp_packet, packetsize); +#endif + default: +#ifdef USE_MTFTP + if (htons(udph -> uh_dport) == net_tftp_uport) + return handle_tftp(fd, udp_packet + sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); + else if (htons(udph -> uh_dport) == net_mtftp_uport) + return handle_tftp(fd, udp_packet + sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); +#endif + return -1; + } +} + +/** + * NET: This function handles situation when "Destination unreachable" + * ICMP-error occurs during sending UDP-packet. + * + * @param err_code Error Code (e.g. "Host unreachable") + * @param packet original UDP-packet + * @param packetsize length of the packet + * @see handle_icmp + */ +void handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t err_code) +{ + struct udphdr * udph = (struct udphdr *) udp_packet; + + if (packetsize < sizeof(struct udphdr)) + return; // packet is too small + + switch (htons(udph -> uh_sport)) { + case UDPPORT_TFTPC: + handle_tftp_dun(err_code); + break; + } +} + +/** + * NET: Creates UDP-packet. Places UDP-header in a packet and fills it + * with corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_ethhdr, fill_iphdr, fill_dnshdr, fill_btphdr). + * + * @param packet Points to the place where UDP-header must be placed. + * @param packetsize Size of the packet in bytes incl. this hdr and data. + * @param src_port UDP source port + * @param dest_port UDP destination port + * @see udphdr + * @see fill_ethhdr + * @see fill_iphdr + * @see fill_dnshdr + * @see fill_btphdr + */ +void fill_udphdr(uint8_t * packet, uint16_t packetsize, + uint16_t src_port, uint16_t dest_port) +{ + struct udphdr * udph = (struct udphdr *) packet; + + udph -> uh_sport = htons(src_port); + udph -> uh_dport = htons(dest_port); + udph -> uh_ulen = htons(packetsize); + udph -> uh_sum = htons(0); +} diff --git a/lib/libnet/udp.h b/lib/libnet/udp.h new file mode 100644 index 0000000..f154542 --- /dev/null +++ b/lib/libnet/udp.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 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 + *****************************************************************************/ + +#ifndef _UDP_H +#define _UDP_H + +#include <stdint.h> + +#define IPTYPE_UDP 17 + +#define UDPPORT_BOOTPS 67 /**< UDP port of BootP/DHCP-server */ +#define UDPPORT_BOOTPC 68 /**< UDP port of BootP/DHCP-client */ +#define UDPPORT_DNSS 53 /**< UDP port of DNS-server */ +#define UDPPORT_DNSC 32769 /**< UDP port of DNS-client */ +#define UDPPORT_TFTPC 2001 /**< UDP port of TFTP-client */ +#define UDPPORT_DHCPV6C 546 /**< UDP port of DHCPv6-client */ + +/** \struct udphdr + * A header for UDP-packets. + * For more information see RFC 768. + */ +struct udphdr { + uint16_t uh_sport; /**< Source port */ + uint16_t uh_dport; /**< Destinantion port */ + uint16_t uh_ulen; /**< Length in octets, incl. this header and data */ + uint16_t uh_sum; /**< Checksum */ +}; +typedef struct udphdr udp_hdr_t; + +typedef int32_t *(*handle_upper_udp_t)(uint8_t *, int32_t); +typedef void *(*handle_upper_udp_dun_t)(uint8_t); + +/* Handles UDP-packets that are detected by any network layer. */ +extern int8_t handle_udp(int fd, uint8_t * udp_packet, uint32_t packetsize); + +/* Handles UDP related ICMP-Dest.Unreachable packets that are detected by + * the network layers. */ +extern void handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t err_code); + +/* fills udp header */ +extern void fill_udphdr(uint8_t *packet, uint16_t packetsize, + uint16_t src_port, uint16_t dest_port); + +#ifdef USE_MTFTP +extern void net_set_tftp_port(uint16_t tftp_port); +extern void net_set_mtftp_port(uint16_t tftp_port); +#endif + +#endif |