1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * bridged - bridging control daemon.  This module provides the door-based
29 * interface used by user applications to gather bridge status information.
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <fcntl.h>
36#include <sys/stat.h>
37#include <sys/types.h>
38#include <syslog.h>
39#include <door.h>
40#include <errno.h>
41#include <alloca.h>
42#include <libdlpi.h>
43#include <libdlbridge.h>
44#include <stp_in.h>
45#include <net/bridge.h>
46
47#include "global.h"
48
49#define	DOOR_DIRMODE	0755
50#define	DOOR_FILEMODE	0444
51
52static int door_fd = -1;
53static char doorname[MAXPATHLEN];
54
55/*ARGSUSED*/
56static void
57bridge_door_server(void *cookie, char *argp, size_t arg_size, door_desc_t *dp,
58    uint_t ndesc)
59{
60	/* LINTED: alignment */
61	bridge_door_cmd_t *bdc = (bridge_door_cmd_t *)argp;
62	int retv = EINVAL;
63	bridge_door_cfg_t bdcf;
64	UID_STP_STATE_T smstate;
65	UID_STP_PORT_CFG_T portcfg;
66	UID_STP_PORT_STATE_T portstate;
67	struct portdata *pdp;
68	int twoints[2];
69
70	if (arg_size < sizeof (*bdc) || lock_engine() != 0) {
71		(void) door_return((char *)&retv, sizeof (retv), NULL, 0);
72		return;
73	}
74
75	switch (bdc->bdc_type) {
76	case bdcBridgeGetConfig:
77		if ((retv = STP_IN_stpm_get_cfg(0, &bdcf.bdcf_cfg)) != 0)
78			break;
79		bdcf.bdcf_prot = protect;
80		unlock_engine();
81		(void) door_return((char *)&bdcf, sizeof (bdcf), NULL, 0);
82		return;
83
84	case bdcBridgeGetState:
85		if ((retv = STP_IN_stpm_get_state(0, &smstate)) != 0)
86			break;
87		unlock_engine();
88		(void) door_return((char *)&smstate, sizeof (smstate), NULL, 0);
89		return;
90
91	case bdcBridgeGetPorts: {
92		datalink_id_t *dlp;
93		int *rbuf;
94		size_t rlen;
95		int i, nports;
96
97		if (nextport == 0) {
98			twoints[0] = 0;
99			rbuf = twoints;
100			rlen = sizeof (twoints);
101		} else {
102			rlen = sizeof (int) + nextport * sizeof (datalink_id_t);
103			rbuf = alloca(rlen);
104			dlp = (datalink_id_t *)(rbuf + 1);
105			for (i = nports = 0; i < nextport; i++) {
106				if (allports[i]->kern_added)
107					dlp[nports++] = allports[i]->linkid;
108			}
109			rbuf[0] = nports;
110			rlen = sizeof (int) + nports * sizeof (datalink_id_t);
111		}
112		unlock_engine();
113		(void) door_return((char *)rbuf, rlen, NULL, 0);
114		return;
115	}
116
117	case bdcBridgeGetRefreshCount:
118		twoints[0] = refresh_count;
119		twoints[1] = 0;
120		unlock_engine();
121		(void) door_return((char *)twoints, sizeof (twoints), NULL, 0);
122		return;
123
124	case bdcPortGetConfig:
125		if ((pdp = find_by_linkid(bdc->bdc_linkid)) == NULL)
126			break;
127		retv = STP_IN_port_get_cfg(0, pdp->port_index, &portcfg);
128		if (retv != 0)
129			break;
130		unlock_engine();
131		(void) door_return((char *)&portcfg, sizeof (portcfg), NULL, 0);
132		return;
133
134	case bdcPortGetState:
135		if ((pdp = find_by_linkid(bdc->bdc_linkid)) == NULL)
136			break;
137		portstate.port_no = pdp->port_index;
138		if ((retv = STP_IN_port_get_state(0, &portstate)) != 0)
139			break;
140		if (pdp->sdu_failed)
141			portstate.state = UID_PORT_BADSDU;
142		else if (protect != DLADM_BRIDGE_PROT_STP)
143			portstate.state = UID_PORT_NON_STP;
144		else if (pdp->admin_non_stp && pdp->bpdu_protect)
145			portstate.state = UID_PORT_DISABLED;
146		unlock_engine();
147		(void) door_return((char *)&portstate, sizeof (portstate),
148		    NULL, 0);
149		return;
150
151	case bdcPortGetForwarding:
152		if ((pdp = find_by_linkid(bdc->bdc_linkid)) == NULL)
153			break;
154		twoints[0] = pdp->admin_status ? 1 : 0;
155		twoints[1] = 0;
156		unlock_engine();
157		(void) door_return((char *)twoints, sizeof (twoints), NULL, 0);
158		return;
159	}
160	unlock_engine();
161	(void) door_return((char *)&retv, sizeof (retv), NULL, 0);
162}
163
164static void
165cleanup_door(void)
166{
167	if (door_fd != -1) {
168		(void) door_revoke(door_fd);
169		door_fd = -1;
170	}
171	if (doorname[0] != '\0') {
172		(void) unlink(doorname);
173		doorname[0] = '\0';
174	}
175}
176
177void
178init_door(void)
179{
180	int fd;
181
182	/* Make sure that the control directory exists */
183	(void) mkdir(DOOR_DIRNAME, DOOR_DIRMODE);
184
185	/* Each instance gets a separate door. */
186	(void) snprintf(doorname, sizeof (doorname), "%s/%s", DOOR_DIRNAME,
187	    instance_name);
188
189	/* Do a low-overhead "touch" on the file that will be the door node. */
190	fd = open(doorname,
191	    O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_NONBLOCK,
192	    DOOR_FILEMODE);
193	if (fd != -1) {
194		(void) close(fd);
195	} else if (errno != EEXIST) {
196		syslog(LOG_ERR, "unable to create control door node: %m");
197		exit(EXIT_FAILURE);
198	}
199
200	(void) atexit(cleanup_door);
201
202	/* Create the door. */
203	door_fd = door_create(bridge_door_server, NULL,
204	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
205	if (door_fd == -1) {
206		syslog(LOG_ERR, "unable to create control door: %m");
207		exit(EXIT_FAILURE);
208	}
209
210	/* Attach the door to the file. */
211	(void) fdetach(doorname);
212	if (fattach(door_fd, doorname) == -1) {
213		syslog(LOG_ERR, "unable to attach control door: %m");
214		exit(EXIT_FAILURE);
215	}
216}
217