diff --git a/Makefile b/Makefile
index 27b7624..7f37eca 100644
--- a/Makefile
+++ b/Makefile
@@ -9,11 +9,13 @@ CXX ?= g++
GZIP ?= /bin/gzip
MANDIR ?= ${DESTDIR}${PREFIX}/share/man
SBINDIR ?= ${DESTDIR}${PREFIX}/sbin
+PKG_CONFIG ?= pkg-config
-LIBS =
+LIBS = `${PKG_CONFIG} --libs glib-2.0 libnl-3.0 libnl-route-3.0` -pthread
+CPPFLAGS = `${PKG_CONFIG} --cflags glib-2.0 libnl-3.0 libnl-route-3.0`
OBJS = src/logger.o src/ndppd.o src/iface.o src/proxy.o src/address.o \
- src/rule.o src/session.o src/conf.o src/route.o
+ src/rule.o src/session.o src/conf.o src/route.o src/nd-netlink.o
all: ndppd ndppd.1.gz ndppd.conf.5.gz nd-proxy
@@ -35,7 +37,7 @@ ndppd: ${OBJS}
${CXX} -o ndppd ${LDFLAGS} ${LIBS} ${OBJS}
nd-proxy: nd-proxy.c
- ${CXX} -o nd-proxy -Wall -Werror ${LDFLAGS} `pkg-config --cflags glib-2.0` nd-proxy.c `pkg-config --libs glib-2.0`
+ ${CXX} -o nd-proxy -Wall -Werror ${LDFLAGS} `${PKG_CONFIG} --cflags glib-2.0` nd-proxy.c `${PKG_CONFIG} --libs glib-2.0`
.cc.o:
${CXX} -c ${CPPFLAGS} $(CXXFLAGS) -o $@ $<
diff --git a/src/nd-netlink.cc b/src/nd-netlink.cc
new file mode 100644
index 0000000..f0137c4
--- /dev/null
+++ b/src/nd-netlink.cc
@@ -0,0 +1,275 @@
+//
+//@file nd-netlink.cc
+//
+// Copyright 2016, Allied Telesis Labs New Zealand, Ltd
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include
+#include
+#include
+#include
+#include "ndppd.h"
+#include
+
+NDPPD_NS_BEGIN
+
+pthread_mutex_t cs_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct in6_addr*
+address_create_ipv6(struct in6_addr *local)
+{
+ struct in6_addr *addr = (struct in6_addr *)calloc(1, sizeof(struct in6_addr));
+ memcpy(addr, local, sizeof(struct in6_addr));
+ return addr;
+}
+
+void if_add_to_list(int ifindex, const ptr& ifa)
+{
+ bool found = false;
+
+ pthread_mutex_lock (&cs_mutex);
+ for (std::vector::iterator it = interfaces.begin();
+ it != interfaces.end(); it++) {
+ if ((*it).ifindex == ifindex) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ logger::debug() << "rule::add_iface() if=" << ifa->name();
+ interface anInterface;
+ anInterface._name = ifa->name();
+ anInterface.ifindex = ifindex;
+ interfaces.push_back(anInterface);
+ }
+ pthread_mutex_unlock (&cs_mutex);
+}
+
+void
+if_addr_add(int ifindex, struct in6_addr *iaddr)
+{
+ pthread_mutex_lock (&cs_mutex);
+ for (std::vector::iterator it = interfaces.begin();
+ it != interfaces.end(); it++) {
+ if ((*it).ifindex == ifindex) {
+ address addr = address(*iaddr);
+ logger::debug() << "Adding addr " << addr.to_string();
+ std::list::iterator it_addr;
+ it_addr = std::find((*it).addresses.begin(), (*it).addresses.end(), addr);
+ if (it_addr == (*it).addresses.end()) {
+ (*it).addresses.push_back(addr);
+ }
+ break;
+ }
+ }
+ free(iaddr);
+ pthread_mutex_unlock (&cs_mutex);
+}
+
+void
+if_addr_del(int ifindex, struct in6_addr *iaddr)
+{
+ pthread_mutex_lock (&cs_mutex);
+ for (std::vector::iterator it = interfaces.begin();
+ it != interfaces.end(); it++) {
+ if ((*it).ifindex == ifindex) {
+ address addr = address(*iaddr);
+ logger::debug() << "Deleting addr " << addr.to_string();
+ (*it).addresses.remove(addr);
+ break;
+ }
+ }
+ free(iaddr);
+ pthread_mutex_unlock (&cs_mutex);
+}
+
+bool
+if_addr_find(std::string iface, const struct in6_addr *iaddr)
+{
+ bool found = false;
+
+ pthread_mutex_lock (&cs_mutex);
+ for (std::vector::iterator it = interfaces.begin();
+ it != interfaces.end(); it++) {
+ if (iface.compare((*it)._name) == 0) {
+ address addr = address(*iaddr);
+ std::list::iterator it_addr;
+ it_addr = std::find((*it).addresses.begin(), (*it).addresses.end(), addr);
+ if (it_addr != (*it).addresses.end()) {
+ found = true;
+ break;
+ }
+ }
+ }
+ pthread_mutex_unlock (&cs_mutex);
+ return found;
+}
+
+static void
+nl_msg_newaddr(struct nlmsghdr *hdr)
+{
+ struct ifaddrmsg *ifaddr =
+ (struct ifaddrmsg *)(((char *) hdr) + (sizeof(struct nlmsghdr)));
+ // parse the attributes
+ struct nlattr *attrs[IFA_MAX + 1];
+ struct nlattr *s = (struct nlattr *)(((char *) ifaddr) + (sizeof(struct ifaddrmsg)));
+ int len = nlmsg_datalen(hdr) - sizeof(struct ifinfomsg);
+ memset(&attrs, '\0', sizeof(attrs));
+ nla_parse(attrs, IFA_MAX, s, len, NULL);
+
+ struct in6_addr* addr = NULL;
+
+ if (ifaddr->ifa_family == AF_INET6) {
+ addr = address_create_ipv6((struct in6_addr *)nla_data(attrs[IFA_ADDRESS]));
+ if_addr_add(ifaddr->ifa_index, addr);
+ }
+}
+
+static void
+nl_msg_deladdr(struct nlmsghdr *hdr)
+{
+ struct ifaddrmsg *ifaddr =
+ (struct ifaddrmsg *)(((char *) hdr) + (sizeof(struct nlmsghdr)));
+ // parse the attributes
+ struct nlattr *attrs[IFA_MAX + 1];
+ struct nlattr *s = (struct nlattr *)(((char *) ifaddr) + (sizeof(struct ifaddrmsg)));
+ int len = nlmsg_datalen(hdr) - sizeof(struct ifinfomsg);
+ memset(&attrs, '\0', sizeof(attrs));
+ nla_parse(attrs, IFA_MAX, s, len, NULL);
+
+ struct in6_addr* addr = NULL;
+
+ if (ifaddr->ifa_family == AF_INET6) {
+ addr = address_create_ipv6((struct in6_addr *)nla_data(attrs[IFA_ADDRESS]));
+ if_addr_del(ifaddr->ifa_index, addr);
+ }
+}
+
+static void
+new_addr(struct nl_object *obj, void *p)
+{
+ struct rtnl_addr *addr = (struct rtnl_addr *) obj;
+ struct nl_addr *local = rtnl_addr_get_local(addr);
+ int family = rtnl_addr_get_family(addr);
+ int ifindex = rtnl_addr_get_ifindex(addr);
+ struct in6_addr* in_addr = NULL;
+
+ char ipstr[INET6_ADDRSTRLEN];
+ inet_ntop(family, nl_addr_get_binary_addr(local), ipstr, INET6_ADDRSTRLEN);
+
+ switch (family) {
+ case AF_INET:
+ break;
+ case AF_INET6:
+ in_addr = address_create_ipv6((struct in6_addr *)nl_addr_get_binary_addr(local));
+ if_addr_add(ifindex, in_addr);
+ break;
+ default:
+ logger::error() << "Unknown message family: " << family;
+ }
+}
+
+static int
+nl_msg_handler(struct nl_msg *msg, void *arg)
+{
+ logger::debug() << "nl_msg_handler";
+ struct nlmsghdr *hdr = nlmsg_hdr(msg);
+
+ switch (hdr->nlmsg_type) {
+ case RTM_NEWADDR:
+ nl_msg_newaddr(hdr);
+ break;
+ case RTM_DELADDR:
+ nl_msg_deladdr(hdr);
+ break;
+ default:
+ logger::error() << "Unknown message type: " << hdr->nlmsg_type;
+ }
+
+ return NL_OK;
+}
+
+static void *
+netlink_monitor(void *p)
+{
+ struct nl_sock *sock = (struct nl_sock *) p;
+ struct nl_cache *addr_cache;
+
+ // get all the current addresses
+ if (rtnl_addr_alloc_cache(sock, &addr_cache) < 0) {
+ perror("rtnl_addr_alloc_cache");
+ return NULL;
+ }
+
+ // add existing addresses
+ nl_cache_foreach(addr_cache, new_addr, NULL);
+ // destroy the cache
+ nl_cache_free(addr_cache);
+
+ // switch to notification mode
+ // disable sequence checking
+ nl_socket_disable_seq_check(sock);
+ // set the callback we want
+ nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, nl_msg_handler, NULL);
+
+ // subscribe to the IPv6 address change callbacks
+ nl_socket_add_memberships(sock, RTNLGRP_IPV6_IFADDR, 0);
+
+ while (1)
+ {
+ nl_recvmsgs_default(sock);
+ }
+ return NULL;
+}
+
+static pthread_t monitor_thread;
+static struct nl_sock *monitor_sock;
+struct nl_sock *control_sock;
+
+bool
+netlink_setup()
+{
+ // create a netlink socket
+ control_sock = nl_socket_alloc();
+ nl_connect(control_sock, NETLINK_ROUTE);
+
+ // create a thread to run the netlink monitor in
+ // create a netlink socket
+ monitor_sock = nl_socket_alloc();
+ nl_connect(monitor_sock, NETLINK_ROUTE);
+ // increase the recv buffer size to capture all notifications
+ nl_socket_set_buffer_size(monitor_sock, 2048000, 0);
+
+ pthread_create(&monitor_thread, NULL, netlink_monitor, monitor_sock);
+ pthread_setname_np(monitor_thread, "netlink");
+ if (pthread_setschedprio(monitor_thread, -10) < 0)
+ {
+ logger::warning() << "setschedprio: " << strerror(errno);
+ }
+ return true;
+}
+
+bool
+netlink_teardown()
+{
+ void *res = 0;
+ pthread_cancel(monitor_thread);
+ pthread_join(monitor_thread, &res);
+ nl_socket_free(monitor_sock);
+ nl_socket_free(control_sock);
+ return true;
+}
+
+NDPPD_NS_END
diff --git a/src/nd-netlink.h b/src/nd-netlink.h
new file mode 100644
index 0000000..c612569
--- /dev/null
+++ b/src/nd-netlink.h
@@ -0,0 +1,28 @@
+//
+// @file nd-netlink.h
+//
+// Copyright 2016, Allied Telesis Labs New Zealand, Ltd
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#pragma once
+
+NDPPD_NS_BEGIN
+
+bool netlink_teardown();
+bool netlink_setup();
+bool if_addr_find(std::string iface, const struct in6_addr *iaddr);
+void if_add_to_list(int ifindex, const ptr& ifa);
+
+NDPPD_NS_END
diff --git a/src/ndppd.cc b/src/ndppd.cc
index 8642759..fe6f05b 100644
--- a/src/ndppd.cc
+++ b/src/ndppd.cc
@@ -286,6 +286,8 @@ int main(int argc, char* argv[], char* env[])
gettimeofday(&t1, 0);
+ netlink_setup();
+
while (running) {
if (iface::poll_all() < 0) {
if (running) {
@@ -308,6 +310,7 @@ int main(int argc, char* argv[], char* env[])
session::update_all(elapsed_time);
}
+ netlink_teardown();
logger::notice() << "Bye";
return 0;
diff --git a/src/ndppd.h b/src/ndppd.h
index 008726c..57ba829 100644
--- a/src/ndppd.h
+++ b/src/ndppd.h
@@ -35,3 +35,4 @@
#include "proxy.h"
#include "session.h"
#include "rule.h"
+#include "nd-netlink.h"
diff --git a/src/proxy.cc b/src/proxy.cc
index 8bbc2e6..d3b6e66 100644
--- a/src/proxy.cc
+++ b/src/proxy.cc
@@ -126,6 +126,11 @@ void proxy::handle_solicit(const address& saddr, const address& daddr,
return;
} else {
se->add_iface((*it)->ifa());
+ if (if_addr_find((*it)->ifa()->name(), &taddr.const_addr())) {
+ logger::debug() << "Sending NA out " << (*it)->ifa()->name();
+ se->add_iface(_ifa);
+ se->handle_advert();
+ }
}
}
}
diff --git a/src/rule.cc b/src/rule.cc
index 9e72480..ad4a7ec 100644
--- a/src/rule.cc
+++ b/src/rule.cc
@@ -16,6 +16,7 @@
#include
#include
#include
+#include
#include "ndppd.h"
#include "rule.h"
@@ -24,6 +25,8 @@
NDPPD_NS_BEGIN
+std::vector interfaces;
+
rule::rule()
{
}
@@ -36,6 +39,12 @@ ptr rule::create(const ptr& pr, const address& addr, const ptr_ifa = ifa;
ru->_addr = addr;
ru->_aut = false;
+ unsigned int ifindex;
+
+ ifindex = if_nametoindex(pr->ifa()->name().c_str());
+ if_add_to_list(ifindex, pr->ifa());
+ ifindex = if_nametoindex(ifa->name().c_str());
+ if_add_to_list(ifindex, ifa);
logger::debug() << "rule::create() if=" << pr->ifa()->name() << ", addr=" << addr;
diff --git a/src/rule.h b/src/rule.h
index 6663066..72c30ea 100644
--- a/src/rule.h
+++ b/src/rule.h
@@ -18,6 +18,7 @@
#include
#include
#include