Package: klibc-utils
Version: 2.0.4-9
Severity: wishlist
Tags: upstream patch

Hi,

ipconfig from klibc-utils does not support classless static routes (as
specified in RFC3442). There is already an Ubuntu bug requesting that
feature and we also want to use it. I went ahead and implemented the
support and tested it. The patch is attached.

-- 
Benjamin Drung
System Developer
Debian & Ubuntu Developer

ProfitBricks GmbH
Greifswalder Str. 207
D - 10405 Berlin

Email: benjamin.dr...@profitbricks.com
URL: https://www.profitbricks.de

Sitz der Gesellschaft: Berlin
Registergericht: Amtsgericht Charlottenburg, HRB 125506 B
Geschäftsführer: Achim Weiss, Matthias Steinberg
>From 2812cb5cd5f3c48b74e19fe07dde61c93fc8c85a Mon Sep 17 00:00:00 2001
From: Benjamin Drung <benjamin.dr...@profitbricks.com>
Date: Wed, 13 Dec 2017 23:04:29 +0100
Subject: [PATCH] Implement classless static routes

Implement classless static routes support as specified in RFC3442.

Bug-Ubuntu: https://launchpad.net/bugs/1526956
Signed-off-by: Benjamin Drung <benjamin.dr...@profitbricks.com>
---
 usr/kinit/ipconfig/bootp_proto.c | 112 +++++++++++++++++++++++++++++++++++++++
 usr/kinit/ipconfig/dhcp_proto.c  |   1 +
 usr/kinit/ipconfig/main.c        |  43 ++++++++++++---
 usr/kinit/ipconfig/netdev.c      |  45 +++++++++++-----
 usr/kinit/ipconfig/netdev.h      |  17 ++++++
 5 files changed, 197 insertions(+), 21 deletions(-)

diff --git a/usr/kinit/ipconfig/bootp_proto.c b/usr/kinit/ipconfig/bootp_proto.c
index 150ebfa7..ae050127 100644
--- a/usr/kinit/ipconfig/bootp_proto.c
+++ b/usr/kinit/ipconfig/bootp_proto.c
@@ -267,6 +267,87 @@ static char *bootp_ext119_decode(const void *ext, int16_t 
ext_size, void *tmp)
        return decoded_str;
 }
 
+/*
+ * DESCRIPTION
+ *  bootp_ext121_decode() decodes Classless Route Option data.
+ *
+ * ARGUMENTS
+ *  const uint8_t *ext
+ *   *ext is a pointer to a DHCP Classless Route Option data.
+ *   For example, if *ext is {16, 192, 168, 192, 168, 42, 1},
+ *   this function returns a pointer to
+ *   {
+ *     subnet = 192.168.0.0;
+ *     netmask_width = 16;
+ *     gateway = 192.168.42.1;
+ *     next = NULL;
+ *   }
+ *
+ *  int16_t ext_size
+ *   ext_size is the memory size of *ext. For example,
+ *   if *ext is {16, 192, 168, 192, 168, 42, 1}, ext_size must be 7.
+ *
+ * RETURN VALUE
+ *  if OK, a pointer to a decoded struct route malloc-ed
+ *  else , NULL
+ *
+ * SEE ALSO RFC3442
+ */
+struct route *bootp_ext121_decode(const uint8_t *ext, int16_t ext_size)
+{
+       int16_t index = 0;
+       uint8_t netmask_width;
+       uint8_t significant_octets;
+       struct route *routes = NULL;
+       struct route *prev_route = NULL;
+
+       while(index < ext_size) {
+               netmask_width = ext[index];
+               index++;
+               if(netmask_width > 32) {
+                       printf("IP-Config: Given Classless Route Option subnet 
mask width '%u' "
+                           "exceeds IPv4 limit of 32. Ignoring remaining 
option.\n",
+                               netmask_width);
+                       return routes;
+               }
+               significant_octets = netmask_width / 8 + (netmask_width % 8 > 
0);
+               if(ext_size - index < significant_octets + 4) {
+                       printf("IP-Config: Given Classless Route Option 
remaining lengths (%u octets) "
+                               "is shorter than the expected %u octets. 
Ignoring remaining options.\n",
+                               ext_size - index, significant_octets + 4);
+                       return routes;
+               }
+
+               struct route *route = malloc(sizeof(struct route));
+               if (route == NULL)
+                       return NULL;
+
+               /* convert only significant octets from array into network byte 
order */
+               route->subnet = 0;
+               memcpy(&route->subnet, &ext[index], significant_octets);
+               index += significant_octets;
+               /* RFC3442 demands: After deriving a subnet number and subnet 
mask from
+                  each destination descriptor, the DHCP client MUST zero any 
bits in
+                  the subnet number where the corresponding bit in the mask is 
zero. */
+               route->subnet &= netdev_genmask(netmask_width);
+
+               /* convert octet array into network byte order */
+               route->gateway = *(uint32_t*)&ext[index];
+               index += 4;
+
+               route->netmask_width = netmask_width;
+               route->next = NULL;
+
+               if (prev_route == NULL) {
+                       routes = route;
+               } else {
+                       prev_route->next = route;
+               }
+               prev_route = route;
+       }
+       return routes;
+}
+
 /*
  * Parse a bootp reply packet
  */
@@ -275,6 +356,8 @@ int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr,
 {
        uint8_t ext119_buf[BOOTP_EXTS_SIZE];
        int16_t ext119_len = 0;
+       uint8_t ext121_buf[BOOTP_EXTS_SIZE];
+       int16_t ext121_len = 0;
 
        dev->bootp.gateway      = hdr->giaddr;
        dev->ip_addr            = hdr->yiaddr;
@@ -367,6 +450,16 @@ int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr,
                                } else
                                        ext119_len = -1;
 
+                               break;
+                       case 121:       /* Classless Static Route Option 
(RFC3442) */
+                               if (ext121_len >= 0 &&
+                                   ext121_len + len <= sizeof(ext121_buf)) {
+                                       memcpy(ext121_buf + ext121_len,
+                                              ext, len);
+                                       ext121_len += len;
+                               } else
+                                       ext121_len = -1;
+
                                break;
                        }
 
@@ -385,6 +478,25 @@ int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr,
                }
        }
 
+       if (ext121_len > 0) {
+               struct route *ret;
+
+               ret = bootp_ext121_decode(ext121_buf, ext121_len);
+               if (ret != NULL) {
+                       if (dev->routes != NULL) {
+                               struct route *cur = dev->routes;
+                               struct route *next;
+                               while(cur->next != NULL) {
+                                       next = cur->next;
+                                       free(cur);
+                                       cur = next;
+                               }
+                               free(cur);
+                       }
+                       dev->routes = ret;
+               }
+       }
+
        /*
         * Got packet.
         */
diff --git a/usr/kinit/ipconfig/dhcp_proto.c b/usr/kinit/ipconfig/dhcp_proto.c
index ebf79cc0..ab7bdadc 100644
--- a/usr/kinit/ipconfig/dhcp_proto.c
+++ b/usr/kinit/ipconfig/dhcp_proto.c
@@ -26,6 +26,7 @@ static uint8_t dhcp_params[] = {
        28,                     /* broadcast addr */
        40,                     /* NIS domain name (why?) */
        119,                    /* Domain Search Option */
+       121,                    /* Classless Static Route Option (RFC3442) */
 };
 
 static uint8_t dhcp_discover_hdr[] = {
diff --git a/usr/kinit/ipconfig/main.c b/usr/kinit/ipconfig/main.c
index 7be2a1fc..0e12db4d 100644
--- a/usr/kinit/ipconfig/main.c
+++ b/usr/kinit/ipconfig/main.c
@@ -79,9 +79,22 @@ static void print_device_config(struct netdev *dev)
        printf(":\n address: %-16s ", my_inet_ntoa(dev->ip_addr));
        printf("broadcast: %-16s ", my_inet_ntoa(dev->ip_broadcast));
        printf("netmask: %-16s\n", my_inet_ntoa(dev->ip_netmask));
-       printf(" gateway: %-16s ", my_inet_ntoa(dev->ip_gateway));
-       printf("dns0     : %-16s ", my_inet_ntoa(dev->ip_nameserver[0]));
-       printf("dns1   : %-16s\n", my_inet_ntoa(dev->ip_nameserver[1]));
+       if (dev->routes != NULL) {
+               struct route *cur;
+               char *delim = "";
+               printf(" routes :");
+               for (cur = dev->routes; cur != NULL; cur = cur->next) {
+                       printf("%s %s/%u", delim, my_inet_ntoa(cur->subnet), 
cur->netmask_width);
+                       printf(" via %s", my_inet_ntoa(cur->gateway));
+                       delim = ",";
+               }
+               printf("\n dns0   : %-16s ", 
my_inet_ntoa(dev->ip_nameserver[0]));
+               printf("dns1     : %-16s\n", 
my_inet_ntoa(dev->ip_nameserver[1]));
+       } else {
+               printf(" gateway: %-16s ", my_inet_ntoa(dev->ip_gateway));
+               printf("dns0     : %-16s ", 
my_inet_ntoa(dev->ip_nameserver[0]));
+               printf("dns1   : %-16s\n", my_inet_ntoa(dev->ip_nameserver[1]));
+       }
        if (dev->hostname[0])
                printf(" host   : %-64s\n", dev->hostname);
        if (dev->dnsdomainname[0])
@@ -105,8 +118,8 @@ static void configure_device(struct netdev *dev)
        if (netdev_setaddress(dev))
                printf("IP-Config: failed to set addresses on %s\n",
                       dev->name);
-       if (netdev_setdefaultroute(dev))
-               printf("IP-Config: failed to set default route on %s\n",
+       if (netdev_setroutes(dev))
+               printf("IP-Config: failed to set routes on %s\n",
                       dev->name);
        if (dev->hostname[0] &&
                        sethostname(dev->hostname, strlen(dev->hostname)))
@@ -160,8 +173,24 @@ static void dump_device_config(struct netdev *dev)
                                my_inet_ntoa(dev->ip_broadcast));
                write_option(f, "IPV4NETMASK",
                                my_inet_ntoa(dev->ip_netmask));
-               write_option(f, "IPV4GATEWAY",
-                               my_inet_ntoa(dev->ip_gateway));
+               if (dev->routes != NULL) {
+                       /* Use 6 digits to encode the index */
+                       char key[23];
+                       char value[19];
+                       int i = 0;
+                       struct route *cur;
+                       for (cur = dev->routes; cur != NULL; cur = cur->next) {
+                               snprintf(key, sizeof(key), "IPV4ROUTE%iSUBNET", 
i);
+                               snprintf(value, sizeof(value), "%s/%u", 
my_inet_ntoa(cur->subnet), cur->netmask_width);
+                               write_option(f, key, value);
+                               snprintf(key, sizeof(key), 
"IPV4ROUTE%iGATEWAY", i);
+                               write_option(f, key, 
my_inet_ntoa(cur->gateway));
+                               i++;
+                       }
+               } else {
+                       write_option(f, "IPV4GATEWAY",
+                                       my_inet_ntoa(dev->ip_gateway));
+               }
                write_option(f, "IPV4DNS0",
                                my_inet_ntoa(dev->ip_nameserver[0]));
                write_option(f, "IPV4DNS1",
diff --git a/usr/kinit/ipconfig/netdev.c b/usr/kinit/ipconfig/netdev.c
index e203d0c6..446b4d87 100644
--- a/usr/kinit/ipconfig/netdev.c
+++ b/usr/kinit/ipconfig/netdev.c
@@ -88,23 +88,40 @@ static void set_s_addr(struct sockaddr *saddr, uint32_t 
ipaddr)
        memcpy(saddr, &sin, sizeof sin);
 }
 
-int netdev_setdefaultroute(struct netdev *dev)
+int netdev_setroutes(struct netdev *dev)
 {
        struct rtentry r;
 
-       if (dev->ip_gateway == INADDR_ANY)
-               return 0;
-
-       memset(&r, 0, sizeof(r));
-
-       set_s_addr(&r.rt_dst, INADDR_ANY);
-       set_s_addr(&r.rt_gateway, dev->ip_gateway);
-       set_s_addr(&r.rt_genmask, INADDR_ANY);
-       r.rt_flags = RTF_UP | RTF_GATEWAY;
-
-       if (ioctl(cfd, SIOCADDRT, &r) == -1 && errno != EEXIST) {
-               perror("SIOCADDRT");
-               return -1;
+       /* RFC3442 demands:
+          If the DHCP server returns both a Classless Static Routes option and
+          a Router option, the DHCP client MUST ignore the Router option. */
+       if (dev->routes != NULL) {
+               struct route *cur;
+               for (cur = dev->routes; cur != NULL; cur = cur->next) {
+                       memset(&r, 0, sizeof(r));
+
+                       set_s_addr(&r.rt_dst, cur->subnet);
+                       set_s_addr(&r.rt_gateway, cur->gateway);
+                       set_s_addr(&r.rt_genmask, 
netdev_genmask(cur->netmask_width));
+                       r.rt_flags = RTF_UP | RTF_GATEWAY;
+
+                       if (ioctl(cfd, SIOCADDRT, &r) == -1 && errno != EEXIST) 
{
+                               perror("SIOCADDRT");
+                               return -1;
+                       }
+               }
+       } else if (dev->ip_gateway != INADDR_ANY) {
+               memset(&r, 0, sizeof(r));
+
+               set_s_addr(&r.rt_dst, INADDR_ANY);
+               set_s_addr(&r.rt_gateway, dev->ip_gateway);
+               set_s_addr(&r.rt_genmask, INADDR_ANY);
+               r.rt_flags = RTF_UP | RTF_GATEWAY;
+
+               if (ioctl(cfd, SIOCADDRT, &r) == -1 && errno != EEXIST) {
+                       perror("SIOCADDRT");
+                       return -1;
+               }
        }
        return 0;
 }
diff --git a/usr/kinit/ipconfig/netdev.h b/usr/kinit/ipconfig/netdev.h
index cd853b6c..e909ef22 100644
--- a/usr/kinit/ipconfig/netdev.h
+++ b/usr/kinit/ipconfig/netdev.h
@@ -1,12 +1,20 @@
 #ifndef IPCONFIG_NETDEV_H
 #define IPCONFIG_NETDEV_H
 
+#include <arpa/inet.h>
 #include <sys/utsname.h>
 #include <net/if.h>
 
 #define BPLEN          256
 #define FNLEN          128                     /* from DHCP  RFC 2131 */
 
+struct route {
+       uint32_t subnet;                        /* subnet            */
+       uint32_t netmask_width; /* subnet mask width */
+       uint32_t gateway;               /* gateway           */
+       struct route *next;
+};
+
 struct netdev {
        const char *name;       /* Device name          */
        unsigned int ifindex;   /* interface index      */
@@ -44,6 +52,7 @@ struct netdev {
        char bootpath[BPLEN];   /* boot path            */
        char filename[FNLEN];   /* filename             */
        char *domainsearch;     /* decoded, NULL or malloc-ed  */
+       struct route *routes;   /* decoded, NULL or malloc-ed list */
        long uptime;            /* when complete configuration */
        struct netdev *next;    /* next configured i/f  */
 };
@@ -70,6 +79,7 @@ extern struct netdev *ifaces;
 int netdev_getflags(struct netdev *dev, short *flags);
 int netdev_setaddress(struct netdev *dev);
 int netdev_setdefaultroute(struct netdev *dev);
+int netdev_setroutes(struct netdev *dev);
 int netdev_up(struct netdev *dev);
 int netdev_down(struct netdev *dev);
 int netdev_init_if(struct netdev *dev);
@@ -83,4 +93,11 @@ static inline int netdev_running(struct netdev *dev)
        return ret ? 0 : !!(flags & IFF_RUNNING);
 }
 
+static inline uint32_t netdev_genmask(uint32_t netmask_width)
+{
+       /* Map netmask width to network mask in network byte order.
+          Example: 24 -> "255.255.255.0" -> htonl(0xFFFFFF00) */
+       return htonl(~((1u << (32 - netmask_width)) - 1));
+}
+
 #endif /* IPCONFIG_NETDEV_H */
-- 
2.14.1

Reply via email to