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