From e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb Mon Sep 17 00:00:00 2001 From: Yang Zhang Date: Fri, 28 Aug 2015 09:58:54 +0800 Subject: Add qemu 2.4.0 Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5 Signed-off-by: Yang Zhang --- qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c | 768 +++++++++++++++++++++++ 1 file changed, 768 insertions(+) create mode 100644 qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c (limited to 'qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c') diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c new file mode 100644 index 000000000..0cb0a2e7b --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c @@ -0,0 +1,768 @@ +/****************************************************************************** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 **************************/ + +/* Own IPv6 address */ +static struct ip6addr_list_entry *own_ip6; + +/* Null IPv6 address */ +static ip6_addr_t null_ip6; + +/* helper variables */ +static uint8_t null_mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + +/****************************** 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) +{ + own_ip6 = malloc (sizeof(struct ip6addr_list_entry)); + + /* If no address was passed as a parameter generate a link-local + * address from our MAC address.*/ + if (_own_ip6 == NULL) + memcpy(&(own_ip6->addr.addr), + ip6_create_ll_address(get_mac_address()), + IPV6_ADDR_LENGTH); + else + memcpy (&(own_ip6->addr.addr), _own_ip6, 16); + + /* Add to our list of IPv6 addresses */ + ip6addr_add (own_ip6); + + ipv6_init(fd); +} + +/** + * 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, int32_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. + *

+ * 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 + * @return ll_addr pointer to newly created link-local address + */ +ip6_addr_t * +ip6_create_ll_address (const uint8_t *own_mac) +{ + ip6_addr_t *ll_addr; + + ll_addr = malloc (sizeof (struct ip6addr_list_entry)); + memset (ll_addr, 0, IPV6_ADDR_LENGTH); + ll_addr->part.prefix |= IPV6_LL_PREFIX; + ll_addr->part.interface_id |= mac2eui64((uint8_t *) own_mac); + + return ll_addr; +} + +/* + * 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; + + 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; + + /* 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; + + 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 (NULL == first_ip6) + first_ip6 = new_address; + 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; + all_dhcpv6_ll.addr.part.prefix = 0xff02000000000000ULL; + all_dhcpv6_ll.addr.part.interface_id = 0x10002ULL; + all_routers_ll.addr.part.prefix = 0xff02000000000000; + all_routers_ll.addr.part.interface_id = 2; + + ip6addr_add(&all_nodes_ll); + /* ... */ + + /* Router list */ + first_router = NULL; + last_router = first_router; + + /* Init Neighbour cache */ + first_neighbor = NULL; + last_neighbor = first_neighbor; + + 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) +{ + uint8_t mc = 0xFF; + return ! memcmp(&ip->addr[0], &mc, 1); +} + +/** + * NET: Generate multicast MAC address from IPv6 address + * (e.g. UDP or ICMPv6) + * + * @param *ip - pointer to IPv6 address + * @return pointer to Multicast MAC address + */ +static uint8_t * +ip6_to_multicast_mac (ip6_addr_t * ip) +{ + uint8_t *mc_mac; + + mc_mac = malloc(ETH_ALEN); + if (!mc_mac) + return NULL; + + mc_mac[0] = 0x33; + mc_mac[1] = 0x33; + memcpy (mc_mac+2, (uint8_t *) &(ip->addr)+12, 4); + + return mc_mac; +} + +/** + * 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 == ERRROR + * return of handle_udp() or handle_icmp6() + * + * @see receive_ether + * @see ip6hdr + */ +int +send_ipv6 (int fd, void* buffer, int len) +{ + struct neighbor *n; + struct ip6hdr *ip6h; + struct udphdr *udph; + struct icmp6hdr *icmp6h; + ip6_addr_t ip_dst; + uint8_t *mac_addr, mac[6]; + + mac_addr = 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) > 1500) + 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); + + n = find_neighbor (&ip_dst); + + // If packet is a neighbor solicitation + if (icmp6h->type == ICMPV6_NEIGHBOUR_SOLICITATION) { + mac_addr = ip6_to_multicast_mac (&ip_dst); + fill_ethhdr( buffer-sizeof(struct ethhdr), htons(ETHERTYPE_IPv6), + get_mac_address(), + mac_addr); + } + + // If address is a multicast address, create a proper mac address + else if (ip6_is_multicast (&ip_dst)) { + mac_addr = ip6_to_multicast_mac (&ip_dst); + } + else { + // Check if the MAC address is already cached + if (n) { + if (memcmp(n->mac, null_mac, ETH_ALEN) != 0) + memcpy (mac_addr, &(n->mac), ETH_ALEN); /* found it */ + } else { + mac_addr = null_mac; + n = malloc(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 + memset(n->eth_frame, 0, 1500); + 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); + } while (get_timer() > 0); + } + } + } + + fill_ethhdr (n->eth_frame, htons(ETHERTYPE_IPv6), get_mac_address(), + mac_addr); + memcpy (&(n->eth_frame[sizeof(struct ethhdr)]), buffer, len); + return send_ether (fd, n->eth_frame, 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