Benjamin Drung
2018-Jun-18 09:10 UTC
[klibc] [PATCH v3 1/2] Implement classless static routes
Implement classless static routes support as specified in RFC3442. Bug-Debian: https://bugs.debian.org/884716 Bug-Ubuntu: https://launchpad.net/bugs/1526956 Signed-off-by: Benjamin Drung <benjamin.drung at profitbricks.com> --- usr/kinit/ipconfig/bootp_proto.c | 109 +++++++++++++++++++++++++++++++ usr/kinit/ipconfig/dhcp_proto.c | 1 + usr/kinit/ipconfig/main.c | 54 ++++++++++++--- usr/kinit/ipconfig/netdev.c | 49 ++++++++++---- usr/kinit/ipconfig/netdev.h | 24 ++++++- 5 files changed, 212 insertions(+), 25 deletions(-) diff --git a/usr/kinit/ipconfig/bootp_proto.c b/usr/kinit/ipconfig/bootp_proto.c index 150ebfa7..f6f9dd43 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 routes; + + /* convert only significant octets from byte array into integer in 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 */ + memcpy(&route->gateway, &ext[index], 4); + 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,22 @@ 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) { + struct route *cur = dev->routes; + struct route *next; + while (cur != NULL) { + next = cur->next; + free(cur); + cur = next; + } + 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..8c7fa7d2 100644 --- a/usr/kinit/ipconfig/main.c +++ b/usr/kinit/ipconfig/main.c @@ -70,6 +70,8 @@ static inline const char *my_inet_ntoa(uint32_t addr) static void print_device_config(struct netdev *dev) { + int dns0_spaces; + int dns1_spaces; printf("IP-Config: %s complete", dev->name); if (dev->proto == PROTO_BOOTP || dev->proto == PROTO_DHCP) printf(" (%s from %s)", protoinfos[dev->proto].name, @@ -79,9 +81,27 @@ 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); + if (cur->gateway != 0) { + printf(" via %s", my_inet_ntoa(cur->gateway)); + } + delim = ","; + } + printf("\n"); + dns0_spaces = 3; + dns1_spaces = 5; + } else { + printf(" gateway: %-16s", my_inet_ntoa(dev->ip_gateway)); + dns0_spaces = 5; + dns1_spaces = 3; + } + printf(" dns0%*c: %-16s", dns0_spaces, ' ', my_inet_ntoa(dev->ip_nameserver[0])); + printf(" dns1%*c: %-16s\n", dns1_spaces, ' ', my_inet_ntoa(dev->ip_nameserver[1])); if (dev->hostname[0]) printf(" host : %-64s\n", dev->hostname); if (dev->dnsdomainname[0]) @@ -105,8 +125,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 +180,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", @@ -539,7 +575,7 @@ bail: static int add_all_devices(struct netdev *template); -static int parse_device(struct netdev *dev, const char *ip) +static int parse_device(struct netdev *dev, char *ip) { char *cp; int opt; @@ -652,7 +688,7 @@ static void bringup_one_dev(struct netdev *template, struct netdev *dev) bringup_device(dev); } -static struct netdev *add_device(const char *info) +static struct netdev *add_device(char *info) { struct netdev *dev; int i; diff --git a/usr/kinit/ipconfig/netdev.c b/usr/kinit/ipconfig/netdev.c index e203d0c6..de87f960 100644 --- a/usr/kinit/ipconfig/netdev.c +++ b/usr/kinit/ipconfig/netdev.c @@ -88,23 +88,44 @@ 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)); + + r.rt_dev = dev->name; + 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; + if (cur->gateway != 0) { + r.rt_flags |= 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..02caca3a 100644 --- a/usr/kinit/ipconfig/netdev.h +++ b/usr/kinit/ipconfig/netdev.h @@ -1,14 +1,22 @@ #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 */ + char *name; /* Device name */ unsigned int ifindex; /* interface index */ unsigned int hwtype; /* ARPHRD_xxx */ unsigned int hwlen; /* HW address length */ @@ -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 */ }; @@ -69,7 +78,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 +92,15 @@ 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) */ + if (netmask_width == 0) { + return 0; + } else { + return htonl(~((1u << (32 - netmask_width)) - 1)); + } +} + #endif /* IPCONFIG_NETDEV_H */ -- 2.17.1
Benjamin Drung
2018-Jun-18 09:10 UTC
[klibc] [PATCH 2/2] mount_main: Fix empty string check
gcc 7.3.0 complains: ``` usr/utils/mount_main.c: In function ?print_mount?: usr/utils/mount_main.c:46:46: warning: comparison between pointer and zero character constant [-Wpointer-compare] if (mnt->mnt_type != NULL && mnt->mnt_type != '\0') ^~ usr/utils/mount_main.c:46:32: note: did you mean to dereference the pointer? if (mnt->mnt_type != NULL && mnt->mnt_type != '\0') ^ usr/utils/mount_main.c:48:46: warning: comparison between pointer and zero character constant [-Wpointer-compare] if (mnt->mnt_opts != NULL && mnt->mnt_opts != '\0') ^~ usr/utils/mount_main.c:48:32: note: did you mean to dereference the pointer? if (mnt->mnt_opts != NULL && mnt->mnt_opts != '\0') ^ ``` --- usr/utils/mount_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/utils/mount_main.c b/usr/utils/mount_main.c index ab3cb718..0d299c43 100644 --- a/usr/utils/mount_main.c +++ b/usr/utils/mount_main.c @@ -43,9 +43,9 @@ static __noreturn print_mount(char *type) if (type && mnt->mnt_type && strcmp(type, mnt->mnt_type)) continue; printf("%s on %s", mnt->mnt_fsname, mnt->mnt_dir); - if (mnt->mnt_type != NULL && mnt->mnt_type != '\0') + if (mnt->mnt_type != NULL && *mnt->mnt_type != '\0') printf(" type %s", mnt->mnt_type); - if (mnt->mnt_opts != NULL && mnt->mnt_opts != '\0') + if (mnt->mnt_opts != NULL && *mnt->mnt_opts != '\0') printf(" (%s)", mnt->mnt_opts); printf("\n"); } -- 2.17.1