Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members  

wvinterface.cc

Go to the documentation of this file.
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * A WvInterface stores information about a particular network interface.
00006  */
00007 #include "wvinterface.h"
00008 //#include "wvpipe.h"
00009 #include "wvsubproc.h"
00010 #include "wvfile.h"
00011 
00012 #include <sys/ioctl.h>
00013 #include <sys/socket.h>
00014 #include <sys/wait.h>
00015 #include <net/if_arp.h>
00016 #include <net/if.h>
00017 #include <net/route.h>
00018 #include <unistd.h>
00019 #include <linux/sockios.h>
00020 #include <errno.h>
00021 
00022 #define min(x,y) ((x) < (y) ? (x) : (y))
00023 
00024 WvInterfaceDictBase WvInterfaceDict::slist(15);
00025 int WvInterfaceDict::links = 0;
00026 
00027 
00028 WvInterface::WvInterface(const WvString &_name) 
00029         : err("Net Interface", WvLog::Error), name(_name)
00030 {
00031     name.unique();
00032     my_hwaddr = my_ipaddr = NULL;
00033     valid = true;
00034 }
00035 
00036 
00037 WvInterface::~WvInterface()
00038 {
00039     rescan();
00040 }
00041 
00042 
00043 int WvInterface::getinfo(struct ifreq *ifr, int ioctl_num)
00044 {
00045     int sock, retval;
00046 
00047     sock = socket(AF_INET, SOCK_STREAM, 0);
00048     strncpy(ifr->ifr_name, name, IFNAMSIZ-1);
00049     ifr->ifr_name[IFNAMSIZ-1] = 0;
00050     ifr->ifr_addr.sa_family = AF_INET;
00051     
00052     retval = ioctl(sock, ioctl_num, ifr);
00053     close(sock);
00054     return retval;
00055 }
00056 
00057 
00058 // forget all stored information about the address(es) of this interface
00059 void WvInterface::rescan()
00060 {
00061     if (my_hwaddr)
00062     {
00063         delete my_hwaddr;
00064         my_hwaddr = NULL;
00065     }
00066     
00067     if (my_ipaddr)
00068     {
00069         delete my_ipaddr;
00070         my_ipaddr = NULL;
00071     }
00072 }
00073 
00074 
00075 // get the hardware address of this interface
00076 const WvAddr &WvInterface::hwaddr()
00077 {
00078     struct ifreq ifr;
00079     
00080     if (!my_hwaddr)
00081     {
00082         if (getinfo(&ifr, SIOCGIFHWADDR))
00083             my_hwaddr = new WvStringAddr("Unknown", WvEncap::Unknown);
00084         else
00085             my_hwaddr = WvAddr::gen(&ifr.ifr_hwaddr);
00086     }
00087     
00088     return *my_hwaddr;
00089 }
00090 
00091 
00092 // get the local IP net of this interface
00093 const WvIPNet &WvInterface::ipaddr()
00094 {
00095     struct ifreq ifr, ifr2;
00096     
00097     if (!my_ipaddr)
00098     {
00099         if (getinfo(&ifr, SIOCGIFADDR) || getinfo(&ifr2, SIOCGIFNETMASK))
00100             my_ipaddr = new WvIPNet();
00101         else
00102             my_ipaddr = new WvIPNet(&ifr.ifr_addr, &ifr2.ifr_netmask);
00103     }
00104     
00105     return *my_ipaddr;
00106 }
00107 
00108 
00109 // get the point-to-point IP address of this interface
00110 const WvIPAddr WvInterface::dstaddr()
00111 {
00112     struct ifreq ifr;
00113     if (!(getflags() & IFF_POINTOPOINT) || getinfo(&ifr, SIOCGIFDSTADDR))
00114         return WvIPAddr();
00115     else
00116         return WvIPAddr(&ifr.ifr_dstaddr);
00117 }
00118 
00119 
00120 int WvInterface::getflags()
00121 {
00122     struct ifreq ifr;
00123     int sock, errnum;
00124     
00125     sock = socket(AF_INET, SOCK_STREAM, 0);
00126     strncpy(ifr.ifr_name, name, IFNAMSIZ-1);
00127     ifr.ifr_name[IFNAMSIZ-1] = 0;
00128 
00129     if (ioctl(sock, SIOCGIFFLAGS, &ifr))
00130     {
00131         errnum = errno;
00132         //if (errnum != EACCES && errnum != EPERM)
00133         //    err.perror(WvString("GetFlags %s", name));
00134         close(sock);
00135         valid = false;
00136         return 0;
00137     }
00138     
00139     close(sock);
00140     return ifr.ifr_flags;
00141 }
00142 
00143 
00144 int WvInterface::setflags(int clear, int set)
00145 {
00146     struct ifreq ifr;
00147     int sock, errnum;
00148     
00149     sock = socket(AF_INET, SOCK_STREAM, 0);
00150     strncpy(ifr.ifr_name, name, IFNAMSIZ-1);
00151     ifr.ifr_name[IFNAMSIZ-1] = 0;
00152 
00153     if (ioctl(sock, SIOCGIFFLAGS, &ifr))
00154     {
00155         errnum = errno;
00156         //if (errnum != EACCES && errnum != EPERM)
00157         //    err.perror(WvString("GetFlags2 %s", name));
00158         close(sock);
00159         return errnum;
00160     }
00161     
00162     if (((ifr.ifr_flags & ~clear) | set) != ifr.ifr_flags)
00163     {
00164         ifr.ifr_flags &= ~clear;
00165         ifr.ifr_flags |= set;
00166         
00167         if (ioctl(sock, SIOCSIFFLAGS, &ifr))
00168         {
00169             errnum = errno;
00170             if (errnum != EACCES && errnum != EPERM)
00171                 err.perror(WvString("SetFlags %s", name));
00172             close(sock);
00173             return errnum;
00174         }
00175     }
00176     
00177     close(sock);
00178     return 0;
00179 }
00180 
00181 
00182 void WvInterface::up(bool enable)
00183 {
00184     setflags(IFF_UP, enable ? IFF_UP : 0);
00185     rescan();
00186 }
00187 
00188 
00189 bool WvInterface::isup()
00190 {
00191     return (valid && (getflags() & IFF_UP)) ? 1 : 0;
00192 }
00193 
00194 
00195 void WvInterface::promisc(bool enable)
00196 {
00197     setflags(IFF_PROMISC, enable ? IFF_PROMISC : 0);
00198 }
00199 
00200 
00201 bool WvInterface::ispromisc()
00202 {
00203     return (getflags() & IFF_PROMISC) ? 1 : 0;
00204 }
00205 
00206 
00207 int WvInterface::setipaddr(const WvIPNet &addr)
00208 {
00209     struct ifreq ifr;
00210     struct sockaddr *sa;
00211     size_t len;
00212     int sock;
00213     WvIPAddr none;
00214     
00215     if (addr != ipaddr())
00216         err(WvLog::Info, "Changing %s address to %s (%s bits)\n", name,
00217             addr.base(), addr.bits());
00218     
00219     sock = socket(AF_INET, SOCK_STREAM, 0);
00220     strncpy(ifr.ifr_name, name, IFNAMSIZ-1);
00221     ifr.ifr_name[IFNAMSIZ-1] = 0;
00222     ifr.ifr_addr.sa_family = AF_INET;
00223 
00224     len = min(sizeof(sockaddr), addr.sockaddr_len());
00225 
00226     sa = addr.sockaddr();
00227     memcpy(&ifr.ifr_addr, sa, len);
00228     delete sa;
00229     if (ioctl(sock, SIOCSIFADDR, &ifr))
00230     {
00231         if (errno != EACCES && errno != EPERM)
00232             err.perror(WvString("SetIfAddress %s", name));
00233         close(sock);
00234         return -1;
00235     }
00236     
00237     // 2.1 kernels error when we try to change netmask/broadcast for
00238     // a 0.0.0.0 address.
00239     if (addr.base() != none)
00240     {
00241         sa = addr.netmask().sockaddr();
00242         memcpy(&ifr.ifr_netmask, sa, len);
00243         delete sa;
00244         if (ioctl(sock, SIOCSIFNETMASK, &ifr))
00245         {
00246             if (errno != EACCES && errno != EPERM)
00247                 err.perror(WvString("SetNetmask %s", name));
00248             close(sock);
00249             return -1;
00250         }
00251         
00252         if (!strchr(name, ':')) // otherwise, an alias, and no broadcast addr!
00253         {
00254             sa = addr.broadcast().sockaddr();
00255             memcpy(&ifr.ifr_broadaddr, sa, len);
00256             delete sa;
00257             if (ioctl(sock, SIOCSIFBRDADDR, &ifr))
00258             {
00259                 if (errno != EACCES && errno != EPERM)
00260                     err.perror(WvString("SetBroadcast %s", name));
00261                 close(sock);
00262                 return -1;
00263             }
00264         }
00265     }
00266     
00267     // addroute(addr); // not necessary on 2.1 and higher kernels
00268     close(sock);
00269 
00270     rescan();
00271     return 0;
00272 }
00273 
00274 
00275 int WvInterface::setmtu(int mtu)
00276 {
00277     struct ifreq ifr;
00278     int sock, errnum;
00279     
00280     sock = socket(AF_INET, SOCK_STREAM, 0);
00281     strncpy(ifr.ifr_name, name, IFNAMSIZ-1);
00282     ifr.ifr_name[IFNAMSIZ-1] = 0;
00283 
00284     ifr.ifr_mtu = mtu;
00285     
00286     if (ioctl(sock, SIOCSIFMTU, &ifr))
00287     {
00288         errnum = errno;
00289         if (errnum != EACCES && errnum != EPERM)
00290             err.perror(WvString("SetMTU %s", name));
00291         close(sock);
00292         return errnum;
00293     }
00294     
00295     close(sock);
00296     return 0;
00297 }
00298 
00299 
00300 // Fill a routing table entry with the given information.
00301 void WvInterface::fill_rte(struct rtentry *rte, char ifname[17],
00302                            const WvIPNet &dest, const WvIPAddr &gw,
00303                            int metric)
00304 {
00305     struct sockaddr *net, *mask, *gwaddr;
00306     size_t len;
00307     bool is_direct = (gw == WvIPAddr());
00308     bool is_host = dest.is_host();
00309     
00310     memset(rte, 0, sizeof(struct rtentry));
00311     rte->rt_metric = metric + 1;
00312     
00313     strncpy(ifname, name, 17);
00314     ifname[17-1] = 0;
00315     rte->rt_dev = ifname;
00316 
00317     len = min(sizeof(sockaddr), dest.sockaddr_len());
00318     
00319     net = dest.network().sockaddr();
00320     memcpy(&rte->rt_dst, net, len);
00321     delete net;
00322     
00323     if (!is_host)
00324     {
00325         mask = dest.netmask().sockaddr();
00326         memcpy(&rte->rt_genmask, mask, len);
00327         delete mask;
00328     }
00329 
00330     if (!is_direct)
00331     {
00332         gwaddr = gw.sockaddr();
00333         memcpy(&rte->rt_gateway, gwaddr, len);
00334         delete gwaddr;
00335     }
00336     
00337     rte->rt_flags = (RTF_UP
00338                     | (is_host ? RTF_HOST : 0)
00339                     | (is_direct ? 0 : RTF_GATEWAY));
00340 }
00341 
00342 
00343 int WvInterface::addroute(const WvIPNet &dest, const WvIPAddr &gw,
00344                            int metric, const WvString &table)
00345 {
00346     struct rtentry rte;
00347     char ifname[17];
00348     int sock;
00349     WvString deststr(dest), gwstr(gw), metr(metric);
00350 
00351     const char * const argv[] = {
00352         "ip", "route", "add",
00353         deststr,
00354         "table", table,
00355         "dev", name,
00356         "via", gwstr,
00357         "metric", metr,
00358         NULL
00359     };
00360 
00361 
00362     if (dest.is_default() || table != "default")
00363     {
00364         err(WvLog::Debug2, "addroute: ");
00365         for (int i = 0; argv[i]; i++)
00366             err(WvLog::Debug2, "%s ", argv[i]);
00367         err(WvLog::Debug2, "\n");
00368         
00369         WvSubProc checkProc;
00370         checkProc.startv(*argv, argv);
00371         checkProc.wait(-1);
00372 
00373         //if (WvPipe(argv[0], argv, false, false, false).finish() != 242)
00374         if (checkProc.estatus != 242)
00375         {
00376             // added a default route via the subprogram
00377             // 242 is the magic "WvPipe could not exec program..." exit code.
00378             return 0;
00379         }
00380     }
00381     
00382     // if we get here, it is not a default route or the 'ip' command is
00383     // broken somehow.
00384 
00385     fill_rte(&rte, ifname, dest, gw, metric);
00386     
00387     sock = socket(AF_INET, SOCK_STREAM, 0);
00388     if (ioctl(sock, SIOCADDRT, &rte))
00389     {
00390         if (errno != EACCES && errno != EPERM && errno != EEXIST
00391           && errno != ENOENT)
00392             err.perror(WvString("AddRoute %s %s (up=%s)",
00393                                 name, dest, isup()));
00394         close(sock);
00395         return -1;
00396     }
00397     
00398     close(sock);
00399     return 0;
00400 }
00401 
00402 
00403 // add a route with no gateway, ie. direct to interface
00404 int WvInterface::addroute(const WvIPNet &dest, int metric,
00405                           const WvString &table)
00406 {
00407     return addroute(dest, WvIPAddr(), metric, table);
00408 }
00409 
00410 
00411 int WvInterface::delroute(const WvIPNet &dest, const WvIPAddr &gw,
00412                           int metric, const WvString &table)
00413 {
00414     struct rtentry rte;
00415     char ifname[17];
00416     int sock;
00417     WvString deststr(dest), gwstr(gw), metr(metric);
00418     const char *argv[] = {
00419         "ip", "route", "del",
00420         deststr,
00421         "table", table,
00422         "dev", name,
00423         "via", gwstr,
00424         "metric", metr,
00425         NULL
00426     };
00427     
00428     if (dest.is_default() || table != "default")
00429     {
00430         err(WvLog::Debug2, "addroute: ");
00431         for (int i = 0; argv[i]; i++)
00432             err(WvLog::Debug2, "%s ", argv[i]);
00433         err(WvLog::Debug2, "\n");
00434         
00435         WvSubProc checkProc;
00436         checkProc.startv(*argv, (char * const *)argv);
00437         checkProc.wait(-1);
00438 
00439         //if (WvPipe(argv[0], argv, false, false, false).finish() == 0)
00440         if (!WEXITSTATUS(checkProc.estatus))
00441         {
00442             // successfully deleted a default route via the subprogram
00443             return 0;
00444         }
00445     }
00446 
00447     fill_rte(&rte, ifname, dest, gw, metric);
00448     
00449     sock = socket(AF_INET, SOCK_STREAM, 0);
00450     if (ioctl(sock, SIOCDELRT, &rte))
00451     {
00452         if (errno != EACCES && errno != EPERM && errno != EEXIST)
00453             err.perror(WvString("DelRoute %s", name));
00454         close(sock);
00455         return -1;
00456     }
00457     
00458     close(sock);
00459     return 0;
00460 }
00461 
00462 
00463 // delete a route with no gateway, ie. direct to interface
00464 int WvInterface::delroute(const WvIPNet &dest, int metric, const WvString &table)
00465 {
00466     return delroute(dest, WvIPAddr(), metric, table);
00467 }
00468 
00469 
00470 // add an ARP or proxy ARP entry on this interface
00471 int WvInterface::addarp(const WvIPNet &dest, const WvAddr &hw, bool proxy)
00472 {
00473     int sock;
00474     struct arpreq ar;
00475     struct sockaddr *sa;
00476     size_t len;
00477     
00478     sa = dest.network().sockaddr();
00479     len = min(dest.sockaddr_len(), sizeof(ar.arp_pa));
00480     memcpy(&ar.arp_pa, sa, len);
00481     delete sa;
00482     
00483     sa = hw.sockaddr();
00484     len = min(hw.sockaddr_len(), sizeof(ar.arp_ha));
00485     memcpy(&ar.arp_ha, sa, len);
00486     delete sa;
00487     
00488     sa = dest.netmask().sockaddr();
00489     len = min(dest.sockaddr_len(), sizeof(ar.arp_netmask));
00490     memcpy(&ar.arp_netmask, sa, len);
00491     delete sa;
00492     
00493     strncpy(ar.arp_dev, name, sizeof(ar.arp_dev));
00494     
00495     ar.arp_flags = (ATF_COM | ATF_PERM
00496                     | (proxy ? ATF_PUBL : 0)
00497                     | (proxy && dest.is_host() ? ATF_NETMASK : 0));
00498     
00499     sock = socket(AF_INET, SOCK_STREAM, 0);
00500     if (ioctl(sock, SIOCSARP, &ar))
00501     {
00502         if (errno != EACCES && errno != EPERM)
00503             err.perror(WvString("AddARP %s", name));
00504         close(sock);
00505         return -1;
00506     }
00507     
00508     close(sock);
00509     return 0;
00510 }
00511 
00512 
00513 bool WvInterface::isarp()
00514 {
00515     int f = getflags();
00516     return !(f & (IFF_NOARP | IFF_LOOPBACK)) && (f & IFF_BROADCAST);
00517 }
00518 
00519 
00520 static char *find_ifname(char *line)
00521 {
00522     if (!line) return NULL;
00523     
00524     // skip leading whitespace
00525     while (*line==' ') line++;
00526 
00527     // everything before the last colon is the device name
00528     char *cptr = strrchr(line, ':');
00529     if (!cptr)
00530         return NULL;
00531     *cptr = 0;
00532     return line;
00533 }
00534 
00535 
00537 
00538 
00539 WvInterfaceDict::WvInterfaceDict() : log("Net Interface", WvLog::Info)
00540 {
00541     links++;
00542     update();
00543 }
00544 
00545 
00546 WvInterfaceDict::~WvInterfaceDict()
00547 {
00548     links--;
00549     
00550     if (!links)
00551         slist.zap();
00552 }
00553 
00554 
00555 // auto-fill the list of interfaces using the list from /proc/net/dev.
00556 //
00557 // I wish there was a better way to do this, but the SIOCGIFCONF ioctl
00558 // ignores 'down' interfaces, which is not what we want.
00559 //
00560 void WvInterfaceDict::update()
00561 {
00562     int sock;
00563     struct ifconf ifconf;
00564     char buf[sizeof(ifconf.ifc_req) * 100]; // room for 100 interfaces
00565     WvLog err(log.split(WvLog::Error));
00566     WvFile procdev("/proc/net/dev", O_RDONLY);
00567     char *ifname;
00568 
00569         
00570     // mark all interfaces in list invalid for now
00571     Iter i(*this);
00572     for (i.rewind(); i.next(); )
00573         i().valid = false;
00574     
00575 
00576     // get list of all non-aliased interfaces from /proc/net/dev
00577     
00578     
00579     // skip the two header lines
00580     procdev.getline(-1); procdev.getline(-1);
00581     
00582     // add/validate existing interfaces
00583     while ((ifname = find_ifname(procdev.getline(-1))) != NULL)
00584     {
00585         WvString s(ifname);
00586         WvInterface *ifc = (*this)[s];
00587         
00588         if (!ifc)
00589         {
00590             ifc = new WvInterface(ifname);
00591             slist.add(ifc, true);
00592             log(WvLog::Debug3, "Found %-16s  [%s]\n", ifname, ifc->hwaddr());
00593         }
00594         else
00595             ifc->rescan();
00596         ifc->valid = true;
00597     }
00598 
00599     
00600     // get list of "up" and aliased interfaces with SIOCGIFCONF ioctl
00601     
00602     
00603     ifconf.ifc_buf = buf;
00604     ifconf.ifc_len = sizeof(buf);
00605     
00606     sock = socket(AF_INET, SOCK_STREAM, 0);
00607     if (! ioctl(sock, SIOCGIFCONF, &ifconf))
00608     {
00609         int count, max = ifconf.ifc_len / sizeof(ifconf.ifc_req[0]);
00610         
00611         for (count = 0; count < max; count++)
00612         {
00613             struct ifreq &ifr = ifconf.ifc_req[count];
00614             WvInterface *ifc = (*this)[ifr.ifr_name];
00615             
00616             if (!ifc)
00617             {
00618                 ifc = new WvInterface(ifr.ifr_name);
00619                 slist.add(ifc, true);
00620             }
00621             else
00622                 ifc->rescan();
00623             ifc->valid = true;
00624         }
00625     }
00626     close(sock);
00627 }
00628 
00629 
00630 // determine if the given address belongs to the local system
00631 bool WvInterfaceDict::islocal(const WvAddr &addr)
00632 {
00633     static WvIPAddr bcast("255.255.255.255"); // always a local address!
00634     
00635     if (addr == bcast)
00636         return true;
00637     
00638     Iter i(*this);
00639     for (i.rewind(); i.next(); )
00640     {
00641         WvInterface &ifc(i);
00642         if (!ifc.valid) continue;
00643         
00644         if (ifc.ipaddr() == addr || ifc.ipaddr().base() == addr
00645           || ifc.ipaddr().broadcast() == addr)
00646             return true;
00647 
00648         if (ifc.hwaddr() == addr)
00649             return true;
00650     }
00651     
00652     return false;
00653 }
00654 
00655 
00656 bool WvInterfaceDict::on_local_net(const WvIPNet &addr)
00657 {
00658     WvIPAddr zero;
00659     
00660     if (islocal(addr))
00661         return true;
00662     
00663     Iter i(*this);
00664     for (i.rewind(); i.next(); )
00665     {
00666         WvInterface &ifc = i;
00667         if (!ifc.valid) continue;
00668         
00669         if (ifc.isup() && WvIPAddr(ifc.ipaddr()) != zero
00670           && ifc.ipaddr().includes(addr))
00671             return true;
00672     }
00673     
00674     return false;
00675 }

Generated on Sun Aug 25 02:29:31 2002 for WvStreams by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002