common/nit.c

Go to the documentation of this file.
00001 /* nit.c
00002 
00003    Network Interface Tap (NIT) network interface code, by Ted Lemon
00004    with one crucial tidbit of help from Stu Grossmen. */
00005 
00006 /*
00007  * Copyright (c) 2004,2007,2009,2014 by Internet Systems Consortium, Inc. ("ISC")
00008  * Copyright (c) 1996-2003 by Internet Software Consortium
00009  *
00010  * Permission to use, copy, modify, and distribute this software for any
00011  * purpose with or without fee is hereby granted, provided that the above
00012  * copyright notice and this permission notice appear in all copies.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
00015  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00016  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
00017  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00018  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00019  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
00020  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00021  *
00022  *   Internet Systems Consortium, Inc.
00023  *   950 Charter Street
00024  *   Redwood City, CA 94063
00025  *   <info@isc.org>
00026  *   https://www.isc.org/
00027  *
00028  */
00029 
00030 #include "dhcpd.h"
00031 #if defined (USE_NIT_SEND) || defined (USE_NIT_RECEIVE)
00032 #include <sys/ioctl.h>
00033 #include <sys/uio.h>
00034 
00035 #include <sys/time.h>
00036 #include <net/nit.h>
00037 #include <net/nit_if.h>
00038 #include <net/nit_pf.h>
00039 #include <net/nit_buf.h>
00040 #include <sys/stropts.h>
00041 #include <net/packetfilt.h>
00042 
00043 #include <netinet/in_systm.h>
00044 #include "includes/netinet/ip.h"
00045 #include "includes/netinet/udp.h"
00046 #include "includes/netinet/if_ether.h"
00047 
00048 /* Reinitializes the specified interface after an address change.   This
00049    is not required for packet-filter APIs. */
00050 
00051 #ifdef USE_NIT_SEND
00052 void if_reinitialize_send (info)
00053         struct interface_info *info;
00054 {
00055 }
00056 #endif
00057 
00058 #ifdef USE_NIT_RECEIVE
00059 void if_reinitialize_receive (info)
00060         struct interface_info *info;
00061 {
00062 }
00063 #endif
00064 
00065 /* Called by get_interface_list for each interface that's discovered.
00066    Opens a packet filter for each interface and adds it to the select
00067    mask. */
00068 
00069 int if_register_nit (info)
00070         struct interface_info *info;
00071 {
00072         int sock;
00073         char filename[50];
00074         struct ifreq ifr;
00075         struct strioctl sio;
00076 
00077         /* Open a NIT device */
00078         sock = open ("/dev/nit", O_RDWR | O_CLOEXEC);
00079         if (sock < 0)
00080                 log_fatal ("Can't open NIT device for %s: %m", info -> name);
00081 
00082         /* Set the NIT device to point at this interface. */
00083         sio.ic_cmd = NIOCBIND;
00084         sio.ic_len = sizeof *(info -> ifp);
00085         sio.ic_dp = (char *)(info -> ifp);
00086         sio.ic_timout = INFTIM;
00087         if (ioctl (sock, I_STR, &sio) < 0)
00088                 log_fatal ("Can't attach interface %s to nit device: %m",
00089                        info -> name);
00090 
00091         /* Get the low-level address... */
00092         sio.ic_cmd = SIOCGIFADDR;
00093         sio.ic_len = sizeof ifr;
00094         sio.ic_dp = (char *)&ifr;
00095         sio.ic_timout = INFTIM;
00096         if (ioctl (sock, I_STR, &sio) < 0)
00097                 log_fatal ("Can't get physical layer address for %s: %m",
00098                        info -> name);
00099 
00100         /* XXX code below assumes ethernet interface! */
00101         info -> hw_address.hlen = 7;
00102         info -> hw_address.hbuf [0] = ARPHRD_ETHER;
00103         memcpy (&info -> hw_address.hbuf [1],
00104                 ifr.ifr_ifru.ifru_addr.sa_data, 6);
00105 
00106         if (ioctl (sock, I_PUSH, "pf") < 0)
00107                 log_fatal ("Can't push packet filter onto NIT for %s: %m",
00108                        info -> name);
00109 
00110         return sock;
00111 }
00112 #endif /* USE_NIT_SEND || USE_NIT_RECEIVE */
00113 
00114 #ifdef USE_NIT_SEND
00115 void if_register_send (info)
00116         struct interface_info *info;
00117 {
00118         /* If we're using the nit API for sending and receiving,
00119            we don't need to register this interface twice. */
00120 #ifndef USE_NIT_RECEIVE
00121         struct packetfilt pf;
00122         struct strioctl sio;
00123 
00124         info -> wfdesc = if_register_nit (info);
00125 
00126         pf.Pf_Priority = 0;
00127         pf.Pf_FilterLen = 1;
00128         pf.Pf_Filter [0] = ENF_PUSHZERO;
00129 
00130         /* Set up an NIT filter that rejects everything... */
00131         sio.ic_cmd = NIOCSETF;
00132         sio.ic_len = sizeof pf;
00133         sio.ic_dp = (char *)&pf;
00134         sio.ic_timout = INFTIM;
00135         if (ioctl (info -> wfdesc, I_STR, &sio) < 0)
00136                 log_fatal ("Can't set NIT filter: %m");
00137 #else
00138         info -> wfdesc = info -> rfdesc;
00139 #endif
00140         if (!quiet_interface_discovery)
00141                 log_info ("Sending on   NIT/%s%s%s",
00142                       print_hw_addr (info -> hw_address.hbuf [0],
00143                                      info -> hw_address.hlen - 1,
00144                                      &info -> hw_address.hbuf [1]),
00145                       (info -> shared_network ? "/" : ""),
00146                       (info -> shared_network ?
00147                        info -> shared_network -> name : ""));
00148 }
00149 
00150 void if_deregister_send (info)
00151         struct interface_info *info;
00152 {
00153         /* If we're using the nit API for sending and receiving,
00154            we don't need to register this interface twice. */
00155 #ifndef USE_NIT_RECEIVE
00156         close (info -> wfdesc);
00157 #endif
00158         info -> wfdesc = -1;
00159         if (!quiet_interface_discovery)
00160                 log_info ("Disabling output on NIT/%s%s%s",
00161                       print_hw_addr (info -> hw_address.hbuf [0],
00162                                      info -> hw_address.hlen - 1,
00163                                      &info -> hw_address.hbuf [1]),
00164                       (info -> shared_network ? "/" : ""),
00165                       (info -> shared_network ?
00166                        info -> shared_network -> name : ""));
00167 }
00168 #endif /* USE_NIT_SEND */
00169 
00170 #ifdef USE_NIT_RECEIVE
00171 /* Packet filter program...
00172    XXX Changes to the filter program may require changes to the constant
00173    offsets used in if_register_send to patch the NIT program! XXX */
00174 
00175 void if_register_receive (info)
00176         struct interface_info *info;
00177 {
00178         int flag = 1;
00179         u_int32_t x;
00180         struct packetfilt pf;
00181         struct strioctl sio;
00182         u_int16_t addr [2];
00183         struct timeval t;
00184 
00185         /* Open a NIT device and hang it on this interface... */
00186         info -> rfdesc = if_register_nit (info);
00187 
00188         /* Set the snap length to 0, which means always take the whole
00189            packet. */
00190         x = 0;
00191         if (ioctl (info -> rfdesc, NIOCSSNAP, &x) < 0)
00192                 log_fatal ("Can't set NIT snap length on %s: %m", info -> name);
00193 
00194         /* Set the stream to byte stream mode */
00195         if (ioctl (info -> rfdesc, I_SRDOPT, RMSGN) != 0)
00196                 log_info ("I_SRDOPT failed on %s: %m", info -> name);
00197 
00198 #if 0
00199         /* Push on the chunker... */
00200         if (ioctl (info -> rfdesc, I_PUSH, "nbuf") < 0)
00201                 log_fatal ("Can't push chunker onto NIT STREAM: %m");
00202 
00203         /* Set the timeout to zero. */
00204         t.tv_sec = 0;
00205         t.tv_usec = 0;
00206         if (ioctl (info -> rfdesc, NIOCSTIME, &t) < 0)
00207                 log_fatal ("Can't set chunk timeout: %m");
00208 #endif
00209 
00210         /* Ask for no header... */
00211         x = 0;
00212         if (ioctl (info -> rfdesc, NIOCSFLAGS, &x) < 0)
00213                 log_fatal ("Can't set NIT flags on %s: %m", info -> name);
00214 
00215         /* Set up the NIT filter program. */
00216         /* XXX Unlike the BPF filter program, this one won't work if the
00217            XXX IP packet is fragmented or if there are options on the IP
00218            XXX header. */
00219         pf.Pf_Priority = 0;
00220         pf.Pf_FilterLen = 0;
00221 
00222         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 6;
00223         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
00224         pf.Pf_Filter [pf.Pf_FilterLen++] = htons (ETHERTYPE_IP);
00225         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT;
00226         pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP);
00227         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 11;
00228         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_AND;
00229         pf.Pf_Filter [pf.Pf_FilterLen++] = htons (0xFF);
00230         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_CAND;
00231         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 18;
00232         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
00233         pf.Pf_Filter [pf.Pf_FilterLen++] = local_port;
00234 
00235         /* Install the filter... */
00236         sio.ic_cmd = NIOCSETF;
00237         sio.ic_len = sizeof pf;
00238         sio.ic_dp = (char *)&pf;
00239         sio.ic_timout = INFTIM;
00240         if (ioctl (info -> rfdesc, I_STR, &sio) < 0)
00241                 log_fatal ("Can't set NIT filter on %s: %m", info -> name);
00242 
00243         if (!quiet_interface_discovery)
00244                 log_info ("Listening on NIT/%s%s%s",
00245                       print_hw_addr (info -> hw_address.hbuf [0],
00246                                      info -> hw_address.hlen - 1,
00247                                      &info -> hw_address.hbuf [1]),
00248                       (info -> shared_network ? "/" : ""),
00249                       (info -> shared_network ?
00250                        info -> shared_network -> name : ""));
00251 }
00252 
00253 void if_deregister_receive (info)
00254         struct interface_info *info;
00255 {
00256         /* If we're using the nit API for sending and receiving,
00257            we don't need to register this interface twice. */
00258         close (info -> rfdesc);
00259         info -> rfdesc = -1;
00260 
00261         if (!quiet_interface_discovery)
00262                 log_info ("Disabling input on NIT/%s%s%s",
00263                       print_hw_addr (info -> hw_address.hbuf [0],
00264                                      info -> hw_address.hlen - 1,
00265                                      &info -> hw_address.hbuf [1]),
00266                       (info -> shared_network ? "/" : ""),
00267                       (info -> shared_network ?
00268                        info -> shared_network -> name : ""));
00269 }
00270 #endif /* USE_NIT_RECEIVE */
00271 
00272 #ifdef USE_NIT_SEND
00273 ssize_t send_packet (interface, packet, raw, len, from, to, hto)
00274         struct interface_info *interface;
00275         struct packet *packet;
00276         struct dhcp_packet *raw;
00277         size_t len;
00278         struct in_addr from;
00279         struct sockaddr_in *to;
00280         struct hardware *hto;
00281 {
00282         unsigned hbufp, ibufp;
00283         double hh [16];
00284         double ih [1536 / sizeof (double)];
00285         unsigned char *buf = (unsigned char *)ih;
00286         struct sockaddr *junk;
00287         struct strbuf ctl, data;
00288         struct sockaddr_in foo;
00289         int result;
00290 
00291         if (!strcmp (interface -> name, "fallback"))
00292                 return send_fallback (interface, packet, raw,
00293                                       len, from, to, hto);
00294 
00295         if (hto == NULL && interface->anycast_mac_addr.hlen)
00296                 hto = &interface->anycast_mac_addr;
00297 
00298         /* Start with the sockaddr struct... */
00299         junk = (struct sockaddr *)&hh [0];
00300         hbufp = (((unsigned char *)&junk -> sa_data [0]) -
00301                  (unsigned char *)&hh[0]);
00302         ibufp = 0;
00303 
00304         /* Assemble the headers... */
00305         assemble_hw_header (interface, (unsigned char *)junk, &hbufp, hto);
00306         assemble_udp_ip_header (interface, buf, &ibufp,
00307                                 from.s_addr, to -> sin_addr.s_addr,
00308                                 to -> sin_port, (unsigned char *)raw, len);
00309 
00310         /* Copy the data into the buffer (yuk). */
00311         memcpy (buf + ibufp, raw, len);
00312 
00313         /* Set up the sockaddr structure... */
00314 #if USE_SIN_LEN
00315         junk -> sa_len = hbufp - 2; /* XXX */
00316 #endif
00317         junk -> sa_family = AF_UNSPEC;
00318 
00319         /* Set up the msg_buf structure... */
00320         ctl.buf = (char *)&hh [0];
00321         ctl.maxlen = ctl.len = hbufp;
00322         data.buf = (char *)&ih [0];
00323         data.maxlen = data.len = ibufp + len;
00324 
00325         result = putmsg (interface -> wfdesc, &ctl, &data, 0);
00326         if (result < 0)
00327                 log_error ("send_packet: %m");
00328         return result;
00329 }
00330 #endif /* USE_NIT_SEND */
00331 
00332 #ifdef USE_NIT_RECEIVE
00333 ssize_t receive_packet (interface, buf, len, from, hfrom)
00334         struct interface_info *interface;
00335         unsigned char *buf;
00336         size_t len;
00337         struct sockaddr_in *from;
00338         struct hardware *hfrom;
00339 {
00340         int nread;
00341         int length = 0;
00342         int offset = 0;
00343         unsigned char ibuf [1536];
00344         int bufix = 0;
00345         unsigned paylen;
00346 
00347         length = read (interface -> rfdesc, ibuf, sizeof ibuf);
00348         if (length <= 0)
00349                 return length;
00350 
00351         /* Decode the physical header... */
00352         offset = decode_hw_header (interface, ibuf, bufix, hfrom);
00353 
00354         /* If a physical layer checksum failed (dunno of any
00355            physical layer that supports this, but WTH), skip this
00356            packet. */
00357         if (offset < 0) {
00358                 return 0;
00359         }
00360 
00361         bufix += offset;
00362         length -= offset;
00363 
00364         /* Decode the IP and UDP headers... */
00365         offset = decode_udp_ip_header (interface, ibuf, bufix,
00366                                        from, length, &paylen, 0);
00367 
00368         /* If the IP or UDP checksum was bad, skip the packet... */
00369         if (offset < 0)
00370                 return 0;
00371 
00372         bufix += offset;
00373         length -= offset;
00374 
00375         if (length < paylen)
00376                 log_fatal("Internal inconsistency at %s:%d.", MDL);
00377 
00378         /* Copy out the data in the packet... */
00379         memcpy(buf, &ibuf[bufix], paylen);
00380         return paylen;
00381 }
00382 
00383 int can_unicast_without_arp (ip)
00384         struct interface_info *ip;
00385 {
00386         return 1;
00387 }
00388 
00389 int can_receive_unicast_unconfigured (ip)
00390         struct interface_info *ip;
00391 {
00392         return 1;
00393 }
00394 
00395 int supports_multiple_interfaces (ip)
00396         struct interface_info *ip;
00397 {
00398         return 1;
00399 }
00400 
00401 void maybe_setup_fallback ()
00402 {
00403         isc_result_t status;
00404         struct interface_info *fbi = (struct interface_info *)0;
00405         if (setup_fallback (&fbi, MDL)) {
00406                 if_register_fallback (fbi);
00407                 status = omapi_register_io_object ((omapi_object_t *)fbi,
00408                                                    if_readsocket, 0,
00409                                                    fallback_discard, 0, 0);
00410                 if (status != ISC_R_SUCCESS)
00411                         log_fatal ("Can't register I/O handle for %s: %s",
00412                                    fbi -> name, isc_result_totext (status));
00413                 interface_dereference (&fbi, MDL);
00414         }
00415 }
00416 #endif

Generated on 5 Apr 2014 for ISC DHCP by  doxygen 1.6.1