netifd: Make interface identifier of delegated IPv6 address configurable
authorHans Dedecker <dedeckeh@gmail.com>
Mon, 12 Jan 2015 19:07:55 +0000 (20:07 +0100)
committerSteven Barth <steven@midlink.org>
Mon, 19 Jan 2015 08:35:08 +0000 (09:35 +0100)
The ip6ifaceid UCI interface parameter makes the interface identifier of the delegated IPv6 address configurable.
The parameter can have the following values:
    eui64 : Interface identifier is generated from the interface's MAC address
    random : Interface identifier is generated randomly
    fixed value : Interface identifier is a fixed value (eg ::1:2)

The latter is the default value with a fixed value of ::1 for backwards compatibility

Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
Signed-off-by: Joeri Barbarien <joeri.barbarien@gmail.com>
interface-ip.c
interface-ip.h
interface.c
interface.h

index 6659f8bac16dc0a3ad974250103e0201e7272034..1a22ce635206ad0cd2f85ad9c625593e11601de3 100644 (file)
@@ -648,6 +648,56 @@ interface_update_host_route(struct vlist_tree *tree,
        }
 }
 
+static void
+random_ifaceid(struct in6_addr *addr)
+{
+       static bool initialized = false;
+       struct timeval t;
+
+       if (!initialized) {
+               long int seed = 0;
+               gettimeofday(&t, NULL);
+               seed = t.tv_sec ^ t.tv_usec ^ getpid();
+               srand48(seed);
+               initialized = true;
+       }
+       addr->s6_addr32[2] = (uint32_t)mrand48();
+       addr->s6_addr32[3] = (uint32_t)mrand48();
+}
+
+static void
+eui64_ifaceid(struct interface *iface, struct in6_addr *addr)
+{
+       /* get mac address */
+       uint8_t *macaddr = iface->l3_dev.dev->settings.macaddr;
+       uint8_t *ifaceid = addr->s6_addr + 8;
+       memcpy(ifaceid,macaddr,3);
+       memcpy(ifaceid + 5,macaddr + 3, 3);
+       ifaceid[3] = 0xff;
+       ifaceid[4] = 0xfe;
+       ifaceid[0] ^= 0x02;
+}
+
+static void
+generate_ifaceid(struct interface *iface, struct in6_addr *addr)
+{
+       /* generate new iface id */
+       switch (iface->assignment_iface_id_selection) {
+       case IFID_FIXED:
+               /* fixed */
+               /* copy host part from assignment_fixed_iface_id */
+               memcpy(addr->s6_addr + 8, iface->assignment_fixed_iface_id.s6_addr + 8, 8);
+               break;
+       case IFID_RANDOM:
+               /* randomize last 64 bits */
+               random_ifaceid(addr);
+               break;
+       case IFID_EUI64:
+               /* eui64 */
+               eui64_ifaceid(iface, addr);
+               break;
+       }
+}
 
 static void
 interface_set_prefix_address(struct device_prefix_assignment *assignment,
@@ -661,9 +711,16 @@ interface_set_prefix_address(struct device_prefix_assignment *assignment,
 
        struct device_addr addr;
        memset(&addr, 0, sizeof(addr));
-       addr.addr.in6 = prefix->addr;
-       addr.addr.in6.s6_addr32[1] |= htonl(assignment->assigned);
-       addr.addr.in6.s6_addr[15] += 1;
+
+       if (IN6_IS_ADDR_UNSPECIFIED(&assignment->addr)) {
+               addr.addr.in6 = prefix->addr;
+               addr.addr.in6.s6_addr32[1] |= htonl(assignment->assigned);
+               generate_ifaceid(iface, &addr.addr.in6);
+               assignment->addr = addr.addr.in6;
+       }
+       else
+               addr.addr.in6 = assignment->addr;
+
        addr.mask = assignment->length;
        addr.flags = DEVADDR_INET6;
        addr.preferred_until = prefix->preferred_until;
@@ -775,6 +832,7 @@ static void interface_update_prefix_assignments(struct device_prefix *prefix, bo
        c->assigned = 1 << (64 - prefix->length);
        c->length = 64;
        c->name[0] = 0;
+       c->addr = in6addr_any;
        list_add(&c->head, &prefix->assignments);
 
        // Excluded prefix
@@ -784,6 +842,7 @@ static void interface_update_prefix_assignments(struct device_prefix *prefix, bo
                c->assigned = ntohl(prefix->excl_addr.s6_addr32[1]) &
                                ((1 << (64 - prefix->length)) - 1);
                c->length = prefix->excl_length;
+               c->addr = in6addr_any;
                memcpy(c->name, name, sizeof(name));
                list_add(&c->head, &prefix->assignments);
        }
@@ -815,6 +874,7 @@ static void interface_update_prefix_assignments(struct device_prefix *prefix, bo
                c = malloc(sizeof(*c) + namelen);
                c->length = iface->assignment_length;
                c->assigned = iface->assignment_hint;
+               c->addr = in6addr_any;
                c->enabled = false;
                memcpy(c->name, iface->name, namelen);
 
index a5612e5079790f4143e036fe9eb1517ef6ec54f4..7c4a8aeacf3b506f66e2570237bf72fb2ce8777d 100644 (file)
@@ -59,6 +59,7 @@ struct device_prefix_assignment {
        struct list_head head;
        int32_t assigned;
        uint8_t length;
+       struct in6_addr addr;
        bool enabled;
        char name[];
 };
index 83d7f94fe8268354925d65463ca62db7c54d93bf..4ab243120c393dd2b492ca18adfef67655e37328 100644 (file)
@@ -43,6 +43,7 @@ enum {
        IFACE_ATTR_IP6TABLE,
        IFACE_ATTR_IP6CLASS,
        IFACE_ATTR_DELEGATE,
+       IFACE_ATTR_IP6IFACEID,
        IFACE_ATTR_FORCE_LINK,
        IFACE_ATTR_MAX
 };
@@ -63,6 +64,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
        [IFACE_ATTR_IP6TABLE] = { .name = "ip6table", .type = BLOBMSG_TYPE_STRING },
        [IFACE_ATTR_IP6CLASS] = { .name = "ip6class", .type = BLOBMSG_TYPE_ARRAY },
        [IFACE_ATTR_DELEGATE] = { .name = "delegate", .type = BLOBMSG_TYPE_BOOL },
+       [IFACE_ATTR_IP6IFACEID] = { .name = "ip6ifaceid", .type = BLOBMSG_TYPE_STRING },
        [IFACE_ATTR_FORCE_LINK] = { .name = "force_link", .type = BLOBMSG_TYPE_BOOL },
 };
 
@@ -423,6 +425,10 @@ interface_merge_assignment_data(struct interface *old, struct interface *new)
 {
        bool changed = (old->assignment_hint != new->assignment_hint ||
                        old->assignment_length != new->assignment_length ||
+                       old->assignment_iface_id_selection != new->assignment_iface_id_selection ||
+                       (old->assignment_iface_id_selection == IFID_FIXED &&
+                        memcmp(&old->assignment_fixed_iface_id, &new->assignment_fixed_iface_id,
+                               sizeof(old->assignment_fixed_iface_id))) ||
                        list_empty(&old->assignment_classes) != list_empty(&new->assignment_classes));
 
        struct interface_assignment_class *c;
@@ -455,6 +461,8 @@ interface_merge_assignment_data(struct interface *old, struct interface *new)
        if (changed) {
                old->assignment_hint = new->assignment_hint;
                old->assignment_length = new->assignment_length;
+               old->assignment_iface_id_selection = new->assignment_iface_id_selection;
+               old->assignment_fixed_iface_id = new->assignment_fixed_iface_id;
                interface_refresh_assignments(true);
        }
 }
@@ -703,6 +711,33 @@ interface_alloc(const char *name, struct blob_attr *config)
        if ((cur = tb[IFACE_ATTR_IP6ASSIGN]))
                iface->assignment_length = blobmsg_get_u32(cur);
 
+       /* defaults */
+       iface->assignment_iface_id_selection = IFID_FIXED;
+       iface->assignment_fixed_iface_id = in6addr_any;
+       iface->assignment_fixed_iface_id.s6_addr[15] = 1;
+
+       if ((cur = tb[IFACE_ATTR_IP6IFACEID])) {
+               const char *ifaceid = blobmsg_data(cur);
+               if (!strcmp(ifaceid, "random")) {
+                       iface->assignment_iface_id_selection = IFID_RANDOM;
+               }
+               else if (!strcmp(ifaceid, "eui64")) {
+                       iface->assignment_iface_id_selection = IFID_EUI64;
+               }
+               else {
+                       /* we expect an IPv6 address with network id zero here -> fixed iface id
+                          if we cannot parse -> revert to iface id 1 */
+                       if (inet_pton(AF_INET6,ifaceid,&iface->assignment_fixed_iface_id) != 1 ||
+                                       iface->assignment_fixed_iface_id.s6_addr32[0] != 0 ||
+                                       iface->assignment_fixed_iface_id.s6_addr32[1] != 0) {
+                               iface->assignment_fixed_iface_id = in6addr_any;
+                               iface->assignment_fixed_iface_id.s6_addr[15] = 1;
+                               netifd_log_message(L_WARNING, "Failed to parse ip6ifaceid for interface '%s', \
+                                                       falling back to iface id 1.\n", iface->name);
+                       }
+               }
+       }
+
        iface->assignment_hint = -1;
        if ((cur = tb[IFACE_ATTR_IP6HINT]))
                iface->assignment_hint = strtol(blobmsg_get_string(cur), NULL, 16) &
index 90087fc0298fe137102c01e8dfdb433f4e4f005e..8eeb7db8a1c34c3afeb97c9dbf283ef885acf728 100644 (file)
@@ -41,6 +41,12 @@ enum interface_config_state {
        IFC_REMOVE
 };
 
+enum interface_id_selection_type {
+       IFID_FIXED,
+       IFID_RANDOM,
+       IFID_EUI64
+};
+
 enum interface_update_flags {
        IUF_ADDRESS     = (1 << 0),
        IUF_ROUTE       = (1 << 1),
@@ -139,6 +145,8 @@ struct interface {
        unsigned int ip6table;
 
        /* IPv6 assignment parameters */
+       enum interface_id_selection_type assignment_iface_id_selection;
+       struct in6_addr assignment_fixed_iface_id;
        uint8_t assignment_length;
        int32_t assignment_hint;
        struct list_head assignment_classes;