Annotation of embedaddon/dhcp/common/nit.c, revision 1.1

1.1     ! misho       1: /* nit.c
        !             2: 
        !             3:    Network Interface Tap (NIT) network interface code, by Ted Lemon
        !             4:    with one crucial tidbit of help from Stu Grossmen. */
        !             5: 
        !             6: /*
        !             7:  * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
        !             8:  * Copyright (c) 1996-2003 by Internet Software Consortium
        !             9:  *
        !            10:  * Permission to use, copy, modify, and distribute this software for any
        !            11:  * purpose with or without fee is hereby granted, provided that the above
        !            12:  * copyright notice and this permission notice appear in all copies.
        !            13:  *
        !            14:  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
        !            15:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            16:  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
        !            17:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            18:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            19:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
        !            20:  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            21:  *
        !            22:  *   Internet Systems Consortium, Inc.
        !            23:  *   950 Charter Street
        !            24:  *   Redwood City, CA 94063
        !            25:  *   <info@isc.org>
        !            26:  *   https://www.isc.org/
        !            27:  *
        !            28:  * This software has been written for Internet Systems Consortium
        !            29:  * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
        !            30:  * To learn more about Internet Systems Consortium, see
        !            31:  * ``https://www.isc.org/''.  To learn more about Vixie Enterprises,
        !            32:  * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
        !            33:  * ``http://www.nominum.com''.
        !            34:  */
        !            35: 
        !            36: #include "dhcpd.h"
        !            37: #if defined (USE_NIT_SEND) || defined (USE_NIT_RECEIVE)
        !            38: #include <sys/ioctl.h>
        !            39: #include <sys/uio.h>
        !            40: 
        !            41: #include <sys/time.h>
        !            42: #include <net/nit.h>
        !            43: #include <net/nit_if.h>
        !            44: #include <net/nit_pf.h>
        !            45: #include <net/nit_buf.h>
        !            46: #include <sys/stropts.h>
        !            47: #include <net/packetfilt.h>
        !            48: 
        !            49: #include <netinet/in_systm.h>
        !            50: #include "includes/netinet/ip.h"
        !            51: #include "includes/netinet/udp.h"
        !            52: #include "includes/netinet/if_ether.h"
        !            53: 
        !            54: /* Reinitializes the specified interface after an address change.   This
        !            55:    is not required for packet-filter APIs. */
        !            56: 
        !            57: #ifdef USE_NIT_SEND
        !            58: void if_reinitialize_send (info)
        !            59:        struct interface_info *info;
        !            60: {
        !            61: }
        !            62: #endif
        !            63: 
        !            64: #ifdef USE_NIT_RECEIVE
        !            65: void if_reinitialize_receive (info)
        !            66:        struct interface_info *info;
        !            67: {
        !            68: }
        !            69: #endif
        !            70: 
        !            71: /* Called by get_interface_list for each interface that's discovered.
        !            72:    Opens a packet filter for each interface and adds it to the select
        !            73:    mask. */
        !            74: 
        !            75: int if_register_nit (info)
        !            76:        struct interface_info *info;
        !            77: {
        !            78:        int sock;
        !            79:        char filename[50];
        !            80:        struct ifreq ifr;
        !            81:        struct strioctl sio;
        !            82: 
        !            83:        /* Open a NIT device */
        !            84:        sock = open ("/dev/nit", O_RDWR);
        !            85:        if (sock < 0)
        !            86:                log_fatal ("Can't open NIT device for %s: %m", info -> name);
        !            87: 
        !            88:        /* Set the NIT device to point at this interface. */
        !            89:        sio.ic_cmd = NIOCBIND;
        !            90:        sio.ic_len = sizeof *(info -> ifp);
        !            91:        sio.ic_dp = (char *)(info -> ifp);
        !            92:        sio.ic_timout = INFTIM;
        !            93:        if (ioctl (sock, I_STR, &sio) < 0)
        !            94:                log_fatal ("Can't attach interface %s to nit device: %m",
        !            95:                       info -> name);
        !            96: 
        !            97:        /* Get the low-level address... */
        !            98:        sio.ic_cmd = SIOCGIFADDR;
        !            99:        sio.ic_len = sizeof ifr;
        !           100:        sio.ic_dp = (char *)&ifr;
        !           101:        sio.ic_timout = INFTIM;
        !           102:        if (ioctl (sock, I_STR, &sio) < 0)
        !           103:                log_fatal ("Can't get physical layer address for %s: %m",
        !           104:                       info -> name);
        !           105: 
        !           106:        /* XXX code below assumes ethernet interface! */
        !           107:        info -> hw_address.hlen = 7;
        !           108:        info -> hw_address.hbuf [0] = ARPHRD_ETHER;
        !           109:        memcpy (&info -> hw_address.hbuf [1],
        !           110:                ifr.ifr_ifru.ifru_addr.sa_data, 6);
        !           111: 
        !           112:        if (ioctl (sock, I_PUSH, "pf") < 0)
        !           113:                log_fatal ("Can't push packet filter onto NIT for %s: %m",
        !           114:                       info -> name);
        !           115: 
        !           116:        return sock;
        !           117: }
        !           118: #endif /* USE_NIT_SEND || USE_NIT_RECEIVE */
        !           119: 
        !           120: #ifdef USE_NIT_SEND
        !           121: void if_register_send (info)
        !           122:        struct interface_info *info;
        !           123: {
        !           124:        /* If we're using the nit API for sending and receiving,
        !           125:           we don't need to register this interface twice. */
        !           126: #ifndef USE_NIT_RECEIVE
        !           127:        struct packetfilt pf;
        !           128:        struct strioctl sio;
        !           129: 
        !           130:        info -> wfdesc = if_register_nit (info);
        !           131: 
        !           132:        pf.Pf_Priority = 0;
        !           133:        pf.Pf_FilterLen = 1;
        !           134:        pf.Pf_Filter [0] = ENF_PUSHZERO;
        !           135: 
        !           136:        /* Set up an NIT filter that rejects everything... */
        !           137:        sio.ic_cmd = NIOCSETF;
        !           138:        sio.ic_len = sizeof pf;
        !           139:        sio.ic_dp = (char *)&pf;
        !           140:        sio.ic_timout = INFTIM;
        !           141:        if (ioctl (info -> wfdesc, I_STR, &sio) < 0)
        !           142:                log_fatal ("Can't set NIT filter: %m");
        !           143: #else
        !           144:        info -> wfdesc = info -> rfdesc;
        !           145: #endif
        !           146:         if (!quiet_interface_discovery)
        !           147:                log_info ("Sending on   NIT/%s%s%s",
        !           148:                      print_hw_addr (info -> hw_address.hbuf [0],
        !           149:                                     info -> hw_address.hlen - 1,
        !           150:                                     &info -> hw_address.hbuf [1]),
        !           151:                      (info -> shared_network ? "/" : ""),
        !           152:                      (info -> shared_network ?
        !           153:                       info -> shared_network -> name : ""));
        !           154: }
        !           155: 
        !           156: void if_deregister_send (info)
        !           157:        struct interface_info *info;
        !           158: {
        !           159:        /* If we're using the nit API for sending and receiving,
        !           160:           we don't need to register this interface twice. */
        !           161: #ifndef USE_NIT_RECEIVE
        !           162:        close (info -> wfdesc);
        !           163: #endif
        !           164:        info -> wfdesc = -1;
        !           165:         if (!quiet_interface_discovery)
        !           166:                log_info ("Disabling output on NIT/%s%s%s",
        !           167:                      print_hw_addr (info -> hw_address.hbuf [0],
        !           168:                                     info -> hw_address.hlen - 1,
        !           169:                                     &info -> hw_address.hbuf [1]),
        !           170:                      (info -> shared_network ? "/" : ""),
        !           171:                      (info -> shared_network ?
        !           172:                       info -> shared_network -> name : ""));
        !           173: }
        !           174: #endif /* USE_NIT_SEND */
        !           175: 
        !           176: #ifdef USE_NIT_RECEIVE
        !           177: /* Packet filter program...
        !           178:    XXX Changes to the filter program may require changes to the constant
        !           179:    offsets used in if_register_send to patch the NIT program! XXX */
        !           180: 
        !           181: void if_register_receive (info)
        !           182:        struct interface_info *info;
        !           183: {
        !           184:        int flag = 1;
        !           185:        u_int32_t x;
        !           186:        struct packetfilt pf;
        !           187:        struct strioctl sio;
        !           188:        u_int16_t addr [2];
        !           189:        struct timeval t;
        !           190: 
        !           191:        /* Open a NIT device and hang it on this interface... */
        !           192:        info -> rfdesc = if_register_nit (info);
        !           193: 
        !           194:        /* Set the snap length to 0, which means always take the whole
        !           195:           packet. */
        !           196:        x = 0;
        !           197:        if (ioctl (info -> rfdesc, NIOCSSNAP, &x) < 0)
        !           198:                log_fatal ("Can't set NIT snap length on %s: %m", info -> name);
        !           199: 
        !           200:        /* Set the stream to byte stream mode */
        !           201:        if (ioctl (info -> rfdesc, I_SRDOPT, RMSGN) != 0)
        !           202:                log_info ("I_SRDOPT failed on %s: %m", info -> name);
        !           203: 
        !           204: #if 0
        !           205:        /* Push on the chunker... */
        !           206:        if (ioctl (info -> rfdesc, I_PUSH, "nbuf") < 0)
        !           207:                log_fatal ("Can't push chunker onto NIT STREAM: %m");
        !           208: 
        !           209:        /* Set the timeout to zero. */
        !           210:        t.tv_sec = 0;
        !           211:        t.tv_usec = 0;
        !           212:        if (ioctl (info -> rfdesc, NIOCSTIME, &t) < 0)
        !           213:                log_fatal ("Can't set chunk timeout: %m");
        !           214: #endif
        !           215: 
        !           216:        /* Ask for no header... */
        !           217:        x = 0;
        !           218:        if (ioctl (info -> rfdesc, NIOCSFLAGS, &x) < 0)
        !           219:                log_fatal ("Can't set NIT flags on %s: %m", info -> name);
        !           220: 
        !           221:        /* Set up the NIT filter program. */
        !           222:        /* XXX Unlike the BPF filter program, this one won't work if the
        !           223:           XXX IP packet is fragmented or if there are options on the IP
        !           224:           XXX header. */
        !           225:        pf.Pf_Priority = 0;
        !           226:        pf.Pf_FilterLen = 0;
        !           227: 
        !           228:        pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 6;
        !           229:        pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
        !           230:        pf.Pf_Filter [pf.Pf_FilterLen++] = htons (ETHERTYPE_IP);
        !           231:        pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT;
        !           232:        pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP);
        !           233:        pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 11;
        !           234:        pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_AND;
        !           235:        pf.Pf_Filter [pf.Pf_FilterLen++] = htons (0xFF);
        !           236:        pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_CAND;
        !           237:        pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 18;
        !           238:        pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
        !           239:        pf.Pf_Filter [pf.Pf_FilterLen++] = local_port;
        !           240: 
        !           241:        /* Install the filter... */
        !           242:        sio.ic_cmd = NIOCSETF;
        !           243:        sio.ic_len = sizeof pf;
        !           244:        sio.ic_dp = (char *)&pf;
        !           245:        sio.ic_timout = INFTIM;
        !           246:        if (ioctl (info -> rfdesc, I_STR, &sio) < 0)
        !           247:                log_fatal ("Can't set NIT filter on %s: %m", info -> name);
        !           248: 
        !           249:         if (!quiet_interface_discovery)
        !           250:                log_info ("Listening on NIT/%s%s%s",
        !           251:                      print_hw_addr (info -> hw_address.hbuf [0],
        !           252:                                     info -> hw_address.hlen - 1,
        !           253:                                     &info -> hw_address.hbuf [1]),
        !           254:                      (info -> shared_network ? "/" : ""),
        !           255:                      (info -> shared_network ?
        !           256:                       info -> shared_network -> name : ""));
        !           257: }
        !           258: 
        !           259: void if_deregister_receive (info)
        !           260:        struct interface_info *info;
        !           261: {
        !           262:        /* If we're using the nit API for sending and receiving,
        !           263:           we don't need to register this interface twice. */
        !           264:        close (info -> rfdesc);
        !           265:        info -> rfdesc = -1;
        !           266: 
        !           267:         if (!quiet_interface_discovery)
        !           268:                log_info ("Disabling input on NIT/%s%s%s",
        !           269:                      print_hw_addr (info -> hw_address.hbuf [0],
        !           270:                                     info -> hw_address.hlen - 1,
        !           271:                                     &info -> hw_address.hbuf [1]),
        !           272:                      (info -> shared_network ? "/" : ""),
        !           273:                      (info -> shared_network ?
        !           274:                       info -> shared_network -> name : ""));
        !           275: }
        !           276: #endif /* USE_NIT_RECEIVE */
        !           277: 
        !           278: #ifdef USE_NIT_SEND
        !           279: ssize_t send_packet (interface, packet, raw, len, from, to, hto)
        !           280:        struct interface_info *interface;
        !           281:        struct packet *packet;
        !           282:        struct dhcp_packet *raw;
        !           283:        size_t len;
        !           284:        struct in_addr from;
        !           285:        struct sockaddr_in *to;
        !           286:        struct hardware *hto;
        !           287: {
        !           288:        unsigned hbufp, ibufp;
        !           289:        double hh [16];
        !           290:        double ih [1536 / sizeof (double)];
        !           291:        unsigned char *buf = (unsigned char *)ih;
        !           292:        struct sockaddr *junk;
        !           293:        struct strbuf ctl, data;
        !           294:        struct sockaddr_in foo;
        !           295:        int result;
        !           296: 
        !           297:        if (!strcmp (interface -> name, "fallback"))
        !           298:                return send_fallback (interface, packet, raw,
        !           299:                                      len, from, to, hto);
        !           300: 
        !           301:        /* Start with the sockaddr struct... */
        !           302:        junk = (struct sockaddr *)&hh [0];
        !           303:        hbufp = (((unsigned char *)&junk -> sa_data [0]) -
        !           304:                 (unsigned char *)&hh[0]);
        !           305:        ibufp = 0;
        !           306: 
        !           307:        /* Assemble the headers... */
        !           308:        assemble_hw_header (interface, (unsigned char *)junk, &hbufp, hto);
        !           309:        assemble_udp_ip_header (interface, buf, &ibufp,
        !           310:                                from.s_addr, to -> sin_addr.s_addr,
        !           311:                                to -> sin_port, (unsigned char *)raw, len);
        !           312: 
        !           313:        /* Copy the data into the buffer (yuk). */
        !           314:        memcpy (buf + ibufp, raw, len);
        !           315: 
        !           316:        /* Set up the sockaddr structure... */
        !           317: #if USE_SIN_LEN
        !           318:        junk -> sa_len = hbufp - 2; /* XXX */
        !           319: #endif
        !           320:        junk -> sa_family = AF_UNSPEC;
        !           321: 
        !           322:        /* Set up the msg_buf structure... */
        !           323:        ctl.buf = (char *)&hh [0];
        !           324:        ctl.maxlen = ctl.len = hbufp;
        !           325:        data.buf = (char *)&ih [0];
        !           326:        data.maxlen = data.len = ibufp + len;
        !           327: 
        !           328:        result = putmsg (interface -> wfdesc, &ctl, &data, 0);
        !           329:        if (result < 0)
        !           330:                log_error ("send_packet: %m");
        !           331:        return result;
        !           332: }
        !           333: #endif /* USE_NIT_SEND */
        !           334: 
        !           335: #ifdef USE_NIT_RECEIVE
        !           336: ssize_t receive_packet (interface, buf, len, from, hfrom)
        !           337:        struct interface_info *interface;
        !           338:        unsigned char *buf;
        !           339:        size_t len;
        !           340:        struct sockaddr_in *from;
        !           341:        struct hardware *hfrom;
        !           342: {
        !           343:        int nread;
        !           344:        int length = 0;
        !           345:        int offset = 0;
        !           346:        unsigned char ibuf [1536];
        !           347:        int bufix = 0;
        !           348:        unsigned paylen;
        !           349: 
        !           350:        length = read (interface -> rfdesc, ibuf, sizeof ibuf);
        !           351:        if (length <= 0)
        !           352:                return length;
        !           353: 
        !           354:        /* Decode the physical header... */
        !           355:        offset = decode_hw_header (interface, ibuf, bufix, hfrom);
        !           356: 
        !           357:        /* If a physical layer checksum failed (dunno of any
        !           358:           physical layer that supports this, but WTH), skip this
        !           359:           packet. */
        !           360:        if (offset < 0) {
        !           361:                return 0;
        !           362:        }
        !           363: 
        !           364:        bufix += offset;
        !           365:        length -= offset;
        !           366: 
        !           367:        /* Decode the IP and UDP headers... */
        !           368:        offset = decode_udp_ip_header (interface, ibuf, bufix,
        !           369:                                       from, length, &paylen);
        !           370: 
        !           371:        /* If the IP or UDP checksum was bad, skip the packet... */
        !           372:        if (offset < 0)
        !           373:                return 0;
        !           374: 
        !           375:        bufix += offset;
        !           376:        length -= offset;
        !           377: 
        !           378:        if (length < paylen)
        !           379:                log_fatal("Internal inconsistency at %s:%d.", MDL);
        !           380: 
        !           381:        /* Copy out the data in the packet... */
        !           382:        memcpy(buf, &ibuf[bufix], paylen);
        !           383:        return paylen;
        !           384: }
        !           385: 
        !           386: int can_unicast_without_arp (ip)
        !           387:        struct interface_info *ip;
        !           388: {
        !           389:        return 1;
        !           390: }
        !           391: 
        !           392: int can_receive_unicast_unconfigured (ip)
        !           393:        struct interface_info *ip;
        !           394: {
        !           395:        return 1;
        !           396: }
        !           397: 
        !           398: int supports_multiple_interfaces (ip)
        !           399:        struct interface_info *ip;
        !           400: {
        !           401:        return 1;
        !           402: }
        !           403: 
        !           404: void maybe_setup_fallback ()
        !           405: {
        !           406:        isc_result_t status;
        !           407:        struct interface_info *fbi = (struct interface_info *)0;
        !           408:        if (setup_fallback (&fbi, MDL)) {
        !           409:                if_register_fallback (fbi);
        !           410:                status = omapi_register_io_object ((omapi_object_t *)fbi,
        !           411:                                                   if_readsocket, 0,
        !           412:                                                   fallback_discard, 0, 0);
        !           413:                if (status != ISC_R_SUCCESS)
        !           414:                        log_fatal ("Can't register I/O handle for %s: %s",
        !           415:                                   fbi -> name, isc_result_totext (status));
        !           416:                interface_dereference (&fbi, MDL);
        !           417:        }
        !           418: }
        !           419: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>