summaryrefslogtreecommitdiff
path: root/addrs_dlpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'addrs_dlpi.c')
-rw-r--r--addrs_dlpi.c395
1 files changed, 395 insertions, 0 deletions
diff --git a/addrs_dlpi.c b/addrs_dlpi.c
new file mode 100644
index 0000000..188fef8
--- /dev/null
+++ b/addrs_dlpi.c
@@ -0,0 +1,395 @@
+/*
+ * addrs_dlpi.c:
+ *
+ * Provides the get_addrs_dlpi() function for use on systems that require
+ * the use of the System V STREAMS DataLink Programming Interface for
+ * acquiring low-level ethernet information about interfaces.
+ *
+ * Like Solaris.
+ *
+ */
+
+#include "config.h"
+
+#ifdef HAVE_DLPI
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/sockio.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/dlpi.h>
+#include <net/if.h>
+
+#include "dlcommon.h"
+
+extern char *split_dname(char *device, int *unitp);
+extern char *strncpy2(char *dest, char *src, int n);
+extern char *strncat2(char *dest, char *src, int n);
+
+/*
+ * This function identifies the IP address and ethernet address for the interface
+ * specified
+ *
+ * This function returns -1 on catastrophic failure, or a bitwise OR of the
+ * following values:
+ * XXX: change this to perfom "best effort" identification of addresses.
+ * Failure to find an address - for whatever reason - isn't fatal, just a
+ * nuisance.
+ *
+ * 1 - Was able to get the ethernet address
+ * 2 - Was able to get the IP address
+ *
+ * This function should return 3 if all information was found
+ */
+
+int
+get_addrs_dlpi(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr)
+{
+ int got_hw_addr = 0;
+ int got_ip_addr = 0;
+
+ int fd;
+ long buf[MAXDLBUF]; /* long aligned */
+ union DL_primitives *dlp;
+
+ char *cp;
+ int unit_num = 0;
+ int sap = 0;
+
+ char *devname = NULL;
+ char *devname2 = NULL;
+ char fulldevpath[256];
+
+ struct ifreq ifr = {};
+
+ /* -- */
+
+ memset(if_hw_addr, 0, 6);
+
+ // we want to be able to process either a fully qualified /dev/ge0
+ // type interface definition, or just ge0.
+
+ if (strncmp(interface, "/dev/", strlen("/dev/")) == 0) {
+ devname = interface + strlen("/dev/");
+ } else {
+ devname = interface;
+ }
+
+ strncpy2(fulldevpath, "/dev/", sizeof(fulldevpath)-1);
+ cp = strncat2(fulldevpath, interface, sizeof(fulldevpath));
+
+ if (strlen(cp) != 0) {
+ fprintf(stderr, "device name buffer overflow %s\n", fulldevpath);
+ return -1;
+ }
+
+ fprintf(stderr,"interface: %s\n", devname);
+
+ // on Solaris, even though we are wanting to talk to ethernet device
+ // ge0, we have to open /dev/ge, then bind to unit 0. Dupe our
+ // full path, then identify and cut off the unit number
+
+ devname2 = strdup(fulldevpath);
+
+ cp = split_dname(devname2, &unit_num);
+
+ if (cp == NULL) {
+ free(devname2);
+ goto get_ip_address;
+ } else {
+ *cp = '\0'; /* null terminate devname2 right before numeric extension */
+ }
+
+ // devname2 should now be something akin to /dev/ge. Try to open
+ // it, and if it fails, fall back to the full /dev/ge0.
+
+ if ((fd = open(devname2, O_RDWR)) < 0) {
+ if (errno != ENOENT) {
+ fprintf(stderr, "Couldn't open %s\n", devname2);
+ free(devname2);
+ goto get_ip_address;
+ } else {
+ if ((fd = open(fulldevpath, O_RDWR)) < 0) {
+ fprintf(stderr, "Couldn't open %s\n", fulldevpath);
+ free(devname2);
+ goto get_ip_address;
+ }
+ }
+ }
+
+ free(devname2);
+ devname2 = NULL;
+
+ /* Use the dlcommon functions to get access to the DLPI information for this
+ * interface. All of these functions exit() out on failure
+ */
+
+ dlp = (union DL_primitives*) buf;
+
+ /*
+ * DLPI attach to our low-level device
+ */
+
+ dlattachreq(fd, unit_num);
+ dlokack(fd, buf);
+
+ /*
+ * DLPI bind
+ */
+
+ dlbindreq(fd, sap, 0, DL_CLDLS, 0, 0);
+ dlbindack(fd, buf);
+
+ /*
+ * DLPI DL_INFO_REQ
+ */
+
+ dlinforeq(fd);
+ dlinfoack(fd, buf);
+
+ /*
+ printdlprim(dlp); // uncomment this to dump out info from DLPI
+ */
+
+ if (dlp->info_ack.dl_addr_length + dlp->info_ack.dl_sap_length == 6) {
+ memcpy(if_hw_addr,
+ OFFADDR(dlp, dlp->info_ack.dl_addr_offset),
+ dlp->info_ack.dl_addr_length);
+ got_hw_addr = 1;
+ } else {
+ fprintf(stderr, "Error, bad length for hardware interface %s -- %d\n",
+ interface,
+ dlp->info_ack.dl_addr_length);
+ }
+
+ close(fd);
+
+ get_ip_address:
+
+ /* Get the IP address of the interface */
+
+#ifdef SIOCGIFADDR
+
+ fd = socket(PF_INET, SOCK_DGRAM, 0); /* any sort of IP socket will do */
+
+ strncpy(ifr.ifr_name, interface, IFNAMSIZ);
+
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = AF_INET;
+
+ if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
+ fprintf(stderr, "Error getting IP address for interface: %s\n", "ge0");
+ perror("ioctl(SIOCGIFADDR)");
+ } else {
+ memcpy(if_ip_addr, &((*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr), sizeof(struct in_addr));
+ got_ip_addr = 2;
+ }
+#else
+ fprintf(stderr, "Cannot obtain IP address on this platform\n");
+#endif
+
+ close(fd);
+
+ return got_hw_addr + got_ip_addr;
+}
+
+/*
+ * Split a device name into a device type name and a unit number;
+ * return the a pointer to the beginning of the unit number, which
+ * is the end of the device type name, and set "*unitp" to the unit
+ * number.
+ *
+ * Returns NULL on error, and fills "ebuf" with an error message.
+ */
+char *
+split_dname(char *device, int *unitp)
+{
+ char *cp;
+ char *eos;
+ int unit;
+
+ /* -- */
+
+ /*
+ * Look for a number at the end of the device name string.
+ */
+
+ cp = device + strlen(device) - 1;
+ if (*cp < '0' || *cp > '9') {
+ fprintf(stderr, "%s missing unit number", device);
+ return (NULL);
+ }
+
+ /* Digits at end of string are unit number */
+ while (cp-1 >= device && *(cp-1) >= '0' && *(cp-1) <= '9')
+ cp--;
+
+ unit = (int) strtol(cp, &eos, 10);
+ if (*eos != '\0') {
+ fprintf(stderr, "%s bad unit number", device);
+ return (NULL);
+ }
+ *unitp = unit;
+ return (cp);
+}
+
+/*------------------------------------------------------------------------------
+ strncpy2()
+
+strncpy2() is like strncpy(), except that strncpy2() will always
+insure that the <dest> buffer is null terminated. strncpy() will not
+NULL terminate the destination buffer if the <src> string is <n>
+characters long or longer, not counting the terminating NULL character.
+
+ STRNCPY2() IS NOT A COMPATIBLE REPLACEMENT FOR STRNCPY()!!
+
+There are two reasons to use strncpy2().
+
+The first reason is to guarantee that <dest> buffer's bounds are not
+violated. In this case, <n> should be the size of the <dest> buffer
+minus one.
+
+i.e.,
+
+char tempstring[MAXLINE];
+
+strncpy2(tempstring, my_own_string, MAXLINE - 1);
+
+The second reason is to copy a specific number of characters from
+<src> to <dest>. In this case, <n> should be the number of characters
+you want to transfer, not including the terminating NULL character.
+
+The following example copies "abc" into tempstring, and NULL
+terminates it.
+
+char tempstring[MAXLINE];
+
+strncpy2(tempstring, "abcdef123", 3);
+
+strncpy2() returns a pointer to the first character in <src> that was
+not copied to <dest>. If all of <src> was copied to <dest>,
+strncpy2() will return a pointer to the NULL character terminating the
+<src> string.
+
+------------------------------------------------------------------------------*/
+char *
+strncpy2(char *dest, char *src, int n)
+{
+ int
+ i = 0;
+
+ char
+ *char_ptr;
+
+ /* -- */
+
+ if ((!dest) || (!src))
+ return(src);
+
+ char_ptr = dest;
+
+ while ((i++ < n) && *src)
+ *char_ptr++ = *src++;
+
+ *char_ptr = '\0';
+
+ return(src);
+}
+
+/*------------------------------------------------------------------------------
+ strncat2()
+
+Similar to strncat except that <n> is the size of the <dest> buffer
+(INCLUDING SPACE FOR THE TRAILING NULL CHAR), NOT the number of
+characters to add to the buffer.
+
+ STRNCAT2() IS NOT A COMPATIBLE REPLACEMENT FOR STRNCAT()!
+
+strncat2() always guarantees that the <dest> will be null terminated, and that
+the buffer limits will be honored. strncat2() will not write even one
+byte beyond the end of the <dest> buffer.
+
+strncat2() concatenates up to <n-1> - strlen(<dest>) characters from
+<src> to <dest>.
+
+So if the <dest> buffer has a size of 20 bytes (including trailing NULL),
+and <dest> contains a 19 character string, nothing will be done to
+<dest>.
+
+If the string in <dest> is longer than <n-1> characters upon entry to
+strncat2(), <dest> will be truncated after the <n-1>th character.
+
+strncat2() returns a pointer to the first character in the src buffer that
+was not copied into dest.. so if strncat2() returns a non-zero character,
+string truncation occurred in the concat operation.
+
+------------------------------------------------------------------------------*/
+char *
+strncat2(char *dest, char *src, int n)
+{
+ int
+ i = 0;
+
+ char
+ *dest_ptr,
+ *src_ptr;
+
+ /* -- */
+
+ if (!dest || !src)
+ return NULL;
+
+ dest_ptr = dest;
+ src_ptr = src;
+
+ /* i = 0 */
+
+ while ((i < (n-1)) && *dest_ptr)
+ {
+ i++;
+ dest_ptr++;
+ }
+
+ /* i is the number of characters in dest before the concatenation
+ operation.. a number between 0 and n-1 */
+
+ while ((i++ < (n-1)) && *src_ptr)
+ *dest_ptr++ = *src_ptr++;
+
+ /* i is the number of characters in dest after the concatenation
+ operation, or n if the concat operation got truncated.. a number
+ between 0 and n
+
+ We need to check src_ptr here because i will be equal to n if
+ <dest> was full before the concatenation operation started (which
+ effectively causes instant truncation even if the <src> string is
+ empty..
+
+ We could just test src_ptr here, but that would report
+ a string truncation if <src> was empty, which we don't
+ necessarily want. */
+
+ if ((i == n) && *src_ptr)
+ {
+ // we could log truncation here
+ }
+
+ *dest_ptr = '\0';
+
+ /* should point to a non-empty substring only if the concatenation
+ operation got truncated.
+
+ If src_ptr points to an empty string, the operation always
+ succeeded, either due to an empty <src> or because of
+ sufficient room in <dest>. */
+
+ return(src_ptr);
+}
+
+#endif /* HAVE_DLPI */