| /****************************************************************************** |
| * 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 <time.h> |
| #include <ctype.h> |
| #include <sys/socket.h> |
| #include <netlib/ethernet.h> |
| #include <netlib/ipv6.h> |
| #include <netlib/icmpv6.h> |
| #include <netlib/ndp.h> |
| #include <netlib/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 **************************/ |
| |
| /* 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. |
| * <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 |
| * @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<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, "::"); |
| } |
| } |