I'm making a fine mess off this :( On 2021-11-25 10:23 -07, Joel Knight <knight.j...@gmail.com> wrote: > On Wed, Nov 24, 2021 at 4:46 AM Florian Obser <flor...@openbsd.org> wrote: > >> Thanks, I had indeed missed this. I went through the RFC and found that >> we MUST NOT send the server identifier in rebooting state. While here I >> also made it explicit that we are not sending server identifier in >> rebinding state. This was already the case since we only transition from >> renewing to rebinding, but this is clearer. > > The behavior for this option looks consistent with prior behavior. > However, the RFC confuses me wrt this option and when it should/should > not be set so I'm only relying on the fact that broadcast and unicast > requests are working fine in testing.
yeah, I don't get it either > >> @@ -1486,9 +1488,17 @@ request_dhcp_request(struct dhcpleased_iface *iface) >> iface->xid = arc4random(); >> imsg_req_request.if_index = iface->if_index; >> imsg_req_request.xid = iface->xid; >> + if (iface->state == IF_RENEWING || iface->state == IF_REBINDING) >> + imsg_req_request.ciaddr.s_addr = iface->requested_ip.s_addr; >> + else >> + imsg_req_request.ciaddr.s_addr = 0; >> imsg_req_request.server_identifier.s_addr = >> iface->server_identifier.s_addr; >> - imsg_req_request.requested_ip.s_addr = iface->requested_ip.s_addr; >> + if (iface->state == IF_RENEWING || iface->state == IF_REBINDING) >> + imsg_req_request.requested_ip.s_addr = 0; >> + else >> + imsg_req_request.requested_ip.s_addr = >> + iface->requested_ip.s_addr; > > These two if/else statements could be merged. > >> ssize_t >> build_packet(uint8_t message_type, char *if_name, uint32_t xid, >> - struct ether_addr *hw_address, struct in_addr *requested_ip, >> - struct in_addr *server_identifier) >> + struct ether_addr *hw_address, struct in_addr *ciaddr, struct in_addr >> + *requested_ip, struct in_addr *server_identifier) >> { >> static uint8_t dhcp_cookie[] = DHCP_COOKIE; >> static uint8_t dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1, >> @@ -926,6 +929,7 @@ build_packet(uint8_t message_type, char *if_name, >> uint32_t xid, >> hdr->hops = 0; >> hdr->xid = xid; >> hdr->secs = 0; >> + hdr->ciaddr.s_addr = ciaddr->s_addr; >> memcpy(hdr->chaddr, hw_address, sizeof(*hw_address)); >> p += sizeof(struct dhcp_hdr); >> memcpy(p, dhcp_cookie, sizeof(dhcp_cookie)); > > The problem with build_packet() now is that it will unconditionally > insert opt 50 (requested IP address) even if ciaddr is being set. The > request packet ends up with ciaddr as the correct client IP and opt 50 > as "0.0.0.0". > Thanks for keeping me on my toes. I have decided to refactor the whole thing. I think this makes it easier to understand and the logic is contained in request_dhcp_discover() and request_dhcp_request() in engine.c and not all over the place. I have now also tested all the cases and carefuly compared them with RFC 2131 4.3.6, Table 4. This might now be correct. diff --git dhcpleased.h dhcpleased.h index b3b4938ad3c..6df38be6f5b 100644 --- dhcpleased.h +++ dhcpleased.h @@ -287,15 +287,10 @@ struct imsg_dhcp { uint8_t packet[1500]; }; - -struct imsg_req_discover { - uint32_t if_index; - uint32_t xid; -}; - -struct imsg_req_request { +struct imsg_req_dhcp { uint32_t if_index; uint32_t xid; + struct in_addr ciaddr; struct in_addr requested_ip; struct in_addr server_identifier; struct in_addr dhcp_server; diff --git engine.c engine.c index 17e65fbe789..87cee256600 100644 --- engine.c +++ engine.c @@ -1170,6 +1170,7 @@ parse_dhcp(struct dhcpleased_iface *iface, struct imsg_dhcp *dhcp) return; } iface->server_identifier.s_addr = server_identifier.s_addr; + iface->dhcp_server.s_addr = server_identifier.s_addr; iface->requested_ip.s_addr = dhcp_hdr->yiaddr.s_addr; state_transition(iface, IF_REQUESTING); break; @@ -1354,11 +1355,8 @@ state_transition(struct dhcpleased_iface *iface, enum if_state new_state) case IF_REBOOTING: if (old_state == IF_REBOOTING) iface->timo.tv_sec *= 2; - else { - /* make sure we send broadcast */ - iface->dhcp_server.s_addr = INADDR_ANY; + else iface->timo.tv_sec = START_EXP_BACKOFF; - } request_dhcp_request(iface); break; case IF_REQUESTING: @@ -1377,10 +1375,6 @@ state_transition(struct dhcpleased_iface *iface, enum if_state new_state) break; case IF_RENEWING: if (old_state == IF_BOUND) { - iface->dhcp_server.s_addr = - iface->server_identifier.s_addr; - iface->server_identifier.s_addr = INADDR_ANY; - iface->timo.tv_sec = (iface->rebinding_time - iface->renewal_time) / 2; /* RFC 2131 4.4.5 */ } else @@ -1392,7 +1386,6 @@ state_transition(struct dhcpleased_iface *iface, enum if_state new_state) break; case IF_REBINDING: if (old_state == IF_RENEWING) { - iface->dhcp_server.s_addr = INADDR_ANY; iface->timo.tv_sec = (iface->lease_time - iface->rebinding_time) / 2; /* RFC 2131 4.4.5 */ } else @@ -1470,28 +1463,90 @@ iface_timeout(int fd, short events, void *arg) void request_dhcp_discover(struct dhcpleased_iface *iface) { - struct imsg_req_discover imsg_req_discover; + struct imsg_req_dhcp imsg; - imsg_req_discover.if_index = iface->if_index; - imsg_req_discover.xid = iface->xid = arc4random(); - engine_imsg_compose_frontend(IMSG_SEND_DISCOVER, 0, &imsg_req_discover, - sizeof(imsg_req_discover)); + memset(&imsg, 0, sizeof(imsg)); + + iface->xid = arc4random(); + imsg.if_index = iface->if_index; + imsg.xid = iface->xid; + + /* + * similar to RFC 2131 4.3.6, Table 4 for DHCPDISCOVER + * ------------------------------ + * | | INIT | + * ------------------------------ + * |broad/unicast | broadcast | + * |server-ip | MUST NOT | + * |requested-ip | MAY | + * |ciaddr | zero | + * ------------------------------ + * + * Leaving everything at 0 from the memset results in this table with + * requested-ip not set. + */ + + engine_imsg_compose_frontend(IMSG_SEND_DISCOVER, 0, &imsg, sizeof(imsg)); } void request_dhcp_request(struct dhcpleased_iface *iface) { - struct imsg_req_request imsg_req_request; + struct imsg_req_dhcp imsg; iface->xid = arc4random(); - imsg_req_request.if_index = iface->if_index; - imsg_req_request.xid = iface->xid; - imsg_req_request.server_identifier.s_addr = - iface->server_identifier.s_addr; - imsg_req_request.requested_ip.s_addr = iface->requested_ip.s_addr; - imsg_req_request.dhcp_server.s_addr = iface->dhcp_server.s_addr; - engine_imsg_compose_frontend(IMSG_SEND_REQUEST, 0, &imsg_req_request, - sizeof(imsg_req_request)); + imsg.if_index = iface->if_index; + imsg.xid = iface->xid; + + /* + * RFC 2131 4.3.6, Table 4 + * --------------------------------------------------------------------- + * | |REBOOTING |REQUESTING |RENEWING |REBINDING | + * --------------------------------------------------------------------- + * |broad/unicast |broadcast |broadcast |unicast |broadcast | + * |server-ip |MUST NOT |MUST |MUST NOT |MUST NOT | + * |requested-ip |MUST |MUST |MUST NOT |MUST NOT | + * |ciaddr |zero |zero |IP address |IP address| + * --------------------------------------------------------------------- + */ + switch (iface->state) { + case IF_DOWN: + fatalx("invalid state IF_DOWN in %s", __func__); + break; + case IF_INIT: + fatalx("invalid state IF_INIT in %s", __func__); + break; + case IF_BOUND: + fatalx("invalid state IF_BOUND in %s", __func__); + break; + case IF_REBOOTING: + imsg.dhcp_server.s_addr = INADDR_ANY; /* broadcast */ + imsg.server_identifier.s_addr = INADDR_ANY; /* MUST NOT */ + imsg.requested_ip = iface->requested_ip; /* MUST */ + imsg.ciaddr.s_addr = INADDR_ANY; /* zero */ + break; + case IF_REQUESTING: + imsg.dhcp_server.s_addr = INADDR_ANY; /* broadcast */ + imsg.server_identifier = + iface->server_identifier; /* MUST */ + imsg.requested_ip = iface->requested_ip; /* MUST */ + imsg.ciaddr.s_addr = INADDR_ANY; /* zero */ + break; + case IF_RENEWING: + imsg.dhcp_server = iface->dhcp_server; /* unicast */ + imsg.server_identifier.s_addr = INADDR_ANY; /* MUST NOT */ + imsg.requested_ip.s_addr = INADDR_ANY; /* MUST NOT */ + imsg.ciaddr = iface->requested_ip; /* IP address */ + break; + case IF_REBINDING: + imsg.dhcp_server.s_addr = INADDR_ANY; /* broadcast */ + imsg.server_identifier.s_addr = INADDR_ANY; /* MUST NOT */ + imsg.requested_ip.s_addr = INADDR_ANY; /* MUST NOT */ + imsg.ciaddr = iface->requested_ip; /* IP address */ + break; + } + + engine_imsg_compose_frontend(IMSG_SEND_REQUEST, 0, &imsg, sizeof(imsg)); } void diff --git frontend.c frontend.c index bd1e37fe952..e2c626ce5d2 100644 --- frontend.c +++ frontend.c @@ -70,6 +70,7 @@ struct iface { struct imsg_ifinfo ifinfo; int send_discover; uint32_t xid; + struct in_addr ciaddr; struct in_addr requested_ip; struct in_addr server_identifier; struct in_addr dhcp_server; @@ -90,10 +91,10 @@ int get_xflags(char *); struct iface *get_iface_by_id(uint32_t); void remove_iface(uint32_t); void set_bpfsock(int, uint32_t); +void iface_data_from_imsg(struct iface*, struct imsg_req_dhcp *); ssize_t build_packet(uint8_t, char *, uint32_t, struct ether_addr *, - struct in_addr *, struct in_addr *); -void send_discover(struct iface *); -void send_request(struct iface *); + struct in_addr *, struct in_addr *, struct in_addr *); +void send_packet(uint8_t, struct iface *); void bpf_send_packet(struct iface *, uint8_t *, ssize_t); int udp_send_packet(struct iface *, uint8_t *, ssize_t); #ifndef SMALL @@ -480,39 +481,39 @@ frontend_dispatch_engine(int fd, short event, void *bula) break; #endif /* SMALL */ case IMSG_SEND_DISCOVER: { - struct imsg_req_discover imsg_req_discover; - if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_discover)) + struct imsg_req_dhcp imsg_req_dhcp; + if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_dhcp)) fatalx("%s: IMSG_SEND_DISCOVER wrong " "length: %lu", __func__, IMSG_DATA_SIZE(imsg)); - memcpy(&imsg_req_discover, imsg.data, - sizeof(imsg_req_discover)); - iface = get_iface_by_id(imsg_req_discover.if_index); - if (iface != NULL) { - iface->xid = imsg_req_discover.xid; - send_discover(iface); - } + memcpy(&imsg_req_dhcp, imsg.data, + sizeof(imsg_req_dhcp)); + + iface = get_iface_by_id(imsg_req_dhcp.if_index); + + if (iface == NULL) + break; + + iface_data_from_imsg(iface, &imsg_req_dhcp); + send_packet(DHCPDISCOVER, iface); break; } case IMSG_SEND_REQUEST: { - struct imsg_req_request imsg_req_request; - if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_request)) + struct imsg_req_dhcp imsg_req_dhcp; + if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_dhcp)) fatalx("%s: IMSG_SEND_REQUEST wrong " "length: %lu", __func__, IMSG_DATA_SIZE(imsg)); - memcpy(&imsg_req_request, imsg.data, - sizeof(imsg_req_request)); - iface = get_iface_by_id(imsg_req_request.if_index); - if (iface != NULL) { - iface->xid = imsg_req_request.xid; - iface->requested_ip.s_addr = - imsg_req_request.requested_ip.s_addr; - iface->server_identifier.s_addr = - imsg_req_request.server_identifier.s_addr; - iface->dhcp_server.s_addr = - imsg_req_request.dhcp_server.s_addr; - send_request(iface); - } + memcpy(&imsg_req_dhcp, imsg.data, + sizeof(imsg_req_dhcp)); + + iface = get_iface_by_id(imsg_req_dhcp.if_index); + + if (iface == NULL) + break; + + iface_data_from_imsg(iface, &imsg_req_dhcp); + send_packet(DHCPREQUEST, iface); break; } default: @@ -885,10 +886,20 @@ bpf_receive(int fd, short events, void *arg) } } +void +iface_data_from_imsg(struct iface* iface, struct imsg_req_dhcp *imsg) +{ + iface->xid = imsg->xid; + iface->ciaddr.s_addr = imsg->ciaddr.s_addr; + iface->requested_ip.s_addr = imsg->requested_ip.s_addr; + iface->server_identifier.s_addr = imsg->server_identifier.s_addr; + iface->dhcp_server.s_addr = imsg->dhcp_server.s_addr; +} + ssize_t build_packet(uint8_t message_type, char *if_name, uint32_t xid, - struct ether_addr *hw_address, struct in_addr *requested_ip, - struct in_addr *server_identifier) + struct ether_addr *hw_address, struct in_addr *ciaddr, struct in_addr + *requested_ip, struct in_addr *server_identifier) { static uint8_t dhcp_cookie[] = DHCP_COOKIE; static uint8_t dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1, @@ -926,6 +937,7 @@ build_packet(uint8_t message_type, char *if_name, uint32_t xid, hdr->hops = 0; hdr->xid = xid; hdr->secs = 0; + hdr->ciaddr.s_addr = ciaddr->s_addr; memcpy(hdr->chaddr, hw_address, sizeof(*hw_address)); p += sizeof(struct dhcp_hdr); memcpy(p, dhcp_cookie, sizeof(dhcp_cookie)); @@ -966,20 +978,20 @@ build_packet(uint8_t message_type, char *if_name, uint32_t xid, memcpy(p, dhcp_req_list, sizeof(dhcp_req_list)); p += sizeof(dhcp_req_list); - if (message_type == DHCPREQUEST) { + if (requested_ip->s_addr != INADDR_ANY) { memcpy(dhcp_requested_address + 2, requested_ip, sizeof(*requested_ip)); memcpy(p, dhcp_requested_address, sizeof(dhcp_requested_address)); p += sizeof(dhcp_requested_address); + } - if (server_identifier->s_addr != INADDR_ANY) { - memcpy(dhcp_server_identifier + 2, server_identifier, - sizeof(*server_identifier)); - memcpy(p, dhcp_server_identifier, - sizeof(dhcp_server_identifier)); - p += sizeof(dhcp_server_identifier); - } + if (server_identifier->s_addr != INADDR_ANY) { + memcpy(dhcp_server_identifier + 2, server_identifier, + sizeof(*server_identifier)); + memcpy(p, dhcp_server_identifier, + sizeof(dhcp_server_identifier)); + p += sizeof(dhcp_server_identifier); } *p = DHO_END; @@ -995,7 +1007,7 @@ build_packet(uint8_t message_type, char *if_name, uint32_t xid, } void -send_discover(struct iface *iface) +send_packet(uint8_t message_type, struct iface *iface) { ssize_t pkt_len; char ifnamebuf[IF_NAMESIZE], *if_name; @@ -1004,27 +1016,17 @@ send_discover(struct iface *iface) iface->send_discover = 1; return; } - iface->send_discover = 0; - - if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf); - log_debug("DHCPDISCOVER on %s", if_name == NULL ? "?" : if_name); - pkt_len = build_packet(DHCPDISCOVER, if_name, iface->xid, - &iface->ifinfo.hw_address, &iface->requested_ip, NULL); - bpf_send_packet(iface, dhcp_packet, pkt_len); -} + iface->send_discover = 0; -void -send_request(struct iface *iface) -{ - ssize_t pkt_len; - char ifnamebuf[IF_NAMESIZE], *if_name; + if ((if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf)) == NULL) + return; /* iface went away, nothing to do */ - if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf); - log_debug("DHCPREQUEST on %s", if_name == NULL ? "?" : if_name); + log_debug("%s on %s", message_type == DHCPDISCOVER ? "DHCPDISCOVER" : + "DHCPREQUEST", if_name); - pkt_len = build_packet(DHCPREQUEST, if_name, iface->xid, - &iface->ifinfo.hw_address, &iface->requested_ip, + pkt_len = build_packet(message_type, if_name, iface->xid, + &iface->ifinfo.hw_address, &iface->ciaddr, &iface->requested_ip, &iface->server_identifier); if (iface->dhcp_server.s_addr != INADDR_ANY) { if (udp_send_packet(iface, dhcp_packet, pkt_len) == -1) @@ -1162,7 +1164,7 @@ set_bpfsock(int bpfsock, uint32_t if_index) EV_PERSIST, bpf_receive, iface); event_add(&iface->bpfev.ev, NULL); if (iface->send_discover) - send_discover(iface); + send_packet(DHCPDISCOVER, iface); } } -- I'm not entirely sure you are real.