LCOV - code coverage report
Current view: top level - netinet - ip_divert.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 174 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: ip_divert.c,v 1.57 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/in_var.h>
      34             : #include <netinet/ip.h>
      35             : #include <netinet/ip_var.h>
      36             : #include <netinet/in_pcb.h>
      37             : #include <netinet/ip_divert.h>
      38             : #include <netinet/tcp.h>
      39             : #include <netinet/udp.h>
      40             : #include <netinet/ip_icmp.h>
      41             : 
      42             : #include <net/pfvar.h>
      43             : 
      44             : struct  inpcbtable      divbtable;
      45             : struct  cpumem          *divcounters;
      46             : 
      47             : #ifndef DIVERT_SENDSPACE
      48             : #define DIVERT_SENDSPACE        (65536 + 100)
      49             : #endif
      50             : u_int   divert_sendspace = DIVERT_SENDSPACE;
      51             : #ifndef DIVERT_RECVSPACE
      52             : #define DIVERT_RECVSPACE        (65536 + 100)
      53             : #endif
      54             : u_int   divert_recvspace = DIVERT_RECVSPACE;
      55             : 
      56             : #ifndef DIVERTHASHSIZE
      57             : #define DIVERTHASHSIZE  128
      58             : #endif
      59             : 
      60             : int *divertctl_vars[DIVERTCTL_MAXID] = DIVERTCTL_VARS;
      61             : 
      62             : int divbhashsize = DIVERTHASHSIZE;
      63             : 
      64             : int     divert_output(struct inpcb *, struct mbuf *, struct mbuf *,
      65             :             struct mbuf *);
      66             : void
      67           0 : divert_init(void)
      68             : {
      69           0 :         in_pcbinit(&divbtable, divbhashsize);
      70           0 :         divcounters = counters_alloc(divs_ncounters);
      71           0 : }
      72             : 
      73             : int
      74           0 : divert_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam,
      75             :     struct mbuf *control)
      76             : {
      77           0 :         struct sockaddr_in *sin;
      78             :         int error, min_hdrlen, off, dir;
      79             :         struct ip *ip;
      80             : 
      81           0 :         m_freem(control);
      82             : 
      83           0 :         if ((error = in_nam2sin(nam, &sin)))
      84             :                 goto fail;
      85             : 
      86             :         /* Do basic sanity checks. */
      87           0 :         if (m->m_pkthdr.len < sizeof(struct ip))
      88             :                 goto fail;
      89           0 :         if ((m = m_pullup(m, sizeof(struct ip))) == NULL) {
      90             :                 /* m_pullup() has freed the mbuf, so just return. */
      91           0 :                 divstat_inc(divs_errors);
      92           0 :                 return (ENOBUFS);
      93             :         }
      94           0 :         ip = mtod(m, struct ip *);
      95           0 :         if (ip->ip_v != IPVERSION)
      96             :                 goto fail;
      97           0 :         off = ip->ip_hl << 2;
      98           0 :         if (off < sizeof(struct ip) || ntohs(ip->ip_len) < off ||
      99           0 :             m->m_pkthdr.len < ntohs(ip->ip_len))
     100             :                 goto fail;
     101             : 
     102           0 :         dir = (sin->sin_addr.s_addr == INADDR_ANY ? PF_OUT : PF_IN);
     103             : 
     104           0 :         switch (ip->ip_p) {
     105             :         case IPPROTO_TCP:
     106             :                 min_hdrlen = sizeof(struct tcphdr);
     107           0 :                 m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
     108           0 :                 break;
     109             :         case IPPROTO_UDP:
     110             :                 min_hdrlen = sizeof(struct udphdr);
     111           0 :                 m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
     112           0 :                 break;
     113             :         case IPPROTO_ICMP:
     114             :                 min_hdrlen = ICMP_MINLEN;
     115           0 :                 m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
     116           0 :                 break;
     117             :         default:
     118             :                 min_hdrlen = 0;
     119           0 :                 break;
     120             :         }
     121           0 :         if (min_hdrlen && m->m_pkthdr.len < off + min_hdrlen)
     122             :                 goto fail;
     123             : 
     124           0 :         m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED_PACKET;
     125             : 
     126           0 :         if (dir == PF_IN) {
     127             :                 struct rtentry *rt;
     128             :                 struct ifnet *ifp;
     129             : 
     130           0 :                 rt = rtalloc(sintosa(sin), 0, inp->inp_rtableid);
     131           0 :                 if (!rtisvalid(rt) || !ISSET(rt->rt_flags, RTF_LOCAL)) {
     132           0 :                         rtfree(rt);
     133             :                         error = EADDRNOTAVAIL;
     134           0 :                         goto fail;
     135             :                 }
     136           0 :                 m->m_pkthdr.ph_ifidx = rt->rt_ifidx;
     137           0 :                 rtfree(rt);
     138             : 
     139             :                 /*
     140             :                  * Recalculate IP and protocol checksums for the inbound packet
     141             :                  * since the userspace application may have modified the packet
     142             :                  * prior to reinjection.
     143             :                  */
     144           0 :                 ip->ip_sum = 0;
     145           0 :                 ip->ip_sum = in_cksum(m, off);
     146           0 :                 in_proto_cksum_out(m, NULL);
     147             : 
     148           0 :                 ifp = if_get(m->m_pkthdr.ph_ifidx);
     149           0 :                 if (ifp == NULL) {
     150             :                         error = ENETDOWN;
     151           0 :                         goto fail;
     152             :                 }
     153           0 :                 ipv4_input(ifp, m);
     154           0 :                 if_put(ifp);
     155           0 :         } else {
     156           0 :                 m->m_pkthdr.ph_rtableid = inp->inp_rtableid;
     157             : 
     158           0 :                 error = ip_output(m, NULL, &inp->inp_route,
     159             :                     IP_ALLOWBROADCAST | IP_RAWOUTPUT, NULL, NULL, 0);
     160           0 :                 if (error == EACCES)    /* translate pf(4) error for userland */
     161             :                         error = EHOSTUNREACH;
     162             :         }
     163             : 
     164           0 :         divstat_inc(divs_opackets);
     165           0 :         return (error);
     166             : 
     167             : fail:
     168           0 :         m_freem(m);
     169           0 :         divstat_inc(divs_errors);
     170           0 :         return (error ? error : EINVAL);
     171           0 : }
     172             : 
     173             : int
     174           0 : divert_packet(struct mbuf *m, int dir, u_int16_t divert_port)
     175             : {
     176             :         struct inpcb *inp;
     177             :         struct socket *sa = NULL;
     178           0 :         struct sockaddr_in addr;
     179             : 
     180             :         inp = NULL;
     181           0 :         divstat_inc(divs_ipackets);
     182             : 
     183           0 :         if (m->m_len < sizeof(struct ip) &&
     184           0 :             (m = m_pullup(m, sizeof(struct ip))) == NULL) {
     185           0 :                 divstat_inc(divs_errors);
     186           0 :                 return (0);
     187             :         }
     188             : 
     189           0 :         TAILQ_FOREACH(inp, &divbtable.inpt_queue, inp_queue) {
     190           0 :                 if (inp->inp_lport == divert_port)
     191             :                         break;
     192             :         }
     193             : 
     194           0 :         memset(&addr, 0, sizeof(addr));
     195           0 :         addr.sin_family = AF_INET;
     196           0 :         addr.sin_len = sizeof(addr);
     197             : 
     198           0 :         if (dir == PF_IN) {
     199             :                 struct ifaddr *ifa;
     200             :                 struct ifnet *ifp;
     201             : 
     202           0 :                 ifp = if_get(m->m_pkthdr.ph_ifidx);
     203           0 :                 if (ifp == NULL) {
     204           0 :                         m_freem(m);
     205           0 :                         return (0);
     206             :                 }
     207           0 :                 TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
     208           0 :                         if (ifa->ifa_addr->sa_family != AF_INET)
     209             :                                 continue;
     210           0 :                         addr.sin_addr.s_addr = satosin(
     211           0 :                             ifa->ifa_addr)->sin_addr.s_addr;
     212           0 :                         break;
     213             :                 }
     214           0 :                 if_put(ifp);
     215           0 :         }
     216             : 
     217           0 :         if (inp) {
     218           0 :                 sa = inp->inp_socket;
     219           0 :                 if (sbappendaddr(sa, &sa->so_rcv, sintosa(&addr), m, NULL) == 0) {
     220           0 :                         divstat_inc(divs_fullsock);
     221           0 :                         m_freem(m);
     222           0 :                         return (0);
     223             :                 } else {
     224           0 :                         KERNEL_LOCK();
     225           0 :                         sorwakeup(inp->inp_socket);
     226           0 :                         KERNEL_UNLOCK();
     227             :                 }
     228           0 :         }
     229             : 
     230           0 :         if (sa == NULL) {
     231           0 :                 divstat_inc(divs_noport);
     232           0 :                 m_freem(m);
     233           0 :         }
     234           0 :         return (0);
     235           0 : }
     236             : 
     237             : /*ARGSUSED*/
     238             : int
     239           0 : divert_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *addr,
     240             :     struct mbuf *control, struct proc *p)
     241             : {
     242           0 :         struct inpcb *inp = sotoinpcb(so);
     243             :         int error = 0;
     244             : 
     245           0 :         if (req == PRU_CONTROL) {
     246           0 :                 return (in_control(so, (u_long)m, (caddr_t)addr,
     247           0 :                     (struct ifnet *)control));
     248             :         }
     249             : 
     250           0 :         soassertlocked(so);
     251             : 
     252           0 :         if (inp == NULL) {
     253             :                 error = EINVAL;
     254           0 :                 goto release;
     255             :         }
     256           0 :         switch (req) {
     257             : 
     258             :         case PRU_BIND:
     259           0 :                 error = in_pcbbind(inp, addr, p);
     260           0 :                 break;
     261             : 
     262             :         case PRU_SHUTDOWN:
     263           0 :                 socantsendmore(so);
     264           0 :                 break;
     265             : 
     266             :         case PRU_SEND:
     267           0 :                 return (divert_output(inp, m, addr, control));
     268             : 
     269             :         case PRU_ABORT:
     270           0 :                 soisdisconnected(so);
     271           0 :                 in_pcbdetach(inp);
     272           0 :                 break;
     273             : 
     274             :         case PRU_SOCKADDR:
     275           0 :                 in_setsockaddr(inp, addr);
     276           0 :                 break;
     277             : 
     278             :         case PRU_PEERADDR:
     279           0 :                 in_setpeeraddr(inp, addr);
     280           0 :                 break;
     281             : 
     282             :         case PRU_SENSE:
     283           0 :                 return (0);
     284             : 
     285             :         case PRU_LISTEN:
     286             :         case PRU_CONNECT:
     287             :         case PRU_CONNECT2:
     288             :         case PRU_ACCEPT:
     289             :         case PRU_DISCONNECT:
     290             :         case PRU_SENDOOB:
     291             :         case PRU_FASTTIMO:
     292             :         case PRU_SLOWTIMO:
     293             :         case PRU_PROTORCV:
     294             :         case PRU_PROTOSEND:
     295             :                 error =  EOPNOTSUPP;
     296           0 :                 break;
     297             : 
     298             :         case PRU_RCVD:
     299             :         case PRU_RCVOOB:
     300           0 :                 return (EOPNOTSUPP);    /* do not free mbuf's */
     301             : 
     302             :         default:
     303           0 :                 panic("divert_usrreq");
     304             :         }
     305             : 
     306             : release:
     307           0 :         m_freem(control);
     308           0 :         m_freem(m);
     309           0 :         return (error);
     310           0 : }
     311             : 
     312             : int
     313           0 : divert_attach(struct socket *so, int proto)
     314             : {
     315             :         int error;
     316             : 
     317           0 :         if (so->so_pcb != NULL)
     318           0 :                 return EINVAL;
     319           0 :         if ((so->so_state & SS_PRIV) == 0)
     320           0 :                 return EACCES;
     321             : 
     322           0 :         error = in_pcballoc(so, &divbtable);
     323           0 :         if (error)
     324           0 :                 return error;
     325             : 
     326           0 :         error = soreserve(so, divert_sendspace, divert_recvspace);
     327           0 :         if (error)
     328           0 :                 return error;
     329             : 
     330           0 :         sotoinpcb(so)->inp_flags |= INP_HDRINCL;
     331           0 :         return (0);
     332           0 : }
     333             : 
     334             : int
     335           0 : divert_detach(struct socket *so)
     336             : {
     337           0 :         struct inpcb *inp = sotoinpcb(so);
     338             : 
     339           0 :         soassertlocked(so);
     340             : 
     341           0 :         if (inp == NULL)
     342           0 :                 return (EINVAL);
     343             : 
     344           0 :         in_pcbdetach(inp);
     345           0 :         return (0);
     346           0 : }
     347             : 
     348             : int
     349           0 : divert_sysctl_divstat(void *oldp, size_t *oldlenp, void *newp)
     350             : {
     351           0 :         uint64_t counters[divs_ncounters];
     352           0 :         struct divstat divstat;
     353           0 :         u_long *words = (u_long *)&divstat;
     354             :         int i;
     355             : 
     356             :         CTASSERT(sizeof(divstat) == (nitems(counters) * sizeof(u_long)));
     357           0 :         memset(&divstat, 0, sizeof divstat);
     358           0 :         counters_read(divcounters, counters, nitems(counters));
     359             : 
     360           0 :         for (i = 0; i < nitems(counters); i++)
     361           0 :                 words[i] = (u_long)counters[i];
     362             : 
     363           0 :         return (sysctl_rdstruct(oldp, oldlenp, newp,
     364             :             &divstat, sizeof(divstat)));
     365           0 : }
     366             : 
     367             : /*
     368             :  * Sysctl for divert variables.
     369             :  */
     370             : int
     371           0 : divert_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
     372             :     size_t newlen)
     373             : {
     374             :         int error;
     375             : 
     376             :         /* All sysctl names at this level are terminal. */
     377           0 :         if (namelen != 1)
     378           0 :                 return (ENOTDIR);
     379             : 
     380           0 :         switch (name[0]) {
     381             :         case DIVERTCTL_SENDSPACE:
     382           0 :                 NET_LOCK();
     383           0 :                 error = sysctl_int(oldp, oldlenp, newp, newlen,
     384             :                     &divert_sendspace);
     385           0 :                 NET_UNLOCK();
     386           0 :                 return (error);
     387             :         case DIVERTCTL_RECVSPACE:
     388           0 :                 NET_LOCK();
     389           0 :                 error = sysctl_int(oldp, oldlenp, newp, newlen,
     390             :                     &divert_recvspace);
     391           0 :                 NET_UNLOCK();
     392           0 :                 return (error);
     393             :         case DIVERTCTL_STATS:
     394           0 :                 return (divert_sysctl_divstat(oldp, oldlenp, newp));
     395             :         default:
     396           0 :                 if (name[0] < DIVERTCTL_MAXID) {
     397           0 :                         NET_LOCK();
     398           0 :                         error = sysctl_int_arr(divertctl_vars, name, namelen,
     399             :                             oldp, oldlenp, newp, newlen);
     400           0 :                         NET_UNLOCK();
     401           0 :                         return (error);
     402             :                 }
     403             : 
     404           0 :                 return (ENOPROTOOPT);
     405             :         }
     406             :         /* NOTREACHED */
     407           0 : }

Generated by: LCOV version 1.13