Преглед на файлове

misc/ipaddr6.h: complete implementation

ambrop7 преди 13 години
родител
ревизия
131fe33bb9
променени са 4 файла, в които са добавени 300 реда и са изтрити 17 реда
  1. 295 14
      misc/ipaddr6.h
  2. 2 2
      ncd/NCDInterfaceMonitor.c
  3. 2 0
      ncd/NCDInterfaceMonitor.h
  4. 1 1
      ncd/modules/net_ipv6_wait_dynamic_addr.c

+ 295 - 14
misc/ipaddr6.h

@@ -34,29 +34,310 @@
 #ifndef BADVPN_MISC_IPADDR6_H
 #define BADVPN_MISC_IPADDR6_H
 
-#include <string.h>
 #include <stdio.h>
 #include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
 
 #include <misc/debug.h>
+#include <misc/byteorder.h>
+#include <misc/parse_number.h>
+#include <misc/find_char.h>
 
-// from /etc/iproute2/rt_scopes
-#define IPADDR6_SCOPE_GLOBAL 0
-#define IPADDR6_SCOPE_HOST 254
-#define IPADDR6_SCOPE_LINK 253
-#define IPADDR6_SCOPE_SITE 200
+struct ipv6_addr {
+    uint8_t bytes[16];
+};
 
 struct ipv6_ifaddr {
-    uint8_t addr[16];
+    struct ipv6_addr addr;
     int prefix;
-    int scope;
 };
 
+static int ipaddr6_parse_ipv6_addr_bin (const char *name, size_t name_len, struct ipv6_addr *out_addr);
+static int ipaddr6_parse_ipv6_addr (const char *name, struct ipv6_addr *out_addr);
+static int ipaddr6_parse_ipv6_prefix_bin (const char *str, size_t str_len, int *out_num);
+static int ipaddr6_parse_ipv6_prefix (const char *str, int *out_num);
+static int ipaddr6_parse_ipv6_ifaddr_bin (const char *str, size_t str_len, struct ipv6_ifaddr *out);
+static int ipaddr6_parse_ipv6_ifaddr (const char *str, struct ipv6_ifaddr *out);
+static int ipaddr6_ipv6_ifaddr_from_addr_mask (struct ipv6_addr addr, struct ipv6_addr mask, struct ipv6_ifaddr *out);
+static void ipaddr6_ipv6_mask_from_prefix (int prefix, struct ipv6_addr *out_mask);
+static int ipaddr6_ipv6_prefix_from_mask (struct ipv6_addr mask, int *out_prefix);
+static int ipaddr6_ipv6_addrs_in_network (struct ipv6_addr addr1, struct ipv6_addr addr2, int netprefix);
+
 #define IPADDR6_PRINT_MAX 46
 
-static void ipaddr6_print_addr (const uint8_t *addr, char *out_buf);
+static void ipaddr6_print_addr (struct ipv6_addr addr, char *out_buf);
+
+int ipaddr6_parse_ipv6_addr_bin (const char *name, size_t name_len, struct ipv6_addr *out_addr)
+{
+    int num_blocks = 0;
+    int compress_pos = -1;
+    uint16_t block = 0;
+    int empty = 1;
+    
+    size_t i = 0;
+    
+    while (i < name_len) {
+        if (name[i] == '.') {
+            goto ipv4_ending;
+        } else if (name[i] == ':') {
+            int is_double = (i + 1 < name_len && name[i + 1] == ':');
+            
+            if (i > 0) {
+                if (empty || num_blocks == 7) {
+                    return 0;
+                }
+                out_addr->bytes[2 * num_blocks + 0] = block >> 8;
+                out_addr->bytes[2 * num_blocks + 1] = block & 0xFF;
+                num_blocks++;
+                block = 0;
+                empty = 1;
+            }
+            else if (!is_double) {
+                return 0;
+            }
+            
+            if (is_double) {
+                if (compress_pos != -1) {
+                    return 0;
+                }
+                compress_pos = num_blocks;
+            }
+            
+            i += 1 + is_double;
+        } else {
+            int digit = decode_hex_digit(name[i]);
+            if (digit < 0) {
+                return 0;
+            }
+            if (block > UINT16_MAX / 16) {
+                return 0;
+            }
+            block *= 16;
+            if (digit > UINT16_MAX - block) {
+                return 0;
+            }
+            block += digit;
+            empty = 0;
+            i += 1;
+        }
+    }
+    
+    if (!empty) {
+        out_addr->bytes[2 * num_blocks + 0] = block >> 8;
+        out_addr->bytes[2 * num_blocks + 1] = block & 0xFF;
+        num_blocks++;
+    }
+    else if (num_blocks != compress_pos) {
+        return 0;
+    }
+    
+ipv4_done:
+    if (compress_pos == -1) {
+        if (num_blocks != 8) {
+            return 0;
+        }
+        compress_pos = 0;
+    }
+    
+    int num_rear = num_blocks - compress_pos;
+    memmove(out_addr->bytes + 2 * (8 - num_rear), out_addr->bytes + 2 * compress_pos, 2 * num_rear);
+    memset(out_addr->bytes + 2 * compress_pos, 0, 2 * (8 - num_rear - compress_pos));
+    
+    return 1;
+    
+ipv4_ending:
+    if (empty || (num_blocks == 0 && compress_pos == -1)) {
+        return 0;
+    }
+    
+    while (name[i - 1] != ':') {
+        i--;
+    }
+    
+    uint8_t bytes[4];
+    int cur_byte = 0;
+    uint8_t byte = 0;
+    empty = 1;
+    
+    while (i < name_len) {
+        if (name[i] == '.') {
+            if (empty || cur_byte == 3) {
+                return 0;
+            }
+            bytes[cur_byte] = byte;
+            cur_byte++;
+            byte = 0;
+            empty = 1;
+        } else {
+            if (!empty && byte == 0) {
+                return 0;
+            }
+            int digit = decode_decimal_digit(name[i]);
+            if (digit < 0) {
+                return 0;
+            }
+            if (byte > UINT8_MAX / 10) {
+                return 0;
+            }
+            byte *= 10;
+            if (digit > UINT8_MAX - byte) {
+                return 0;
+            }
+            byte += digit;
+            empty = 0;
+        }
+        i++;
+    }
+    
+    if (cur_byte != 3 || empty) {
+        return 0;
+    }
+    bytes[cur_byte] = byte;
+    
+    if (8 - num_blocks < 2) {
+        return 0;
+    }
+    memcpy(out_addr->bytes + 2 * num_blocks, bytes, 4);
+    num_blocks += 2;
+    
+    goto ipv4_done;
+}
+
+int ipaddr6_parse_ipv6_addr (const char *name, struct ipv6_addr *out_addr)
+{
+    return ipaddr6_parse_ipv6_addr_bin(name, strlen(name), out_addr);
+}
+
+int ipaddr6_parse_ipv6_prefix_bin (const char *str, size_t str_len, int *out_num)
+{
+    uintmax_t d;
+    if (!parse_unsigned_integer_bin(str, str_len, &d)) {
+        return 0;
+    }
+    if (d > 128) {
+        return 0;
+    }
+    
+    *out_num = d;
+    return 1;
+}
+
+int ipaddr6_parse_ipv6_prefix (const char *str, int *out_num)
+{
+    return ipaddr6_parse_ipv6_prefix_bin(str, strlen(str), out_num);
+}
+
+int ipaddr6_parse_ipv6_ifaddr_bin (const char *str, size_t str_len, struct ipv6_ifaddr *out)
+{
+    size_t slash_pos;
+    if (!b_find_char_bin(str, str_len, '/', &slash_pos)) {
+        return 0;
+    }
+    
+    return (ipaddr6_parse_ipv6_addr_bin(str, slash_pos, &out->addr) &&
+            ipaddr6_parse_ipv6_prefix_bin(str + slash_pos + 1, str_len - slash_pos - 1, &out->prefix));
+}
+
+int ipaddr6_parse_ipv6_ifaddr (const char *str, struct ipv6_ifaddr *out)
+{
+    return ipaddr6_parse_ipv6_ifaddr_bin(str, strlen(str), out);
+}
+
+int ipaddr6_ipv6_ifaddr_from_addr_mask (struct ipv6_addr addr, struct ipv6_addr mask, struct ipv6_ifaddr *out)
+{
+    int prefix;
+    if (!ipaddr6_ipv6_prefix_from_mask(mask, &prefix)) {
+        return 0;
+    }
+    
+    out->addr = addr;
+    out->prefix = prefix;
+    return 1;
+}
+
+void ipaddr6_ipv6_mask_from_prefix (int prefix, struct ipv6_addr *out_mask)
+{
+    ASSERT(prefix >= 0)
+    ASSERT(prefix <= 128)
+    
+    int quot = prefix / 8;
+    int rem = prefix % 8;
+    
+    memset(out_mask->bytes, UINT8_MAX, quot);
+    memset(out_mask->bytes + quot, 0, 16 - quot);
+    
+    for (int i = 0; i < rem; i++) {
+        out_mask->bytes[quot] |= (uint8_t)1 << (8 - i - 1);
+    }
+}
+
+int ipaddr6_ipv6_prefix_from_mask (struct ipv6_addr mask, int *out_prefix)
+{
+    int prefix = 0;
+    int i = 0;
+    
+    while (i < 16 && mask.bytes[i] == UINT8_MAX) {
+        prefix += 8;
+        i++;
+    }
+    
+    if (i < 16) {
+        uint8_t t = 0;
+        int j;
+        for (j = 0; j <= 8; j++) {
+            if (mask.bytes[i] == t) {
+                break;
+            }
+            if (j < 8) {
+                t |= ((uint8_t)1 << (8 - j - 1));
+            }
+        }
+        if (!(j <= 8)) {
+            return 0;
+        }
+        
+        prefix += j;
+        i++;
+        
+        while (i < 16) {
+            if (mask.bytes[i] != 0) {
+                return 0;
+            }
+            i++;
+        }
+    }
+    
+    *out_prefix = prefix;
+    return 1;
+}
+
+int ipaddr6_ipv6_addrs_in_network (struct ipv6_addr addr1, struct ipv6_addr addr2, int netprefix)
+{
+    ASSERT(netprefix >= 0)
+    ASSERT(netprefix <= 128)
+    
+    int quot = netprefix / 8;
+    int rem = netprefix % 8;
+    
+    if (memcmp(addr1.bytes, addr2.bytes, quot)) {
+        return 0;
+    }
+    
+    if (rem == 0) {
+        return 1;
+    }
+    
+    uint8_t t = 0;
+    for (int i = 0; i < rem; i++) {
+        t |= (uint8_t)1 << (8 - i - 1);
+    }
+    
+    return ((addr1.bytes[quot] & t) == (addr2.bytes[quot] & t));
+}
 
-void ipaddr6_print_addr (const uint8_t *addr, char *out_buf)
+void ipaddr6_print_addr (struct ipv6_addr addr, char *out_buf)
 {
     int largest_start = 0;
     int largest_len = 0;
@@ -64,7 +345,7 @@ void ipaddr6_print_addr (const uint8_t *addr, char *out_buf)
     int current_len = 0;
     
     for (int i = 0; i < 8; i++) {
-        if (addr[2 * i] == 0 && addr[2 * i + 1] == 0) {
+        if (addr.bytes[2 * i] == 0 && addr.bytes[2 * i + 1] == 0) {
             current_len++;
             if (current_len > largest_len) {
                 largest_start = current_start;
@@ -78,7 +359,7 @@ void ipaddr6_print_addr (const uint8_t *addr, char *out_buf)
     
     if (largest_len > 1) {
         for (int i = 0; i < largest_start; i++) {
-            uint16_t block = ((uint16_t)addr[2 * i] << 8) | addr[2 * i + 1];
+            uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1];
             out_buf += sprintf(out_buf, "%"PRIx16":", block);
         }
         if (largest_start == 0) {
@@ -86,7 +367,7 @@ void ipaddr6_print_addr (const uint8_t *addr, char *out_buf)
         }
         
         for (int i = largest_start + largest_len; i < 8; i++) {
-            uint16_t block = ((uint16_t)addr[2 * i] << 8) | addr[2 * i + 1];
+            uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1];
             out_buf += sprintf(out_buf, ":%"PRIx16, block);
         }
         if (largest_start + largest_len == 8) {
@@ -95,7 +376,7 @@ void ipaddr6_print_addr (const uint8_t *addr, char *out_buf)
     } else {
         const char *prefix = "";
         for (int i = 0; i < 8; i++) {
-            uint16_t block = ((uint16_t)addr[2 * i] << 8) | addr[2 * i + 1];
+            uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1];
             out_buf += sprintf(out_buf, "%s%"PRIx16, prefix, block);
             prefix = ":";
         }

+ 2 - 2
ncd/NCDInterfaceMonitor.c

@@ -226,10 +226,10 @@ void process_buffer (NCDInterfaceMonitor *o)
                     }
                     
                     ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED;
-                    memcpy(ev.u.ipv6_addr.addr.addr, ((struct in6_addr *)addr)->s6_addr, 16);
+                    memcpy(ev.u.ipv6_addr.addr.addr.bytes, ((struct in6_addr *)addr)->s6_addr, 16);
                     ev.u.ipv6_addr.addr.prefix = msg->ifa_prefixlen;
-                    ev.u.ipv6_addr.addr.scope = msg->ifa_scope;
                     ev.u.ipv6_addr.addr_flags = 0;
+                    ev.u.ipv6_addr.scope = msg->ifa_scope;
                     if (!(msg->ifa_flags & IFA_F_PERMANENT)) {
                         ev.u.ipv6_addr.addr_flags |= NCDIFMONITOR_ADDR_FLAG_DYNAMIC;
                     }

+ 2 - 0
ncd/NCDInterfaceMonitor.h

@@ -30,6 +30,7 @@
 #ifndef BADVPN_NCD_NCDINTERFACEMONITOR_H
 #define BADVPN_NCD_NCDINTERFACEMONITOR_H
 
+#include <stdint.h>
 #include <sys/socket.h>
 #include <linux/netlink.h>
 
@@ -63,6 +64,7 @@ struct NCDInterfaceMonitor_event {
         struct {
             struct ipv6_ifaddr addr;
             int addr_flags;
+            uint8_t scope;
         } ipv6_addr;
     } u;
 };

+ 1 - 1
ncd/modules/net_ipv6_wait_dynamic_addr.c

@@ -75,7 +75,7 @@ static void monitor_handler (struct instance *o, struct NCDInterfaceMonitor_even
         // signal up
         NCDModuleInst_Backend_Up(o->i);
     }
-    else if (o->up && event.event == NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED && !memcmp(event.u.ipv6_addr.addr.addr, o->ifaddr.addr, 16) && event.u.ipv6_addr.addr.prefix == o->ifaddr.prefix) {
+    else if (o->up && event.event == NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED && !memcmp(event.u.ipv6_addr.addr.addr.bytes, o->ifaddr.addr.bytes, 16) && event.u.ipv6_addr.addr.prefix == o->ifaddr.prefix) {
         // set not up
         o->up = 0;