Annotation of XNU/bsd/netinet/ip_divert.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
        !             3:  *
        !             4:  * @APPLE_LICENSE_HEADER_START@
        !             5:  * 
        !             6:  * The contents of this file constitute Original Code as defined in and
        !             7:  * are subject to the Apple Public Source License Version 1.1 (the
        !             8:  * "License").  You may not use this file except in compliance with the
        !             9:  * License.  Please obtain a copy of the License at
        !            10:  * http://www.apple.com/publicsource and read it before using this file.
        !            11:  * 
        !            12:  * This Original Code and all software distributed under the License are
        !            13:  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
        !            14:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
        !            15:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
        !            16:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
        !            17:  * License for the specific language governing rights and limitations
        !            18:  * under the License.
        !            19:  * 
        !            20:  * @APPLE_LICENSE_HEADER_END@
        !            21:  */
        !            22: /*
        !            23:  * Copyright (c) 1982, 1986, 1988, 1993
        !            24:  *     The Regents of the University of California.  All rights reserved.
        !            25:  *
        !            26:  * Redistribution and use in source and binary forms, with or without
        !            27:  * modification, are permitted provided that the following conditions
        !            28:  * are met:
        !            29:  * 1. Redistributions of source code must retain the above copyright
        !            30:  *    notice, this list of conditions and the following disclaimer.
        !            31:  * 2. Redistributions in binary form must reproduce the above copyright
        !            32:  *    notice, this list of conditions and the following disclaimer in the
        !            33:  *    documentation and/or other materials provided with the distribution.
        !            34:  * 3. All advertising materials mentioning features or use of this software
        !            35:  *    must display the following acknowledgement:
        !            36:  *     This product includes software developed by the University of
        !            37:  *     California, Berkeley and its contributors.
        !            38:  * 4. Neither the name of the University nor the names of its contributors
        !            39:  *    may be used to endorse or promote products derived from this software
        !            40:  *    without specific prior written permission.
        !            41:  *
        !            42:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
        !            43:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            44:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            45:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            46:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            47:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            48:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            49:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            50:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            51:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            52:  * SUCH DAMAGE.
        !            53:  *
        !            54:  */
        !            55: 
        !            56: #if ISFB31
        !            57: #include "opt_inet.h"
        !            58: #include "opt_ipfw.h"
        !            59: #include "opt_ipdivert.h"
        !            60: #endif
        !            61: 
        !            62: #ifndef INET
        !            63: #error "IPDIVERT requires INET."
        !            64: #endif
        !            65: 
        !            66: #include <sys/param.h>
        !            67: #include <sys/malloc.h>
        !            68: #include <sys/mbuf.h>
        !            69: #include <sys/socket.h>
        !            70: #include <sys/protosw.h>
        !            71: #include <sys/socketvar.h>
        !            72: #include <sys/systm.h>
        !            73: #include <sys/proc.h>
        !            74: 
        !            75: #if ISFB31
        !            76: #include <vm/vm_zone.h>
        !            77: #endif
        !            78: 
        !            79: #include <net/if.h>
        !            80: #include <net/route.h>
        !            81: 
        !            82: #include <netinet/in.h>
        !            83: #include <netinet/in_systm.h>
        !            84: #include <netinet/ip.h>
        !            85: #include <netinet/in_pcb.h>
        !            86: #include <netinet/in_var.h>
        !            87: #include <netinet/ip_var.h>
        !            88: 
        !            89: /*
        !            90:  * Divert sockets
        !            91:  */
        !            92: 
        !            93: /*
        !            94:  * Allocate enough space to hold a full IP packet
        !            95:  */
        !            96: #define        DIVSNDQ         (65536 + 100)
        !            97: #define        DIVRCVQ         (65536 + 100)
        !            98: 
        !            99: /* Global variables */
        !           100: 
        !           101: /*
        !           102:  * ip_input() and ip_output() set this secret value before calling us to
        !           103:  * let us know which divert port to divert a packet to; this is done so
        !           104:  * we can use the existing prototype for struct protosw's pr_input().
        !           105:  * This is stored in host order.
        !           106:  */
        !           107: u_short ip_divert_port;
        !           108: 
        !           109: /*
        !           110:  * A 16 bit cookie is passed to the user process.
        !           111:  * The user process can send it back to help the caller know something
        !           112:  * about where the packet came from.
        !           113:  *
        !           114:  * If IPFW is the caller then the cookie is the rule that sent
        !           115:  * us here. On reinjection is is the rule after which processing
        !           116:  * should continue. Leaving it the same will make processing start
        !           117:  * at the rule number after that which sent it here. Setting it to
        !           118:  * 0 will restart processing at the beginning. 
        !           119:  */
        !           120: u_int16_t ip_divert_cookie;
        !           121: 
        !           122: /* Internal variables */
        !           123: 
        !           124: static struct inpcbhead divcb;
        !           125: static struct inpcbinfo divcbinfo;
        !           126: 
        !           127: static u_long  div_sendspace = DIVSNDQ;        /* XXX sysctl ? */
        !           128: static u_long  div_recvspace = DIVRCVQ;        /* XXX sysctl ? */
        !           129: 
        !           130: /* Optimization: have this preinitialized */
        !           131: static struct sockaddr_in divsrc = { sizeof(divsrc), AF_INET };
        !           132: 
        !           133: /* Internal functions */
        !           134: 
        !           135: static int div_output(struct socket *so,
        !           136:                struct mbuf *m, struct sockaddr *addr, struct mbuf *control);
        !           137: 
        !           138: /*
        !           139:  * Initialize divert connection block queue.
        !           140:  */
        !           141: void
        !           142: div_init(void)
        !           143: {
        !           144:        LIST_INIT(&divcb);
        !           145:        divcbinfo.listhead = &divcb;
        !           146:        /*
        !           147:         * XXX We don't use the hash list for divert IP, but it's easier
        !           148:         * to allocate a one entry hash list than it is to check all
        !           149:         * over the place for hashbase == NULL.
        !           150:         */
        !           151:        divcbinfo.hashbase = hashinit(1, M_PCB, &divcbinfo.hashmask);
        !           152:        divcbinfo.porthashbase = hashinit(1, M_PCB, &divcbinfo.porthashmask);
        !           153:        divcbinfo.ipi_zone = (void *) zinit(sizeof(struct inpcb),(maxsockets * sizeof(struct inpcb)),
        !           154:                                   4096, "divzone");
        !           155: 
        !           156: /*
        !           157:  * ### LD 08/03: init IP forwarding at this point [ipfw is not a module yet]
        !           158:  */
        !           159:        ip_fw_init();
        !           160: }
        !           161: 
        !           162: /*
        !           163:  * Setup generic address and protocol structures
        !           164:  * for div_input routine, then pass them along with
        !           165:  * mbuf chain. ip->ip_len is assumed to have had
        !           166:  * the header length (hlen) subtracted out already.
        !           167:  * We tell whether the packet was incoming or outgoing
        !           168:  * by seeing if hlen == 0, which is a hack.
        !           169:  */
        !           170: void
        !           171: div_input(struct mbuf *m, int hlen)
        !           172: {
        !           173:        struct ip *ip;
        !           174:        struct inpcb *inp;
        !           175:        struct socket *sa;
        !           176: 
        !           177:        /* Sanity check */
        !           178:        if (ip_divert_port == 0)
        !           179:                panic("div_input: port is 0");
        !           180: 
        !           181:        /* Assure header */
        !           182:        if (m->m_len < sizeof(struct ip) &&
        !           183:            (m = m_pullup(m, sizeof(struct ip))) == 0) {
        !           184:                return;
        !           185:        }
        !           186:        ip = mtod(m, struct ip *);
        !           187: 
        !           188:        /* Record divert cookie */
        !           189:        divsrc.sin_port = ip_divert_cookie;
        !           190:        ip_divert_cookie = 0;
        !           191: 
        !           192:        /* Restore packet header fields */
        !           193:        ip->ip_len += hlen;
        !           194:        HTONS(ip->ip_len);
        !           195:        HTONS(ip->ip_off);
        !           196: 
        !           197:        /*
        !           198:         * Record receive interface address, if any
        !           199:         * But only for incoming packets.
        !           200:         */
        !           201:        divsrc.sin_addr.s_addr = 0;
        !           202:        if (hlen) {
        !           203:                struct ifaddr *ifa;
        !           204: 
        !           205: #if DIAGNOSTIC
        !           206:                /* Sanity check */
        !           207:                if (!(m->m_flags & M_PKTHDR))
        !           208:                        panic("div_input: no pkt hdr");
        !           209: #endif
        !           210: 
        !           211:                /* More fields affected by ip_input() */
        !           212:                HTONS(ip->ip_id);
        !           213: 
        !           214:                /* Find IP address for receive interface */
        !           215:                for (ifa = m->m_pkthdr.rcvif->if_addrhead.tqh_first;
        !           216:                    ifa != NULL; ifa = ifa->ifa_link.tqe_next) {
        !           217:                        if (ifa->ifa_addr == NULL)
        !           218:                                continue;
        !           219:                        if (ifa->ifa_addr->sa_family != AF_INET)
        !           220:                                continue;
        !           221:                        divsrc.sin_addr =
        !           222:                            ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
        !           223:                        break;
        !           224:                }
        !           225:        }
        !           226:        /*
        !           227:         * Record the incoming interface name whenever we have one.
        !           228:         */
        !           229:        bzero(&divsrc.sin_zero, sizeof(divsrc.sin_zero));
        !           230:        if (m->m_pkthdr.rcvif) {
        !           231:                /*
        !           232:                 * Hide the actual interface name in there in the 
        !           233:                 * sin_zero array. XXX This needs to be moved to a
        !           234:                 * different sockaddr type for divert, e.g.
        !           235:                 * sockaddr_div with multiple fields like 
        !           236:                 * sockaddr_dl. Presently we have only 7 bytes
        !           237:                 * but that will do for now as most interfaces
        !           238:                 * are 4 or less + 2 or less bytes for unit.
        !           239:                 * There is probably a faster way of doing this,
        !           240:                 * possibly taking it from the sockaddr_dl on the iface.
        !           241:                 * This solves the problem of a P2P link and a LAN interface
        !           242:                 * having the same address, which can result in the wrong
        !           243:                 * interface being assigned to the packet when fed back
        !           244:                 * into the divert socket. Theoretically if the daemon saves
        !           245:                 * and re-uses the sockaddr_in as suggested in the man pages,
        !           246:                 * this iface name will come along for the ride.
        !           247:                 * (see div_output for the other half of this.)
        !           248:                 */ 
        !           249:                snprintf(divsrc.sin_zero, sizeof(divsrc.sin_zero),
        !           250:                        "%s%d", m->m_pkthdr.rcvif->if_name,
        !           251:                        m->m_pkthdr.rcvif->if_unit);
        !           252:        }
        !           253: 
        !           254:        /* Put packet on socket queue, if any */
        !           255:        sa = NULL;
        !           256:        for (inp = divcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
        !           257:                if (inp->inp_lport == htons(ip_divert_port))
        !           258:                        sa = inp->inp_socket;
        !           259:        }
        !           260:        ip_divert_port = 0;
        !           261:        if (sa) {
        !           262:                if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&divsrc,
        !           263:                                m, (struct mbuf *)0) == 0)
        !           264:                        m_freem(m);
        !           265:                else
        !           266:                        sorwakeup(sa);
        !           267:        } else {
        !           268:                m_freem(m);
        !           269:                ipstat.ips_noproto++;
        !           270:                ipstat.ips_delivered--;
        !           271:         }
        !           272: }
        !           273: 
        !           274: /*
        !           275:  * Deliver packet back into the IP processing machinery.
        !           276:  *
        !           277:  * If no address specified, or address is 0.0.0.0, send to ip_output();
        !           278:  * otherwise, send to ip_input() and mark as having been received on
        !           279:  * the interface with that address.
        !           280:  */
        !           281: static int
        !           282: div_output(so, m, addr, control)
        !           283:        struct socket *so;
        !           284:        register struct mbuf *m;
        !           285:        struct sockaddr *addr;
        !           286:        struct mbuf *control;
        !           287: {
        !           288:        register struct inpcb *const inp = sotoinpcb(so);
        !           289:        register struct ip *const ip = mtod(m, struct ip *);
        !           290:        struct sockaddr_in *sin = (struct sockaddr_in *)addr;
        !           291:        int error = 0;
        !           292: 
        !           293:        if (control)
        !           294:                m_freem(control);               /* XXX */
        !           295: 
        !           296:        /* Loopback avoidance and state recovery */
        !           297:        if (sin) {
        !           298:                int     len = 0;
        !           299:                char    *c = sin->sin_zero;
        !           300: 
        !           301:                ip_divert_cookie = sin->sin_port;
        !           302: 
        !           303:                /*
        !           304:                 * Find receive interface with the given name or IP address.
        !           305:                 * The name is user supplied data so don't trust it's size or 
        !           306:                 * that it is zero terminated. The name has priority.
        !           307:                 * We are presently assuming that the sockaddr_in 
        !           308:                 * has not been replaced by a sockaddr_div, so we limit it
        !           309:                 * to 16 bytes in total. the name is stuffed (if it exists)
        !           310:                 * in the sin_zero[] field.
        !           311:                 */
        !           312:                while (*c++ && (len++ < sizeof(sin->sin_zero)));
        !           313:                if ((len > 0) && (len < sizeof(sin->sin_zero)))
        !           314:                        m->m_pkthdr.rcvif = ifunit(sin->sin_zero);
        !           315:        } else {
        !           316:                ip_divert_cookie = 0;
        !           317:        }
        !           318: 
        !           319:        /* Reinject packet into the system as incoming or outgoing */
        !           320:        if (!sin || sin->sin_addr.s_addr == 0) {
        !           321:                /*
        !           322:                 * Don't allow both user specified and setsockopt options,
        !           323:                 * and don't allow packet length sizes that will crash
        !           324:                 */
        !           325:                if (((ip->ip_hl != (sizeof (*ip) >> 2)) && inp->inp_options) ||
        !           326:                     ((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) {
        !           327:                        error = EINVAL;
        !           328:                        goto cantsend;
        !           329:                }
        !           330: 
        !           331:                /* Convert fields to host order for ip_output() */
        !           332:                NTOHS(ip->ip_len);
        !           333:                NTOHS(ip->ip_off);
        !           334: 
        !           335:                /* Send packet to output processing */
        !           336:                ipstat.ips_rawout++;                    /* XXX */
        !           337:                error = ip_output(m, inp->inp_options, &inp->inp_route,
        !           338:                        (so->so_options & SO_DONTROUTE) |
        !           339:                        IP_ALLOWBROADCAST | IP_RAWOUTPUT, inp->inp_moptions);
        !           340:        } else {
        !           341:                struct  ifaddr *ifa;
        !           342: 
        !           343:                /* If no luck with the name above. check by IP address.  */
        !           344:                if (m->m_pkthdr.rcvif == NULL) {
        !           345:                        /*
        !           346:                         * Make sure there are no distractions
        !           347:                         * for ifa_ifwithaddr. Clear the port and the ifname.
        !           348:                         * Maybe zap all 8 bytes at once using a 64bit write?
        !           349:                         */
        !           350:                        bzero(sin->sin_zero, sizeof(sin->sin_zero));
        !           351:                        /* *((u_int64_t *)sin->sin_zero) = 0; */ /* XXX ?? */
        !           352:                        sin->sin_port = 0;
        !           353:                        if (!(ifa = ifa_ifwithaddr((struct sockaddr *) sin))) {
        !           354:                                error = EADDRNOTAVAIL;
        !           355:                                goto cantsend;
        !           356:                        }
        !           357:                        m->m_pkthdr.rcvif = ifa->ifa_ifp;
        !           358:                }
        !           359: 
        !           360:                /* Send packet to input processing */
        !           361:                ip_input(m);
        !           362:        }
        !           363: 
        !           364:        /* paranoid: Reset for next time (and other packets) */
        !           365:        /* almost definitly already done in the ipfw filter but.. */
        !           366:        ip_divert_cookie = 0;
        !           367:        return error;
        !           368: 
        !           369: cantsend:
        !           370:        ip_divert_cookie = 0;
        !           371:        m_freem(m);
        !           372:        return error;
        !           373: }
        !           374: 
        !           375: static int
        !           376: div_attach(struct socket *so, int proto, struct proc *p)
        !           377: {
        !           378:        struct inpcb *inp;
        !           379:        int error, s;
        !           380: 
        !           381:        inp  = sotoinpcb(so);
        !           382:        if (inp)
        !           383:                panic("div_attach");
        !           384:        if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
        !           385:                return error;
        !           386: 
        !           387:        s = splnet();
        !           388:        error = in_pcballoc(so, &divcbinfo, p);
        !           389:        splx(s);
        !           390:        if (error)
        !           391:                return error;
        !           392:        error = soreserve(so, div_sendspace, div_recvspace);
        !           393:        if (error)
        !           394:                return error;
        !           395:        inp = (struct inpcb *)so->so_pcb;
        !           396:        inp->inp_ip_p = proto;
        !           397:        inp->inp_flags |= INP_HDRINCL;
        !           398:        /* The socket is always "connected" because
        !           399:           we always know "where" to send the packet */
        !           400:        so->so_state |= SS_ISCONNECTED;
        !           401:        return 0;
        !           402: }
        !           403: 
        !           404: static int
        !           405: div_detach(struct socket *so)
        !           406: {
        !           407:        struct inpcb *inp;
        !           408: 
        !           409:        inp = sotoinpcb(so);
        !           410:        if (inp == 0)
        !           411:                panic("div_detach");
        !           412:        in_pcbdetach(inp);
        !           413:        return 0;
        !           414: }
        !           415: 
        !           416: static int
        !           417: div_abort(struct socket *so)
        !           418: {
        !           419:        soisdisconnected(so);
        !           420:        return div_detach(so);
        !           421: }
        !           422: 
        !           423: static int
        !           424: div_disconnect(struct socket *so)
        !           425: {
        !           426:        if ((so->so_state & SS_ISCONNECTED) == 0)
        !           427:                return ENOTCONN;
        !           428:        return div_abort(so);
        !           429: }
        !           430: 
        !           431: static int
        !           432: div_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
        !           433: {
        !           434:        struct inpcb *inp;
        !           435:        int s;
        !           436:        int error;
        !           437: 
        !           438:        s = splnet();
        !           439:        inp = sotoinpcb(so);
        !           440:        error = in_pcbbind(inp, nam, p);
        !           441:        splx(s);
        !           442:        return 0;
        !           443: }
        !           444: 
        !           445: static int
        !           446: div_shutdown(struct socket *so)
        !           447: {
        !           448:        socantsendmore(so);
        !           449:        return 0;
        !           450: }
        !           451: 
        !           452: static int
        !           453: div_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
        !           454:         struct mbuf *control, struct proc *p)
        !           455: {
        !           456:        /* Packet must have a header (but that's about it) */
        !           457:        if (m->m_len < sizeof (struct ip) ||
        !           458:            (m = m_pullup(m, sizeof (struct ip))) == 0) {
        !           459:                ipstat.ips_toosmall++;
        !           460:                m_freem(m);
        !           461:                return EINVAL;
        !           462:        }
        !           463: 
        !           464:        /* Send packet */
        !           465:        return div_output(so, m, nam, control);
        !           466: }
        !           467: 
        !           468: struct pr_usrreqs div_usrreqs = {
        !           469:        div_abort, pru_accept_notsupp, div_attach, div_bind,
        !           470:        pru_connect_notsupp, pru_connect2_notsupp, in_control, div_detach,
        !           471:        div_disconnect, pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp,
        !           472:        pru_rcvoob_notsupp, div_send, pru_sense_null, div_shutdown,
        !           473:        in_setsockaddr, sosend, soreceive, sopoll
        !           474: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.