xref: /freebsd/sys/netlink/netlink_domain.c (revision 0aa2700123e22c2b0a977375e087dc2759b8e980)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2021 Ng Peng Nam Sean
5  * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
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  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  * This file contains socket and protocol bindings for netlink.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/malloc.h>
35 #include <sys/lock.h>
36 #include <sys/rmlock.h>
37 #include <sys/domain.h>
38 #include <sys/mbuf.h>
39 #include <sys/protosw.h>
40 #include <sys/proc.h>
41 #include <sys/ck.h>
42 #include <sys/socket.h>
43 #include <sys/socketvar.h>
44 #include <sys/sysent.h>
45 #include <sys/syslog.h>
46 #include <sys/priv.h> /* priv_check */
47 
48 #include <netlink/netlink.h>
49 #include <netlink/netlink_ctl.h>
50 #include <netlink/netlink_var.h>
51 
52 #define	DEBUG_MOD_NAME	nl_domain
53 #define	DEBUG_MAX_LEVEL	LOG_DEBUG3
54 #include <netlink/netlink_debug.h>
55 _DECLARE_DEBUG(LOG_DEBUG);
56 
57 
58 #define	NLCTL_TRACKER		struct rm_priotracker nl_tracker
59 #define	NLCTL_RLOCK(_ctl)	rm_rlock(&((_ctl)->ctl_lock), &nl_tracker)
60 #define	NLCTL_RUNLOCK(_ctl)	rm_runlock(&((_ctl)->ctl_lock), &nl_tracker)
61 
62 #define	NLCTL_WLOCK(_ctl)	rm_wlock(&((_ctl)->ctl_lock))
63 #define	NLCTL_WUNLOCK(_ctl)	rm_wunlock(&((_ctl)->ctl_lock))
64 
65 static u_long nl_sendspace = NLSNDQ;
66 SYSCTL_ULONG(_net_netlink, OID_AUTO, sendspace, CTLFLAG_RW, &nl_sendspace, 0,
67     "Default netlink socket send space");
68 
69 static u_long nl_recvspace = NLSNDQ;
70 SYSCTL_ULONG(_net_netlink, OID_AUTO, recvspace, CTLFLAG_RW, &nl_recvspace, 0,
71     "Default netlink socket receive space");
72 
73 extern u_long sb_max_adj;
74 static u_long nl_maxsockbuf = 512 * 1024 * 1024; /* 512M, XXX: init based on physmem */
75 
76 uint32_t
77 nlp_get_pid(const struct nlpcb *nlp)
78 {
79 	return (nlp->nl_process_id);
80 }
81 
82 /*
83  * Looks up a nlpcb struct based on the @portid. Need to claim nlsock_mtx.
84  * Returns nlpcb pointer if present else NULL
85  */
86 static struct nlpcb *
87 nl_port_lookup(uint32_t port_id)
88 {
89 	struct nlpcb *nlp;
90 
91 	CK_LIST_FOREACH(nlp, &V_nl_ctl->ctl_port_head, nl_port_next) {
92 		if (nlp->nl_port == port_id)
93 			return (nlp);
94 	}
95 	return (NULL);
96 }
97 
98 static void
99 nl_update_groups_locked(struct nlpcb *nlp, uint64_t nl_groups)
100 {
101 	/* Update group mask */
102 	NL_LOG(LOG_DEBUG2, "socket %p, groups 0x%X -> 0x%X",
103 	    nlp->nl_socket, (uint32_t)nlp->nl_groups, (uint32_t)nl_groups);
104 	nlp->nl_groups = nl_groups;
105 }
106 
107 /*
108  * Broadcasts message @m to the protocol @proto group specified by @group_id
109  */
110 void
111 nl_send_group(struct mbuf *m, int num_messages, int proto, int group_id)
112 {
113 	struct nlpcb *nlp_last = NULL;
114 	struct nlpcb *nlp;
115 	NLCTL_TRACKER;
116 
117 	IF_DEBUG_LEVEL(LOG_DEBUG2) {
118 		struct nlmsghdr *hdr = mtod(m, struct nlmsghdr *);
119 		NL_LOG(LOG_DEBUG2, "MCAST mbuf len %u msg type %d len %u to group %d/%d",
120 		    m->m_len, hdr->nlmsg_type, hdr->nlmsg_len, proto, group_id);
121 	}
122 
123 	struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
124 	if (__predict_false(ctl == NULL)) {
125 		/*
126 		 * Can be the case when notification is sent within VNET
127 		 * which doesn't have any netlink sockets.
128 		 */
129 		m_freem(m);
130 		return;
131 	}
132 
133 	NLCTL_RLOCK(ctl);
134 
135 	int io_flags = NL_IOF_UNTRANSLATED;
136 	uint64_t groups_mask = 1 << ((uint64_t)group_id - 1);
137 
138 	CK_LIST_FOREACH(nlp, &ctl->ctl_pcb_head, nl_next) {
139 		if (nlp->nl_groups & groups_mask && nlp->nl_proto == proto) {
140 			if (nlp_last != NULL) {
141 				struct mbuf *m_copy;
142 				m_copy = m_copym(m, 0, M_COPYALL, M_NOWAIT);
143 				if (m_copy != NULL)
144 					nl_send_one(m_copy, nlp_last, num_messages, io_flags);
145 				else {
146 					NLP_LOCK(nlp_last);
147 					if (nlp_last->nl_socket != NULL)
148 						sorwakeup(nlp_last->nl_socket);
149 					NLP_UNLOCK(nlp_last);
150 				}
151 			}
152 			nlp_last = nlp;
153 		}
154 	}
155 	if (nlp_last != NULL)
156 		nl_send_one(m, nlp_last, num_messages, io_flags);
157 	else
158 		m_freem(m);
159 
160 	NLCTL_RUNLOCK(ctl);
161 }
162 
163 bool
164 nl_has_listeners(int netlink_family, uint32_t groups_mask)
165 {
166 	return (V_nl_ctl != NULL);
167 }
168 
169 bool
170 nlp_has_priv(struct nlpcb *nlp, int priv)
171 {
172 	return (priv_check_cred(nlp->nl_cred, priv) == 0);
173 }
174 
175 static uint32_t
176 nl_find_port() {
177 	/*
178 	 * app can open multiple netlink sockets.
179 	 * Start with current pid, if already taken,
180 	 * try random numbers in 65k..256k+65k space,
181 	 * avoiding clash with pids.
182 	 */
183 	if (nl_port_lookup(curproc->p_pid) == NULL)
184 		return (curproc->p_pid);
185 	for (int i = 0; i < 16; i++) {
186 		uint32_t nl_port = (arc4random() % 65536) + 65536 * 4;
187 		if (nl_port_lookup(nl_port) == 0)
188 			return (nl_port);
189 		NL_LOG(LOG_DEBUG3, "tried %u\n", nl_port);
190 	}
191 	return (curproc->p_pid);
192 }
193 
194 static int
195 nl_bind_locked(struct nlpcb *nlp, struct sockaddr_nl *snl)
196 {
197 	if (nlp->nl_bound) {
198 		if (nlp->nl_port != snl->nl_pid) {
199 			NL_LOG(LOG_DEBUG,
200 			    "bind() failed: program pid %d "
201 			    "is different from provided pid %d",
202 			    nlp->nl_port, snl->nl_pid);
203 			return (EINVAL); // XXX: better error
204 		}
205 	} else {
206 		if (snl->nl_pid == 0)
207 			snl->nl_pid = nl_find_port();
208 		if (nl_port_lookup(snl->nl_pid) != NULL)
209 			return (EADDRINUSE);
210 		nlp->nl_port = snl->nl_pid;
211 		nlp->nl_bound = true;
212 		CK_LIST_INSERT_HEAD(&V_nl_ctl->ctl_port_head, nlp, nl_port_next);
213 	}
214 	nl_update_groups_locked(nlp, snl->nl_groups);
215 
216 	return (0);
217 }
218 
219 static int
220 nl_pru_attach(struct socket *so, int proto, struct thread *td)
221 {
222 	struct nlpcb *nlp;
223 	int error;
224 
225 	if (__predict_false(netlink_unloading != 0))
226 		return (EAFNOSUPPORT);
227 
228 	error = nl_verify_proto(proto);
229 	if (error != 0)
230 		return (error);
231 
232 	bool is_linux = SV_PROC_ABI(td->td_proc) == SV_ABI_LINUX;
233 	NL_LOG(LOG_DEBUG2, "socket %p, %sPID %d: attaching socket to %s",
234 	    so, is_linux ? "(linux) " : "", curproc->p_pid,
235 	    nl_get_proto_name(proto));
236 
237 	/* Create per-VNET state on first socket init */
238 	struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
239 	if (ctl == NULL)
240 		ctl = vnet_nl_ctl_init();
241 	KASSERT(V_nl_ctl != NULL, ("nl_attach: vnet_sock_init() failed"));
242 
243 	MPASS(sotonlpcb(so) == NULL);
244 
245 	nlp = malloc(sizeof(struct nlpcb), M_PCB, M_WAITOK | M_ZERO);
246 	error = soreserve(so, nl_sendspace, nl_recvspace);
247 	if (error != 0) {
248 		free(nlp, M_PCB);
249 		return (error);
250 	}
251 	so->so_pcb = nlp;
252 	nlp->nl_socket = so;
253 	/* Copy so_cred to avoid having socket_var.h in every header */
254 	nlp->nl_cred = so->so_cred;
255 	nlp->nl_proto = proto;
256 	nlp->nl_process_id = curproc->p_pid;
257 	nlp->nl_linux = is_linux;
258 	nlp->nl_active = true;
259 	NLP_LOCK_INIT(nlp);
260 	refcount_init(&nlp->nl_refcount, 1);
261 	nl_init_io(nlp);
262 
263 	nlp->nl_taskqueue = taskqueue_create("netlink_socket", M_WAITOK,
264 	    taskqueue_thread_enqueue, &nlp->nl_taskqueue);
265 	TASK_INIT(&nlp->nl_task, 0, nl_taskqueue_handler, nlp);
266 	taskqueue_start_threads(&nlp->nl_taskqueue, 1, PWAIT,
267 	    "netlink_socket (PID %u)", nlp->nl_process_id);
268 
269 	NLCTL_WLOCK(ctl);
270 	/* XXX: check ctl is still alive */
271 	CK_LIST_INSERT_HEAD(&ctl->ctl_pcb_head, nlp, nl_next);
272 	NLCTL_WUNLOCK(ctl);
273 
274 	soisconnected(so);
275 
276 	return (0);
277 }
278 
279 static void
280 nl_pru_abort(struct socket *so)
281 {
282 	NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
283 	MPASS(sotonlpcb(so) != NULL);
284 	soisdisconnected(so);
285 }
286 
287 static int
288 nl_pru_bind(struct socket *so, struct sockaddr *sa, struct thread *td)
289 {
290 	struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
291 	struct nlpcb *nlp = sotonlpcb(so);
292 	struct sockaddr_nl *snl = (struct sockaddr_nl *)sa;
293 	int error;
294 
295 	NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
296 	if (snl->nl_len != sizeof(*snl)) {
297 		NL_LOG(LOG_DEBUG, "socket %p, wrong sizeof(), ignoring bind()", so);
298 		return (EINVAL);
299 	}
300 
301 
302 	NLCTL_WLOCK(ctl);
303 	NLP_LOCK(nlp);
304 	error = nl_bind_locked(nlp, snl);
305 	NLP_UNLOCK(nlp);
306 	NLCTL_WUNLOCK(ctl);
307 	NL_LOG(LOG_DEBUG2, "socket %p, bind() to %u, groups %u, error %d", so,
308 	    snl->nl_pid, snl->nl_groups, error);
309 
310 	return (error);
311 }
312 
313 
314 static int
315 nl_assign_port(struct nlpcb *nlp, uint32_t port_id)
316 {
317 	struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
318 	struct sockaddr_nl snl = {
319 		.nl_pid = port_id,
320 	};
321 	int error;
322 
323 	NLCTL_WLOCK(ctl);
324 	NLP_LOCK(nlp);
325 	snl.nl_groups = nlp->nl_groups;
326 	error = nl_bind_locked(nlp, &snl);
327 	NLP_UNLOCK(nlp);
328 	NLCTL_WUNLOCK(ctl);
329 
330 	NL_LOG(LOG_DEBUG3, "socket %p, port assign: %d, error: %d", nlp->nl_socket, port_id, error);
331 	return (error);
332 }
333 
334 /*
335  * nl_autobind_port binds a unused portid to @nlp
336  * @nlp: pcb data for the netlink socket
337  * @candidate_id: first id to consider
338  */
339 static int
340 nl_autobind_port(struct nlpcb *nlp, uint32_t candidate_id)
341 {
342 	struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
343 	uint32_t port_id = candidate_id;
344 	NLCTL_TRACKER;
345 	bool exist;
346 	int error;
347 
348 	for (int i = 0; i < 10; i++) {
349 		NL_LOG(LOG_DEBUG3, "socket %p, trying to assign port %d", nlp->nl_socket, port_id);
350 		NLCTL_RLOCK(ctl);
351 		exist = nl_port_lookup(port_id) != 0;
352 		NLCTL_RUNLOCK(ctl);
353 		if (!exist) {
354 			error = nl_assign_port(nlp, port_id);
355 			if (error != EADDRINUSE)
356 				break;
357 		}
358 		port_id++;
359 	}
360 	NL_LOG(LOG_DEBUG3, "socket %p, autobind to %d, error: %d", nlp->nl_socket, port_id, error);
361 	return (error);
362 }
363 
364 static int
365 nl_pru_connect(struct socket *so, struct sockaddr *sa, struct thread *td)
366 {
367 	struct sockaddr_nl *snl = (struct sockaddr_nl *)sa;
368 	struct nlpcb *nlp;
369 
370 	NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
371 	if (snl->nl_len != sizeof(*snl)) {
372 		NL_LOG(LOG_DEBUG, "socket %p, wrong sizeof(), ignoring bind()", so);
373 		return (EINVAL);
374 	}
375 
376 	nlp = sotonlpcb(so);
377 	if (!nlp->nl_bound) {
378 		int error = nl_autobind_port(nlp, td->td_proc->p_pid);
379 		if (error != 0) {
380 			NL_LOG(LOG_DEBUG, "socket %p, nl_autobind() failed: %d", so, error);
381 			return (error);
382 		}
383 	}
384 	/* XXX: Handle socket flags & multicast */
385 	soisconnected(so);
386 
387 	NL_LOG(LOG_DEBUG2, "socket %p, connect to %u", so, snl->nl_pid);
388 
389 	return (0);
390 }
391 
392 static void
393 destroy_nlpcb(struct nlpcb *nlp)
394 {
395 	NLP_LOCK(nlp);
396 	nl_free_io(nlp);
397 	NLP_LOCK_DESTROY(nlp);
398 	free(nlp, M_PCB);
399 }
400 
401 static void
402 destroy_nlpcb_epoch(epoch_context_t ctx)
403 {
404 	struct nlpcb *nlp;
405 
406 	nlp = __containerof(ctx, struct nlpcb, nl_epoch_ctx);
407 
408 	destroy_nlpcb(nlp);
409 }
410 
411 
412 static void
413 nl_pru_detach(struct socket *so)
414 {
415 	struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
416 	MPASS(sotonlpcb(so) != NULL);
417 	struct nlpcb *nlp;
418 
419 	NL_LOG(LOG_DEBUG2, "detaching socket %p, PID %d", so, curproc->p_pid);
420 	nlp = sotonlpcb(so);
421 
422 	/* Mark as inactive so no new work can be enqueued */
423 	NLP_LOCK(nlp);
424 	bool was_bound = nlp->nl_bound;
425 	nlp->nl_active = false;
426 	NLP_UNLOCK(nlp);
427 
428 	/* Wait till all scheduled work has been completed  */
429 	taskqueue_drain_all(nlp->nl_taskqueue);
430 	taskqueue_free(nlp->nl_taskqueue);
431 
432 	NLCTL_WLOCK(ctl);
433 	NLP_LOCK(nlp);
434 	if (was_bound) {
435 		CK_LIST_REMOVE(nlp, nl_port_next);
436 		NL_LOG(LOG_DEBUG3, "socket %p, unlinking bound pid %u", so, nlp->nl_port);
437 	}
438 	CK_LIST_REMOVE(nlp, nl_next);
439 	nlp->nl_socket = NULL;
440 	NLP_UNLOCK(nlp);
441 	NLCTL_WUNLOCK(ctl);
442 
443 	so->so_pcb = NULL;
444 
445 	NL_LOG(LOG_DEBUG3, "socket %p, detached", so);
446 
447 	/* XXX: is delayed free needed? */
448 	epoch_call(net_epoch_preempt, destroy_nlpcb_epoch, &nlp->nl_epoch_ctx);
449 }
450 
451 static int
452 nl_pru_disconnect(struct socket *so)
453 {
454 	NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
455 	MPASS(sotonlpcb(so) != NULL);
456 	return (ENOTCONN);
457 }
458 
459 static int
460 nl_pru_peeraddr(struct socket *so, struct sockaddr **sa)
461 {
462 	NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
463 	MPASS(sotonlpcb(so) != NULL);
464 	return (ENOTCONN);
465 }
466 
467 static int
468 nl_pru_shutdown(struct socket *so)
469 {
470 	NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
471 	MPASS(sotonlpcb(so) != NULL);
472 	socantsendmore(so);
473 	return (0);
474 }
475 
476 static int
477 nl_pru_sockaddr(struct socket *so, struct sockaddr **sa)
478 {
479 	struct sockaddr_nl *snl;
480 
481 	snl = malloc(sizeof(struct sockaddr_nl), M_SONAME, M_WAITOK | M_ZERO);
482 	/* TODO: set other fields */
483 	snl->nl_len = sizeof(struct sockaddr_nl);
484 	snl->nl_family = AF_NETLINK;
485 	snl->nl_pid = sotonlpcb(so)->nl_port;
486 	*sa = (struct sockaddr *)snl;
487 	return (0);
488 }
489 
490 static void
491 nl_pru_close(struct socket *so)
492 {
493 	NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
494 	MPASS(sotonlpcb(so) != NULL);
495 	soisdisconnected(so);
496 }
497 
498 static int
499 nl_pru_output(struct mbuf *m, struct socket *so, ...)
500 {
501 
502 	if (__predict_false(m == NULL ||
503 	    ((m->m_len < sizeof(struct nlmsghdr)) &&
504 		(m = m_pullup(m, sizeof(struct nlmsghdr))) == NULL)))
505 		return (ENOBUFS);
506 	MPASS((m->m_flags & M_PKTHDR) != 0);
507 
508 	NL_LOG(LOG_DEBUG3, "sending message to kernel async processing");
509 	nl_receive_async(m, so);
510 	return (0);
511 }
512 
513 
514 static int
515 nl_pru_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *sa,
516     struct mbuf *control, struct thread *td)
517 {
518         NL_LOG(LOG_DEBUG2, "sending message to kernel");
519 
520 	if (__predict_false(control != NULL)) {
521 		if (control->m_len) {
522 			m_freem(control);
523 			return (EINVAL);
524 		}
525 		m_freem(control);
526 	}
527 
528 	return (nl_pru_output(m, so));
529 }
530 
531 static int
532 nl_pru_rcvd(struct socket *so, int flags)
533 {
534 	NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
535 	MPASS(sotonlpcb(so) != NULL);
536 
537 	nl_on_transmit(sotonlpcb(so));
538 
539 	return (0);
540 }
541 
542 static int
543 nl_getoptflag(int sopt_name)
544 {
545 	switch (sopt_name) {
546 	case NETLINK_CAP_ACK:
547 		return (NLF_CAP_ACK);
548 	case NETLINK_EXT_ACK:
549 		return (NLF_EXT_ACK);
550 	case NETLINK_GET_STRICT_CHK:
551 		return (NLF_STRICT);
552 	}
553 
554 	return (0);
555 }
556 
557 static int
558 nl_ctloutput(struct socket *so, struct sockopt *sopt)
559 {
560 	struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
561 	struct nlpcb *nlp = sotonlpcb(so);
562 	uint32_t flag;
563 	uint64_t groups, group_mask;
564 	int optval, error = 0;
565 	NLCTL_TRACKER;
566 
567 	NL_LOG(LOG_DEBUG2, "%ssockopt(%p, %d)", (sopt->sopt_dir) ? "set" : "get",
568 	    so, sopt->sopt_name);
569 
570 	switch (sopt->sopt_dir) {
571 	case SOPT_SET:
572 		switch (sopt->sopt_name) {
573 		case NETLINK_ADD_MEMBERSHIP:
574 		case NETLINK_DROP_MEMBERSHIP:
575 			sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval));
576 			if (optval <= 0 || optval >= 64) {
577 				error = ERANGE;
578 				break;
579 			}
580 			group_mask = (uint64_t)1 << (optval - 1);
581 			NL_LOG(LOG_DEBUG2, "ADD/DEL group %d mask (%X)",
582 			    (uint32_t)optval, (uint32_t)group_mask);
583 
584 			NLCTL_WLOCK(ctl);
585 			if (sopt->sopt_name == NETLINK_ADD_MEMBERSHIP)
586 				groups = nlp->nl_groups | group_mask;
587 			else
588 				groups = nlp->nl_groups & ~group_mask;
589 			nl_update_groups_locked(nlp, groups);
590 			NLCTL_WUNLOCK(ctl);
591 			break;
592 		case NETLINK_CAP_ACK:
593 		case NETLINK_EXT_ACK:
594 		case NETLINK_GET_STRICT_CHK:
595 			sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval));
596 
597 			flag = nl_getoptflag(sopt->sopt_name);
598 
599 			NLCTL_WLOCK(ctl);
600 			if (optval != 0)
601 				nlp->nl_flags |= flag;
602 			else
603 				nlp->nl_flags &= ~flag;
604 			NLCTL_WUNLOCK(ctl);
605 			break;
606 		default:
607 			error = ENOPROTOOPT;
608 		}
609 		break;
610 	case SOPT_GET:
611 		switch (sopt->sopt_name) {
612 		case NETLINK_LIST_MEMBERSHIPS:
613 			NLCTL_RLOCK(ctl);
614 			optval = nlp->nl_groups;
615 			NLCTL_RUNLOCK(ctl);
616 			error = sooptcopyout(sopt, &optval, sizeof(optval));
617 			break;
618 		case NETLINK_CAP_ACK:
619 		case NETLINK_EXT_ACK:
620 		case NETLINK_GET_STRICT_CHK:
621 			NLCTL_RLOCK(ctl);
622 			optval = (nlp->nl_flags & nl_getoptflag(sopt->sopt_name)) != 0;
623 			NLCTL_RUNLOCK(ctl);
624 			error = sooptcopyout(sopt, &optval, sizeof(optval));
625 			break;
626 		default:
627 			error = ENOPROTOOPT;
628 		}
629 		break;
630 	default:
631 		error = ENOPROTOOPT;
632 	}
633 
634 	return (error);
635 }
636 
637 static int
638 nl_setsbopt(struct socket *so, struct sockopt *sopt)
639 {
640 	int error, optval;
641 	bool result;
642 
643 	if (sopt->sopt_name != SO_RCVBUF)
644 		return (sbsetopt(so, sopt));
645 
646 	/* Allow to override max buffer size in certain conditions */
647 
648 	error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval);
649 	if (error != 0)
650 		return (error);
651 	NL_LOG(LOG_DEBUG2, "socket %p, PID %d, SO_RCVBUF=%d", so, curproc->p_pid, optval);
652 	if (optval > sb_max_adj) {
653 		if (priv_check(curthread, PRIV_NET_ROUTE) != 0)
654 			return (EPERM);
655 	}
656 
657 	SOCK_RECVBUF_LOCK(so);
658 	result = sbreserve_locked_limit(so, SO_RCV, optval, nl_maxsockbuf, curthread);
659 	SOCK_RECVBUF_UNLOCK(so);
660 
661 	return (result ? 0 : ENOBUFS);
662 }
663 
664 static struct protosw netlinksw = {
665 	.pr_type = SOCK_RAW,
666 	.pr_flags = PR_ATOMIC | PR_ADDR | PR_WANTRCVD,
667 	.pr_ctloutput = nl_ctloutput,
668 	.pr_setsbopt = nl_setsbopt,
669 	.pr_abort = nl_pru_abort,
670 	.pr_attach = nl_pru_attach,
671 	.pr_bind = nl_pru_bind,
672 	.pr_connect = nl_pru_connect,
673 	.pr_detach = nl_pru_detach,
674 	.pr_disconnect = nl_pru_disconnect,
675 	.pr_peeraddr = nl_pru_peeraddr,
676 	.pr_send = nl_pru_send,
677 	.pr_rcvd = nl_pru_rcvd,
678 	.pr_shutdown = nl_pru_shutdown,
679 	.pr_sockaddr = nl_pru_sockaddr,
680 	.pr_close = nl_pru_close
681 };
682 
683 static struct domain netlinkdomain = {
684 	.dom_family = PF_NETLINK,
685 	.dom_name = "netlink",
686 	.dom_flags = DOMF_UNLOADABLE,
687 	.dom_nprotosw =		1,
688 	.dom_protosw =		{ &netlinksw },
689 };
690 
691 DOMAIN_SET(netlink);
692