Skip to content

Commit

Permalink
Add functionality to reply to NS packets which are destined for a pro…
Browse files Browse the repository at this point in the history
…xied interface.

Uses netlink sockets to learn the addresses on the proxied
interfaces so that NS packets can be replied to (with a NA)
when the address is present.

Signed-off-by: Carl Smith <[email protected]>
  • Loading branch information
Jason Rippon authored and carlgsmith committed Oct 27, 2016
1 parent a35def5 commit ab7b2aa
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 3 deletions.
8 changes: 5 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 $@ $<
Expand Down
275 changes: 275 additions & 0 deletions src/nd-netlink.cc
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

#include <sys/socket.h>
#include <errno.h>
#include <netlink/route/addr.h>
#include <arpa/inet.h>
#include "ndppd.h"
#include <algorithm>

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<iface>& ifa)
{
bool found = false;

pthread_mutex_lock (&cs_mutex);
for (std::vector<interface>::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<interface>::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<address>::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<interface>::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<interface>::iterator it = interfaces.begin();
it != interfaces.end(); it++) {
if (iface.compare((*it)._name) == 0) {
address addr = address(*iaddr);
std::list<address>::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
28 changes: 28 additions & 0 deletions src/nd-netlink.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

#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<iface>& ifa);

NDPPD_NS_END
3 changes: 3 additions & 0 deletions src/ndppd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -308,6 +310,7 @@ int main(int argc, char* argv[], char* env[])
session::update_all(elapsed_time);
}

netlink_teardown();
logger::notice() << "Bye";

return 0;
Expand Down
1 change: 1 addition & 0 deletions src/ndppd.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@
#include "proxy.h"
#include "session.h"
#include "rule.h"
#include "nd-netlink.h"
5 changes: 5 additions & 0 deletions src/proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/rule.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>

#include "ndppd.h"
#include "rule.h"
Expand All @@ -24,6 +25,8 @@

NDPPD_NS_BEGIN

std::vector<interface> interfaces;

rule::rule()
{
}
Expand All @@ -36,6 +39,12 @@ ptr<rule> rule::create(const ptr<proxy>& pr, const address& addr, const ptr<ifac
ru->_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;

Expand Down
Loading

0 comments on commit ab7b2aa

Please sign in to comment.