Merge pull request #30 from john-sharratt/master

Feature to automatically wire the routes for a gateway
This commit is contained in:
Daniel Adolfsson 2017-08-07 13:18:13 +02:00 committed by GitHub
commit 6ef7a57b2e
16 changed files with 1223 additions and 189 deletions

View File

@ -1,3 +1,44 @@
2017-01-07 Johnathan Sharratt <johnathan.sharratt@gmail.com>
* 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 <daniel@priv.nu> 2016-04-18 Daniel Adolfsson <daniel@priv.nu>
* Version 0.2.5 * Version 0.2.5

View File

@ -4,6 +4,12 @@
route-ttl 30000 route-ttl 30000
# address-ttl <integer> (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 <interface> # proxy <interface>
# This sets up a listener, that will listen for any Neighbor Solicitation # 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). # messages, and respond to them according to a set of rules (see below).
@ -23,6 +29,36 @@ proxy eth0 {
timeout 500 timeout 500
# autowire <yes|no|true|false>
# 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 <yes|no|true|false>
# 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 <integer>
# 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 <yes|no|true|false>
# 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 <integer> # ttl <integer>
# Controls how long a valid or invalid entry remains in the cache, in # Controls how long a valid or invalid entry remains in the cache, in
# milliseconds. Default value is '30000' (30 seconds). # milliseconds. Default value is '30000' (30 seconds).
@ -55,6 +91,13 @@ proxy eth0 {
auto auto
# autovia <yes|no|true|false>
# 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 # 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 # method, it defaulted to 'static'. For compatibility reasons we choose
# to keep this behavior - for now (it may be removed in a future version). # to keep this behavior - for now (it may be removed in a future version).

View File

@ -48,6 +48,20 @@ Controls how long
.B ndppd .B ndppd
will cache an entry. This is in milliseconds, and the default value will cache an entry. This is in milliseconds, and the default value
is 30000 (30 seconds). is 30000 (30 seconds).
.IP "autowire <yes|no>"
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 <yes|no>"
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 <value>" .IP "timeout <value>"
Controls how long Controls how long
.B ndppd .B ndppd

View File

@ -15,6 +15,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <string> #include <string>
#include <vector> #include <vector>
#include <fstream>
#include <list>
#include <map> #include <map>
#include <cstring> #include <cstring>
@ -27,9 +29,16 @@
#include "ndppd.h" #include "ndppd.h"
#include "address.h" #include "address.h"
#include "route.h"
NDPPD_NS_BEGIN NDPPD_NS_BEGIN
std::list<ptr<route> > address::_addresses;
int address::_ttl;
int address::_c_ttl;
address::address() address::address()
{ {
reset(); reset();
@ -48,6 +57,19 @@ address::address(const address& addr)
_mask.s6_addr32[3] = addr._mask.s6_addr32[3]; _mask.s6_addr32[3] = addr._mask.s6_addr32[3];
} }
address::address(const ptr<address>& 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) address::address(const std::string& str)
{ {
parse_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])); ((_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() void address::reset()
{ {
_addr.s6_addr32[0] = 0; _addr.s6_addr32[0] = 0;
@ -299,7 +336,93 @@ bool address::is_multicast() const
bool address::is_unicast() 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; return _addr.s6_addr[0] != 0xff;
} }
void address::add(const address& addr, const std::string& ifname)
{
ptr<route> rt(new route(addr, ifname));
// logger::debug() << "address::create() addr=" << addr << ", ifname=" << ifname;
_addresses.push_back(rt);
}
std::list<ptr<route> >::iterator address::addresses_begin()
{
return _addresses.begin();
}
std::list<ptr<route> >::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<ptr<route> > 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 NDPPD_NS_END

View File

@ -16,6 +16,7 @@
#pragma once #pragma once
#include <string> #include <string>
#include <list>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include "ndppd.h" #include "ndppd.h"
@ -24,16 +25,25 @@ NDPPD_NS_BEGIN
class iface; class iface;
class route;
class address { class address {
public: public:
address(); address();
address(const address& addr); address(const address& addr);
address(const ptr<address>& addr);
address(const std::string& str); address(const std::string& str);
address(const char* str); address(const char* str);
address(const in6_addr& addr); address(const in6_addr& addr);
address(const in6_addr& addr, const in6_addr& mask); address(const in6_addr& addr, const in6_addr& mask);
address(const in6_addr& addr, int prefix); 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(); struct in6_addr& addr();
const struct in6_addr& const_addr() const; const struct in6_addr& const_addr() const;
@ -47,6 +57,8 @@ public:
void reset(); void reset();
bool is_empty() const;
const std::string to_string() const; const std::string to_string() const;
bool parse_string(const std::string& str); bool parse_string(const std::string& str);
@ -61,7 +73,21 @@ public:
operator std::string() 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<ptr<route> >::iterator addresses_begin();
static std::list<ptr<route> >::iterator addresses_end();
private: private:
static int _ttl;
static int _c_ttl;
static std::list<ptr<route> > _addresses;
struct in6_addr _addr, _mask; struct in6_addr _addr, _mask;
}; };

View File

@ -40,6 +40,7 @@
#include <map> #include <map>
#include "ndppd.h" #include "ndppd.h"
#include "route.h"
NDPPD_NS_BEGIN NDPPD_NS_BEGIN
@ -65,13 +66,19 @@ iface::~iface()
if (_prev_allmulti >= 0) { if (_prev_allmulti >= 0) {
allmulti(_prev_allmulti); allmulti(_prev_allmulti);
} }
if (_prev_promiscuous >= 0) {
promiscuous(_prev_promiscuous);
}
close(_pfd); close(_pfd);
} }
_map_dirty = true; _map_dirty = true;
_serves.clear();
_parents.clear();
} }
ptr<iface> iface::open_pfd(const std::string& name) ptr<iface> iface::open_pfd(const std::string& name, bool promiscuous)
{ {
int fd = 0; int fd = 0;
@ -170,6 +177,13 @@ ptr<iface> iface::open_pfd(const std::string& name)
// Eh. Allmulti. // Eh. Allmulti.
ifa->_prev_allmulti = ifa->allmulti(1); 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; _map_dirty = true;
return ifa; return ifa;
@ -287,7 +301,7 @@ ptr<iface> iface::open_ifd(const std::string& name)
return ifa; 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 msghdr mhdr;
struct iovec iov; 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)); memset(&mhdr, 0, sizeof(mhdr));
mhdr.msg_name = (caddr_t)saddr; mhdr.msg_name = (caddr_t)saddr;
mhdr.msg_namelen = sizeof(struct sockaddr); mhdr.msg_namelen = saddr_size;
mhdr.msg_iov =& iov; mhdr.msg_iov =& iov;
mhdr.msg_iovlen = 1; mhdr.msg_iovlen = 1;
if ((len = recvmsg(fd,& mhdr, 0)) < 0) if ((len = recvmsg(fd,& mhdr, 0)) < 0)
{
logger::error() << "iface::read() failed! error=" << logger::err() << ", ifa=" << name();
return -1; return -1;
}
logger::debug() << "iface::read() ifa=" << name() << ", len=" << len;
if (len < sizeof(struct icmp6_hdr)) if (len < sizeof(struct icmp6_hdr))
return -1; return -1;
logger::debug() << "iface::read() len=" << len;
return 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_iov =& iov;
mhdr.msg_iovlen = 1; 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; << size;
int len; int len;
if ((len = sendmsg(fd,& mhdr, 0)) < 0) if ((len = sendmsg(fd,& mhdr, 0)) < 0)
{ {
int e = errno; logger::error() << "iface::write() failed! error=" << logger::err() << ", ifa=" << name() << ", daddr=" << daddr.to_string();
logger::error() << "iface::write() failed! errno=" << e;
return -1; return -1;
} }
@ -357,8 +373,10 @@ ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr)
uint8_t msg[256]; uint8_t msg[256];
ssize_t len; 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; return -1;
}
struct ip6_hdr* ip6h = struct ip6_hdr* ip6h =
(struct ip6_hdr* )(msg + ETH_HLEN); (struct ip6_hdr* )(msg + ETH_HLEN);
@ -370,8 +388,13 @@ ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr)
daddr = ip6h->ip6_dst; daddr = ip6h->ip6_dst;
saddr = ip6h->ip6_src; 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() 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; return len;
} }
@ -452,11 +475,22 @@ ssize_t iface::read_advert(address& saddr, address& taddr)
uint8_t msg[256]; uint8_t msg[256];
ssize_t len; ssize_t len;
if ((len = read(_ifd, (struct sockaddr* )&t_saddr, msg, sizeof(msg))) < 0) 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, sizeof(struct sockaddr_in6), msg, sizeof(msg))) < 0) {
logger::warning() << "iface::read_advert() failed: " << logger::err();
return -1; return -1;
}
saddr = t_saddr.sin6_addr; 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) if (((struct icmp6_hdr* )msg)->icmp6_type != ND_NEIGHBOR_ADVERT)
return -1; return -1;
@ -467,6 +501,79 @@ ssize_t iface::read_advert(address& saddr, address& taddr)
return len; 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<ptr<route> >::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<ptr<route> >::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<weak_ptr<proxy> >::iterator pit = serves_begin(); pit != serves_end(); pit++) {
ptr<proxy> pr = (*pit);
if (!pr) continue;
for (std::list<ptr<rule> >::iterator it = pr->rules_begin(); it != pr->rules_end(); it++) {
ptr<rule> 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<weak_ptr<proxy> >::iterator pit = parents_begin(); pit != parents_end(); pit++) {
ptr<proxy> 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<ptr<rule> >::iterator it = parent->rules_begin(); it != parent->rules_end(); it++) {
ptr<rule> 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() void iface::fixup_pollfds()
{ {
_pollfds.resize(_map.size()* 2); _pollfds.resize(_map.size()* 2);
@ -489,22 +596,6 @@ void iface::fixup_pollfds()
} }
} }
void iface::remove_session(const ptr<session>& se)
{
for (std::list<weak_ptr<session> >::iterator it = _sessions.begin();
it != _sessions.end(); it++) {
if (*it == se) {
_sessions.erase(it);
break;
}
}
}
void iface::add_session(const ptr<session>& se)
{
_sessions.push_back(se);
}
void iface::cleanup() void iface::cleanup()
{ {
for (std::map<std::string, weak_ptr<iface> >::iterator it = _map.begin(); for (std::map<std::string, weak_ptr<iface> >::iterator it = _map.begin();
@ -534,6 +625,7 @@ int iface::poll_all()
int len; int len;
if ((len = ::poll(&_pollfds[0], _pollfds.size(), 50)) < 0) { if ((len = ::poll(&_pollfds[0], _pollfds.size(), 50)) < 0) {
logger::error() << "Failed to poll interfaces: " << logger::err();
return -1; return -1;
} }
@ -562,36 +654,95 @@ int iface::poll_all()
ptr<iface> ifa = i_it->second; ptr<iface> ifa = i_it->second;
address saddr, daddr, taddr; address saddr, daddr, taddr;
ssize_t size;
if (is_pfd) { 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(); logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str();
continue; continue;
} }
if (size == 0) {
if (!saddr.is_unicast()/* || !daddr.is_multicast()*/) { logger::debug() << "iface::read_solicit() loopback received and ignored";
continue; continue;
} }
if (ifa->_pr) { // Process any local addresses for interfaces that we are proxying
ifa->_pr->handle_solicit(saddr, daddr, taddr); 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<weak_ptr<proxy> >::iterator pit = ifa->serves_begin(); pit != ifa->serves_end(); pit++) {
ptr<proxy> 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 { } 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(); logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str();
continue; continue;
} }
if (size == 0) {
logger::debug() << "iface::read_advert() loopback received and ignored";
continue;
}
for (std::list<weak_ptr<session> >::iterator s_it = ifa->_sessions.begin(); // Process the NDP advert
s_it != ifa->_sessions.end(); s_it++) { bool handled = false;
assert(!s_it->is_null()); for (std::list<weak_ptr<proxy> >::iterator pit = ifa->parents_begin(); pit != ifa->parents_end(); pit++) {
ptr<proxy> pr = (*pit);
const ptr<session> sess = *s_it; if (!pr || !pr->ifa()) {
continue;
if ((sess->taddr() == taddr) && (sess->status() == session::WAITING)) {
sess->handle_advert();
break;
} }
// 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<ptr<rule> >::iterator it = pr->rules_begin(); it != pr->rules_end(); it++) {
ptr<rule> 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; 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 const std::string& iface::name() const
{ {
return _name; return _name;
} }
void iface::pr(const ptr<proxy>& pr) void iface::add_serves(const ptr<proxy>& pr)
{ {
_pr = pr; _serves.push_back(pr);
} }
const ptr<proxy>& iface::pr() const std::list<weak_ptr<proxy> >::iterator iface::serves_begin()
{ {
return _pr; return _serves.begin();
}
std::list<weak_ptr<proxy> >::iterator iface::serves_end()
{
return _serves.end();
}
void iface::add_parent(const ptr<proxy>& pr)
{
_parents.push_back(pr);
}
std::list<weak_ptr<proxy> >::iterator iface::parents_begin()
{
return _parents.begin();
}
std::list<weak_ptr<proxy> >::iterator iface::parents_end()
{
return _parents.end();
} }
NDPPD_NS_END NDPPD_NS_END

View File

@ -38,13 +38,13 @@ public:
static ptr<iface> open_ifd(const std::string& name); static ptr<iface> open_ifd(const std::string& name);
static ptr<iface> open_pfd(const std::string& name); static ptr<iface> open_pfd(const std::string& name, bool promiscuous);
static int poll_all(); 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. // Writes a NB_NEIGHBOR_SOLICIT message to the _ifd socket.
ssize_t write_solicit(const address& taddr); ssize_t write_solicit(const address& taddr);
@ -58,20 +58,30 @@ public:
// Reads a NB_NEIGHBOR_ADVERT message from the _ifd socket; // Reads a NB_NEIGHBOR_ADVERT message from the _ifd socket;
ssize_t read_advert(address& saddr, address& taddr); 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. // Returns the name of the interface.
const std::string& name() const; const std::string& name() const;
// Adds a session to be monitored for ND_NEIGHBOR_ADVERT messages. std::list<weak_ptr<proxy> >::iterator serves_begin();
void add_session(const ptr<session>& se);
void remove_session(const ptr<session>& se); std::list<weak_ptr<proxy> >::iterator serves_end();
void pr(const ptr<proxy>& pr); void add_serves(const ptr<proxy>& proxy);
const ptr<proxy>& pr() const; std::list<weak_ptr<proxy> >::iterator parents_begin();
std::list<weak_ptr<proxy> >::iterator parents_end();
void add_parent(const ptr<proxy>& parent);
static std::map<std::string, weak_ptr<iface> > _map;
private: private:
static std::map<std::string, weak_ptr<iface> > _map;
static bool _map_dirty; static bool _map_dirty;
@ -97,14 +107,15 @@ private:
// Previous state of ALLMULTI for the interface. // Previous state of ALLMULTI for the interface.
int _prev_allmulti; int _prev_allmulti;
// Previous state of PROMISC for the interface
int _prev_promiscuous;
// Name of this interface. // Name of this interface.
std::string _name; std::string _name;
// An array of sessions that are monitoring this interface for std::list<weak_ptr<proxy> > _serves;
// ND_NEIGHBOR_ADVERT messages.
std::list<weak_ptr<session> > _sessions;
weak_ptr<proxy> _pr; std::list<weak_ptr<proxy> > _parents;
// The link-layer address of this interface. // The link-layer address of this interface.
struct ether_addr hwaddr; struct ether_addr hwaddr;
@ -113,6 +124,10 @@ private:
// or -1 if there was an error. // or -1 if there was an error.
int allmulti(int state); 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. // Constructor.
iface(); iface();
}; };

View File

@ -36,9 +36,10 @@ using namespace ndppd;
static int daemonize() static int daemonize()
{ {
pid_t pid = fork(); pid_t pid = fork();
if (pid < 0) {
if (pid < 0) logger::error() << "Failed to fork during daemonize: " << logger::err();
return -1; return -1;
}
if (pid > 0) if (pid > 0)
exit(0); exit(0);
@ -46,12 +47,15 @@ static int daemonize()
umask(0); umask(0);
pid_t sid = setsid(); pid_t sid = setsid();
if (sid < 0) {
if (sid < 0) logger::error() << "Failed to setsid during daemonize: " << logger::err();
return -1; return -1;
}
if (chdir("/") < 0) if (chdir("/") < 0) {
logger::error() << "Failed to change path during daemonize: " << logger::err();
return -1; return -1;
}
close(STDIN_FILENO); close(STDIN_FILENO);
close(STDOUT_FILENO); close(STDOUT_FILENO);
@ -138,6 +142,13 @@ static bool configure(ptr<conf>& cf)
else else
route::ttl(*x_cf); route::ttl(*x_cf);
if (!(x_cf = cf->find("address-ttl")))
address::ttl(30000);
else
address::ttl(*x_cf);
std::list<ptr<rule> > myrules;
std::vector<ptr<conf> >::const_iterator p_it; std::vector<ptr<conf> >::const_iterator p_it;
std::vector<ptr<conf> > proxies(cf->find_all("proxy")); std::vector<ptr<conf> > proxies(cf->find_all("proxy"));
@ -149,10 +160,15 @@ static bool configure(ptr<conf>& cf)
return false; return false;
} }
ptr<proxy> pr = proxy::open(*pr_cf); bool promiscuous = false;
if (!(x_cf = pr_cf->find("promiscuous")))
promiscuous = false;
else
promiscuous = *x_cf;
if (!pr) { ptr<proxy> pr = proxy::open(*pr_cf, promiscuous);
return true; if (!pr || pr.is_null() == true) {
return false;
} }
if (!(x_cf = pr_cf->find("router"))) if (!(x_cf = pr_cf->find("router")))
@ -160,11 +176,31 @@ static bool configure(ptr<conf>& cf)
else else
pr->router(*x_cf); 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"))) if (!(x_cf = pr_cf->find("ttl")))
pr->ttl(30000); pr->ttl(30000);
else else
pr->ttl(*x_cf); 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"))) if (!(x_cf = pr_cf->find("timeout")))
pr->timeout(500); pr->timeout(500);
else else
@ -179,16 +215,70 @@ static bool configure(ptr<conf>& cf)
address addr(*ru_cf); address addr(*ru_cf);
if (x_cf = ru_cf->find("iface")) { bool autovia = false;
pr->add_rule(addr, iface::open_ifd(*x_cf)); if (!(x_cf = ru_cf->find("autovia")))
autovia = false;
else
autovia = *x_cf;
if (x_cf = ru_cf->find("iface"))
{
ptr<iface> 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")) { } else if (ru_cf->find("auto")) {
pr->add_rule(addr, true); myrules.push_back(pr->add_rule(addr, true));
} else { } else {
pr->add_rule(addr, false); myrules.push_back(pr->add_rule(addr, false));
} }
} }
} }
// Print out all the topology
for (std::map<std::string, weak_ptr<iface> >::iterator i_it = iface::_map.begin(); i_it != iface::_map.end(); i_it++) {
ptr<iface> ifa = i_it->second;
logger::debug() << "iface " << ifa->name() << " {";
for (std::list<weak_ptr<proxy> >::iterator pit = ifa->serves_begin(); pit != ifa->serves_end(); pit++) {
ptr<proxy> pr = (*pit);
if (!pr) continue;
logger::debug() << " " << "proxy " << logger::format("%x", pr.get_pointer()) << " {";
for (std::list<ptr<rule> >::iterator rit = pr->rules_begin(); rit != pr->rules_end(); rit++) {
ptr<rule> 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<weak_ptr<proxy> >::iterator pit = ifa->parents_begin(); pit != ifa->parents_end(); pit++) {
ptr<proxy> pr = (*pit);
logger::debug() << " " << "parent " << logger::format("%x", pr.get_pointer()) << ";";
}
logger::debug() << " }";
logger::debug() << "}";
}
return true; return true;
} }
@ -261,18 +351,16 @@ int main(int argc, char* argv[], char* env[])
if (cf.is_null()) if (cf.is_null())
return -1; return -1;
if (!configure(cf))
return -1;
if (daemon) { if (daemon) {
logger::syslog(true); logger::syslog(true);
if (daemonize() < 0) { if (daemonize() < 0)
logger::error() << "Failed to daemonize process";
return 1; return 1;
}
} }
if (!configure(cf))
return -1;
if (!pidfile.empty()) { if (!pidfile.empty()) {
std::ofstream pf; std::ofstream pf;
pf.open(pidfile.c_str(), std::ios::out | std::ios::trunc); pf.open(pidfile.c_str(), std::ios::out | std::ios::trunc);
@ -311,6 +399,9 @@ int main(int argc, char* argv[], char* env[])
if (rule::any_auto()) if (rule::any_auto())
route::update(elapsed_time); route::update(elapsed_time);
if (rule::any_iface())
address::update(elapsed_time);
session::update_all(elapsed_time); session::update_all(elapsed_time);
} }

View File

@ -27,84 +27,96 @@
NDPPD_NS_BEGIN NDPPD_NS_BEGIN
static address all_nodes = address("ff02::1");
std::list<ptr<proxy> > proxy::_list; std::list<ptr<proxy> > proxy::_list;
proxy::proxy() : 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> proxy::create(const ptr<iface>& ifa) ptr<proxy> proxy::find_aunt(const std::string& ifname, const address& taddr)
{
for (std::list<ptr<proxy> >::iterator sit = _list.begin();
sit != _list.end(); sit++)
{
ptr<proxy> pr = (*sit);
bool has_addr = false;
for (std::list<ptr<rule> >::iterator it = pr->_rules.begin(); it != pr->_rules.end(); it++) {
ptr<rule> 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<proxy>();
}
ptr<proxy> proxy::create(const ptr<iface>& ifa, bool promiscuous)
{ {
ptr<proxy> pr(new proxy()); ptr<proxy> pr(new proxy());
pr->_ptr = pr; pr->_ptr = pr;
pr->_ifa = ifa; pr->_ifa = ifa;
pr->_promiscuous = promiscuous;
_list.push_back(pr); _list.push_back(pr);
ifa->pr(pr); ifa->add_serves(pr);
logger::debug() << "proxy::create() if=" << ifa->name(); logger::debug() << "proxy::create() if=" << ifa->name();
return pr; return pr;
} }
ptr<proxy> proxy::open(const std::string& ifname) ptr<proxy> proxy::open(const std::string& ifname, bool promiscuous)
{ {
ptr<iface> ifa = iface::open_pfd(ifname); ptr<iface> ifa = iface::open_pfd(ifname, promiscuous);
if (!ifa) { if (!ifa) {
return ptr<proxy>(); return ptr<proxy>();
} }
return create(ifa); return create(ifa, promiscuous);
} }
void proxy::handle_solicit(const address& saddr, const address& daddr, ptr<session> proxy::find_or_create_session(const address& taddr)
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 // Let's check this proxy's list of sessions to see if we can
// find one with the same target address. // find one with the same target address.
for (std::list<ptr<session> >::iterator sit = _sessions.begin(); for (std::list<ptr<session> >::iterator sit = _sessions.begin();
sit != _sessions.end(); sit++) { sit != _sessions.end(); sit++) {
if ((*sit)->taddr() == taddr) { if ((*sit)->taddr() == taddr)
switch ((*sit)->status()) { return (*sit);
case session::WAITING:
case session::INVALID:
break;
case session::VALID:
(*sit)->send_advert();
}
return;
}
} }
ptr<session> se;
// Since we couldn't find a session that matched, we'll try to find // 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. // a matching rule instead, and then set up a new session.
ptr<session> se;
for (std::list<ptr<rule> >::iterator it = _rules.begin(); for (std::list<ptr<rule> >::iterator it = _rules.begin();
it != _rules.end(); it++) { it != _rules.end(); it++) {
ptr<rule> ru = *it; ptr<rule> ru = *it;
logger::debug() << "checking " << ru->addr() << " against " << taddr; logger::debug() << "checking " << ru->addr() << " against " << taddr;
if (!daddr.is_multicast() && ru->addr() != daddr) {
continue;
}
if (ru->addr() == taddr) { if (ru->addr() == taddr) {
if (!se) { if (!se) {
se = session::create(_ptr, saddr, daddr, taddr); se = session::create(_ptr, taddr, _autowire, _keepalive, _retries);
} }
if (ru->is_auto()) { if (ru->is_auto()) {
@ -115,20 +127,24 @@ void proxy::handle_solicit(const address& saddr, const address& daddr,
} else { } else {
ptr<iface> ifa = rt->ifa(); ptr<iface> ifa = rt->ifa();
if (ifa && (ifa != ru->ifa())) { if (ifa && (ifa != ru->daughter())) {
se->add_iface(ifa); se->add_iface(ifa);
} }
} }
} else if (!ru->ifa()) { } else if (!ru->daughter()) {
// This rule doesn't have an interface, and thus we'll consider // This rule doesn't have an interface, and thus we'll consider
// it "static" and immediately send the response. // it "static" and immediately send the response.
se->handle_advert(); se->handle_advert();
return; return se;
} else { } else {
se->add_iface((*it)->ifa());
ptr<iface> ifa = ru->daughter();
se->add_iface(ifa);
#ifdef WITH_ND_NETLINK #ifdef WITH_ND_NETLINK
if (if_addr_find((*it)->ifa()->name(), &taddr.const_addr())) { if (if_addr_find(ifa->name(), &taddr.const_addr())) {
logger::debug() << "Sending NA out " << (*it)->ifa()->name(); logger::debug() << "Sending NA out " << ifa->name();
se->add_iface(_ifa); se->add_iface(_ifa);
se->handle_advert(); se->handle_advert();
} }
@ -139,13 +155,72 @@ void proxy::handle_solicit(const address& saddr, const address& daddr,
if (se) { if (se) {
_sessions.push_back(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<ptr<session> >::iterator s_it = _sessions.begin();
s_it != _sessions.end(); s_it++)
{
const ptr<session> sess = *s_it;
if ((sess->taddr() == taddr)) {
sess->handle_advert(saddr, ifname, use_via);
}
} }
} }
ptr<rule> proxy::add_rule(const address& addr, const ptr<iface>& 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<session> 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<session> 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<rule> proxy::add_rule(const address& addr, const ptr<iface>& ifa, bool autovia)
{ {
ptr<rule> ru(rule::create(_ptr, addr, ifa)); ptr<rule> ru(rule::create(_ptr, addr, ifa));
ru->autovia(autovia);
_rules.push_back(ru); _rules.push_back(ru);
return ru; return ru;
} }
@ -157,6 +232,16 @@ ptr<rule> proxy::add_rule(const address& addr, bool aut)
return ru; return ru;
} }
std::list<ptr<rule> >::iterator proxy::rules_begin()
{
return _rules.begin();
}
std::list<ptr<rule> >::iterator proxy::rules_end()
{
return _rules.end();
}
void proxy::remove_session(const ptr<session>& se) void proxy::remove_session(const ptr<session>& se)
{ {
_sessions.remove(se); _sessions.remove(se);
@ -167,6 +252,11 @@ const ptr<iface>& proxy::ifa() const
return _ifa; return _ifa;
} }
bool proxy::promiscuous() const
{
return _promiscuous;
}
bool proxy::router() const bool proxy::router() const
{ {
return _router; return _router;
@ -177,6 +267,36 @@ void proxy::router(bool val)
_router = 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 int proxy::ttl() const
{ {
return _ttl; return _ttl;
@ -187,6 +307,16 @@ void proxy::ttl(int val)
_ttl = (val >= 0) ? val : 30000; _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 int proxy::timeout() const
{ {
return _timeout; return _timeout;

View File

@ -30,25 +30,50 @@ class rule;
class proxy { class proxy {
public: public:
static ptr<proxy> create(const ptr<iface>& ifa); static ptr<proxy> create(const ptr<iface>& ifa, bool promiscuous);
static ptr<proxy> open(const std::string& ifn); static ptr<proxy> find_aunt(const std::string& ifname, const address& taddr);
void handle_solicit(const address& saddr, const address& daddr, static ptr<proxy> open(const std::string& ifn, bool promiscuous);
const address& taddr);
ptr<session> 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<session>& se); void remove_session(const ptr<session>& se);
ptr<rule> add_rule(const address& addr, const ptr<iface>& ifa); ptr<rule> add_rule(const address& addr, const ptr<iface>& ifa, bool autovia);
ptr<rule> add_rule(const address& addr, bool aut = false); ptr<rule> add_rule(const address& addr, bool aut = false);
std::list<ptr<rule> >::iterator rules_begin();
std::list<ptr<rule> >::iterator rules_end();
const ptr<iface>& ifa() const; const ptr<iface>& ifa() const;
bool promiscuous() const;
bool router() const; bool router() const;
void router(bool val); 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; int timeout() const;
void timeout(int val); void timeout(int val);
@ -57,6 +82,10 @@ public:
void ttl(int val); void ttl(int val);
int deadtime() const;
void deadtime(int val);
private: private:
static std::list<ptr<proxy> > _list; static std::list<ptr<proxy> > _list;
@ -68,9 +97,17 @@ private:
std::list<ptr<session> > _sessions; std::list<ptr<session> > _sessions;
bool _promiscuous;
bool _router; bool _router;
int _ttl, _timeout; bool _autowire;
int _retries;
bool _keepalive;
int _ttl, _deadtime, _timeout;
proxy(); proxy();
}; };

View File

@ -105,19 +105,19 @@ void route::load(const std::string& path)
unsigned char pfx; 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? // TODO: Warn here?
continue; continue;
} }
if (hexdec(buf + 33,& pfx, 1) != 1) { if (route::hexdec(buf + 33,& pfx, 1) != 1) {
// TODO: Warn here? // TODO: Warn here?
continue; continue;
} }
addr.prefix((int)pfx); addr.prefix((int)pfx);
route::create(addr, token(buf + 141)); route::create(addr, route::token(buf + 141));
} }
} catch (std::ifstream::failure e) { } catch (std::ifstream::failure e) {
logger::warning() << "Failed to parse IPv6 routing data from '" << path << "'"; logger::warning() << "Failed to parse IPv6 routing data from '" << path << "'";

View File

@ -45,6 +45,12 @@ public:
ptr<iface> ifa(); ptr<iface> 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: private:
static int _ttl; static int _ttl;
@ -56,14 +62,8 @@ private:
ptr<iface> _ifa; ptr<iface> _ifa;
static size_t hexdec(const char* str, unsigned char* buf, size_t size);
static std::string token(const char* str);
static std::list<ptr<route> > _routes; static std::list<ptr<route> > _routes;
route(const address& addr, const std::string& ifname);
}; };
NDPPD_NS_END NDPPD_NS_END

View File

@ -29,6 +29,10 @@ std::vector<interface> interfaces;
bool rule::_any_aut = false; bool rule::_any_aut = false;
bool rule::_any_iface = false;
bool rule::_any_static = false;
rule::rule() rule::rule()
{ {
} }
@ -38,9 +42,10 @@ ptr<rule> rule::create(const ptr<proxy>& pr, const address& addr, const ptr<ifac
ptr<rule> ru(new rule()); ptr<rule> ru(new rule());
ru->_ptr = ru; ru->_ptr = ru;
ru->_pr = pr; ru->_pr = pr;
ru->_ifa = ifa; ru->_daughter = ifa;
ru->_addr = addr; ru->_addr = addr;
ru->_aut = false; ru->_aut = false;
_any_iface = true;
unsigned int ifindex; unsigned int ifindex;
ifindex = if_nametoindex(pr->ifa()->name().c_str()); ifindex = if_nametoindex(pr->ifa()->name().c_str());
@ -52,7 +57,7 @@ ptr<rule> rule::create(const ptr<proxy>& pr, const address& addr, const ptr<ifac
if_add_to_list(ifindex, ifa); if_add_to_list(ifindex, ifa);
#endif #endif
logger::debug() << "rule::create() if=" << pr->ifa()->name() << ", addr=" << addr; logger::debug() << "rule::create() if=" << pr->ifa()->name() << ", slave=" << ifa->name() << ", addr=" << addr;
return ru; return ru;
} }
@ -66,6 +71,9 @@ ptr<rule> rule::create(const ptr<proxy>& pr, const address& addr, bool aut)
ru->_aut = aut; ru->_aut = aut;
_any_aut = _any_aut || aut; _any_aut = _any_aut || aut;
if (aut == false)
_any_static = true;
logger::debug() logger::debug()
<< "rule::create() if=" << pr->ifa()->name().c_str() << ", addr=" << addr << "rule::create() if=" << pr->ifa()->name().c_str() << ", addr=" << addr
<< ", auto=" << (aut ? "yes" : "no"); << ", auto=" << (aut ? "yes" : "no");
@ -78,9 +86,9 @@ const address& rule::addr() const
return _addr; return _addr;
} }
ptr<iface> rule::ifa() const ptr<iface> rule::daughter() const
{ {
return _ifa; return _daughter;
} }
bool rule::is_auto() const bool rule::is_auto() const
@ -88,11 +96,31 @@ bool rule::is_auto() const
return _aut; return _aut;
} }
bool rule::autovia() const
{
return _autovia;
}
void rule::autovia(bool val)
{
_autovia = val;
}
bool rule::any_auto() bool rule::any_auto()
{ {
return _any_aut; return _any_aut;
} }
bool rule::any_iface()
{
return _any_iface;
}
bool rule::any_static()
{
return _any_static;
}
bool rule::check(const address& addr) const bool rule::check(const address& addr) const
{ {
return _addr == addr; return _addr == addr;

View File

@ -37,7 +37,7 @@ public:
const address& addr() const; const address& addr() const;
ptr<iface> ifa() const; ptr<iface> daughter() const;
bool is_auto() const; bool is_auto() const;
@ -45,12 +45,20 @@ public:
static bool any_auto(); static bool any_auto();
static bool any_static();
static bool any_iface();
bool autovia() const;
void autovia(bool val);
private: private:
weak_ptr<rule> _ptr; weak_ptr<rule> _ptr;
weak_ptr<proxy> _pr; weak_ptr<proxy> _pr;
ptr<iface> _ifa; ptr<iface> _daughter;
address _addr; address _addr;
@ -58,6 +66,12 @@ private:
static bool _any_aut; static bool _any_aut;
static bool _any_static;
static bool _any_iface;
bool _autovia;
rule(); rule();
}; };

View File

@ -14,6 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <algorithm> #include <algorithm>
#include <sstream>
#include "ndppd.h" #include "ndppd.h"
#include "proxy.h" #include "proxy.h"
@ -42,10 +43,54 @@ void session::update_all(int elapsed_time)
} }
switch (se->_status) { switch (se->_status) {
case session::WAITING: case session::WAITING:
logger::debug() << "session is now invalid"; if (se->_fails < se->_retries) {
se->_status = session::INVALID; logger::debug() << "session will keep trying [taddr=" << se->_taddr << "]";
se->_ttl = se->_pr->ttl();
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; break;
default: default:
@ -58,29 +103,33 @@ session::~session()
{ {
logger::debug() << "session::~session() this=" << logger::format("%x", this); logger::debug() << "session::~session() this=" << logger::format("%x", this);
for (std::list<ptr<iface> >::iterator it = _ifaces.begin(); if (_wired == true) {
for (std::list<ptr<iface> >::iterator it = _ifaces.begin();
it != _ifaces.end(); it++) { it != _ifaces.end(); it++) {
(*it)->remove_session(_ptr); handle_auto_unwire((*it)->name());
}
} }
} }
ptr<session> session::create(const ptr<proxy>& pr, const address& saddr, ptr<session> session::create(const ptr<proxy>& pr, const address& taddr, bool auto_wire, bool keepalive, int retries)
const address& daddr, const address& taddr)
{ {
ptr<session> se(new session()); ptr<session> se(new session());
se->_ptr = se; se->_ptr = se;
se->_pr = pr; se->_pr = pr;
se->_saddr = address("::") == saddr ? all_nodes : saddr; se->_taddr = taddr;
se->_taddr = taddr; se->_autowire = auto_wire;
se->_daddr = daddr; se->_keepalive = keepalive;
se->_ttl = pr->timeout(); se->_retries = retries;
se->_wired = false;
se->_ttl = pr->ttl();
se->_touched = false;
_sessions.push_back(se); _sessions.push_back(se);
logger::debug() logger::debug()
<< "session::create() pr=" << logger::format("%x", (proxy* )pr) << ", saddr=" << saddr << "session::create() pr=" << logger::format("%x", (proxy* )pr) << ", proxy=" << ((pr->ifa()) ? pr->ifa()->name() : "null")
<< ", daddr=" << daddr << ", taddr=" << taddr << " =" << logger::format("%x", (session* )se); << ", taddr=" << taddr << " =" << logger::format("%x", (session* )se);
return se; return se;
} }
@ -90,10 +139,19 @@ void session::add_iface(const ptr<iface>& ifa)
if (std::find(_ifaces.begin(), _ifaces.end(), ifa) != _ifaces.end()) if (std::find(_ifaces.begin(), _ifaces.end(), ifa) != _ifaces.end())
return; return;
ifa->add_session(_ptr);
_ifaces.push_back(ifa); _ifaces.push_back(ifa);
} }
void session::add_pending(const address& addr)
{
for (std::list<ptr<address> >::iterator ad = _pending.begin(); ad != _pending.end(); ad++) {
if (addr == (*ad))
return;
}
_pending.push_back(new address(addr));
}
void session::send_solicit() void session::send_solicit()
{ {
logger::debug() << "session::send_solicit() (_ifaces.size() = " << _ifaces.size() << ")"; 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() void session::handle_advert()
{ {
_status = VALID; logger::debug()
_ttl = _pr->ttl(); << "session::handle_advert() taddr=" << _taddr << ", ttl=" << _pr->ttl();
send_advert(); if (_status != VALID) {
_status = VALID;
logger::debug() << "session is active [taddr=" << _taddr << "]";
}
_ttl = _pr->ttl();
_fails = 0;
if (!_pending.empty()) {
for (std::list<ptr<address> >::iterator ad = _pending.begin();
ad != _pending.end(); ad++) {
ptr<address> addr = (*ad);
logger::debug() << " - forward to " << addr;
send_advert(addr);
}
_pending.clear();
}
} }
const address& session::taddr() const const address& session::taddr() const
@ -123,14 +326,34 @@ const address& session::taddr() const
return _taddr; 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 int session::status() const

View File

@ -16,6 +16,7 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <string>
#include "ndppd.h" #include "ndppd.h"
@ -32,14 +33,30 @@ private:
address _saddr, _daddr, _taddr; address _saddr, _daddr, _taddr;
bool _autowire;
bool _keepalive;
bool _wired;
address _wired_via;
bool _touched;
// An array of interfaces this session is monitoring for // An array of interfaces this session is monitoring for
// ND_NEIGHBOR_ADVERT on. // ND_NEIGHBOR_ADVERT on.
std::list<ptr<iface> > _ifaces; std::list<ptr<iface> > _ifaces;
std::list<ptr<address> > _pending;
// The remaining time in miliseconds the object will stay in the // The remaining time in miliseconds the object will stay in the
// interface's session array or cache. // interface's session array or cache.
int _ttl; int _ttl;
int _fails;
int _retries;
int _status; int _status;
static std::list<weak_ptr<session> > _sessions; static std::list<weak_ptr<session> > _sessions;
@ -47,9 +64,10 @@ private:
public: public:
enum enum
{ {
WAITING, // Waiting for an advert response. WAITING, // Waiting for an advert response.
VALID, // Valid; RENEWING, // Renewing;
INVALID // Invalid; VALID, // Valid;
INVALID // Invalid;
}; };
static void update_all(int elapsed_time); static void update_all(int elapsed_time);
@ -57,24 +75,45 @@ public:
// Destructor. // Destructor.
~session(); ~session();
static ptr<session> create(const ptr<proxy>& pr, const address& saddr, static ptr<session> create(const ptr<proxy>& pr, const address& taddr, bool autowire, bool keepalive, int retries);
const address& daddr, const address& taddr);
void add_iface(const ptr<iface>& ifa); void add_iface(const ptr<iface>& ifa);
void add_pending(const address& addr);
const address& taddr() const; const address& taddr() const;
const address& daddr() const; const address& daddr() const;
const address& saddr() 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; int status() const;
void status(int val); void status(int val);
void handle_advert(); 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(); void send_solicit();