#include <stdlib.h>
#include <string.h>
#include <search.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/dhcp.h>
#include <netinet/udp.h>
#include <netinet/ip_var.h>
#include <netinet/udp_var.h>
#include <arpa/inet.h>
#include <dhcp_hostconf.h>
#include <dhcpagent_util.h>
#include <dhcpmsg.h>
#include "states.h"
#include "util.h"
#include "packet.h"
#include "interface.h"
#include "agent.h"
static PKT_LIST *select_best(dhcp_smach_t *);
static void request_failed(dhcp_smach_t *);
static stop_func_t stop_requesting;
void
send_v6_request(dhcp_smach_t *dsmp)
{
dhcp_pkt_t *dpkt;
dhcpv6_ia_na_t d6in;
dpkt = init_pkt(dsmp, DHCPV6_MSG_REQUEST);
(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, dsmp->dsm_serverid,
dsmp->dsm_serveridlen);
d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid);
d6in.d6in_t1 = htonl(0);
d6in.d6in_t2 = htonl(0);
(void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
(dhcpv6_option_t *)&d6in + 1,
sizeof (d6in) - sizeof (dhcpv6_option_t));
(void) add_pkt_prl(dpkt, dsmp);
(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_requesting,
DHCPV6_REQ_TIMEOUT, DHCPV6_REQ_MAX_RT);
(void) set_smach_state(dsmp, REQUESTING);
}
void
server_unicast_option(dhcp_smach_t *dsmp, PKT_LIST *plp)
{
const dhcpv6_option_t *d6o;
uint_t olen;
d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_UNICAST, &olen);
olen -= sizeof (*d6o);
if (d6o == NULL) {
} else if (olen != sizeof (dsmp->dsm_server)) {
dhcpmsg(MSG_WARNING, "server_unicast_option: %s has Server "
"Unicast option with bad length",
pkt_type_to_string(pkt_recv_type(plp), B_TRUE));
} else {
in6_addr_t addr;
(void) memcpy(&addr, d6o + 1, olen);
if (IN6_IS_ADDR_UNSPECIFIED(&addr)) {
dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
"to unspecified address ignored");
} else if (IN6_IS_ADDR_MULTICAST(&addr)) {
dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
"to multicast address ignored");
} else if (IN6_IS_ADDR_V4COMPAT(&addr) ||
IN6_IS_ADDR_V4MAPPED(&addr)) {
dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
"to invalid address ignored");
} else {
dsmp->dsm_server = addr;
}
}
}
void
dhcp_requesting(iu_tq_t *tqp, void *arg)
{
dhcp_smach_t *dsmp = arg;
dhcp_pkt_t *dpkt;
PKT_LIST *offer;
lease_t lease;
boolean_t isv6 = dsmp->dsm_isv6;
if (tqp != NULL) {
dhcpmsg(MSG_VERBOSE,
"dhcp_requesting: offer wait timer on v%d %s",
isv6 ? 6 : 4, dsmp->dsm_name);
dsmp->dsm_offer_timer = -1;
if (!verify_smach(dsmp))
return;
} else {
cancel_offer_timer(dsmp);
}
offer = select_best(dsmp);
if (offer == NULL) {
dhcpmsg(MSG_VERBOSE,
"no OFFERs/Advertisements on %s, waiting...",
dsmp->dsm_name);
if ((dsmp->dsm_offer_timer = iu_schedule_timer(tq,
dsmp->dsm_offer_wait, dhcp_requesting, dsmp)) == -1) {
dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot "
"reschedule callback, reverting to INIT state on "
"%s", dsmp->dsm_name);
stop_pkt_retransmission(dsmp);
(void) set_smach_state(dsmp, INIT);
dsmp->dsm_dflags |= DHCP_IF_FAILED;
ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
} else {
hold_smach(dsmp);
}
return;
}
if (!isv6)
free_pkt_list(&dsmp->dsm_recv_pkt_list);
stop_pkt_retransmission(dsmp);
if (!isv6 && offer->opts[CD_DHCP_TYPE] == NULL) {
free_pkt_list(&dsmp->dsm_recv_pkt_list);
if (!set_smach_state(dsmp, REQUESTING)) {
dhcp_restart(dsmp);
return;
}
if (!dhcp_bound(dsmp, offer)) {
dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
"failed for %s", dsmp->dsm_name);
dhcp_restart(dsmp);
return;
}
return;
}
save_domainname(dsmp, offer);
if (isv6) {
const char *estr, *msg;
const dhcpv6_option_t *d6o;
uint_t olen, msglen;
d6o = dhcpv6_pkt_option(offer, NULL, DHCPV6_OPT_STATUS_CODE,
&olen);
(void) dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
print_server_msg(dsmp, msg, msglen);
if (!save_server_id(dsmp, offer))
goto failure;
server_unicast_option(dsmp, offer);
send_v6_request(dsmp);
} else {
if (offer->opts[CD_MESSAGE] != NULL) {
print_server_msg(dsmp,
(char *)offer->opts[CD_MESSAGE]->value,
offer->opts[CD_MESSAGE]->len);
}
dpkt = init_pkt(dsmp, REQUEST);
(void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
sizeof (lease_t));
(void) add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
(void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
htons(dsmp->dsm_lif->lif_pif->pif_mtu -
sizeof (struct udpiphdr)));
(void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
offer->pkt->yiaddr.s_addr);
(void) add_pkt_opt(dpkt, CD_SERVER_ID,
offer->opts[CD_SERVER_ID]->value,
offer->opts[CD_SERVER_ID]->len);
if (class_id_len != 0) {
(void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
class_id_len);
}
(void) add_pkt_prl(dpkt, dsmp);
if (!dhcp_add_fqdn_opt(dpkt, dsmp) &&
dsmp->dsm_reqhost != NULL) {
(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
strlen(dsmp->dsm_reqhost));
}
(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
if (!set_smach_state(dsmp, REQUESTING)) {
dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot switch to "
"REQUESTING state; reverting to INIT on %s",
dsmp->dsm_name);
goto failure;
}
(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
stop_requesting);
}
free_pkt_entry(offer);
return;
failure:
dsmp->dsm_dflags |= DHCP_IF_FAILED;
(void) set_smach_state(dsmp, INIT);
ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
free_pkt_list(&dsmp->dsm_recv_pkt_list);
}
static int
compute_points_v6(const PKT_LIST *pkt, const dhcp_smach_t *dsmp)
{
char abuf[INET6_ADDRSTRLEN];
int points = 0;
const dhcpv6_option_t *d6o, *d6so;
uint_t olen, solen;
int i;
const char *estr, *msg;
uint_t msglen;
points++;
d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_STATUS_CODE, &olen);
if (dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen) ==
DHCPV6_STAT_NOADDRS) {
dhcpmsg(MSG_INFO,
"discard advertisement from %s on %s: no address status",
inet_ntop(AF_INET6,
&((struct sockaddr_in6 *)&pkt->pktfrom)->sin6_addr,
abuf, sizeof (abuf)), dsmp->dsm_name);
return (-1);
}
d6o = NULL;
while ((d6o = dhcpv6_pkt_option(pkt, d6o, DHCPV6_OPT_IA_NA,
&olen)) != NULL) {
d6so = dhcpv6_find_option(
(const char *)d6o + sizeof (dhcpv6_ia_na_t),
olen - sizeof (dhcpv6_ia_na_t), NULL,
DHCPV6_OPT_STATUS_CODE, &solen);
if (dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen) ==
DHCPV6_STAT_NOADDRS)
return (-1);
points += 2;
}
for (i = 0; i < dsmp->dsm_prllen; i++) {
if (dhcpv6_pkt_option(pkt, NULL, dsmp->dsm_prl[i], NULL) !=
NULL)
points++;
}
d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_PREFERENCE, &olen);
if (d6o != NULL && olen == sizeof (*d6o) + 1) {
int pref = *(const uchar_t *)(d6o + 1);
if (pref == 255)
return (-2);
points += 10 * pref;
}
return (points);
}
static int
compute_points_v4(const PKT_LIST *pkt)
{
int points = 0;
if (pkt->opts[CD_DHCP_TYPE] == NULL) {
dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid BOOTP reply");
goto valid_offer;
}
if (pkt->opts[CD_LEASE_TIME] == NULL) {
dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without lease "
"time");
return (-1);
}
if (pkt->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
"lease time");
return (-1);
}
if (pkt->opts[CD_SERVER_ID] == NULL) {
dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without server "
"id");
return (-1);
}
if (pkt->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
"server id");
return (-1);
}
dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid OFFER packet");
points += 30;
valid_offer:
if (pkt->rfc1048)
points += 5;
if (pkt->opts[CD_VENDOR_SPEC] != NULL)
points += 80;
if (pkt->opts[CD_SUBNETMASK] != NULL)
points++;
if (pkt->opts[CD_ROUTER] != NULL)
points++;
if (pkt->opts[CD_HOSTNAME] != NULL)
points += 5;
return (points);
}
static PKT_LIST *
select_best(dhcp_smach_t *dsmp)
{
PKT_LIST *current = dsmp->dsm_recv_pkt_list;
PKT_LIST *next, *best = NULL;
int points, best_points = -1;
for (; current != NULL; current = next) {
next = current->next;
points = current->isv6 ?
compute_points_v6(current, dsmp) :
compute_points_v4(current);
if (points == -1) {
remque(current);
free_pkt_entry(current);
continue;
}
dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points);
if (points == -2) {
best = current;
break;
}
if (points >= best_points) {
best_points = points;
best = current;
}
}
if (best != NULL) {
dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points);
remque(best);
} else {
dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply");
}
return (best);
}
static void
accept_v4_acknak(dhcp_smach_t *dsmp, PKT_LIST *plp)
{
dsmp->dsm_received++;
if (*plp->opts[CD_DHCP_TYPE]->value == ACK) {
if (dsmp->dsm_state != INFORM_SENT &&
dsmp->dsm_state != INFORMATION &&
(plp->opts[CD_LEASE_TIME] == NULL ||
plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
dhcpmsg(MSG_WARNING, "accept_v4_acknak: ACK packet on "
"%s missing mandatory lease option, ignored",
dsmp->dsm_name);
dsmp->dsm_bad_offers++;
free_pkt_entry(plp);
return;
}
if ((dsmp->dsm_state == RENEWING ||
dsmp->dsm_state == REBINDING) &&
dsmp->dsm_leases->dl_lifs->lif_addr !=
plp->pkt->yiaddr.s_addr) {
dhcpmsg(MSG_WARNING, "accept_v4_acknak: renewal ACK "
"packet has a different IP address (%s), ignored",
inet_ntoa(plp->pkt->yiaddr));
dsmp->dsm_bad_offers++;
free_pkt_entry(plp);
return;
}
}
stop_pkt_retransmission(dsmp);
if (*plp->opts[CD_DHCP_TYPE]->value == NAK) {
dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s",
dsmp->dsm_name);
dsmp->dsm_bad_offers++;
free_pkt_entry(plp);
dhcp_restart(dsmp);
(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
return;
}
if (plp->opts[CD_SERVER_ID] == NULL ||
plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
dhcpmsg(MSG_ERROR, "accept_v4_acknak: ACK with no valid "
"server id on %s", dsmp->dsm_name);
dsmp->dsm_bad_offers++;
free_pkt_entry(plp);
dhcp_restart(dsmp);
return;
}
if (plp->opts[CD_MESSAGE] != NULL) {
print_server_msg(dsmp, (char *)plp->opts[CD_MESSAGE]->value,
plp->opts[CD_MESSAGE]->len);
}
dhcpmsg(MSG_VERBOSE, "accept_v4_acknak: ACK on %s", dsmp->dsm_name);
if (!dhcp_bound(dsmp, plp)) {
dhcpmsg(MSG_WARNING, "accept_v4_acknak: dhcp_bound failed "
"for %s", dsmp->dsm_name);
dhcp_restart(dsmp);
}
}
static void
accept_v6_message(dhcp_smach_t *dsmp, PKT_LIST *plp, const char *pname,
uchar_t recv_type)
{
const dhcpv6_option_t *d6o;
uint_t olen;
const char *estr, *msg;
uint_t msglen;
int status;
dsmp->dsm_received++;
if (recv_type == DHCPV6_MSG_RECONFIGURE) {
dhcpmsg(MSG_VERBOSE, "accept_v6_message: ignored Reconfigure "
"on %s", dsmp->dsm_name);
free_pkt_entry(plp);
return;
}
d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_CLIENTID, &olen);
olen -= sizeof (*d6o);
if (d6o == NULL || olen != dsmp->dsm_cidlen ||
memcmp(d6o + 1, dsmp->dsm_cid, olen) != 0) {
dhcpmsg(MSG_VERBOSE,
"accept_v6_message: discarded %s on %s: %s Client ID",
pname, dsmp->dsm_name, d6o == NULL ? "no" : "wrong");
free_pkt_entry(plp);
return;
}
d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_SERVERID, &olen);
if (d6o == NULL) {
dhcpmsg(MSG_DEBUG,
"accept_v6_message: discarded %s on %s: no Server ID",
pname, dsmp->dsm_name);
free_pkt_entry(plp);
return;
}
if (recv_type == DHCPV6_MSG_REPLY && dsmp->dsm_state != SELECTING &&
dsmp->dsm_state != INIT_REBOOT && dsmp->dsm_state != REBINDING &&
dsmp->dsm_state != INFORM_SENT) {
olen -= sizeof (*d6o);
if (olen != dsmp->dsm_serveridlen ||
memcmp(d6o + 1, dsmp->dsm_serverid, olen) != 0) {
dhcpmsg(MSG_DEBUG, "accept_v6_message: discarded %s on "
"%s: wrong Server ID", pname, dsmp->dsm_name);
free_pkt_entry(plp);
return;
}
}
switch (dsmp->dsm_state) {
case SELECTING:
if (recv_type == DHCPV6_MSG_REPLY) {
if (dhcpv6_pkt_option(plp, NULL,
DHCPV6_OPT_RAPID_COMMIT, &olen) == NULL) {
dhcpmsg(MSG_DEBUG, "accept_v6_message: Reply "
"on %s lacks Rapid-Commit; ignoring",
dsmp->dsm_name);
break;
}
dhcpmsg(MSG_VERBOSE,
"accept_v6_message: rapid-commit Reply on %s",
dsmp->dsm_name);
cancel_offer_timer(dsmp);
goto rapid_commit;
}
if (recv_type != DHCPV6_MSG_ADVERTISE)
break;
d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_PREFERENCE,
&olen);
if (d6o != NULL && olen == sizeof (*d6o) + 1 &&
*(const uchar_t *)(d6o + 1) == 255) {
pkt_smach_enqueue(dsmp, plp);
dhcpmsg(MSG_DEBUG, "accept_v6_message: preference 255;"
" immediate Request on %s", dsmp->dsm_name);
dhcp_requesting(NULL, dsmp);
} else {
pkt_smach_enqueue(dsmp, plp);
}
return;
case PRE_BOUND:
case BOUND:
break;
case REQUESTING:
case INIT_REBOOT:
case RENEWING:
case REBINDING:
case INFORM_SENT:
if (recv_type != DHCPV6_MSG_REPLY)
break;
dhcpmsg(MSG_VERBOSE,
"accept_v6_message: received Reply message on %s",
dsmp->dsm_name);
rapid_commit:
d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
&olen);
status = dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
if (status == DHCPV6_STAT_USEMCAST) {
if (IN6_IS_ADDR_MULTICAST(
&dsmp->dsm_send_dest.v6.sin6_addr)) {
break;
} else {
free_pkt_entry(plp);
dsmp->dsm_send_dest.v6.sin6_addr =
ipv6_all_dhcp_relay_and_servers;
retransmit_now(dsmp);
return;
}
}
print_server_msg(dsmp, msg, msglen);
if (status == DHCPV6_STAT_SUCCESS ||
status == DHCPV6_STAT_NOBINDING) {
if (dhcp_bound(dsmp, plp)) {
server_unicast_option(dsmp, plp);
} else {
stop_pkt_retransmission(dsmp);
dhcpmsg(MSG_WARNING, "accept_v6_message: "
"dhcp_bound failed for %s", dsmp->dsm_name);
(void) remove_hostconf(dsmp->dsm_name,
dsmp->dsm_isv6);
dhcp_restart(dsmp);
}
} else {
dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
estr);
stop_pkt_retransmission(dsmp);
free_pkt_entry(plp);
if (dsmp->dsm_state == INFORM_SENT) {
(void) set_smach_state(dsmp, INIT);
ipc_action_finish(dsmp, DHCP_IPC_E_SRVFAILED);
} else {
(void) remove_hostconf(dsmp->dsm_name,
dsmp->dsm_isv6);
request_failed(dsmp);
}
}
return;
case DECLINING:
if (recv_type != DHCPV6_MSG_REPLY)
break;
stop_pkt_retransmission(dsmp);
d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
&olen);
if (dhcpv6_status_code(d6o, olen, &estr, &msg,
&msglen) == DHCPV6_STAT_SUCCESS) {
print_server_msg(dsmp, msg, msglen);
} else {
dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
estr);
}
free_pkt_entry(plp);
if (dsmp->dsm_leases == NULL) {
dhcpmsg(MSG_VERBOSE, "accept_v6_message: %s has no "
"leases left", dsmp->dsm_name);
dhcp_restart(dsmp);
} else if (dsmp->dsm_lif_wait == 0) {
(void) set_smach_state(dsmp, BOUND);
} else {
(void) set_smach_state(dsmp, PRE_BOUND);
}
return;
case RELEASING:
if (recv_type != DHCPV6_MSG_REPLY)
break;
stop_pkt_retransmission(dsmp);
d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
&olen);
if (dhcpv6_status_code(d6o, olen, &estr, &msg,
&msglen) == DHCPV6_STAT_SUCCESS) {
print_server_msg(dsmp, msg, msglen);
} else {
dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
estr);
}
free_pkt_entry(plp);
finished_smach(dsmp, DHCP_IPC_SUCCESS);
return;
}
dhcpmsg(MSG_VERBOSE,
"accept_v6_message: discarded v6 %s on %s; state %s",
pname, dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state));
free_pkt_entry(plp);
}
void
dhcp_acknak_global(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
void *arg)
{
PKT_LIST *plp;
dhcp_pif_t *pif;
uchar_t recv_type;
const char *pname;
uint_t xid;
dhcp_smach_t *dsmp;
boolean_t isv6 = (fd == v6_sock_fd);
struct sockaddr_in sin;
const char *reason;
size_t sinlen = sizeof (sin);
int sock;
plp = recv_pkt(fd, get_max_mtu(isv6), isv6);
if (plp == NULL)
return;
recv_type = pkt_recv_type(plp);
pname = pkt_type_to_string(recv_type, isv6);
xid = pkt_get_xid(plp->pkt, isv6);
for (dsmp = lookup_smach_by_xid(xid, NULL, isv6); dsmp != NULL;
dsmp = lookup_smach_by_xid(xid, dsmp, isv6)) {
pif = dsmp->dsm_lif->lif_pif;
if (pif->pif_index == plp->ifindex ||
pif->pif_under_ipmp && pif->pif_grindex == plp->ifindex)
break;
}
if (dsmp == NULL) {
dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet"
" on ifindex %d: unknown state machine", isv6 ? 6 : 4,
pname, plp->ifindex);
free_pkt_entry(plp);
return;
}
if (!isv6 && !pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
reason = "not ACK or NAK";
goto drop;
}
if (!isv6) {
sock = dsmp->dsm_lif->lif_sock_ip_fd;
if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) != -1 &&
sin.sin_addr.s_addr == INADDR_ANY) {
reason = "handled by lif_sock_ip_fd";
goto drop;
}
}
if (isv6)
accept_v6_message(dsmp, plp, pname, recv_type);
else
accept_v4_acknak(dsmp, plp);
return;
drop:
dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet for %s "
"received on global socket: %s", isv6 ? 6 : 4, pname, pif->pif_name,
reason);
free_pkt_entry(plp);
}
static void
request_failed(dhcp_smach_t *dsmp)
{
PKT_LIST *offer;
dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
if ((offer = select_best(dsmp)) != NULL) {
insque(offer, &dsmp->dsm_recv_pkt_list);
dhcp_requesting(NULL, dsmp);
} else {
dhcpmsg(MSG_INFO, "no offers left on %s; restarting",
dsmp->dsm_name);
dhcp_selecting(dsmp);
}
}
void
dhcp_packet_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
void *arg)
{
dhcp_lif_t *lif = arg;
PKT_LIST *plp;
uchar_t recv_type;
const char *pname;
uint_t xid;
dhcp_smach_t *dsmp;
if ((plp = recv_pkt(fd, lif->lif_pif->pif_mtu, B_FALSE)) == NULL)
return;
recv_type = pkt_recv_type(plp);
pname = pkt_type_to_string(recv_type, B_FALSE);
if (!pkt_v4_match(recv_type,
DHCP_PACK | DHCP_PNAK | DHCP_PUNTYPED | DHCP_POFFER)) {
dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored v4 %s packet "
"received via LIF %s", pname, lif->lif_name);
free_pkt_entry(plp);
return;
}
xid = pkt_get_xid(plp->pkt, B_FALSE);
for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL;
dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) {
if (dsmp->dsm_lif == lif)
break;
}
if (dsmp == NULL)
goto drop;
if (pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
accept_v4_acknak(dsmp, plp);
} else {
if (is_bound_state(dsmp->dsm_state))
goto drop;
pkt_smach_enqueue(dsmp, plp);
}
return;
drop:
dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored %s packet xid "
"%x received via LIF %s; %s", pname, xid, lif->lif_name,
dsmp == NULL ? "unknown state machine" : "bound");
free_pkt_entry(plp);
}
void
dhcp_restart(dhcp_smach_t *dsmp)
{
if (dsmp->dsm_state == INFORM_SENT || dsmp->dsm_state == INFORMATION)
return;
deprecate_leases(dsmp);
if (!set_start_timer(dsmp)) {
dhcpmsg(MSG_ERROR, "dhcp_restart: cannot schedule dhcp_start, "
"reverting to INIT state on %s", dsmp->dsm_name);
(void) set_smach_state(dsmp, INIT);
dsmp->dsm_dflags |= DHCP_IF_FAILED;
ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
} else {
dhcpmsg(MSG_DEBUG, "dhcp_restart: restarting DHCP on %s",
dsmp->dsm_name);
}
}
static boolean_t
stop_requesting(dhcp_smach_t *dsmp, unsigned int n_requests)
{
uint_t maxreq;
maxreq = dsmp->dsm_isv6 ? DHCPV6_REQ_MAX_RC : DHCP_MAX_REQUESTS;
if (n_requests >= maxreq) {
dhcpmsg(MSG_INFO, "no ACK/NAK/Reply to REQUEST on %s",
dsmp->dsm_name);
request_failed(dsmp);
return (B_TRUE);
} else {
return (B_FALSE);
}
}