]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netlink/netlink_domain.c
netpfil tests: Serialize
[FreeBSD/FreeBSD.git] / sys / netlink / netlink_domain.c
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/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/lock.h>
37 #include <sys/rmlock.h>
38 #include <sys/domain.h>
39 #include <sys/mbuf.h>
40 #include <sys/protosw.h>
41 #include <sys/proc.h>
42 #include <sys/ck.h>
43 #include <sys/socket.h>
44 #include <sys/socketvar.h>
45 #include <sys/sysent.h>
46 #include <sys/syslog.h>
47 #include <sys/priv.h> /* priv_check */
48
49 #include <netlink/netlink.h>
50 #include <netlink/netlink_ctl.h>
51 #include <netlink/netlink_var.h>
52
53 #define DEBUG_MOD_NAME  nl_domain
54 #define DEBUG_MAX_LEVEL LOG_DEBUG3
55 #include <netlink/netlink_debug.h>
56 _DECLARE_DEBUG(LOG_DEBUG);
57
58 _Static_assert((NLP_MAX_GROUPS % 64) == 0,
59     "NLP_MAX_GROUPS has to be multiple of 64");
60 _Static_assert(NLP_MAX_GROUPS >= 64,
61     "NLP_MAX_GROUPS has to be at least 64");
62
63 #define NLCTL_TRACKER           struct rm_priotracker nl_tracker
64 #define NLCTL_RLOCK(_ctl)       rm_rlock(&((_ctl)->ctl_lock), &nl_tracker)
65 #define NLCTL_RUNLOCK(_ctl)     rm_runlock(&((_ctl)->ctl_lock), &nl_tracker)
66
67 #define NLCTL_WLOCK(_ctl)       rm_wlock(&((_ctl)->ctl_lock))
68 #define NLCTL_WUNLOCK(_ctl)     rm_wunlock(&((_ctl)->ctl_lock))
69
70 static u_long nl_sendspace = NLSNDQ;
71 SYSCTL_ULONG(_net_netlink, OID_AUTO, sendspace, CTLFLAG_RW, &nl_sendspace, 0,
72     "Default netlink socket send space");
73
74 static u_long nl_recvspace = NLSNDQ;
75 SYSCTL_ULONG(_net_netlink, OID_AUTO, recvspace, CTLFLAG_RW, &nl_recvspace, 0,
76     "Default netlink socket receive space");
77
78 extern u_long sb_max_adj;
79 extern u_long nl_maxsockbuf;
80 static int sysctl_handle_nl_maxsockbuf(SYSCTL_HANDLER_ARGS);
81 SYSCTL_OID(_net_netlink, OID_AUTO, nl_maxsockbuf,
82     CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, &nl_maxsockbuf, 0,
83     sysctl_handle_nl_maxsockbuf, "LU",
84     "Maximum Netlink socket buffer size");
85
86 uint32_t
87 nlp_get_pid(const struct nlpcb *nlp)
88 {
89         return (nlp->nl_process_id);
90 }
91
92 /*
93  * Looks up a nlpcb struct based on the @portid. Need to claim nlsock_mtx.
94  * Returns nlpcb pointer if present else NULL
95  */
96 static struct nlpcb *
97 nl_port_lookup(uint32_t port_id)
98 {
99         struct nlpcb *nlp;
100
101         CK_LIST_FOREACH(nlp, &V_nl_ctl->ctl_port_head, nl_port_next) {
102                 if (nlp->nl_port == port_id)
103                         return (nlp);
104         }
105         return (NULL);
106 }
107
108 static void
109 nl_add_group_locked(struct nlpcb *nlp, unsigned int group_id)
110 {
111         MPASS(group_id <= NLP_MAX_GROUPS);
112         --group_id;
113
114         nlp->nl_groups[group_id / 64] |= (uint64_t)1 << (group_id % 64);
115 }
116
117 static void
118 nl_del_group_locked(struct nlpcb *nlp, unsigned int group_id)
119 {
120         MPASS(group_id <= NLP_MAX_GROUPS);
121         --group_id;
122
123         nlp->nl_groups[group_id / 64] &= ~((uint64_t)1 << (group_id % 64));
124 }
125
126 static bool
127 nl_isset_group_locked(struct nlpcb *nlp, unsigned int group_id)
128 {
129         MPASS(group_id <= NLP_MAX_GROUPS);
130         --group_id;
131
132         return (nlp->nl_groups[group_id / 64] & ((uint64_t)1 << (group_id % 64)));
133 }
134
135 static uint32_t
136 nl_get_groups_compat(struct nlpcb *nlp)
137 {
138         uint32_t groups_mask = 0;
139
140         for (int i = 0; i < 32; i++) {
141                 if (nl_isset_group_locked(nlp, i + 1))
142                         groups_mask |= (1 << i);
143         }
144
145         return (groups_mask);
146 }
147
148 /*
149  * Broadcasts message @m to the protocol @proto group specified by @group_id
150  */
151 void
152 nl_send_group(struct mbuf *m, int num_messages, int proto, int group_id)
153 {
154         struct nlpcb *nlp_last = NULL;
155         struct nlpcb *nlp;
156         NLCTL_TRACKER;
157
158         IF_DEBUG_LEVEL(LOG_DEBUG2) {
159                 struct nlmsghdr *hdr = mtod(m, struct nlmsghdr *);
160                 NL_LOG(LOG_DEBUG2, "MCAST mbuf len %u msg type %d len %u to group %d/%d",
161                     m->m_len, hdr->nlmsg_type, hdr->nlmsg_len, proto, group_id);
162         }
163
164         struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
165         if (__predict_false(ctl == NULL)) {
166                 /*
167                  * Can be the case when notification is sent within VNET
168                  * which doesn't have any netlink sockets.
169                  */
170                 m_freem(m);
171                 return;
172         }
173
174         NLCTL_RLOCK(ctl);
175
176         int io_flags = NL_IOF_UNTRANSLATED;
177
178         CK_LIST_FOREACH(nlp, &ctl->ctl_pcb_head, nl_next) {
179                 if (nl_isset_group_locked(nlp, group_id) && nlp->nl_proto == proto) {
180                         if (nlp_last != NULL) {
181                                 struct mbuf *m_copy;
182                                 m_copy = m_copym(m, 0, M_COPYALL, M_NOWAIT);
183                                 if (m_copy != NULL)
184                                         nl_send_one(m_copy, nlp_last, num_messages, io_flags);
185                                 else {
186                                         NLP_LOCK(nlp_last);
187                                         if (nlp_last->nl_socket != NULL)
188                                                 sorwakeup(nlp_last->nl_socket);
189                                         NLP_UNLOCK(nlp_last);
190                                 }
191                         }
192                         nlp_last = nlp;
193                 }
194         }
195         if (nlp_last != NULL)
196                 nl_send_one(m, nlp_last, num_messages, io_flags);
197         else
198                 m_freem(m);
199
200         NLCTL_RUNLOCK(ctl);
201 }
202
203 bool
204 nl_has_listeners(int netlink_family, uint32_t groups_mask)
205 {
206         return (V_nl_ctl != NULL);
207 }
208
209 bool
210 nlp_has_priv(struct nlpcb *nlp, int priv)
211 {
212         return (priv_check_cred(nlp->nl_cred, priv) == 0);
213 }
214
215 static uint32_t
216 nl_find_port(void)
217 {
218         /*
219          * app can open multiple netlink sockets.
220          * Start with current pid, if already taken,
221          * try random numbers in 65k..256k+65k space,
222          * avoiding clash with pids.
223          */
224         if (nl_port_lookup(curproc->p_pid) == NULL)
225                 return (curproc->p_pid);
226         for (int i = 0; i < 16; i++) {
227                 uint32_t nl_port = (arc4random() % 65536) + 65536 * 4;
228                 if (nl_port_lookup(nl_port) == 0)
229                         return (nl_port);
230                 NL_LOG(LOG_DEBUG3, "tried %u\n", nl_port);
231         }
232         return (curproc->p_pid);
233 }
234
235 static int
236 nl_bind_locked(struct nlpcb *nlp, struct sockaddr_nl *snl)
237 {
238         if (nlp->nl_bound) {
239                 if (nlp->nl_port != snl->nl_pid) {
240                         NL_LOG(LOG_DEBUG,
241                             "bind() failed: program pid %d "
242                             "is different from provided pid %d",
243                             nlp->nl_port, snl->nl_pid);
244                         return (EINVAL); // XXX: better error
245                 }
246         } else {
247                 if (snl->nl_pid == 0)
248                         snl->nl_pid = nl_find_port();
249                 if (nl_port_lookup(snl->nl_pid) != NULL)
250                         return (EADDRINUSE);
251                 nlp->nl_port = snl->nl_pid;
252                 nlp->nl_bound = true;
253                 CK_LIST_INSERT_HEAD(&V_nl_ctl->ctl_port_head, nlp, nl_port_next);
254         }
255         for (int i = 0; i < 32; i++) {
256                 if (snl->nl_groups & ((uint32_t)1 << i))
257                         nl_add_group_locked(nlp, i + 1);
258                 else
259                         nl_del_group_locked(nlp, i + 1);
260         }
261
262         return (0);
263 }
264
265 static int
266 nl_pru_attach(struct socket *so, int proto, struct thread *td)
267 {
268         struct nlpcb *nlp;
269         int error;
270
271         if (__predict_false(netlink_unloading != 0))
272                 return (EAFNOSUPPORT);
273
274         error = nl_verify_proto(proto);
275         if (error != 0)
276                 return (error);
277
278         bool is_linux = SV_PROC_ABI(td->td_proc) == SV_ABI_LINUX;
279         NL_LOG(LOG_DEBUG2, "socket %p, %sPID %d: attaching socket to %s",
280             so, is_linux ? "(linux) " : "", curproc->p_pid,
281             nl_get_proto_name(proto));
282
283         /* Create per-VNET state on first socket init */
284         struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
285         if (ctl == NULL)
286                 ctl = vnet_nl_ctl_init();
287         KASSERT(V_nl_ctl != NULL, ("nl_attach: vnet_sock_init() failed"));
288
289         MPASS(sotonlpcb(so) == NULL);
290
291         nlp = malloc(sizeof(struct nlpcb), M_PCB, M_WAITOK | M_ZERO);
292         error = soreserve(so, nl_sendspace, nl_recvspace);
293         if (error != 0) {
294                 free(nlp, M_PCB);
295                 return (error);
296         }
297         so->so_pcb = nlp;
298         nlp->nl_socket = so;
299         /* Copy so_cred to avoid having socket_var.h in every header */
300         nlp->nl_cred = so->so_cred;
301         nlp->nl_proto = proto;
302         nlp->nl_process_id = curproc->p_pid;
303         nlp->nl_linux = is_linux;
304         nlp->nl_active = true;
305         NLP_LOCK_INIT(nlp);
306         refcount_init(&nlp->nl_refcount, 1);
307         nl_init_io(nlp);
308
309         nlp->nl_taskqueue = taskqueue_create("netlink_socket", M_WAITOK,
310             taskqueue_thread_enqueue, &nlp->nl_taskqueue);
311         TASK_INIT(&nlp->nl_task, 0, nl_taskqueue_handler, nlp);
312         taskqueue_start_threads(&nlp->nl_taskqueue, 1, PWAIT,
313             "netlink_socket (PID %u)", nlp->nl_process_id);
314
315         NLCTL_WLOCK(ctl);
316         /* XXX: check ctl is still alive */
317         CK_LIST_INSERT_HEAD(&ctl->ctl_pcb_head, nlp, nl_next);
318         NLCTL_WUNLOCK(ctl);
319
320         soisconnected(so);
321
322         return (0);
323 }
324
325 static void
326 nl_pru_abort(struct socket *so)
327 {
328         NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
329         MPASS(sotonlpcb(so) != NULL);
330         soisdisconnected(so);
331 }
332
333 static int
334 nl_pru_bind(struct socket *so, struct sockaddr *sa, struct thread *td)
335 {
336         struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
337         struct nlpcb *nlp = sotonlpcb(so);
338         struct sockaddr_nl *snl = (struct sockaddr_nl *)sa;
339         int error;
340
341         NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
342         if (snl->nl_len != sizeof(*snl)) {
343                 NL_LOG(LOG_DEBUG, "socket %p, wrong sizeof(), ignoring bind()", so);
344                 return (EINVAL);
345         }
346
347
348         NLCTL_WLOCK(ctl);
349         NLP_LOCK(nlp);
350         error = nl_bind_locked(nlp, snl);
351         NLP_UNLOCK(nlp);
352         NLCTL_WUNLOCK(ctl);
353         NL_LOG(LOG_DEBUG2, "socket %p, bind() to %u, groups %u, error %d", so,
354             snl->nl_pid, snl->nl_groups, error);
355
356         return (error);
357 }
358
359
360 static int
361 nl_assign_port(struct nlpcb *nlp, uint32_t port_id)
362 {
363         struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
364         struct sockaddr_nl snl = {
365                 .nl_pid = port_id,
366         };
367         int error;
368
369         NLCTL_WLOCK(ctl);
370         NLP_LOCK(nlp);
371         snl.nl_groups = nl_get_groups_compat(nlp);
372         error = nl_bind_locked(nlp, &snl);
373         NLP_UNLOCK(nlp);
374         NLCTL_WUNLOCK(ctl);
375
376         NL_LOG(LOG_DEBUG3, "socket %p, port assign: %d, error: %d", nlp->nl_socket, port_id, error);
377         return (error);
378 }
379
380 /*
381  * nl_autobind_port binds a unused portid to @nlp
382  * @nlp: pcb data for the netlink socket
383  * @candidate_id: first id to consider
384  */
385 static int
386 nl_autobind_port(struct nlpcb *nlp, uint32_t candidate_id)
387 {
388         struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
389         uint32_t port_id = candidate_id;
390         NLCTL_TRACKER;
391         bool exist;
392         int error;
393
394         for (int i = 0; i < 10; i++) {
395                 NL_LOG(LOG_DEBUG3, "socket %p, trying to assign port %d", nlp->nl_socket, port_id);
396                 NLCTL_RLOCK(ctl);
397                 exist = nl_port_lookup(port_id) != 0;
398                 NLCTL_RUNLOCK(ctl);
399                 if (!exist) {
400                         error = nl_assign_port(nlp, port_id);
401                         if (error != EADDRINUSE)
402                                 break;
403                 }
404                 port_id++;
405         }
406         NL_LOG(LOG_DEBUG3, "socket %p, autobind to %d, error: %d", nlp->nl_socket, port_id, error);
407         return (error);
408 }
409
410 static int
411 nl_pru_connect(struct socket *so, struct sockaddr *sa, struct thread *td)
412 {
413         struct sockaddr_nl *snl = (struct sockaddr_nl *)sa;
414         struct nlpcb *nlp;
415
416         NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
417         if (snl->nl_len != sizeof(*snl)) {
418                 NL_LOG(LOG_DEBUG, "socket %p, wrong sizeof(), ignoring bind()", so);
419                 return (EINVAL);
420         }
421
422         nlp = sotonlpcb(so);
423         if (!nlp->nl_bound) {
424                 int error = nl_autobind_port(nlp, td->td_proc->p_pid);
425                 if (error != 0) {
426                         NL_LOG(LOG_DEBUG, "socket %p, nl_autobind() failed: %d", so, error);
427                         return (error);
428                 }
429         }
430         /* XXX: Handle socket flags & multicast */
431         soisconnected(so);
432
433         NL_LOG(LOG_DEBUG2, "socket %p, connect to %u", so, snl->nl_pid);
434
435         return (0);
436 }
437
438 static void
439 destroy_nlpcb(struct nlpcb *nlp)
440 {
441         NLP_LOCK(nlp);
442         nl_free_io(nlp);
443         NLP_LOCK_DESTROY(nlp);
444         free(nlp, M_PCB);
445 }
446
447 static void
448 destroy_nlpcb_epoch(epoch_context_t ctx)
449 {
450         struct nlpcb *nlp;
451
452         nlp = __containerof(ctx, struct nlpcb, nl_epoch_ctx);
453
454         destroy_nlpcb(nlp);
455 }
456
457
458 static void
459 nl_pru_detach(struct socket *so)
460 {
461         struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
462         MPASS(sotonlpcb(so) != NULL);
463         struct nlpcb *nlp;
464
465         NL_LOG(LOG_DEBUG2, "detaching socket %p, PID %d", so, curproc->p_pid);
466         nlp = sotonlpcb(so);
467
468         /* Mark as inactive so no new work can be enqueued */
469         NLP_LOCK(nlp);
470         bool was_bound = nlp->nl_bound;
471         nlp->nl_active = false;
472         NLP_UNLOCK(nlp);
473
474         /* Wait till all scheduled work has been completed  */
475         taskqueue_drain_all(nlp->nl_taskqueue);
476         taskqueue_free(nlp->nl_taskqueue);
477
478         NLCTL_WLOCK(ctl);
479         NLP_LOCK(nlp);
480         if (was_bound) {
481                 CK_LIST_REMOVE(nlp, nl_port_next);
482                 NL_LOG(LOG_DEBUG3, "socket %p, unlinking bound pid %u", so, nlp->nl_port);
483         }
484         CK_LIST_REMOVE(nlp, nl_next);
485         nlp->nl_socket = NULL;
486         NLP_UNLOCK(nlp);
487         NLCTL_WUNLOCK(ctl);
488
489         so->so_pcb = NULL;
490
491         NL_LOG(LOG_DEBUG3, "socket %p, detached", so);
492
493         /* XXX: is delayed free needed? */
494         NET_EPOCH_CALL(destroy_nlpcb_epoch, &nlp->nl_epoch_ctx);
495 }
496
497 static int
498 nl_pru_disconnect(struct socket *so)
499 {
500         NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
501         MPASS(sotonlpcb(so) != NULL);
502         return (ENOTCONN);
503 }
504
505 static int
506 nl_pru_peeraddr(struct socket *so, struct sockaddr **sa)
507 {
508         NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
509         MPASS(sotonlpcb(so) != NULL);
510         return (ENOTCONN);
511 }
512
513 static int
514 nl_pru_shutdown(struct socket *so)
515 {
516         NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
517         MPASS(sotonlpcb(so) != NULL);
518         socantsendmore(so);
519         return (0);
520 }
521
522 static int
523 nl_pru_sockaddr(struct socket *so, struct sockaddr **sa)
524 {
525         struct sockaddr_nl *snl;
526
527         snl = malloc(sizeof(struct sockaddr_nl), M_SONAME, M_WAITOK | M_ZERO);
528         /* TODO: set other fields */
529         snl->nl_len = sizeof(struct sockaddr_nl);
530         snl->nl_family = AF_NETLINK;
531         snl->nl_pid = sotonlpcb(so)->nl_port;
532         *sa = (struct sockaddr *)snl;
533         return (0);
534 }
535
536 static void
537 nl_pru_close(struct socket *so)
538 {
539         NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
540         MPASS(sotonlpcb(so) != NULL);
541         soisdisconnected(so);
542 }
543
544 static int
545 nl_pru_output(struct mbuf *m, struct socket *so, ...)
546 {
547
548         if (__predict_false(m == NULL ||
549             ((m->m_len < sizeof(struct nlmsghdr)) &&
550                 (m = m_pullup(m, sizeof(struct nlmsghdr))) == NULL)))
551                 return (ENOBUFS);
552         MPASS((m->m_flags & M_PKTHDR) != 0);
553
554         NL_LOG(LOG_DEBUG3, "sending message to kernel async processing");
555         nl_receive_async(m, so);
556         return (0);
557 }
558
559
560 static int
561 nl_pru_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *sa,
562     struct mbuf *control, struct thread *td)
563 {
564         NL_LOG(LOG_DEBUG2, "sending message to kernel");
565
566         if (__predict_false(control != NULL)) {
567                 if (control->m_len) {
568                         m_freem(control);
569                         return (EINVAL);
570                 }
571                 m_freem(control);
572         }
573
574         return (nl_pru_output(m, so));
575 }
576
577 static int
578 nl_pru_rcvd(struct socket *so, int flags)
579 {
580         NL_LOG(LOG_DEBUG3, "socket %p, PID %d", so, curproc->p_pid);
581         MPASS(sotonlpcb(so) != NULL);
582
583         nl_on_transmit(sotonlpcb(so));
584
585         return (0);
586 }
587
588 static int
589 nl_getoptflag(int sopt_name)
590 {
591         switch (sopt_name) {
592         case NETLINK_CAP_ACK:
593                 return (NLF_CAP_ACK);
594         case NETLINK_EXT_ACK:
595                 return (NLF_EXT_ACK);
596         case NETLINK_GET_STRICT_CHK:
597                 return (NLF_STRICT);
598         }
599
600         return (0);
601 }
602
603 static int
604 nl_ctloutput(struct socket *so, struct sockopt *sopt)
605 {
606         struct nl_control *ctl = atomic_load_ptr(&V_nl_ctl);
607         struct nlpcb *nlp = sotonlpcb(so);
608         uint32_t flag;
609         int optval, error = 0;
610         NLCTL_TRACKER;
611
612         NL_LOG(LOG_DEBUG2, "%ssockopt(%p, %d)", (sopt->sopt_dir) ? "set" : "get",
613             so, sopt->sopt_name);
614
615         switch (sopt->sopt_dir) {
616         case SOPT_SET:
617                 switch (sopt->sopt_name) {
618                 case NETLINK_ADD_MEMBERSHIP:
619                 case NETLINK_DROP_MEMBERSHIP:
620                         sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval));
621                         if (optval <= 0 || optval >= NLP_MAX_GROUPS) {
622                                 error = ERANGE;
623                                 break;
624                         }
625                         NL_LOG(LOG_DEBUG2, "ADD/DEL group %d", (uint32_t)optval);
626
627                         NLCTL_WLOCK(ctl);
628                         if (sopt->sopt_name == NETLINK_ADD_MEMBERSHIP)
629                                 nl_add_group_locked(nlp, optval);
630                         else
631                                 nl_del_group_locked(nlp, optval);
632                         NLCTL_WUNLOCK(ctl);
633                         break;
634                 case NETLINK_CAP_ACK:
635                 case NETLINK_EXT_ACK:
636                 case NETLINK_GET_STRICT_CHK:
637                         sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval));
638
639                         flag = nl_getoptflag(sopt->sopt_name);
640
641                         NLCTL_WLOCK(ctl);
642                         if (optval != 0)
643                                 nlp->nl_flags |= flag;
644                         else
645                                 nlp->nl_flags &= ~flag;
646                         NLCTL_WUNLOCK(ctl);
647                         break;
648                 default:
649                         error = ENOPROTOOPT;
650                 }
651                 break;
652         case SOPT_GET:
653                 switch (sopt->sopt_name) {
654                 case NETLINK_LIST_MEMBERSHIPS:
655                         NLCTL_RLOCK(ctl);
656                         optval = nl_get_groups_compat(nlp);
657                         NLCTL_RUNLOCK(ctl);
658                         error = sooptcopyout(sopt, &optval, sizeof(optval));
659                         break;
660                 case NETLINK_CAP_ACK:
661                 case NETLINK_EXT_ACK:
662                 case NETLINK_GET_STRICT_CHK:
663                         NLCTL_RLOCK(ctl);
664                         optval = (nlp->nl_flags & nl_getoptflag(sopt->sopt_name)) != 0;
665                         NLCTL_RUNLOCK(ctl);
666                         error = sooptcopyout(sopt, &optval, sizeof(optval));
667                         break;
668                 default:
669                         error = ENOPROTOOPT;
670                 }
671                 break;
672         default:
673                 error = ENOPROTOOPT;
674         }
675
676         return (error);
677 }
678
679 static int
680 sysctl_handle_nl_maxsockbuf(SYSCTL_HANDLER_ARGS)
681 {
682         int error = 0;
683         u_long tmp_maxsockbuf = nl_maxsockbuf;
684
685         error = sysctl_handle_long(oidp, &tmp_maxsockbuf, arg2, req);
686         if (error || !req->newptr)
687                 return (error);
688         if (tmp_maxsockbuf < MSIZE + MCLBYTES)
689                 return (EINVAL);
690         nl_maxsockbuf = tmp_maxsockbuf;
691
692         return (0);
693 }
694
695 #if 0
696 static int
697 nl_setsbopt(struct socket *so, struct sockopt *sopt)
698 {
699         int error, optval;
700         bool result;
701
702         if (sopt->sopt_name != SO_RCVBUF)
703                 return (sbsetopt(so, sopt));
704
705         /* Allow to override max buffer size in certain conditions */
706
707         error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval);
708         if (error != 0)
709                 return (error);
710         NL_LOG(LOG_DEBUG2, "socket %p, PID %d, SO_RCVBUF=%d", so, curproc->p_pid, optval);
711         if (optval > sb_max_adj) {
712                 if (priv_check(curthread, PRIV_NET_ROUTE) != 0)
713                         return (EPERM);
714         }
715
716         SOCK_RECVBUF_LOCK(so);
717         result = sbreserve_locked_limit(so, SO_RCV, optval, nl_maxsockbuf, curthread);
718         SOCK_RECVBUF_UNLOCK(so);
719
720         return (result ? 0 : ENOBUFS);
721 }
722 #endif
723
724 struct pr_usrreqs nl_usrreqs = {
725         .pru_abort =            nl_pru_abort,
726         .pru_attach =           nl_pru_attach,
727         .pru_bind =             nl_pru_bind,
728         .pru_connect =          nl_pru_connect,
729         .pru_detach =           nl_pru_detach,
730         .pru_disconnect =       nl_pru_disconnect,
731         .pru_peeraddr =         nl_pru_peeraddr,
732         .pru_rcvd =             nl_pru_rcvd,
733         .pru_send =             nl_pru_send,
734         //.pru_soreceive =      soreceive_dgram,
735         //.pru_sosend =         sosend_dgram,
736         .pru_shutdown =         nl_pru_shutdown,
737         .pru_sockaddr =         nl_pru_sockaddr,
738         //.pru_sosetlabel =     in_pcbsosetlabel,
739         .pru_close =            nl_pru_close,
740 };
741
742 static struct domain netlinkdomain;
743
744 static struct protosw netlinksw[] = {
745 {
746         .pr_type = SOCK_RAW,
747         .pr_domain = &netlinkdomain,
748         .pr_protocol = 0, // IPPROTO_UDP
749         .pr_flags = PR_ATOMIC | PR_ADDR | PR_WANTRCVD,
750         .pr_ctloutput = nl_ctloutput,
751         .pr_usrreqs = &nl_usrreqs,
752 },
753 {
754         .pr_type = SOCK_DGRAM,
755         .pr_domain = &netlinkdomain,
756         .pr_protocol = 0, // IPPROTO_UDP
757         .pr_flags = PR_ATOMIC | PR_ADDR | PR_WANTRCVD,
758         .pr_ctloutput = nl_ctloutput,
759         .pr_usrreqs = &nl_usrreqs,
760 }
761 };
762
763 static struct domain netlinkdomain = {
764         .dom_family = AF_NETLINK,
765         .dom_name = "netlink",
766 #ifdef  DOMF_UNLOADABLE
767         .dom_flags = DOMF_UNLOADABLE,
768 #endif
769         .dom_protosw =          &netlinksw[0],
770         .dom_protoswNPROTOSW =  (&netlinksw[0] + 2),
771 };
772
773 DOMAIN_SET(netlink);