Mobile Agents in Click Tushar Mohan School of Computing University of Utah tushar@cs.utah.edu Abstract Click is a modular, flexible and extensible router software architecture. It is designed to permit the user to form router configurations to support their specific needs. Click’s unit of modularity is an element. Different Click elements, each of which performs a specific task, are arranged in flow graphs to perform customized packet processing. At the time of writing this paper, Click running on commodity Pentium III PCs could achieve peak forwarding rate of 1.3 million 64-byte packets per second. With time, numerous elements have been added to Click to perform specific functions such as IPv6 Neighborhood Advertisement etc. However the only form of mobile IP support in Click was, to manually configure a Click router to encapsulate and forward packets to a particular mobile host. Registrations with home agents could not be done automatically as specified in the mobile IP standard. For a spring semester project, I have added elements in Click to perform the task of home and foreign agents in Click. Click configurations can use these elements, like any other Click elements, in Click graphs. The added elements support a subset of the features required for home and foreign agents as mandated by the current Internet standard - RFC 2002. In this paper I provide a brief introduction to Click (section I), followed by a brief description of the Mobile IP protocol as specified in the current Internet standard (section II). Subsequently I provide a detailed description of the elements I have added, along with their proper use in Click configurations (section III). This is followed by a description of a configuration used to test the functionality of the elements (section IV). The following section summarizes the features implemented by the added elements followed by an outline on what remains to be done, for enhancing these elements (section V). A brief note regarding related work and some observations regarding Click in general and the agent elements in particular conclude the paper (section VI). The code for the newly added elements and sample Click configurations using them are included in the Appendix. 1. Introduction Few projects have been done in the area of developing flexible routers. The most prominent one amongst Click developed at MIT. Click is a modular, flexible and extensible router software architecture, developed by Eddie Kohler and others, at the LCS, MIT [1,2] While the need for developing extensible and modular routers is recognized when one considers the varied configurations required for practical networks, it becomes critical in deveoping, testing and deployment of new network protocols. However a formidable obstacle in the development of modular sofware routers has been the demand for high-speed routers. Even fast software router implementations in Linux, achieve less than 500,000 packets per second on commodity hardware. Major improvements in speed can be achieved by modifying the interface driver to run in polling mode in contrast to the customary interrupt-driven mode [1,2] Click, with polling mode interface drivers can achieve 1.3 million packets/second on a P-III with the Intel gigabit driver. Perhaps more important than its high speed forwarding is the ease of writing Click configurations to perform highly specialized packet processing. 1.1 Click Elements The fundemental unit of configurablity in Click is an element. Each element in Click performs specific tasks. These elements are arranged in flow graphs specified in a Click configuration file to achieve an aggregation of tasks to suit one’s particular needs. Examples of elements include: DecIPTTL, LookupIPRoute, ARPResponder, IPClassifier All elements follow the Element class interface. Currently this interface and all other existing elements are written in C++. Examples of methods in this interface include: configure, initialize, push, pull. The act of passing a packet down to a downstream element corresponds to invoking that element object’s push() method through a virtual function call. The ability to connect diverse elements in differing configurations arises from the virtual function call invocation, mechanism of C++. 1.1.1 Ports Each element has zero or more input and output ports. Sources of packets like FromDevice have no input ports, while packet sinks like Discard and ToDevice have no ouptut ports. Typical elements have a single input port and 2 output ports. Often the second output port is used to emit error packets, which are then passed to ICMP elements to create ICMP error messages. Each port is exactly one of: PUSH, PULL and AGNOSTIC. A single PUSH input and output port means the element will be handed a packet down from the upstream element. This element cannot request a packet from the upstream element. A pull port implies that the element downstream requests the packet. The ToDevice element has a single PULL input, for instance, since it has to see when the outoing line is free and then request a packet from above and send it on the wire. PUSH outputs can only be connect to PUSH (or AGNOSTIC) inputs. PULL elements are analogous. AGNOSTIC ports can connect to either. A Queue element has a single PUSH input and a single PULL output. This corresponds to the notion that a queue must accept all packets handed down to it and continue to keep them until the element downstream requests a packet. 1.2 Click graphs The Click configuration file is a specification of a packet flow graph in the Click language. The language is simple and very intutive. Various tools are supplied with the Click toolkit to check that these configurations (graphs) satify certain properties. As an illustration, below is a specification for a router which checks each IP packet and then prints its contents FromDevice(eth0)->Strip(14)->CheckIPHeader->IPPRint(“eth0 : “)->Discard For more complex configurations and details on Click, see the Click homepage and refer to [1, 2] 2. Mobile-IP protocol Mobile nodes MUST be able to keep their IP address(es) to maintain existing transport connections. However the exisiting IP routing infrastructure assumes that IP datagrams should be routed based on their network prefix. An implemented solution is to establish a home agent who forwards IP datagrams meant for a mobile host, to a care-of-address on the visited foreign network. The datagram to be forwarded is first encapsulated in an IP packet using encapsulation techniques like IP-in-IP, GRE, minimal encapsulation, etc. The care-of-address may be a foreign agent on the foreign network or the mobile host itself, if has means of obtaining a valid IP address on the visited network (valid here means, it can be reached by traditional IP routing mechanisms). The care-of-address should be viewed as the endpoint of the IP tunnel. At the care-ofaddress, the packet is decapsulated and the internal datagram is retrieved. Subsequently if the care-ofaddress is different from the mobile host (foreign agent is the endpoint of the tunnel), the packet is sent over a link-layer connection to the mobile host. Operationally the protocol can be dissected into 3 portions. i) Agent advertisements and Solicitations ii) Registrations and Replies iii) Routing considerations 2.1 Agent Advertisements and Solicitations RFC 2002 (the specification of the Internet mobile-IP standard) requires home and foreign agents periodically generate agent advertisements and broadcast them over their agent interface. Agent interface, now and hereafter, will be used to refer to the interface on the agent, which is connected to the link on which it provides the serivices of an agent. The advertisements are actually extensions, appended to existing ICMP router advertisements. These advertisements must be sent to the all hosts on the link, multicast address 224.0.0.1 if the link supports multicast or the limited braodcast address 255.255.255.255, if it doesn’t. The standard recommends time intervals at which these advertisements should be generated. These advertisements also serve to let the mobile node know, on which network (home or foreign) it is, currently. A mobile host, when visiting a foreign network, or when it wishes to know the home agent address, may generate a Solicitation message. The agent solicitation message is identical to the ICMP Router Solicitation request, except it MUST have a TTL=1. This request is sent to the ‘all routers on link’ multicast address - 224.0.0.2 On receiving a solicitation, an agent MUST generate an advertisement and broadcast it (or unicast it to the node which requested it). 2.2 Registration and Replies A mobile host on discovering itself to be on a foreign network attempts to create a binding (forwarding entry) at the home (and foreign agent, if it is using one). Henceforth, the term foreign agent will be used mean an “actual” foreign agent, or the mobile host if it is performing the task of the foreign agent itself. The mobile host creates a Registration Request packet. This is a UDP packet with a registration extentsion appended after the UDP header (as the payload). This packet is delivered to the foreign agent by a link layer connection. It is assumed that the mobile host has means to discover the foreign agent address (either through advertisements or through link layer mechanisms such as DHCP). The foreign agent validates the request and relays it to the home agent. The home agent on receiving the request, validates it and creates a binding (or replaces an exisiting binding). At this point it MUST send a gratuitous ARP (see Routing Considerations 2.3). It then creates a Registration Reply (similar in form to the Registration Request) and sends it to the foreign agent (source of the request). The foreign agent then relays the reply to the mobile host. At this point the registration process is complete. It is important to note that each registration has an associated lifetime and automatically expires after its lifetime. Hence a mobile host MUST periodically re-register with the home agent (via the foreign agent). 2.3 Routing Considerations : The home agent must ensure that it receives packets destined for the mobile host. To this end, it must do proxy ARP replies on its agent interface for the mobile host and serve as the incoming router for outside packets that are destined for the mobile host. On receiving an acceptable regsitration request the home agent MUST send a (possibly more than one) gratuitous ARPs on its agent interface. The foreign agent should have a link layer connection with the mobile host (though other configrations where this is not true may work). All packets sent by the foreign agent to the mobile host must use a destination IP address that is the same as the mobile host’s home address. The mobile host must provide ARP replies to queries for its home address while visiting a foreign network. 3. Design To succesfully implement the protocol, I created the following elements: i) RouterAdvertise ii) AgentAdvertise iii) Home/Foreign Agent Central to the creation of new elements was Click’s philosophy on the role of elements. Click builds on idea of having small simple elements that do specific tasks. It is contrary to Click’s philosophy to create an unweildy sophisticated element. Small elements encourage element re-use at the userlevel, while enherited elements support code re-use at the programmer level. Further Click encourages elements to have different ports to serve different flows, for two reasons: i) ii) 3.1 Saving some time, from packet classification at each element Easier to use in flow graphs at the user level. RouterAdvertise [Appendix A] : This element was NOT strictly needed for the purpose of a mobile-IP implementation. However in tune with Click’s philosophy and with a view to strengthening the base for Click (since routers should create ICMP router advertisments and respond to solicitation requests), I felt it worthwile to create this element. Furthermore the AgentAdvertise was created as a sub-class of the RouterAdvertise class and a significant portion of the code was reused. The element code and design is simple. Since router advertisements should be generated both asynchronously and on receving a request, it became apparent that the element should have an optional single push input and necessary single PUSH output. In a fully compliant implementation of the RFC 1256 (the current router advertisement standard), the input should be connected so as to receive solicitation requests. On receiving a valid request an ICMP router advertisement packet is emitted on output 0. Irrespective of whether the input is connected (ie. whether a solictation request is received), an advertisement is generated periodically (of the order of a minute apart by default) and emitted on output 0. Schematically the element would be connected as follows : Router/Agent Advertisement Element ToDevice EtherEncap FromDevice Classifier AgentAdvertisement Queue A sample Click configuration file [Appendix A] uses this element with reasonable arguments to create ICMP packets and print them on the console. The output is connected with the EtherEncap element to create a link level broadcast packet. This packet is then sent on the agent interface. Since periodic messages must be generated, this element has code to pro-actively schedule itself at intervals. This is in addition to any scheduling which done implicitly as a result of solicitation requests. 3.2 AgentAdvertise : This is very similar to the RouterAdvertisement element. It differs only to the extent that the agent advertisement fields must be filled (RFC 1256) appropriately and appended to the ICMP message. As a consequence, it uses methods from the RouterAdvertise class. (AgentAdvertise is derived from RouterAdvertise). 3.3 Agents [Appendix B, C]: The design of these elements was non-trivial. Ultimately the following observations factored in the final design : The agents needed to receive packets directly from their resepective agent interfaces. The foreign agent didn’t nessarily need to receive packets directly from the interface since packets sent by the mobile host would have the correct IP addresses. Yet I felt it would be appropriate to connect one port directly to the interface, in case of the foreign agent, to parallel the case for the home agent. For the home agent a direct connection was a must, since it needed to receive packets for the mobile host on the local link using proxy ARPs. Since I could not reconfigure other elements, in an existing graph, this was the simplest way out. With this design, the foreign agent would receive registration requests on this port. The agents needed to have an output directly connected to their agent interface. In this case the home agent needed to send proxy ARPs on this interface. The foreign agent needed to send link layer messages to the mobile host. These messages would only get delivered if they were sent direcltly to the mobile host by a link layer connection, and bypassed the traditional IP routing mechanism. In this design the foreign agent sent registration replies and decapsulated packets to the mobile host, while the home agent sent proxy and gratuitous arps on this port. Both agents needed to receive a subset of the IP packets passing through the router. The home agent needed to receive registration requests and IP packets destined for the mobile host. The foreign agent needed to receive registration replies and encapsulated packets forwarded by the home agent. Home/Foreign Agent Element To Classifier 1 FromDevice eth1 IP 0 Strip FromDevice eth0 Classifier 1 ToDevice eth1 2 0 Agent LookUpIPRoute ARPResponder The above observations implied both agent elements would have : Input 0 (PUSH) : To receive ALL IP packets passing through the the router. Output 1 (PUSH) : To pass down IP packets which are not for the agent and also IP packets, the agent generates. Simply put, all outgoing IP packets are sent on this port. Input 1 (PUSH) : Link-layer packets received from the agent interface. Output 1(PUSH) : Un-processed link layer packets emitted from this port Output 2(PUSH) : For emitting link-layer packets on the agent interface. Since the elements were in the forwarding path of the router, it was imperative that the case when the packet was not destined for processing by the agent needed to be done fast. To this end, an early check is introduced to tackle this case. Two comparisons and one virtual function call is the only overhead of the agent elements for unprocessed elements(by the agents). 4. Test Configuration : For purposes of verifying the minimal functionality of the elements added, the following network was modelled in Click. The Click configuration file for this setup alongwith the run output generated by the router are provided in Appendix C. Schematic for agent.click InfiniteSource 0(in) 1(in) (out)0 InfiniteSource F H 1(out) Discard 0(out) 2 (out) 0(in) 1(out) Discard 2(out) Print Print Discard Discard The InfiniteSource element creates an initial registration request. This element simply passes the data specified in the configuration string of the element - in this case an Ethernet packet containing a registration request. The element was configured to send the request exactly once. The output of the InfiniteSource element is connected to the foreign agent element’s input 1 (which is to receive Ethernet packets from the agent interface). On receiving the request, the foreign agent element processes the request, and creates a temporary binding. Then it relays the request as an IP packet and pushes it down output 0. Recall, that output 0, was used to send all outgoing (non-local) IP packets by both agents. In our configuration the output 0, of the foreign agent is connected to the input 0 of the home agent (and vice versa). On receiving the registration request, the home agent creates a new binding for the mobile host, with the care-of-address of the foreign agent. Then a Registration Reply is created and emitted on output 0 of the home agent. This port, is connected to input 0 of the foreign agent. The foreign agent recognizes the Registration Reply, makes its temporary binding permanent and relays the reply to the mobile host by creating an Ethernet packet with the link layer address set to that, of the mobile host. This packet is then pushed down output 2 (the direct outgoing connection to the agent’s interface). At this point the registration is complete. In the test, at this stage another InfiniteSource is used to send a packet to the mobile host. This packet is intercepted by the home agent (on input 0); encapsulated and pushed on output 0. The foreign agent on receiving the encapsulated packet, locates an existing binding and emits it after decapsulation on output 2. Admittedly this test is not an actually conducted experiment. Yet, it captures the essence of the mobile-IP functionality required for the agents. Click provides in this case, an ideal inexpensive but reliable means to test the elements. The tests could be made comprehensive though, by incorporating stray IP packets for forwarding and invalid requests and replies. 5. Implementation features and limitations : As of consequence of limited time, only minimally functional mobile agents are created. Specifically, these elements should only be viewed as a starting point for a more rigourous implementation, befitting Click. These agents are NOT currently fully standard (RFC 2002) compliant and are woefully inadequate in terms of request reply validations and error messages generation. The agent code (in the source files) is annotated by comments which mention specifics regarding which features are missing and at which section of the code, changes need to be made. The following is a brief summary of the limitations. No authentication : RFC 2002 mandates minimally a keyed-MD5 authentication between the mobile host and the home agent. Simultaneous bindings unsupported : The standard doesn’t require simultaneous bindings. Error responses not generated : The standard requires and in some case recommends ICMP and UDP error messages in cases of unreachable home agents and invalid requests/replies. Time-out of bindings not performed : This is REQUIRED by the standard. Proxy and gratuitous ARPs by the home agent not generated : this is required by the standard, on creation of a new binding. The element code needs external documentation in form of man pages etc. Speed optmizations need to be performed : Such as using hash container classes and reducing the number of function calls to standard libraries. 6. Conclusions 6.1 Related Work : Mobile-IP agent and host implementations exist for Solaris and Linux. The Linux implementation by the MosquitoNet group at Berkeley provides enhancements to the protocol. More information regarding the standards themselves can be found in the appropriate RFCs 2002, 1256 Details regarding Click and the current release details can be found at the Click home page. 6.2 Final Observations : Much needs to be done to complete this mobile agent implementation. It is important to evaluate the cost of including these agents in the forwarding path of the router. Click provides an elegant and easy to use platform for both developing implementations for new protocols, as well as a higly configurable working router configuration. As of writing this paper, 1.2 was the latest release. It provides SMP support in the form of multiple threads of execution within the kernel module. In its current form, writing elements can be both fast and pleasurable. Bibliography 1. 2. 3. 4. Click - TOCS ’00 paper (ACM Transactions on Computer Systems 18(3), August 2000, pages 263-297). Eddie Kohler's thesis -This has more detail and examples of using Click than the TOCS paper IP Mobility Support – RFC 2002 ICMP Router Discovery Messages – RFC 1256 Appendix A RouterAdvertisement Element Code /* * Element periodically creates ICMP Router Discovery Advertisements and pushes * them on output 0. Can additionally also accept solicitation requests * on input 0. * Copyright (c) 2000-2001 University of Utah * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * Further elaboration of this license, including a DISCLAIMER OF ANY * WARRANTY, EXPRESS OR IMPLIED, is provided in the LICENSE file, which is * also accessible at http://www.pdos.lcs.mit.edu/click/license.html */ #include <iostream.h> #ifdef HAVE_CONFIG_H # include <config.h> #endif #include <click/config.h> #include <click/package.hh> #include "routeradvertise.hh" #include <click/confparse.hh> #include <click/error.hh> #include <click/glue.hh> #include <click/click_ip.h> #include <click/click_icmp.h> #include <click/packet_anno.hh> RouterAdvertise::RouterAdvertise() : Element(0, 1), _max_interval(600), _id(1), _timer(this) { MOD_INC_USE_COUNT; } RouterAdvertise::~RouterAdvertise() { MOD_DEC_USE_COUNT; } RouterAdvertise * RouterAdvertise::clone() const { return new RouterAdvertise; } void RouterAdvertise::notify_ninputs(int i) { if (i) add_input(); return ; } int RouterAdvertise::configure(const Vector<String> &conf, ErrorHandler *errh) { Vector<String> rest_conf ; unsigned int tlife = 0 ; unsigned int tmin = 0 ; /* temporaries */ int before = errh->nerrors(); for (int i = 0; i < conf.size(); i++) { IPAddress rip ; int pref ; Vector<String> words; cp_spacevec(conf[i], words); if (words.size() == 2) { if (cp_ip_address(words[0], &rip, this) && cp_integer(words[1], &pref)) { _ip_pref_t *ip_pref = new _ip_pref_t ; ip_pref->ip = rip ; ip_pref->pref = pref ; _router_pref.push_back(ip_pref); } else errh->error("argument %d, in incorrect format : should be IPADDR Preference", i); } else { // finished processing address preference pairs for (int j = i ; j < conf.size() ; j++) rest_conf.push_back(conf[j]); break ; } } if (cp_va_parse(rest_conf, this, errh, cpIPAddress, "source address for adverisements", &_src_ip, cpIPAddress, "Advertisement IP address - multicast/briadcast address of interface", &_dst_ip, cpOptional, cpKeywords, "LIFE", cpUnsigned, "lifetime (in seconds)", &tlife, "MAX", cpUnsigned, "max. interval (in seconds)", &_max_interval, "MIN", cpUnsigned, "min. interval (in seconds)", &tmin, 0) < 0) errh->error("Arguments given incorrectly"); if (_max_interval > 1800 || _max_interval < 4) errh->error("max. interval should lie between 4 and 1800 (refer RFC. 1256)"); // default values for these (RFC 1256) _min_interval = static_cast <unsigned int> (0.75 * _max_interval); _life = 3 * _max_interval ; if (tmin) _min_interval = tmin ; if (tlife) _life = tlife ; if (_min_interval > _max_interval || _min_interval < 3) errh->error("min. interval should lie between 3 and MAX_INTERVAL (%d) (refer RFC. 1256)", _max_interval); if (_life > 9000 || _life < _max_interval ) errh->error("LIFE should lie between MAX_INTERVAL (%d) and 9000s (refer RFC. 1256)", _max_interval); return (before==errh->nerrors() ? 0 : -1); } int RouterAdvertise::initialize(ErrorHandler *) { // technically, this should be randomized uniformly within the // the interval [_min_interval, _max_interval] - see RFC 1256 if (_max_interval > _min_interval) _interval = _min_interval + static_cast <unsigned int>(random() % (_max_interval-_min_interval+1)); else _interval = _max_interval ; for (int i=0 ; i< _router_pref.size() ; i++) cout << _router_pref[i]->ip.s().mutable_c_str() << "(" << _router_pref[i]->pref << ")" << endl ; cout << "lifetime=" << _life << "\t" << "max=" << _max_interval << "\t" << "min=" << _min_interval << endl ; cout << "src=" << _src_ip.s().mutable_c_str() << "\t" << "dst=" << _dst_ip.s().mutable_c_str() << endl ; _timer.attach(this); _timer.schedule_after_ms(_interval*1000); return 0; } void RouterAdvertise::uninitialize() { _timer.unschedule(); } void RouterAdvertise::run_scheduled() { WritablePacket *q = Packet::make(sizeof(click_ip) + sizeof(struct icmp_router_advertisement) + _router_pref.size()*8); memset(q->data(), '\0', q->length()); click_ip *nip = reinterpret_cast<click_ip *>(q->data()); nip->ip_v = 4; nip->ip_hl = sizeof(click_ip) >> 2; nip->ip_len = htons(q->length()); nip->ip_id = htons(_id++); nip->ip_p = IP_PROTO_ICMP; /* icmp */ nip->ip_ttl = 1; /* all advertisements have TTL 1 (RFC 1256) */ nip->ip_src = _src_ip; nip->ip_dst = _dst_ip; nip->ip_sum = in_cksum((unsigned char *)nip, sizeof(click_ip)); struct icmp_router_advertisement *icp = (struct icmp_router_advertisement *) (nip + 1); icp->icmp_type = ICMP_ROUTER_ADVERTISEMENT; icp->icmp_code = 0; icp->num_addrs = _router_pref.size(); icp->addr_entry_size = 2 ; /* size of each pair : RFC 1256 */ icp->lifetime = htons(_life); // stuff in the router:pref pairs _ip_pref_t *rpref = reinterpret_cast<_ip_pref_t *> (icp+1); for (int i = 0 ; i < _router_pref.size() ; i++) { rpref->ip = _router_pref[i]->ip ; rpref->pref = htonl(_router_pref[i]->pref) ; rpref++ ; } icp->icmp_cksum = in_cksum((unsigned char *)icp, (sizeof(struct icmp_router_advertisement) + _router_pref.size())); q->set_dst_ip_anno(IPAddress(_dst_ip)); q->set_ip_header(nip, sizeof(click_ip)); output(0).push(q); // generate a random interval if (_max_interval > _min_interval) _interval = _min_interval + static_cast <unsigned int>(random() % (_max_interval-_min_interval+1)); else _interval = _max_interval ; _timer.schedule_after_ms(_interval*1000); } // Actually we should be checking, that this is a valid solicitation void RouterAdvertise::push(int, Packet *p) { if (p) p->kill(); _timer.unschedule(); this->run_scheduled(); return ; } EXPORT_ELEMENT(RouterAdvertise) #ifndef ROUTERADVERTISE_HH #define ROUTERADVERTISE_HH #include <click/element.hh> #include <click/timer.hh> class RouterAdvertise : public Element { typedef struct { IPAddress ip ; int pref ; } _ip_pref_t ; Vector <_ip_pref_t *>_router_pref ; protected : unsigned int _max_interval; unsigned int _min_interval; unsigned int _life ; unsigned int _interval ; unsigned int _id ; IPAddress _src_ip ; IPAddress _dst_ip ; Timer _timer; public: RouterAdvertise(); ~RouterAdvertise(); const char *class_name() const const char *processing() const { return "RouterAdvertise"; } { return PUSH; } RouterAdvertise *clone() const; void notify_ninputs(int); int configure(const Vector<String> &, ErrorHandler *); int initialize(ErrorHandler *); void uninitialize(); void run_scheduled(); void push(int, Packet*); }; #endif AgentAdvertise Element Code /* * agentadvertise.cc * * Element periodically emits agent advertisements on output 0. Additionally * It can also accept solicitations on input 0 * * Copyright (c) 2000-2001 University of Utah * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * Further elaboration of this license, including a DISCLAIMER OF ANY * WARRANTY, EXPRESS OR IMPLIED, is provided in the LICENSE file, which is * also accessible at http://www.pdos.lcs.mit.edu/click/license.html */ #include <iostream.h> #ifdef HAVE_CONFIG_H # include <config.h> #endif #include <click/config.h> #include <click/package.hh> #include "agentadvertise.hh" #include <click/confparse.hh> #include <click/error.hh> #include <click/glue.hh> #include <click/click_ip.h> #include <click/click_icmp.h> #include <click/packet_anno.hh> #include <click/ipaddressset.hh> AgentAdvertise::AgentAdvertise() : RouterAdvertise(), _reg_life(0xffff), _encap(IP_in_IP), _pad(true), _router(true), _seq(0), _reg_req(false) { MOD_INC_USE_COUNT; } AgentAdvertise::~AgentAdvertise() { MOD_DEC_USE_COUNT; } /* infinity - RFC 2002 */ AgentAdvertise * AgentAdvertise::clone() const { return new AgentAdvertise; } int AgentAdvertise::configure(const Vector<String> &conf, ErrorHandler *errh) { String agent, encap ; IPAddressSet care_set ; unsigned int tlife = 0 ; unsigned int tmin = 0 ; /* temporaries */ int before = errh->nerrors(); if (cp_va_parse(conf, this, errh, cpWord, "HOME/FOREIGN (agent)", &agent, cpIPAddress, "source address for adverisements", &_src_ip, cpIPAddress, "Advertisement IP address - multicast/briadcast address of interface", &_dst_ip, cpIPAddressSet, "one or more care-of-addresses, in case of foreign agents ONLY\n" "In case of home agents give 0.0.0.0", &care_set, cpOptional, cpKeywords, "REG_LIFE", cpUnsigned, "registration lifetime (in seconds)", &_reg_life, "REG_REQ", cpBool, "registration required ? in case of foreign agents ONLY", &_reg_req, "ENCAP", cpWord, "MINIMAL/GRE encapsulation of tunelled packets", &encap, "PAD", cpBool, "pad ? (to even bytes for ICMP message)", &_pad, "DEFAULT_ROUTER", cpBool, "can the agent be used as a default router ?", &_router, "ADV_LIFE", cpUnsigned, "advertisement lifetime (in seconds)", &tlife, "MAX", cpUnsigned, "max. interval (in seconds)", &_max_interval, "MIN", cpUnsigned, "min. interval (in seconds)", &tmin, 0) < 0) errh->error("Arguments given incorrectly"); // set values for ICMP Router advertisement portion if (_max_interval > 1800 || _max_interval < 4) errh->error("max. interval should lie between 4 and 1800 (refer RFC. 2002)"); // default values for these (RFC 2002) _min_interval = static_cast <unsigned int> (0.75 * _max_interval); _life = 3 * _max_interval ; if (tmin) _min_interval = tmin ; if (tlife) _life = tlife ; if (_min_interval > _max_interval || _min_interval < 3) errh->error("min. interval should lie between 3 and MAX_INTERVAL (%d) (refer RFC. 2002)", _max_interval); if (_life > 9000 || _life < _max_interval ) errh->error("LIFE should lie between MAX_INTERVAL (%d) and 9000s (refer RFC. 2002)", _max_interval); /* agent advertisement portion */ if (agent.upper() == "HOME") _agent = HOME ; else if (agent.upper() == "FOREIGN") _agent = FOREIGN ; else errh->error("Agent should either be HOME or FOREIGN"); if (_agent == FOREIGN) { if (!care_set.size()) errh->error("Foreign agents MUST have one or more care-ofaddresses"); else { _care_of_addrs = care_set.list_copy(); _num_care_of_addrs = care_set.size(); } } if (_agent == HOME) { if (_reg_req) errh->error("REG_REQD is ONLY for foreign agents"); if (care_set.size() > 1) errh->error("care-of-addresses are ONLY for foreign agents, apart from 0.0.0.0"); _care_of_addrs = NULL ; _num_care_of_addrs = 0; } if (encap.upper() == "GRE") _encap = GRE ; else if (encap.upper() == "MINIMAL") _encap = MINIMAL ; return (before==errh->nerrors() ? 0 : -1); } int AgentAdvertise::initialize(ErrorHandler *) { // technically, this should be randomized uniformly within the // the interval [_min_interval, _max_interval] - see RFC 1256 if (_max_interval > _min_interval) _interval = _min_interval + static_cast <unsigned int>(random() % (_max_interval-_min_interval+1)); else _interval = _max_interval ; _timer.attach(this); _timer.schedule_after_ms(_interval*1000); return 0; } void AgentAdvertise::uninitialize() { _timer.unschedule(); } void AgentAdvertise::run_scheduled() { WritablePacket *q = Packet::make(sizeof(click_ip) + sizeof(struct icmp_router_advertisement) + sizeof(agent_adv_ext_t) + _num_care_of_addrs*4); memset(q->data(), '\0', q->length()); /* IP fields */ click_ip *nip = reinterpret_cast<click_ip *>(q->data()); nip->ip_v = 4; nip->ip_hl = sizeof(click_ip) >> 2; nip->ip_len = htons(q->length()); nip->ip_id = htons(_id++); nip->ip_p = IP_PROTO_ICMP; /* icmp */ nip->ip_ttl = 1; /* all advertisements have TTL 1 (RFC 2002) */ nip->ip_src = _src_ip; nip->ip_dst = _dst_ip; nip->ip_sum = in_cksum((unsigned char *)nip, sizeof(click_ip)); /* ICMP router advertisement fields */ struct icmp_router_advertisement *icp = reinterpret_cast <struct icmp_router_advertisement *> (nip + 1); icp->icmp_type = ICMP_ROUTER_ADVERTISEMENT; if (_router) icp->icmp_code = 0; else icp->icmp_code = 16; /* we could have added router addresses, see RFC 2002 */ icp->num_addrs = 0; icp->addr_entry_size = 2 ; /* size of each pair : RFC 1256 */ icp->lifetime = htons(_life) ; /* Agent advertisement extension fields : RFC 2002 */ agent_adv_ext_t *ext = reinterpret_cast <agent_adv_ext_t *> (icp+1); ext->type = 16 ; ext->length = 6 + 4*_num_care_of_addrs ; ext->seq = htons(_seq) ; if (_seq == 0xffff) different from boot time */ _seq = 256 ; else _seq++ ; ext->lifetime = htons(_reg_life); ext->req = _reg_req ; ext->busy = 0; ext->home = (_agent==HOME ? 1 : 0); ext->foreign = (_agent==FOREIGN ? 1:0); ext->minimal = (_encap==MINIMAL); ext->gre = (_encap==GRE); ext->vj = 0; enabled */ ext->reserved = 0; uint *addr = reinterpret_cast <uint *> (ext+1); for (int i=0; i<_num_care_of_addrs ; i++) *(addr++) = _care_of_addrs[i]; /* after roll-over, /* this could be /* now compute the ICMP checksum */ icp->icmp_cksum = in_cksum((unsigned char *)icp, (sizeof(struct icmp_router_advertisement)) + (sizeof(agent_adv_ext_t)) + _num_care_of_addrs*4); q->set_dst_ip_anno(IPAddress(_dst_ip)); q->set_ip_header(nip, sizeof(click_ip)); output(0).push(q); // generate a random interval if (_max_interval > _min_interval) _interval = _min_interval + static_cast <unsigned int>(random() % (_max_interval-_min_interval+1)); else _interval = _max_interval ; _timer.schedule_after_ms(_interval*1000); } // Actually we should be checking, that this is a valid solicitation void AgentAdvertise::push(int, Packet *p) { if (p) p->kill(); _timer.unschedule(); this->run_scheduled(); return ; } EXPORT_ELEMENT(AgentAdvertise) /* agentadvertise.hh */ #ifndef AGENTADVERTISE_HH #define AGENTADVERTISE_HH #include <click/element.hh> #include <click/timer.hh> #include "routeradvertise.hh" class AgentAdvertise : public RouterAdvertise { private : typedef enum { HOME, FOREIGN } agent_t ; typedef enum { IP_in_IP, MINIMAL, GRE } encap_t ; typedef struct { unsigned char type ; unsigned char length ; unsigned short seq ; unsigned short lifetime ; #if __BYTE_ORDER == __BIG_ENDIAN unsigned char req:1 ; /* registration required */ unsigned char busy:1 ; unsigned char home:1; unsigned char foreign:1; unsigned char minimal:1; /* encapsulation */ unsigned char gre:1; unsigned char vj:1; /* Van Jacobson compression */ unsigned char fill:1; /* 0 */ #else unsigned char fill:1; /* 0 */ unsigned char vj:1; /* Van Jacobson compression */ unsigned char gre:1; unsigned char minimal:1; /* encapsulation */ unsigned char foreign:1; unsigned char home:1; unsigned char busy:1 ; unsigned char req:1 ; /* registration required */ #endif unsigned char reserved ; /* 0 */ /* zero or more care-of-addresses follow */ } agent_adv_ext_t ; agent_t _agent ; ushort _reg_life ; encap_t _encap ; bool _pad ; bool _router ; unsigned short _seq ; /* used only for foreign agents */ /* default router ? */ /* agent advertisement number */ uint *_care_of_addrs ; ushort _num_care_of_addrs ; bool _reg_req ; public: AgentAdvertise(); ~AgentAdvertise(); const char *class_name() const const char *processing() const { return "AgentAdvertise"; } { return PUSH; } AgentAdvertise *clone() const; int configure(const Vector<String> &, ErrorHandler *); int initialize(ErrorHandler *); void uninitialize(); void run_scheduled(); void push(int, Packet*); }; #endif Sample Click Configuration File Using Advertisements /* adv.click */ /* RouterAdvertise(192.168.123.116 255, 10.0.0.31 65535, 192.168.123.116, 224.0.0.1, MAX=10, LIFE=100, MIN=3) */ AgentAdvertise( HOME, 192.168.123.116, 224.0.0.1, 0.0.0.0, REG_LIFE=255, MAX=5, ENCAP=GRE, DEFAULT_ROUTER=false, MIN=5 ) ->IPPrint("Adv", CONTENTS=hex, ID=true) ->Discard; Appendix B HomeAgent Element Code /* * homeagent.cc * * HomeAgent implements a mobile home agent. * Expects IP packets on input 0 and Ethernet frames from agent interface on input 1 * Emits unprocessed and newly created IP packets on output 0 * Emits unprocessed Ethernet frames from input 1, on output * Creates Proxy and Gratuitous ARPs on output 1 * * Copyright (c) 2000-2001 University of Utah * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * Further elaboration of this license, including a DISCLAIMER OF ANY * WARRANTY, EXPRESS OR IMPLIED, is provided in the LICENSE file, which is * also accessible at http://www.pdos.lcs.mit.edu/click/license.html */ #ifdef HAVE_CONFIG_H # include <config.h> #endif #include <click/config.h> #include <click/package.hh> #include <click/ipaddress.hh> #include <click/ipaddressset.hh> #include <click/etheraddress.hh> #include <click/click_ether.h> #include <click/click_ip.h> #include <click/click_udp.h> #include "homeagent.hh" #include <click/confparse.hh> #include <click/error.hh> #include <click/glue.hh> #include "elements/standard/alignmentinfo.hh" #ifdef __KERNEL__ # include <net/checksum.h> #endif #include <iostream> // input 0 - IP packets from the outside world // input 1 - Ethernet packets from the interface, where we are a home agent // output 0 - IP packets created by us, and ALSO those passed from above // output 1 - Ethernet packets from the home agent interface, which we // don't process // output 2 - Proxy/gratuitous ARPs sent on this interface // // Limitations : // Only IP-in-IP encapsulation supported for the time being (Not a MUST) // VJ header compression unsupported (not a MUST) // Mobile-Home auth. not performed - keyed-MD5 (MUST) // No response sent to mobile node/foreign agent // denying the registration request (for whatever ground) (SHOULD/MUST) // No Home-Foreign auth. supported (not a MUST) // Dynamic Home Agent resolution not supported (MAY) // Multiple simultaneous bindings not supported // Do not check if the packet is a mis-routed packet // sent by the foreign agent. In such a case the packet // will be an encapsulated one with the outer IP dst // address == inner dst address == mobile node's home address // Broadcast packets not forwarded (SHOULD) // // // // // // // // // // // Misc. things TO DO (NOW) Set timer to expire bindings - both requested ones and ones which have been setup (MUST) Make it possible to specify the list of IPs which can register with us, using a mask. Gratuitous ARP to be performed (MUST), for successful registrations AND deregistrations (sender link layer address should be set to our agent interface link address) The ARPs must be broadcast over the local link and SHOULD be retransmitted a small number of times Proxy ARP MUST be performed const int HomeAgent::UDP_DST_PORT = 434 ; HomeAgent::HomeAgent() : Element(2, 3), _reg_ips(NULL), _n_reg_ips(0), _our_ips(NULL), _n_our_ips(0), _life_max(1800), _check_reg_ips(true), _verbose(false) { MOD_INC_USE_COUNT; } HomeAgent::~HomeAgent() { MOD_DEC_USE_COUNT; delete [] _reg_ips ; delete [] _our_ips ; for (int i=0; i<_mh_list.size(); i++) delete [] _mh_list[i]; } HomeAgent * HomeAgent::clone() const { // RFC 2002 return new HomeAgent; } // Add arguments to filter out IP/masks and/or accept IP/masks // for registration requests int HomeAgent::configure(const Vector<String> &conf, ErrorHandler *errh) { int before = errh->nerrors(); if (cp_va_parse (conf, this, errh, cpIPAddressSet, "Acceptable mobile IP addresses", &_reg_ipsset, cpIPAddress, "outgoing IP address (IP src field)", &_canonical_out_ip, cpIPAddress, "IP address for agent interface", &_agent_ip, cpEthernetAddress, "ethernet for agent interface", &_agent_ether, cpOptional, cpIPAddressSet, "Our unicast addresses", &_our_ipsset, cpKeywords, "LIFE", cpUnsigned, "max. registration lifetime(in seconds)",&_life_max, "VERBOSE", cpBool, "display what's going on ?", &_verbose, 0) < 0) return -1; _n_reg_ips = _reg_ipsset.size(); _reg_ips = _reg_ipsset.list_copy(); if (_n_reg_ips==1 && !_reg_ips[0]) _check_reg_ips = false ; if (!_canonical_out_ip) errh->error("Outgoing IP cannot be 0.0.0.0 !!!"); if (!_agent_ip) errh->error("Agent IP cannot be 0.0.0.0 !!!"); if (!_agent_ether) errh->error("Agent ethernet address cannot be 00:00:00:00:00:00 !!!"); // make sure our canonical outgoing IP is in this if (!_our_ipsset.contains(_canonical_out_ip)) _our_ipsset.insert(_canonical_out_ip); _n_our_ips = _our_ipsset.size(); _our_ips = _our_ipsset.list_copy(); return (errh->nerrors() == before ? 0 : -1); } int HomeAgent::initialize(ErrorHandler *) { print_config(); return 0; } // IP packtes and registration requests (UDP ) // arive on input port 0. Other IP packets also come from here // and those MUST be passed down on output port 0, ASAP. // // de-registration (and possibly other) Ethernet packet arrive on input port 1 // Those of these which are destined for the mobile host MUST be forwarded void HomeAgent::push(int port, Packet *p) { bool for_us = false ; // could be a request/some passing packet bool for_mh = false ; // for mobile host click_ip *ip ; click_ether *ether; // packet destination switch (port) { case 0 : // Only IP packets come here ip = reinterpret_cast <click_ip *>(p->data()); // is the packet for us ? for_us = _our_ipsset.contains(IPAddress(ip->ip_dst)); for_mh = _mobile_ipsset.contains(IPAddress(ip->ip_dst)); // If it isn't for us, just forward it. if (!for_us && !for_mh) break ; if (for_mh) { mk_ipip(p); return ; } if (for_us && ip->ip_p == IP_PROTO_UDP) { click_udp *udp = reinterpret_cast <click_udp *>(ip+1); if (udp->uh_dport == htons(UDP_DST_PORT)) { handle_request(p); return ; } } break ; case 1 : // Following packets (ONLY Ethernet packets) come here : // a) packets to be forwarded to the MH // b) de-registration requests by MH // c) passing packets to be sent on output(1) // For (a) and (b), we strip the header and handle as for case `0' ether = reinterpret_cast <click_ether *> (p->data()); // is it an IPv4 packet ? if (ether->ether_type != htons(ETHERTYPE_IP)) goto PUSH_ETH ; ip = reinterpret_cast <click_ip *>(ether+1); for_us = _our_ipsset.contains(IPAddress(ip->ip_dst)); for_mh = _mobile_ipsset.contains(IPAddress(ip->ip_dst)); // If it isn't for us, just forward it. if (!for_us && !for_mh) goto PUSH_ETH ; if (for_mh) { // strip the ethernet header p->pull(14); mk_ipip(p); return ; } if (for_us && ip->ip_p == IP_PROTO_UDP) { click_udp *udp = reinterpret_cast <click_udp *>(ip+1); if (udp->uh_dport == htons(UDP_DST_PORT)) { // ALL packets sent to this port are ASSUMED to // be registration requests - see Assigned Numbers (rfc 790) // strip ethernet header p->pull(14); handle_request(p); return ; } } PUSH_ETH : // If control reaches here, then it is an Ethernet packet, but // not for us cerr << "Ethernet packet, but not for us. Forwarding it" << endl; output(1).push(p); return ; } // switch // if the packet comes till here, means it isn't a packet for us cerr << "forwarding IP packet - ain't for us" << endl ; output(0).push(p); return ; } /************************ Private Methods ***************************/ /********************************************************************/ // // // // // // // // // // // // // // // // If care-of-address == home_address of mobile node and lifetime==0 delete ALL bindings of this host If care-of-address != home address and lifetime==0 then only delete the specific entry If the lifetime!=0, then add new entry (/replace old entries) Currently we don't support simultaneous bindings, so we SHOULD generate a successful reply with code=1 If an old entry is being replaced, DON'T send a (de-)registration message to the earlier foreign agent. The RFC allows a message of this sort to be sent, only if we share security information with the foreign agent If the lifetime requested than our maximum permissible, send a successful reply with a lifetime set to our maximum If the registration is a duplicate (home_addr, cof, id match), then we MUST not extend the lifetime New bindings require that the ARP procedures MUST be followed // MUST generate a reply in the above cases // Registration replies : // -------------------// IP src : Copied from IP dst of request, provided it is not // not a broadcast/multicast address. In such a case // set it to our unicast address // NOTE : right now we are using our canonical outgoing IP // since we don't want to check if the address // is unicast/multicast // IP dst : Copied from IP src of request // If the mobile host has returned to his home network, // then the de-registering request will have the IP src // field set, to the home address of the mobile host // In such a case the reply must be sent directly on the // home network and all bindings MUST be ignored. // Even if request is rejected, the reject message MUST // ignore the binding // UDP src : Copied from UDP dst of request // UDP dst : Copied from UDP src of request // Lifetime : Copied from request, unless it is greater than our max. // Home Address : Copied from request // Home Agent : Copied from request, unless it is a multicast address, // in which case the request is rejected // MUST Re-do the UDP checksum calculation since we modify the UDP // source port // Expects an IP packet to UDP port 434 void HomeAgent::handle_request(Packet *p) { click_ip *ip = reinterpret_cast <click_ip *>(p->data()); click_udp *udp = reinterpret_cast <click_udp *>(ip+1); reg_req_t *req = reinterpret_cast <reg_req_t *>(udp+1); if (validate_req(p) <0) return ; // control reaches here only if this is a valid registration request if (_verbose) { click_chatter("Received the following REGISTRATION REQUEST"); dump_packet(p, IP_T); } // if we supported multiple bindings the we'd also need // to pass the cof_addr to get_entry() mh_entry_t *mh = get_entry(req->home_addr); if (mh) { if (_verbose) click_chatter("(H) : Existing entry found : %s -> %s, life=%d", IPAddress(mh->home_addr).s().mutable_c_str(), IPAddress(mh->cof_addr).s().mutable_c_str(), mh->life); // this could be one of 3 requests // a) de-registering // b) re-registering to renew lifetime // c) change of care-of-address // De-registering if (req->lifetime==0 && ((IPAddress(req->cof_addr))==mh->cof_addr || (IPAddress(req->cof_addr)==mh->home_addr))) { // MUST send gratuitous ARP here !! // remove the binding remove_entry(mh->home_addr); _mobile_ipsset.remove(mh->home_addr); if (_verbose) click_chatter("(H) : Request to de-register : REMOVED binding\n" "%s->%s DEAD !!!", IPAddress(mh->home_addr).s().mutable_c_str(), IPAddress(mh->cof_addr).s().mutable_c_str()); goto PUSH_REPLY ; } // renewing life if ((req->lifetime>0) && (IPAddress(req->cof_addr) == mh->cof_addr)) { mh->life = min(_life_max, ntohs(req->lifetime)); if (_verbose) click_chatter("(H) : Re-registering : %s->%s | Lifetime set to %d", IPAddress(mh->home_addr).s().mutable_c_str(), IPAddress(mh->cof_addr).s().mutable_c_str(), mh->life); goto PUSH_REPLY ; } // replacing entry if ((req->lifetime>0) && (IPAddress(req->cof_addr) != mh->cof_addr)) { mh->life = min(_life_max, ntohs(req->lifetime)); mh->cof_addr = req->cof_addr ; if (_verbose) click_chatter("(H) : Changed binding : %s->%s (new) | Life = %d", IPAddress(mh->home_addr).s().mutable_c_str(), IPAddress(mh->cof_addr).s().mutable_c_str(), mh->life); goto PUSH_REPLY ; } } else { /* new binding needs to be created */ mh = new mh_entry_t ; mh->life = min(_life_max, ntohs(req->lifetime)); mh->home_addr = req->home_addr ; mh->cof_addr = req->cof_addr ; _mobile_ipsset.insert(mh->home_addr); mh->id = req->id ; _mh_list.push_back(mh); if (_verbose) click_chatter("(H) : Created NEW binding : %s->%s | Life = %d", IPAddress(mh->home_addr).s().mutable_c_str(), IPAddress(mh->cof_addr).s().mutable_c_str(), mh->life); // Send gratuitous ARP here !!! // send packet ... } PUSH_REPLY : // send the reply u_short tport = udp->uh_dport ; udp->uh_dport = udp->uh_sport ; udp->uh_sport = tport ; // MUST recompute the UDP checksum ! ip->ip_dst = ip->ip_src ; ip->ip_src = _canonical_out_ip ; ip->ip_ttl = (ip->ip_ttl < 5 ? 64 : ip->ip_ttl-1); // ip->ip_len unchanged ip->ip_sum = 0; ip->ip_sum = in_cksum((u_char *)ip, sizeof(click_ip)); p->clear_annotations(); p->set_dst_ip_anno(IPAddress(ip->ip_dst)) ; if (_verbose) { click_chatter("(H) : Sending the following registration REPLY"); dump_packet(p, IP_T); } output(0).push(p); return ; } // expects IP-in-IP encapsulated packets // ip_len void HomeAgent::mk_ipip(Packet *p) const { if (validate_ipip(p) <0) return ; if (_verbose) { click_chatter("(H) : Received the following packet to be forwarded to MH"); dump_packet(p, IP_T); } click_ip *ip = reinterpret_cast <click_ip *> (p->data()); mh_entry_t *mh = get_entry(IPAddress(ip->ip_dst)); assert(mh); if (_verbose) click_chatter("(H) : Using binding to encapsulate: %s -> %s", IPAddress(mh->home_addr).s().mutable_c_str(), IPAddress(mh->cof_addr).s().mutable_c_str()); // wrap in an IP packet WritablePacket *q = Packet::make(sizeof(click_ip) + p->length()); memcpy((q->data()+sizeof(click_ip)), p->data(), p->length()); ip = reinterpret_cast <click_ip *> (q->data()); ip->ip_dst = mh->cof_addr ; ip->ip_src = _canonical_out_ip ; ip->ip_p = IP_PROTO_IPIP ; ip->ip_ttl = 255 ; ip->ip_tos = 0; ip->ip_len = htons(p->length()+sizeof(click_ip)); ip->ip_off = 0; ip->ip_sum = 0; ip->ip_sum = in_cksum((u_char *)ip, sizeof(click_ip)); q->set_dst_ip_anno(mh->cof_addr); p->kill(); if (_verbose) { click_chatter("(H) : Sending the following packet AFTER encapsulation"); dump_packet(q, IPIP_T); } // send the packet on our agent inteface output(0).push(q); return ; } // // // // // // // // // // // // // // // // // // // // // // // // // Silently discard, if non-zero UDP checksum Check the Mobile-Home authentication. Rejects SHOULD generate replies with code 131 Validate the `id' using the SPI field. Rejects SHOULD generate replies with code 133 If Foreign-Home authentication is supported and fails, reply with 132 If the request is to the subnet broadcast address, reply with 136 AND our unicast address - see dynamic agent resolution, RFC 2002 Until we support, multiple simultaneous bindings, any requests with the `S' bit set, should make us send the REPLY with code 1 Is it within our ACCEPT set ? Destination UDP port == 434 ? Deny request if : i) Requested lifetime too long (denial contains the acceptable value, 69) ii) poorly formed request/reply iii) Requested encapsulation/compression unavailable v) Non-zero reserved field (return status code 70) The registration fields from the original request must be copied. IP source address for denials, is copied from the request(destination) IP dest address for denials, is copied from the request(src) UDP source port for denials is 434 UDP dest port for denials, is the src UDP port of request For co-located COF, the IP source address MUST be the COF Otherwise the IP source address MUST be the home address The link layer destination address MUST be the agent's unicast address // // // // // // // IP destination address is either agent's interface IP address/224.0.0.11 If IP destination address is 224.0.0.11 then TTL MUST be 1. `D' bit set iff a co-located COF address is used. Requested lifetime, MUST not exceed that, which was advertised by the agent Encapsulation MUST be supported. Type MUST be 1 int validate_req(Packet *p); // Silently discard if low-order 32-bits of `id' don't match // UDP destination, should match the outgoing port the request // was relayed from // reply->type == 3 (for Registration Reply) // Act upon the denial codes // Discard, if invalid non-zero UDP checksum // Replies to non-pending requests must be silently discarded //int validate_reply(Packet *p); // Used to retrieve the entry from the visitor list, whose // home address matches `ip' // This should be implemented using C++ hash_map container class //mh_entry_t * HomeAgent::mh_entry_t * HomeAgent::get_entry(IPAddress ip) const { for (int i = 0; i< _mh_list.size() ; i++) if (_mh_list[i]->home_addr == ip) return _mh_list[i]; return NULL ; } int HomeAgent::remove_entry(IPAddress ip) { for (int i = 0; i< _mh_list.size() ; i++) if (_mh_list[i]->home_addr == ip) { _mh_list[i]=_mh_list[_mh_list.size()-1]; _mh_list.pop_back(); return 0; } return -1 ; } void HomeAgent::print_config(void) const { cerr << class_name() << endl << "-----------------" << endl ; for (int i=0 ; i<_n_reg_ips ; i++) cerr << "_reg_ips[" << i << "]=" << IPAddress(_reg_ips[i]).s().mutable_c_str()<< endl ; for (int i=0 ; i<_n_our_ips ; i++) cerr << "_our_ips[" << i << "]=" << IPAddress(_our_ips[i]).s().mutable_c_str()<< endl ; cerr << "Agent IP : " << _agent_ip.s().mutable_c_str() << endl << "Agent Ethernet Address : " << _agent_ether.s().mutable_c_str() << endl << "Canonical outgoing IP : " << _canonical_out_ip.s().mutable_c_str() << endl << "Lifetime = " << _life_max << endl << "Check mobile host IPs ? " << _check_reg_ips << endl << "Verbose = " << _verbose << endl << "-----------------" << endl ; return ; } void HomeAgent::dump_packet(const Packet *p, packet_t type) const { click_ether *eth = NULL ; click_ip *ip = NULL ; click_ip *ipip = NULL ; click_udp *udp = NULL ; reg_req_t *reg = NULL ; click_chatter("(H) : ************* PACKET DUMP *****************"); switch(type) { case IPIP_T : ip = reinterpret_cast<click_ip *>(p->data()); assert(ip->ip_p == IP_PROTO_IPIP); click_chatter("(H) : IP-in-IP encapsulated packet\n" " ----------------------------"); click_chatter("(H) : Outer-IP\n" " --------"); click_chatter("(H) : Length=%d | id=%d | off=%d | ttl=%d | " "Proto=%d | Chksum=%d\n" " src=%s | dst=%s", htons(ip->ip_len), htons(ip->ip_id), htons(ip->ip_off), ip->ip_ttl, ip->ip_p, htons(ip->ip_sum), IPAddress(ip->ip_src).s().mutable_c_str(), IPAddress(ip->ip_dst).s().mutable_c_str()); ipip = reinterpret_cast <click_ip *>(ip+1); click_chatter("(H) : Inner-IP\n" " --------"); click_chatter("(H) : Length=%d | id=%d | off=%d | ttl=%d | " "Proto=%d | Chksum=%d\n" " src=%s | dst=%s", htons(ipip->ip_len), htons(ipip->ip_id), htons(ipip->ip_off), ipip->ip_ttl, ipip->ip_p, htons(ipip->ip_sum), IPAddress(ipip->ip_src).s().mutable_c_str(), IPAddress(ipip->ip_dst).s().mutable_c_str()); break ; case ETHER_T : eth = reinterpret_cast<click_ether *>(p->data()); click_chatter("(H) : Ethernet\n" " --------"); click_chatter("(H) : src=%s | dst=%s | type=%d", EtherAddress(eth->ether_shost).s().mutable_c_str(), EtherAddress(eth->ether_dhost).s().mutable_c_str(), ntohs(eth->ether_type)); if (ntohs(eth->ether_type)!= ETHERTYPE_IP) break; ip = reinterpret_cast <click_ip *> (eth+1); case IP_T : if (!ip) ip = reinterpret_cast <click_ip *>(p->data()); click_chatter("(H) : IP\n" " --"); click_chatter("(H) : Length=%d | id=%d | off=%d | ttl=%d | " "Proto=%d | Chksum=%d\n" " src=%s | dst=%s", htons(ip->ip_len), htons(ip->ip_id), htons(ip->ip_off), ip->ip_ttl, ip->ip_p, htons(ip->ip_sum), IPAddress(ip->ip_src).s().mutable_c_str(), IPAddress(ip->ip_dst).s().mutable_c_str()); if (ip->ip_p == IP_PROTO_UDP) udp = reinterpret_cast<click_udp*> (ip+1); case UDP_T : if (!udp) break ; click_chatter("(H) : UDP\n" " ---"); click_chatter("(H) : uh_sport=%d | uh_dport=%d | len=%d | chksum=%d", htons(udp->uh_sport), htons(udp->uh_dport), htons(udp->uh_ulen), htons(udp->uh_sum)); if (htons(udp->uh_ulen) > 20) reg = reinterpret_cast<reg_req_t *>(udp+1); case REG_T : if (!reg) break ; if (reg->type!=1 && reg->type!=3) break; click_chatter("(H) : Registration details\n" " --------------------"); click_chatter("(H) : type=%d | code=SKIPPED | life=%d | " "home_addr=%s | home_agent=%s", reg->type, htons(reg->lifetime), IPAddress(reg->home_addr).s().mutable_c_str(), IPAddress(reg->home_agent).s().mutable_c_str()); if (reg->type == 1) /* registration request */ click_chatter("(H) : cof=%s", IPAddress(reg->cof_addr).s().mutable_c_str()); } click_chatter("(H) : *******************************************"); return; } EXPORT_ELEMENT(HomeAgent) /* homeagent.hh */ #ifndef HOMEAGENT_HH #define HOMEAGENT_HH #include #include #include #include #include <click/element.hh> <click/ipaddress.hh> <click/ipaddressset.hh> <click/etheraddress.hh> <click/glue.hh> #ifndef min(a,b) #define min(a,b) (((a) > (b)) ? (b) : (a)) #endif class HomeAgent : public Element { typedef struct { u_char type ; #if __BYTE_ORDER == __LITTLE_ENDIAN u_char rsv:2 ; u_char vj:1; u_char gre:1; u_char minim:1; u_char decap:1; u_char bcast:1; u_char simul:1; #else u_char simul:1; u_char bcast:1; u_char decap:1; u_char minim:1; u_char gre:1; u_char vj:1; u_char rsv:2 ; #endif u_short lifetime ; // 0xffff indicates infinity u_int home_addr; u_int home_agent; u_int cof_addr; // end-of-tunnel double id; /* extentions go here */ /* Mobile-Home authentication MUST be sent here */ } reg_req_t; typedef struct { u_char type ; u_char code ; u_short lifetime ; u_int home_addr; u_int home_agent; double id; /* extentions go here */ /* Mobile-Home authentication MUST be sent here */ /* Foreign-Home authentication MAY be present */ } reg_reply_t; typedef struct { IPAddress home_addr ; IPAddress cof_addr ; double id; u_short life ; } mh_entry_t ; typedef enum { IPIP_T, ETHER_T, IP_T, UDP_T, REG_T } packet_t; Vector <mh_entry_t *> _mh_list ; u_int *_reg_ips ; // list of IP addresses for which we accept // registration requests u_short _n_reg_ips ; // number of IPs in above list u_int *_our_ips ; // Our unicast IP addresses // _canonical_out MUST be one of these u_short _n_our_ips ; IPAddress _agent_ip ; // IP on our home agent interface IPAddress _canonical_out_ip ; // Used in ALL our outgoing relays // except replies to requests // which were addressed to our unicast // In the above case, we just copy // the address from the reply // // Packets addressed to this IP // (by the foreign agent) must be received // by us. EtherAddress _agent_ether ; // Ethernet address of interface where // we receive link layer // mobile (de)registration requests u_short _life_max ; // maximum lifetime of registration (in sec.) // defaults to 1800s - see RFC2002 static const int UDP_DST_PORT ; bool _check_reg_ips ; bool _verbose ; // cross-check against list ? IPAddressSet _reg_ipsset ; IPAddressSet _our_ipsset ; IPAddressSet _mobile_ipsset ; void handle_request(Packet *); // process registration request void mk_ipip(Packet *) const;// validate -> encapsulate -> forward // need to write these procedures. // The validate_* are a MUST. // auth_* are optional int validate_req(Packet *) const { return 0; } void auth_req(Packet *) const {} int validate_ipip(Packet *) const { return 0; } mh_entry_t *get_entry(IPAddress) const; int remove_entry(IPAddress); void print_config(void) const; void dump_packet(const Packet *, packet_t) const; public: HomeAgent(); ~HomeAgent(); const char *class_name() const const char *processing() const { return "HomeAgent"; } { return PUSH; } HomeAgent *clone() const; int configure(const Vector<String> &, ErrorHandler *); int initialize(ErrorHandler *); void push(int, Packet *); }; #endif ForeignAgent Element Code /* * foreignagent.cc * Implements a foreign agent. * * Copyright (c) 2000-2001 University of Utah * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * Further elaboration of this license, including a DISCLAIMER OF ANY * WARRANTY, EXPRESS OR IMPLIED, is provided in the LICENSE file, which is * also accessible at http://www.pdos.lcs.mit.edu/click/license.html */ #ifdef HAVE_CONFIG_H # include <config.h> #endif #include <click/config.h> #include <click/package.hh> #include <click/ipaddress.hh> #include <click/ipaddressset.hh> #include <click/etheraddress.hh> #include <click/click_ether.h> #include <click/click_ip.h> #include <click/click_udp.h> #include "foreignagent.hh" #include <click/confparse.hh> #include <click/error.hh> #include <click/glue.hh> #include "elements/standard/alignmentinfo.hh" #ifdef __KERNEL__ # include <net/checksum.h> #endif #include <iostream> // // // // // // // // // // input 0 - IP packets from the outside world input 1 - Ethernet packets from the interface, where we are a foreign agent output 0 - IP packets created by us, and ALSO those passed from above output 1 - Ethernet packets from foreign agent interface, which we don't process output 2 - Ethernet packets which we create to send to the mobile host This MUST be connected (via a queue or a similar element) to the interface on which we interact with the mobile host. And the mobile host MUST have a link-layer connection on this interface. // Limitations : // Only IP-in-IP encapsulation supported for the time being (Not a MUST) // // // // // // // // // // // // // // // // // // // // // // // // VJ header compression unsupported (not a MUST) Mobile-Home auth. not performed - keyed-MD5 (MUST) No response sent to mobile node for the foreign agent denying the registration request (for whatever ground) (MUST) No Mobile-Foreign auth. supported (not a MUST) No Home-Foreign auth. supported (not a MUST) Minimum registration delay, not enforced between successive registration requests. (not a MUST) Relayed registration have their source IP address set to an ougoing pre-configured IP address. Ideally this should have been the IP address of the interface on which this request is actually relayed. Problem is, we have no means of knowing it before-hand in such a case. Since the home agent will respond to this address, we MUST be able to receive packets sent to this address and MUST also recognize it to be one of our unicast IP addresses. Unreachable home agent not handled (SHOULD be handled) Misc. things TO DO (NOW) Set timer to expire bindings - both requested ones and ones which have been setup (MUST) Make it possible to specify the list of IPs which can register with us, using a mask. const int ForeignAgent::UDP_DST_PORT = 434 ; ForeignAgent::ForeignAgent() : Element(2, 3), _reg_ips(NULL), _n_reg_ips(0), _our_ips(NULL), _n_our_ips(0), _cof_ips(NULL), _n_cof_ips(0), _life_max(1800),_relay_udp(1024), _check_reg_ips(true), _verbose(false) { MOD_INC_USE_COUNT; } ForeignAgent::~ForeignAgent() { MOD_DEC_USE_COUNT; delete [] _reg_ips ; delete [] _our_ips ; delete [] _cof_ips ; for (int i=0; i<_visitor_list.size(); i++) delete [] _visitor_list[i]; } ForeignAgent * ForeignAgent::clone() const { return new ForeignAgent; } // RFC 2002 // Add arguments to filter out IP/masks and/or accept IP/masks // for registration requests int ForeignAgent::configure(const Vector<String> &conf, ErrorHandler *errh) { int before = errh->nerrors(); if (cp_va_parse (conf, this, errh, cpIPAddressSet, "Acceptable mobile IP addresses", &_reg_ipsset, cpIPAddress, "outgoing IP address (IP src field)", &_canonical_out_ip, cpIPAddress, "IP address for agent interface", &_agent_ip, cpEthernetAddress, "ethernet for agent interface", &_agent_ether, cpOptional, cpIPAddressSet, "Set of care-of-addresses", &_cof_ipsset, cpIPAddressSet, "Our unicast addresses", &_our_ipsset, cpKeywords, "LIFE", cpUnsigned, "max. registration lifetime(in seconds)",&_life_max, "RELAY_UDP_PORT", cpUnsigned, "request relayed from here", &_relay_udp, "VERBOSE", cpBool, "display what's going on ?", &_verbose, 0) < 0) return -1; _n_reg_ips = _reg_ipsset.size(); _reg_ips = _reg_ipsset.list_copy(); if (_n_reg_ips==1 && !_reg_ips[0]) _check_reg_ips = false ; if (!_canonical_out_ip) errh->error("Outgoing IP cannot be 0.0.0.0 !!!"); if (!_agent_ip) errh->error("Agent IP cannot be 0.0.0.0 !!!"); if (!_agent_ether) errh->error("Agent ethernet address cannot be 00:00:00:00:00:00 !!!"); // make sure our canonical outgoing IP is in this if (!_our_ipsset.contains(_canonical_out_ip)) _our_ipsset.insert(_canonical_out_ip); if (!_cof_ipsset.size()) _cof_ipsset.insert(_canonical_out_ip); _n_our_ips = _our_ipsset.size(); _our_ips = _our_ipsset.list_copy(); _n_cof_ips = _cof_ipsset.size(); _cof_ips = _cof_ipsset.list_copy(); return (errh->nerrors() == before ? 0 : -1); } int ForeignAgent::initialize(ErrorHandler *) { print_config(); return 0; } // Incoming encapuslated IP packtes and registration requests (UDP ) // arive on input port 0. Other IP packets also come from here // and those MUST be passed down on output port 0, ASAP. // // Registration (and possibly other) Ethernet packet arrive on input port 1 void ForeignAgent::push(int port, Packet *p) { bool for_us = false ; click_ip *ip ; click_ether *ether; // packet destination switch (port) { case 0 : ip = reinterpret_cast <click_ip *>(p->data()); // is the packet for us ? for_us = _our_ipsset.contains(IPAddress(ip->ip_dst)); // If it isn't for us, just forward it. if (!for_us) break ; if (ip->ip_p == IP_PROTO_IPIP) { ip_in_ip(p); return ; } if (ip->ip_p == IP_PROTO_UDP) { click_udp *udp = reinterpret_cast <click_udp *>(ip+1); if (udp->uh_dport == htons(_relay_udp)) { reg_reply(p); return ; } } break ; case 1 : ether = reinterpret_cast <click_ether *> (p->data()); // is it an IPv4 packet ? if (ether->ether_type != htons(ETHERTYPE_IP)) break ; ip = reinterpret_cast <click_ip *>(ether+1); // registration requests may be sent to the all agents' multicast // address = 224.0.0.11 if (_agent_ip == IPAddress(ip->ip_dst) || IPAddress(ip->ip_dst) == IPAddress("224.0.0.11")) { // for us ? if (ip->ip_p == IP_PROTO_UDP) { click_udp *udp = reinterpret_cast <click_udp *>(ip+1); if (udp->uh_dport == htons(UDP_DST_PORT)) { // validate and process request. // If the request is to be relayed, then relay and return reg_request(p); return ; } } } // If control reaches here, then it is an Ethernet packet, but // not for us cerr << "Ethernet packet, but not for us. Forwarding it" << endl; output(1).push(p); return ; } // switch // if the packet comes till here, means it isn't a packet for us cerr << "forwarding it" << endl ; output(0).push(p); return ; } /************************ Private Methods ***************************/ /********************************************************************/ // Make sure any existing entries for this mobile node are // not affected. The new entry should be made seperately // MUST Re-do the UDP checksum calculation since we modify the UDP // source port // Expects an Ethernet packet, which is IP/UDP port 434 void ForeignAgent::reg_request(Packet *p) { if (validate_req(p) <0) return ; // control reaches here only if this is a valid registration request if (_verbose) { click_chatter("(F) : Received the following REGISTRATION REQUEST"); dump_packet(p, ETHER_T); } // extract needed fields from the request click_ether *ether = reinterpret_cast <click_ether *> (p->data()); click_ip *ip = reinterpret_cast <click_ip *> (ether+1); click_udp *udp = reinterpret_cast <click_udp *> (ip+1); reg_req_t *req = reinterpret_cast <reg_req_t *> (udp+1); // populate our data structures, so we are prepared to // to receive the reply when and if, it comes. If it doesn't // we have to timeout and update our structures visitor_entry_t *v = new visitor_entry_t; memcpy(v->link_addr_mobile, ether->ether_shost, 6); v->home_addr = IPAddress(req->home_addr); v->dst_addr = IPAddress(ip->ip_src); v->udp_src_port = htons(udp->uh_sport); v->home_agent = IPAddress(req->home_agent); v->id = req->id ; // should be converted to host order v->req_life = htons(req->lifetime); v->rem_life = htons(req->lifetime); v->pending = true; _visitor_list.push_back(v); if (_verbose) { click_chatter("(F) : \nCreated registration request entry :\n" "home_addr=%s | home_agent=%s | link=%s | Pending ? %d", IPAddress(v->home_addr).s().mutable_c_str(), IPAddress(v->home_agent).s().mutable_c_str(), EtherAddress(v->link_addr_mobile).s().mutable_c_str(), v->pending); } // remove the ethernet header from p p->pull(14); p->clear_annotations(); p->set_dst_ip_anno(v->home_agent); ip = reinterpret_cast <click_ip *>(p->data()); ip->ip_src = _canonical_out_ip ; ip->ip_dst = v->home_agent ; ip->ip_ttl = 255; // ip->ip_len remains same ip->ip_tos = 0; ip->ip_off = 0; ip->ip_sum = 0 ; // for recomputing the checksum ip->ip_sum = in_cksum((u_char *) ip, sizeof(click_ip)); // right now, to avoid computing the UDP checksum, we // are avoiding changing the UDP source port to `_relay_udp' // Later, we'd like to use the _relay port, which is set // up during the element configuration udp= reinterpret_cast <click_udp *>(ip+1); udp->uh_sport = htons(_relay_udp); // do the UDP checksum computation // do any post-processing of the registration request // such as adding a Home-Foreign authentication appending // send the packet on its way if (_verbose) { click_chatter("(F) : RELAYING the following registration REQUEST ... "); dump_packet(p, IP_T); } output(0).push(p); return ; } // Expects an IP packet to our UDP port : _relay_udp // ip_len ? void ForeignAgent::reg_reply(Packet *p) { if (validate_reply(p) <0) return ; if (_verbose) { click_chatter("(F) : Received the following REGISTRATION REPLY"); dump_packet(p, IP_T); } // control reaches here only if this is a valid registration reply // extract needed fields from the request click_ip *ip = reinterpret_cast <click_ip *> (p->data()); click_udp *udp = reinterpret_cast <click_udp *> (ip+1); reg_reply_t *reply = reinterpret_cast <reg_reply_t *> (udp+1); // we MUST be having a mapping, if we have gotten so far visitor_entry_t *v = get_entry(IPAddress(reply->home_addr)); assert(v); if (_verbose) click_chatter("(F) : Found binding :\n" "home_addr=%s | home_agent=%s", IPAddress(v->home_addr).s().mutable_c_str(), IPAddress(v->home_agent).s().mutable_c_str()); // now check the reply code // we would want to move these magic numbers to a header switch (reply->code) { case 0 : case 1 : if (reply->lifetime == 0) { // remove the binding remove_entry(v->home_addr); if (_verbose) click_chatter("(F) : Reply Code=0/1, Lifetime=0 => removing entry :" "home_addr=%s | home_agent=%s\n", IPAddress(v->home_addr).s().mutable_c_str(), IPAddress(v->home_agent).s().mutable_c_str()); } else { // issue a warning if reply->lifetime > v->req_life v->rem_life = min(htons(reply->lifetime), v->req_life); v->pending = false; assert(v->rem_life); if (_verbose) click_chatter("(F) : Reply Code=0/1, reply says life remaining=%d\n" "Using life=%d, updating entry, setting" " pending status of entry to FALSE\n", htons(reply->lifetime), v->rem_life); } break ; // process the error codes - create the registration // entry, but NOT the visitor list entry // and return } // relay the correct and successful registration request // add the ethernet header to p WritablePacket *q = Packet::make(sizeof(click_ether)+p->length()); click_ether *ether = reinterpret_cast <click_ether *>(q->data()); ip = reinterpret_cast <click_ip *>(ether+1); memcpy(ip, p->data(), p->length()); q->set_dst_ip_anno(v->dst_addr); memcpy(ether->ether_dhost, (u_char *)v->link_addr_mobile, 6); memcpy(ether->ether_shost, _agent_ether.data(), 6); ether->ether_type = htons(ETHERTYPE_IP); // set the ip->ip_src ip->ip_dst ip->ip_ttl ip->ip_tos ip->ip_off ip->ip_sum ip->ip_sum IP fields to the right values = _agent_ip ; = v->dst_addr ; = 1 ; = 0; = 0; = 0 ; // for recomputing the checksum = in_cksum((u_char *) ip, sizeof(click_ip)); udp= reinterpret_cast <click_udp *>(ip+1); udp->uh_sport = htons(_relay_udp); udp->uh_dport = htons(v->udp_src_port) ; // MUST do the UDP checksum computation p->kill(); if (_verbose) { click_chatter("(F) : RELAYING the following registration REPLY ... "); dump_packet(q, ETHER_T); } // send the packet on our agent inteface output(2).push(q); return ; } // expects IP-in-IP encapsulated packets // ip_len void ForeignAgent::ip_in_ip(Packet *p) const { if (validate_ipip(p) <0) return ; if (_verbose) { click_chatter("(F) : Received the following IP-in-IP encapsulated packet"); dump_packet(p, IPIP_T); } // decapsulate packet // assuming only a 20 byte header. This should be generalized // using ip_header() click_ip *ip = reinterpret_cast <click_ip *> (p->data()+sizeof(click_ip)); visitor_entry_t *v = get_entry(IPAddress(ip->ip_dst)); if (_verbose) { click_chatter("(F) : Using existing binding :\n" " Sending to -> %s using the link address %s", IPAddress(v->home_addr).s().mutable_c_str(), EtherAddress(v->link_addr_mobile).s().mutable_c_str()); } assert(v); // wrap in an ethernet packet WritablePacket *q = Packet::make(sizeof(click_ether) + (static_cast<u_short>(p->length())) - sizeof(click_ip)); click_ether *ether = reinterpret_cast <click_ether *>(q->data()); memcpy(ether->ether_dhost, v->link_addr_mobile, 6); memcpy(ether->ether_shost, _agent_ether.data(), 6); ether->ether_type = htons(ETHERTYPE_IP); memcpy((q->data()+sizeof(click_ether)), (p->data()+sizeof(click_ip)), (p->length()-sizeof(click_ip))); q->set_dst_ip_anno(v->dst_addr); p->kill(); if (_verbose) { click_chatter("(F) : Sending the following packet AFTER decapsulation"); dump_packet(q, ETHER_T); } // send the packet on our agent inteface output(2).push(q); return ; } // // // // // // // // // // // // // // // // // // // // // // // // // Silently discard, if non-zero UDP checksum Is it within our ACCEPT set ? Destination UDP port == 434 ? Deny request if : i) Requested lifetime too long (denial contains the acceptable value, 69) ii) poorly formed request/reply iii) Requested encapsulation/compression unavailable iv) Home agent unreachable v) Non-zero reserved field (return status code 70) Denials SHOULD be sent to the agent interface. The registration fields from the original request must be copied. IP source address for denials, is copied from the request(destination) IP dest address for denials, is copied from the request(src) UDP source port for denials is 434 UDP dest port for denials, is the src UDP port of request For co-located COF, the IP source address MUST be the COF Otherwise the IP source address MUST be the home address The link layer destination address MUST be the agent's unicast address IP destination address is either agent's interface IP address/224.0.0.11 If IP destination address is 224.0.0.11 then TTL MUST be 1. `D' bit set iff a co-located COF address is used. Requested lifetime, MUST not exceed that, which was advertised by the agent Encapsulation MUST be supported. Type MUST be 1 int validate_req(Packet *p); // Silently discard if low-order 32-bits of `id' don't match // UDP destination, should match the outgoing port the request // was relayed from // reply->type == 3 (for Registration Reply) // Act upon the denial codes // Discard, if invalid non-zero UDP checksum // Replies to non-pending requests must be silently discarded //int validate_reply(Packet *p); // Used to retrieve the entry from the visitor list, whose // home address matches `ip' // This should be implemented using C++ hash_map container class //visitor_entry_t * ForeignAgent::visitor_entry_t * ForeignAgent::get_entry(IPAddress ip) const { for (int i = 0; i< _visitor_list.size() ; i++) if (_visitor_list[i]->home_addr == ip) return _visitor_list[i]; return NULL ; } int ForeignAgent::remove_entry(IPAddress ip) { for (int i = 0; i< _visitor_list.size() ; i++) if (_visitor_list[i]->home_addr == ip) { _visitor_list[i]=_visitor_list[_visitor_list.size()-1]; _visitor_list.pop_back(); return 0; } return -1 ; } void ForeignAgent::print_config(void) const { cerr << class_name() << endl << "-----------------" << endl ; for (int i=0 ; i<_n_reg_ips ; i++) cerr << "_reg_ips[" << i << "]=" << IPAddress(_reg_ips[i]).s().mutable_c_str()<< endl ; for (int i=0 ; i<_n_our_ips ; i++) cerr << "_our_ips[" << i << "]=" << IPAddress(_our_ips[i]).s().mutable_c_str()<< endl ; for (int i=0 ; i<_n_cof_ips ; i++) cerr << "_cof_ips[" << i << "]=" << IPAddress(_cof_ips[i]).s().mutable_c_str()<< endl ; cerr << "Agent IP : " << _agent_ip.s().mutable_c_str() << endl << "Agent Ethernet Address : " << _agent_ether.s().mutable_c_str() << endl << "Canonical outgoing IP : " << _canonical_out_ip.s().mutable_c_str() << endl << "Lifetime = " << _life_max << endl << "Relay UDP port = " << _relay_udp << endl << "Check mobile host IPs ? " << _check_reg_ips << endl << "Verbose = " << _verbose << endl << "-----------------" << endl ; return ; } void ForeignAgent::dump_packet(const Packet *p, packet_t type) const { click_ether *eth = NULL ; click_ip *ip = NULL ; click_ip *ipip = NULL ; click_udp *udp = NULL ; reg_req_t *reg = NULL ; click_chatter("(F) : ************* PACKET DUMP *****************"); switch(type) { case IPIP_T : ip = reinterpret_cast<click_ip *>(p->data()); assert(ip->ip_p == IP_PROTO_IPIP); click_chatter("(F) : IP-in-IP encapsulated packet\n" " ----------------------------"); click_chatter("(F) : Outer-IP\n" " --------"); click_chatter("(F) : Length=%d | id=%d | off=%d | ttl=%d | " "Proto=%d | Chksum=%d\n" " src=%s | dst=%s", htons(ip->ip_len), htons(ip->ip_id), htons(ip->ip_off), ip->ip_ttl, ip->ip_p, htons(ip->ip_sum), IPAddress(ip->ip_src).s().mutable_c_str(), IPAddress(ip->ip_dst).s().mutable_c_str()); ipip = reinterpret_cast <click_ip *>(ip+1); click_chatter("(F) : Inner-IP\n" " --------"); click_chatter("(F) : Length=%d | id=%d | off=%d | ttl=%d | " "Proto=%d | Chksum=%d\n" " src=%s | dst=%s", htons(ipip->ip_len), htons(ipip->ip_id), htons(ipip->ip_off), ipip->ip_ttl, ipip->ip_p, htons(ipip->ip_sum), IPAddress(ipip->ip_src).s().mutable_c_str(), IPAddress(ipip->ip_dst).s().mutable_c_str()); break ; case ETHER_T : eth = reinterpret_cast<click_ether *>(p->data()); click_chatter("(F) : Ethernet\n" " --------"); click_chatter("(F) : src=%s | dst=%s | type=%d", EtherAddress(eth->ether_shost).s().mutable_c_str(), EtherAddress(eth->ether_dhost).s().mutable_c_str(), ntohs(eth->ether_type)); if (ntohs(eth->ether_type)!= ETHERTYPE_IP) break; ip = reinterpret_cast <click_ip *> (eth+1); case IP_T : if (!ip) ip = reinterpret_cast <click_ip *>(p->data()); click_chatter("(F) : IP\n" " --"); click_chatter("(F) : Length=%d | id=%d | off=%d | ttl=%d | " "Proto=%d | Chksum=%d\n" " src=%s | dst=%s", htons(ip->ip_len), htons(ip->ip_id), htons(ip->ip_off), ip->ip_ttl, ip->ip_p, htons(ip->ip_sum), IPAddress(ip->ip_src).s().mutable_c_str(), IPAddress(ip->ip_dst).s().mutable_c_str()); if (ip->ip_p == IP_PROTO_UDP) udp = reinterpret_cast<click_udp*> (ip+1); case UDP_T : if (!udp) break ; click_chatter("(F) : UDP\n" " ---"); click_chatter("(F) : uh_sport=%d | uh_dport=%d | len=%d | chksum=%d", htons(udp->uh_sport), htons(udp->uh_dport), htons(udp->uh_ulen), htons(udp->uh_sum)); if (htons(udp->uh_ulen) > 20) reg = reinterpret_cast<reg_req_t *>(udp+1); case REG_T : if (!reg) break ; if (reg->type!=1 && reg->type!=3) break; click_chatter("(F) : Registration details\n" " --------------------"); click_chatter("(F) : type=%d | code=SKIPPED | life=%d | " "home_addr=%s | home_agent=%s", reg->type, htons(reg->lifetime), IPAddress(reg->home_addr).s().mutable_c_str(), IPAddress(reg->home_agent).s().mutable_c_str()); if (reg->type == 1) /* registration request */ click_chatter("(F) : cof=%s", IPAddress(reg->cof_addr).s().mutable_c_str()); } click_chatter("(F) : *******************************************"); return; } EXPORT_ELEMENT(ForeignAgent) /* foreignagent.hh */ #ifndef FOREIGNAGENT_HH #define FOREIGNAGENT_HH #include #include #include #include #include <click/element.hh> <click/ipaddress.hh> <click/ipaddressset.hh> <click/etheraddress.hh> <click/glue.hh> #ifndef min(a,b) #define min(a,b) (((a) > (b)) ? (b) : (a)) #endif class ForeignAgent : public Element { typedef struct { u_char type ; #if __BYTE_ORDER == __LITTLE_ENDIAN u_char rsv:2 ; u_char vj:1; u_char gre:1; u_char minim:1; u_char decap:1; u_char bcast:1; u_char simul:1; #else u_char simul:1; u_char bcast:1; u_char decap:1; u_char minim:1; u_char gre:1; u_char vj:1; u_char rsv:2 ; #endif u_short lifetime ; // 0xffff indicates infinity u_int home_addr; u_int home_agent; u_int cof_addr; // end-of-tunnel double id; /* extentions go here */ /* Mobile-Home authentication MUST be sent here */ } reg_req_t; typedef struct { u_char type ; u_char code ; u_short lifetime ; u_int home_addr; u_int home_agent; double id; /* extentions go here */ /* Mobile-Home authentication MUST be sent here */ /* Foreign-Home authentication MAY be present */ } reg_reply_t; typedef struct { u_char link_addr_mobile[6] ; IPAddress home_addr ; IPAddress dst_addr ; u_short udp_src_port ; IPAddress home_agent ; double id; u_short req_life ; u_short rem_life ; bool pending ; // true, if the registration hasn't been accepted yet } visitor_entry_t ; typedef enum { IPIP_T, ETHER_T, IP_T, UDP_T, REG_T } packet_t; Vector <visitor_entry_t *> _visitor_list ; u_int *_reg_ips ; // list of IP addresses for which we accept // registration requests u_short _n_reg_ips ; // number of IPs in above list u_int *_our_ips ; // Our unicast IP addresses // _canonical_out MUST be one of these u_short _n_our_ips ; u_int *_cof_ips ; u_short _n_cof_ips ; // MUST be a subset of _our_ips IPAddress _agent_ip ; // IP on our foreign agent interface IPAddress _canonical_out_ip ; // Used in ALL our outgoing relays // Packets addressed to this IP // (by the home agent) must be received // by us. Registration replies will // come to this address. // This need not be one of our COF addresses EtherAddress _agent_ether ; // Ethernet address of interface where // we receive mobile registration requests u_short _life_max ; // maximum lifetime of registration (in sec.) // defaults to 1800s - see RFC2002 ushort _relay_udp ; static const int UDP_DST_PORT ; bool _check_reg_ips ; bool _verbose ; // cross-check against list ? IPAddressSet _reg_ipsset ; IPAddressSet _our_ipsset ; IPAddressSet _cof_ipsset ; void reg_request(Packet *); void reg_reply(Packet *); // process registration request // process registration reply void ip_in_ip(Packet *) const;// validate and decapsulate/forward // need to write these procedures. // The validate_* are a MUST. // auth_* are optional int validate_req(Packet *) const { return 0; } void auth_req(Packet *) const {} int validate_reply(Packet *) const { return 0; } void auth_reply(Packet *) const {} int validate_ipip(Packet *) const { return 0; } visitor_entry_t *get_entry(IPAddress) const; int remove_entry(IPAddress); void print_config(void) const; void dump_packet(const Packet *, packet_t) const; public: ForeignAgent(); ~ForeignAgent(); const char *class_name() const const char *processing() const { return "ForeignAgent"; } { return PUSH; } ForeignAgent *clone() const; int configure(const Vector<String> &, ErrorHandler *); int initialize(ErrorHandler *); void push(int, Packet *); }; #endif Appendix C Sample Agent Test Configuration File f::ForeignAgent(0.0.0.0, // accept all registration requests 192.168.0.1, // outgoing IP 192.168.15.1, // agent interface address 45:56:a4:d0:ef:3f, // agent interface 192.168.0.1, // cof address 192.168.0.1 192.168.15.1, // our unicast addresses LIFE 900, RELAY_UDP_PORT 1024, VERBOSE true); h:: HomeAgent(0.0.0.0, // accept all registration requests 202.54.1.1, // outgoing IP 202.54.15.10, // agent interface address de:34:09:ab:65:70, // agent interface 202.54.1.1 202.54.15.10, // our unicast addresses LIFE 900, VERBOSE true); // Ethernet packet containing a valid registration request request::InfiniteSource(\< // ethernet header 45 56 a4 d0 ef 3f // agent ethernet 01 ef 23 cd 45 ab // our ethernet 08 00 // ETHERTYPE_IP // IP header 45 00 00 34 // v4/header_len/tos/ip_len=52 00 01 00 00 // id/offset 01 11 00 00 // ttl/prot_udp/checksum ca 36 0f 01 // home address - 202.54.15.1 c0 a8 0f 01 // agent address- 192.168.15.1 // udp 00 ff 01 b2 // src_port=255, dest_port=434 00 20 00 00 // head_len/checksum // reg 01 00 01 00 // type/flags/256s lifetime ca 36 0f 01 // home address ca 36 01 01 // home agent - 202.54.1.1 c0 a8 00 01 // cof address- 192.168.0.1 01 12 23 34 // 8-byte id 45 56 67 78 >, 1, 1); // send it once // IP packet containing registration reply reply::InfiniteSource(\< // IP header 45 00 00 30 // v4/header_len/tos/ip_len=52 00 05 00 00 // id/offset 01 11 2d d8 // ttl/prot_udp/checksum ca 36 01 01 // src IP = home agent=202.54.15.1 c0 a8 00 01 // dst IP // = canonical out IP=192.168.0.1 // udp 00 fa 04 00 // src_port=250, dest_port=434 00 20 00 00 // head_len/checksum // reg reply 03 00 00 80 ca 36 0f 01 ca 36 01 01 01 12 23 34 45 56 67 78 >, 1, 1); // // // // // IP-in-IP encapsulated packet ipip::InfiniteSource(\< // IP header 45 00 00 38 // 00 07 00 00 // 06 04 28 db // ca 36 01 01 c0 a8 00 01 // // inner-ip 45 00 00 24 // 00 33 00 00 // 45 11 3d f4 // 9b 63 c3 01 ca 36 0f 01 // type/flags/128s lifetime home address home agent - 202.54.1.1 8-byte id // send it once v4/header_len/tos/ip_len=56 id/offset ttl/prot_ipip/checksum // src IP = home agent=202.54.15.1 dst IP = cof address=192.168.0.1 v4/header_len/tos/ip_len=36 id/offset ttl/prot_udp/checksum // src IP = 155.99.195.1 dst IP = home address = 202.54.15.1 // inner IP payload // udp headers 00 fa 00 c0 // src_port=250, dest_port=192 00 10 00 00 // head_len/checksum // UDP payload 01 02 03 04 04 05 06 07 >, 1, 1); // Random IP packet sent to ToMH::InfiniteSource(\< // 45 00 00 00 07 00 06 06 c9 0c ca 36 0f // 34 10 09 >, 1, // send it once the home address of the mobile host IP header 20 // 00 // 21 // 23 06 56 01 // v4/header_len/tos/ip_len=32 id/offset ttl/prot_tcp/checksum // src IP = 12.35.6.86 dst IP = home address = 202.54.15.1 random IP payload 54 98 f0 02 7a bc a9 b2 f3 1); //InfiniteSource(0,0,1,false) -> f ; //InfiniteSource(0,0,1,false) -> f ; request->[1]f[1]->Discard; f[2]->Print("Foreign-OUT(2)", 100)->Discard; InfiniteSource(0,0,1,false) -> [1]h[1] -> Print("Home-OUT(1)", 100) -> Discard ; h[2]->Print("Home-OUT(2)", 100) ->Discard; // n :: Null ; reply ->Discard ; ipip ->Discard ; f -> -> -> -> -> CheckIPHeader(,0,VERBOSE true) IPPrint("Foreign-OUT(0)/Home-IN(0) ", CONTENTS hex, ID true) h IPPrint("Home-OUT(0)/Foreign-IN(0) ", CONTENTS hex, ID true) f ; ToMH->h ; Run Output for Agent Test ForeignAgent ----------------_reg_ips[0]=0.0.0.0 _our_ips[0]=192.168.0.1 _our_ips[1]=192.168.15.1 _cof_ips[0]=192.168.0.1 Agent IP : 192.168.15.1 Agent Ethernet Address : 45:56:a4:d0:ef:3f Canonical outgoing IP : 192.168.0.1 Lifetime = 900 Relay UDP port = 1024 Check mobile host IPs ? 0 Verbose = 1 ----------------HomeAgent ----------------_reg_ips[0]=0.0.0.0 _our_ips[0]=202.54.1.1 _our_ips[1]=202.54.15.10 Agent IP : 202.54.15.10 Agent Ethernet Address : de:34:09:ab:65:70 Canonical outgoing IP : 202.54.1.1 Lifetime = 900 Check mobile host IPs ? 0 Verbose = 1 ----------------(F) : Received the following REGISTRATION REQUEST (F) : ************* PACKET DUMP ***************** (F) : Ethernet -------(F) : src=01:ef:23:cd:45:ab | dst=45:56:a4:d0:ef:3f | type=2048 (F) : IP -(F) : Length=52 | id=1 | off=0 | ttl=1 | Proto=17 | Chksum=0 src=202.54.15.1 | dst=192.168.15.1 (F) : UDP --(F) : uh_sport=255 | uh_dport=434 | len=32 | chksum=0 (F) : Registration details -------------------(F) : type=1 | code=SKIPPED | life=256 | home_addr=202.54.15.1 | home_agent=202.54.1.1 (F) : cof=192.168.0.1 (F) : ******************************************* (F) : Created registration request entry : home_addr=202.54.15.1 | home_agent=202.54.1.1 | link=01:ef:23:cd:45:ab | Pending ? 1 (F) : RELAYING the following registration REQUEST ... (F) : ************* PACKET DUMP ***************** (F) : IP -(F) : Length=52 | id=1 | off=0 | ttl=255 | Proto=17 | Chksum=12247 src=192.168.0.1 | dst=202.54.1.1 (F) : UDP --(F) : uh_sport=1024 | uh_dport=434 | len=32 | chksum=0 (F) : Registration details -------------------(F) : type=1 | code=SKIPPED | life=256 | home_addr=202.54.15.1 | home_agent=202.54.1.1 (F) : cof=192.168.0.1 (F) : ******************************************* Foreign-OUT(0)/Home-IN(0) : id 1: 192.168.0.1.1024 > 202.54.1.1.434: udp 32 45000034 00010000 ff112fd7 c0a80001 ca360101 040001b2 00200000 01000100 ca360f01 ca360101 c0a80001 01122334 45566778 Received the following REGISTRATION REQUEST (H) : ************* PACKET DUMP ***************** (H) : IP -(H) : Length=52 | id=1 | off=0 | ttl=255 | Proto=17 | Chksum=12247 src=192.168.0.1 | dst=202.54.1.1 (H) : UDP --(H) : uh_sport=1024 | uh_dport=434 | len=32 | chksum=0 (H) : Registration details -------------------(H) : type=1 | code=SKIPPED | life=256 | home_addr=202.54.15.1 | home_agent=202.54.1.1 (H) : cof=192.168.0.1 (H) : ******************************************* (H) : Created NEW binding : 202.54.15.1->192.168.0.1 | Life = 256 (H) : Sending the following registration REPLY (H) : ************* PACKET DUMP ***************** (H) : IP -(H) : Length=52 | id=1 | off=0 | ttl=254 | Proto=17 | Chksum=12503 src=202.54.1.1 | dst=192.168.0.1 (H) : UDP --(H) : uh_sport=434 | uh_dport=1024 | len=32 | chksum=0 (H) : Registration details -------------------(H) : type=1 | code=SKIPPED | life=256 | home_addr=202.54.15.1 | home_agent=202.54.1.1 (H) : cof=192.168.0.1 (H) : ******************************************* (F) : Received the following REGISTRATION REPLY (F) : ************* PACKET DUMP ***************** (F) : IP -(F) : Length=52 | id=1 | off=0 | ttl=254 | Proto=17 | Chksum=12503 src=202.54.1.1 | dst=192.168.0.1 (F) : UDP --(F) : uh_sport=434 | uh_dport=1024 | len=32 | chksum=0 (F) : Registration details -------------------(F) : type=1 | code=SKIPPED | life=256 | home_addr=202.54.15.1 | home_agent=202.54.1.1 (F) : cof=192.168.0.1 (F) : ******************************************* (F) : Found binding : home_addr=202.54.15.1 | home_agent=202.54.1.1 (F) : Reply Code=0/1, reply says life remaining=256 Using life=256, updating entry, setting pending status of entry to FALSE (F) : RELAYING the following registration REPLY ... (F) : ************* PACKET DUMP ***************** (F) : Ethernet -------(F) : src=45:56:a4:d0:ef:3f | dst=01:ef:23:cd:45:ab | type=2048 (F) : IP -(F) : Length=52 | id=1 | off=0 | ttl=1 | Proto=17 | Chksum=4312 src=192.168.15.1 | dst=202.54.15.1 (F) : UDP --(F) : uh_sport=1024 | uh_dport=255 | len=32 | chksum=0 (F) : Registration details -------------------(F) : type=1 | code=SKIPPED | life=256 | home_addr=202.54.15.1 | home_agent=202.54.1.1 (F) : cof=192.168.0.1 (F) : ******************************************* Print Foreign-OUT(2) | 66 : 01ef23cd 45ab4556 a4d0ef3f 08004500 00340001 00000111 10d8c0a8 0f01ca36 0f010400 00ff0020 00000100 0100ca36 0f01ca36 0101c0a8 00010112 23344556 6778 (H) : Received the following packet to be forwarded to MH (H) : ************* PACKET DUMP ***************** (H) : IP -(H) : Length=32 | id=7 | off=0 | ttl=6 | Proto=6 | Chksum=51489 src=12.35.6.86 | dst=202.54.15.1 (H) : ******************************************* (H) : Using binding to encapsulate: 202.54.15.1 -> 192.168.0.1 (H) : Sending the following packet AFTER encapsulation (H) : ************* PACKET DUMP ***************** (H) : IP-in-IP encapsulated packet ---------------------------(H) : Outer-IP -------(H) : Length=52 | id=1024 | off=0 | ttl=255 | Proto=4 | Chksum=28133 src=202.54.1.1 | dst=192.168.0.1 (H) : Inner-IP -------(H) : Length=32 | id=7 | off=0 | ttl=6 | Proto=6 | Chksum=51489 src=12.35.6.86 | dst=202.54.15.1 (H) : ******************************************* (F) : Received the following IP-in-IP encapsulated packet (F) : ************* PACKET DUMP ***************** (F) : IP-in-IP encapsulated packet ---------------------------(F) : Outer-IP -------(F) : Length=52 | id=1024 | off=0 | ttl=255 | Proto=4 | Chksum=28133 src=202.54.1.1 | dst=192.168.0.1 (F) : Inner-IP -------(F) : Length=32 | id=7 | off=0 | ttl=6 | Proto=6 | Chksum=51489 src=12.35.6.86 | dst=202.54.15.1 (F) : ******************************************* (F) : Using existing binding : Sending to -> 202.54.15.1 using the link address 01:ef:23:cd:45:ab (F) : Sending the following packet AFTER decapsulation (F) : ************* PACKET DUMP ***************** (F) : Ethernet -------(F) : src=45:56:a4:d0:ef:3f | dst=01:ef:23:cd:45:ab | type=2048 (F) : IP -(F) : Length=32 | id=7 | off=0 | ttl=6 | Proto=6 | Chksum=51489 src=12.35.6.86 | dst=202.54.15.1 (F) : ******************************************* Print Foreign-OUT(2) | 46 : 01ef23cd 45ab4556 a4d0ef3f 08004500 00200007 00000606 c9210c23 0656ca36 0f013454 98f01002 7abc09a9 b2f3