uipc_domain.c revision 1.60
1/*	$NetBSD: uipc_domain.c,v 1.60 2006/07/23 22:06:11 ad Exp $	*/
2
3/*
4 * Copyright (c) 1982, 1986, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 *	@(#)uipc_domain.c	8.3 (Berkeley) 2/14/95
32 */
33
34#include <sys/cdefs.h>
35__KERNEL_RCSID(0, "$NetBSD: uipc_domain.c,v 1.60 2006/07/23 22:06:11 ad Exp $");
36
37#include <sys/param.h>
38#include <sys/socket.h>
39#include <sys/socketvar.h>
40#include <sys/protosw.h>
41#include <sys/domain.h>
42#include <sys/mbuf.h>
43#include <sys/time.h>
44#include <sys/kernel.h>
45#include <sys/systm.h>
46#include <sys/callout.h>
47#include <sys/queue.h>
48#include <sys/proc.h>
49#include <sys/sysctl.h>
50#include <sys/un.h>
51#include <sys/unpcb.h>
52#include <sys/file.h>
53#include <sys/kauth.h>
54
55void	pffasttimo(void *);
56void	pfslowtimo(void *);
57
58struct domainhead domains = STAILQ_HEAD_INITIALIZER(domains);
59
60struct callout pffasttimo_ch, pfslowtimo_ch;
61
62/*
63 * Current time values for fast and slow timeouts.  We can use u_int
64 * relatively safely.  The fast timer will roll over in 27 years and
65 * the slow timer in 68 years.
66 */
67u_int	pfslowtimo_now;
68u_int	pffasttimo_now;
69
70void
71domaininit(void)
72{
73	__link_set_decl(domains, struct domain);
74	struct domain * const * dpp;
75	struct domain *rt_domain = NULL;
76
77	/*
78	 * Add all of the domains.  Make sure the PF_ROUTE
79	 * domain is added last.
80	 */
81	__link_set_foreach(dpp, domains) {
82		if ((*dpp)->dom_family == PF_ROUTE)
83			rt_domain = *dpp;
84		else
85			domain_attach(*dpp);
86	}
87	if (rt_domain)
88		domain_attach(rt_domain);
89
90	callout_init(&pffasttimo_ch);
91	callout_init(&pfslowtimo_ch);
92
93	callout_reset(&pffasttimo_ch, 1, pffasttimo, NULL);
94	callout_reset(&pfslowtimo_ch, 1, pfslowtimo, NULL);
95}
96
97void
98domain_attach(struct domain *dp)
99{
100	const struct protosw *pr;
101
102	STAILQ_INSERT_TAIL(&domains, dp, dom_link);
103
104	if (dp->dom_init)
105		(*dp->dom_init)();
106
107#ifdef MBUFTRACE
108	if (dp->dom_mowner.mo_name[0] == '\0') {
109		strncpy(dp->dom_mowner.mo_name, dp->dom_name,
110		    sizeof(dp->dom_mowner.mo_name));
111		MOWNER_ATTACH(&dp->dom_mowner);
112	}
113#endif
114	for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) {
115		if (pr->pr_init)
116			(*pr->pr_init)();
117	}
118
119	if (max_linkhdr < 16)		/* XXX */
120		max_linkhdr = 16;
121	max_hdr = max_linkhdr + max_protohdr;
122	max_datalen = MHLEN - max_hdr;
123}
124
125struct domain *
126pffinddomain(int family)
127{
128	struct domain *dp;
129
130	DOMAIN_FOREACH(dp)
131		if (dp->dom_family == family)
132			return (dp);
133	return (NULL);
134}
135
136const struct protosw *
137pffindtype(int family, int type)
138{
139	struct domain *dp;
140	const struct protosw *pr;
141
142	dp = pffinddomain(family);
143	if (dp == NULL)
144		return (NULL);
145
146	for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
147		if (pr->pr_type && pr->pr_type == type)
148			return (pr);
149
150	return (NULL);
151}
152
153const struct protosw *
154pffindproto(int family, int protocol, int type)
155{
156	struct domain *dp;
157	const struct protosw *pr;
158	const struct protosw *maybe = NULL;
159
160	if (family == 0)
161		return (NULL);
162
163	dp = pffinddomain(family);
164	if (dp == NULL)
165		return (NULL);
166
167	for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) {
168		if ((pr->pr_protocol == protocol) && (pr->pr_type == type))
169			return (pr);
170
171		if (type == SOCK_RAW && pr->pr_type == SOCK_RAW &&
172		    pr->pr_protocol == 0 && maybe == NULL)
173			maybe = pr;
174	}
175	return (maybe);
176}
177
178/*
179 * sysctl helper to stuff PF_LOCAL pcbs into sysctl structures
180 */
181static void
182sysctl_dounpcb(struct kinfo_pcb *pcb, const struct socket *so)
183{
184	struct unpcb *unp = sotounpcb(so);
185	struct sockaddr_un *un = unp->unp_addr;
186
187	memset(pcb, 0, sizeof(*pcb));
188
189	pcb->ki_family = so->so_proto->pr_domain->dom_family;
190	pcb->ki_type = so->so_proto->pr_type;
191	pcb->ki_protocol = so->so_proto->pr_protocol;
192	pcb->ki_pflags = unp->unp_flags;
193
194	pcb->ki_pcbaddr = PTRTOUINT64(unp);
195	/* pcb->ki_ppcbaddr = unp has no ppcb... */
196	pcb->ki_sockaddr = PTRTOUINT64(so);
197
198	pcb->ki_sostate = so->so_state;
199	/* pcb->ki_prstate = unp has no state... */
200
201	pcb->ki_rcvq = so->so_rcv.sb_cc;
202	pcb->ki_sndq = so->so_snd.sb_cc;
203
204	un = (struct sockaddr_un *)&pcb->ki_src;
205	/*
206	 * local domain sockets may bind without having a local
207	 * endpoint.  bleah!
208	 */
209	if (unp->unp_addr != NULL) {
210		un->sun_len = unp->unp_addr->sun_len;
211		un->sun_family = unp->unp_addr->sun_family;
212		strlcpy(un->sun_path, unp->unp_addr->sun_path,
213		    sizeof(pcb->ki_s));
214	}
215	else {
216		un->sun_len = offsetof(struct sockaddr_un, sun_path);
217		un->sun_family = pcb->ki_family;
218	}
219	if (unp->unp_conn != NULL) {
220		un = (struct sockaddr_un *)&pcb->ki_dst;
221		if (unp->unp_conn->unp_addr != NULL) {
222			un->sun_len = unp->unp_conn->unp_addr->sun_len;
223			un->sun_family = unp->unp_conn->unp_addr->sun_family;
224			un->sun_family = unp->unp_conn->unp_addr->sun_family;
225			strlcpy(un->sun_path, unp->unp_conn->unp_addr->sun_path,
226				sizeof(pcb->ki_d));
227		}
228		else {
229			un->sun_len = offsetof(struct sockaddr_un, sun_path);
230			un->sun_family = pcb->ki_family;
231		}
232	}
233
234	pcb->ki_inode = unp->unp_ino;
235	pcb->ki_vnode = PTRTOUINT64(unp->unp_vnode);
236	pcb->ki_conn = PTRTOUINT64(unp->unp_conn);
237	pcb->ki_refs = PTRTOUINT64(unp->unp_refs);
238	pcb->ki_nextref = PTRTOUINT64(unp->unp_nextref);
239}
240
241static int
242sysctl_unpcblist(SYSCTLFN_ARGS)
243{
244	struct file *fp;
245	struct socket *so;
246	struct kinfo_pcb pcb;
247	char *dp;
248	u_int op, arg;
249	size_t len, needed, elem_size, out_size;
250	int error, elem_count, pf, type, pf2;
251
252	if (namelen == 1 && name[0] == CTL_QUERY)
253		return (sysctl_query(SYSCTLFN_CALL(rnode)));
254
255	if (namelen != 4)
256		return (EINVAL);
257
258	if (oldp != NULL) {
259		len = *oldlenp;
260		elem_size = name[2];
261		elem_count = name[3];
262		if (elem_size != sizeof(pcb))
263			return EINVAL;
264	} else {
265		len = 0;
266		elem_size = sizeof(pcb);
267		elem_count = INT_MAX;
268	}
269	error = 0;
270	dp = oldp;
271	op = name[0];
272	arg = name[1];
273	out_size = elem_size;
274	needed = 0;
275
276	if (name - oname != 4)
277		return (EINVAL);
278
279	pf = oname[1];
280	type = oname[2];
281	pf2 = (oldp == NULL) ? 0 : pf;
282
283	/*
284	 * there's no "list" of local domain sockets, so we have
285	 * to walk the file list looking for them.  :-/
286	 */
287	LIST_FOREACH(fp, &filehead, f_list) {
288		if (kauth_authorize_generic(l->l_cred,
289		    KAUTH_GENERIC_CANSEE, fp->f_cred) != 0)
290			continue;
291		if (fp->f_type != DTYPE_SOCKET)
292			continue;
293		so = (struct socket *)fp->f_data;
294		if (so->so_type != type)
295			continue;
296		if (so->so_proto->pr_domain->dom_family != pf)
297			continue;
298		if (len >= elem_size && elem_count > 0) {
299			sysctl_dounpcb(&pcb, so);
300			error = copyout(&pcb, dp, out_size);
301			if (error)
302				break;
303			dp += elem_size;
304			len -= elem_size;
305		}
306		if (elem_count > 0) {
307			needed += elem_size;
308			if (elem_count != INT_MAX)
309				elem_count--;
310		}
311	}
312
313	*oldlenp = needed;
314	if (oldp == NULL)
315		*oldlenp += PCB_SLOP * sizeof(struct kinfo_pcb);
316
317	return (error);
318}
319
320SYSCTL_SETUP(sysctl_net_setup, "sysctl net subtree setup")
321{
322	sysctl_createv(clog, 0, NULL, NULL,
323		       CTLFLAG_PERMANENT,
324		       CTLTYPE_NODE, "net", NULL,
325		       NULL, 0, NULL, 0,
326		       CTL_NET, CTL_EOL);
327	sysctl_createv(clog, 0, NULL, NULL,
328		       CTLFLAG_PERMANENT,
329		       CTLTYPE_NODE, "local",
330		       SYSCTL_DESCR("PF_LOCAL related settings"),
331		       NULL, 0, NULL, 0,
332		       CTL_NET, PF_LOCAL, CTL_EOL);
333	sysctl_createv(clog, 0, NULL, NULL,
334		       CTLFLAG_PERMANENT,
335		       CTLTYPE_NODE, "stream",
336		       SYSCTL_DESCR("SOCK_STREAM settings"),
337		       NULL, 0, NULL, 0,
338		       CTL_NET, PF_LOCAL, SOCK_STREAM, CTL_EOL);
339	sysctl_createv(clog, 0, NULL, NULL,
340		       CTLFLAG_PERMANENT,
341		       CTLTYPE_NODE, "dgram",
342		       SYSCTL_DESCR("SOCK_DGRAM settings"),
343		       NULL, 0, NULL, 0,
344		       CTL_NET, PF_LOCAL, SOCK_DGRAM, CTL_EOL);
345
346	sysctl_createv(clog, 0, NULL, NULL,
347		       CTLFLAG_PERMANENT,
348		       CTLTYPE_STRUCT, "pcblist",
349		       SYSCTL_DESCR("SOCK_STREAM protocol control block list"),
350		       sysctl_unpcblist, 0, NULL, 0,
351		       CTL_NET, PF_LOCAL, SOCK_STREAM, CTL_CREATE, CTL_EOL);
352	sysctl_createv(clog, 0, NULL, NULL,
353		       CTLFLAG_PERMANENT,
354		       CTLTYPE_STRUCT, "pcblist",
355		       SYSCTL_DESCR("SOCK_DGRAM protocol control block list"),
356		       sysctl_unpcblist, 0, NULL, 0,
357		       CTL_NET, PF_LOCAL, SOCK_DGRAM, CTL_CREATE, CTL_EOL);
358}
359
360void
361pfctlinput(int cmd, struct sockaddr *sa)
362{
363	struct domain *dp;
364	const struct protosw *pr;
365
366	DOMAIN_FOREACH(dp)
367		for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
368			if (pr->pr_ctlinput)
369				(*pr->pr_ctlinput)(cmd, sa, NULL);
370}
371
372void
373pfctlinput2(int cmd, struct sockaddr *sa, void *ctlparam)
374{
375	struct domain *dp;
376	const struct protosw *pr;
377
378	if (!sa)
379		return;
380
381	DOMAIN_FOREACH(dp) {
382		/*
383		 * the check must be made by xx_ctlinput() anyways, to
384		 * make sure we use data item pointed to by ctlparam in
385		 * correct way.  the following check is made just for safety.
386		 */
387		if (dp->dom_family != sa->sa_family)
388			continue;
389
390		for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
391			if (pr->pr_ctlinput)
392				(*pr->pr_ctlinput)(cmd, sa, ctlparam);
393	}
394}
395
396void
397pfslowtimo(void *arg)
398{
399	struct domain *dp;
400	const struct protosw *pr;
401
402	pfslowtimo_now++;
403
404	DOMAIN_FOREACH(dp) {
405		for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
406			if (pr->pr_slowtimo)
407				(*pr->pr_slowtimo)();
408	}
409	callout_reset(&pfslowtimo_ch, hz / 2, pfslowtimo, NULL);
410}
411
412void
413pffasttimo(void *arg)
414{
415	struct domain *dp;
416	const struct protosw *pr;
417
418	pffasttimo_now++;
419
420	DOMAIN_FOREACH(dp) {
421		for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
422			if (pr->pr_fasttimo)
423				(*pr->pr_fasttimo)();
424	}
425	callout_reset(&pffasttimo_ch, hz / 5, pffasttimo, NULL);
426}
427