]> gitweb.fperrin.net Git - iftop.git/blob - addrs_dlpi.c
Import iftop-1.0pre4
[iftop.git] / addrs_dlpi.c
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 */