/****************************************************************************** * 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 "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 *******************************/ 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; 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. *

* 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(void) { 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 address 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 Addressing 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 addresses * * @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 !memcmp(ip_1.addr, ip_2.addr, 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 char *packet - pointer to header of upper-layer * protocol * @param int bytes - number of bytes * starting from *packet * @return checksum */ static unsigned short ip6_checksum(struct ip6hdr *ip6h, unsigned char *packet, int bytes) { int i; unsigned long checksum; union { struct ip6hdr ip6h; uint16_t raw[sizeof(struct ip6hdr) / sizeof(uint16_t)]; } pseudo; memcpy (&pseudo.ip6h, ip6h, sizeof(struct ip6hdr)); pseudo.ip6h.hl = ip6h->nh; pseudo.ip6h.ver_tc_fl = 0; pseudo.ip6h.nh = 0; for (checksum = 0, i = 0; i < bytes; i += 2) checksum += (packet[i] << 8) | packet[i + 1]; for (i = 0; i < (int)(sizeof(pseudo.raw) / sizeof(pseudo.raw[0])); i++) checksum += pseudo.raw[i]; 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 occurred * 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 char *) udph, ip6h->pl); /* 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 char *) icmp6h, ip6h->pl); 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