Solaris/autoconf patch from Jonathan Abbey <jonabbey@ganymeta.org>
This commit is contained in:
395
addrs_dlpi.c
Normal file
395
addrs_dlpi.c
Normal file
@@ -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:
|
||||
*
|
||||
* 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,"if: %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);
|
||||
return -1;
|
||||
} 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);
|
||||
return -1;
|
||||
} else {
|
||||
if ((fd = open(fulldevpath, O_RDWR)) < 0) {
|
||||
fprintf(stderr, "Couldn't open %s\n", fulldevpath);
|
||||
free(devname2);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
/* 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)");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
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 */
|
||||
Reference in New Issue
Block a user