4 * Provides the get_addrs_dlpi() function for use on systems that require
5 * the use of the System V STREAMS DataLink Programming Interface for
6 * acquiring low-level ethernet information about interfaces.
23 #include <sys/types.h>
24 #include <sys/sockio.h>
25 #include <sys/ioctl.h>
26 #include <sys/socket.h>
32 extern char *split_dname(char *device, int *unitp);
33 extern char *strncpy2(char *dest, char *src, int n);
34 extern char *strncat2(char *dest, char *src, int n);
37 * This function identifies the IP address and ethernet address for the interface
40 * This function returns -1 on catastrophic failure, or a bitwise OR of the
42 * XXX: change this to perfom "best effort" identification of addresses.
43 * Failure to find an address - for whatever reason - isn't fatal, just a
46 * 1 - Was able to get the ethernet address
47 * 2 - Was able to get the IP address
49 * This function should return 3 if all information was found
53 get_addrs_dlpi(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr)
59 long buf[MAXDLBUF]; /* long aligned */
60 union DL_primitives *dlp;
67 char *devname2 = NULL;
68 char fulldevpath[256];
70 struct ifreq ifr = {};
74 memset(if_hw_addr, 0, 6);
76 // we want to be able to process either a fully qualified /dev/ge0
77 // type interface definition, or just ge0.
79 if (strncmp(interface, "/dev/", strlen("/dev/")) == 0) {
80 devname = interface + strlen("/dev/");
85 strncpy2(fulldevpath, "/dev/", sizeof(fulldevpath)-1);
86 cp = strncat2(fulldevpath, interface, sizeof(fulldevpath));
88 if (strlen(cp) != 0) {
89 fprintf(stderr, "device name buffer overflow %s\n", fulldevpath);
93 fprintf(stderr,"interface: %s\n", devname);
95 // on Solaris, even though we are wanting to talk to ethernet device
96 // ge0, we have to open /dev/ge, then bind to unit 0. Dupe our
97 // full path, then identify and cut off the unit number
99 devname2 = strdup(fulldevpath);
101 cp = split_dname(devname2, &unit_num);
107 *cp = '\0'; /* null terminate devname2 right before numeric extension */
110 // devname2 should now be something akin to /dev/ge. Try to open
111 // it, and if it fails, fall back to the full /dev/ge0.
113 if ((fd = open(devname2, O_RDWR)) < 0) {
114 if (errno != ENOENT) {
115 fprintf(stderr, "Couldn't open %s\n", devname2);
119 if ((fd = open(fulldevpath, O_RDWR)) < 0) {
120 fprintf(stderr, "Couldn't open %s\n", fulldevpath);
130 /* Use the dlcommon functions to get access to the DLPI information for this
131 * interface. All of these functions exit() out on failure
134 dlp = (union DL_primitives*) buf;
137 * DLPI attach to our low-level device
140 dlattachreq(fd, unit_num);
147 dlbindreq(fd, sap, 0, DL_CLDLS, 0, 0);
158 printdlprim(dlp); // uncomment this to dump out info from DLPI
161 if (dlp->info_ack.dl_addr_length + dlp->info_ack.dl_sap_length == 6) {
163 OFFADDR(dlp, dlp->info_ack.dl_addr_offset),
164 dlp->info_ack.dl_addr_length);
167 fprintf(stderr, "Error, bad length for hardware interface %s -- %d\n",
169 dlp->info_ack.dl_addr_length);
176 /* Get the IP address of the interface */
180 fd = socket(PF_INET, SOCK_DGRAM, 0); /* any sort of IP socket will do */
182 strncpy(ifr.ifr_name, interface, IFNAMSIZ);
184 (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = AF_INET;
186 if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
187 fprintf(stderr, "Error getting IP address for interface: %s\n", "ge0");
188 perror("ioctl(SIOCGIFADDR)");
190 memcpy(if_ip_addr, &((*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr), sizeof(struct in_addr));
194 fprintf(stderr, "Cannot obtain IP address on this platform\n");
199 return got_hw_addr + got_ip_addr;
203 * Split a device name into a device type name and a unit number;
204 * return the a pointer to the beginning of the unit number, which
205 * is the end of the device type name, and set "*unitp" to the unit
208 * Returns NULL on error, and fills "ebuf" with an error message.
211 split_dname(char *device, int *unitp)
220 * Look for a number at the end of the device name string.
223 cp = device + strlen(device) - 1;
224 if (*cp < '0' || *cp > '9') {
225 fprintf(stderr, "%s missing unit number", device);
229 /* Digits at end of string are unit number */
230 while (cp-1 >= device && *(cp-1) >= '0' && *(cp-1) <= '9')
233 unit = (int) strtol(cp, &eos, 10);
235 fprintf(stderr, "%s bad unit number", device);
242 /*------------------------------------------------------------------------------
245 strncpy2() is like strncpy(), except that strncpy2() will always
246 insure that the <dest> buffer is null terminated. strncpy() will not
247 NULL terminate the destination buffer if the <src> string is <n>
248 characters long or longer, not counting the terminating NULL character.
250 STRNCPY2() IS NOT A COMPATIBLE REPLACEMENT FOR STRNCPY()!!
252 There are two reasons to use strncpy2().
254 The first reason is to guarantee that <dest> buffer's bounds are not
255 violated. In this case, <n> should be the size of the <dest> buffer
260 char tempstring[MAXLINE];
262 strncpy2(tempstring, my_own_string, MAXLINE - 1);
264 The second reason is to copy a specific number of characters from
265 <src> to <dest>. In this case, <n> should be the number of characters
266 you want to transfer, not including the terminating NULL character.
268 The following example copies "abc" into tempstring, and NULL
271 char tempstring[MAXLINE];
273 strncpy2(tempstring, "abcdef123", 3);
275 strncpy2() returns a pointer to the first character in <src> that was
276 not copied to <dest>. If all of <src> was copied to <dest>,
277 strncpy2() will return a pointer to the NULL character terminating the
280 ------------------------------------------------------------------------------*/
282 strncpy2(char *dest, char *src, int n)
292 if ((!dest) || (!src))
297 while ((i++ < n) && *src)
298 *char_ptr++ = *src++;
305 /*------------------------------------------------------------------------------
308 Similar to strncat except that <n> is the size of the <dest> buffer
309 (INCLUDING SPACE FOR THE TRAILING NULL CHAR), NOT the number of
310 characters to add to the buffer.
312 STRNCAT2() IS NOT A COMPATIBLE REPLACEMENT FOR STRNCAT()!
314 strncat2() always guarantees that the <dest> will be null terminated, and that
315 the buffer limits will be honored. strncat2() will not write even one
316 byte beyond the end of the <dest> buffer.
318 strncat2() concatenates up to <n-1> - strlen(<dest>) characters from
321 So if the <dest> buffer has a size of 20 bytes (including trailing NULL),
322 and <dest> contains a 19 character string, nothing will be done to
325 If the string in <dest> is longer than <n-1> characters upon entry to
326 strncat2(), <dest> will be truncated after the <n-1>th character.
328 strncat2() returns a pointer to the first character in the src buffer that
329 was not copied into dest.. so if strncat2() returns a non-zero character,
330 string truncation occurred in the concat operation.
332 ------------------------------------------------------------------------------*/
334 strncat2(char *dest, char *src, int n)
353 while ((i < (n-1)) && *dest_ptr)
359 /* i is the number of characters in dest before the concatenation
360 operation.. a number between 0 and n-1 */
362 while ((i++ < (n-1)) && *src_ptr)
363 *dest_ptr++ = *src_ptr++;
365 /* i is the number of characters in dest after the concatenation
366 operation, or n if the concat operation got truncated.. a number
369 We need to check src_ptr here because i will be equal to n if
370 <dest> was full before the concatenation operation started (which
371 effectively causes instant truncation even if the <src> string is
374 We could just test src_ptr here, but that would report
375 a string truncation if <src> was empty, which we don't
378 if ((i == n) && *src_ptr)
380 // we could log truncation here
385 /* should point to a non-empty substring only if the concatenation
386 operation got truncated.
388 If src_ptr points to an empty string, the operation always
389 succeeded, either due to an empty <src> or because of
390 sufficient room in <dest>. */
395 #endif /* HAVE_DLPI */