#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <syslog.h>
#include <door.h>
#include <errno.h>
#include <alloca.h>
#include <libdlpi.h>
#include <libdlbridge.h>
#include <stp_in.h>
#include <net/bridge.h>
#include "global.h"
#define DOOR_DIRMODE 0755
#define DOOR_FILEMODE 0444
static int door_fd = -1;
static char doorname[MAXPATHLEN];
static void
bridge_door_server(void *cookie, char *argp, size_t arg_size, door_desc_t *dp,
uint_t ndesc)
{
bridge_door_cmd_t *bdc = (bridge_door_cmd_t *)argp;
int retv = EINVAL;
bridge_door_cfg_t bdcf;
UID_STP_STATE_T smstate;
UID_STP_PORT_CFG_T portcfg;
UID_STP_PORT_STATE_T portstate;
struct portdata *pdp;
int twoints[2];
if (arg_size < sizeof (*bdc) || lock_engine() != 0) {
(void) door_return((char *)&retv, sizeof (retv), NULL, 0);
return;
}
switch (bdc->bdc_type) {
case bdcBridgeGetConfig:
if ((retv = STP_IN_stpm_get_cfg(0, &bdcf.bdcf_cfg)) != 0)
break;
bdcf.bdcf_prot = protect;
unlock_engine();
(void) door_return((char *)&bdcf, sizeof (bdcf), NULL, 0);
return;
case bdcBridgeGetState:
if ((retv = STP_IN_stpm_get_state(0, &smstate)) != 0)
break;
unlock_engine();
(void) door_return((char *)&smstate, sizeof (smstate), NULL, 0);
return;
case bdcBridgeGetPorts: {
datalink_id_t *dlp;
int *rbuf;
size_t rlen;
int i, nports;
if (nextport == 0) {
twoints[0] = 0;
rbuf = twoints;
rlen = sizeof (twoints);
} else {
rlen = sizeof (int) + nextport * sizeof (datalink_id_t);
rbuf = alloca(rlen);
dlp = (datalink_id_t *)(rbuf + 1);
for (i = nports = 0; i < nextport; i++) {
if (allports[i]->kern_added)
dlp[nports++] = allports[i]->linkid;
}
rbuf[0] = nports;
rlen = sizeof (int) + nports * sizeof (datalink_id_t);
}
unlock_engine();
(void) door_return((char *)rbuf, rlen, NULL, 0);
return;
}
case bdcBridgeGetRefreshCount:
twoints[0] = refresh_count;
twoints[1] = 0;
unlock_engine();
(void) door_return((char *)twoints, sizeof (twoints), NULL, 0);
return;
case bdcPortGetConfig:
if ((pdp = find_by_linkid(bdc->bdc_linkid)) == NULL)
break;
retv = STP_IN_port_get_cfg(0, pdp->port_index, &portcfg);
if (retv != 0)
break;
unlock_engine();
(void) door_return((char *)&portcfg, sizeof (portcfg), NULL, 0);
return;
case bdcPortGetState:
if ((pdp = find_by_linkid(bdc->bdc_linkid)) == NULL)
break;
portstate.port_no = pdp->port_index;
if ((retv = STP_IN_port_get_state(0, &portstate)) != 0)
break;
if (pdp->sdu_failed)
portstate.state = UID_PORT_BADSDU;
else if (protect != DLADM_BRIDGE_PROT_STP)
portstate.state = UID_PORT_NON_STP;
else if (pdp->admin_non_stp && pdp->bpdu_protect)
portstate.state = UID_PORT_DISABLED;
unlock_engine();
(void) door_return((char *)&portstate, sizeof (portstate),
NULL, 0);
return;
case bdcPortGetForwarding:
if ((pdp = find_by_linkid(bdc->bdc_linkid)) == NULL)
break;
twoints[0] = pdp->admin_status ? 1 : 0;
twoints[1] = 0;
unlock_engine();
(void) door_return((char *)twoints, sizeof (twoints), NULL, 0);
return;
}
unlock_engine();
(void) door_return((char *)&retv, sizeof (retv), NULL, 0);
}
static void
cleanup_door(void)
{
if (door_fd != -1) {
(void) door_revoke(door_fd);
door_fd = -1;
}
if (doorname[0] != '\0') {
(void) unlink(doorname);
doorname[0] = '\0';
}
}
void
init_door(void)
{
int fd;
(void) mkdir(DOOR_DIRNAME, DOOR_DIRMODE);
(void) snprintf(doorname, sizeof (doorname), "%s/%s", DOOR_DIRNAME,
instance_name);
fd = open(doorname,
O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_NONBLOCK,
DOOR_FILEMODE);
if (fd != -1) {
(void) close(fd);
} else if (errno != EEXIST) {
syslog(LOG_ERR, "unable to create control door node: %m");
exit(EXIT_FAILURE);
}
(void) atexit(cleanup_door);
door_fd = door_create(bridge_door_server, NULL,
DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
if (door_fd == -1) {
syslog(LOG_ERR, "unable to create control door: %m");
exit(EXIT_FAILURE);
}
(void) fdetach(doorname);
if (fattach(door_fd, doorname) == -1) {
syslog(LOG_ERR, "unable to attach control door: %m");
exit(EXIT_FAILURE);
}
}