From 4ca86ef88ef3baea324f8fcef1b4102deef06bcd Mon Sep 17 00:00:00 2001 From: John Sharratt Date: Tue, 4 Jul 2017 23:52:43 +0200 Subject: [PATCH] Added a fix to stop nasty NDP loops when being proactive on the NDP adverts --- src/address.cc | 13 ++++++++ src/address.h | 1 + src/iface.cc | 22 +++++++++--- src/proxy.cc | 90 +++++++++++++++++++------------------------------- src/proxy.h | 6 ++-- src/session.cc | 48 ++++++++++++++++----------- src/session.h | 9 +++-- 7 files changed, 103 insertions(+), 86 deletions(-) diff --git a/src/address.cc b/src/address.cc index f0298d7..993a237 100644 --- a/src/address.cc +++ b/src/address.cc @@ -57,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); diff --git a/src/address.h b/src/address.h index 90b1a03..f320d49 100644 --- a/src/address.h +++ b/src/address.h @@ -31,6 +31,7 @@ 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); diff --git a/src/iface.cc b/src/iface.cc index 6c09f60..56357e3 100644 --- a/src/iface.cc +++ b/src/iface.cc @@ -385,7 +385,7 @@ ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr) saddr = ip6h->ip6_src; 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; } @@ -589,10 +589,22 @@ int iface::poll_all() if (!saddr.is_unicast()/* || !daddr.is_multicast()*/) { continue; } + + if (ifa->_pr) + { + // Setup the reverse path + if (saddr.is_unicast()) { + ptr se = ifa->_pr->find_or_create_session(saddr); + if (se) { + se->add_iface(ifa); + se->handle_advert(ifa); + } + } - if (ifa->_pr) { - ifa->_pr->handle_solicit(saddr, daddr, taddr); + // Now process the solicit + ifa->_pr->handle_solicit(saddr, taddr); } + } else { if (ifa->read_advert(saddr, taddr) < 0) { logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str(); @@ -606,7 +618,7 @@ int iface::poll_all() const ptr sess = *s_it; - if ((sess->taddr() == taddr) && (sess->status() == session::WAITING || sess->status() == session::RENEWING)) { + if ((sess->taddr() == taddr)) { sess->handle_advert(ifa); found = true; break; @@ -615,7 +627,7 @@ int iface::poll_all() if (found == false) { if (ifa->owner()) { - ifa->owner()->handle_advert(saddr, taddr, ifa); + ifa->owner()->handle_advert(taddr, ifa); } else { logger::debug() << "iface::poll_all - ignoring advert on proxy iface=" << ifa->name(); } diff --git a/src/proxy.cc b/src/proxy.cc index 4197232..5cbc81f 100644 --- a/src/proxy.cc +++ b/src/proxy.cc @@ -63,7 +63,7 @@ ptr proxy::open(const std::string& ifname, bool promiscuous) return create(ifa, promiscuous); } -ptr proxy::find_or_create_session(const address& saddr, const address& daddr, const address& taddr, const ptr& receiver) +ptr proxy::find_or_create_session(const address& taddr) { // Let's check this proxy's list of sessions to see if we can // find one with the same target address. @@ -75,31 +75,8 @@ ptr proxy::find_or_create_session(const address& saddr, const address& return (*sit); } - // Check if there is an address for this specific interface it was received - // on (if so then immediately return a notice) - ptr se; - if (receiver) { - for (std::list >::iterator ad = address::addresses_begin(); - ad != address::addresses_end(); ad++) - { - if ((*ad)->addr() == taddr && (*ad)->ifname() == receiver->name()) - { - se = session::create(_ptr, saddr, daddr, taddr, _autowire, _keepalive, _retries); - if (se) { - se->add_iface(receiver); - _sessions.push_back(se); - - logger::debug() << "proxy::handle_advert() found local taddr=" << taddr; - se->handle_advert(receiver); - - return 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. @@ -109,16 +86,9 @@ ptr proxy::find_or_create_session(const address& saddr, const address& logger::debug() << "checking " << ru->addr() << " against " << taddr; - if (!daddr.is_multicast() && ru->addr() != daddr && daddr != taddr) { - continue; - } - - if (receiver && ru->ifa() && receiver->name() != ru->ifa()->name()) - continue; - if (ru->addr() == taddr) { if (!se) { - se = session::create(_ptr, saddr, daddr, taddr, _autowire, _keepalive, _retries); + se = session::create(_ptr, taddr, _autowire, _keepalive, _retries); } if (ru->is_auto()) { @@ -138,6 +108,7 @@ ptr proxy::find_or_create_session(const address& saddr, const address& // it "static" and immediately send the response. se->handle_advert(); return se; + } else { ptr ifa = (*it)->ifa(); @@ -150,18 +121,6 @@ ptr proxy::find_or_create_session(const address& saddr, const address& se->handle_advert(); } #endif - - // If a local address exists and it is for the requested interface - // then we already have a valid session - for (std::list >::iterator ad = address::addresses_begin(); - ad != address::addresses_end(); ad++) - { - if ((*ad)->addr() == taddr && (*ad)->ifname() == ifa->name()) { - logger::debug() << "proxy::handle_advert() found local taddr=" << taddr; - se->handle_advert(ifa); - break; - } - } } } } @@ -173,27 +132,46 @@ ptr proxy::find_or_create_session(const address& saddr, const address& return se; } -void proxy::handle_advert(const address& saddr, const address& taddr, const ptr& receiver) +void proxy::handle_advert(const address& taddr, const ptr& receiver) { logger::debug() - << "proxy::handle_advert() proxy=" << (ifa() ? ifa()->name() : "null") << ", receiver=" << (receiver ? receiver->name() : "null") - << ", saddr=" << saddr.to_string() - << ", taddr=" << taddr.to_string(); + << "proxy::handle_advert() proxy=" << (ifa() ? ifa()->name() : "null") << ", receiver=" << (receiver ? receiver->name() : "null"); - ptr se = find_or_create_session(all_nodes, all_nodes, taddr, receiver); + ptr se = find_or_create_session(taddr); if (!se) return; se->handle_advert(receiver); } -void proxy::handle_solicit(const address& saddr, const address& daddr, const address& taddr) +void proxy::handle_solicit(const address& saddr, const address& taddr) { logger::debug() - << "proxy::handle_solicit() ifa=" << ((_ifa) ? _ifa->name() : "null") - << ", saddr=" << saddr.to_string() - << ", taddr=" << taddr.to_string(); + << "proxy::handle_solicit() ifa=" << ((_ifa) ? _ifa->name() : "null"); - ptr se = find_or_create_session(saddr, daddr, taddr, ptr()); + // 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) + { + for (std::list >::iterator it = _rules.begin(); it != _rules.end(); it++) { + ptr ru = *it; + + if (ru->ifa() && ru->ifa()->name() == (*ad)->ifname()) + { + logger::debug() << "proxy::handle_solicit() found local taddr=" << taddr; + + if (_ifa) + _ifa->write_advert(saddr, taddr, router()); + } + } + + return; + } + } + + // Otherwise find or create a session to scan for this address + ptr se = find_or_create_session(taddr); if (!se) return; se->touch(); @@ -201,11 +179,11 @@ void proxy::handle_solicit(const address& saddr, const address& daddr, const add switch (se->status()) { case session::WAITING: case session::INVALID: - return; + se->add_pending(saddr); case session::VALID: case session::RENEWING: - se->send_advert(); + se->send_advert(saddr); } } diff --git a/src/proxy.h b/src/proxy.h index e7faffc..d045e52 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -34,11 +34,11 @@ public: static ptr open(const std::string& ifn, bool promiscuous); - ptr find_or_create_session(const address& saddr, const address& daddr, const address& taddr, const ptr& receiver); + ptr find_or_create_session(const address& taddr); - void handle_advert(const address& saddr, const address& taddr, const ptr& receiver); + void handle_advert(const address& taddr, const ptr& receiver); - void handle_solicit(const address& saddr, const address& daddr, const address& taddr); + void handle_solicit(const address& saddr, const address& taddr); void remove_session(const ptr& se); diff --git a/src/session.cc b/src/session.cc index 317a31f..1159a60 100644 --- a/src/session.cc +++ b/src/session.cc @@ -114,16 +114,13 @@ session::~session() } } -ptr session::create(const ptr& pr, const address& saddr, - const address& daddr, const address& taddr, bool auto_wire, bool keepalive, int retries) +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->_autowire = auto_wire; se->_keepalive = keepalive; se->_retries = retries; @@ -134,8 +131,8 @@ ptr session::create(const ptr& pr, const address& saddr, _sessions.push_back(se); logger::debug() - << "session::create() pr=" << logger::format("%x", (proxy* )pr) << ", proxy=" << ((pr->ifa()) ? pr->ifa()->name() : "null") << ", 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; } @@ -149,6 +146,16 @@ void session::add_iface(const ptr& ifa) _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() << ")"; @@ -171,13 +178,16 @@ void session::touch() } } -void session::send_advert() +void session::send_advert(const address& daddr) { - _pr->ifa()->write_advert(_saddr, _taddr, _pr->router()); + _pr->ifa()->write_advert(daddr, _taddr, _pr->router()); } void session::handle_auto_wire(const ptr& ifa) { + if (_wired == true) + return; + logger::debug() << "session::handle_auto_wire() taddr=" << _taddr << ", ifa=" << ifa->name(); @@ -238,8 +248,18 @@ void session::handle_advert() _status = VALID; _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 @@ -247,16 +267,6 @@ const address& session::taddr() const return _taddr; } -const address& session::saddr() const -{ - return _saddr; -} - -const address& session::daddr() const -{ - return _daddr; -} - bool session::autowire() const { return _autowire; diff --git a/src/session.h b/src/session.h index fd2b82a..08eb73d 100644 --- a/src/session.h +++ b/src/session.h @@ -43,6 +43,8 @@ private: // 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. @@ -70,10 +72,11 @@ public: // Destructor. ~session(); - static ptr create(const ptr& pr, const address& saddr, - const address& daddr, const address& taddr, bool autowire, bool keepalive, int retries); + 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; @@ -107,7 +110,7 @@ public: void touch(); - void send_advert(); + void send_advert(const address& daddr); void send_solicit();