]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
DLPI support, contributed by Eric James Negaard.
authorTed Lemon <source@isc.org>
Sun, 15 Mar 1998 20:55:19 +0000 (20:55 +0000)
committerTed Lemon <source@isc.org>
Sun, 15 Mar 1998 20:55:19 +0000 (20:55 +0000)
common/dlpi.c [new file with mode: 0644]

diff --git a/common/dlpi.c b/common/dlpi.c
new file mode 100644 (file)
index 0000000..f30a5af
--- /dev/null
@@ -0,0 +1,1231 @@
+/* dlpi.c
+   Data Link Provider Interface (DLPI) network interface code. */
+
+/*
+ * Copyright (c) 1998 The Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ *    of its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Eric James Negaard, <lmdejn@lmd.ericsson.se>.  To learn more about
+ * the Internet Software Consortium, see ``http://www.vix.com/isc''.
+ */
+
+/*
+ * 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.
+ */
+
+#include "dhcpd.h"
+
+#if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE)
+#include "dhcpd.h"
+
+#ifdef USE_DLPI
+#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))
+#endif
+
+static 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 */
+#define DLPI_DEFAULTSAP                0x0800  /* IP protocol */
+
+static void dlpi_makeaddr PROTO ((unsigned char *physaddr, int physaddrlen,
+                                 unsigned char *sap, int saplen,
+                                 unsigned char *buf));
+static void dlpi_parseaddr PROTO ((unsigned char *buf,
+                                  unsigned char *physaddr,
+                                  int physaddrlen, unsigned char *sap,
+                                  int saplen));
+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_POLL
+static void    sigalrm PROTO ((int sig));
+#endif
+static 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_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) {
+           error ("Can't open DLPI device for %s: %m", info -> name);
+       }
+
+       /*
+        * Get information about the provider.
+        */
+
+       /*
+        * 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) {
+           error ("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.htype = HTYPE_ETHER;
+               break;
+             default:
+               error ("%s: unknown DLPI MAC type %d",
+                      info -> name,
+                      dlp -> info_ack.dl_mac_type);
+               break;
+           }
+       }
+
+       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) {
+               error ("Can't attach DLPI device for %s: %m", info -> name);
+           }
+       }
+
+       /*
+        * Bind to the IP service access point (SAP), connectionless (CLDLS).
+        */
+       if (dlpibindreq (sock, DLPI_DEFAULTSAP, 0, DL_CLDLS, 0, 0) < 0
+           || dlpibindack (sock, (char *)buf) < 0) {
+           error ("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) {
+           error ("Can't get DLPI hardware address for %s: %m",
+                  info -> name);
+       }
+
+       info -> hw_address.hlen = dlp -> physaddr_ack.dl_addr_length;
+       memcpy (info -> hw_address.haddr,
+               (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) {
+           error ("Can't set DLPI RAW mode for %s: %m",
+                  info -> name);
+       }
+#endif
+
+#ifdef USE_DLPI_PFMOD
+       if (ioctl (sock, I_PUSH, "pfmod") < 0) {
+           error ("Can't push packet filter onto DLPI for %s: %m",
+                  info -> name);
+       }
+#endif
+
+       return sock;
+}
+
+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;
+    }
+}
+
+#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) {
+           error ("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)
+               note ("Sending on   %s/%s/%s",
+                     DLPI_MODNAME,
+                     print_hw_addr (info -> hw_address.htype,
+                                    info -> hw_address.hlen,
+                                    info -> hw_address.haddr),
+                     (info -> shared_network ?
+                      info -> shared_network -> name : "unattached"));
+
+#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
+}
+#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;
+#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;
+
+#ifdef USE_DLPI_RAW
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 6;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = htons (ETHERTYPE_IP);
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP);
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 11;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSH00FF + ENF_AND;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_CAND;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 18;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = local_port;
+#else
+       /*
+        * 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.
+        */
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP);
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 4;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSH00FF + ENF_AND;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_CAND;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 11;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
+       pf.Pf_Filter [pf.Pf_FilterLen++] = local_port;
+#endif
+
+       /* Install the filter... */
+       if (strioctl (info -> rfdesc, PFIOCSETF, INFTIM,
+                     sizeof (pf), (char *)&pf) < 0) {
+           error ("Can't set PFMOD receive filter on %s: %m", info -> name);
+       }
+#endif
+
+        if (!quiet_interface_discovery)
+               note ("Listening on %s/%s/%s",
+                     DLPI_MODNAME,
+                     print_hw_addr (info -> hw_address.htype,
+                                    info -> hw_address.hlen,
+                                    info -> hw_address.haddr),
+                     (info -> shared_network ?
+                      info -> shared_network -> name : "unattached"));
+
+#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
+}
+#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;
+{
+       int dbuflen;
+       unsigned char dbuf [1536];
+       unsigned char sap [2];
+       unsigned char dstaddr [DLPI_MAXDLADDR];
+       unsigned addrlen;
+       int saplen;
+       int rslt;
+
+       dbuflen = 0;
+
+       /* Assemble the headers... */
+#ifdef USE_DLPI_RAW
+       assemble_hw_header (interface, dbuf, &dbuflen, hto);
+#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
+       rslt = write (interface -> wfdesc, dbuf, dbuflen);
+#else
+       /* XXX: Assumes ethernet, with two byte SAP */
+       sap [0] = 0;            /* ETHERTYPE_IP, high byte */
+       sap [1] = 0x80;         /* ETHERTYPE_IP, low byte */
+       saplen = -2;            /* -2 indicates a two byte SAP at the end
+                                  of the address */
+
+       /* Setup the destination address */
+       if (hto && hto -> hlen == interface -> hw_address.hlen) {
+           dlpi_makeaddr (hto -> haddr, hto -> hlen, sap, saplen, dstaddr);
+       } else {
+           /* XXX: Assumes broadcast addr is all ones */
+           /* Really should get the broadcast address as part of the
+            * dlpiinforeq, and store it somewhere in the interface structure.
+            */
+           unsigned char bcast_ether [DLPI_MAXDLADDR];
+
+           memset ((char *)bcast_ether, 0xFF, interface -> hw_address.hlen);
+           dlpi_makeaddr (bcast_ether, interface -> hw_address.hlen,
+                        sap, saplen, dstaddr);
+       }
+       addrlen = interface -> hw_address.hlen + ABS (saplen);
+
+       /* Send the packet down the wire... */
+       rslt = dlpiunitdatareq (interface -> wfdesc, dstaddr, addrlen,
+                               0, 0, dbuf, dbuflen);
+#endif
+       return rslt;
+}
+#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 sap [2];
+       unsigned char srcaddr [DLPI_MAXDLADDR];
+       unsigned long srcaddrlen;
+       int saplen;
+       int flags = 0;
+       int length = 0;
+       int offset = 0;
+       int bufix = 0;
+       int rslt;
+       
+#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) {
+           return length;
+       }
+
+#ifndef USE_DLPI_RAW
+       /* Copy sender info */
+       /* XXX: Assumes ethernet, where SAP comes at end of haddr */
+       saplen = -2;
+       if (hfrom && srcaddrlen == ABS(saplen) + interface -> hw_address.hlen) {
+           hfrom -> htype = interface -> hw_address.htype;
+           hfrom -> hlen = interface -> hw_address.hlen;
+           dlpi_parseaddr (srcaddr, hfrom -> haddr,
+                           interface -> hw_address.hlen, sap, saplen);
+       } else if (hfrom) {
+           memset ((char *)hfrom, '\0', sizeof (*hfrom));
+       }
+#endif
+
+       /* 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, (unsigned char *)0, length);
+
+       /* If the IP or UDP checksum was bad, skip the packet... */
+       if (offset < 0) {
+           return 0;
+       }
+
+       bufix += offset;
+       length -= offset;
+
+       /* Copy out the data in the packet... */
+       memcpy (buf, &dbuf [bufix], length);
+       return length;
+}
+#endif
+
+/* Common DLPI routines ...
+ *
+ * Written by Eric James Negaard, <lmdejn@lmd.ericsson.se>
+ *
+ * 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 */
+
+static void dlpi_makeaddr (physaddr, physaddrlen, sap, saplen, buf)
+       unsigned char *physaddr;
+       int physaddrlen;
+       unsigned char *sap;
+       int saplen;
+       unsigned char *buf;
+{
+       /*
+        * If the saplen is negative, the SAP goes at the end of the address,
+        * otherwise it goes at the beginning.
+        */
+       if (saplen >= 0) {
+               memcpy ((char *)buf, (char *)sap, saplen);
+               memcpy ((char *)&buf [saplen], (char *)physaddr, physaddrlen);
+       } else {
+               memcpy ((char *)buf, (char *)physaddr, physaddrlen);
+               memcpy ((char *)&buf [physaddrlen], (char *)sap, 0 - saplen);
+       }
+}
+
+static void dlpi_parseaddr (buf, physaddr, physaddrlen, sap, saplen)
+       unsigned char *buf;
+       unsigned char *physaddr;
+       int physaddrlen;
+       unsigned char *sap;
+       int saplen;
+{
+       /*
+        * If the saplen is negative, the SAP is at the end of the address,
+        * otherwise it is at the beginning.
+        */
+       if (saplen >= 0) {
+               memcpy ((char *)sap, (char *)buf, saplen);
+               memcpy ((char *)physaddr, (char *)&buf [saplen], physaddrlen);
+       } else {
+               memcpy ((char *)physaddr, (char *)buf, physaddrlen);
+               memcpy ((char *)sap, (char *)&buf [physaddrlen], 0 - saplen);
+       }
+}
+
+/*
+ * Parse an interface name and extract the unit number
+ */
+
+static int dlpiunit (ifname)
+       char *ifname;
+{
+       int fd;
+       char *cp, *dp, *ep;
+       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 (ifname)
+       char *ifname;
+{
+       char devname [50];
+       char *cp, *dp, *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);
+}
+
+/*
+ * dlpiunbindreq - send a request to unbind.
+ */
+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.
+ */
+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);
+}
+
+
+/*
+ * 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) < 0) {
+               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) < 0) {
+               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) < 0) {
+               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) < 0) {
+               return -1;
+       }
+
+       if (ctl.len < sizeof (dl_phys_addr_ack_t)) {
+               /* Returned structure is too short */
+               return -1;
+       }
+       
+       return 0;
+}
+
+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 rslt;
+
+       /* 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;
+       
+       rslt = getmsg (fd, &ctl, &data, &flags);
+       
+       if (rslt != 0) {
+               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, &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, &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;
+}
+
+/*
+ * 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 0;
+       }
+
+       if (dlp -> dl_primitive != prim) {
+               /* Incorrect/unexpected return message */
+               return 0;
+       }
+       
+       return 1;
+}
+
+/*
+ * 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 rslt;
+#ifdef USE_POLL
+       struct pollfd pfd;
+       int count;
+       time_t now;
+       time_t starttime;
+       int to_msec;
+#endif
+       
+#ifdef USE_POLL
+       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) {
+                       /* error ("strgetmsg: timeout"); */
+                       return -1;
+               } else if (count < 0) {
+                       if (errno == EAGAIN || errno == EINTR) {
+                               time (&now);
+                               continue;
+                       } else {
+                               /* error ("poll: %m"); */
+                               return -1;
+                       }
+               } else {
+                       break;
+               }
+       }
+#else  /* defined (USE_POLL) */
+       /*
+        * Start timer.  Can't use select, since it might return true if there
+        * were non High-Priority data available on the stream.
+        */
+       (void) sigset (SIGALRM, sigalrm);
+       
+       if (alarm (DLPI_MAXWAIT) < 0) {
+               /* error ("alarm: %m"); */
+               return -1;
+       }
+#endif /* !defined (USE_POLL) */
+
+       /*
+        * Set flags argument and issue getmsg ().
+        */
+       *flagsp = 0;
+       if ((rslt = getmsg (fd, ctlp, datap, flagsp)) < 0) {
+               return rslt;
+       }
+
+#ifndef USE_POLL
+       /*
+        * Stop timer.
+        */     
+       if (alarm (0) < 0) {
+               /* error ("alarm: %m"); */
+               return -1;
+       }
+#endif
+
+       /*
+        * Check for MOREDATA and/or MORECTL.
+        */
+       if (rslt & (MORECTL|MOREDATA)) {
+               return -1;
+       }
+
+       /*
+        * Check for at least sizeof (long) control data portion.
+        */
+       if (ctlp -> len < sizeof (long)) {
+               return -1;
+       }
+
+       return 0;
+}
+
+#ifndef USE_POLL
+/*
+ * sigalrm - handle alarms.
+ */
+static void sigalrm (sig)
+       int sig;
+{
+       fprintf (stderr, "strgetmsg: timeout");
+       exit (1);
+}
+#endif /* !defined (USE_POLL) */
+
+#endif /* USE_DLPI */