275 lines
6.5 KiB
C
275 lines
6.5 KiB
C
/* $NetBSD: main.c,v 1.4 2015/06/16 22:54:10 christos Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2011 Antti Kantee. All Rights Reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <rump/rump_syscalls.h>
|
|
#include <rump/rumpclient.h>
|
|
|
|
#include "configure.h"
|
|
#include "dhcp.h"
|
|
#include "net.h"
|
|
|
|
struct interface *ifaces;
|
|
|
|
__dead static void
|
|
usage(void)
|
|
{
|
|
|
|
fprintf(stderr, "Usage: %s ifname\n", getprogname());
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
get_hwaddr(struct interface *ifp)
|
|
{
|
|
struct if_laddrreq iflr;
|
|
struct sockaddr_dl *sdl;
|
|
int s, sverrno;
|
|
|
|
memset(&iflr, 0, sizeof(iflr));
|
|
strlcpy(iflr.iflr_name, ifp->name, sizeof(iflr.iflr_name));
|
|
iflr.addr.ss_family = AF_LINK;
|
|
|
|
sdl = satosdl(&iflr.addr);
|
|
sdl->sdl_alen = ETHER_ADDR_LEN;
|
|
|
|
if ((s = rump_sys_socket(AF_LINK, SOCK_DGRAM, 0)) == -1)
|
|
return -1;
|
|
|
|
if (rump_sys_ioctl(s, SIOCGLIFADDR, &iflr) == -1) {
|
|
sverrno = errno;
|
|
rump_sys_close(s);
|
|
errno = sverrno;
|
|
return -1;
|
|
}
|
|
|
|
/* XXX: is that the right way to copy the link address? */
|
|
memcpy(ifp->hwaddr, sdl->sdl_data+strlen(ifp->name), ETHER_ADDR_LEN);
|
|
ifp->hwlen = ETHER_ADDR_LEN;
|
|
ifp->family = ARPHRD_ETHER;
|
|
|
|
rump_sys_close(s);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
send_discover(struct interface *ifp)
|
|
{
|
|
struct dhcp_message *dhcp;
|
|
uint8_t *udp;
|
|
ssize_t mlen, ulen;
|
|
struct in_addr ia;
|
|
|
|
memset(&ia, 0, sizeof(ia));
|
|
|
|
mlen = make_message(&dhcp, ifp, DHCP_DISCOVER);
|
|
ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia);
|
|
if (send_raw_packet(ifp, ETHERTYPE_IP, udp, ulen) == -1)
|
|
err(EXIT_FAILURE, "sending discover failed");
|
|
}
|
|
|
|
static void
|
|
send_request(struct interface *ifp)
|
|
{
|
|
struct dhcp_message *dhcp;
|
|
uint8_t *udp;
|
|
ssize_t mlen, ulen;
|
|
struct in_addr ia;
|
|
|
|
memset(&ia, 0, sizeof(ia));
|
|
|
|
mlen = make_message(&dhcp, ifp, DHCP_REQUEST);
|
|
ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia);
|
|
if (send_raw_packet(ifp, ETHERTYPE_IP, udp, ulen) == -1)
|
|
err(EXIT_FAILURE, "sending discover failed");
|
|
}
|
|
|
|
/* wait for 5s by default */
|
|
#define RESPWAIT 5000
|
|
static void
|
|
get_network(struct interface *ifp, uint8_t **rawp,
|
|
const struct dhcp_message **dhcpp)
|
|
{
|
|
struct pollfd pfd;
|
|
const struct dhcp_message *dhcp;
|
|
const uint8_t *data;
|
|
uint8_t *raw;
|
|
ssize_t n;
|
|
|
|
pfd.fd = ifp->raw_fd;
|
|
pfd.events = POLLIN;
|
|
|
|
raw = xmalloc(udp_dhcp_len);
|
|
for (;;) {
|
|
switch (rump_sys_poll(&pfd, 1, RESPWAIT)) {
|
|
case 0:
|
|
errx(EXIT_FAILURE, "timed out waiting for response");
|
|
case -1:
|
|
err(EXIT_FAILURE, "poll failed");
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ((n = get_raw_packet(ifp, ETHERTYPE_IP,
|
|
raw, udp_dhcp_len)) < 1)
|
|
continue;
|
|
|
|
if (valid_udp_packet(raw, n, NULL) == -1) {
|
|
fprintf(stderr, "invalid packet received. retrying\n");
|
|
continue;
|
|
}
|
|
|
|
n = get_udp_data(&data, raw);
|
|
if ((size_t)n > sizeof(*dhcp)) {
|
|
fprintf(stderr, "invalid packet size. retrying\n");
|
|
continue;
|
|
}
|
|
dhcp = (const void *)data;
|
|
|
|
/* XXX: what if packet is too small? */
|
|
|
|
/* some sanity checks */
|
|
if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
|
|
/* ignore */
|
|
continue;
|
|
}
|
|
|
|
if (ifp->state->xid != dhcp->xid) {
|
|
fprintf(stderr, "invalid transaction. retrying\n");
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
*rawp = raw;
|
|
*dhcpp = dhcp;
|
|
}
|
|
|
|
static void
|
|
get_offer(struct interface *ifp)
|
|
{
|
|
const struct dhcp_message *dhcp;
|
|
uint8_t *raw;
|
|
uint8_t type;
|
|
|
|
get_network(ifp, &raw, &dhcp);
|
|
|
|
get_option_uint8(&type, dhcp, DHO_MESSAGETYPE);
|
|
switch (type) {
|
|
case DHCP_OFFER:
|
|
break;
|
|
case DHCP_NAK:
|
|
errx(EXIT_FAILURE, "got NAK from dhcp server");
|
|
default:
|
|
errx(EXIT_FAILURE, "didn't receive offer");
|
|
}
|
|
|
|
ifp->state->offer = xzalloc(sizeof(*ifp->state->offer));
|
|
memcpy(ifp->state->offer, dhcp, sizeof(*ifp->state->offer));
|
|
ifp->state->lease.addr.s_addr = dhcp->yiaddr;
|
|
ifp->state->lease.cookie = dhcp->cookie;
|
|
free(raw);
|
|
}
|
|
|
|
static void
|
|
get_ack(struct interface *ifp)
|
|
{
|
|
const struct dhcp_message *dhcp;
|
|
uint8_t *raw;
|
|
uint8_t type;
|
|
|
|
get_network(ifp, &raw, &dhcp);
|
|
get_option_uint8(&type, dhcp, DHO_MESSAGETYPE);
|
|
if (type != DHCP_ACK)
|
|
errx(EXIT_FAILURE, "didn't receive ack");
|
|
|
|
ifp->state->new = ifp->state->offer;
|
|
get_lease(&ifp->state->lease, ifp->state->new);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct interface *ifp;
|
|
struct if_options *ifo;
|
|
const int mib[] = { CTL_KERN, KERN_HOSTNAME };
|
|
size_t hlen;
|
|
|
|
setprogname(argv[0]);
|
|
if (argc != 2)
|
|
usage();
|
|
|
|
if (rumpclient_init() == -1)
|
|
err(EXIT_FAILURE, "init failed");
|
|
|
|
if (init_sockets() == -1)
|
|
err(EXIT_FAILURE, "failed to init sockets");
|
|
if ((ifp = init_interface(argv[1])) == NULL)
|
|
err(EXIT_FAILURE, "cannot init %s", argv[1]);
|
|
ifaces = ifp;
|
|
if (open_socket(ifp, ETHERTYPE_IP) == -1)
|
|
err(EXIT_FAILURE, "bpf");
|
|
up_interface(ifp);
|
|
|
|
ifp->state = xzalloc(sizeof(*ifp->state));
|
|
ifp->state->options = ifo = xzalloc(sizeof(*ifp->state->options));
|
|
ifp->state->xid = arc4random();
|
|
|
|
hlen = sizeof(ifo->hostname);
|
|
if (rump_sys___sysctl(mib, __arraycount(mib), ifo->hostname, &hlen,
|
|
NULL, 0) == -1)
|
|
snprintf(ifo->hostname, sizeof(ifo->hostname),
|
|
"unknown.hostname");
|
|
ifo->options = DHCPCD_GATEWAY | DHCPCD_HOSTNAME;
|
|
|
|
if (get_hwaddr(ifp) == -1)
|
|
err(EXIT_FAILURE, "failed to get hwaddr for %s", ifp->name);
|
|
|
|
send_discover(ifp);
|
|
get_offer(ifp);
|
|
send_request(ifp);
|
|
get_ack(ifp);
|
|
|
|
configure(ifp);
|
|
|
|
return 0;
|
|
}
|