LCOV - code coverage report
Current view: top level - netinet6 - ip6_divert.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 170 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 8 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: ip6_divert.c,v 1.56 2018/04/24 15:40:55 pirofti 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             : int *divert6ctl_vars[DIVERT6CTL_MAXID] = DIVERT6CTL_VARS;
      62             : 
      63             : int divb6hashsize = DIVERTHASHSIZE;
      64             : 
      65             : int     divert6_output(struct inpcb *, struct mbuf *, struct mbuf *,
      66             :             struct mbuf *);
      67             : 
      68             : void
      69           0 : divert6_init(void)
      70             : {
      71           0 :         in_pcbinit(&divb6table, divb6hashsize);
      72           0 :         div6counters = counters_alloc(div6s_ncounters);
      73           0 : }
      74             : 
      75             : int
      76           0 : divert6_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam,
      77             :     struct mbuf *control)
      78             : {
      79           0 :         struct sockaddr_in6 *sin6;
      80           0 :         int error, min_hdrlen, nxt, off, dir;
      81             :         struct ip6_hdr *ip6;
      82             : 
      83           0 :         m_freem(control);
      84             : 
      85           0 :         if ((error = in6_nam2sin6(nam, &sin6)))
      86             :                 goto fail;
      87             : 
      88             :         /* Do basic sanity checks. */
      89           0 :         if (m->m_pkthdr.len < sizeof(struct ip6_hdr))
      90             :                 goto fail;
      91           0 :         if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
      92             :                 /* m_pullup() has freed the mbuf, so just return. */
      93           0 :                 div6stat_inc(div6s_errors);
      94           0 :                 return (ENOBUFS);
      95             :         }
      96           0 :         ip6 = mtod(m, struct ip6_hdr *);
      97           0 :         if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
      98             :                 goto fail;
      99           0 :         if (m->m_pkthdr.len < sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen))
     100             :                 goto fail;
     101             : 
     102             :         /*
     103             :          * Recalculate the protocol checksum since the userspace application
     104             :          * may have modified the packet prior to reinjection.
     105             :          */
     106           0 :         off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
     107           0 :         if (off < sizeof(struct ip6_hdr))
     108             :                 goto fail;
     109             : 
     110           0 :         dir = (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ? PF_OUT : PF_IN);
     111             : 
     112           0 :         switch (nxt) {
     113             :         case IPPROTO_TCP:
     114             :                 min_hdrlen = sizeof(struct tcphdr);
     115           0 :                 m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
     116           0 :                 break;
     117             :         case IPPROTO_UDP:
     118             :                 min_hdrlen = sizeof(struct udphdr);
     119           0 :                 m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
     120           0 :                 break;
     121             :         case IPPROTO_ICMPV6:
     122             :                 min_hdrlen = sizeof(struct icmp6_hdr);
     123           0 :                 m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
     124           0 :                 break;
     125             :         default:
     126             :                 min_hdrlen = 0;
     127           0 :                 break;
     128             :         }
     129           0 :         if (min_hdrlen && m->m_pkthdr.len < off + min_hdrlen)
     130             :                 goto fail;
     131             : 
     132           0 :         m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED_PACKET;
     133             : 
     134           0 :         if (dir == PF_IN) {
     135             :                 struct rtentry *rt;
     136             :                 struct ifnet *ifp;
     137             : 
     138           0 :                 rt = rtalloc(sin6tosa(sin6), 0, inp->inp_rtableid);
     139           0 :                 if (!rtisvalid(rt) || !ISSET(rt->rt_flags, RTF_LOCAL)) {
     140           0 :                         rtfree(rt);
     141             :                         error = EADDRNOTAVAIL;
     142           0 :                         goto fail;
     143             :                 }
     144           0 :                 m->m_pkthdr.ph_ifidx = rt->rt_ifidx;
     145           0 :                 rtfree(rt);
     146             : 
     147             :                 /*
     148             :                  * Recalculate the protocol checksum for the inbound packet
     149             :                  * since the userspace application may have modified the packet
     150             :                  * prior to reinjection.
     151             :                  */
     152           0 :                 in6_proto_cksum_out(m, NULL);
     153             : 
     154           0 :                 ifp = if_get(m->m_pkthdr.ph_ifidx);
     155           0 :                 if (ifp == NULL) {
     156             :                         error = ENETDOWN;
     157           0 :                         goto fail;
     158             :                 }
     159           0 :                 ipv6_input(ifp, m);
     160           0 :                 if_put(ifp);
     161           0 :         } else {
     162           0 :                 m->m_pkthdr.ph_rtableid = inp->inp_rtableid;
     163             : 
     164           0 :                 error = ip6_output(m, NULL, &inp->inp_route6,
     165             :                     IP_ALLOWBROADCAST | IP_RAWOUTPUT, NULL, NULL);
     166             :         }
     167             : 
     168           0 :         div6stat_inc(div6s_opackets);
     169           0 :         return (error);
     170             : 
     171             : fail:
     172           0 :         div6stat_inc(div6s_errors);
     173           0 :         m_freem(m);
     174           0 :         return (error ? error : EINVAL);
     175           0 : }
     176             : 
     177             : int
     178           0 : divert6_packet(struct mbuf *m, int dir, u_int16_t divert_port)
     179             : {
     180             :         struct inpcb *inp;
     181             :         struct socket *sa = NULL;
     182           0 :         struct sockaddr_in6 addr;
     183             : 
     184             :         inp = NULL;
     185           0 :         div6stat_inc(div6s_ipackets);
     186             : 
     187           0 :         if (m->m_len < sizeof(struct ip6_hdr) &&
     188           0 :             (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
     189           0 :                 div6stat_inc(div6s_errors);
     190           0 :                 return (0);
     191             :         }
     192             : 
     193           0 :         TAILQ_FOREACH(inp, &divb6table.inpt_queue, inp_queue) {
     194           0 :                 if (inp->inp_lport == divert_port)
     195             :                         break;
     196             :         }
     197             : 
     198           0 :         memset(&addr, 0, sizeof(addr));
     199           0 :         addr.sin6_family = AF_INET6;
     200           0 :         addr.sin6_len = sizeof(addr);
     201             : 
     202           0 :         if (dir == PF_IN) {
     203             :                 struct ifaddr *ifa;
     204             :                 struct ifnet *ifp;
     205             : 
     206           0 :                 ifp = if_get(m->m_pkthdr.ph_ifidx);
     207           0 :                 if (ifp == NULL) {
     208           0 :                         m_freem(m);
     209           0 :                         return (0);
     210             :                 }
     211           0 :                 TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
     212           0 :                         if (ifa->ifa_addr->sa_family != AF_INET6)
     213             :                                 continue;
     214           0 :                         addr.sin6_addr = satosin6(ifa->ifa_addr)->sin6_addr;
     215           0 :                         break;
     216             :                 }
     217           0 :                 if_put(ifp);
     218           0 :         }
     219             : 
     220           0 :         if (inp) {
     221           0 :                 sa = inp->inp_socket;
     222           0 :                 if (sbappendaddr(sa, &sa->so_rcv, sin6tosa(&addr), m, NULL) == 0) {
     223           0 :                         div6stat_inc(div6s_fullsock);
     224           0 :                         m_freem(m);
     225           0 :                         return (0);
     226             :                 } else {
     227           0 :                         KERNEL_LOCK();
     228           0 :                         sorwakeup(inp->inp_socket);
     229           0 :                         KERNEL_UNLOCK();
     230             :                 }
     231           0 :         }
     232             : 
     233           0 :         if (sa == NULL) {
     234           0 :                 div6stat_inc(div6s_noport);
     235           0 :                 m_freem(m);
     236           0 :         }
     237           0 :         return (0);
     238           0 : }
     239             : 
     240             : /*ARGSUSED*/
     241             : int
     242           0 : divert6_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *addr,
     243             :     struct mbuf *control, struct proc *p)
     244             : {
     245           0 :         struct inpcb *inp = sotoinpcb(so);
     246             :         int error = 0;
     247             : 
     248           0 :         if (req == PRU_CONTROL) {
     249           0 :                 return (in6_control(so, (u_long)m, (caddr_t)addr,
     250           0 :                     (struct ifnet *)control));
     251             :         }
     252             : 
     253           0 :         soassertlocked(so);
     254             : 
     255           0 :         if (inp == NULL) {
     256             :                 error = EINVAL;
     257           0 :                 goto release;
     258             :         }
     259           0 :         switch (req) {
     260             : 
     261             :         case PRU_BIND:
     262           0 :                 error = in_pcbbind(inp, addr, p);
     263           0 :                 break;
     264             : 
     265             :         case PRU_SHUTDOWN:
     266           0 :                 socantsendmore(so);
     267           0 :                 break;
     268             : 
     269             :         case PRU_SEND:
     270           0 :                 return (divert6_output(inp, m, addr, control));
     271             : 
     272             :         case PRU_ABORT:
     273           0 :                 soisdisconnected(so);
     274           0 :                 in_pcbdetach(inp);
     275           0 :                 break;
     276             : 
     277             :         case PRU_SOCKADDR:
     278           0 :                 in6_setsockaddr(inp, addr);
     279           0 :                 break;
     280             : 
     281             :         case PRU_PEERADDR:
     282           0 :                 in6_setpeeraddr(inp, addr);
     283           0 :                 break;
     284             : 
     285             :         case PRU_SENSE:
     286           0 :                 return (0);
     287             : 
     288             :         case PRU_LISTEN:
     289             :         case PRU_CONNECT:
     290             :         case PRU_CONNECT2:
     291             :         case PRU_ACCEPT:
     292             :         case PRU_DISCONNECT:
     293             :         case PRU_SENDOOB:
     294             :         case PRU_FASTTIMO:
     295             :         case PRU_SLOWTIMO:
     296             :         case PRU_PROTORCV:
     297             :         case PRU_PROTOSEND:
     298             :                 error =  EOPNOTSUPP;
     299           0 :                 break;
     300             : 
     301             :         case PRU_RCVD:
     302             :         case PRU_RCVOOB:
     303           0 :                 return (EOPNOTSUPP);    /* do not free mbuf's */
     304             : 
     305             :         default:
     306           0 :                 panic("divert6_usrreq");
     307             :         }
     308             : 
     309             : release:
     310           0 :         m_freem(control);
     311           0 :         m_freem(m);
     312           0 :         return (error);
     313           0 : }
     314             : 
     315             : int
     316           0 : divert6_attach(struct socket *so, int proto)
     317             : {
     318             :         int error;
     319             : 
     320           0 :         if (so->so_pcb != NULL)
     321           0 :                 return EINVAL;
     322             : 
     323           0 :         if ((so->so_state & SS_PRIV) == 0)
     324           0 :                 return EACCES;
     325             : 
     326           0 :         error = in_pcballoc(so, &divb6table);
     327           0 :         if (error)
     328           0 :                 return (error);
     329             : 
     330           0 :         error = soreserve(so, divert6_sendspace, divert6_recvspace);
     331           0 :         if (error)
     332           0 :                 return (error);
     333           0 :         sotoinpcb(so)->inp_flags |= INP_HDRINCL;
     334           0 :         return (0);
     335           0 : }
     336             : 
     337             : int
     338           0 : divert6_detach(struct socket *so)
     339             : {
     340           0 :         struct inpcb *inp = sotoinpcb(so);
     341             : 
     342           0 :         soassertlocked(so);
     343             : 
     344           0 :         if (inp == NULL)
     345           0 :                 return (EINVAL);
     346             : 
     347           0 :         in_pcbdetach(inp);
     348             : 
     349           0 :         return (0);
     350           0 : }
     351             : 
     352             : int
     353           0 : divert6_sysctl_div6stat(void *oldp, size_t *oldlenp, void *newp)
     354             : {
     355           0 :         uint64_t counters[div6s_ncounters];
     356           0 :         struct div6stat div6stat;
     357           0 :         u_long *words = (u_long *)&div6stat;
     358             :         int i;
     359             : 
     360             :         CTASSERT(sizeof(div6stat) == (nitems(counters) * sizeof(u_long)));
     361             : 
     362           0 :         counters_read(div6counters, counters, nitems(counters));
     363             : 
     364           0 :         for (i = 0; i < nitems(counters); i++)
     365           0 :                 words[i] = (u_long)counters[i];
     366             : 
     367           0 :         return (sysctl_rdstruct(oldp, oldlenp, newp,
     368             :             &div6stat, sizeof(div6stat)));
     369           0 : }
     370             : 
     371             : /*
     372             :  * Sysctl for divert variables.
     373             :  */
     374             : int
     375           0 : divert6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
     376             :     void *newp, size_t newlen)
     377             : {
     378             :         int error;
     379             : 
     380             :         /* All sysctl names at this level are terminal. */
     381           0 :         if (namelen != 1)
     382           0 :                 return (ENOTDIR);
     383             : 
     384           0 :         switch (name[0]) {
     385             :         case DIVERT6CTL_SENDSPACE:
     386           0 :                 NET_LOCK();
     387           0 :                 error = sysctl_int(oldp, oldlenp, newp, newlen,
     388             :                     &divert6_sendspace);
     389           0 :                 NET_UNLOCK();
     390           0 :                 return (error);
     391             :         case DIVERT6CTL_RECVSPACE:
     392           0 :                 NET_LOCK();
     393           0 :                 error = sysctl_int(oldp, oldlenp, newp, newlen,
     394             :                     &divert6_recvspace);
     395           0 :                 NET_UNLOCK();
     396           0 :                 return (error);
     397             :         case DIVERT6CTL_STATS:
     398           0 :                 return (divert6_sysctl_div6stat(oldp, oldlenp, newp));
     399             :         default:
     400           0 :                 if (name[0] < DIVERT6CTL_MAXID) {
     401           0 :                         NET_LOCK();
     402           0 :                         error = sysctl_int_arr(divert6ctl_vars, name, namelen,
     403             :                             oldp, oldlenp, newp, newlen);
     404           0 :                         NET_UNLOCK();
     405           0 :                         return (error);
     406             :                 }
     407             : 
     408           0 :                 return (ENOPROTOOPT);
     409             :         }
     410             :         /* NOTREACHED */
     411           0 : }

Generated by: LCOV version 1.13