Home | History | Annotate | Line # | Download | only in rump_dhcpclient
      1 /*	$NetBSD: main.c,v 1.4 2015/06/16 22:54:10 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2011 Antti Kantee.  All Rights Reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     18  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     25  * SUCH DAMAGE.
     26  */
     27 
     28 #include <sys/types.h>
     29 #include <sys/ioctl.h>
     30 #include <sys/socket.h>
     31 #include <sys/sysctl.h>
     32 
     33 #include <net/if.h>
     34 #include <net/if_dl.h>
     35 
     36 #include <err.h>
     37 #include <errno.h>
     38 #include <poll.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 
     42 #include <rump/rump_syscalls.h>
     43 #include <rump/rumpclient.h>
     44 
     45 #include "configure.h"
     46 #include "dhcp.h"
     47 #include "net.h"
     48 
     49 struct interface *ifaces;
     50 
     51 __dead static void
     52 usage(void)
     53 {
     54 
     55 	fprintf(stderr, "Usage: %s ifname\n", getprogname());
     56 	exit(1);
     57 }
     58 
     59 int
     60 get_hwaddr(struct interface *ifp)
     61 {
     62 	struct if_laddrreq iflr;
     63 	struct sockaddr_dl *sdl;
     64 	int s, sverrno;
     65 
     66 	memset(&iflr, 0, sizeof(iflr));
     67 	strlcpy(iflr.iflr_name, ifp->name, sizeof(iflr.iflr_name));
     68 	iflr.addr.ss_family = AF_LINK;
     69 
     70 	sdl = satosdl(&iflr.addr);
     71 	sdl->sdl_alen = ETHER_ADDR_LEN;
     72 
     73 	if ((s = rump_sys_socket(AF_LINK, SOCK_DGRAM, 0)) == -1)
     74 		return -1;
     75 
     76 	if (rump_sys_ioctl(s, SIOCGLIFADDR, &iflr) == -1) {
     77 		sverrno = errno;
     78 		rump_sys_close(s);
     79 		errno = sverrno;
     80 		return -1;
     81 	}
     82 
     83 	/* XXX: is that the right way to copy the link address? */
     84 	memcpy(ifp->hwaddr, sdl->sdl_data+strlen(ifp->name), ETHER_ADDR_LEN);
     85 	ifp->hwlen = ETHER_ADDR_LEN;
     86 	ifp->family = ARPHRD_ETHER;
     87 
     88 	rump_sys_close(s);
     89 	return 0;
     90 }
     91 
     92 static void
     93 send_discover(struct interface *ifp)
     94 {
     95 	struct dhcp_message *dhcp;
     96 	uint8_t *udp;
     97 	ssize_t mlen, ulen;
     98 	struct in_addr ia;
     99 
    100 	memset(&ia, 0, sizeof(ia));
    101 
    102 	mlen = make_message(&dhcp, ifp, DHCP_DISCOVER);
    103 	ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia);
    104 	if (send_raw_packet(ifp, ETHERTYPE_IP, udp, ulen) == -1)
    105 		err(EXIT_FAILURE, "sending discover failed");
    106 }
    107 
    108 static void
    109 send_request(struct interface *ifp)
    110 {
    111 	struct dhcp_message *dhcp;
    112 	uint8_t *udp;
    113 	ssize_t mlen, ulen;
    114 	struct in_addr ia;
    115 
    116 	memset(&ia, 0, sizeof(ia));
    117 
    118 	mlen = make_message(&dhcp, ifp, DHCP_REQUEST);
    119 	ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia);
    120 	if (send_raw_packet(ifp, ETHERTYPE_IP, udp, ulen) == -1)
    121 		err(EXIT_FAILURE, "sending discover failed");
    122 }
    123 
    124 /* wait for 5s by default */
    125 #define RESPWAIT 5000
    126 static void
    127 get_network(struct interface *ifp, uint8_t **rawp,
    128 	const struct dhcp_message **dhcpp)
    129 {
    130 	struct pollfd pfd;
    131 	const struct dhcp_message *dhcp;
    132 	const uint8_t *data;
    133 	uint8_t *raw;
    134 	ssize_t n;
    135 
    136 	pfd.fd = ifp->raw_fd;
    137 	pfd.events = POLLIN;
    138 
    139 	raw = xmalloc(udp_dhcp_len);
    140 	for (;;) {
    141 		switch (rump_sys_poll(&pfd, 1, RESPWAIT)) {
    142 		case 0:
    143 			errx(EXIT_FAILURE, "timed out waiting for response");
    144 		case -1:
    145 			err(EXIT_FAILURE, "poll failed");
    146 		default:
    147 			break;
    148 		}
    149 
    150 		if ((n = get_raw_packet(ifp, ETHERTYPE_IP,
    151 		    raw, udp_dhcp_len)) < 1)
    152 			continue;
    153 
    154 		if (valid_udp_packet(raw, n, NULL) == -1) {
    155 			fprintf(stderr, "invalid packet received.  retrying\n");
    156 			continue;
    157 		}
    158 
    159 		n = get_udp_data(&data, raw);
    160 		if ((size_t)n > sizeof(*dhcp)) {
    161 			fprintf(stderr, "invalid packet size.  retrying\n");
    162 			continue;
    163 		}
    164 		dhcp = (const void *)data;
    165 
    166 		/* XXX: what if packet is too small? */
    167 
    168 		/* some sanity checks */
    169 		if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
    170 			/* ignore */
    171 			continue;
    172 		}
    173 
    174 		if (ifp->state->xid != dhcp->xid) {
    175 			fprintf(stderr, "invalid transaction.  retrying\n");
    176 			continue;
    177 		}
    178 
    179 		break;
    180 	}
    181 
    182 	*rawp = raw;
    183 	*dhcpp = dhcp;
    184 }
    185 
    186 static void
    187 get_offer(struct interface *ifp)
    188 {
    189 	const struct dhcp_message *dhcp;
    190 	uint8_t *raw;
    191 	uint8_t type;
    192 
    193 	get_network(ifp, &raw, &dhcp);
    194 
    195 	get_option_uint8(&type, dhcp, DHO_MESSAGETYPE);
    196 	switch (type) {
    197 	case DHCP_OFFER:
    198 		break;
    199 	case DHCP_NAK:
    200 		errx(EXIT_FAILURE, "got NAK from dhcp server");
    201 	default:
    202 		errx(EXIT_FAILURE, "didn't receive offer");
    203 	}
    204 
    205 	ifp->state->offer = xzalloc(sizeof(*ifp->state->offer));
    206 	memcpy(ifp->state->offer, dhcp, sizeof(*ifp->state->offer));
    207 	ifp->state->lease.addr.s_addr = dhcp->yiaddr;
    208 	ifp->state->lease.cookie = dhcp->cookie;
    209 	free(raw);
    210 }
    211 
    212 static void
    213 get_ack(struct interface *ifp)
    214 {
    215 	const struct dhcp_message *dhcp;
    216 	uint8_t *raw;
    217 	uint8_t type;
    218 
    219 	get_network(ifp, &raw, &dhcp);
    220 	get_option_uint8(&type, dhcp, DHO_MESSAGETYPE);
    221 	if (type != DHCP_ACK)
    222 		errx(EXIT_FAILURE, "didn't receive ack");
    223 
    224 	ifp->state->new = ifp->state->offer;
    225 	get_lease(&ifp->state->lease, ifp->state->new);
    226 }
    227 
    228 int
    229 main(int argc, char *argv[])
    230 {
    231 	struct interface *ifp;
    232 	struct if_options *ifo;
    233 	const int mib[] = { CTL_KERN, KERN_HOSTNAME };
    234 	size_t hlen;
    235 
    236 	setprogname(argv[0]);
    237 	if (argc != 2)
    238 		usage();
    239 
    240 	if (rumpclient_init() == -1)
    241 		err(EXIT_FAILURE, "init failed");
    242 
    243 	if (init_sockets() == -1)
    244 		err(EXIT_FAILURE, "failed to init sockets");
    245 	if ((ifp = init_interface(argv[1])) == NULL)
    246 		err(EXIT_FAILURE, "cannot init %s", argv[1]);
    247 	ifaces = ifp;
    248 	if (open_socket(ifp, ETHERTYPE_IP) == -1)
    249 		err(EXIT_FAILURE, "bpf");
    250 	up_interface(ifp);
    251 
    252 	ifp->state = xzalloc(sizeof(*ifp->state));
    253 	ifp->state->options = ifo = xzalloc(sizeof(*ifp->state->options));
    254 	ifp->state->xid = arc4random();
    255 
    256 	hlen = sizeof(ifo->hostname);
    257 	if (rump_sys___sysctl(mib, __arraycount(mib), ifo->hostname, &hlen,
    258 	    NULL, 0) == -1)
    259 		snprintf(ifo->hostname, sizeof(ifo->hostname),
    260 		    "unknown.hostname");
    261 	ifo->options = DHCPCD_GATEWAY | DHCPCD_HOSTNAME;
    262 
    263 	if (get_hwaddr(ifp) == -1)
    264 		err(EXIT_FAILURE, "failed to get hwaddr for %s", ifp->name);
    265 
    266 	send_discover(ifp);
    267 	get_offer(ifp);
    268 	send_request(ifp);
    269 	get_ack(ifp);
    270 
    271 	configure(ifp);
    272 
    273 	return 0;
    274 }
    275