-/* $OpenBSD: dhcpleased.c,v 1.11 2021/05/01 11:52:36 florian Exp $ */
+/* $OpenBSD: dhcpleased.c,v 1.12 2021/06/16 14:06:17 florian Exp $ */
/*
* Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org>
void configure_interface(struct imsg_configure_interface *);
void deconfigure_interface(struct imsg_configure_interface *);
void propose_rdns(struct imsg_propose_rdns *);
-void configure_gateway(struct imsg_configure_interface *, uint8_t);
+void configure_routes(uint8_t, struct imsg_configure_interface *);
+void configure_route(uint8_t, uint32_t, int, struct sockaddr_in *, struct
+ sockaddr_in *, struct sockaddr_in *, struct sockaddr_in *, int);
void read_lease_file(struct imsg_ifinfo *);
static int main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *);
IMSG_DATA_SIZE(imsg));
memcpy(&imsg_interface, imsg.data,
sizeof(imsg_interface));
+ if (imsg_interface.routes_len >= MAX_DHCP_ROUTES)
+ fatalx("%s: too many routes in imsg", __func__);
configure_interface(&imsg_interface);
break;
}
IMSG_DATA_SIZE(imsg));
memcpy(&imsg_interface, imsg.data,
sizeof(imsg_interface));
+ if (imsg_interface.routes_len >= MAX_DHCP_ROUTES)
+ fatalx("%s: too many routes in imsg", __func__);
deconfigure_interface(&imsg_interface);
main_imsg_compose_frontend(IMSG_CLOSE_UDPSOCK, -1,
&imsg_interface.if_index,
if (ioctl(ioctl_sock, SIOCAIFADDR, &ifaliasreq) == -1)
fatal("SIOCAIFADDR");
- /* XXX check weird shit in dhclient/kroute.c set_routes() */
- if (imsg->router.s_addr != INADDR_ANY)
- configure_gateway(imsg, RTM_ADD);
+ if (imsg->routes_len > 0)
+ configure_routes(RTM_ADD, imsg);
}
req_sin_addr->sin_port = ntohs(CLIENT_PORT);
if ((udpsock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
memset(&ifaliasreq, 0, sizeof(ifaliasreq));
- if (imsg->router.s_addr != INADDR_ANY)
- configure_gateway(imsg, RTM_DELETE);
+ if (imsg->routes_len > 0)
+ configure_routes(RTM_DELETE, imsg);
if (if_indextoname(imsg->if_index, ifaliasreq.ifra_name) == NULL) {
log_warnx("%s: cannot find interface %d", __func__,
}
}
+void
+configure_routes(uint8_t rtm_type, struct imsg_configure_interface *imsg)
+{
+ struct sockaddr_in dst, mask, gw, ifa;
+ in_addr_t addrnet, gwnet;
+ int i;
+
+ memset(&ifa, 0, sizeof(ifa));
+ ifa.sin_family = AF_INET;
+ ifa.sin_len = sizeof(ifa);
+ ifa.sin_addr.s_addr = imsg->addr.s_addr;
+
+ memset(&dst, 0, sizeof(dst));
+ dst.sin_family = AF_INET;
+ dst.sin_len = sizeof(dst);
+
+ memset(&mask, 0, sizeof(mask));
+ mask.sin_family = AF_INET;
+ mask.sin_len = sizeof(mask);
+
+ memset(&gw, 0, sizeof(gw));
+ gw.sin_family = AF_INET;
+ gw.sin_len = sizeof(gw);
+
+ addrnet = imsg->addr.s_addr & imsg->mask.s_addr;
+
+ for (i = 0; i < imsg->routes_len; i++) {
+ dst.sin_addr.s_addr = imsg->routes[i].dst.s_addr;
+ mask.sin_addr.s_addr = imsg->routes[i].mask.s_addr;
+ gw.sin_addr.s_addr = imsg->routes[i].gw.s_addr;
+
+ if (gw.sin_addr.s_addr == INADDR_ANY) {
+ /* direct route */
+ configure_route(rtm_type, imsg->if_index,
+ imsg->rdomain, &dst, &mask, &ifa, NULL,
+ RTF_CLONING);
+ } else if (mask.sin_addr.s_addr == INADDR_ANY) {
+ /* default route */
+ gwnet = gw.sin_addr.s_addr & imsg->mask.s_addr;
+ if (addrnet != gwnet) {
+ /*
+ * The gateway for the default route is outside
+ * the configured prefix. Install a direct
+ * cloning route for the gateway to make the
+ * default route reachable.
+ */
+ mask.sin_addr.s_addr = 0xffffffff;
+ configure_route(rtm_type, imsg->if_index,
+ imsg->rdomain, &gw, &mask, &ifa, NULL,
+ RTF_CLONING);
+ mask.sin_addr.s_addr =
+ imsg->routes[i].mask.s_addr;
+ }
+
+ if (gw.sin_addr.s_addr == ifa.sin_addr.s_addr) {
+ /* directly connected default */
+ configure_route(rtm_type, imsg->if_index,
+ imsg->rdomain, &dst, &mask, &gw, NULL, 0);
+ } else {
+ /* default route via gateway */
+ configure_route(rtm_type, imsg->if_index,
+ imsg->rdomain, &dst, &mask, &gw, &ifa,
+ RTF_GATEWAY);
+ }
+ } else {
+ /* non-default via gateway */
+ configure_route(rtm_type, imsg->if_index, imsg->rdomain,
+ &dst, &mask, &gw, NULL, RTF_GATEWAY);
+ }
+ }
+}
+
#define ROUNDUP(a) \
(((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a))
void
-configure_gateway(struct imsg_configure_interface *imsg, uint8_t rtm_type)
+configure_route(uint8_t rtm_type, uint32_t if_index, int rdomain, struct
+ sockaddr_in *dst, struct sockaddr_in *mask, struct sockaddr_in *gw,
+ struct sockaddr_in *ifa, int rtm_flags)
{
struct rt_msghdr rtm;
struct sockaddr_rtlabel rl;
- struct sockaddr_in dst, gw, mask, ifa;
struct iovec iov[12];
long pad = 0;
int iovcnt = 0, padlen;
rtm.rtm_version = RTM_VERSION;
rtm.rtm_type = rtm_type;
rtm.rtm_msglen = sizeof(rtm);
- rtm.rtm_tableid = imsg->rdomain;
- rtm.rtm_index = imsg->if_index;
+ rtm.rtm_index = if_index;
+ rtm.rtm_tableid = rdomain;
rtm.rtm_seq = ++rtm_seq;
rtm.rtm_priority = RTP_NONE;
- rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFA |
- RTA_LABEL;
- rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC | RTF_MPATH;
+ rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_LABEL;
+ rtm.rtm_flags = RTF_UP | RTF_STATIC | RTF_MPATH | rtm_flags;
+
+ if (ifa)
+ rtm.rtm_addrs |= RTA_IFA;
iov[iovcnt].iov_base = &rtm;
iov[iovcnt++].iov_len = sizeof(rtm);
- memset(&dst, 0, sizeof(dst));
- dst.sin_family = AF_INET;
- dst.sin_len = sizeof(struct sockaddr_in);
-
- iov[iovcnt].iov_base = &dst;
- iov[iovcnt++].iov_len = sizeof(dst);
- rtm.rtm_msglen += sizeof(dst);
- padlen = ROUNDUP(sizeof(dst)) - sizeof(dst);
+ iov[iovcnt].iov_base = dst;
+ iov[iovcnt++].iov_len = dst->sin_len;
+ rtm.rtm_msglen += dst->sin_len;
+ padlen = ROUNDUP(dst->sin_len) - dst->sin_len;
if (padlen > 0) {
iov[iovcnt].iov_base = &pad;
iov[iovcnt++].iov_len = padlen;
rtm.rtm_msglen += padlen;
}
- memset(&gw, 0, sizeof(gw));
- memcpy(&gw.sin_addr, &imsg->router, sizeof(gw.sin_addr));
- gw.sin_family = AF_INET;
- gw.sin_len = sizeof(struct sockaddr_in);
- iov[iovcnt].iov_base = &gw;
- iov[iovcnt++].iov_len = sizeof(gw);
- rtm.rtm_msglen += sizeof(gw);
- padlen = ROUNDUP(sizeof(gw)) - sizeof(gw);
+ iov[iovcnt].iov_base = gw;
+ iov[iovcnt++].iov_len = gw->sin_len;
+ rtm.rtm_msglen += gw->sin_len;
+ padlen = ROUNDUP(gw->sin_len) - gw->sin_len;
if (padlen > 0) {
iov[iovcnt].iov_base = &pad;
iov[iovcnt++].iov_len = padlen;
rtm.rtm_msglen += padlen;
}
- memset(&mask, 0, sizeof(mask));
- mask.sin_family = AF_INET;
- mask.sin_len = sizeof(struct sockaddr_in);
- iov[iovcnt].iov_base = &mask;
- iov[iovcnt++].iov_len = sizeof(mask);
- rtm.rtm_msglen += sizeof(mask);
- padlen = ROUNDUP(sizeof(mask)) - sizeof(mask);
+ iov[iovcnt].iov_base = mask;
+ iov[iovcnt++].iov_len = mask->sin_len;
+ rtm.rtm_msglen += mask->sin_len;
+ padlen = ROUNDUP(mask->sin_len) - mask->sin_len;
if (padlen > 0) {
iov[iovcnt].iov_base = &pad;
iov[iovcnt++].iov_len = padlen;
rtm.rtm_msglen += padlen;
}
- memset(&ifa, 0, sizeof(ifa));
- memcpy(&ifa.sin_addr, &imsg->addr, sizeof(ifa.sin_addr));
- ifa.sin_family = AF_INET;
- ifa.sin_len = sizeof(struct sockaddr_in);
- iov[iovcnt].iov_base = &ifa;
- iov[iovcnt++].iov_len = sizeof(ifa);
- rtm.rtm_msglen += sizeof(ifa);
- padlen = ROUNDUP(sizeof(ifa)) - sizeof(ifa);
- if (padlen > 0) {
- iov[iovcnt].iov_base = &pad;
- iov[iovcnt++].iov_len = padlen;
- rtm.rtm_msglen += padlen;
+ if (ifa) {
+ iov[iovcnt].iov_base = ifa;
+ iov[iovcnt++].iov_len = ifa->sin_len;
+ rtm.rtm_msglen += ifa->sin_len;
+ padlen = ROUNDUP(ifa->sin_len) - ifa->sin_len;
+ if (padlen > 0) {
+ iov[iovcnt].iov_base = &pad;
+ iov[iovcnt++].iov_len = padlen;
+ rtm.rtm_msglen += padlen;
+ }
}
memset(&rl, 0, sizeof(rl));
-/* $OpenBSD: engine.c,v 1.14 2021/05/01 11:51:59 florian Exp $ */
+/* $OpenBSD: engine.c,v 1.15 2021/06/16 14:06:17 florian Exp $ */
/*
* Copyright (c) 2017, 2021 Florian Obser <florian@openbsd.org>
struct in_addr dhcp_server; /* for unicast */
struct in_addr requested_ip;
struct in_addr mask;
- struct in_addr router;
+ struct dhcp_route routes[MAX_DHCP_ROUTES];
+ int routes_len;
struct in_addr nameservers[MAX_RDNS_COUNT];
uint32_t lease_time;
uint32_t renewal_time;
cei.dhcp_server.s_addr = iface->dhcp_server.s_addr;
cei.requested_ip.s_addr = iface->requested_ip.s_addr;
cei.mask.s_addr = iface->mask.s_addr;
- cei.router.s_addr = iface->router.s_addr;
+ cei.routes_len = iface->routes_len;
+ memcpy(cei.routes, iface->routes, sizeof(cei.routes));
memcpy(cei.nameservers, iface->nameservers, sizeof(cei.nameservers));
cei.lease_time = iface->lease_time;
cei.renewal_time = iface->renewal_time;
struct ip *ip;
struct udphdr *udp;
struct dhcp_hdr *dhcp_hdr;
- struct in_addr server_identifier, subnet_mask, router;
+ struct in_addr server_identifier, subnet_mask;
struct in_addr nameservers[MAX_RDNS_COUNT];
+ struct dhcp_route routes[MAX_DHCP_ROUTES];
size_t rem, i;
uint32_t sum, usum, lease_time = 0, renewal_time = 0;
uint32_t rebinding_time = 0;
uint8_t *p, dho = DHO_PAD, dho_len;
uint8_t dhcp_message_type = 0;
+ int routes_len = 0;
char from[sizeof("xx:xx:xx:xx:xx:xx")];
char to[sizeof("xx:xx:xx:xx:xx:xx")];
char hbuf_src[INET_ADDRSTRLEN];
memset(&server_identifier, 0, sizeof(server_identifier));
memset(&subnet_mask, 0, sizeof(subnet_mask));
- memset(&router, 0, sizeof(router));
+ memset(&routes, 0, sizeof(routes));
memset(&nameservers, 0, sizeof(nameservers));
while (rem > 0 && dho != DHO_END) {
rem -= dho_len;
break;
case DHO_ROUTERS:
- if (dho_len < sizeof(router))
+ if (dho_len < sizeof(routes[routes_len].gw))
goto wrong_length;
- if (dho_len % sizeof(router) != 0)
+ if (dho_len % sizeof(routes[routes_len].gw) != 0)
goto wrong_length;
- /* we only use one router */
- memcpy(&router, p, sizeof(router));
- if (log_getverbose() > 1) {
- log_debug("DHO_ROUTER: %s (1/%lu)",
- inet_ntop(AF_INET, &router, hbuf,
- sizeof(hbuf)), dho_len / sizeof(router));
+
+ while (routes_len < MAX_DHCP_ROUTES && dho_len > 0) {
+ memcpy(&routes[routes_len].gw, p,
+ sizeof(routes[routes_len].gw));
+ if (log_getverbose() > 1) {
+ log_debug("DHO_ROUTER: %s",
+ inet_ntop(AF_INET,
+ &routes[routes_len].gw, hbuf,
+ sizeof(hbuf)));
+ }
+ p += sizeof(routes[routes_len].gw);
+ rem -= sizeof(routes[routes_len].gw);
+ dho_len -= sizeof(routes[routes_len].gw);
+ routes_len++;
+ }
+ if (dho_len != 0) {
+ /* ignore > MAX_DHCP_ROUTES routes */
+ p += dho_len;
+ rem -= dho_len;
}
- p += dho_len;
- rem -= dho_len;
break;
case DHO_DOMAIN_NAME_SERVERS:
if (dho_len < sizeof(nameservers[0]))
p += dho_len;
rem -= dho_len;
break;
+ case DHO_CLASSLESS_STATIC_ROUTES: {
+ int prefixlen, compressed_prefixlen;
+
+ while (routes_len < MAX_DHCP_ROUTES && dho_len > 0) {
+ prefixlen = *p;
+ p += 1;
+ rem -= 1;
+ dho_len -= 1;
+
+ if (prefixlen < 0 || prefixlen > 32) {
+ log_warnx("%s: invalid prefixlen: %d",
+ __func__, prefixlen);
+ return;
+ }
+
+ if (prefixlen > 0)
+ routes[routes_len].mask.s_addr =
+ htonl(0xffffffff << (32 -
+ prefixlen));
+ else
+ routes[routes_len].mask.s_addr =
+ INADDR_ANY;
+
+ compressed_prefixlen = (prefixlen + 7) / 8;
+ if (dho_len < compressed_prefixlen)
+ goto wrong_length;
+
+ memcpy(&routes[routes_len].dst, p,
+ compressed_prefixlen);
+ p += compressed_prefixlen;
+ rem -= compressed_prefixlen;
+ dho_len -= compressed_prefixlen;
+
+ if (dho_len < sizeof(routes[routes_len].gw))
+ goto wrong_length;
+
+ memcpy(&routes[routes_len].gw, p,
+ sizeof(routes[routes_len].gw));
+ p += sizeof(routes[routes_len].gw);
+ rem -= sizeof(routes[routes_len].gw);
+ dho_len -= sizeof(routes[routes_len].gw);
+
+ routes_len++;
+ }
+
+ if (dho_len != 0) {
+ /* ignore > MAX_DHCP_ROUTES routes */
+ p += dho_len;
+ rem -= dho_len;
+ }
+ break;
+ }
default:
if (log_getverbose() > 1)
log_debug("DHO_%u, len: %u", dho, dho_len);
iface->server_identifier.s_addr = server_identifier.s_addr;
iface->requested_ip.s_addr = dhcp_hdr->yiaddr.s_addr;
iface->mask.s_addr = subnet_mask.s_addr;
- iface->router.s_addr = router.s_addr;
+ iface->routes_len = routes_len;
+ memcpy(iface->routes, routes, sizeof(iface->routes));
iface->lease_time = lease_time;
iface->renewal_time = renewal_time;
iface->rebinding_time = rebinding_time;
imsg.rdomain = iface->rdomain;
imsg.addr.s_addr = iface->requested_ip.s_addr;
imsg.mask.s_addr = iface->mask.s_addr;
- imsg.router.s_addr = iface->router.s_addr;
+ imsg.routes_len = iface->routes_len;
+ memcpy(imsg.routes, iface->routes, sizeof(imsg.routes));
engine_imsg_compose_main(IMSG_CONFIGURE_INTERFACE, 0, &imsg,
sizeof(imsg));
}
imsg.rdomain = iface->rdomain;
imsg.addr.s_addr = iface->requested_ip.s_addr;
imsg.mask.s_addr = iface->mask.s_addr;
- imsg.router.s_addr = iface->router.s_addr;
+ imsg.routes_len = iface->routes_len;
+ memcpy(imsg.routes, iface->routes, sizeof(imsg.routes));
engine_imsg_compose_main(IMSG_DECONFIGURE_INTERFACE, 0, &imsg,
sizeof(imsg));
iface->dhcp_server.s_addr = INADDR_ANY;
iface->requested_ip.s_addr = INADDR_ANY;
iface->mask.s_addr = INADDR_ANY;
- iface->router.s_addr = INADDR_ANY;
+ iface->routes_len = 0;
+ memset(iface->routes, 0, sizeof(iface->routes));
}
void
-/* $OpenBSD: dhcpleasectl.c,v 1.3 2021/03/23 17:46:20 florian Exp $ */
+/* $OpenBSD: dhcpleasectl.c,v 1.4 2021/06/16 14:06:18 florian Exp $ */
/*
* Copyright (c) 2021 Florian Obser <florian@openbsd.org>
char buf[IF_NAMESIZE], *bufp;
char ipbuf[INET_ADDRSTRLEN];
char maskbuf[INET_ADDRSTRLEN];
- char routerbuf[INET_ADDRSTRLEN];
+ char gwbuf[INET_ADDRSTRLEN];
switch (imsg->hdr.type) {
case IMSG_CTL_SHOW_INTERFACE_INFO:
timespecsub(&now, &cei->request_time, &diff);
memset(ipbuf, 0, sizeof(ipbuf));
memset(maskbuf, 0, sizeof(maskbuf));
- memset(routerbuf, 0, sizeof(routerbuf));
+ memset(gwbuf, 0, sizeof(gwbuf));
if (inet_ntop(AF_INET, &cei->requested_ip, ipbuf,
sizeof(ipbuf)) == NULL)
ipbuf[0] = '\0';
if (inet_ntop(AF_INET, &cei->mask, maskbuf,
sizeof(maskbuf)) == NULL)
maskbuf[0] = '\0';
- if (inet_ntop(AF_INET, &cei->router, routerbuf,
- sizeof(routerbuf)) == NULL)
- routerbuf[0] = '\0';
printf("\t IP: %s/%s\n", ipbuf, maskbuf);
- if (cei->router.s_addr != INADDR_ANY)
- printf("\trouter: %s\n", routerbuf);
+ for (i = 0; i < cei->routes_len; i++) {
+ if (inet_ntop(AF_INET, &cei->routes[i].dst,
+ ipbuf, sizeof(ipbuf)) == NULL)
+ ipbuf[0] = '\0';
+ if (inet_ntop(AF_INET, &cei->routes[i].mask,
+ maskbuf, sizeof(maskbuf)) == NULL)
+ maskbuf[0] = '\0';
+ if (inet_ntop(AF_INET, &cei->routes[i].gw,
+ gwbuf, sizeof(gwbuf)) == NULL)
+ gwbuf[0] = '\0';
+
+ printf("\t%s\t%s/%s - %s\n", i == 0 ? "routes:"
+ : "", ipbuf, maskbuf, gwbuf);
+ }
if (cei->nameservers[0].s_addr != INADDR_ANY) {
printf("\t DNS:");
for (i = 0; i < MAX_RDNS_COUNT &&