aboutsummaryrefslogtreecommitdiff
path: root/common/dlpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/dlpi.c')
-rw-r--r--common/dlpi.c1416
1 files changed, 1416 insertions, 0 deletions
diff --git a/common/dlpi.c b/common/dlpi.c
new file mode 100644
index 0000000..8f2c73d
--- /dev/null
+++ b/common/dlpi.c
@@ -0,0 +1,1416 @@
+/* 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
+ * <info@isc.org>
+ * https://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 ``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 <sys/ioctl.h>
+# include <sys/time.h>
+# include <sys/dlpi.h>
+# include <stropts.h>
+# ifdef USE_DLPI_PFMOD
+# include <sys/pfmod.h>
+# endif
+#include <poll.h>
+#include <errno.h>
+
+# 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
+
+#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];
+#endif
+ double ih [1536 / sizeof (double)];
+ unsigned char *dbuf = (unsigned char *)ih;
+ unsigned dbuflen;
+ unsigned char dstaddr [DLPI_MAXDLADDR];
+ unsigned addrlen;
+ int result;
+ int fudge;
+
+ 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;
+#else
+ fudge = 0;
+#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, <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 */
+
+
+/*
+ * 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 */