#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/sockio.h>
#include <unistd.h>
#include <time.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <search.h>
#include <sys/sysmacros.h>
#include <dhcp_hostconf.h>
#include <dhcpagent_util.h>
#include <dhcpmsg.h>
#include "states.h"
#include "packet.h"
#include "util.h"
#include "agent.h"
#include "interface.h"
#include "script_handler.h"
#include "defaults.h"
enum v6_bind_result {
v6Restart,
v6Resent,
v6Done
};
static enum v6_bind_result configure_v6_leases(dhcp_smach_t *);
static boolean_t configure_v4_lease(dhcp_smach_t *);
static boolean_t configure_v4_timers(dhcp_smach_t *);
static int
bound_event_cb(dhcp_smach_t *dsmp, void *arg)
{
if (dsmp->dsm_ia.ia_fd != -1)
ipc_action_finish(dsmp, DHCP_IPC_SUCCESS);
else
async_finish(dsmp);
return (1);
}
boolean_t
dhcp_bound(dhcp_smach_t *dsmp, PKT_LIST *ack)
{
DHCPSTATE oldstate;
lease_t new_lease;
dhcp_lif_t *lif;
dhcp_lease_t *dlp;
enum v6_bind_result v6b;
if (ack != NULL) {
if (dsmp->dsm_ack != dsmp->dsm_orig_ack)
free_pkt_entry(dsmp->dsm_ack);
dsmp->dsm_ack = ack;
if (dsmp->dsm_orig_ack == NULL)
dsmp->dsm_orig_ack = ack;
save_domainname(dsmp, ack);
}
oldstate = dsmp->dsm_state;
switch (oldstate) {
case ADOPTING:
if (ack->opts[CD_DHCP_TYPE] == NULL)
return (B_FALSE);
(void) memcpy(&new_lease, ack->opts[CD_LEASE_TIME]->value,
sizeof (lease_t));
new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX));
(void) memcpy(ack->opts[CD_LEASE_TIME]->value, &new_lease,
sizeof (lease_t));
dsmp->dsm_newstart_monosec = monosec();
if (dsmp->dsm_isv6) {
if ((v6b = configure_v6_leases(dsmp)) != v6Done)
return (v6b == v6Resent);
} else {
if (!configure_v4_lease(dsmp))
return (B_FALSE);
if (!configure_v4_timers(dsmp))
return (B_FALSE);
}
dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
write_lease_to_hostconf(dsmp);
break;
case SELECTING:
case REQUESTING:
case INIT_REBOOT:
if (dsmp->dsm_isv6) {
if ((v6b = configure_v6_leases(dsmp)) != v6Done)
return (v6b == v6Resent);
} else {
if (!configure_v4_lease(dsmp))
return (B_FALSE);
if (!configure_v4_timers(dsmp))
return (B_FALSE);
if (!clear_lif_deprecated(dsmp->dsm_lif))
return (B_FALSE);
}
stop_pkt_retransmission(dsmp);
if (dsmp->dsm_leases == NULL) {
dhcpmsg(MSG_WARNING,
"dhcp_bound: no address lease established");
return (B_FALSE);
}
if (ack != NULL &&
(oldstate == SELECTING || oldstate == INIT_REBOOT) &&
dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) {
dhcpmsg(MSG_ERROR,
"dhcp_bound: unable to save server ID on %s",
dsmp->dsm_name);
return (B_FALSE);
}
if (!set_smach_state(dsmp, PRE_BOUND))
return (B_FALSE);
if (dsmp->dsm_lif_wait == 0)
dhcp_bound_complete(dsmp);
break;
case PRE_BOUND:
case BOUND:
case INFORMATION:
return (B_TRUE);
case RENEWING:
case REBINDING:
if (dsmp->dsm_isv6) {
if ((v6b = configure_v6_leases(dsmp)) != v6Done)
return (v6b == v6Resent);
} else {
if (!configure_v4_timers(dsmp))
return (B_FALSE);
if (!clear_lif_deprecated(dsmp->dsm_lif))
return (B_FALSE);
}
if ((lif = find_expired_lif(dsmp)) != NULL) {
hold_lif(lif);
dhcp_expire(NULL, lif);
while ((lif = find_expired_lif(dsmp)) != NULL) {
dlp = lif->lif_lease;
unplumb_lif(lif);
if (dlp->dl_nlifs == 0)
remove_lease(dlp);
}
if (dsmp->dsm_leases == NULL)
return (B_FALSE);
}
if (oldstate == REBINDING && dsmp->dsm_isv6 &&
!save_server_id(dsmp, ack)) {
return (B_FALSE);
}
for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
if (dlp->dl_stale && dlp->dl_nlifs > 0)
break;
}
if (dlp != NULL) {
dhcpmsg(MSG_DEBUG, "dhcp_bound: lease not updated; "
"allow retransmit");
return (B_TRUE);
}
if (!set_smach_state(dsmp, BOUND))
return (B_FALSE);
(void) script_start(dsmp, dsmp->dsm_isv6 ? EVENT_EXTEND6 :
EVENT_EXTEND, bound_event_cb, NULL, NULL);
dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
write_lease_to_hostconf(dsmp);
stop_pkt_retransmission(dsmp);
break;
case INFORM_SENT:
if (dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) {
return (B_FALSE);
}
(void) bound_event_cb(dsmp, NULL);
if (!set_smach_state(dsmp, INFORMATION))
return (B_FALSE);
stop_pkt_retransmission(dsmp);
break;
default:
dhcpmsg(MSG_DEBUG,
"dhcp_bound: called in unexpected state: %s",
dhcp_state_to_string(dsmp->dsm_state));
return (B_FALSE);
}
return (B_TRUE);
}
void
dhcp_bound_complete(dhcp_smach_t *dsmp)
{
PKT_LIST *ack = dsmp->dsm_ack;
DHCP_OPT *router_list;
DHCPSTATE oldstate;
dhcp_lif_t *lif = dsmp->dsm_lif;
boolean_t ignore_mtu = B_FALSE;
boolean_t manage_mtu;
if (dsmp->dsm_isv6) {
(void) set_smach_state(dsmp, BOUND);
dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s",
dsmp->dsm_name);
(void) script_start(dsmp, EVENT_BOUND6, bound_event_cb, NULL,
NULL);
dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
write_lease_to_hostconf(dsmp);
return;
}
manage_mtu = df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_SET_MTU);
router_list = ack->opts[CD_ROUTER];
for (int i = 0; i < dsmp->dsm_pillen; i++) {
switch (dsmp->dsm_pil[i]) {
case CD_MTU:
ignore_mtu = B_TRUE;
break;
case CD_ROUTER:
router_list = NULL;
break;
}
}
if (manage_mtu) {
DHCP_OPT *mtu;
uint16_t mtuval = 0;
if (!ignore_mtu && (mtu = ack->opts[CD_MTU]) != NULL &&
mtu->len == sizeof (uint16_t)) {
(void) memcpy(&mtuval, mtu->value, sizeof (mtuval));
mtuval = ntohs(mtuval);
set_lif_mtu(lif, mtuval);
} else {
clear_lif_mtu(lif);
}
}
if (router_list != NULL &&
(router_list->len % sizeof (ipaddr_t)) == 0 &&
strchr(lif->lif_name, ':') == NULL &&
!lif->lif_pif->pif_under_ipmp) {
dsmp->dsm_nrouters = router_list->len / sizeof (ipaddr_t);
dsmp->dsm_routers = malloc(router_list->len);
if (dsmp->dsm_routers == NULL) {
dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot allocate "
"default router list, ignoring default routers");
dsmp->dsm_nrouters = 0;
}
for (uint_t i = 0; i < dsmp->dsm_nrouters; i++) {
(void) memcpy(&dsmp->dsm_routers[i].s_addr,
router_list->value + (i * sizeof (ipaddr_t)),
sizeof (ipaddr_t));
if (!add_default_route(lif->lif_pif->pif_index,
&dsmp->dsm_routers[i])) {
dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot "
"add default router %s on %s", inet_ntoa(
dsmp->dsm_routers[i]), dsmp->dsm_name);
dsmp->dsm_routers[i].s_addr = htonl(INADDR_ANY);
continue;
}
dhcpmsg(MSG_INFO, "added default router %s on %s",
inet_ntoa(dsmp->dsm_routers[i]), dsmp->dsm_name);
}
}
oldstate = dsmp->dsm_state;
if (!set_smach_state(dsmp, BOUND)) {
dhcpmsg(MSG_ERR,
"dhcp_bound_complete: cannot set bound state on %s",
dsmp->dsm_name);
return;
}
dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", dsmp->dsm_name);
if (ack->opts[CD_DHCP_TYPE] == NULL)
dsmp->dsm_dflags |= DHCP_IF_BOOTP;
if (oldstate != ADOPTING) {
(void) script_start(dsmp, EVENT_BOUND, bound_event_cb, NULL,
NULL);
}
dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
write_lease_to_hostconf(dsmp);
}
static double
fuzzify(uint32_t sec, double pct)
{
return (sec * (pct + (drand48() - 0.5) / 25.0));
}
static void
get_pkt_times(PKT_LIST *ack, lease_t *lease, lease_t *t1, lease_t *t2)
{
*lease = DHCP_PERM;
*t1 = DHCP_PERM;
*t2 = DHCP_PERM;
if (ack->opts[CD_DHCP_TYPE] == NULL) {
dhcpmsg(MSG_VERBOSE,
"get_pkt_times: BOOTP response; infinite lease");
return;
}
if (ack->opts[CD_LEASE_TIME] == NULL) {
dhcpmsg(MSG_VERBOSE,
"get_pkt_times: no lease option provided");
return;
}
if (ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
dhcpmsg(MSG_VERBOSE, "get_pkt_times: invalid lease option");
}
(void) memcpy(lease, ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
*lease = ntohl(*lease);
if (*lease == DHCP_PERM) {
dhcpmsg(MSG_VERBOSE, "get_pkt_times: infinite lease granted");
return;
}
if (ack->opts[CD_T1_TIME] != NULL &&
ack->opts[CD_T1_TIME]->len == sizeof (lease_t)) {
(void) memcpy(t1, ack->opts[CD_T1_TIME]->value, sizeof (*t1));
*t1 = ntohl(*t1);
}
if (ack->opts[CD_T2_TIME] != NULL &&
ack->opts[CD_T2_TIME]->len == sizeof (lease_t)) {
(void) memcpy(t2, ack->opts[CD_T2_TIME]->value, sizeof (*t2));
*t2 = ntohl(*t2);
}
if ((*t1 == DHCP_PERM) || (*t1 >= *lease))
*t1 = (lease_t)fuzzify(*lease, DHCP_T1_FACT);
if ((*t2 == DHCP_PERM) || (*t2 > *lease) || (*t2 <= *t1))
*t2 = (lease_t)fuzzify(*lease, DHCP_T2_FACT);
dhcpmsg(MSG_VERBOSE, "get_pkt_times: lease %u t1 %u t2 %u",
*lease, *t1, *t2);
}
static boolean_t
configure_v4_timers(dhcp_smach_t *dsmp)
{
PKT_LIST *ack = dsmp->dsm_ack;
lease_t lease, t1, t2;
dhcp_lease_t *dlp;
dhcp_lif_t *lif;
dlp = dsmp->dsm_leases;
lif = dlp->dl_lifs;
if (ack->opts[CD_DHCP_TYPE] != NULL &&
(ack->opts[CD_LEASE_TIME] == NULL ||
ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
lif_mark_decline(lif, "Missing or corrupted lease time");
send_declines(dsmp);
dhcpmsg(MSG_WARNING, "configure_v4_timers: %s lease time in "
"ACK on %s", ack->opts[CD_LEASE_TIME] == NULL ? "missing" :
"corrupt", dsmp->dsm_name);
return (B_FALSE);
}
cancel_lease_timers(dlp);
cancel_lif_timers(lif);
get_pkt_times(ack, &lease, &t1, &t2);
if (lif->lif_expire.dt_start != 0 &&
abs((dsmp->dsm_newstart_monosec + lease) -
(dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start)) <
DHCP_LEASE_EPS) {
const char *noext = "configure_v4_timers: lease renewed but "
"time not extended";
int msg_level;
uint_t minleft;
if (lif->lif_expire.dt_start < DHCP_LEASE_ERROR_THRESH)
msg_level = MSG_ERROR;
else
msg_level = MSG_VERBOSE;
minleft = (lif->lif_expire.dt_start + 30) / 60;
if (lif->lif_expire.dt_start < 60) {
dhcpmsg(msg_level, "%s; expires in %d seconds",
noext, lif->lif_expire.dt_start);
} else if (minleft == 1) {
dhcpmsg(msg_level, "%s; expires in 1 minute", noext);
} else if (minleft > 120) {
dhcpmsg(msg_level, "%s; expires in %d hours",
noext, (minleft + 30) / 60);
} else {
dhcpmsg(msg_level, "%s; expires in %d minutes",
noext, minleft);
}
}
init_timer(&dlp->dl_t1, t1);
init_timer(&dlp->dl_t2, t2);
init_timer(&lif->lif_expire, lease);
if (lease == DHCP_PERM) {
dhcpmsg(MSG_INFO,
"configure_v4_timers: %s acquired permanent lease",
dsmp->dsm_name);
return (B_TRUE);
}
dhcpmsg(MSG_INFO, "configure_v4_timers: %s acquired lease, expires %s",
dsmp->dsm_name,
monosec_to_string(dsmp->dsm_newstart_monosec + lease));
dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins renewal at %s",
dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec +
dlp->dl_t1.dt_start));
dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins rebinding at %s",
dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec +
dlp->dl_t2.dt_start));
if (!schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire))
goto failure;
if (lease < DHCP_REBIND_MIN) {
dhcpmsg(MSG_WARNING, "configure_v4_timers: lease on %s is for "
"less than %d seconds!", dsmp->dsm_name, DHCP_REBIND_MIN);
return (B_TRUE);
}
if (!schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew))
goto failure;
if (!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))
goto failure;
return (B_TRUE);
failure:
cancel_lease_timers(dlp);
cancel_lif_timers(lif);
dhcpmsg(MSG_WARNING,
"configure_v4_timers: cannot schedule lease timers");
return (B_FALSE);
}
static enum v6_bind_result
configure_v6_leases(dhcp_smach_t *dsmp)
{
const dhcpv6_option_t *d6o, *d6so, *d6sso;
const char *optbase, *estr, *msg;
uint_t olen, solen, ssolen, msglen;
dhcpv6_ia_na_t d6in;
dhcpv6_iaaddr_t d6ia;
dhcp_lease_t *dlp;
uint32_t shortest;
dhcp_lif_t *lif;
uint_t nlifs;
boolean_t got_iana = B_FALSE;
uint_t scode;
for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next)
dlp->dl_stale = B_TRUE;
d6o = NULL;
while ((d6o = dhcpv6_pkt_option(dsmp->dsm_ack, d6o, DHCPV6_OPT_IA_NA,
&olen)) != NULL) {
if (olen < sizeof (d6in)) {
dhcpmsg(MSG_WARNING,
"configure_v6_leases: garbled IA_NA");
continue;
}
(void) memcpy(&d6in, d6o, sizeof (d6in));
d6in.d6in_iaid = ntohl(d6in.d6in_iaid);
if (d6in.d6in_iaid != dsmp->dsm_lif->lif_iaid) {
dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored "
"IA_NA for IAID %x (not %x)", d6in.d6in_iaid,
dsmp->dsm_lif->lif_iaid);
continue;
}
if ((dlp = dsmp->dsm_leases) != NULL)
dlp->dl_stale = B_FALSE;
if (got_iana) {
dhcpmsg(MSG_WARNING, "configure_v6_leases: unexpected "
"extra IA_NA ignored");
continue;
}
d6in.d6in_t1 = ntohl(d6in.d6in_t1);
d6in.d6in_t2 = ntohl(d6in.d6in_t2);
if (d6in.d6in_t1 > d6in.d6in_t2 && d6in.d6in_t2 != 0) {
dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored "
"IA_NA with invalid T1 %u > T2 %u", d6in.d6in_t1,
d6in.d6in_t2);
continue;
}
optbase = (const char *)d6o + sizeof (d6in);
olen -= sizeof (d6in);
d6so = dhcpv6_find_option(optbase, olen, NULL,
DHCPV6_OPT_STATUS_CODE, &solen);
scode = dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen);
if (scode != DHCPV6_STAT_SUCCESS) {
dhcpmsg(MSG_WARNING,
"configure_v6_leases: IA_NA: %s: %.*s",
estr, msglen, msg);
}
print_server_msg(dsmp, msg, msglen);
if (scode == DHCPV6_STAT_NOADDRS) {
dhcpmsg(MSG_DEBUG, "configure_v6_leases: ignoring "
"no-addrs status in IA_NA");
continue;
}
if (scode == DHCPV6_STAT_NOBINDING) {
send_v6_request(dsmp);
return (v6Resent);
}
if ((dlp = dsmp->dsm_leases) == NULL &&
(dlp = insert_lease(dsmp)) == NULL) {
dhcpmsg(MSG_ERROR, "configure_v6_leases: unable to "
"allocate memory for lease");
return (v6Restart);
}
shortest = DHCPV6_INFTIME;
d6so = NULL;
while ((d6so = dhcpv6_find_option(optbase, olen, d6so,
DHCPV6_OPT_IAADDR, &solen)) != NULL) {
if (solen < sizeof (d6ia)) {
dhcpmsg(MSG_WARNING,
"configure_v6_leases: garbled IAADDR");
continue;
}
(void) memcpy(&d6ia, d6so, sizeof (d6ia));
d6ia.d6ia_preflife = ntohl(d6ia.d6ia_preflife);
d6ia.d6ia_vallife = ntohl(d6ia.d6ia_vallife);
if (d6ia.d6ia_preflife > d6ia.d6ia_vallife) {
dhcpmsg(MSG_WARNING,
"configure_v6_leases: ignored IAADDR with "
"preferred lifetime %u > valid %u",
d6ia.d6ia_preflife, d6ia.d6ia_vallife);
continue;
}
d6sso = dhcpv6_find_option((const char *)d6so +
sizeof (d6ia), solen - sizeof (d6ia), NULL,
DHCPV6_OPT_STATUS_CODE, &ssolen);
scode = dhcpv6_status_code(d6sso, ssolen, &estr, &msg,
&msglen);
print_server_msg(dsmp, msg, msglen);
if (scode == DHCPV6_STAT_NOADDRS) {
dhcpmsg(MSG_DEBUG, "configure_v6_leases: "
"ignoring no-addrs status in IAADDR");
continue;
}
if (scode == DHCPV6_STAT_NOBINDING) {
send_v6_request(dsmp);
return (v6Resent);
}
if (scode != DHCPV6_STAT_SUCCESS) {
dhcpmsg(MSG_WARNING,
"configure_v6_leases: IAADDR: %s", estr);
}
lif = dlp->dl_lifs;
for (nlifs = dlp->dl_nlifs; nlifs > 0;
nlifs--, lif = lif->lif_next) {
if (IN6_ARE_ADDR_EQUAL(&d6ia.d6ia_addr,
&lif->lif_v6addr))
break;
}
if (d6ia.d6ia_vallife == 0) {
if (nlifs != 0) {
dhcpmsg(MSG_DEBUG,
"configure_v6_leases: lif %s has "
"expired", lif->lif_name);
lif->lif_expired = B_TRUE;
}
continue;
}
if (nlifs == 0) {
lif = plumb_lif(dsmp->dsm_lif->lif_pif,
&d6ia.d6ia_addr);
if (lif == NULL)
continue;
if (++dlp->dl_nlifs == 1) {
dlp->dl_lifs = lif;
} else {
remque(lif);
insque(lif, dlp->dl_lifs);
}
lif->lif_lease = dlp;
lif->lif_dad_wait = _B_TRUE;
dsmp->dsm_lif_wait++;
} else {
cancel_lif_timers(lif);
if (d6ia.d6ia_preflife != 0 &&
!clear_lif_deprecated(lif)) {
unplumb_lif(lif);
continue;
}
}
init_timer(&lif->lif_preferred, d6ia.d6ia_preflife);
init_timer(&lif->lif_expire, d6ia.d6ia_vallife);
if (d6ia.d6ia_preflife == 0) {
set_lif_deprecated(lif);
} else if (d6ia.d6ia_preflife != DHCPV6_INFTIME &&
d6ia.d6ia_preflife != d6ia.d6ia_vallife &&
!schedule_lif_timer(lif, &lif->lif_preferred,
dhcp_deprecate)) {
unplumb_lif(lif);
continue;
}
if (d6ia.d6ia_vallife != DHCPV6_INFTIME &&
!schedule_lif_timer(lif, &lif->lif_expire,
dhcp_expire)) {
unplumb_lif(lif);
continue;
}
if (d6ia.d6ia_preflife < shortest)
shortest = d6ia.d6ia_preflife;
}
if (dlp->dl_nlifs == 0) {
dhcpmsg(MSG_WARNING,
"configure_v6_leases: no IAADDRs found in IA_NA");
remove_lease(dlp);
continue;
}
if (d6in.d6in_t1 == 0 && d6in.d6in_t2 == 0) {
if ((d6in.d6in_t1 = shortest / 2) == 0)
d6in.d6in_t1 = 1;
d6in.d6in_t2 = shortest - shortest / 5;
}
cancel_lease_timers(dlp);
init_timer(&dlp->dl_t1, d6in.d6in_t1);
init_timer(&dlp->dl_t2, d6in.d6in_t2);
if ((d6in.d6in_t1 != DHCPV6_INFTIME &&
!schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) ||
(d6in.d6in_t2 != DHCPV6_INFTIME &&
!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))) {
dhcpmsg(MSG_WARNING, "configure_v6_leases: unable to "
"set renew/rebind timers");
} else {
got_iana = B_TRUE;
}
}
if (!got_iana) {
dhcpmsg(MSG_WARNING,
"configure_v6_leases: no usable IA_NA option found");
}
return (v6Done);
}
static boolean_t
configure_v4_lease(dhcp_smach_t *dsmp)
{
struct lifreq lifr;
struct sockaddr_in *sin;
PKT_LIST *ack = dsmp->dsm_ack;
dhcp_lease_t *dlp;
dhcp_lif_t *lif;
uint32_t addrhbo;
struct in_addr inaddr;
if (ack->opts[CD_DHCP_TYPE] != NULL) {
(void) memcpy(&inaddr, ack->opts[CD_SERVER_ID]->value,
sizeof (inaddr));
IN6_INADDR_TO_V4MAPPED(&inaddr, &dsmp->dsm_server);
}
if ((dlp = dsmp->dsm_leases) == NULL &&
(dlp = insert_lease(dsmp)) == NULL) {
dhcpmsg(MSG_ERROR, "configure_v4_lease: unable to allocate "
"memory for lease");
return (B_FALSE);
}
if (dlp->dl_nlifs == 0) {
dlp->dl_lifs = dsmp->dsm_lif;
dlp->dl_nlifs = 1;
hold_lif(dlp->dl_lifs);
dlp->dl_lifs->lif_lease = dlp;
}
lif = dlp->dl_lifs;
IN6_INADDR_TO_V4MAPPED(&ack->pkt->yiaddr, &lif->lif_v6addr);
addrhbo = ntohl(ack->pkt->yiaddr.s_addr);
if ((addrhbo & IN_CLASSA_NET) == 0 ||
(addrhbo >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
IN_CLASSD(addrhbo)) {
dhcpmsg(MSG_ERROR,
"configure_v4_lease: got invalid IP address %s for %s",
inet_ntoa(ack->pkt->yiaddr), lif->lif_name);
return (B_FALSE);
}
(void) memset(&lifr, 0, sizeof (struct lifreq));
(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
sin = (struct sockaddr_in *)&lifr.lifr_addr;
sin->sin_family = AF_INET;
(void) memset(&lif->lif_v6mask, 0xff, sizeof (lif->lif_v6mask));
if (ack->opts[CD_SUBNETMASK] != NULL &&
ack->opts[CD_SUBNETMASK]->len == sizeof (inaddr)) {
(void) memcpy(&inaddr, ack->opts[CD_SUBNETMASK]->value,
sizeof (inaddr));
} else {
if (ack->opts[CD_SUBNETMASK] != NULL &&
ack->opts[CD_SUBNETMASK]->len != sizeof (inaddr)) {
dhcpmsg(MSG_WARNING, "configure_v4_lease: specified "
"subnet mask length is %d instead of %d, ignoring",
ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t));
} else {
dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP "
"netmask specified for %s, making best guess",
lif->lif_name);
}
if (IN_CLASSA(addrhbo))
inaddr.s_addr = htonl(IN_CLASSA_NET);
else if (IN_CLASSB(addrhbo))
inaddr.s_addr = htonl(IN_CLASSB_NET);
else if (IN_CLASSC(addrhbo))
inaddr.s_addr = htonl(IN_CLASSC_NET);
else {
inaddr.s_addr = htonl(IN_CLASSE_NET);
}
}
lif->lif_v6mask._S6_un._S6_u32[3] = inaddr.s_addr;
sin->sin_addr = inaddr;
dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP netmask to %s on %s",
inet_ntoa(sin->sin_addr), lif->lif_name);
if (ioctl(v4_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP netmask "
"on %s", lif->lif_name);
return (B_FALSE);
}
IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &sin->sin_addr);
dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP address to %s on %s",
inet_ntoa(sin->sin_addr), lif->lif_name);
if (ioctl(v4_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP address "
"on %s", lif->lif_name);
return (B_FALSE);
}
if (!lif->lif_dad_wait) {
lif->lif_dad_wait = _B_TRUE;
dsmp->dsm_lif_wait++;
}
if (ack->opts[CD_BROADCASTADDR] != NULL &&
ack->opts[CD_BROADCASTADDR]->len == sizeof (inaddr)) {
(void) memcpy(&inaddr, ack->opts[CD_BROADCASTADDR]->value,
sizeof (inaddr));
} else {
if (ack->opts[CD_BROADCASTADDR] != NULL &&
ack->opts[CD_BROADCASTADDR]->len != sizeof (inaddr)) {
dhcpmsg(MSG_WARNING, "configure_v4_lease: specified "
"broadcast address length is %d instead of %d, "
"ignoring", ack->opts[CD_BROADCASTADDR]->len,
sizeof (inaddr));
} else {
dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP "
"broadcast specified for %s, making best guess",
lif->lif_name);
}
IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &inaddr);
inaddr.s_addr |= ~lif->lif_v6mask._S6_un._S6_u32[3];
}
if (ioctl(v4_sock_fd, SIOCGLIFBRDADDR, &lifr) == -1) {
dhcpmsg(MSG_ERR, "configure_v4_lease: cannot get broadcast "
"address for %s", lif->lif_name);
return (B_FALSE);
}
if (inaddr.s_addr != sin->sin_addr.s_addr) {
dhcpmsg(MSG_WARNING, "configure_v4_lease: incorrect broadcast "
"address %s specified for %s; ignoring", inet_ntoa(inaddr),
lif->lif_name);
}
lif->lif_broadcast = sin->sin_addr.s_addr;
dhcpmsg(MSG_INFO,
"configure_v4_lease: using broadcast address %s on %s",
inet_ntoa(inaddr), lif->lif_name);
return (B_TRUE);
}
boolean_t
save_server_id(dhcp_smach_t *dsmp, PKT_LIST *msg)
{
const dhcpv6_option_t *d6o;
uint_t olen;
d6o = dhcpv6_pkt_option(msg, NULL, DHCPV6_OPT_SERVERID, &olen);
if (d6o == NULL)
return (B_FALSE);
olen -= sizeof (*d6o);
free(dsmp->dsm_serverid);
if ((dsmp->dsm_serverid = malloc(olen)) == NULL) {
return (B_FALSE);
} else {
dsmp->dsm_serveridlen = olen;
(void) memcpy(dsmp->dsm_serverid, d6o + 1, olen);
return (B_TRUE);
}
}