On Thu, Nov 02, 2017 at 10:26:55PM +0100, Reyk Floeter wrote:
> Hi,
> 
> the problem got reported a few times and a similar diff was floating
> around: vmd's "local interface" implements a simple auto-magic BOOTP
> server, but udhcpc doesn't support BOOTP, which is the predecessor and
> a valid subset of DHCP.  udhcpc is used by busybox and many embedded
> Linux distributions, maybe even by Android.
> 
> The following diff adds minimal DHCP support which works with udhcpc.
> 
> OK?
> 
> Reyk
> 

no objections here, thanks.

-ml

> Index: usr.sbin/vmd/dhcp.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/vmd/dhcp.c,v
> retrieving revision 1.3
> diff -u -p -u -p -r1.3 dhcp.c
> --- usr.sbin/vmd/dhcp.c       24 Apr 2017 07:14:27 -0000      1.3
> +++ usr.sbin/vmd/dhcp.c       2 Nov 2017 21:15:03 -0000
> @@ -22,6 +22,7 @@
>  #include <net/if.h>
>  #include <netinet/in.h>
>  #include <netinet/if_ether.h>
> +#include <arpa/inet.h>
>  
>  #include <stdlib.h>
>  #include <string.h>
> @@ -38,12 +39,13 @@ extern struct vmd *env;
>  ssize_t
>  dhcp_request(struct vionet_dev *dev, char *buf, size_t buflen, char **obuf)
>  {
> -     unsigned char           *respbuf = NULL;
> +     unsigned char           *respbuf = NULL, *op, *oe, dhcptype = 0;
>       ssize_t                  offset, respbuflen = 0;
>       struct packet_ctx        pc;
>       struct dhcp_packet       req, resp;
> -     struct in_addr           in, mask;
> +     struct in_addr           server_addr, mask, client_addr, requested_addr;
>       size_t                   resplen, o;
> +     uint32_t                 ltime;
>  
>       if (buflen < (ssize_t)(BOOTP_MIN_LEN + sizeof(struct ether_header)))
>               return (-1);
> @@ -76,24 +78,54 @@ dhcp_request(struct vionet_dev *dev, cha
>       if (req.ciaddr.s_addr != 0 || req.file[0] != '\0' || req.hops != 0)
>               return (-1);
>  
> +     /* Get a few DHCP options (best effort as we fall back to BOOTP) */
> +     if (memcmp(&req.options,
> +         DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN) == 0) {
> +             memset(&requested_addr, 0, sizeof(requested_addr));
> +             op = req.options + DHCP_OPTIONS_COOKIE_LEN;
> +             oe = req.options + sizeof(req.options);
> +             while (*op != DHO_END && op < oe) {
> +                     if (op[0] == DHO_PAD) {
> +                             op++;
> +                             continue;
> +                     }
> +                     if (op + 1 + op[1] >= oe)
> +                             break;
> +                     if (op[0] == DHO_DHCP_MESSAGE_TYPE &&
> +                         op[1] == 1)
> +                             dhcptype = op[2];
> +                     else if (op[0] == DHO_DHCP_REQUESTED_ADDRESS &&
> +                         op[1] == sizeof(requested_addr))
> +                             memcpy(&requested_addr, &op[2],
> +                                 sizeof(requested_addr));
> +                     op += 2 + op[1];
> +             }
> +     }
> +
>       memset(&resp, 0, sizeof(resp));
>       resp.op = BOOTREPLY;
>       resp.htype = req.htype;
>       resp.hlen = req.hlen;
>       resp.xid = req.xid;
>  
> -     if ((in.s_addr = vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
> +     if ((client_addr.s_addr =
> +         vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
>           dev->vm_vmid, dev->idx, 1)) == 0)
>               return (-1);
> -     memcpy(&resp.yiaddr, &in, sizeof(in));
> -     memcpy(&ss2sin(&pc.pc_dst)->sin_addr, &in, sizeof(in));
> +     memcpy(&resp.yiaddr, &client_addr,
> +         sizeof(client_addr));
> +     memcpy(&ss2sin(&pc.pc_dst)->sin_addr, &client_addr,
> +         sizeof(client_addr));
>       ss2sin(&pc.pc_dst)->sin_port = htons(CLIENT_PORT);
>  
> -     if ((in.s_addr = vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
> +     if ((server_addr.s_addr =
> +         vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
>           dev->vm_vmid, dev->idx, 0)) == 0)
>               return (-1);
> -     memcpy(&resp.siaddr, &in, sizeof(in));
> -     memcpy(&ss2sin(&pc.pc_src)->sin_addr, &in, sizeof(in));
> +     memcpy(&resp.siaddr, &server_addr,
> +         sizeof(server_addr));
> +     memcpy(&ss2sin(&pc.pc_src)->sin_addr, &server_addr,
> +         sizeof(server_addr));
>       ss2sin(&pc.pc_src)->sin_port = htons(SERVER_PORT);
>  
>       /* Packet is already allocated */
> @@ -124,6 +156,44 @@ dhcp_request(struct vionet_dev *dev, cha
>           DHCP_OPTIONS_COOKIE, DHCP_OPTIONS_COOKIE_LEN);
>       o+= DHCP_OPTIONS_COOKIE_LEN;
>  
> +     /* Did we receive a DHCP request or was it just BOOTP? */
> +     if (dhcptype) {
> +             /*
> +              * There is no need for a real state machine as we always
> +              * answer with the same client IP and options for the VM.
> +              */
> +             if (dhcptype == DHCPDISCOVER)
> +                     dhcptype = DHCPOFFER;
> +             else if (dhcptype == DHCPREQUEST &&
> +                 (requested_addr.s_addr == 0 ||
> +                 client_addr.s_addr == requested_addr.s_addr))
> +                     dhcptype = DHCPACK;
> +             else
> +                     dhcptype = DHCPNAK;
> +
> +             resp.options[o++] = DHO_DHCP_MESSAGE_TYPE;
> +             resp.options[o++] = sizeof(dhcptype);
> +             memcpy(&resp.options[o], &dhcptype, sizeof(dhcptype));
> +             o += sizeof(dhcptype);
> +
> +             /* Our lease never changes, use the maximum lease time */
> +             resp.options[o++] = DHO_DHCP_LEASE_TIME;
> +             resp.options[o++] = sizeof(ltime);
> +             ltime = ntohl(0xffffffff);
> +             memcpy(&resp.options[o], &ltime, sizeof(ltime));
> +             o += sizeof(ltime);
> +
> +             resp.options[o++] = DHO_DHCP_SERVER_IDENTIFIER;
> +             resp.options[o++] = sizeof(server_addr);
> +             memcpy(&resp.options[o], &server_addr, sizeof(server_addr));
> +             o += sizeof(server_addr);
> +     }
> +
> +     resp.options[o++] = DHO_DOMAIN_NAME_SERVERS;
> +     resp.options[o++] = sizeof(server_addr);
> +     memcpy(&resp.options[o], &server_addr, sizeof(server_addr));
> +     o += sizeof(server_addr);
> +
>       resp.options[o++] = DHO_SUBNET_MASK;
>       resp.options[o++] = sizeof(mask);
>       mask.s_addr = htonl(0xfffffffe);
> @@ -131,14 +201,14 @@ dhcp_request(struct vionet_dev *dev, cha
>       o += sizeof(mask);
>  
>       resp.options[o++] = DHO_ROUTERS;
> -     resp.options[o++] = sizeof(in);
> -     memcpy(&resp.options[o], &in, sizeof(in));
> -     o += sizeof(in);
> +     resp.options[o++] = sizeof(server_addr);
> +     memcpy(&resp.options[o], &server_addr, sizeof(server_addr));
> +     o += sizeof(server_addr);
>  
>       resp.options[o++] = DHO_DOMAIN_NAME_SERVERS;
> -     resp.options[o++] = sizeof(in);
> -     memcpy(&resp.options[o], &in, sizeof(in));
> -     o += sizeof(in);
> +     resp.options[o++] = sizeof(server_addr);
> +     memcpy(&resp.options[o], &server_addr, sizeof(server_addr));
> +     o += sizeof(server_addr);
>  
>       resp.options[o++] = DHO_END;
>  
> @@ -163,4 +233,3 @@ dhcp_request(struct vionet_dev *dev, cha
>       free(respbuf);
>       return (0);
>  }
> -
> Index: usr.sbin/vmd/vm.conf.5
> ===================================================================
> RCS file: /cvs/src/usr.sbin/vmd/vm.conf.5,v
> retrieving revision 1.23
> diff -u -p -u -p -r1.23 vm.conf.5
> --- usr.sbin/vmd/vm.conf.5    30 Oct 2017 03:37:33 -0000      1.23
> +++ usr.sbin/vmd/vm.conf.5    2 Nov 2017 21:15:03 -0000
> @@ -185,7 +185,7 @@ A
>  .Cm local
>  interface will auto-generate an IPv4 subnet for the interface,
>  configure a gateway address on the VM host side,
> -and run a simple DHCP (BOOTP) server for the VM.
> +and run a simple DHCP/BOOTP server for the VM.
>  This option can be used for layer 3 mode without configuring a switch.
>  .It Cm interfaces Ar count
>  Optional minimum number of network interfaces to add to the VM.
> Index: usr.sbin/vmctl/vmctl.8
> ===================================================================
> RCS file: /cvs/src/usr.sbin/vmctl/vmctl.8,v
> retrieving revision 1.34
> diff -u -p -u -p -r1.34 vmctl.8
> --- usr.sbin/vmctl/vmctl.8    5 Sep 2017 22:06:49 -0000       1.34
> +++ usr.sbin/vmctl/vmctl.8    2 Nov 2017 21:15:03 -0000
> @@ -106,7 +106,7 @@ Add a local network interface.
>  .Xr vmd 8
>  will auto-generate an IPv4 subnet for the interface,
>  configure a gateway address on the VM host side,
> -and run a simple DHCP (BOOTP) server for the VM.
> +and run a simple DHCP/BOOTP server for the VM.
>  See
>  .Sx LOCAL INTERFACES
>  below for more information on how addresses are calculated and assigned when
> 

Reply via email to