#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/uio.h>
#include <sys/utsname.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <errno.h>
#include <event.h>
#include <ifaddrs.h>
#include <imsg.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "log.h"
#include "dhcp6leased.h"
#include "frontend.h"
#include "control.h"
#define ALL_DHCP_RELAY_AGENTS_AND_SERVERS "ff02::1:2"
#define ROUTE_SOCKET_BUF_SIZE 16384
struct iface {
LIST_ENTRY(iface) entries;
struct event udpev;
struct imsg_ifinfo ifinfo;
int send_solicit;
int elapsed_time;
uint8_t xid[XID_SIZE];
int serverid_len;
uint8_t serverid[SERVERID_SIZE];
struct prefix pds[MAX_IA];
};
__dead void frontend_shutdown(void);
void frontend_sig_handler(int, short, void *);
void frontend_startup(void);
void update_iface(uint32_t);
void route_receive(int, short, void *);
void handle_route_message(struct rt_msghdr *, struct sockaddr **);
void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
void udp_receive(int, short, void *);
int get_flags(char *);
struct iface *get_iface_by_id(uint32_t);
struct iface *get_iface_by_name(const char *);
void remove_iface(uint32_t);
void set_udpsock(int, uint32_t);
void iface_data_from_imsg(struct iface*, struct imsg_req_dhcp *);
ssize_t build_packet(uint8_t, struct iface *, char *);
void send_packet(uint8_t, struct iface *);
int iface_conf_cmp(struct iface_conf *, struct iface_conf *);
LIST_HEAD(, iface) interfaces;
struct dhcp6leased_conf *frontend_conf;
static struct imsgev *iev_main;
static struct imsgev *iev_engine;
struct event ev_route;
struct sockaddr_in6 dst;
int ioctlsock;
uint8_t dhcp_packet[1500];
static struct dhcp_duid duid;
char *vendor_class_data;
int vendor_class_len;
void
frontend_sig_handler(int sig, short event, void *bula)
{
switch (sig) {
case SIGINT:
case SIGTERM:
frontend_shutdown();
default:
fatalx("unexpected signal");
}
}
void
frontend(int debug, int verbose)
{
struct event ev_sigint, ev_sigterm;
struct passwd *pw;
struct utsname utsname;
frontend_conf = config_new_empty();
log_init(debug, LOG_DAEMON);
log_setverbose(verbose);
if ((pw = getpwnam(DHCP6LEASED_USER)) == NULL)
fatal("getpwnam");
if (chdir("/") == -1)
fatal("chdir(\"/\")");
if (unveil("/", "") == -1)
fatal("unveil /");
if (unveil(NULL, NULL) == -1)
fatal("unveil");
setproctitle("%s", "frontend");
log_procinit("frontend");
if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1)
fatal("socket");
if (setgroups(1, &pw->pw_gid) ||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
fatal("can't drop privileges");
if (pledge("stdio unix recvfd route", NULL) == -1)
fatal("pledge");
if (uname(&utsname) == -1)
fatal("uname");
vendor_class_len = asprintf(&vendor_class_data, "%s %s %s",
utsname.sysname, utsname.release, utsname.machine);
if (vendor_class_len == -1)
fatal("Cannot generate vendor-class-data");
memset(&dst, 0, sizeof(dst));
dst.sin6_family = AF_INET6;
if (inet_pton(AF_INET6, ALL_DHCP_RELAY_AGENTS_AND_SERVERS,
&dst.sin6_addr.s6_addr) != 1)
fatal("inet_pton");
dst.sin6_port = ntohs(SERVER_PORT);
event_init();
signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL);
signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL);
signal_add(&ev_sigint, NULL);
signal_add(&ev_sigterm, NULL);
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN);
if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
fatal(NULL);
if (imsgbuf_init(&iev_main->ibuf, 3) == -1)
fatal(NULL);
imsgbuf_allow_fdpass(&iev_main->ibuf);
iev_main->handler = frontend_dispatch_main;
iev_main->events = EV_READ;
event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
iev_main->handler, iev_main);
event_add(&iev_main->ev, NULL);
LIST_INIT(&interfaces);
event_dispatch();
frontend_shutdown();
}
__dead void
frontend_shutdown(void)
{
imsgbuf_write(&iev_engine->ibuf);
imsgbuf_clear(&iev_engine->ibuf);
close(iev_engine->ibuf.fd);
imsgbuf_write(&iev_main->ibuf);
imsgbuf_clear(&iev_main->ibuf);
close(iev_main->ibuf.fd);
config_clear(frontend_conf);
free(iev_engine);
free(iev_main);
log_info("frontend exiting");
exit(0);
}
int
frontend_imsg_compose_main(int type, pid_t pid, void *data,
uint16_t datalen)
{
return (imsg_compose_event(iev_main, type, 0, pid, -1, data,
datalen));
}
int
frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid,
void *data, uint16_t datalen)
{
return (imsg_compose_event(iev_engine, type, peerid, pid, -1,
data, datalen));
}
void
frontend_dispatch_main(int fd, short event, void *bula)
{
static struct dhcp6leased_conf *nconf;
static struct iface_conf *iface_conf;
static struct iface_ia_conf *iface_ia_conf;
struct iface_pd_conf *iface_pd_conf;
struct imsg imsg;
struct imsgev *iev = bula;
struct imsgbuf *ibuf = &iev->ibuf;
ssize_t n;
int shut = 0, udpsock, if_index;
if (event & EV_READ) {
if ((n = imsgbuf_read(ibuf)) == -1)
fatal("imsgbuf_read error");
if (n == 0)
shut = 1;
}
if (event & EV_WRITE) {
if (imsgbuf_write(ibuf) == -1) {
if (errno == EPIPE)
shut = 1;
else
fatal("imsgbuf_write");
}
}
for (;;) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
fatal("%s: imsg_get error", __func__);
if (n == 0)
break;
switch (imsg.hdr.type) {
case IMSG_SOCKET_IPC:
if (iev_engine)
fatalx("%s: received unexpected imsg fd "
"to frontend", __func__);
if ((fd = imsg_get_fd(&imsg)) == -1)
fatalx("%s: expected to receive imsg fd to "
"frontend but didn't receive any",
__func__);
iev_engine = malloc(sizeof(struct imsgev));
if (iev_engine == NULL)
fatal(NULL);
if (imsgbuf_init(&iev_engine->ibuf, fd) == -1)
fatal(NULL);
iev_engine->handler = frontend_dispatch_engine;
iev_engine->events = EV_READ;
event_set(&iev_engine->ev, iev_engine->ibuf.fd,
iev_engine->events, iev_engine->handler, iev_engine);
event_add(&iev_engine->ev, NULL);
break;
case IMSG_UDPSOCK:
if ((udpsock = imsg_get_fd(&imsg)) == -1)
fatalx("%s: expected to receive imsg "
"udp fd but didn't receive any",
__func__);
if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
fatalx("%s: IMSG_UDPSOCK wrong length: "
"%lu", __func__, IMSG_DATA_SIZE(imsg));
memcpy(&if_index, imsg.data, sizeof(if_index));
set_udpsock(udpsock, if_index);
break;
case IMSG_ROUTESOCK:
if ((fd = imsg_get_fd(&imsg)) == -1)
fatalx("%s: expected to receive imsg "
"routesocket fd but didn't receive any",
__func__);
event_set(&ev_route, fd, EV_READ | EV_PERSIST,
route_receive, NULL);
break;
case IMSG_UUID:
if (IMSG_DATA_SIZE(imsg) != sizeof(duid.uuid))
fatalx("%s: IMSG_UUID wrong length: "
"%lu", __func__, IMSG_DATA_SIZE(imsg));
duid.type = htons(DUID_UUID_TYPE);
memcpy(duid.uuid, imsg.data, sizeof(duid.uuid));
break;
case IMSG_STARTUP:
frontend_startup();
break;
case IMSG_RECONF_CONF:
if (nconf != NULL)
fatalx("%s: IMSG_RECONF_CONF already in "
"progress", __func__);
if (IMSG_DATA_SIZE(imsg) !=
sizeof(struct dhcp6leased_conf))
fatalx("%s: IMSG_RECONF_CONF wrong length: %lu",
__func__, IMSG_DATA_SIZE(imsg));
if ((nconf = malloc(sizeof(struct dhcp6leased_conf))) ==
NULL)
fatal(NULL);
memcpy(nconf, imsg.data,
sizeof(struct dhcp6leased_conf));
SIMPLEQ_INIT(&nconf->iface_list);
break;
case IMSG_RECONF_IFACE:
if (IMSG_DATA_SIZE(imsg) != sizeof(struct
iface_conf))
fatalx("%s: IMSG_RECONF_IFACE wrong length: "
"%lu", __func__, IMSG_DATA_SIZE(imsg));
if ((iface_conf = malloc(sizeof(struct iface_conf)))
== NULL)
fatal(NULL);
memcpy(iface_conf, imsg.data, sizeof(struct
iface_conf));
if (iface_conf->name[sizeof(iface_conf->name) - 1]
!= '\0')
fatalx("%s: IMSG_RECONF_IFACE invalid name",
__func__);
SIMPLEQ_INIT(&iface_conf->iface_ia_list);
SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
iface_conf, entry);
iface_conf->ia_count = 0;
break;
case IMSG_RECONF_IFACE_IA:
if (IMSG_DATA_SIZE(imsg) != sizeof(struct
iface_ia_conf))
fatalx("%s: IMSG_RECONF_IFACE_IA wrong "
"length: %lu", __func__,
IMSG_DATA_SIZE(imsg));
if ((iface_ia_conf =
malloc(sizeof(struct iface_ia_conf))) == NULL)
fatal(NULL);
memcpy(iface_ia_conf, imsg.data, sizeof(struct
iface_ia_conf));
SIMPLEQ_INIT(&iface_ia_conf->iface_pd_list);
SIMPLEQ_INSERT_TAIL(&iface_conf->iface_ia_list,
iface_ia_conf, entry);
iface_ia_conf->id = iface_conf->ia_count++;
if (iface_conf->ia_count > MAX_IA)
fatalx("Too many prefix delegation requests.");
break;
case IMSG_RECONF_IFACE_PD:
if (IMSG_DATA_SIZE(imsg) != sizeof(struct
iface_pd_conf))
fatalx("%s: IMSG_RECONF_IFACE_PD wrong length: "
"%lu", __func__, IMSG_DATA_SIZE(imsg));
if ((iface_pd_conf =
malloc(sizeof(struct iface_pd_conf))) == NULL)
fatal(NULL);
memcpy(iface_pd_conf, imsg.data, sizeof(struct
iface_pd_conf));
if (iface_pd_conf->name[sizeof(iface_pd_conf->name) - 1]
!= '\0')
fatalx("%s: IMSG_RECONF_IFACE_PD invalid name",
__func__);
SIMPLEQ_INSERT_TAIL(&iface_ia_conf->iface_pd_list,
iface_pd_conf, entry);
break;
case IMSG_RECONF_IFACE_IA_END:
iface_ia_conf = NULL;
break;
case IMSG_RECONF_IFACE_END:
iface_conf = NULL;
break;
case IMSG_RECONF_END: {
int i;
int *ifaces;
char ifnamebuf[IF_NAMESIZE], *if_name;
if (nconf == NULL)
fatalx("%s: IMSG_RECONF_END without "
"IMSG_RECONF_CONF", __func__);
ifaces = changed_ifaces(frontend_conf, nconf);
merge_config(frontend_conf, nconf);
nconf = NULL;
for (i = 0; ifaces[i] != 0; i++) {
if_index = ifaces[i];
if_name = if_indextoname(if_index, ifnamebuf);
log_debug("changed iface: %s[%d]", if_name !=
NULL ? if_name : "<unknown>", if_index);
update_iface(if_index);
frontend_imsg_compose_engine(
IMSG_REQUEST_REBOOT, 0, 0, &if_index,
sizeof(if_index));
}
free(ifaces);
break;
}
case IMSG_CONTROLFD:
if ((fd = imsg_get_fd(&imsg)) == -1)
fatalx("%s: expected to receive imsg "
"control fd but didn't receive any",
__func__);
control_listen(fd);
break;
case IMSG_CTL_END:
control_imsg_relay(&imsg);
break;
default:
log_debug("%s: error handling imsg %d", __func__,
imsg.hdr.type);
break;
}
imsg_free(&imsg);
}
if (!shut)
imsg_event_add(iev);
else {
event_del(&iev->ev);
event_loopexit(NULL);
}
}
void
frontend_dispatch_engine(int fd, short event, void *bula)
{
struct imsgev *iev = bula;
struct imsgbuf *ibuf = &iev->ibuf;
struct imsg imsg;
struct iface *iface;
ssize_t n;
int shut = 0;
if (event & EV_READ) {
if ((n = imsgbuf_read(ibuf)) == -1)
fatal("imsgbuf_read error");
if (n == 0)
shut = 1;
}
if (event & EV_WRITE) {
if (imsgbuf_write(ibuf) == -1) {
if (errno == EPIPE)
shut = 1;
else
fatal("imsgbuf_write");
}
}
for (;;) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
fatal("%s: imsg_get error", __func__);
if (n == 0)
break;
switch (imsg.hdr.type) {
case IMSG_CTL_END:
case IMSG_CTL_SHOW_INTERFACE_INFO:
control_imsg_relay(&imsg);
break;
case IMSG_SEND_SOLICIT:
case IMSG_SEND_REQUEST:
case IMSG_SEND_RENEW:
case IMSG_SEND_REBIND: {
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_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);
switch (imsg.hdr.type) {
case IMSG_SEND_SOLICIT:
send_packet(DHCPSOLICIT, iface);
break;
case IMSG_SEND_REQUEST:
send_packet(DHCPREQUEST, iface);
break;
case IMSG_SEND_RENEW:
send_packet(DHCPRENEW, iface);
break;
case IMSG_SEND_REBIND:
send_packet(DHCPREBIND, iface);
break;
}
break;
}
default:
log_debug("%s: error handling imsg %d", __func__,
imsg.hdr.type);
break;
}
imsg_free(&imsg);
}
if (!shut)
imsg_event_add(iev);
else {
event_del(&iev->ev);
event_loopexit(NULL);
}
}
int
get_flags(char *if_name)
{
struct ifreq ifr;
strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) {
log_warn("SIOCGIFFLAGS");
return -1;
}
return ifr.ifr_flags;
}
void
update_iface(uint32_t if_index)
{
struct ifaddrs *ifap, *ifa;
struct iface *iface;
struct imsg_ifinfo ifinfo;
int flags;
char ifnamebuf[IF_NAMESIZE], *if_name;
if ((if_name = if_indextoname(if_index, ifnamebuf)) == NULL)
return;
if ((flags = get_flags(if_name)) == -1)
return;
if (find_iface_conf(&frontend_conf->iface_list, if_name) == NULL)
return;
if (getifaddrs(&ifap) != 0)
fatal("getifaddrs");
memset(&ifinfo, 0, sizeof(ifinfo));
ifinfo.if_index = if_index;
ifinfo.link_state = -1;
ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) ==
(IFF_UP | IFF_RUNNING);
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
if (strcmp(if_name, ifa->ifa_name) != 0)
continue;
if (ifa->ifa_addr == NULL)
continue;
switch (ifa->ifa_addr->sa_family) {
case AF_LINK: {
struct if_data *if_data;
if_data = (struct if_data *)ifa->ifa_data;
ifinfo.link_state = if_data->ifi_link_state;
ifinfo.rdomain = if_data->ifi_rdomain;
goto out;
}
default:
break;
}
}
out:
freeifaddrs(ifap);
iface = get_iface_by_id(if_index);
if (iface == NULL) {
if ((iface = calloc(1, sizeof(*iface))) == NULL)
fatal("calloc");
memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo));
LIST_INSERT_HEAD(&interfaces, iface, entries);
frontend_imsg_compose_main(IMSG_OPEN_UDPSOCK, 0,
&if_index, sizeof(if_index));
} else
memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo));
frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo,
sizeof(iface->ifinfo));
}
void
frontend_startup(void)
{
if (!event_initialized(&ev_route))
fatalx("%s: did not receive a route socket from the main "
"process", __func__);
event_add(&ev_route, NULL);
}
void
route_receive(int fd, short events, void *arg)
{
static uint8_t *buf;
struct rt_msghdr *rtm;
struct sockaddr *sa, *rti_info[RTAX_MAX];
ssize_t n;
if (buf == NULL) {
buf = malloc(ROUTE_SOCKET_BUF_SIZE);
if (buf == NULL)
fatal("malloc");
}
rtm = (struct rt_msghdr *)buf;
if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) {
if (errno == EAGAIN || errno == EINTR)
return;
log_warn("dispatch_rtmsg: read error");
return;
}
if (n == 0)
fatal("routing socket closed");
if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) {
log_warnx("partial rtm of %zd in buffer", n);
return;
}
if (rtm->rtm_version != RTM_VERSION)
return;
sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen);
get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
handle_route_message(rtm, rti_info);
}
void
handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
{
struct if_announcemsghdr *ifan;
uint32_t if_index;
switch (rtm->rtm_type) {
case RTM_IFINFO:
if_index = ((struct if_msghdr *)rtm)->ifm_index;
update_iface(if_index);
break;
case RTM_IFANNOUNCE:
ifan = (struct if_announcemsghdr *)rtm;
if_index = ifan->ifan_index;
if (ifan->ifan_what == IFAN_DEPARTURE) {
frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0,
&if_index, sizeof(if_index));
remove_iface(if_index);
}
break;
default:
log_debug("unexpected RTM: %d", rtm->rtm_type);
break;
}
}
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
void
get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
{
int i;
for (i = 0; i < RTAX_MAX; i++) {
if (addrs & (1 << i)) {
rti_info[i] = sa;
sa = (struct sockaddr *)((char *)(sa) +
ROUNDUP(sa->sa_len));
} else
rti_info[i] = NULL;
}
}
void
udp_receive(int fd, short events, void *arg)
{
struct imsg_dhcp imsg_dhcp;
struct iface *iface;
ssize_t len;
iface = (struct iface *)arg;
memset(&imsg_dhcp, 0, sizeof(imsg_dhcp));
if ((len = read(fd, imsg_dhcp.packet, 1500)) == -1) {
log_warn("%s: read", __func__);
return;
}
if (len == 0)
fatal("%s len == 0", __func__);
imsg_dhcp.if_index = iface->ifinfo.if_index;
imsg_dhcp.len = len;
frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp,
sizeof(imsg_dhcp));
}
void
iface_data_from_imsg(struct iface* iface, struct imsg_req_dhcp *imsg)
{
memcpy(iface->xid, imsg->xid, sizeof(iface->xid));
iface->elapsed_time = imsg->elapsed_time;
iface->serverid_len = imsg->serverid_len;
memcpy(iface->serverid, imsg->serverid, SERVERID_SIZE);
memcpy(iface->pds, imsg->pds, sizeof(iface->pds));
}
ssize_t
build_packet(uint8_t message_type, struct iface *iface, char *if_name)
{
struct iface_conf *iface_conf;
struct iface_ia_conf *ia_conf;
struct dhcp_hdr hdr;
struct dhcp_option_hdr opt_hdr;
struct dhcp_iapd iapd;
struct dhcp_iaprefix iaprefix;
struct dhcp_vendor_class vendor_class;
size_t i;
ssize_t len;
uint16_t request_option_code, elapsed_time;
const uint16_t options[] = {DHO_SOL_MAX_RT,
DHO_INF_MAX_RT};
uint8_t *p;
switch (message_type) {
case DHCPSOLICIT:
case DHCPREQUEST:
case DHCPRENEW:
case DHCPREBIND:
break;
default:
fatalx("%s: %s not implemented", __func__,
dhcp_message_type2str(message_type));
}
iface_conf = find_iface_conf(&frontend_conf->iface_list, if_name);
memset(dhcp_packet, 0, sizeof(dhcp_packet));
p = dhcp_packet;
hdr.msg_type = message_type;
memcpy(hdr.xid, iface->xid, sizeof(hdr.xid));
memcpy(p, &hdr, sizeof(struct dhcp_hdr));
p += sizeof(struct dhcp_hdr);
opt_hdr.code = htons(DHO_CLIENTID);
opt_hdr.len = htons(sizeof(struct dhcp_duid));
memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
p += sizeof(struct dhcp_option_hdr);
memcpy(p, &duid, sizeof(struct dhcp_duid));
p += sizeof(struct dhcp_duid);
switch (message_type) {
case DHCPSOLICIT:
case DHCPREBIND:
break;
case DHCPREQUEST:
case DHCPRENEW:
opt_hdr.code = htons(DHO_SERVERID);
opt_hdr.len = htons(iface->serverid_len);
memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
p += sizeof(struct dhcp_option_hdr);
memcpy(p, iface->serverid, iface->serverid_len);
p += iface->serverid_len;
break;
default:
fatalx("%s: %s not implemented", __func__,
dhcp_message_type2str(message_type));
}
SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) {
struct prefix *pd;
opt_hdr.code = htons(DHO_IA_PD);
opt_hdr.len = htons(sizeof(struct dhcp_iapd) +
sizeof(struct dhcp_option_hdr) +
sizeof(struct dhcp_iaprefix));
memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
p += sizeof(struct dhcp_option_hdr);
iapd.iaid = htonl(ia_conf->id);
iapd.t1 = 0;
iapd.t2 = 0;
memcpy(p, &iapd, sizeof(struct dhcp_iapd));
p += sizeof(struct dhcp_iapd);
opt_hdr.code = htons(DHO_IA_PREFIX);
opt_hdr.len = htons(sizeof(struct dhcp_iaprefix));
memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
p += sizeof(struct dhcp_option_hdr);
memset(&iaprefix, 0, sizeof(struct dhcp_iaprefix));
switch (message_type) {
case DHCPSOLICIT:
iaprefix.prefix_len = ia_conf->prefix_len;
break;
case DHCPREQUEST:
case DHCPRENEW:
case DHCPREBIND:
pd = &iface->pds[ia_conf->id];
if (pd->prefix_len > 0) {
iaprefix.prefix_len = pd->prefix_len;
memcpy(&iaprefix.prefix, &pd->prefix,
sizeof(struct in6_addr));
} else
iaprefix.prefix_len = ia_conf->prefix_len;
break;
default:
fatalx("%s: %s not implemented", __func__,
dhcp_message_type2str(message_type));
}
memcpy(p, &iaprefix, sizeof(struct dhcp_iaprefix));
p += sizeof(struct dhcp_iaprefix);
}
opt_hdr.code = htons(DHO_ORO);
opt_hdr.len = htons(sizeof(request_option_code) * nitems(options));
memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
p += sizeof(struct dhcp_option_hdr);
for (i = 0; i < nitems(options); i++) {
request_option_code = htons(options[i]);
memcpy(p, &request_option_code, sizeof(uint16_t));
p += sizeof(uint16_t);
}
opt_hdr.code = htons(DHO_ELAPSED_TIME);
opt_hdr.len = htons(2);
memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
p += sizeof(struct dhcp_option_hdr);
elapsed_time = htons(iface->elapsed_time);
memcpy(p, &elapsed_time, sizeof(uint16_t));
p += sizeof(uint16_t);
if (message_type == DHCPSOLICIT && frontend_conf->rapid_commit) {
opt_hdr.code = htons(DHO_RAPID_COMMIT);
opt_hdr.len = htons(0);
memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
p += sizeof(struct dhcp_option_hdr);
}
opt_hdr.code = htons(DHO_VENDOR_CLASS);
opt_hdr.len = htons(sizeof(struct dhcp_vendor_class) +
vendor_class_len);
memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr));
p += sizeof(struct dhcp_option_hdr);
vendor_class.enterprise_number = htonl(OPENBSD_ENTERPRISENO);
vendor_class.vendor_class_len = htons(vendor_class_len);
memcpy(p, &vendor_class, sizeof(struct dhcp_vendor_class));
p += sizeof(struct dhcp_vendor_class);
memcpy(p, vendor_class_data, vendor_class_len);
p += vendor_class_len;
len = p - dhcp_packet;
return (len);
}
void
send_packet(uint8_t message_type, struct iface *iface)
{
ssize_t pkt_len;
char ifnamebuf[IF_NAMESIZE], *if_name, *message_name;
if (!event_initialized(&iface->udpev)) {
iface->send_solicit = 1;
return;
}
iface->send_solicit = 0;
if ((if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf))
== NULL)
return;
switch (message_type) {
case DHCPSOLICIT:
message_name = "Soliciting";
break;
case DHCPREQUEST:
message_name = "Requesting";
break;
case DHCPRENEW:
message_name = "Renewing";
break;
case DHCPREBIND:
message_name = "Rebinding";
break;
default:
message_name = NULL;
break;
}
if (message_name)
log_info("%s lease on %s", message_name, if_name);
pkt_len = build_packet(message_type, iface, if_name);
dst.sin6_scope_id = iface->ifinfo.if_index;
if (sendto(EVENT_FD(&iface->udpev), dhcp_packet, pkt_len, 0,
(struct sockaddr *)&dst, sizeof(dst)) == -1)
log_warn("sendto");
}
struct iface*
get_iface_by_id(uint32_t if_index)
{
struct iface *iface;
LIST_FOREACH (iface, &interfaces, entries) {
if (iface->ifinfo.if_index == if_index)
return (iface);
}
return (NULL);
}
struct iface*
get_iface_by_name(const char *if_name)
{
uint32_t ifidx = if_nametoindex(if_name);
if (ifidx == 0)
return (NULL);
return get_iface_by_id(ifidx);
}
void
remove_iface(uint32_t if_index)
{
struct iface *iface;
iface = get_iface_by_id(if_index);
if (iface == NULL)
return;
LIST_REMOVE(iface, entries);
if (event_initialized(&iface->udpev)) {
event_del(&iface->udpev);
close(EVENT_FD(&iface->udpev));
}
free(iface);
}
void
set_udpsock(int udpsock, uint32_t if_index)
{
struct iface *iface;
iface = get_iface_by_id(if_index);
if (iface == NULL) {
close(udpsock);
} else if (event_initialized(&iface->udpev)) {
close(udpsock);
} else {
event_set(&iface->udpev, udpsock, EV_READ |
EV_PERSIST, udp_receive, iface);
event_add(&iface->udpev, NULL);
if (iface->send_solicit)
send_packet(DHCPSOLICIT, iface);
}
}
struct iface_conf*
find_iface_conf(struct iface_conf_head *head, char *if_name)
{
struct iface_conf *iface_conf;
if (if_name == NULL)
return (NULL);
SIMPLEQ_FOREACH(iface_conf, head, entry) {
if (strcmp(iface_conf->name, if_name) == 0)
return iface_conf;
}
return (NULL);
}
int*
changed_ifaces(struct dhcp6leased_conf *oconf, struct dhcp6leased_conf *nconf)
{
struct iface_conf *iface_conf, *oiface_conf;
int *ret, if_index, count = 0, i = 0;
SIMPLEQ_FOREACH(iface_conf, &oconf->iface_list, entry)
count++;
SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry)
count++;
ret = calloc(count + 1, sizeof(int));
SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) {
if ((if_index = if_nametoindex(iface_conf->name)) == 0)
continue;
oiface_conf = find_iface_conf(&oconf->iface_list,
iface_conf->name);
if (oiface_conf == NULL) {
ret[i++] = if_index;
} else if (iface_conf_cmp(iface_conf, oiface_conf) != 0) {
ret[i++] = if_index;
}
}
SIMPLEQ_FOREACH(oiface_conf, &oconf->iface_list, entry) {
if ((if_index = if_nametoindex(oiface_conf->name)) == 0)
continue;
if (find_iface_conf(&nconf->iface_list, oiface_conf->name) ==
NULL) {
ret[i++] = if_index;
}
}
return ret;
}
int
iface_conf_cmp(struct iface_conf *a, struct iface_conf *b)
{
return 0;
}