1: /*
2: * addrs_dlpi.c:
3: *
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.
7: *
8: * Like Solaris.
9: *
10: */
11:
12: #include "config.h"
13:
14: #ifdef HAVE_DLPI
15:
16: #include <stdio.h>
17: #include <stdlib.h>
18: #include <errno.h>
19: #include <unistd.h>
20: #include <string.h>
21: #include <fcntl.h>
22:
23: #include <sys/types.h>
24: #include <sys/sockio.h>
25: #include <sys/ioctl.h>
26: #include <sys/socket.h>
27: #include <sys/dlpi.h>
28: #include <net/if.h>
29:
30: #include "dlcommon.h"
31:
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);
35:
36: /*
37: * This function identifies the IP address and ethernet address for the interface
38: * specified
39: *
40: * This function returns -1 on catastrophic failure, or a bitwise OR of the
41: * following values:
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
44: * nuisance.
45: *
46: * 1 - Was able to get the ethernet address
47: * 2 - Was able to get the IP address
48: *
49: * This function should return 3 if all information was found
50: */
51:
52: int
53: get_addrs_dlpi(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr)
54: {
55: int got_hw_addr = 0;
56: int got_ip_addr = 0;
57:
58: int fd;
59: long buf[MAXDLBUF]; /* long aligned */
60: union DL_primitives *dlp;
61:
62: char *cp;
63: int unit_num = 0;
64: int sap = 0;
65:
66: char *devname = NULL;
67: char *devname2 = NULL;
68: char fulldevpath[256];
69:
70: struct ifreq ifr = {};
71:
72: /* -- */
73:
74: memset(if_hw_addr, 0, 6);
75:
76: // we want to be able to process either a fully qualified /dev/ge0
77: // type interface definition, or just ge0.
78:
79: if (strncmp(interface, "/dev/", strlen("/dev/")) == 0) {
80: devname = interface + strlen("/dev/");
81: } else {
82: devname = interface;
83: }
84:
85: strncpy2(fulldevpath, "/dev/", sizeof(fulldevpath)-1);
86: cp = strncat2(fulldevpath, interface, sizeof(fulldevpath));
87:
88: if (strlen(cp) != 0) {
89: fprintf(stderr, "device name buffer overflow %s\n", fulldevpath);
90: return -1;
91: }
92:
93: fprintf(stderr,"interface: %s\n", devname);
94:
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
98:
99: devname2 = strdup(fulldevpath);
100:
101: cp = split_dname(devname2, &unit_num);
102:
103: if (cp == NULL) {
104: free(devname2);
105: goto get_ip_address;
106: } else {
107: *cp = '\0'; /* null terminate devname2 right before numeric extension */
108: }
109:
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.
112:
113: if ((fd = open(devname2, O_RDWR)) < 0) {
114: if (errno != ENOENT) {
115: fprintf(stderr, "Couldn't open %s\n", devname2);
116: free(devname2);
117: goto get_ip_address;
118: } else {
119: if ((fd = open(fulldevpath, O_RDWR)) < 0) {
120: fprintf(stderr, "Couldn't open %s\n", fulldevpath);
121: free(devname2);
122: goto get_ip_address;
123: }
124: }
125: }
126:
127: free(devname2);
128: devname2 = NULL;
129:
130: /* Use the dlcommon functions to get access to the DLPI information for this
131: * interface. All of these functions exit() out on failure
132: */
133:
134: dlp = (union DL_primitives*) buf;
135:
136: /*
137: * DLPI attach to our low-level device
138: */
139:
140: dlattachreq(fd, unit_num);
141: dlokack(fd, buf);
142:
143: /*
144: * DLPI bind
145: */
146:
147: dlbindreq(fd, sap, 0, DL_CLDLS, 0, 0);
148: dlbindack(fd, buf);
149:
150: /*
151: * DLPI DL_INFO_REQ
152: */
153:
154: dlinforeq(fd);
155: dlinfoack(fd, buf);
156:
157: /*
158: printdlprim(dlp); // uncomment this to dump out info from DLPI
159: */
160:
161: if (dlp->info_ack.dl_addr_length + dlp->info_ack.dl_sap_length == 6) {
162: memcpy(if_hw_addr,
163: OFFADDR(dlp, dlp->info_ack.dl_addr_offset),
164: dlp->info_ack.dl_addr_length);
165: got_hw_addr = 1;
166: } else {
167: fprintf(stderr, "Error, bad length for hardware interface %s -- %d\n",
168: interface,
169: dlp->info_ack.dl_addr_length);
170: }
171:
172: close(fd);
173:
174: get_ip_address:
175:
176: /* Get the IP address of the interface */
177:
178: #ifdef SIOCGIFADDR
179:
180: fd = socket(PF_INET, SOCK_DGRAM, 0); /* any sort of IP socket will do */
181:
182: strncpy(ifr.ifr_name, interface, IFNAMSIZ);
183:
184: (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = AF_INET;
185:
186: if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
187: fprintf(stderr, "Error getting IP address for interface: %s\n", "ge0");
188: perror("ioctl(SIOCGIFADDR)");
189: } else {
190: memcpy(if_ip_addr, &((*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr), sizeof(struct in_addr));
191: got_ip_addr = 2;
192: }
193: #else
194: fprintf(stderr, "Cannot obtain IP address on this platform\n");
195: #endif
196:
197: close(fd);
198:
199: return got_hw_addr + got_ip_addr;
200: }
201:
202: /*
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
206: * number.
207: *
208: * Returns NULL on error, and fills "ebuf" with an error message.
209: */
210: char *
211: split_dname(char *device, int *unitp)
212: {
213: char *cp;
214: char *eos;
215: int unit;
216:
217: /* -- */
218:
219: /*
220: * Look for a number at the end of the device name string.
221: */
222:
223: cp = device + strlen(device) - 1;
224: if (*cp < '0' || *cp > '9') {
225: fprintf(stderr, "%s missing unit number", device);
226: return (NULL);
227: }
228:
229: /* Digits at end of string are unit number */
230: while (cp-1 >= device && *(cp-1) >= '0' && *(cp-1) <= '9')
231: cp--;
232:
233: unit = (int) strtol(cp, &eos, 10);
234: if (*eos != '\0') {
235: fprintf(stderr, "%s bad unit number", device);
236: return (NULL);
237: }
238: *unitp = unit;
239: return (cp);
240: }
241:
242: /*------------------------------------------------------------------------------
243: strncpy2()
244:
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.
249:
250: STRNCPY2() IS NOT A COMPATIBLE REPLACEMENT FOR STRNCPY()!!
251:
252: There are two reasons to use strncpy2().
253:
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
256: minus one.
257:
258: i.e.,
259:
260: char tempstring[MAXLINE];
261:
262: strncpy2(tempstring, my_own_string, MAXLINE - 1);
263:
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.
267:
268: The following example copies "abc" into tempstring, and NULL
269: terminates it.
270:
271: char tempstring[MAXLINE];
272:
273: strncpy2(tempstring, "abcdef123", 3);
274:
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
278: <src> string.
279:
280: ------------------------------------------------------------------------------*/
281: char *
282: strncpy2(char *dest, char *src, int n)
283: {
284: int
285: i = 0;
286:
287: char
288: *char_ptr;
289:
290: /* -- */
291:
292: if ((!dest) || (!src))
293: return(src);
294:
295: char_ptr = dest;
296:
297: while ((i++ < n) && *src)
298: *char_ptr++ = *src++;
299:
300: *char_ptr = '\0';
301:
302: return(src);
303: }
304:
305: /*------------------------------------------------------------------------------
306: strncat2()
307:
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.
311:
312: STRNCAT2() IS NOT A COMPATIBLE REPLACEMENT FOR STRNCAT()!
313:
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.
317:
318: strncat2() concatenates up to <n-1> - strlen(<dest>) characters from
319: <src> to <dest>.
320:
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
323: <dest>.
324:
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.
327:
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.
331:
332: ------------------------------------------------------------------------------*/
333: char *
334: strncat2(char *dest, char *src, int n)
335: {
336: int
337: i = 0;
338:
339: char
340: *dest_ptr,
341: *src_ptr;
342:
343: /* -- */
344:
345: if (!dest || !src)
346: return NULL;
347:
348: dest_ptr = dest;
349: src_ptr = src;
350:
351: /* i = 0 */
352:
353: while ((i < (n-1)) && *dest_ptr)
354: {
355: i++;
356: dest_ptr++;
357: }
358:
359: /* i is the number of characters in dest before the concatenation
360: operation.. a number between 0 and n-1 */
361:
362: while ((i++ < (n-1)) && *src_ptr)
363: *dest_ptr++ = *src_ptr++;
364:
365: /* i is the number of characters in dest after the concatenation
366: operation, or n if the concat operation got truncated.. a number
367: between 0 and n
368:
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
372: empty..
373:
374: We could just test src_ptr here, but that would report
375: a string truncation if <src> was empty, which we don't
376: necessarily want. */
377:
378: if ((i == n) && *src_ptr)
379: {
380: // we could log truncation here
381: }
382:
383: *dest_ptr = '\0';
384:
385: /* should point to a non-empty substring only if the concatenation
386: operation got truncated.
387:
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>. */
391:
392: return(src_ptr);
393: }
394:
395: #endif /* HAVE_DLPI */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>