📄 dlpi.c
字号:
/* dlpi.c Data Link Provider Interface (DLPI) network interface code. *//* * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1996-2003 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * 950 Charter Street * Redwood City, CA 94063 * <info@isc.org> * http://www.isc.org/ * * This software was written for Internet Systems Consortium * by Eric James Negaard, <lmdejn@lmd.ericsson.se>. To learn more about * Internet Systems Consortium, see ``http://www.isc.org''. * * Joost Mulders has also done considerable work in debugging the DLPI API * support on Solaris and getting this code to work properly on a variety * of different Solaris platforms. *//* * Based largely in part to the existing NIT code in nit.c. * * This code has been developed and tested on sparc-based machines running * SunOS 5.5.1, with le and hme network interfaces. It should be pretty * generic, though. *//* * Implementation notes: * * I first tried to write this code to the "vanilla" DLPI 2.0 API. * It worked on a Sun Ultra-1 with a hme interface, but didn't work * on Sun SparcStation 5's with "le" interfaces (the packets sent out * via dlpiunitdatareq contained an Ethernet type of 0x0000 instead * of the expected 0x0800). * * Therefore I added the "DLPI_RAW" code which is a Sun extension to * the DLPI standard. This code works on both of the above machines. * This is configurable in the OS-dependent include file by defining * USE_DLPI_RAW. * * It quickly became apparant that I should also use the "pfmod" * STREAMS module to cut down on the amount of user level packet * processing. I don't know how widely available "pfmod" is, so it's * use is conditionally included. This is configurable in the * OS-dependent include file by defining USE_DLPI_PFMOD. * * A major quirk on the Sun's at least, is that no packets seem to get * sent out the interface until six seconds after the interface is * first "attached" to [per system reboot] (it's actually from when * the interface is attached, not when it is plumbed, so putting a * sleep into the dhclient-script at PREINIT time doesn't help). I * HAVE tried, without success to poll the fd to see when it is ready * for writing. This doesn't help at all. If the sleeps are not done, * the initial DHCPREQUEST or DHCPDISCOVER never gets sent out, so * I've put them here, when register_send and register_receive are * called (split up into two three-second sleeps between the notices, * so that it doesn't seem like so long when you're watching :-). The * amount of time to sleep is configurable in the OS-dependent include * file by defining DLPI_FIRST_SEND_WAIT to be the number of seconds * to sleep. */#ifndef lintstatic char copyright[] ="$Id: dlpi.c,v 1.28.2.2 2004/06/10 17:59:17 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";#endif /* not lint */#include "dhcpd.h"#if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE)# include <sys/ioctl.h># include <sys/time.h># include <sys/dlpi.h># include <stropts.h># ifdef USE_DLPI_PFMOD# include <sys/pfmod.h># endif# ifdef USE_POLL# include <poll.h># endif# include <netinet/in_systm.h># include "includes/netinet/ip.h"# include "includes/netinet/udp.h"# include "includes/netinet/if_ether.h"# ifdef USE_DLPI_PFMOD# ifdef USE_DLPI_RAW# define DLPI_MODNAME "DLPI+RAW+PFMOD"# else# define DLPI_MODNAME "DLPI+PFMOD"# endif# else# ifdef USE_DLPI_RAW# define DLPI_MODNAME "DLPI+RAW"# else# define DLPI_MODNAME "DLPI"# endif# endif# ifndef ABS# define ABS(x) ((x) >= 0 ? (x) : 0-(x))# endifstatic int strioctl PROTO ((int fd, int cmd, int timeout, int len, char *dp));#define DLPI_MAXDLBUF 8192 /* Buffer size */#define DLPI_MAXDLADDR 1024 /* Max address size */#define DLPI_DEVDIR "/dev/" /* Device directory */static int dlpiopen PROTO ((char *ifname));static int dlpiunit PROTO ((char *ifname));static int dlpiinforeq PROTO ((int fd));static int dlpiphysaddrreq PROTO ((int fd, unsigned long addrtype));static int dlpiattachreq PROTO ((int fd, unsigned long ppa));static int dlpibindreq PROTO ((int fd, unsigned long sap, unsigned long max_conind, unsigned long service_mode, unsigned long conn_mgmt, unsigned long xidtest));static int dlpidetachreq PROTO ((int fd));static int dlpiunbindreq PROTO ((int fd));static int dlpiokack PROTO ((int fd, char *bufp));static int dlpiinfoack PROTO ((int fd, char *bufp));static int dlpiphysaddrack PROTO ((int fd, char *bufp));static int dlpibindack PROTO ((int fd, char *bufp));static int dlpiunitdatareq PROTO ((int fd, unsigned char *addr, int addrlen, unsigned long minpri, unsigned long maxpri, unsigned char *data, int datalen));static int dlpiunitdataind PROTO ((int fd, unsigned char *dstaddr, unsigned long *dstaddrlen, unsigned char *srcaddr, unsigned long *srcaddrlen, unsigned long *grpaddr, unsigned char *data, int datalen));# ifndef USE_POLLstatic void sigalrm PROTO ((int sig));# endifstatic int expected PROTO ((unsigned long prim, union DL_primitives *dlp, int msgflags));static int strgetmsg PROTO ((int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp, char *caller));/* Reinitializes the specified interface after an address change. This is not required for packet-filter APIs. */#ifdef USE_DLPI_SENDvoid if_reinitialize_send (info) struct interface_info *info;{}#endif#ifdef USE_DLPI_RECEIVEvoid if_reinitialize_receive (info) struct interface_info *info;{}#endif/* Called by get_interface_list for each interface that's discovered. Opens a packet filter for each interface and adds it to the select mask. */int if_register_dlpi (info) struct interface_info *info;{ int sock; int unit; long buf [DLPI_MAXDLBUF]; union DL_primitives *dlp; dlp = (union DL_primitives *)buf; /* Open a DLPI device */ if ((sock = dlpiopen (info -> name)) < 0) { log_fatal ("Can't open DLPI device for %s: %m", info -> name); } /* * Submit a DL_INFO_REQ request, to find the dl_mac_type and * dl_provider_style */ if (dlpiinforeq(sock) < 0 || dlpiinfoack(sock, (char *)buf) < 0) { log_fatal ("Can't get DLPI MAC type for %s: %m", info -> name); } else { switch (dlp -> info_ack.dl_mac_type) { case DL_CSMACD: /* IEEE 802.3 */ case DL_ETHER: info -> hw_address.hbuf [0] = HTYPE_ETHER; break; /* adding token ring 5/1999 - mayer@ping.at */ case DL_TPR: info -> hw_address.hbuf [0] = HTYPE_IEEE802; break; case DL_FDDI: info -> hw_address.hbuf [0] = HTYPE_FDDI; break; default: log_fatal ("%s: unsupported DLPI MAC type %ld", info -> name, dlp -> info_ack.dl_mac_type); break; } /* * copy the sap length and broadcast address of this interface * to interface_info. This fixes nothing but seemed nicer than to * assume -2 and ffffff. */ info -> dlpi_sap_length = dlp -> info_ack.dl_sap_length; info -> dlpi_broadcast_addr.hlen = dlp -> info_ack.dl_brdcst_addr_length; memcpy (info -> dlpi_broadcast_addr.hbuf, (char *)dlp + dlp -> info_ack.dl_brdcst_addr_offset, dlp -> info_ack.dl_brdcst_addr_length); } if (dlp -> info_ack.dl_provider_style == DL_STYLE2) { /* * Attach to the device. If this fails, the device * does not exist. */ unit = dlpiunit (info -> name); if (dlpiattachreq (sock, unit) < 0 || dlpiokack (sock, (char *)buf) < 0) { log_fatal ("Can't attach DLPI device for %s: %m", info -> name); } } /* * Bind to the IP service access point (SAP), connectionless (CLDLS). */ if (dlpibindreq (sock, ETHERTYPE_IP, 0, DL_CLDLS, 0, 0) < 0 || dlpibindack (sock, (char *)buf) < 0) { log_fatal ("Can't bind DLPI device for %s: %m", info -> name); } /* * Submit a DL_PHYS_ADDR_REQ request, to find * the hardware address */ if (dlpiphysaddrreq (sock, DL_CURR_PHYS_ADDR) < 0 || dlpiphysaddrack (sock, (char *)buf) < 0) { log_fatal ("Can't get DLPI hardware address for %s: %m", info -> name); } info -> hw_address.hlen = dlp -> physaddr_ack.dl_addr_length + 1; memcpy (&info -> hw_address.hbuf [1], (char *)buf + dlp -> physaddr_ack.dl_addr_offset, dlp -> physaddr_ack.dl_addr_length);#ifdef USE_DLPI_RAW if (strioctl (sock, DLIOCRAW, INFTIM, 0, 0) < 0) { log_fatal ("Can't set DLPI RAW mode for %s: %m", info -> name); }#endif#ifdef USE_DLPI_PFMOD if (ioctl (sock, I_PUSH, "pfmod") < 0) { log_fatal ("Can't push packet filter onto DLPI for %s: %m", info -> name); }#endif return sock;}static intstrioctl (fd, cmd, timeout, len, dp)int fd;int cmd;int timeout;int len;char *dp;{ struct strioctl sio; int rslt; sio.ic_cmd = cmd; sio.ic_timout = timeout; sio.ic_len = len; sio.ic_dp = dp; if ((rslt = ioctl (fd, I_STR, &sio)) < 0) { return rslt; } else { return sio.ic_len; }}#ifdef USE_DLPI_SENDvoid if_register_send (info) struct interface_info *info;{ /* If we're using the DLPI API for sending and receiving, we don't need to register this interface twice. */#ifndef USE_DLPI_RECEIVE# ifdef USE_DLPI_PFMOD struct packetfilt pf;# endif info -> wfdesc = if_register_dlpi (info);# ifdef USE_DLPI_PFMOD /* Set up an PFMOD filter that rejects everything... */ pf.Pf_Priority = 0; pf.Pf_FilterLen = 1; pf.Pf_Filter [0] = ENF_PUSHZERO; /* Install the filter */ if (strioctl (info -> wfdesc, PFIOCSETF, INFTIM, sizeof (pf), (char *)&pf) < 0) { log_fatal ("Can't set PFMOD send filter on %s: %m", info -> name); }# endif /* USE_DLPI_PFMOD */#else /* !defined (USE_DLPI_RECEIVE) */ /* * If using DLPI for both send and receive, simply re-use * the read file descriptor that was set up earlier. */ info -> wfdesc = info -> rfdesc;#endif if (!quiet_interface_discovery) log_info ("Sending on DLPI/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : ""));#ifdef DLPI_FIRST_SEND_WAIT/* See the implementation notes at the beginning of this file */# ifdef USE_DLPI_RECEIVE sleep (DLPI_FIRST_SEND_WAIT - (DLPI_FIRST_SEND_WAIT / 2));# else sleep (DLPI_FIRST_SEND_WAIT);# endif#endif}void if_deregister_send (info) struct interface_info *info;{ /* If we're using the DLPI API for sending and receiving, we don't need to register this interface twice. */#ifndef USE_DLPI_RECEIVE close (info -> wfdesc);#endif info -> wfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling output on DLPI/%s/%s%s%s", info -> name, print_hw_addr (info -> hw_address.hbuf [0], info -> hw_address.hlen - 1, &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : ""));}#endif /* USE_DLPI_SEND */#ifdef USE_DLPI_RECEIVE/* Packet filter program... XXX Changes to the filter program may require changes to the constant offsets used in if_register_send to patch the NIT program! XXX */void if_register_receive (info) struct interface_info *info;{#ifdef USE_DLPI_PFMOD struct packetfilt pf; struct ip iphdr; u_int16_t offset;#endif /* Open a DLPI device and hang it on this interface... */ info -> rfdesc = if_register_dlpi (info);#ifdef USE_DLPI_PFMOD /* Set up the PFMOD filter program. */ /* XXX Unlike the BPF filter program, this one won't work if the XXX IP packet is fragmented or if there are options on the IP XXX header. */ pf.Pf_Priority = 0; pf.Pf_FilterLen = 0;#if defined (USE_DLPI_RAW)# define ETHER_H_PREFIX (14) /* sizeof (ethernet_header) */ /* * ethertype == ETHERTYPE_IP */ offset = 12; pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND; pf.Pf_Filter [pf.Pf_FilterLen++] = htons (ETHERTYPE_IP);# else# define ETHER_H_PREFIX (0)# endif /* USE_DLPI_RAW */ /* * The packets that will be received on this file descriptor * will be IP packets (due to the SAP that was specified in * the dlbind call). There will be no ethernet header. * Therefore, setup the packet filter to check the protocol * field for UDP, and the destination port number equal * to the local port. All offsets are relative to the start * of an IP packet. */ /* * BOOTPS destination port */ offset = ETHER_H_PREFIX + sizeof (iphdr) + sizeof (u_int16_t); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND; pf.Pf_Filter [pf.Pf_FilterLen++] = local_port; /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -