#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netinet/dhcp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include "dhcp_hostconf.h"
static void relativize_time(DHCP_OPT *, time_t, time_t);
static void relativize_v6(uint32_t *, time_t, time_t);
char *
ifname_to_hostconf(const char *ifname, boolean_t isv6)
{
static char filename[sizeof (DHCP_HOSTCONF_TMPL6) + LIFNAMSIZ];
(void) snprintf(filename, sizeof (filename), "%s%s%s",
DHCP_HOSTCONF_PREFIX, ifname,
isv6 ? DHCP_HOSTCONF_SUFFIX6 : DHCP_HOSTCONF_SUFFIX);
return (filename);
}
int
remove_hostconf(const char *ifname, boolean_t isv6)
{
return (unlink(ifname_to_hostconf(ifname, isv6)));
}
int
read_hostconf(const char *ifname, PKT_LIST **plpp, uint_t plplen,
boolean_t isv6)
{
PKT_LIST *plp = NULL;
PKT *pkt = NULL;
int fd;
time_t orig_time, current_time = time(NULL);
uint32_t lease;
uint32_t magic;
int pcnt = 0;
int retval;
fd = open(ifname_to_hostconf(ifname, isv6), O_RDONLY);
if (fd == -1)
return (-1);
if (read(fd, &magic, sizeof (magic)) != sizeof (magic))
goto failure;
if (magic != (isv6 ? DHCP_HOSTCONF_MAGIC6 : DHCP_HOSTCONF_MAGIC))
goto failure;
if (read(fd, &orig_time, sizeof (orig_time)) != sizeof (orig_time))
goto failure;
for (pcnt = 0; pcnt < plplen; pcnt++) {
plp = NULL;
pkt = NULL;
if ((plp = calloc(1, sizeof (PKT_LIST))) == NULL)
goto failure;
retval = read(fd, &plp->len, sizeof (plp->len));
if (retval == 0 && pcnt != 0) {
free(plp);
break;
} else if (retval != sizeof (plp->len))
goto failure;
if ((pkt = malloc(plp->len)) == NULL)
goto failure;
if (read(fd, pkt, plp->len) != plp->len)
goto failure;
plp->pkt = pkt;
plpp[pcnt] = plp;
if (!isv6 && dhcp_options_scan(plp, B_TRUE) != 0)
goto failure;
if (pcnt == 0)
continue;
if (isv6) {
dhcpv6_option_t d6o;
dhcpv6_ia_na_t d6in;
dhcpv6_iaaddr_t d6ia;
uchar_t *opts, *optmax, *subomax;
opts = (uchar_t *)pkt + sizeof (dhcpv6_message_t);
optmax = (uchar_t *)pkt + plp->len;
while (opts + sizeof (d6o) <= optmax) {
(void) memcpy(&d6o, opts, sizeof (d6o));
d6o.d6o_code = ntohs(d6o.d6o_code);
d6o.d6o_len = ntohs(d6o.d6o_len);
subomax = opts + sizeof (d6o) + d6o.d6o_len;
if (subomax > optmax)
break;
if (d6o.d6o_code != DHCPV6_OPT_IA_NA &&
d6o.d6o_code != DHCPV6_OPT_IA_TA &&
d6o.d6o_code != DHCPV6_OPT_IA_PD) {
opts = subomax;
continue;
}
if (d6o.d6o_code == DHCPV6_OPT_IA_TA) {
opts += sizeof (dhcpv6_ia_ta_t);
} else {
if (opts + sizeof (d6in) > subomax) {
opts = subomax;
continue;
}
(void) memcpy(&d6in, opts,
sizeof (d6in));
relativize_v6(&d6in.d6in_t1, orig_time,
current_time);
relativize_v6(&d6in.d6in_t2, orig_time,
current_time);
(void) memcpy(opts, &d6in,
sizeof (d6in));
opts += sizeof (d6in);
}
while (opts + sizeof (d6o) <= subomax) {
(void) memcpy(&d6o, opts,
sizeof (d6o));
d6o.d6o_code = ntohs(d6o.d6o_code);
d6o.d6o_len = ntohs(d6o.d6o_len);
if (opts + sizeof (d6o) + d6o.d6o_len >
subomax)
break;
if (d6o.d6o_code != DHCPV6_OPT_IAADDR) {
opts += sizeof (d6o) +
d6o.d6o_len;
continue;
}
if (opts + sizeof (d6ia) > subomax)
break;
(void) memcpy(&d6ia, opts,
sizeof (d6ia));
relativize_v6(&d6ia.d6ia_preflife,
orig_time, current_time);
relativize_v6(&d6ia.d6ia_vallife,
orig_time, current_time);
(void) memcpy(opts, &d6ia,
sizeof (d6ia));
opts += sizeof (d6o) + d6o.d6o_len;
}
opts = subomax;
}
} else {
if (plp->opts[CD_LEASE_TIME] != NULL &&
plp->opts[CD_LEASE_TIME]->len ==
sizeof (lease_t)) {
(void) memcpy(&lease,
plp->opts[CD_LEASE_TIME]->value,
sizeof (lease_t));
lease = ntohl(lease);
if ((lease != DHCP_PERM) &&
(orig_time + lease) <= current_time)
goto failure;
}
relativize_time(plp->opts[CD_T1_TIME], orig_time,
current_time);
relativize_time(plp->opts[CD_T2_TIME], orig_time,
current_time);
relativize_time(plp->opts[CD_LEASE_TIME], orig_time,
current_time);
}
}
(void) close(fd);
return (pcnt);
failure:
free(pkt);
free(plp);
while (pcnt-- > 0) {
free(plpp[pcnt]->pkt);
free(plpp[pcnt]);
}
(void) close(fd);
return (-1);
}
int
write_hostconf(
const char *ifname,
PKT_LIST *pl[],
uint_t pllen,
time_t relative_to,
boolean_t isv6)
{
int fd;
struct iovec iov[IOV_MAX];
int retval;
uint32_t magic;
ssize_t explen = 0;
int i, iovlen = 0;
fd = open(ifname_to_hostconf(ifname, isv6), O_WRONLY|O_CREAT|O_TRUNC,
0600);
if (fd == -1)
return (-1);
magic = isv6 ? DHCP_HOSTCONF_MAGIC6 : DHCP_HOSTCONF_MAGIC;
iov[iovlen].iov_base = (caddr_t)&magic;
explen += iov[iovlen++].iov_len = sizeof (magic);
iov[iovlen].iov_base = (caddr_t)&relative_to;
explen += iov[iovlen++].iov_len = sizeof (relative_to);
for (i = 0; i < pllen && iovlen < (IOV_MAX - 1); i++) {
iov[iovlen].iov_base = (caddr_t)&pl[i]->len;
explen += iov[iovlen++].iov_len = sizeof (pl[i]->len);
iov[iovlen].iov_base = (caddr_t)pl[i]->pkt;
explen += iov[iovlen++].iov_len = pl[i]->len;
}
retval = writev(fd, iov, iovlen);
(void) close(fd);
if (retval != explen)
return (-1);
return (0);
}
static void
relativize_time(DHCP_OPT *option, time_t orig_time, time_t current_time)
{
uint32_t pkt_time;
time_t time_diff = current_time - orig_time;
if (option == NULL || option->len != sizeof (lease_t))
return;
(void) memcpy(&pkt_time, option->value, option->len);
if (ntohl(pkt_time) != DHCP_PERM)
pkt_time = htonl(ntohl(pkt_time) - time_diff);
(void) memcpy(option->value, &pkt_time, option->len);
}
static void
relativize_v6(uint32_t *val, time_t orig_time, time_t current_time)
{
uint32_t hval;
time_t time_diff = current_time - orig_time;
hval = ntohl(*val);
if (hval != DHCPV6_INFTIME) {
if (hval < time_diff)
*val = 0;
else
*val = htonl(hval - time_diff);
}
}