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 +#include #include @@ -56,4 +57,19 @@ class rule { rule(); }; +class interface { +public: + // List of IPv6 addresses on this interface + std::list
addresses; + + // Index of this interface + int ifindex; + + // Name of this interface. + std::string _name; + +}; + +extern std::vector interfaces; + NDPPD_NS_END