/***********************************************************/ /* Functions that uses the system devices to determine the */ /* MTU value from a network, the loopback device, the IP */ /* address from an outgoing interface, etc. */ /***********************************************************/ /* To solve: */ /* - Under Solaris 2.5.1 it's not possible to get the */ /* outgoing interface without reading the routing table, */ /* get the interface name and get the netmask from */ /* /etc/netmasks. This implies (among other things) the */ /* use of STREAMS with the /dev/ip device ... */ /* Maybe some day ;) */ /***********************************************************/ #ifndef lint static const char rcsid[] = "$Id: device.c,v 1.3 2001/04/16 15:47:00 slay Exp $"; #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_SYS_SOCKIO_H #include #endif #include #ifdef HAVE_NET_IF_H #include #endif #include #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include #else #ifdef HAVE_NETINET_IN_SYSTEM_H #include #endif #endif #include #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_STRINGS_H #include #endif #ifdef HAVE_BSTRING_H #include #endif #include #ifdef HAVE_UNISTD_H #include #endif #include "device.h" /***************************************************/ /* Try to get the mtu, name and IP address of the */ /* outgoing interface. */ /* First: Try it using the "Stevens way". */ /* Next: Try it fetching the hostname of the host, */ /* then his IP address and then the network */ /* device. */ /* Next: Trying to fetch the first non loopback */ /* network device. */ /***************************************************/ /* No value return. */ /***************************************************/ void where2route( struct sockaddr_in *destination ) { struct hostent *thishost = NULL; char thisname[MAXHOSTNAMELEN+1]; struct sockaddr_in *aux; if ( get_iface_out( destination, (struct sockaddr_in *)&packet.iface2route.ip ) ) { thishost = gethostbyname(thisname); if ( !thishost || gethostname(thisname, MAXHOSTNAMELEN) ) { if (!first_nonloopback((struct mi_ifaz *)&packet.iface2route) ) go_out(1, "Unable to select an interface to route throughout"); } else { aux = (struct sockaddr_in *)&packet.iface2route.ip; copymem( thishost->h_addr_list[0], (char *)&aux->sin_addr.s_addr, sizeof(struct in_addr) ); if ( !look4dev((struct mi_ifaz *)&packet.iface2route) ) { if (!first_nonloopback((struct mi_ifaz *)&packet.iface2route) ) go_out(1,"Unable to select an interface to route throughout"); } } return; } if ( !look4dev((struct mi_ifaz *)&packet.iface2route) ) { if (!first_nonloopback((struct mi_ifaz *)&packet.iface2route) ) go_out(1,"Unable to select an interface to route throughout"); } } /**********************************/ /* Try to get the IP address from */ /* the outgoing interface "ala" */ /* Stevens way. ;) */ /**********************************/ /* Return 1 on error, 0 else other*/ /**********************************/ int get_iface_out( struct sockaddr_in *ip2see, struct sockaddr_in *aux ) { int sock_rt, len, on=1; struct sockaddr_in iface_out; initmem( (char *)&iface_out, sizeof(struct sockaddr_in)); sock_rt = socket(AF_INET, SOCK_DGRAM, 0 ); ip2see->sin_port = htons(26731); if ( setsockopt( sock_rt, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on) ) == -1 ) { #ifdef DEBUG fprintf(stderr, "setsockopt -> %s\n", sys_errlist[errno] ); #endif close(sock_rt); return 1; } if (connect(sock_rt, (struct sockaddr *)ip2see, sizeof(struct sockaddr_in) ) == -1 ) { #ifdef DEBUG fprintf(stderr,"connect -> %s\n", sys_errlist[errno] ); #endif close(sock_rt); return 1; } len = sizeof(iface_out); if ( getsockname( sock_rt, (struct sockaddr *)&iface_out, &len ) == -1 ) { #ifdef DEBUG fprintf(stderr,"getsockname -> %s\n", sys_errlist[errno] ); #endif close(sock_rt); return 1; } close(sock_rt); if ( !iface_out.sin_addr.s_addr ) return 1; copymem( (char *)&iface_out, (char *)aux, sizeof(struct sockaddr_in)); return 0; } /*******************************************/ /* Copy the values from the interface */ /* "nombre" to "mi_interfaz" struct */ /*******************************************/ /* Arguments : */ /* - sock_disp : Socket RAW descriptor */ /* with the kernel. */ /* - nombre : Interface name. */ /* - mi_interfaz : Struct where to put */ /* the interface values. */ /*******************************************/ /* Return : 1 on error, or 0 else other. */ /*******************************************/ int put_interface( int sock_disp, char *nombre, struct mi_ifaz *mi_interfaz ) { struct ifreq if_aux; /* Estructura "auxiliar" :) */ strncpy(if_aux.ifr_name, nombre, sizeof(if_aux.ifr_name)); if (ioctl( sock_disp, SIOCGIFADDR, &if_aux) < 0) { if ( errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) return 1; #ifdef DEBUG fprintf(stderr,"SIOCGIFADDR -> %s\n", sys_errlist[errno]); #endif return 1; } mi_interfaz->ip = if_aux.ifr_addr; strncpy(if_aux.ifr_name, nombre, sizeof(if_aux.ifr_name)); if (ioctl(sock_disp, SIOCGIFMTU, &if_aux) < 0) { #ifdef DEBUG fprintf(stderr,"SIOCGIFMTU (%s) -> %s\n", mi_interfaz->name, sys_errlist[errno]); #endif return 1; } #ifdef SOLARIS mi_interfaz->mtu = if_aux.ifr_metric; #else mi_interfaz->mtu = if_aux.ifr_mtu; #endif strncpy(if_aux.ifr_name, nombre, sizeof(if_aux.ifr_name)); if ( ioctl( sock_disp, SIOCGIFFLAGS, &if_aux ) < 0 ) { #ifdef DEBUG fprintf(stderr,"SIOCGIFFLAGS (%s) -> %s\n", mi_interfaz->name, sys_errlist[errno]); #endif return 1; } mi_interfaz->flags = if_aux.ifr_flags; return 0; } /*********************************************/ /* Look for a device on the device table by */ /* interface name or by IP address. */ /* If 'iface2search->name[0]' != NULL */ /* then we search by name. */ /* If 'iface2search->name[0]' == NULL */ /* then we search by IP address. */ /*********************************************/ /* Return 1 on exit and put the interface IP */ /* address, mtu and flags on 'iface2search'. */ /* Return 0 on error. */ /*********************************************/ int look4dev( struct mi_ifaz *iface2search ) { struct ifconf ifc; struct ifreq *iface; struct mi_ifaz mi_interfaz; struct sockaddr_in *aux_in; struct sockaddr_in *aux_in2=NULL; char buffer[MAX_IFCONF*32]; int i, sock_disp; if ( (sock_disp = socket(AF_INET, SOCK_DGRAM, 0) ) < 0) { fprintf(stderr, "socket -> %s\n", sys_errlist[errno]); return 1; } ifc.ifc_len = sizeof(buffer); ifc.ifc_buf = buffer; if (ioctl( sock_disp, SIOCGIFCONF, &ifc) < 0) { fprintf(stderr, "SIOCGIFCONF -> %s\n", sys_errlist[errno]); close(sock_disp); return 1; } iface = ifc.ifc_req; for ( i=0; i < ifc.ifc_len; iface = (struct ifreq *)((char *)ifc.ifc_req + i ) ) { #ifdef HAVE_SOCKADDR_SA_LEN i+= MAX(sizeof(struct sockaddr),iface->ifr_addr.sa_len) + sizeof(iface->ifr_name); #else i+= sizeof(struct sockaddr) + sizeof(iface->ifr_name); #endif if (!*(iface->ifr_name)) continue; strncpy(mi_interfaz.name, iface->ifr_name, sizeof(mi_interfaz.name)); if ( put_interface( sock_disp, iface->ifr_name, &mi_interfaz ) ) continue; if ( mi_interfaz.flags & IFF_UP ) { if ( iface2search->name[0] ) /* Search by name...*/ { if ( !strcmp(mi_interfaz.name, iface2search->name) ) { copymem((char *)&mi_interfaz, (char *)iface2search, sizeof(struct mi_ifaz) ); close(sock_disp); return 1; } } else /* Search by IP address...*/ { aux_in = (struct sockaddr_in *)&mi_interfaz.ip; aux_in2 = (struct sockaddr_in *)&(iface2search->ip); if ( aux_in2->sin_addr.s_addr == aux_in->sin_addr.s_addr ) { copymem((char *)&mi_interfaz, (char *)iface2search, sizeof(struct mi_ifaz) ); close(sock_disp); return 1; } } } } close(sock_disp); return 0; } /*********************************************/ /* Look for the first non loopback device. */ /*********************************************/ /* Return 1 on exit and put the interface IP */ /* address, mtu and flags on 'iface2search' */ /* Return 0 on error. */ /*********************************************/ int first_nonloopback( struct mi_ifaz *iface2search ) { struct ifconf ifc; struct ifreq *iface; struct mi_ifaz mi_interfaz; char buffer[MAX_IFCONF*32]; int i, sock_disp; if ( (sock_disp = socket(AF_INET, SOCK_DGRAM, 0) ) < 0) { fprintf(stderr, "socket -> %s\n", sys_errlist[errno]); return 1; } ifc.ifc_len = sizeof(buffer); ifc.ifc_buf = buffer; if (ioctl( sock_disp, SIOCGIFCONF, &ifc) < 0) { fprintf(stderr, "SIOCGIFCONF -> %s\n", sys_errlist[errno]); close(sock_disp); return 1; } iface = ifc.ifc_req; for ( i=0; i < ifc.ifc_len; iface = (struct ifreq *)((char *)ifc.ifc_req + i ) ) { #ifdef HAVE_SOCKADDR_SA_LEN i+= MAX(sizeof(struct sockaddr),iface->ifr_addr.sa_len) + sizeof(iface->ifr_name); #else i+= sizeof(struct sockaddr) + sizeof(iface->ifr_name); #endif if (!*(iface->ifr_name)) continue; strncpy(mi_interfaz.name, iface->ifr_name, sizeof(mi_interfaz.name)); if ( put_interface( sock_disp, iface->ifr_name, &mi_interfaz ) ) continue; if ( ( mi_interfaz.flags & IFF_UP ) && !( mi_interfaz.flags & IFF_LOOPBACK) ) { copymem((char *)&mi_interfaz, (char *)iface2search, sizeof(struct mi_ifaz) ); close(sock_disp); return 1; } } close(sock_disp); return 0; }