diff options
Diffstat (limited to 'addrs_dlpi.c')
-rw-r--r-- | addrs_dlpi.c | 395 |
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 */ |