/* dlpi.c Data Link Provider Interface (DLPI) network interface code. */ /* * Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 2004,2007 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 * * https://www.isc.org/ * * This software was written for Internet Systems Consortium * by Eric James Negaard, . To learn more about * Internet Systems Consortium, see ``https://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. */ /* * The Open Group Technical Standard can be found here: * http://www.opengroup.org/onlinepubs/009618899/index.htm * * The HP DLPI Programmer's Guide can be found here: * http://docs.hp.com/en/B2355-90139/index.html */ #include "dhcpd.h" #if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE) || \ defined(USE_DLPI_HWADDR) # include # include # include # include # ifdef USE_DLPI_PFMOD # include # endif #include #include # include # 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)) # endif #if defined(USE_DLPI_PFMOD) || defined(USE_DLPI_RAW) static int strioctl (int fd, int cmd, int timeout, int len, char *dp); #endif #define DLPI_MAXDLBUF 8192 /* Buffer size */ #define DLPI_MAXDLADDR 1024 /* Max address size */ #define DLPI_DEVDIR "/dev/" /* Device directory */ static int dlpiopen(const char *ifname); static int dlpiunit (char *ifname); static int dlpiinforeq (int fd); static int dlpiphysaddrreq (int fd, unsigned long addrtype); static int dlpiattachreq (int fd, unsigned long ppa); static int dlpibindreq (int fd, unsigned long sap, unsigned long max_conind, unsigned long service_mode, unsigned long conn_mgmt, unsigned long xidtest); #if defined(UNUSED_DLPI_INTERFACE) /* These functions are unused at present, but may be used at a later date. * defined out to avoid compiler warnings about unused static functions. */ static int dlpidetachreq (int fd); static int dlpiunbindreq (int fd); #endif static int dlpiokack (int fd, char *bufp); static int dlpiinfoack (int fd, char *bufp); static int dlpiphysaddrack (int fd, char *bufp); static int dlpibindack (int fd, char *bufp); #if defined(USE_DLPI_SEND) || defined(USE_DLPI_RECEIVE) /* These functions are not used if we're only sourcing the get_hw_addr() * function (for USE_SOCKETS). */ static int dlpiunitdatareq (int fd, unsigned char *addr, int addrlen, unsigned long minpri, unsigned long maxpri, unsigned char *data, int datalen); static int dlpiunitdataind (int fd, unsigned char *dstaddr, unsigned long *dstaddrlen, unsigned char *srcaddr, unsigned long *srcaddrlen, unsigned long *grpaddr, unsigned char *data, int datalen); #endif /* !USE_DLPI_HWADDR: USE_DLPI_SEND || USE_DLPI_RECEIVE */ static int expected (unsigned long prim, union DL_primitives *dlp, int msgflags); static int strgetmsg (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_SEND void if_reinitialize_send (info) struct interface_info *info; { } #endif #ifdef USE_DLPI_RECEIVE void 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 %lu", info->name, (unsigned long)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; } #if defined(USE_DLPI_PFMOD) || defined(USE_DLPI_RAW) static int strioctl (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; } } #endif /* USE_DPI_PFMOD || USE_DLPI_RAW */ #ifdef USE_DLPI_SEND void 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; /* * protocol should be udp. this is a byte compare, test for * endianess. */ offset = ETHER_H_PREFIX + ((u_int8_t *)&(iphdr.ip_p) - (u_int8_t *)&iphdr); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_AND; pf.Pf_Filter [pf.Pf_FilterLen++] = htons (0x00FF); pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND; pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP); /* Install the filter... */ if (strioctl (info -> rfdesc, PFIOCSETF, INFTIM, sizeof (pf), (char *)&pf) < 0) { log_fatal ("Can't set PFMOD receive filter on %s: %m", info -> name); } #endif /* USE_DLPI_PFMOD */ if (!quiet_interface_discovery) log_info ("Listening 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_SEND sleep (DLPI_FIRST_SEND_WAIT / 2); # else sleep (DLPI_FIRST_SEND_WAIT); # endif #endif } void if_deregister_receive (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_SEND close (info -> rfdesc); #endif info -> rfdesc = -1; if (!quiet_interface_discovery) log_info ("Disabling input 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_RECEIVE */ #ifdef USE_DLPI_SEND ssize_t send_packet (interface, packet, raw, len, from, to, hto) struct interface_info *interface; struct packet *packet; struct dhcp_packet *raw; size_t len; struct in_addr from; struct sockaddr_in *to; struct hardware *hto; { #ifdef USE_DLPI_RAW double hh [32]; int fudge; #endif double ih [1536 / sizeof (double)]; unsigned char *dbuf = (unsigned char *)ih; unsigned dbuflen; unsigned char dstaddr [DLPI_MAXDLADDR]; unsigned addrlen; int result; if (!strcmp (interface -> name, "fallback")) return send_fallback (interface, packet, raw, len, from, to, hto); if (hto == NULL && interface->anycast_mac_addr.hlen) hto = &interface->anycast_mac_addr; dbuflen = 0; /* Assemble the headers... */ #ifdef USE_DLPI_RAW assemble_hw_header (interface, (unsigned char *)hh, &dbuflen, hto); if (dbuflen > sizeof hh) log_fatal ("send_packet: hh buffer too small.\n"); fudge = dbuflen % 4; /* IP header must be word-aligned. */ memcpy (dbuf + fudge, (unsigned char *)hh, dbuflen); dbuflen += fudge; #endif assemble_udp_ip_header (interface, dbuf, &dbuflen, from.s_addr, to -> sin_addr.s_addr, to -> sin_port, (unsigned char *)raw, len); /* Copy the data into the buffer (yuk). */ memcpy (dbuf + dbuflen, raw, len); dbuflen += len; #ifdef USE_DLPI_RAW result = write (interface -> wfdesc, dbuf + fudge, dbuflen - fudge); #else /* * Setup the destination address (DLSAP) in dstaddr * * If sap_length < 0 we must deliver the DLSAP as phys+sap. * If sap_length > 0 we must deliver the DLSAP as sap+phys. * * sap = Service Access Point == ETHERTYPE_IP * sap + datalink address is called DLSAP in dlpi speak. */ { /* ENCODE DLSAP */ unsigned char phys [DLPI_MAXDLADDR]; unsigned char sap [4]; int sap_len = interface -> dlpi_sap_length; int phys_len = interface -> hw_address.hlen - 1; /* sap = htons (ETHERTYPE_IP) kludge */ memset (sap, 0, sizeof (sap)); # if (BYTE_ORDER == LITTLE_ENDIAN) sap [0] = 0x00; sap [1] = 0x08; # else sap [0] = 0x08; sap [1] = 0x00; # endif if (hto && hto -> hlen == interface -> hw_address.hlen) memcpy ( phys, (char *) &hto -> hbuf [1], phys_len); else memcpy ( phys, interface -> dlpi_broadcast_addr.hbuf, interface -> dlpi_broadcast_addr.hlen); if (sap_len < 0) { memcpy ( dstaddr, phys, phys_len); memcpy ( (char *) &dstaddr [phys_len], sap, ABS (sap_len)); } else { memcpy ( dstaddr, (void *) sap, sap_len); memcpy ( (char *) &dstaddr [sap_len], phys, phys_len); } addrlen = phys_len + ABS (sap_len); } /* ENCODE DLSAP */ result = dlpiunitdatareq (interface -> wfdesc, dstaddr, addrlen, 0, 0, dbuf, dbuflen); #endif /* USE_DLPI_RAW */ if (result < 0) log_error ("send_packet: %m"); return result; } #endif /* USE_DLPI_SEND */ #ifdef USE_DLPI_RECEIVE ssize_t receive_packet (interface, buf, len, from, hfrom) struct interface_info *interface; unsigned char *buf; size_t len; struct sockaddr_in *from; struct hardware *hfrom; { unsigned char dbuf [1536]; unsigned char srcaddr [DLPI_MAXDLADDR]; unsigned long srcaddrlen; int length = 0; int offset = 0; int bufix = 0; unsigned paylen; #ifdef USE_DLPI_RAW length = read (interface -> rfdesc, dbuf, sizeof (dbuf)); #else length = dlpiunitdataind (interface -> rfdesc, (unsigned char *)NULL, (unsigned long *)NULL, srcaddr, &srcaddrlen, (unsigned long *)NULL, dbuf, sizeof (dbuf)); #endif if (length <= 0) { log_error("receive_packet: %m"); return length; } # if !defined (USE_DLPI_RAW) /* * Copy the sender's hw address into hfrom * If sap_len < 0 the DLSAP is as phys+sap. * If sap_len > 0 the DLSAP is as sap+phys. * * sap is discarded here. */ { /* DECODE DLSAP */ int sap_len = interface -> dlpi_sap_length; int phys_len = interface -> hw_address.hlen - 1; if (hfrom && (srcaddrlen == ABS (sap_len) + phys_len )) { hfrom -> hbuf [0] = interface -> hw_address.hbuf [0]; hfrom -> hlen = interface -> hw_address.hlen; if (sap_len < 0) { memcpy ((char *) &hfrom -> hbuf [1], srcaddr, phys_len); } else { memcpy((char *)&hfrom->hbuf[1], srcaddr + sap_len, phys_len); } } else if (hfrom) { memset (hfrom, '\0', sizeof *hfrom); } } /* DECODE_DLSAP */ # endif /* !defined (USE_DLPI_RAW) */ /* Decode the IP and UDP headers... */ bufix = 0; #ifdef USE_DLPI_RAW /* Decode the physical header... */ offset = decode_hw_header (interface, dbuf, bufix, hfrom); /* If a physical layer checksum failed (dunno of any physical layer that supports this, but WTH), skip this packet. */ if (offset < 0) { return 0; } bufix += offset; length -= offset; #endif offset = decode_udp_ip_header (interface, dbuf, bufix, from, length, &paylen); /* * If the IP or UDP checksum was bad, skip the packet... * * Note: this happens all the time when writing packets via the * fallback socket. The packet received by streams does not have * the IP or UDP checksums filled in, as those are calculated by * the hardware. */ if (offset < 0) { return 0; } bufix += offset; length -= offset; if (length < paylen) log_fatal("Internal inconsistency at %s:%d.", MDL); /* Copy out the data in the packet... */ memcpy(buf, &dbuf [bufix], paylen); return paylen; } #endif /* Common DLPI routines ... * * Written by Eric James Negaard, * * Based largely in part to the example code contained in the document * "How to Use the STREAMS Data Link Provider Interface (DLPI)", written * by Neal Nuckolls of SunSoft Internet Engineering. * * 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. * * The usual disclaimers apply. This code works for me. Don't blame me * if it makes your machine or network go down in flames. That taken * into consideration, use this code as you wish. If you make usefull * modifications I'd appreciate hearing about it. */ #define DLPI_MAXWAIT 15 /* Max timeout */ /* * Parse an interface name and extract the unit number */ static int dlpiunit (ifname) char *ifname; { char *cp; int unit; if (!ifname) { return 0; } /* Advance to the end of the name */ cp = ifname; while (*cp) cp++; /* Back up to the start of the first digit */ while ((*(cp-1) >= '0' && *(cp-1) <= '9') || *(cp - 1) == ':') cp--; /* Convert the unit number */ unit = 0; while (*cp >= '0' && *cp <= '9') { unit *= 10; unit += (*cp++ - '0'); } return unit; } /* * dlpiopen - open the DLPI device for a given interface name */ static int dlpiopen(const char *ifname) { char devname [50]; char *dp; const char *cp, *ep; if (!ifname) { return -1; } /* Open a DLPI device */ if (*ifname == '/') { dp = devname; } else { /* Prepend the device directory */ memcpy (devname, DLPI_DEVDIR, strlen (DLPI_DEVDIR)); dp = &devname [strlen (DLPI_DEVDIR)]; } /* Find the end of the interface name */ ep = cp = ifname; while (*ep) ep++; /* And back up to the first digit (unit number) */ while ((*(ep - 1) >= '0' && *(ep - 1) <= '9') || *(ep - 1) == ':') ep--; /* Copy everything up to the unit number */ while (cp < ep) { *dp++ = *cp++; } *dp = '\0'; return open (devname, O_RDWR, 0); } /* * dlpiinforeq - request information about the data link provider. */ static int dlpiinforeq (fd) int fd; { dl_info_req_t info_req; struct strbuf ctl; int flags; info_req.dl_primitive = DL_INFO_REQ; ctl.maxlen = 0; ctl.len = sizeof (info_req); ctl.buf = (char *)&info_req; flags = RS_HIPRI; return putmsg (fd, &ctl, (struct strbuf *)NULL, flags); } /* * dlpiphysaddrreq - request the current physical address. */ static int dlpiphysaddrreq (fd, addrtype) int fd; unsigned long addrtype; { dl_phys_addr_req_t physaddr_req; struct strbuf ctl; int flags; physaddr_req.dl_primitive = DL_PHYS_ADDR_REQ; physaddr_req.dl_addr_type = addrtype; ctl.maxlen = 0; ctl.len = sizeof (physaddr_req); ctl.buf = (char *)&physaddr_req; flags = RS_HIPRI; return putmsg (fd, &ctl, (struct strbuf *)NULL, flags); } /* * dlpiattachreq - send a request to attach to a specific unit. */ static int dlpiattachreq (fd, ppa) unsigned long ppa; int fd; { dl_attach_req_t attach_req; struct strbuf ctl; int flags; attach_req.dl_primitive = DL_ATTACH_REQ; attach_req.dl_ppa = ppa; ctl.maxlen = 0; ctl.len = sizeof (attach_req); ctl.buf = (char *)&attach_req; flags = 0; return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); } /* * dlpibindreq - send a request to bind to a specific SAP address. */ static int dlpibindreq (fd, sap, max_conind, service_mode, conn_mgmt, xidtest) unsigned long sap; unsigned long max_conind; unsigned long service_mode; unsigned long conn_mgmt; unsigned long xidtest; int fd; { dl_bind_req_t bind_req; struct strbuf ctl; int flags; bind_req.dl_primitive = DL_BIND_REQ; bind_req.dl_sap = sap; bind_req.dl_max_conind = max_conind; bind_req.dl_service_mode = service_mode; bind_req.dl_conn_mgmt = conn_mgmt; bind_req.dl_xidtest_flg = xidtest; ctl.maxlen = 0; ctl.len = sizeof (bind_req); ctl.buf = (char *)&bind_req; flags = 0; return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); } #if defined(UNUSED_DLPI_INTERFACE) /* * dlpiunbindreq - send a request to unbind. This function is not actually * used by ISC DHCP, but is included for completeness in case it is * ever required for new work. */ static int dlpiunbindreq (fd) int fd; { dl_unbind_req_t unbind_req; struct strbuf ctl; int flags; unbind_req.dl_primitive = DL_UNBIND_REQ; ctl.maxlen = 0; ctl.len = sizeof (unbind_req); ctl.buf = (char *)&unbind_req; flags = 0; return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); } /* * dlpidetachreq - send a request to detach. This function is not actually * used by ISC DHCP, but is included for completeness in case it is * ever required for new work. */ static int dlpidetachreq (fd) int fd; { dl_detach_req_t detach_req; struct strbuf ctl; int flags; detach_req.dl_primitive = DL_DETACH_REQ; ctl.maxlen = 0; ctl.len = sizeof (detach_req); ctl.buf = (char *)&detach_req; flags = 0; return putmsg (fd, &ctl, (struct strbuf*)NULL, flags); } #endif /* UNUSED_DLPI_INTERFACE */ /* * dlpibindack - receive an ack to a dlbindreq. */ static int dlpibindack (fd, bufp) char *bufp; int fd; { union DL_primitives *dlp; struct strbuf ctl; int flags; ctl.maxlen = DLPI_MAXDLBUF; ctl.len = 0; ctl.buf = bufp; if (strgetmsg (fd, &ctl, (struct strbuf*)NULL, &flags, "dlpibindack") < 0) { return -1; } dlp = (union DL_primitives *)ctl.buf; if (expected (DL_BIND_ACK, dlp, flags) == -1) { return -1; } if (ctl.len < sizeof (dl_bind_ack_t)) { /* Returned structure is too short */ return -1; } return 0; } /* * dlpiokack - general acknowledgement reception. */ static int dlpiokack (fd, bufp) char *bufp; int fd; { union DL_primitives *dlp; struct strbuf ctl; int flags; ctl.maxlen = DLPI_MAXDLBUF; ctl.len = 0; ctl.buf = bufp; if (strgetmsg (fd, &ctl, (struct strbuf*)NULL, &flags, "dlpiokack") < 0) { return -1; } dlp = (union DL_primitives *)ctl.buf; if (expected (DL_OK_ACK, dlp, flags) == -1) { return -1; } if (ctl.len < sizeof (dl_ok_ack_t)) { /* Returned structure is too short */ return -1; } return 0; } /* * dlpiinfoack - receive an ack to a dlinforeq. */ static int dlpiinfoack (fd, bufp) char *bufp; int fd; { union DL_primitives *dlp; struct strbuf ctl; int flags; ctl.maxlen = DLPI_MAXDLBUF; ctl.len = 0; ctl.buf = bufp; if (strgetmsg (fd, &ctl, (struct strbuf *)NULL, &flags, "dlpiinfoack") < 0) { return -1; } dlp = (union DL_primitives *) ctl.buf; if (expected (DL_INFO_ACK, dlp, flags) == -1) { return -1; } if (ctl.len < sizeof (dl_info_ack_t)) { /* Returned structure is too short */ return -1; } return 0; } /* * dlpiphysaddrack - receive an ack to a dlpiphysaddrreq. */ int dlpiphysaddrack (fd, bufp) char *bufp; int fd; { union DL_primitives *dlp; struct strbuf ctl; int flags; ctl.maxlen = DLPI_MAXDLBUF; ctl.len = 0; ctl.buf = bufp; if (strgetmsg (fd, &ctl, (struct strbuf *)NULL, &flags, "dlpiphysaddrack") < 0) { return -1; } dlp = (union DL_primitives *)ctl.buf; if (expected (DL_PHYS_ADDR_ACK, dlp, flags) == -1) { return -1; } if (ctl.len < sizeof (dl_phys_addr_ack_t)) { /* Returned structure is too short */ return -1; } return 0; } #if defined(USE_DLPI_SEND) || defined(USE_DLPI_RECEIVE) int dlpiunitdatareq (fd, addr, addrlen, minpri, maxpri, dbuf, dbuflen) int fd; unsigned char *addr; int addrlen; unsigned long minpri; unsigned long maxpri; unsigned char *dbuf; int dbuflen; { long buf [DLPI_MAXDLBUF]; union DL_primitives *dlp; struct strbuf ctl, data; /* Set up the control information... */ dlp = (union DL_primitives *)buf; dlp -> unitdata_req.dl_primitive = DL_UNITDATA_REQ; dlp -> unitdata_req.dl_dest_addr_length = addrlen; dlp -> unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t); dlp -> unitdata_req.dl_priority.dl_min = minpri; dlp -> unitdata_req.dl_priority.dl_max = maxpri; /* Append the destination address */ memcpy ((char *)buf + dlp -> unitdata_req.dl_dest_addr_offset, addr, addrlen); ctl.maxlen = 0; ctl.len = dlp -> unitdata_req.dl_dest_addr_offset + addrlen; ctl.buf = (char *)buf; data.maxlen = 0; data.buf = (char *)dbuf; data.len = dbuflen; /* Send the packet down the wire... */ return putmsg (fd, &ctl, &data, 0); } static int dlpiunitdataind (fd, daddr, daddrlen, saddr, saddrlen, grpaddr, dbuf, dlen) int fd; unsigned char *daddr; unsigned long *daddrlen; unsigned char *saddr; unsigned long *saddrlen; unsigned long *grpaddr; unsigned char *dbuf; int dlen; { long buf [DLPI_MAXDLBUF]; union DL_primitives *dlp; struct strbuf ctl, data; int flags = 0; int result; /* Set up the msg_buf structure... */ dlp = (union DL_primitives *)buf; dlp -> unitdata_ind.dl_primitive = DL_UNITDATA_IND; ctl.maxlen = DLPI_MAXDLBUF; ctl.len = 0; ctl.buf = (char *)buf; data.maxlen = dlen; data.len = 0; data.buf = (char *)dbuf; result = getmsg (fd, &ctl, &data, &flags); if (result < 0) { log_debug("dlpiunitdataind: %m"); return -1; } if (ctl.len < sizeof (dl_unitdata_ind_t) || dlp -> unitdata_ind.dl_primitive != DL_UNITDATA_IND) { return -1; } if (data.len <= 0) { return data.len; } /* Copy sender info */ if (saddr) { memcpy (saddr, (char *)buf + dlp -> unitdata_ind.dl_src_addr_offset, dlp -> unitdata_ind.dl_src_addr_length); } if (saddrlen) { *saddrlen = dlp -> unitdata_ind.dl_src_addr_length; } /* Copy destination info */ if (daddr) { memcpy (daddr, (char *)buf + dlp -> unitdata_ind.dl_dest_addr_offset, dlp -> unitdata_ind.dl_dest_addr_length); } if (daddrlen) { *daddrlen = dlp -> unitdata_ind.dl_dest_addr_length; } if (grpaddr) { *grpaddr = dlp -> unitdata_ind.dl_group_address; } return data.len; } #endif /* !USE_DLPI_HWADDR: USE_DLPI_RECEIVE || USE_DLPI_SEND */ /* * expected - see if we got what we wanted. */ static int expected (prim, dlp, msgflags) unsigned long prim; union DL_primitives *dlp; int msgflags; { if (msgflags != RS_HIPRI) { /* Message was not M_PCPROTO */ return -1; } if (dlp->dl_primitive != prim) { /* Incorrect/unexpected return message */ return -1; } return 0; } /* * strgetmsg - get a message from a stream, with timeout. */ static int strgetmsg (fd, ctlp, datap, flagsp, caller) struct strbuf *ctlp, *datap; char *caller; int *flagsp; int fd; { int result; struct pollfd pfd; int count; time_t now; time_t starttime; int to_msec; pfd.fd = fd; pfd.events = POLLPRI; /* We're only interested in knowing * when we can receive the next high * priority message. */ pfd.revents = 0; now = time (&starttime); while (now <= starttime + DLPI_MAXWAIT) { to_msec = ((starttime + DLPI_MAXWAIT) - now) * 1000; count = poll (&pfd, 1, to_msec); if (count == 0) { /* log_fatal ("strgetmsg: timeout"); */ return -1; } else if (count < 0) { if (errno == EAGAIN || errno == EINTR) { time (&now); continue; } else { /* log_fatal ("poll: %m"); */ return -1; } } else { break; } } /* * Set flags argument and issue getmsg (). */ *flagsp = 0; if ((result = getmsg (fd, ctlp, datap, flagsp)) < 0) { return result; } /* * Check for MOREDATA and/or MORECTL. */ if (result & (MORECTL|MOREDATA)) { return -1; } /* * Check for at least sizeof (long) control data portion. */ if (ctlp -> len < sizeof (long)) { return -1; } return 0; } #if defined(USE_DLPI_SEND) int can_unicast_without_arp (ip) struct interface_info *ip; { return 1; } int can_receive_unicast_unconfigured (ip) struct interface_info *ip; { return 1; } int supports_multiple_interfaces (ip) struct interface_info *ip; { return 1; } void maybe_setup_fallback () { isc_result_t status; struct interface_info *fbi = (struct interface_info *)0; if (setup_fallback (&fbi, MDL)) { if_register_fallback (fbi); status = omapi_register_io_object ((omapi_object_t *)fbi, if_readsocket, 0, fallback_discard, 0, 0); if (status != ISC_R_SUCCESS) log_fatal ("Can't register I/O handle for %s: %s", fbi -> name, isc_result_totext (status)); interface_dereference (&fbi, MDL); } } #endif /* USE_DLPI_SEND */ void get_hw_addr(const char *name, struct hardware *hw) { int sock, unit; long buf[DLPI_MAXDLBUF]; union DL_primitives *dlp; dlp = (union DL_primitives *)buf; /* * Open a DLPI device. */ sock = dlpiopen(name); if (sock < 0) { log_fatal("Can't open DLPI device for %s: %m", name); } /* * Submit a DL_INFO_REQ request, to find the dl_mac_type and * dl_provider_style */ if (dlpiinforeq(sock) < 0) { log_fatal("Can't request DLPI MAC type for %s: %m", name); } if (dlpiinfoack(sock, (char *)buf) < 0) { log_fatal("Can't get DLPI MAC type for %s: %m", name); } switch (dlp->info_ack.dl_mac_type) { case DL_CSMACD: /* IEEE 802.3 */ case DL_ETHER: hw->hbuf[0] = HTYPE_ETHER; break; case DL_TPR: hw->hbuf[0] = HTYPE_IEEE802; break; case DL_FDDI: hw->hbuf[0] = HTYPE_FDDI; break; default: log_fatal("%s: unsupported DLPI MAC type %lu", name, (unsigned long)dlp->info_ack.dl_mac_type); } if (dlp->info_ack.dl_provider_style == DL_STYLE2) { /* * Attach to the device. If this fails, the device * does not exist. */ unit = dlpiunit((char *)name); if (dlpiattachreq(sock, unit) < 0 || dlpiokack(sock, (char *)buf) < 0) { log_fatal("Can't attach DLPI device for %s: %m", name); } } /* * Submit a DL_PHYS_ADDR_REQ request, to find * the hardware address. */ if (dlpiphysaddrreq(sock, DL_CURR_PHYS_ADDR) < 0) { log_fatal("Can't request DLPI hardware address for %s: %m", name); } if (dlpiphysaddrack(sock, (char *)buf) < 0) { log_fatal("Can't get DLPI hardware address for %s: %m", name); } if (dlp->physaddr_ack.dl_addr_length < sizeof(hw->hbuf)) { memcpy(hw->hbuf+1, (char *)buf + dlp->physaddr_ack.dl_addr_offset, dlp->physaddr_ack.dl_addr_length); hw->hlen = dlp->physaddr_ack.dl_addr_length + 1; } else { memcpy(hw->hbuf+1, (char *)buf + dlp->physaddr_ack.dl_addr_offset, sizeof(hw->hbuf)-1); hw->hlen = sizeof(hw->hbuf); } close(sock); } #endif /* USE_DLPI_SEND || USE_DLPI_RECEIVE || USE_DLPI_HWADDR */