We have a winner here. I tested from INIT through to REBINDING and the
behavior matches what's in the RFC now. ok joel@

One cosmetic thing I noticed this time around: log_dhcp_hdr() in
engine.c should be printing dhcp_hdr->xid in host byte order.



.joel

On Fri, Dec 3, 2021 at 5:21 AM Florian Obser <flor...@openbsd.org> wrote:
>
> Last one, Joel spotted one more bug in the previous one, I was missing
> an assignment to dhcp_server in the ACK case.
>
> This is a rewrite of the package construction logic to send the correct
> information in the right states:
>
> 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|
> ---------------------------------------------------------------------
>
> Previous this logic was all over the place and difficult to check.
> This diff moves it all into the engine.c functions
> request_dhcp_discover() and request_dhcp_request().
>
> The frontend then leaves out dhcp options that are set to INADDR_ARPA,
> but it does not need to know in which state the engine is.
>
> OK?
>
>
> 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..3169bd80d40 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;
> @@ -1228,6 +1229,7 @@ parse_dhcp(struct dhcpleased_iface *iface, struct 
> imsg_dhcp *dhcp)
>
>                 clock_gettime(CLOCK_MONOTONIC, &iface->request_time);
>                 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;
>                 iface->mask.s_addr = subnet_mask.s_addr;
>  #ifndef SMALL
> @@ -1354,11 +1356,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 +1376,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 +1387,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 +1464,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.
>

Reply via email to