The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netlink/netlink_domain.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 5b379f5a9be8d3ee4ae1285c4b78601e


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.