Annotation of embedaddon/iftop/addrs_dlpi.c, revision 1.1

1.1     ! misho       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>