#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netinet/dhcp.h>
#include <netinet/dhcp6.h>
#include <dhcpmsg.h>
#include <dhcp_hostconf.h>
#include <dhcpagent_util.h>
#include "agent.h"
#include "packet.h"
#include "interface.h"
#include "states.h"
static boolean_t stop_release_decline(dhcp_smach_t *, unsigned int);
void
send_declines(dhcp_smach_t *dsmp)
{
dhcp_pkt_t *dpkt;
dhcp_lease_t *dlp, *dlpn;
uint_t nlifs;
dhcp_lif_t *lif, *lifn;
boolean_t got_one;
if (dsmp->dsm_isv6) {
if ((dpkt = init_pkt(dsmp, DHCPV6_MSG_DECLINE)) == NULL)
return;
(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
dsmp->dsm_serverid, dsmp->dsm_serveridlen);
} else {
ipaddr_t serverip;
if (dsmp->dsm_ack->opts[CD_DHCP_TYPE] == NULL)
return;
if ((dpkt = init_pkt(dsmp, DECLINE)) == NULL)
return;
IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip);
(void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip);
}
got_one = B_FALSE;
for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) {
dlpn = dlp->dl_next;
lif = dlp->dl_lifs;
for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lifn) {
lifn = lif->lif_next;
if (lif->lif_declined != NULL) {
(void) add_pkt_lif(dpkt, lif,
DHCPV6_STAT_UNSPECFAIL, lif->lif_declined);
unplumb_lif(lif);
got_one = B_TRUE;
}
}
if (dlp->dl_nlifs == 0)
remove_lease(dlp);
}
if (!got_one)
return;
(void) set_smach_state(dsmp, DECLINING);
if (dsmp->dsm_isv6) {
(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
stop_release_decline, DHCPV6_DEC_TIMEOUT, 0);
} else {
(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST), NULL);
}
}
int
dhcp_release(dhcp_smach_t *dsmp, void *arg)
{
const char *msg = arg;
dhcp_pkt_t *dpkt;
dhcp_lease_t *dlp;
dhcp_lif_t *lif;
ipaddr_t serverip;
uint_t nlifs;
if ((dsmp->dsm_dflags & DHCP_IF_BOOTP) ||
!check_cmd_allowed(dsmp->dsm_state, DHCP_RELEASE)) {
ipc_action_finish(dsmp, DHCP_IPC_E_INT);
return (0);
}
dhcpmsg(MSG_INFO, "releasing leases for state machine %s",
dsmp->dsm_name);
(void) set_smach_state(dsmp, RELEASING);
(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
if (dsmp->dsm_isv6) {
dpkt = init_pkt(dsmp, DHCPV6_MSG_RELEASE);
(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
dsmp->dsm_serverid, dsmp->dsm_serveridlen);
for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
lif = dlp->dl_lifs;
for (nlifs = dlp->dl_nlifs; nlifs > 0;
nlifs--, lif = lif->lif_next) {
(void) add_pkt_lif(dpkt, lif,
DHCPV6_STAT_SUCCESS, NULL);
}
}
deprecate_leases(dsmp);
(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
stop_release_decline, DHCPV6_REL_TIMEOUT, 0);
} else {
if ((dlp = dsmp->dsm_leases) != NULL && dlp->dl_nlifs > 0) {
dpkt = init_pkt(dsmp, RELEASE);
if (msg != NULL) {
(void) add_pkt_opt(dpkt, CD_MESSAGE, msg,
strlen(msg) + 1);
}
lif = dlp->dl_lifs;
(void) add_pkt_lif(dpkt, dlp->dl_lifs, 0, NULL);
IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, serverip);
(void) add_pkt_opt32(dpkt, CD_SERVER_ID, serverip);
(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
(void) send_pkt(dsmp, dpkt, serverip, NULL);
}
(void) usleep(500);
deprecate_leases(dsmp);
finished_smach(dsmp, DHCP_IPC_SUCCESS);
}
return (1);
}
int
dhcp_drop(dhcp_smach_t *dsmp, void *arg)
{
dhcpmsg(MSG_INFO, "dropping leases for state machine %s",
dsmp->dsm_name);
if (dsmp->dsm_state == PRE_BOUND || dsmp->dsm_state == BOUND ||
dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) {
if (dsmp->dsm_dflags & DHCP_IF_BOOTP) {
dhcpmsg(MSG_INFO,
"used bootp; not writing lease file for %s",
dsmp->dsm_name);
} else {
write_lease_to_hostconf(dsmp);
}
} else {
dhcpmsg(MSG_DEBUG, "%s in state %s; not saving lease",
dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state));
}
deprecate_leases(dsmp);
finished_smach(dsmp, DHCP_IPC_SUCCESS);
return (1);
}
static boolean_t
stop_release_decline(dhcp_smach_t *dsmp, unsigned int n_requests)
{
if (dsmp->dsm_state == RELEASING) {
if (n_requests >= DHCPV6_REL_MAX_RC) {
dhcpmsg(MSG_INFO, "no Reply to Release, finishing "
"transaction on %s", dsmp->dsm_name);
finished_smach(dsmp, DHCP_IPC_SUCCESS);
return (B_TRUE);
} else {
return (B_FALSE);
}
} else {
if (n_requests >= DHCPV6_DEC_MAX_RC) {
dhcpmsg(MSG_INFO, "no Reply to Decline on %s",
dsmp->dsm_name);
if (dsmp->dsm_leases == NULL) {
dhcpmsg(MSG_VERBOSE, "stop_release_decline: "
"%s has no leases left", dsmp->dsm_name);
dhcp_restart(dsmp);
}
return (B_TRUE);
} else {
return (B_FALSE);
}
}
}