1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
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
32#include <sys/param.h>
33#include <sys/socket.h>
34#include <sys/protosw.h>
35#include <sys/domain.h>
36#include <sys/eventhandler.h>
37#include <sys/epoch.h>
38#include <sys/mbuf.h>
39#include <sys/kernel.h>
40#include <sys/lock.h>
41#include <sys/mutex.h>
42#include <sys/rmlock.h>
43#include <sys/socketvar.h>
44#include <sys/systm.h>
45
46#include <machine/atomic.h>
47
48#include <net/vnet.h>
49
50struct domainhead domains = SLIST_HEAD_INITIALIZER(&domains);
51int domain_init_status = 1;
52static struct mtx dom_mtx;		/* domain list lock */
53MTX_SYSINIT(domain, &dom_mtx, "domain list", MTX_DEF);
54
55static int
56pr_accept_notsupp(struct socket *so, struct sockaddr *sa)
57{
58	return (EOPNOTSUPP);
59}
60
61static int
62pr_aio_queue_notsupp(struct socket *so, struct kaiocb *job)
63{
64	return (EOPNOTSUPP);
65}
66
67static int
68pr_bind_notsupp(struct socket *so, struct sockaddr *nam, struct thread *td)
69{
70	return (EOPNOTSUPP);
71}
72
73static int
74pr_bindat_notsupp(int fd, struct socket *so, struct sockaddr *nam,
75    struct thread *td)
76{
77	return (EOPNOTSUPP);
78}
79
80static int
81pr_connect_notsupp(struct socket *so, struct sockaddr *nam, struct thread *td)
82{
83	return (EOPNOTSUPP);
84}
85
86static int
87pr_connectat_notsupp(int fd, struct socket *so, struct sockaddr *nam,
88    struct thread *td)
89{
90	return (EOPNOTSUPP);
91}
92
93static int
94pr_connect2_notsupp(struct socket *so1, struct socket *so2)
95{
96	return (EOPNOTSUPP);
97}
98
99static int
100pr_control_notsupp(struct socket *so, u_long cmd, void *data,
101    struct ifnet *ifp, struct thread *td)
102{
103	return (EOPNOTSUPP);
104}
105
106static int
107pr_disconnect_notsupp(struct socket *so)
108{
109	return (EOPNOTSUPP);
110}
111
112static int
113pr_listen_notsupp(struct socket *so, int backlog, struct thread *td)
114{
115	return (EOPNOTSUPP);
116}
117
118static int
119pr_peeraddr_notsupp(struct socket *so, struct sockaddr *nam)
120{
121	return (EOPNOTSUPP);
122}
123
124static int
125pr_rcvd_notsupp(struct socket *so, int flags)
126{
127	return (EOPNOTSUPP);
128}
129
130static int
131pr_rcvoob_notsupp(struct socket *so, struct mbuf *m, int flags)
132{
133	return (EOPNOTSUPP);
134}
135
136static int
137pr_send_notsupp(struct socket *so, int flags, struct mbuf *m,
138    struct sockaddr *addr, struct mbuf *control, struct thread *td)
139{
140	if (control != NULL)
141		m_freem(control);
142	if ((flags & PRUS_NOTREADY) == 0)
143		m_freem(m);
144	return (EOPNOTSUPP);
145}
146
147static int
148pr_ready_notsupp(struct socket *so, struct mbuf *m, int count)
149{
150	return (EOPNOTSUPP);
151}
152
153static int
154pr_shutdown_notsupp(struct socket *so, enum shutdown_how how)
155{
156	return (EOPNOTSUPP);
157}
158
159static int
160pr_sockaddr_notsupp(struct socket *so, struct sockaddr *nam)
161{
162	return (EOPNOTSUPP);
163}
164
165static int
166pr_sosend_notsupp(struct socket *so, struct sockaddr *addr, struct uio *uio,
167    struct mbuf *top, struct mbuf *control, int flags, struct thread *td)
168{
169	return (EOPNOTSUPP);
170}
171
172static int
173pr_soreceive_notsupp(struct socket *so, struct sockaddr **paddr,
174    struct uio *uio, struct mbuf **mp0, struct mbuf **controlp, int *flagsp)
175{
176	return (EOPNOTSUPP);
177}
178
179static int
180pr_sopoll_notsupp(struct socket *so, int events, struct ucred *cred,
181    struct thread *td)
182{
183	return (EOPNOTSUPP);
184}
185
186static void
187pr_init(struct domain *dom, struct protosw *pr)
188{
189
190	KASSERT(pr->pr_attach != NULL,
191	    ("%s: protocol doesn't have pr_attach", __func__));
192
193	pr->pr_domain = dom;
194
195#define	DEFAULT(foo, bar)	if (pr->foo == NULL) pr->foo = bar
196	DEFAULT(pr_sosend, sosend_generic);
197	DEFAULT(pr_soreceive, soreceive_generic);
198	DEFAULT(pr_sopoll, sopoll_generic);
199	DEFAULT(pr_setsbopt, sbsetopt);
200
201#define NOTSUPP(foo)	if (pr->foo == NULL)  pr->foo = foo ## _notsupp
202	NOTSUPP(pr_accept);
203	NOTSUPP(pr_aio_queue);
204	NOTSUPP(pr_bind);
205	NOTSUPP(pr_bindat);
206	NOTSUPP(pr_connect);
207	NOTSUPP(pr_connect2);
208	NOTSUPP(pr_connectat);
209	NOTSUPP(pr_control);
210	NOTSUPP(pr_disconnect);
211	NOTSUPP(pr_listen);
212	NOTSUPP(pr_peeraddr);
213	NOTSUPP(pr_rcvd);
214	NOTSUPP(pr_rcvoob);
215	NOTSUPP(pr_send);
216	NOTSUPP(pr_shutdown);
217	NOTSUPP(pr_sockaddr);
218	NOTSUPP(pr_sosend);
219	NOTSUPP(pr_soreceive);
220	NOTSUPP(pr_sopoll);
221	NOTSUPP(pr_ready);
222}
223
224/*
225 * Add a new protocol domain to the list of supported domains
226 * Note: you can't unload it again because a socket may be using it.
227 * XXX can't fail at this time.
228 */
229void
230domain_add(struct domain *dp)
231{
232	struct protosw *pr;
233
234	MPASS(IS_DEFAULT_VNET(curvnet));
235
236	if (dp->dom_probe != NULL && (*dp->dom_probe)() != 0)
237		return;
238
239	for (int i = 0; i < dp->dom_nprotosw; i++)
240		if ((pr = dp->dom_protosw[i]) != NULL)
241			pr_init(dp, pr);
242
243	mtx_lock(&dom_mtx);
244#ifdef INVARIANTS
245	struct domain *tmp;
246	SLIST_FOREACH(tmp, &domains, dom_next)
247		MPASS(tmp->dom_family != dp->dom_family);
248#endif
249	SLIST_INSERT_HEAD(&domains, dp, dom_next);
250	mtx_unlock(&dom_mtx);
251}
252
253void
254domain_remove(struct domain *dp)
255{
256
257	if ((dp->dom_flags & DOMF_UNLOADABLE) == 0)
258		return;
259
260	mtx_lock(&dom_mtx);
261	SLIST_REMOVE(&domains, dp, domain, dom_next);
262	mtx_unlock(&dom_mtx);
263}
264
265static void
266domainfinalize(void *dummy)
267{
268
269	mtx_lock(&dom_mtx);
270	KASSERT(domain_init_status == 1, ("domainfinalize called too late!"));
271	domain_init_status = 2;
272	mtx_unlock(&dom_mtx);
273}
274SYSINIT(domainfin, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_FIRST, domainfinalize,
275    NULL);
276
277struct domain *
278pffinddomain(int family)
279{
280	struct domain *dp;
281
282	SLIST_FOREACH(dp, &domains, dom_next)
283		if (dp->dom_family == family)
284			return (dp);
285	return (NULL);
286}
287
288struct protosw *
289pffindproto(int family, int type, int proto)
290{
291	struct domain *dp;
292	struct protosw *pr;
293
294	dp = pffinddomain(family);
295	if (dp == NULL)
296		return (NULL);
297
298	for (int i = 0; i < dp->dom_nprotosw; i++)
299		if ((pr = dp->dom_protosw[i]) != NULL && pr->pr_type == type &&
300		    (pr->pr_protocol == 0 || proto == 0 ||
301		     pr->pr_protocol == proto))
302			return (pr);
303
304	return (NULL);
305}
306
307/*
308 * The caller must make sure that the new protocol is fully set up and ready to
309 * accept requests before it is registered.
310 */
311int
312protosw_register(struct domain *dp, struct protosw *npr)
313{
314	struct protosw **prp;
315
316	MPASS(dp);
317	MPASS(npr && npr->pr_type > 0 && npr->pr_protocol > 0);
318
319	prp = NULL;
320	/*
321	 * Protect us against races when two protocol registrations for
322	 * the same protocol happen at the same time.
323	 */
324	mtx_lock(&dom_mtx);
325	for (int i = 0; i < dp->dom_nprotosw; i++) {
326		if (dp->dom_protosw[i] == NULL) {
327			/* Remember the first free spacer. */
328			if (prp == NULL)
329				prp = &dp->dom_protosw[i];
330		} else {
331			/*
332			 * The new protocol must not yet exist.
333			 * XXXAO: Check only protocol?
334			 * XXXGL: Maybe assert that it doesn't exist?
335			 */
336			if ((dp->dom_protosw[i]->pr_type == npr->pr_type) &&
337			    (dp->dom_protosw[i]->pr_protocol ==
338			    npr->pr_protocol)) {
339				mtx_unlock(&dom_mtx);
340				return (EEXIST);
341			}
342
343		}
344	}
345
346	/* If no free spacer is found we can't add the new protocol. */
347	if (prp == NULL) {
348		mtx_unlock(&dom_mtx);
349		return (ENOMEM);
350	}
351
352	pr_init(dp, npr);
353	*prp = npr;
354	mtx_unlock(&dom_mtx);
355
356	return (0);
357}
358
359/*
360 * The caller must make sure the protocol and its functions correctly shut down
361 * all sockets and release all locks and memory references.
362 */
363int
364protosw_unregister(struct protosw *pr)
365{
366	struct domain *dp;
367	struct protosw **prp;
368
369	dp = pr->pr_domain;
370	prp = NULL;
371
372	mtx_lock(&dom_mtx);
373	/* The protocol must exist and only once. */
374	for (int i = 0; i < dp->dom_nprotosw; i++) {
375		if (dp->dom_protosw[i] == pr) {
376			KASSERT(prp == NULL,
377			    ("%s: domain %p protocol %p registered twice\n",
378			    __func__, dp, pr));
379			prp = &dp->dom_protosw[i];
380		}
381	}
382
383	/* Protocol does not exist.  XXXGL: assert that it does? */
384	if (prp == NULL) {
385		mtx_unlock(&dom_mtx);
386		return (EPROTONOSUPPORT);
387	}
388
389	/* De-orbit the protocol and make the slot available again. */
390	*prp = NULL;
391	mtx_unlock(&dom_mtx);
392
393	return (0);
394}
395