diff --git a/ChangeLog b/ChangeLog index ebcc24e..81017cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,44 @@ +2017-01-07 Johnathan Sharratt + + * Version 0.2.6 + + * Added a new configuration setting named "deadtime" which allows + sessions that never made it the VALID to have a different (i.e. + shorter) life before they are removed (and potentially retried) + (defauilt is the same value as usual TTL for backwards compatibility) + + * Added a new configuration setting named "autowire" in the proxy + section (default is off) + + * If the "autowire" setting is on, then upon receiving a NDP + Neighbor Advertisment from one of the rule interfaces, a route will + be automatically added into the linux IP routing tables thus allowing + for a full featured gateway when IPv6 forwarding is turned on. + Note: Be careful as "accept_ra" may need to be set to 2 on the + interface during testing for the routing tables to retain their + default route (unrelated to this patch but took me a while to + discover). + + * When a session ends then anything that was "autowired" will be + automatically removed thus ensuring the routing tables are in a + similar state to before the daemon (or session) made any changes + + * Added a feature where the session will attempt to renew itself + (with a new NDP Solicitation) before it self-terminates, this is + required otherwise packets could be lost when the session terminates + triggering the automatically removal of the route table entry. + + * Ensured that renew operations only take place if the session has + been recently touched by an external solicitation - this ensures + that sessions that become IDLE are cleaned up quickly + + * Moved the daemonizing step till after the system executed the + configure step so that the error exit codes are returned to the daemon + caller. + + * No longer continuing to load the daemon if any of the interfaces fail + to load which should give a more predictable behaviour and better user experience. + 2016-04-18 Daniel Adolfsson * Version 0.2.5 diff --git a/ndppd.conf-dist b/ndppd.conf-dist index 37656c7..35a5db4 100644 --- a/ndppd.conf-dist +++ b/ndppd.conf-dist @@ -4,6 +4,12 @@ route-ttl 30000 +# address-ttl (NEW) +# This tells 'ndppd' how often to reload the IP address file /proc/net/if_inet6 +# Default value is '30000' (30 seconds). + +address-ttl 30000 + # proxy # This sets up a listener, that will listen for any Neighbor Solicitation # messages, and respond to them according to a set of rules (see below). @@ -22,6 +28,36 @@ proxy eth0 { # invalidating the entry, in milliseconds. Default value is '500'. timeout 500 + + # autowire + # Controls whether ndppd will automatically create host entries + # in the routing tables when it receives Neighbor Advertisements on a + # listening interface. The the default value is no. + # Note: Autowire will ignore all rules with 'auto' or 'static' given it + # is expected that the routes are already defined for these paths + + autowire no + + # keepalive + # Controls whether ndppd will automatically attempt to keep routing + # sessions alive by actively sending out NDP Solicitations before the the + # session is expired. The the default value is yes. + + keepalive yes + + # retries + # Number of times a NDP Solicitation will be sent out before the daemon + # considers a route unreachable. The default value is 3 + + retries 3 + + # promiscuous + # Controls whether ndppd will put the proxy listening interface into promiscuous + # mode and hence will react to inbound and outbound NDP commands. This is + # required for machines behind the gateway to talk to each other in more + # complex topology scenarios. The the default value is no. + + promiscuous no # ttl # Controls how long a valid or invalid entry remains in the cache, in @@ -55,6 +91,13 @@ proxy eth0 { auto + # autovia + # Any addresses updated using NDP advertisments will use a gateway to + # route traffic on this particular interface (only works wiith the iface + # rule type). Default is no + + autovia no + # Note that before version 0.2.2 of 'ndppd', if you didn't choose a # method, it defaulted to 'static'. For compatibility reasons we choose # to keep this behavior - for now (it may be removed in a future version). diff --git a/ndppd.conf.5 b/ndppd.conf.5 index 785bce2..21dd024 100644 --- a/ndppd.conf.5 +++ b/ndppd.conf.5 @@ -48,6 +48,20 @@ Controls how long .B ndppd will cache an entry. This is in milliseconds, and the default value is 30000 (30 seconds). +.IP "autowire " +Controls whether +.B ndppd +will automatically create host entries in the routing tables when +.B ndppd receives Neighbor Advertisements on a listening interface. +The default value is no. +.IP "promiscuous " +Controls whether +.B ndppd +will put the proxy listening interface into promiscuous mode and +hence will react to inbound and outbound NDP commands. This is +required for machines behind the gateway to talk to each other in +more complex topology scenarios. +The the default value is no. .IP "timeout " Controls how long .B ndppd diff --git a/src/address.cc b/src/address.cc index 0b1357e..18be7ef 100644 --- a/src/address.cc +++ b/src/address.cc @@ -15,6 +15,8 @@ // along with this program. If not, see . #include #include +#include +#include #include #include @@ -27,9 +29,16 @@ #include "ndppd.h" #include "address.h" +#include "route.h" NDPPD_NS_BEGIN +std::list > address::_addresses; + +int address::_ttl; + +int address::_c_ttl; + address::address() { reset(); @@ -48,6 +57,19 @@ address::address(const address& addr) _mask.s6_addr32[3] = addr._mask.s6_addr32[3]; } +address::address(const ptr
& addr) +{ + _addr.s6_addr32[0] = addr->_addr.s6_addr32[0]; + _addr.s6_addr32[1] = addr->_addr.s6_addr32[1]; + _addr.s6_addr32[2] = addr->_addr.s6_addr32[2]; + _addr.s6_addr32[3] = addr->_addr.s6_addr32[3]; + + _mask.s6_addr32[0] = addr->_mask.s6_addr32[0]; + _mask.s6_addr32[1] = addr->_mask.s6_addr32[1]; + _mask.s6_addr32[2] = addr->_mask.s6_addr32[2]; + _mask.s6_addr32[3] = addr->_mask.s6_addr32[3]; +} + address::address(const std::string& str) { parse_string(str); @@ -110,6 +132,21 @@ bool address::operator!=(const address& addr) const ((_addr.s6_addr32[3] ^ addr._addr.s6_addr32[3]) & _mask.s6_addr32[3])); } +bool address::is_empty() const +{ + if (_addr.s6_addr32[0] == 0 && + _addr.s6_addr32[1] == 0 && + _addr.s6_addr32[2] == 0 && + _addr.s6_addr32[3] == 0 && + _mask.s6_addr32[0] == 0xffffffff && + _mask.s6_addr32[1] == 0xffffffff && + _mask.s6_addr32[2] == 0xffffffff && + _mask.s6_addr32[3] == 0xffffffff) + return true; + + return false; +} + void address::reset() { _addr.s6_addr32[0] = 0; @@ -299,7 +336,93 @@ bool address::is_multicast() const bool address::is_unicast() const { + if (_addr.s6_addr32[2] == 0 && + _addr.s6_addr32[3] == 0) + return false; + return _addr.s6_addr[0] != 0xff; } +void address::add(const address& addr, const std::string& ifname) +{ + ptr rt(new route(addr, ifname)); + // logger::debug() << "address::create() addr=" << addr << ", ifname=" << ifname; + _addresses.push_back(rt); +} + +std::list >::iterator address::addresses_begin() +{ + return _addresses.begin(); +} + +std::list >::iterator address::addresses_end() +{ + return _addresses.end(); +} + +void address::load(const std::string& path) +{ + // Hack to make sure the addresses are not freed prematurely. + std::list > tmp_addresses(_addresses); + _addresses.clear(); + + logger::debug() << "reading IP addresses"; + + try { + std::ifstream ifs; + ifs.exceptions(std::ifstream::badbit | std::ifstream::failbit); + ifs.open(path.c_str(), std::ios::in); + ifs.exceptions(std::ifstream::badbit); + + while (!ifs.eof()) { + char buf[1024]; + ifs.getline(buf, sizeof(buf)); + + if (ifs.gcount() < 53) { + if (ifs.gcount() > 0) + logger::debug() << "skipping entry (size=" << ifs.gcount() << ")"; + continue; + } + + address addr; + + if (route::hexdec(buf, (unsigned char* )&addr.addr(), 16) != 16) { + logger::warning() << "failed to load address (" << buf << ")"; + continue; + } + + addr.prefix(128); + + std::string iface = route::token(buf + 45); + + address::add(addr, iface); + + logger::debug() << "found local addr=" << addr << ", iface=" << iface; + } + } catch (std::ifstream::failure e) { + logger::warning() << "Failed to parse IPv6 address data from '" << path << "'"; + logger::error() << e.what(); + } + + logger::debug() << "completed IP addresses load"; +} + +void address::update(int elapsed_time) +{ + if ((_c_ttl -= elapsed_time) <= 0) { + load("/proc/net/if_inet6"); + _c_ttl = _ttl; + } +} + +int address::ttl() +{ + return _ttl; +} + +void address::ttl(int ttl) +{ + _ttl = ttl; +} + NDPPD_NS_END diff --git a/src/address.h b/src/address.h index dc2ad18..c9b1a30 100644 --- a/src/address.h +++ b/src/address.h @@ -16,6 +16,7 @@ #pragma once #include +#include #include #include "ndppd.h" @@ -24,15 +25,24 @@ NDPPD_NS_BEGIN class iface; +class route; + class address { public: address(); address(const address& addr); + address(const ptr
& addr); address(const std::string& str); address(const char* str); address(const in6_addr& addr); address(const in6_addr& addr, const in6_addr& mask); address(const in6_addr& addr, int prefix); + + static void update(int elapsed_time); + + static int ttl(); + + static void ttl(int ttl); struct in6_addr& addr(); @@ -46,6 +56,8 @@ public: bool operator!=(const address& addr) const; void reset(); + + bool is_empty() const; const std::string to_string() const; @@ -60,8 +72,22 @@ public: bool is_multicast() const; operator std::string() const; + + static void add(const address& addr, const std::string& ifname); + + static void load(const std::string& path); + + static std::list >::iterator addresses_begin(); + + static std::list >::iterator addresses_end(); private: + static int _ttl; + + static int _c_ttl; + + static std::list > _addresses; + struct in6_addr _addr, _mask; }; diff --git a/src/iface.cc b/src/iface.cc index 74d2fd6..f569d3c 100644 --- a/src/iface.cc +++ b/src/iface.cc @@ -40,6 +40,7 @@ #include #include "ndppd.h" +#include "route.h" NDPPD_NS_BEGIN @@ -65,13 +66,19 @@ iface::~iface() if (_prev_allmulti >= 0) { allmulti(_prev_allmulti); } + if (_prev_promiscuous >= 0) { + promiscuous(_prev_promiscuous); + } close(_pfd); } _map_dirty = true; + + _serves.clear(); + _parents.clear(); } -ptr iface::open_pfd(const std::string& name) +ptr iface::open_pfd(const std::string& name, bool promiscuous) { int fd = 0; @@ -169,6 +176,13 @@ ptr iface::open_pfd(const std::string& name) // Eh. Allmulti. ifa->_prev_allmulti = ifa->allmulti(1); + + // Eh. Promiscuous + if (promiscuous == true) { + ifa->_prev_promiscuous = ifa->promiscuous(1); + } else { + ifa->_prev_promiscuous = -1; + } _map_dirty = true; @@ -287,7 +301,7 @@ ptr iface::open_ifd(const std::string& name) return ifa; } -ssize_t iface::read(int fd, struct sockaddr* saddr, uint8_t* msg, size_t size) +ssize_t iface::read(int fd, struct sockaddr* saddr, ssize_t saddr_size, uint8_t* msg, size_t size) { struct msghdr mhdr; struct iovec iov; @@ -301,18 +315,21 @@ ssize_t iface::read(int fd, struct sockaddr* saddr, uint8_t* msg, size_t size) memset(&mhdr, 0, sizeof(mhdr)); mhdr.msg_name = (caddr_t)saddr; - mhdr.msg_namelen = sizeof(struct sockaddr); + mhdr.msg_namelen = saddr_size; mhdr.msg_iov =& iov; mhdr.msg_iovlen = 1; - + if ((len = recvmsg(fd,& mhdr, 0)) < 0) + { + logger::error() << "iface::read() failed! error=" << logger::err() << ", ifa=" << name(); return -1; + } + + logger::debug() << "iface::read() ifa=" << name() << ", len=" << len; if (len < sizeof(struct icmp6_hdr)) return -1; - logger::debug() << "iface::read() len=" << len; - return len; } @@ -336,15 +353,14 @@ ssize_t iface::write(int fd, const address& daddr, const uint8_t* msg, size_t si mhdr.msg_iov =& iov; mhdr.msg_iovlen = 1; - logger::debug() << "iface::write() daddr=" << daddr.to_string() << ", len=" + logger::debug() << "iface::write() ifa=" << name() << ", daddr=" << daddr.to_string() << ", len=" << size; int len; if ((len = sendmsg(fd,& mhdr, 0)) < 0) { - int e = errno; - logger::error() << "iface::write() failed! errno=" << e; + logger::error() << "iface::write() failed! error=" << logger::err() << ", ifa=" << name() << ", daddr=" << daddr.to_string(); return -1; } @@ -357,8 +373,10 @@ ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr) uint8_t msg[256]; ssize_t len; - if ((len = read(_pfd, (struct sockaddr*)&t_saddr, msg, sizeof(msg))) < 0) + if ((len = read(_pfd, (struct sockaddr*)&t_saddr, sizeof(struct sockaddr_ll), msg, sizeof(msg))) < 0) { + logger::warning() << "iface::read_solicit() failed: " << logger::err(); return -1; + } struct ip6_hdr* ip6h = (struct ip6_hdr* )(msg + ETH_HLEN); @@ -369,9 +387,14 @@ ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr) taddr = ns->nd_ns_target; daddr = ip6h->ip6_dst; saddr = ip6h->ip6_src; + + // Ignore packets sent from this machine + if (iface::is_local(saddr) == true) { + return 0; + } logger::debug() << "iface::read_solicit() saddr=" << saddr.to_string() - << ", daddr=" << daddr.to_string() << ", len=" << len; + << ", daddr=" << daddr.to_string() << ", taddr=" << taddr.to_string() << ", len=" << len; return len; } @@ -451,11 +474,22 @@ ssize_t iface::read_advert(address& saddr, address& taddr) struct sockaddr_in6 t_saddr; uint8_t msg[256]; ssize_t len; + + memset(&t_saddr, 0, sizeof(struct sockaddr_in6)); + t_saddr.sin6_family = AF_INET6; + t_saddr.sin6_port = htons(IPPROTO_ICMPV6); // Needed? - if ((len = read(_ifd, (struct sockaddr* )&t_saddr, msg, sizeof(msg))) < 0) + if ((len = read(_ifd, (struct sockaddr* )&t_saddr, sizeof(struct sockaddr_in6), msg, sizeof(msg))) < 0) { + logger::warning() << "iface::read_advert() failed: " << logger::err(); return -1; + } saddr = t_saddr.sin6_addr; + + // Ignore packets sent from this machine + if (iface::is_local(saddr) == true) { + return 0; + } if (((struct icmp6_hdr* )msg)->icmp6_type != ND_NEIGHBOR_ADVERT) return -1; @@ -467,6 +501,79 @@ ssize_t iface::read_advert(address& saddr, address& taddr) return len; } +bool iface::is_local(const address& addr) +{ + // Check if the address is for an interface we own that is attached to + // one of the slave interfaces + for (std::list >::iterator ad = address::addresses_begin(); ad != address::addresses_end(); ad++) + { + if ((*ad)->addr() == addr) + return true; + } + return false; +} + +bool iface::handle_local(const address& saddr, const address& taddr) +{ + // Check if the address is for an interface we own that is attached to + // one of the slave interfaces + for (std::list >::iterator ad = address::addresses_begin(); ad != address::addresses_end(); ad++) + { + if ((*ad)->addr() == taddr) + { + // Loop through all the serves that are using this iface to respond to NDP solicitation requests + for (std::list >::iterator pit = serves_begin(); pit != serves_end(); pit++) { + ptr pr = (*pit); + if (!pr) continue; + + for (std::list >::iterator it = pr->rules_begin(); it != pr->rules_end(); it++) { + ptr ru = *it; + + if (ru->daughter() && ru->daughter()->name() == (*ad)->ifname()) + { + logger::debug() << "proxy::handle_solicit() found local taddr=" << taddr; + write_advert(saddr, taddr, false); + return true; + } + } + } + } + } + + return false; +} + +void iface::handle_reverse_advert(const address& saddr, const std::string& ifname) +{ + if (!saddr.is_unicast()) + return; + + logger::debug() + << "proxy::handle_reverse_advert()"; + + // Loop through all the parents that forward new NDP soliciation requests to this interface + for (std::list >::iterator pit = parents_begin(); pit != parents_end(); pit++) { + ptr parent = (*pit); + if (!parent || !parent->ifa()) { + continue; + } + + // Setup the reverse path on any proxies that are dealing + // with the reverse direction (this helps improve connectivity and + // latency in a full duplex setup) + for (std::list >::iterator it = parent->rules_begin(); it != parent->rules_end(); it++) { + ptr ru = *it; + + if (ru->addr() == saddr && + ru->daughter()->name() == ifname) + { + logger::debug() << " - generating artifical advertisement: " << ifname; + parent->handle_stateless_advert(saddr, saddr, ifname, ru->autovia()); + } + } + } +} + void iface::fixup_pollfds() { _pollfds.resize(_map.size()* 2); @@ -489,22 +596,6 @@ void iface::fixup_pollfds() } } -void iface::remove_session(const ptr& se) -{ - for (std::list >::iterator it = _sessions.begin(); - it != _sessions.end(); it++) { - if (*it == se) { - _sessions.erase(it); - break; - } - } -} - -void iface::add_session(const ptr& se) -{ - _sessions.push_back(se); -} - void iface::cleanup() { for (std::map >::iterator it = _map.begin(); @@ -534,6 +625,7 @@ int iface::poll_all() int len; if ((len = ::poll(&_pollfds[0], _pollfds.size(), 50)) < 0) { + logger::error() << "Failed to poll interfaces: " << logger::err(); return -1; } @@ -562,36 +654,95 @@ int iface::poll_all() ptr ifa = i_it->second; address saddr, daddr, taddr; + ssize_t size; if (is_pfd) { - if (ifa->read_solicit(saddr, daddr, taddr) < 0) { + size = ifa->read_solicit(saddr, daddr, taddr); + if (size < 0) { logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str(); continue; - } - - if (!saddr.is_unicast()/* || !daddr.is_multicast()*/) { + } + if (size == 0) { + logger::debug() << "iface::read_solicit() loopback received and ignored"; continue; } - - if (ifa->_pr) { - ifa->_pr->handle_solicit(saddr, daddr, taddr); + + // Process any local addresses for interfaces that we are proxying + if (ifa->handle_local(saddr, taddr) == true) { + continue; } + + // We have to handle all the parents who may be interested in + // the reverse path towards the one who sent this solicit. + // In fact, the parent need to know the source address in order + // to respond to NDP Solicitations + ifa->handle_reverse_advert(saddr, ifa->name()); + + // Loop through all the proxies that are using this iface to respond to NDP solicitation requests + bool handled = false; + for (std::list >::iterator pit = ifa->serves_begin(); pit != ifa->serves_end(); pit++) { + ptr pr = (*pit); + if (!pr) continue; + + // Process the solicitation request by relating it to other + // interfaces or lookup up any statics routes we have configured + handled = true; + pr->handle_solicit(saddr, taddr, ifa->name()); + } + + // If it was not handled then write an error message + if (handled == false) { + logger::debug() << " - solicit was ignored"; + } + } else { - if (ifa->read_advert(saddr, taddr) < 0) { + size = ifa->read_advert(saddr, taddr); + if (size < 0) { logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str(); continue; } - - for (std::list >::iterator s_it = ifa->_sessions.begin(); - s_it != ifa->_sessions.end(); s_it++) { - assert(!s_it->is_null()); - - const ptr sess = *s_it; - - if ((sess->taddr() == taddr) && (sess->status() == session::WAITING)) { - sess->handle_advert(); - break; + if (size == 0) { + logger::debug() << "iface::read_advert() loopback received and ignored"; + continue; + } + + // Process the NDP advert + bool handled = false; + for (std::list >::iterator pit = ifa->parents_begin(); pit != ifa->parents_end(); pit++) { + ptr pr = (*pit); + if (!pr || !pr->ifa()) { + continue; } + + // The proxy must have a rule for this interface or it is not meant to receive + // any notifications and thus they must be ignored + bool autovia = false; + bool is_relevant = false; + for (std::list >::iterator it = pr->rules_begin(); it != pr->rules_end(); it++) { + ptr ru = *it; + + if (ru->addr() == taddr && + ru->daughter() && + ru->daughter()->name() == ifa->name()) + { + is_relevant = true; + autovia = ru->autovia(); + break; + } + } + if (is_relevant == false) { + logger::debug() << "iface::read_advert() advert is not for " << ifa->name() << "...skipping"; + continue; + } + + // Process the NDP advertisement + handled = true; + pr->handle_advert(saddr, taddr, ifa->name(), autovia); + } + + // If it was not handled then write an error message + if (handled == false) { + logger::debug() << " - advert was ignored"; } } } @@ -638,19 +789,78 @@ int iface::allmulti(int state) return old_state; } +int iface::promiscuous(int state) +{ + struct ifreq ifr; + + logger::debug() + << "iface::promiscuous() state=" + << state << ", _name=\"" << _name << "\""; + + state = !!state; + + memset(&ifr, 0, sizeof(ifr)); + + strncpy(ifr.ifr_name, _name.c_str(), IFNAMSIZ); + + if (ioctl(_pfd, SIOCGIFFLAGS, &ifr) < 0) { + logger::error() << "Failed to get promiscuous: " << logger::err(); + return -1; + } + + int old_state = !!(ifr.ifr_flags &IFF_PROMISC); + + if (state == old_state) { + return old_state; + } + + if (state) { + ifr.ifr_flags |= IFF_PROMISC; + } else { + ifr.ifr_flags &= ~IFF_PROMISC; + } + + if (ioctl(_pfd, SIOCSIFFLAGS, &ifr) < 0) { + logger::error() << "Failed to set promiscuous: " << logger::err(); + return -1; + } + + return old_state; +} + const std::string& iface::name() const { return _name; } -void iface::pr(const ptr& pr) +void iface::add_serves(const ptr& pr) { - _pr = pr; + _serves.push_back(pr); } -const ptr& iface::pr() const +std::list >::iterator iface::serves_begin() { - return _pr; + return _serves.begin(); +} + +std::list >::iterator iface::serves_end() +{ + return _serves.end(); +} + +void iface::add_parent(const ptr& pr) +{ + _parents.push_back(pr); +} + +std::list >::iterator iface::parents_begin() +{ + return _parents.begin(); +} + +std::list >::iterator iface::parents_end() +{ + return _parents.end(); } NDPPD_NS_END diff --git a/src/iface.h b/src/iface.h index efdd1ac..9db59ab 100644 --- a/src/iface.h +++ b/src/iface.h @@ -38,13 +38,13 @@ public: static ptr open_ifd(const std::string& name); - static ptr open_pfd(const std::string& name); + static ptr open_pfd(const std::string& name, bool promiscuous); static int poll_all(); - static ssize_t read(int fd, struct sockaddr* saddr, uint8_t* msg, size_t size); + ssize_t read(int fd, struct sockaddr* saddr, ssize_t saddr_size, uint8_t* msg, size_t size); - static ssize_t write(int fd, const address& daddr, const uint8_t* msg, size_t size); + ssize_t write(int fd, const address& daddr, const uint8_t* msg, size_t size); // Writes a NB_NEIGHBOR_SOLICIT message to the _ifd socket. ssize_t write_solicit(const address& taddr); @@ -57,21 +57,31 @@ public: // Reads a NB_NEIGHBOR_ADVERT message from the _ifd socket; ssize_t read_advert(address& saddr, address& taddr); + + bool handle_local(const address& saddr, const address& taddr); + + bool is_local(const address& addr); + + void handle_reverse_advert(const address& saddr, const std::string& ifname); // Returns the name of the interface. const std::string& name() const; - - // Adds a session to be monitored for ND_NEIGHBOR_ADVERT messages. - void add_session(const ptr& se); - - void remove_session(const ptr& se); - - void pr(const ptr& pr); - - const ptr& pr() const; + + std::list >::iterator serves_begin(); + + std::list >::iterator serves_end(); + + void add_serves(const ptr& proxy); + + std::list >::iterator parents_begin(); + + std::list >::iterator parents_end(); + + void add_parent(const ptr& parent); + + static std::map > _map; private: - static std::map > _map; static bool _map_dirty; @@ -96,15 +106,16 @@ private: // Previous state of ALLMULTI for the interface. int _prev_allmulti; + + // Previous state of PROMISC for the interface + int _prev_promiscuous; // Name of this interface. std::string _name; - - // An array of sessions that are monitoring this interface for - // ND_NEIGHBOR_ADVERT messages. - std::list > _sessions; - - weak_ptr _pr; + + std::list > _serves; + + std::list > _parents; // The link-layer address of this interface. struct ether_addr hwaddr; @@ -112,6 +123,10 @@ private: // Turns on/off ALLMULTI for this interface - returns the previous state // or -1 if there was an error. int allmulti(int state); + + // Turns on/off PROMISC for this interface - returns the previous state + // or -1 if there was an error + int promiscuous(int state); // Constructor. iface(); diff --git a/src/ndppd.cc b/src/ndppd.cc index e645421..d54718f 100644 --- a/src/ndppd.cc +++ b/src/ndppd.cc @@ -36,9 +36,10 @@ using namespace ndppd; static int daemonize() { pid_t pid = fork(); - - if (pid < 0) + if (pid < 0) { + logger::error() << "Failed to fork during daemonize: " << logger::err(); return -1; + } if (pid > 0) exit(0); @@ -46,12 +47,15 @@ static int daemonize() umask(0); pid_t sid = setsid(); - - if (sid < 0) + if (sid < 0) { + logger::error() << "Failed to setsid during daemonize: " << logger::err(); return -1; + } - if (chdir("/") < 0) + if (chdir("/") < 0) { + logger::error() << "Failed to change path during daemonize: " << logger::err(); return -1; + } close(STDIN_FILENO); close(STDOUT_FILENO); @@ -137,6 +141,13 @@ static bool configure(ptr& cf) route::ttl(30000); else route::ttl(*x_cf); + + if (!(x_cf = cf->find("address-ttl"))) + address::ttl(30000); + else + address::ttl(*x_cf); + + std::list > myrules; std::vector >::const_iterator p_it; @@ -148,22 +159,47 @@ static bool configure(ptr& cf) if (pr_cf->empty()) { return false; } + + bool promiscuous = false; + if (!(x_cf = pr_cf->find("promiscuous"))) + promiscuous = false; + else + promiscuous = *x_cf; - ptr pr = proxy::open(*pr_cf); - - if (!pr) { - return true; + ptr pr = proxy::open(*pr_cf, promiscuous); + if (!pr || pr.is_null() == true) { + return false; } if (!(x_cf = pr_cf->find("router"))) pr->router(true); else pr->router(*x_cf); + + if (!(x_cf = pr_cf->find("autowire"))) + pr->autowire(false); + else + pr->autowire(*x_cf); + + if (!(x_cf = pr_cf->find("keepalive"))) + pr->keepalive(true); + else + pr->keepalive(*x_cf); + + if (!(x_cf = pr_cf->find("retries"))) + pr->retries(3); + else + pr->retries(*x_cf); if (!(x_cf = pr_cf->find("ttl"))) pr->ttl(30000); else pr->ttl(*x_cf); + + if (!(x_cf = pr_cf->find("deadtime"))) + pr->deadtime(pr->ttl()); + else + pr->deadtime(*x_cf); if (!(x_cf = pr_cf->find("timeout"))) pr->timeout(500); @@ -178,17 +214,71 @@ static bool configure(ptr& cf) ptr ru_cf =* r_it; address addr(*ru_cf); + + bool autovia = false; + if (!(x_cf = ru_cf->find("autovia"))) + autovia = false; + else + autovia = *x_cf; - if (x_cf = ru_cf->find("iface")) { - pr->add_rule(addr, iface::open_ifd(*x_cf)); + if (x_cf = ru_cf->find("iface")) + { + ptr ifa = iface::open_ifd(*x_cf); + if (!ifa || ifa.is_null() == true) { + return false; + } + + ifa->add_parent(pr); + + myrules.push_back(pr->add_rule(addr, ifa, autovia)); } else if (ru_cf->find("auto")) { - pr->add_rule(addr, true); + myrules.push_back(pr->add_rule(addr, true)); } else { - pr->add_rule(addr, false); + myrules.push_back(pr->add_rule(addr, false)); } } } - + + // Print out all the topology + for (std::map >::iterator i_it = iface::_map.begin(); i_it != iface::_map.end(); i_it++) { + ptr ifa = i_it->second; + + logger::debug() << "iface " << ifa->name() << " {"; + + for (std::list >::iterator pit = ifa->serves_begin(); pit != ifa->serves_end(); pit++) { + ptr pr = (*pit); + if (!pr) continue; + + logger::debug() << " " << "proxy " << logger::format("%x", pr.get_pointer()) << " {"; + + for (std::list >::iterator rit = pr->rules_begin(); rit != pr->rules_end(); rit++) { + ptr ru = *rit; + + logger::debug() << " " << "rule " << logger::format("%x", ru.get_pointer()) << " {"; + logger::debug() << " " << "taddr " << ru->addr()<< ";"; + if (ru->is_auto()) + logger::debug() << " " << "auto;"; + else if (!ru->daughter()) + logger::debug() << " " << "static;"; + else + logger::debug() << " " << "iface " << ru->daughter()->name() << ";"; + logger::debug() << " }"; + } + + logger::debug() << " }"; + } + + logger::debug() << " " << "parents {"; + for (std::list >::iterator pit = ifa->parents_begin(); pit != ifa->parents_end(); pit++) { + ptr pr = (*pit); + + logger::debug() << " " << "parent " << logger::format("%x", pr.get_pointer()) << ";"; + } + logger::debug() << " }"; + + logger::debug() << "}"; + } + return true; } @@ -261,18 +351,16 @@ int main(int argc, char* argv[], char* env[]) if (cf.is_null()) return -1; + if (!configure(cf)) + return -1; + if (daemon) { logger::syslog(true); - if (daemonize() < 0) { - logger::error() << "Failed to daemonize process"; + if (daemonize() < 0) return 1; - } } - if (!configure(cf)) - return -1; - if (!pidfile.empty()) { std::ofstream pf; pf.open(pidfile.c_str(), std::ios::out | std::ios::trunc); @@ -310,6 +398,9 @@ int main(int argc, char* argv[], char* env[]) if (rule::any_auto()) route::update(elapsed_time); + + if (rule::any_iface()) + address::update(elapsed_time); session::update_all(elapsed_time); } diff --git a/src/proxy.cc b/src/proxy.cc index 2de5a3e..cfa4815 100644 --- a/src/proxy.cc +++ b/src/proxy.cc @@ -26,87 +26,99 @@ #include "session.h" NDPPD_NS_BEGIN - + +static address all_nodes = address("ff02::1"); + std::list > proxy::_list; proxy::proxy() : - _router(true), _ttl(30000), _timeout(500) + _router(true), _ttl(30000), _deadtime(3000), _timeout(500), _autowire(false), _keepalive(true), _promiscuous(false), _retries(3) { } -ptr proxy::create(const ptr& ifa) +ptr proxy::find_aunt(const std::string& ifname, const address& taddr) +{ + for (std::list >::iterator sit = _list.begin(); + sit != _list.end(); sit++) + { + ptr pr = (*sit); + + bool has_addr = false; + for (std::list >::iterator it = pr->_rules.begin(); it != pr->_rules.end(); it++) { + ptr ru = *it; + + if (ru->addr() == taddr) { + has_addr = true; + break; + } + } + + if (has_addr == false) { + continue; + } + + if (pr->ifa() && pr->ifa()->name() == ifname) + return pr; + } + + return ptr(); +} + +ptr proxy::create(const ptr& ifa, bool promiscuous) { ptr pr(new proxy()); pr->_ptr = pr; pr->_ifa = ifa; + pr->_promiscuous = promiscuous; _list.push_back(pr); - ifa->pr(pr); + ifa->add_serves(pr); logger::debug() << "proxy::create() if=" << ifa->name(); return pr; } -ptr proxy::open(const std::string& ifname) +ptr proxy::open(const std::string& ifname, bool promiscuous) { - ptr ifa = iface::open_pfd(ifname); + ptr ifa = iface::open_pfd(ifname, promiscuous); if (!ifa) { return ptr(); } - return create(ifa); + return create(ifa, promiscuous); } -void proxy::handle_solicit(const address& saddr, const address& daddr, - const address& taddr) +ptr proxy::find_or_create_session(const address& taddr) { - logger::debug() - << "proxy::handle_solicit() saddr=" << saddr.to_string() - << ", taddr=" << taddr.to_string(); - // Let's check this proxy's list of sessions to see if we can // find one with the same target address. for (std::list >::iterator sit = _sessions.begin(); sit != _sessions.end(); sit++) { - - if ((*sit)->taddr() == taddr) { - switch ((*sit)->status()) { - case session::WAITING: - case session::INVALID: - break; - - case session::VALID: - (*sit)->send_advert(); - } - - return; - } + + if ((*sit)->taddr() == taddr) + return (*sit); } - + + ptr se; + // Since we couldn't find a session that matched, we'll try to find // a matching rule instead, and then set up a new session. - - ptr se; - + for (std::list >::iterator it = _rules.begin(); it != _rules.end(); it++) { ptr ru = *it; logger::debug() << "checking " << ru->addr() << " against " << taddr; - if (!daddr.is_multicast() && ru->addr() != daddr) { - continue; - } - if (ru->addr() == taddr) { if (!se) { - se = session::create(_ptr, saddr, daddr, taddr); + se = session::create(_ptr, taddr, _autowire, _keepalive, _retries); } - + if (ru->is_auto()) { ptr rt = route::find(taddr); @@ -115,20 +127,24 @@ void proxy::handle_solicit(const address& saddr, const address& daddr, } else { ptr ifa = rt->ifa(); - if (ifa && (ifa != ru->ifa())) { + if (ifa && (ifa != ru->daughter())) { se->add_iface(ifa); } } - } else if (!ru->ifa()) { + } else if (!ru->daughter()) { // This rule doesn't have an interface, and thus we'll consider // it "static" and immediately send the response. se->handle_advert(); - return; + return se; + } else { - se->add_iface((*it)->ifa()); + + ptr ifa = ru->daughter(); + se->add_iface(ifa); + #ifdef WITH_ND_NETLINK - if (if_addr_find((*it)->ifa()->name(), &taddr.const_addr())) { - logger::debug() << "Sending NA out " << (*it)->ifa()->name(); + if (if_addr_find(ifa->name(), &taddr.const_addr())) { + logger::debug() << "Sending NA out " << ifa->name(); se->add_iface(_ifa); se->handle_advert(); } @@ -136,16 +152,75 @@ void proxy::handle_solicit(const address& saddr, const address& daddr, } } } - + if (se) { _sessions.push_back(se); - se->send_solicit(); + } + + return se; +} + +void proxy::handle_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via) +{ + // If a session exists then process the advert in the context of the session + for (std::list >::iterator s_it = _sessions.begin(); + s_it != _sessions.end(); s_it++) + { + const ptr sess = *s_it; + + if ((sess->taddr() == taddr)) { + sess->handle_advert(saddr, ifname, use_via); + } } } -ptr proxy::add_rule(const address& addr, const ptr& ifa) +void proxy::handle_stateless_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via) +{ + logger::debug() + << "proxy::handle_stateless_advert() proxy=" << (ifa() ? ifa()->name() : "null") << ", taddr=" << taddr.to_string() << ", ifname=" << ifname; + + ptr se = find_or_create_session(taddr); + if (!se) return; + + if (_autowire == true && se->status() == session::WAITING) { + se->handle_auto_wire(saddr, ifname, use_via); + } +} + +void proxy::handle_solicit(const address& saddr, const address& taddr, const std::string& ifname) +{ + logger::debug() + << "proxy::handle_solicit()"; + + // Otherwise find or create a session to scan for this address + ptr se = find_or_create_session(taddr); + if (!se) return; + + // Touching the session will cause an NDP advert to be transmitted to all + // the daughters + se->touch(); + + // If our session is confirmed then we can respoond with an advert otherwise + // subscribe so that if it does become active we can notify everyone + if (saddr != taddr) { + switch (se->status()) { + case session::WAITING: + case session::INVALID: + se->add_pending(saddr); + break; + + case session::VALID: + case session::RENEWING: + se->send_advert(saddr); + break; + } + } +} + +ptr proxy::add_rule(const address& addr, const ptr& ifa, bool autovia) { ptr ru(rule::create(_ptr, addr, ifa)); + ru->autovia(autovia); _rules.push_back(ru); return ru; } @@ -157,6 +232,16 @@ ptr proxy::add_rule(const address& addr, bool aut) return ru; } +std::list >::iterator proxy::rules_begin() +{ + return _rules.begin(); +} + +std::list >::iterator proxy::rules_end() +{ + return _rules.end(); +} + void proxy::remove_session(const ptr& se) { _sessions.remove(se); @@ -167,6 +252,11 @@ const ptr& proxy::ifa() const return _ifa; } +bool proxy::promiscuous() const +{ + return _promiscuous; +} + bool proxy::router() const { return _router; @@ -177,6 +267,36 @@ void proxy::router(bool val) _router = val; } +bool proxy::autowire() const +{ + return _autowire; +} + +void proxy::autowire(bool val) +{ + _autowire = val; +} + +int proxy::retries() const +{ + return _retries; +} + +void proxy::retries(int val) +{ + _retries = val; +} + +bool proxy::keepalive() const +{ + return _keepalive; +} + +void proxy::keepalive(bool val) +{ + _keepalive = val; +} + int proxy::ttl() const { return _ttl; @@ -187,6 +307,16 @@ void proxy::ttl(int val) _ttl = (val >= 0) ? val : 30000; } +int proxy::deadtime() const +{ + return _deadtime; +} + +void proxy::deadtime(int val) +{ + _deadtime = (val >= 0) ? val : 30000; +} + int proxy::timeout() const { return _timeout; diff --git a/src/proxy.h b/src/proxy.h index 0a77698..8141b2a 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -29,25 +29,50 @@ class iface; class rule; class proxy { -public: - static ptr create(const ptr& ifa); +public: + static ptr create(const ptr& ifa, bool promiscuous); + + static ptr find_aunt(const std::string& ifname, const address& taddr); - static ptr open(const std::string& ifn); - - void handle_solicit(const address& saddr, const address& daddr, - const address& taddr); + static ptr open(const std::string& ifn, bool promiscuous); + + ptr find_or_create_session(const address& taddr); + + void handle_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via); + + void handle_stateless_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via); + + void handle_solicit(const address& saddr, const address& taddr, const std::string& ifname); void remove_session(const ptr& se); - ptr add_rule(const address& addr, const ptr& ifa); + ptr add_rule(const address& addr, const ptr& ifa, bool autovia); ptr add_rule(const address& addr, bool aut = false); + + std::list >::iterator rules_begin(); + + std::list >::iterator rules_end(); const ptr& ifa() const; + + bool promiscuous() const; bool router() const; void router(bool val); + + bool autowire() const; + + void autowire(bool val); + + int retries() const; + + void retries(int val); + + bool keepalive() const; + + void keepalive(bool val); int timeout() const; @@ -56,6 +81,10 @@ public: int ttl() const; void ttl(int val); + + int deadtime() const; + + void deadtime(int val); private: static std::list > _list; @@ -67,10 +96,18 @@ private: std::list > _rules; std::list > _sessions; + + bool _promiscuous; bool _router; + + bool _autowire; + + int _retries; + + bool _keepalive; - int _ttl, _timeout; + int _ttl, _deadtime, _timeout; proxy(); }; diff --git a/src/route.cc b/src/route.cc index 32e475f..6b7343d 100644 --- a/src/route.cc +++ b/src/route.cc @@ -105,19 +105,19 @@ void route::load(const std::string& path) unsigned char pfx; - if (hexdec(buf, (unsigned char* )&addr.addr(), 16) != 16) { + if (route::hexdec(buf, (unsigned char* )&addr.addr(), 16) != 16) { // TODO: Warn here? continue; } - if (hexdec(buf + 33,& pfx, 1) != 1) { + if (route::hexdec(buf + 33,& pfx, 1) != 1) { // TODO: Warn here? continue; } addr.prefix((int)pfx); - route::create(addr, token(buf + 141)); + route::create(addr, route::token(buf + 141)); } } catch (std::ifstream::failure e) { logger::warning() << "Failed to parse IPv6 routing data from '" << path << "'"; diff --git a/src/route.h b/src/route.h index e4fbb5b..ffe4d71 100644 --- a/src/route.h +++ b/src/route.h @@ -44,6 +44,12 @@ public: const address& addr() const; ptr ifa(); + + route(const address& addr, const std::string& ifname); + + static size_t hexdec(const char* str, unsigned char* buf, size_t size); + + static std::string token(const char* str); private: static int _ttl; @@ -56,14 +62,8 @@ private: ptr _ifa; - static size_t hexdec(const char* str, unsigned char* buf, size_t size); - - static std::string token(const char* str); - static std::list > _routes; - route(const address& addr, const std::string& ifname); - }; NDPPD_NS_END diff --git a/src/rule.cc b/src/rule.cc index 3bd66ba..ad64710 100644 --- a/src/rule.cc +++ b/src/rule.cc @@ -29,6 +29,10 @@ std::vector interfaces; bool rule::_any_aut = false; +bool rule::_any_iface = false; + +bool rule::_any_static = false; + rule::rule() { } @@ -38,9 +42,10 @@ ptr rule::create(const ptr& pr, const address& addr, const ptr ru(new rule()); ru->_ptr = ru; ru->_pr = pr; - ru->_ifa = ifa; + ru->_daughter = ifa; ru->_addr = addr; ru->_aut = false; + _any_iface = true; unsigned int ifindex; ifindex = if_nametoindex(pr->ifa()->name().c_str()); @@ -52,7 +57,7 @@ ptr rule::create(const ptr& pr, const address& addr, const ptr_status) { + case session::WAITING: - logger::debug() << "session is now invalid"; - se->_status = session::INVALID; - se->_ttl = se->_pr->ttl(); + if (se->_fails < se->_retries) { + logger::debug() << "session will keep trying [taddr=" << se->_taddr << "]"; + + se->_ttl = se->_pr->timeout(); + se->_fails++; + + // Send another solicit + se->send_solicit(); + } else { + + logger::debug() << "session is now invalid [taddr=" << se->_taddr << "]"; + + se->_status = session::INVALID; + se->_ttl = se->_pr->deadtime(); + } + break; + + case session::RENEWING: + logger::debug() << "session is became invalid [taddr=" << se->_taddr << "]"; + + if (se->_fails < se->_retries) { + se->_ttl = se->_pr->timeout(); + se->_fails++; + + // Send another solicit + se->send_solicit(); + } else { + se->_pr->remove_session(se); + } + break; + + case session::VALID: + if (se->touched() == true || + se->keepalive() == true) + { + logger::debug() << "session is renewing [taddr=" << se->_taddr << "]"; + se->_status = session::RENEWING; + se->_ttl = se->_pr->timeout(); + se->_fails = 0; + se->_touched = false; + + // Send another solicit to make sure the route is still valid + se->send_solicit(); + } else { + se->_pr->remove_session(se); + } break; default: @@ -57,30 +102,34 @@ void session::update_all(int elapsed_time) session::~session() { logger::debug() << "session::~session() this=" << logger::format("%x", this); - - for (std::list >::iterator it = _ifaces.begin(); + + if (_wired == true) { + for (std::list >::iterator it = _ifaces.begin(); it != _ifaces.end(); it++) { - (*it)->remove_session(_ptr); + handle_auto_unwire((*it)->name()); + } } } -ptr session::create(const ptr& pr, const address& saddr, - const address& daddr, const address& taddr) +ptr session::create(const ptr& pr, const address& taddr, bool auto_wire, bool keepalive, int retries) { ptr se(new session()); - se->_ptr = se; - se->_pr = pr; - se->_saddr = address("::") == saddr ? all_nodes : saddr; - se->_taddr = taddr; - se->_daddr = daddr; - se->_ttl = pr->timeout(); + se->_ptr = se; + se->_pr = pr; + se->_taddr = taddr; + se->_autowire = auto_wire; + se->_keepalive = keepalive; + se->_retries = retries; + se->_wired = false; + se->_ttl = pr->ttl(); + se->_touched = false; _sessions.push_back(se); logger::debug() - << "session::create() pr=" << logger::format("%x", (proxy* )pr) << ", saddr=" << saddr - << ", daddr=" << daddr << ", taddr=" << taddr << " =" << logger::format("%x", (session* )se); + << "session::create() pr=" << logger::format("%x", (proxy* )pr) << ", proxy=" << ((pr->ifa()) ? pr->ifa()->name() : "null") + << ", taddr=" << taddr << " =" << logger::format("%x", (session* )se); return se; } @@ -90,10 +139,19 @@ void session::add_iface(const ptr& ifa) if (std::find(_ifaces.begin(), _ifaces.end(), ifa) != _ifaces.end()) return; - ifa->add_session(_ptr); _ifaces.push_back(ifa); } +void session::add_pending(const address& addr) +{ + for (std::list >::iterator ad = _pending.begin(); ad != _pending.end(); ad++) { + if (addr == (*ad)) + return; + } + + _pending.push_back(new address(addr)); +} + void session::send_solicit() { logger::debug() << "session::send_solicit() (_ifaces.size() = " << _ifaces.size() << ")"; @@ -105,17 +163,162 @@ void session::send_solicit() } } -void session::send_advert() +void session::touch() { - _pr->ifa()->write_advert(_saddr, _taddr, _pr->router()); + if (_touched == false) + { + _touched = true; + + if (status() == session::WAITING || status() == session::INVALID) { + _ttl = _pr->timeout(); + + logger::debug() << "session is now probing [taddr=" << _taddr << "]"; + + send_solicit(); + } + } } +void session::send_advert(const address& daddr) +{ + _pr->ifa()->write_advert(daddr, _taddr, _pr->router()); +} + +void session::handle_auto_wire(const address& saddr, const std::string& ifname, bool use_via) +{ + if (_wired == true && (_wired_via.is_empty() || _wired_via == saddr)) + return; + + logger::debug() + << "session::handle_auto_wire() taddr=" << _taddr << ", ifname=" << ifname; + + if (use_via == true && + _taddr != saddr && + saddr.is_unicast() == true && + saddr.is_multicast() == false) + { + std::stringstream route_cmd; + route_cmd << "ip"; + route_cmd << " " << "-6"; + route_cmd << " " << "route"; + route_cmd << " " << "replace"; + route_cmd << " " << std::string(saddr); + route_cmd << " " << "dev"; + route_cmd << " " << ifname; + + logger::debug() + << "session::system(" << route_cmd.str() << ")"; + + system(route_cmd.str().c_str()); + + _wired_via = saddr; + } + else + _wired_via.reset(); + + { + std::stringstream route_cmd; + route_cmd << "ip"; + route_cmd << " " << "-6"; + route_cmd << " " << "route"; + route_cmd << " " << "replace"; + route_cmd << " " << std::string(_taddr); + if (_wired_via.is_empty() == false) { + route_cmd << " " << "via"; + route_cmd << " " << std::string(_wired_via); + } + route_cmd << " " << "dev"; + route_cmd << " " << ifname; + + logger::debug() + << "session::system(" << route_cmd.str() << ")"; + + system(route_cmd.str().c_str()); + } + + _wired = true; +} + +void session::handle_auto_unwire(const std::string& ifname) +{ + logger::debug() + << "session::handle_auto_unwire() taddr=" << _taddr << ", ifname=" << ifname; + + { + std::stringstream route_cmd; + route_cmd << "ip"; + route_cmd << " " << "-6"; + route_cmd << " " << "route"; + route_cmd << " " << "flush"; + route_cmd << " " << std::string(_taddr); + if (_wired_via.is_empty() == false) { + route_cmd << " " << "via"; + route_cmd << " " << std::string(_wired_via); + } + route_cmd << " " << "dev"; + route_cmd << " " << ifname; + + logger::debug() + << "session::system(" << route_cmd.str() << ")"; + + system(route_cmd.str().c_str()); + } + + if (_wired_via.is_empty() == false) { + std::stringstream route_cmd; + route_cmd << "ip"; + route_cmd << " " << "-6"; + route_cmd << " " << "route"; + route_cmd << " " << "flush"; + route_cmd << " " << std::string(_wired_via); + route_cmd << " " << "dev"; + route_cmd << " " << ifname; + + logger::debug() + << "session::system(" << route_cmd.str() << ")"; + + system(route_cmd.str().c_str()); + } + + _wired = false; + _wired_via.reset(); +} + +void session::handle_advert(const address& saddr, const std::string& ifname, bool use_via) +{ + if (_autowire == true && _status == WAITING) { + handle_auto_wire(saddr, ifname, use_via); + } + + handle_advert(); +} + + void session::handle_advert() { - _status = VALID; + logger::debug() + << "session::handle_advert() taddr=" << _taddr << ", ttl=" << _pr->ttl(); + + if (_status != VALID) { + _status = VALID; + + logger::debug() << "session is active [taddr=" << _taddr << "]"; + } + _ttl = _pr->ttl(); + _fails = 0; + + if (!_pending.empty()) { + for (std::list >::iterator ad = _pending.begin(); + ad != _pending.end(); ad++) { + ptr
addr = (*ad); + logger::debug() << " - forward to " << addr; - send_advert(); + send_advert(addr); + } + + _pending.clear(); + } } const address& session::taddr() const @@ -123,14 +326,34 @@ const address& session::taddr() const return _taddr; } -const address& session::saddr() const +bool session::autowire() const { - return _saddr; + return _autowire; } -const address& session::daddr() const +bool session::keepalive() const { - return _daddr; + return _keepalive; +} + +int session::retries() const +{ + return _retries; +} + +int session::fails() const +{ + return _fails; +} + +bool session::wired() const +{ + return _wired; +} + +bool session::touched() const +{ + return _touched; } int session::status() const diff --git a/src/session.h b/src/session.h index d70b930..b4d9ac1 100644 --- a/src/session.h +++ b/src/session.h @@ -16,6 +16,7 @@ #pragma once #include +#include #include "ndppd.h" @@ -31,14 +32,30 @@ private: weak_ptr _pr; address _saddr, _daddr, _taddr; + + bool _autowire; + + bool _keepalive; + + bool _wired; + + address _wired_via; + + bool _touched; // An array of interfaces this session is monitoring for // ND_NEIGHBOR_ADVERT on. std::list > _ifaces; + + std::list > _pending; // The remaining time in miliseconds the object will stay in the // interface's session array or cache. int _ttl; + + int _fails; + + int _retries; int _status; @@ -47,9 +64,10 @@ private: public: enum { - WAITING, // Waiting for an advert response. - VALID, // Valid; - INVALID // Invalid; + WAITING, // Waiting for an advert response. + RENEWING, // Renewing; + VALID, // Valid; + INVALID // Invalid; }; static void update_all(int elapsed_time); @@ -57,24 +75,45 @@ public: // Destructor. ~session(); - static ptr create(const ptr& pr, const address& saddr, - const address& daddr, const address& taddr); + static ptr create(const ptr& pr, const address& taddr, bool autowire, bool keepalive, int retries); void add_iface(const ptr& ifa); + + void add_pending(const address& addr); const address& taddr() const; const address& daddr() const; const address& saddr() const; + + bool autowire() const; + + int retries() const; + + int fails() const; + + bool keepalive() const; + + bool wired() const; + + bool touched() const; int status() const; void status(int val); - + void handle_advert(); - void send_advert(); + void handle_advert(const address& saddr, const std::string& ifname, bool use_via); + + void handle_auto_wire(const address& saddr, const std::string& ifname, bool use_via); + + void handle_auto_unwire(const std::string& ifname); + + void touch(); + + void send_advert(const address& daddr); void send_solicit();