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/netinet6/ip6_divert.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 /*      $OpenBSD: ip6_divert.c,v 1.88 2022/10/17 14:49:02 mvs Exp $ */
    2 
    3 /*
    4  * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
    5  *
    6  * Permission to use, copy, modify, and distribute this software for any
    7  * purpose with or without fee is hereby granted, provided that the above
    8  * copyright notice and this permission notice appear in all copies.
    9  *
   10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17  */
   18 
   19 #include <sys/param.h>
   20 #include <sys/systm.h>
   21 #include <sys/mbuf.h>
   22 #include <sys/protosw.h>
   23 #include <sys/socket.h>
   24 #include <sys/socketvar.h>
   25 #include <sys/sysctl.h>
   26 
   27 #include <net/if.h>
   28 #include <net/route.h>
   29 #include <net/if_var.h>
   30 #include <net/netisr.h>
   31 
   32 #include <netinet/in.h>
   33 #include <netinet/ip.h>
   34 #include <netinet/ip_var.h>
   35 #include <netinet/in_pcb.h>
   36 #include <netinet/ip6.h>
   37 #include <netinet6/in6_var.h>
   38 #include <netinet6/ip6_divert.h>
   39 #include <netinet/tcp.h>
   40 #include <netinet/udp.h>
   41 #include <netinet/icmp6.h>
   42 
   43 #include <net/pfvar.h>
   44 
   45 struct  inpcbtable      divb6table;
   46 struct  cpumem          *div6counters;
   47 
   48 #ifndef DIVERT_SENDSPACE
   49 #define DIVERT_SENDSPACE        (65536 + 100)
   50 #endif
   51 u_int   divert6_sendspace = DIVERT_SENDSPACE;
   52 #ifndef DIVERT_RECVSPACE
   53 #define DIVERT_RECVSPACE        (65536 + 100)
   54 #endif
   55 u_int   divert6_recvspace = DIVERT_RECVSPACE;
   56 
   57 #ifndef DIVERTHASHSIZE
   58 #define DIVERTHASHSIZE  128
   59 #endif
   60 
   61 const struct sysctl_bounded_args divert6ctl_vars[] = {
   62         { DIVERT6CTL_RECVSPACE, &divert6_recvspace, 0, INT_MAX },
   63         { DIVERT6CTL_SENDSPACE, &divert6_sendspace, 0, INT_MAX },
   64 };
   65 
   66 const struct pr_usrreqs divert6_usrreqs = {
   67         .pru_attach     = divert6_attach,
   68         .pru_detach     = divert6_detach,
   69         .pru_lock       = divert6_lock,
   70         .pru_unlock     = divert6_unlock,
   71         .pru_bind       = divert6_bind,
   72         .pru_shutdown   = divert6_shutdown,
   73         .pru_send       = divert6_send,
   74         .pru_control    = in6_control,
   75         .pru_sockaddr   = in6_sockaddr,
   76         .pru_peeraddr   = in6_peeraddr,
   77 };
   78 
   79 int divb6hashsize = DIVERTHASHSIZE;
   80 
   81 int     divert6_output(struct inpcb *, struct mbuf *, struct mbuf *,
   82             struct mbuf *);
   83 
   84 void
   85 divert6_init(void)
   86 {
   87         in_pcbinit(&divb6table, divb6hashsize);
   88         div6counters = counters_alloc(div6s_ncounters);
   89 }
   90 
   91 int
   92 divert6_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam,
   93     struct mbuf *control)
   94 {
   95         struct sockaddr_in6 *sin6;
   96         int error, min_hdrlen, nxt, off, dir;
   97         struct ip6_hdr *ip6;
   98 
   99         m_freem(control);
  100 
  101         if ((error = in6_nam2sin6(nam, &sin6)))
  102                 goto fail;
  103 
  104         /* Do basic sanity checks. */
  105         if (m->m_pkthdr.len < sizeof(struct ip6_hdr))
  106                 goto fail;
  107         if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
  108                 /* m_pullup() has freed the mbuf, so just return. */
  109                 div6stat_inc(div6s_errors);
  110                 return (ENOBUFS);
  111         }
  112         ip6 = mtod(m, struct ip6_hdr *);
  113         if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
  114                 goto fail;
  115         if (m->m_pkthdr.len < sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen))
  116                 goto fail;
  117 
  118         /*
  119          * Recalculate the protocol checksum since the userspace application
  120          * may have modified the packet prior to reinjection.
  121          */
  122         off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
  123         if (off < sizeof(struct ip6_hdr))
  124                 goto fail;
  125 
  126         dir = (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ? PF_OUT : PF_IN);
  127 
  128         switch (nxt) {
  129         case IPPROTO_TCP:
  130                 min_hdrlen = sizeof(struct tcphdr);
  131                 m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
  132                 break;
  133         case IPPROTO_UDP:
  134                 min_hdrlen = sizeof(struct udphdr);
  135                 m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
  136                 break;
  137         case IPPROTO_ICMPV6:
  138                 min_hdrlen = sizeof(struct icmp6_hdr);
  139                 m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
  140                 break;
  141         default:
  142                 min_hdrlen = 0;
  143                 break;
  144         }
  145         if (min_hdrlen && m->m_pkthdr.len < off + min_hdrlen)
  146                 goto fail;
  147 
  148         m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED_PACKET;
  149 
  150         if (dir == PF_IN) {
  151                 struct rtentry *rt;
  152                 struct ifnet *ifp;
  153 
  154                 rt = rtalloc(sin6tosa(sin6), 0, inp->inp_rtableid);
  155                 if (!rtisvalid(rt) || !ISSET(rt->rt_flags, RTF_LOCAL)) {
  156                         rtfree(rt);
  157                         error = EADDRNOTAVAIL;
  158                         goto fail;
  159                 }
  160                 m->m_pkthdr.ph_ifidx = rt->rt_ifidx;
  161                 rtfree(rt);
  162 
  163                 /*
  164                  * Recalculate the protocol checksum for the inbound packet
  165                  * since the userspace application may have modified the packet
  166                  * prior to reinjection.
  167                  */
  168                 in6_proto_cksum_out(m, NULL);
  169 
  170                 ifp = if_get(m->m_pkthdr.ph_ifidx);
  171                 if (ifp == NULL) {
  172                         error = ENETDOWN;
  173                         goto fail;
  174                 }
  175                 ipv6_input(ifp, m);
  176                 if_put(ifp);
  177         } else {
  178                 m->m_pkthdr.ph_rtableid = inp->inp_rtableid;
  179 
  180                 error = ip6_output(m, NULL, &inp->inp_route6,
  181                     IP_ALLOWBROADCAST | IP_RAWOUTPUT, NULL, NULL);
  182         }
  183 
  184         div6stat_inc(div6s_opackets);
  185         return (error);
  186 
  187 fail:
  188         div6stat_inc(div6s_errors);
  189         m_freem(m);
  190         return (error ? error : EINVAL);
  191 }
  192 
  193 void
  194 divert6_packet(struct mbuf *m, int dir, u_int16_t divert_port)
  195 {
  196         struct inpcb *inp = NULL;
  197         struct socket *so;
  198         struct sockaddr_in6 sin6;
  199 
  200         div6stat_inc(div6s_ipackets);
  201 
  202         if (m->m_len < sizeof(struct ip6_hdr) &&
  203             (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
  204                 div6stat_inc(div6s_errors);
  205                 goto bad;
  206         }
  207 
  208         mtx_enter(&divb6table.inpt_mtx);
  209         TAILQ_FOREACH(inp, &divb6table.inpt_queue, inp_queue) {
  210                 if (inp->inp_lport != divert_port)
  211                         continue;
  212                 in_pcbref(inp);
  213                 break;
  214         }
  215         mtx_leave(&divb6table.inpt_mtx);
  216         if (inp == NULL) {
  217                 div6stat_inc(div6s_noport);
  218                 goto bad;
  219         }
  220 
  221         memset(&sin6, 0, sizeof(sin6));
  222         sin6.sin6_family = AF_INET6;
  223         sin6.sin6_len = sizeof(sin6);
  224 
  225         if (dir == PF_IN) {
  226                 struct ifaddr *ifa;
  227                 struct ifnet *ifp;
  228 
  229                 ifp = if_get(m->m_pkthdr.ph_ifidx);
  230                 if (ifp == NULL) {
  231                         div6stat_inc(div6s_errors);
  232                         goto bad;
  233                 }
  234                 TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
  235                         if (ifa->ifa_addr->sa_family != AF_INET6)
  236                                 continue;
  237                         sin6.sin6_addr = satosin6(ifa->ifa_addr)->sin6_addr;
  238                         break;
  239                 }
  240                 if_put(ifp);
  241         }
  242 
  243         mtx_enter(&inp->inp_mtx);
  244         so = inp->inp_socket;
  245         if (sbappendaddr(so, &so->so_rcv, sin6tosa(&sin6), m, NULL) == 0) {
  246                 mtx_leave(&inp->inp_mtx);
  247                 div6stat_inc(div6s_fullsock);
  248                 goto bad;
  249         }
  250         mtx_leave(&inp->inp_mtx);
  251         sorwakeup(so);
  252 
  253         in_pcbunref(inp);
  254         return;
  255 
  256  bad:
  257         if (inp != NULL)
  258                 in_pcbunref(inp);
  259         m_freem(m);
  260 }
  261 
  262 int
  263 divert6_attach(struct socket *so, int proto, int wait)
  264 {
  265         int error;
  266 
  267         if (so->so_pcb != NULL)
  268                 return EINVAL;
  269 
  270         if ((so->so_state & SS_PRIV) == 0)
  271                 return EACCES;
  272 
  273         error = in_pcballoc(so, &divb6table, wait);
  274         if (error)
  275                 return (error);
  276 
  277         error = soreserve(so, divert6_sendspace, divert6_recvspace);
  278         if (error)
  279                 return (error);
  280         sotoinpcb(so)->inp_flags |= INP_HDRINCL;
  281         return (0);
  282 }
  283 
  284 int
  285 divert6_detach(struct socket *so)
  286 {
  287         struct inpcb *inp = sotoinpcb(so);
  288 
  289         soassertlocked(so);
  290 
  291         if (inp == NULL)
  292                 return (EINVAL);
  293 
  294         in_pcbdetach(inp);
  295 
  296         return (0);
  297 }
  298 
  299 void
  300 divert6_lock(struct socket *so)
  301 {
  302         struct inpcb *inp = sotoinpcb(so);
  303 
  304         NET_ASSERT_LOCKED();
  305         mtx_enter(&inp->inp_mtx);
  306 }
  307 
  308 void
  309 divert6_unlock(struct socket *so)
  310 {
  311         struct inpcb *inp = sotoinpcb(so);
  312 
  313         NET_ASSERT_LOCKED();
  314         mtx_leave(&inp->inp_mtx);
  315 }
  316 
  317 int
  318 divert6_bind(struct socket *so, struct mbuf *addr, struct proc *p)
  319 {
  320         struct inpcb *inp = sotoinpcb(so);
  321 
  322         soassertlocked(so);
  323         return in_pcbbind(inp, addr, p);
  324 }
  325 
  326 int
  327 divert6_shutdown(struct socket *so)
  328 {
  329         soassertlocked(so);
  330         socantsendmore(so);
  331 
  332         return (0);
  333 }
  334 
  335 int
  336 divert6_send(struct socket *so, struct mbuf *m, struct mbuf *addr,
  337     struct mbuf *control)
  338 {
  339         struct inpcb *inp = sotoinpcb(so);
  340 
  341         soassertlocked(so);
  342         return (divert6_output(inp, m, addr, control));
  343 }
  344 
  345 int
  346 divert6_sysctl_div6stat(void *oldp, size_t *oldlenp, void *newp)
  347 {
  348         uint64_t counters[div6s_ncounters];
  349         struct div6stat div6stat;
  350         u_long *words = (u_long *)&div6stat;
  351         int i;
  352 
  353         CTASSERT(sizeof(div6stat) == (nitems(counters) * sizeof(u_long)));
  354 
  355         counters_read(div6counters, counters, nitems(counters));
  356 
  357         for (i = 0; i < nitems(counters); i++)
  358                 words[i] = (u_long)counters[i];
  359 
  360         return (sysctl_rdstruct(oldp, oldlenp, newp,
  361             &div6stat, sizeof(div6stat)));
  362 }
  363 
  364 /*
  365  * Sysctl for divert variables.
  366  */
  367 int
  368 divert6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
  369     void *newp, size_t newlen)
  370 {
  371         int error;
  372 
  373         /* All sysctl names at this level are terminal. */
  374         if (namelen != 1)
  375                 return (ENOTDIR);
  376 
  377         switch (name[0]) {
  378         case DIVERT6CTL_STATS:
  379                 return (divert6_sysctl_div6stat(oldp, oldlenp, newp));
  380         default:
  381                 NET_LOCK();
  382                 error = sysctl_bounded_arr(divert6ctl_vars,
  383                     nitems(divert6ctl_vars), name, namelen, oldp, oldlenp,
  384                     newp, newlen);
  385                 NET_UNLOCK();
  386                 return (error);
  387         }
  388         /* NOTREACHED */
  389 }

Cache object: 979842fd1685006c450f469a1381aa81


[ 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.