emulate/arp.cc

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 1998 Regents of the University of California.
00003  * All rights reserved.
00004  *    
00005  * Redistribution and use in source and binary forms, with or without 
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. All advertising materials mentioning features or use of this software
00014  *    must display the following acknowledgement:
00015  *      This product includes software developed by the Network Research
00016  *      Group at Lawrence Berkeley National Laboratory.
00017  * 4. Neither the name of the University nor of the Laboratory may be used
00018  *    to endorse or promote products derived from this software without
00019  *    specific prior written permission.
00020  *   
00021  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00022  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00023  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00024  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00025  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00026  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00027  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
00028  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
00029  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00030  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
00031  * SUCH DAMAGE.
00032  */  
00033 
00034 #ifndef lint
00035 static const char rcsid[] =
00036     "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/emulate/arp.cc,v 1.8 2000/11/09 17:42:23 haoboy Exp $";
00037 #endif
00038 
00039 #include "object.h"
00040 #include "packet.h"
00041 
00042 #include <sys/types.h>
00043 #include <sys/param.h>
00044 #include <sys/socket.h>
00045 #include <sys/ioctl.h>
00046 #include <netinet/in.h>
00047 #include <netinet/in_systm.h>
00048 #include <netinet/ip.h>
00049 #include <net/if.h>
00050 #include <net/ethernet.h>
00051 #include <net/if_arp.h>
00052 #include <netinet/if_ether.h>
00053 #include <arpa/inet.h>
00054 #include <memory.h>
00055 #include <stdio.h>
00056 #include <errno.h>
00057 
00058 #include "emulate/net.h"
00059 #include "emulate/ether.h"
00060 #include "emulate/internet.h"
00061 
00062 // Very very very back hack. Should put this detection in autoconf.
00063 #ifndef ether_aton
00064 extern "C" {
00065 ether_addr* ether_aton(const char *);
00066 }
00067 #endif
00068 
00069 //
00070 // arp.cc -- this object may be used within nse as
00071 // an ARP requestor/responder.  Only the request side
00072 // is implemented now [5/98]
00073 // 
00074 
00075 class ArpAgent : public NsObject, public IOHandler {
00076 public:
00077     ArpAgent();
00078     ~ArpAgent();
00079 
00080 protected:
00081     struct acache_entry {
00082         in_addr ip;
00083         ether_addr ether;
00084         char code;  // 'D' - dynamic, 'P' - publish
00085     };
00086         
00087     char    icode(const char*);
00088     acache_entry* find(in_addr&);
00089     void    insert(in_addr&, ether_addr&, char code);
00090     void    dispatch(int);
00091     int sendreq(in_addr&);
00092     int sendresp(ether_addr&, in_addr&, ether_addr&);
00093     int resolve(const char* host, char*& result, int sendreq);
00094 
00095     void    doreq(ether_arp*);
00096     void    doreply(ether_arp*);
00097 
00098     void recv(Packet*, Handler*) { abort(); }
00099 
00100     int command(int, const char*const*);
00101 
00102     Network*    net_;
00103     ether_header    eh_template_;
00104     ether_arp   ea_template_;
00105     ether_addr  my_ether_;
00106     in_addr     my_ip_;
00107     int     base_size_; // size of rcv buf
00108     u_char*     rcv_buf_;
00109     acache_entry*   acache_;    // arp mapping cache
00110     int     nacache_;   // # entries in cache
00111     int     cur_;       // cur posn in cache
00112     int     pending_;   // resolve pending?
00113 };
00114 
00115 static class ArpAgentClass : public TclClass { 
00116 public:
00117         ArpAgentClass() : TclClass("ArpAgent") {}
00118         TclObject* create(int , const char*const*) {
00119                 return (new ArpAgent());
00120         } 
00121 } class_arpagent;
00122 
00123 ArpAgent::ArpAgent() : net_(NULL), pending_(0)
00124 {
00125     /* dest addr is broadcast */
00126     eh_template_.ether_dhost[0] = 0xff;
00127     eh_template_.ether_dhost[1] = 0xff;
00128     eh_template_.ether_dhost[2] = 0xff;
00129     eh_template_.ether_dhost[3] = 0xff;
00130     eh_template_.ether_dhost[4] = 0xff;
00131     eh_template_.ether_dhost[5] = 0xff;
00132     /* src addr is mine */
00133     memcpy(&eh_template_.ether_shost, &my_ether_, ETHER_ADDR_LEN);
00134     /* type is ARP */
00135     eh_template_.ether_type = htons(ETHERTYPE_ARP);
00136 
00137     ea_template_.ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
00138     ea_template_.ea_hdr.ar_pro = htons(ETHERTYPE_IP);
00139     ea_template_.ea_hdr.ar_hln = ETHER_ADDR_LEN;
00140     ea_template_.ea_hdr.ar_pln = 4;         /* ip addr len */
00141     ea_template_.ea_hdr.ar_op = htons(ARPOP_REQUEST);   
00142     memcpy(&ea_template_.arp_sha, &my_ether_, ETHER_ADDR_LEN);  /* sender hw */
00143     memset(&ea_template_.arp_spa, 0, 4);            /* sender IP */
00144     memset(&ea_template_.arp_tha, 0, ETHER_ADDR_LEN);   /* target hw */
00145     memset(&ea_template_.arp_tpa, 0, 4);            /* target hw */
00146     base_size_ = sizeof(eh_template_) + sizeof(ea_template_);
00147     rcv_buf_ = new u_char[base_size_];
00148 
00149     bind("cachesize_", &nacache_);
00150     acache_ = new acache_entry[nacache_];
00151     memset(acache_, 0, nacache_*sizeof(acache_entry));
00152     cur_ = nacache_;
00153 }
00154 
00155 ArpAgent::~ArpAgent()
00156 {
00157     delete[] rcv_buf_;
00158     delete[] acache_;
00159 }
00160 
00161 ArpAgent::acache_entry*
00162 ArpAgent::find(in_addr& target)
00163 {
00164     int n = nacache_;
00165     acache_entry* ae = &acache_[n-1];
00166     while (--n >= 0) {
00167         if (ae->ip.s_addr == target.s_addr) {
00168             return (ae);
00169         }
00170         --ae;
00171     }
00172     return (NULL);
00173 }
00174 
00175 char
00176 ArpAgent::icode(const char *how)
00177 {
00178     if (strcmp(how, "publish") == 0)
00179         return 'P';
00180 
00181     return 'D';
00182 }
00183 
00184 void
00185 ArpAgent::insert(in_addr& target, ether_addr& eaddr, char code)
00186 {
00187     acache_entry* ae;
00188     if (--cur_ < 0)
00189         cur_ = nacache_ - 1;
00190 
00191     ae = &acache_[cur_];
00192     ae->ip = target;
00193     ae->ether = eaddr;
00194     ae->code = code;
00195 //printf("INSERTED inet %s, ether %s\n",
00196 //inet_ntoa(target), Ethernet::etheraddr_string((u_char*)&eaddr));
00197     return;
00198 }
00199         
00200 int
00201 ArpAgent::sendreq(in_addr& target)
00202 {
00203     int pktsz = sizeof(eh_template_) + sizeof(ea_template_);
00204     if (pktsz < 64)
00205         pktsz = 64;
00206     u_char* buf = new u_char[pktsz];
00207     memset(buf, 0, pktsz);
00208 
00209     ether_header* eh = (ether_header*) buf;
00210     ether_arp* ea = (ether_arp*) (buf + sizeof(eh_template_));
00211     *eh = eh_template_; /* set ether header */
00212     *ea = ea_template_; /* set ether/IP arp pkt */
00213     memcpy(ea->arp_tpa, &target, sizeof(target));
00214 
00215     if (net_->send(buf, pktsz) < 0) {
00216                 fprintf(stderr,
00217                     "ArpAgent(%s): sendpkt (%p, %d): %s\n",
00218                     name(), buf, pktsz, strerror(errno));
00219                 return (-1);
00220     }
00221     delete[] buf;
00222     return (0);
00223 }
00224 
00225 /*
00226  * resp: who to send response to
00227  * tip: the IP address we are responding for
00228  * tea: the ether address we want to advertise with tip
00229  */
00230 
00231 int
00232 ArpAgent::sendresp(ether_addr& dest, in_addr& tip, ether_addr& tea)
00233 {
00234     int pktsz = sizeof(eh_template_) + sizeof(ea_template_);
00235     if (pktsz < 64)
00236         pktsz = 64;
00237     u_char* buf = new u_char[pktsz];
00238     memset(buf, 0, pktsz);
00239 
00240     ether_header* eh = (ether_header*) buf;
00241     ether_arp* ea = (ether_arp*) (buf + sizeof(eh_template_));
00242 
00243     // destination link layer address is back to sender
00244     // (called dest here)
00245     *eh = eh_template_; /* set ether header */
00246     memcpy(eh->ether_dhost, &dest, ETHER_ADDR_LEN);
00247 
00248     // set code as ARP reply
00249     *ea = ea_template_; /* set ether/IP arp pkt */
00250     ea->ea_hdr.ar_op = htons(ARPOP_REPLY);
00251 
00252     // make it look like a regular arp reply
00253     memcpy(ea->arp_tpa, ea->arp_spa, sizeof(in_addr));
00254     memcpy(ea->arp_tha, ea->arp_sha, sizeof(in_addr));
00255 
00256     memcpy(ea->arp_sha, &tea, ETHER_ADDR_LEN);
00257     memcpy(ea->arp_spa, &tip, ETHER_ADDR_LEN);
00258 
00259     if (net_->send(buf, pktsz) < 0) {
00260                 fprintf(stderr,
00261                     "ArpAgent(%s): sendpkt (%p, %d): %s\n",
00262                     name(), buf, pktsz, strerror(errno));
00263                 return (-1);
00264     }
00265     delete[] buf;
00266     return (0);
00267 }
00268 
00269 /*
00270  * receive pkt from network:
00271  *  note that net->recv() gives us the pkt starting
00272  *  just BEYOND the frame header
00273  */
00274 void
00275 ArpAgent::dispatch(int)
00276 {
00277     double ts;
00278     sockaddr sa;
00279     int cc = net_->recv(rcv_buf_, base_size_, sa, ts);
00280     if (cc < int(base_size_ - sizeof(ether_header))) {
00281         if (cc == 0)
00282             return;
00283                 fprintf(stderr,
00284                     "ArpAgent(%s): recv small pkt (%d) [base sz:%d]: %s\n",
00285                     name(), cc, base_size_, strerror(errno));
00286         return;
00287     }
00288     ether_arp* ea = (ether_arp*) rcv_buf_;
00289     int op = ntohs(ea->ea_hdr.ar_op);
00290 
00291 
00292     switch (op) {
00293     case ARPOP_REPLY:
00294         doreply(ea);
00295         break;
00296     case ARPOP_REQUEST:
00297         doreq(ea);
00298         break;
00299     default:
00300         fprintf(stderr,
00301             "ArpAgent(%s): cannot interpret ARP op %d\n",
00302             name(), op);
00303         return;
00304     }
00305     return;
00306 }
00307 
00308 /*
00309  * process an ARP reply frame -- insert into cache
00310  */
00311 void
00312 ArpAgent::doreply(ether_arp* ea)
00313 {
00314     /*
00315      * reply will be from the replier's point of view,
00316      * so, look in the sender ha/pa fields for the info
00317      * we want
00318      */
00319     in_addr t;
00320     ether_addr e;
00321     memcpy(&t, ea->arp_spa, 4); // copy IP address
00322     memcpy(&e, ea->arp_sha, ETHER_ADDR_LEN);
00323     insert(t, e, 'D');
00324     return;
00325 }
00326 
00327 /*
00328  * process an ARP request frame
00329  */
00330 
00331 void
00332 ArpAgent::doreq(ether_arp* ea)
00333 {
00334     in_addr t;
00335     memcpy(&t, ea->arp_tpa, 4); // requested IP addr
00336 
00337     acache_entry *ae;
00338     if ((ae = find(t)) == NULL) {
00339 //printf("doreq: didn't find mapping for IP addr %s\n",
00340 //inet_ntoa(t));
00341         return;
00342     }
00343 
00344     if (ae->code == 'P') {
00345         // return answer to the sender's hardware addr
00346         ether_addr dst;
00347         memcpy(&dst, ea->arp_sha, ETHER_ADDR_LEN);
00348         sendresp(dst, t, ae->ether);
00349     }
00350     return;
00351 }
00352 
00353 
00354 int
00355 ArpAgent::command(int argc, const char*const* argv)
00356 {
00357     Tcl& tcl = Tcl::instance();
00358     if (argc == 2) {
00359                 if (strcmp(argv[1], "network") == 0) { 
00360             if (net_ == NULL)
00361                 tcl.result("");
00362             else
00363                 tcl.result(net_->name());
00364             return (TCL_OK);
00365         }
00366     } else if (argc == 3) {
00367                 if (strcmp(argv[1], "network") == 0) { 
00368                         net_ = (Network *)TclObject::lookup(argv[2]);
00369                         if (net_ != 0) { 
00370                 link(net_->rchannel(), TCL_READABLE);
00371                 return (TCL_OK);
00372                         } else {
00373                                 fprintf(stderr,
00374                                 "ArpAgent(%s): unknown network %s\n",
00375                                     name(), argv[2]);
00376                                 return (TCL_ERROR);
00377                         }       
00378                         return(TCL_OK);
00379                 }       
00380         if (strcmp(argv[1], "myether") == 0) {
00381             my_ether_ = *(::ether_aton((char*)argv[2]));
00382             memcpy(&eh_template_.ether_shost, &my_ether_,
00383                 ETHER_ADDR_LEN);
00384             memcpy(&ea_template_.arp_sha,
00385                 &my_ether_, ETHER_ADDR_LEN);
00386             return (TCL_OK);
00387         }
00388         if (strcmp(argv[1], "myip") == 0) {
00389             u_long a = inet_addr(argv[2]);
00390             if (a == 0)
00391                 return (TCL_ERROR);
00392             in_addr ia;
00393             ia.s_addr = a;
00394             my_ip_ = ia;
00395             memcpy(&ea_template_.arp_spa,
00396                 &my_ip_, 4);
00397             return (TCL_OK);
00398         }
00399         if (strcmp(argv[1], "lookup") == 0) {
00400             char *p = NULL;
00401             if (resolve(argv[2], p, 0) < 0)
00402                 return (TCL_ERROR);
00403             if (p)
00404                 tcl.result(p);
00405             return (TCL_OK);
00406         }
00407         if (strcmp(argv[1], "resolve") == 0) {
00408             char *p = NULL;
00409             if (resolve(argv[2], p, 1) < 0)
00410                 return (TCL_ERROR);
00411             if (p)
00412                 tcl.resultf("%s", p);
00413             return (TCL_OK);
00414         }
00415     } else if (argc == 5) {
00416         // $obj insert iaddr eaddr how
00417         if (strcmp(argv[1], "insert") == 0) {
00418             u_long a = inet_addr(argv[2]);
00419             if (a == 0)
00420                 return (TCL_ERROR);
00421             in_addr ia;
00422             ia.s_addr = a;
00423             ether_addr ea = *(::ether_aton((char*)argv[3]));
00424             insert(ia, ea, icode(argv[4]));
00425             return (TCL_OK);
00426         }
00427     }
00428 
00429     return (NsObject::command(argc, argv));
00430 }
00431 
00432 int
00433 ArpAgent::resolve(const char* host, char*& result, int doreq)
00434 {
00435     u_long a = inet_addr(host);
00436     in_addr ia;
00437     ia.s_addr = a;
00438     acache_entry* ae;
00439     if ((ae = find(ia)) == NULL) {
00440         result = NULL;
00441         if (doreq)
00442             return(sendreq(ia));
00443         return (0);
00444     }
00445     result = Ethernet::etheraddr_string((u_char*) &ae->ether);
00446     return (1);
00447 }

Generated on Tue Mar 6 16:47:43 2007 for ns2 Network Simulator 2.29 by  doxygen 1.4.6