Compare commits

..

10 Commits

Author SHA1 Message Date
Daniel Adolfsson
9c17195bdd Update debian/changelog 2012-09-21 13:13:56 +00:00
Daniel Adolfsson
168b57ef0f Update maintainer information and remove libconfuse dependency 2012-09-21 13:13:56 +00:00
Daniel Adolfsson
8ebff291ca Update 'changelog' for the new 0.2.2 version 2012-09-21 13:13:56 +00:00
Daniel Adolfsson
6c3c9a4f49 Fix remaining Lintian warnings 2012-09-21 13:13:55 +00:00
Daniel Adolfsson
9bdb6c6293 Update debian/changelog to close ITP bug #644932 2012-09-21 13:13:55 +00:00
Daniel Adolfsson
d6126fb7b8 Get rid of Lintian warnings 2012-09-21 13:13:55 +00:00
Daniel Adolfsson
33cdc183d9 Clean up debian packaging 2012-09-21 13:13:55 +00:00
Daniel Adolfsson
09582acd46 Update changelog for 0.2.1 release 2012-09-21 13:13:55 +00:00
Daniel Adolfsson
e187093f32 Fix for build-depends-on-1-revision 2012-09-21 13:13:55 +00:00
Daniel Adolfsson
ce24a858f6 Add debian/ for generating Debian package 2012-09-21 13:13:55 +00:00
37 changed files with 445 additions and 2394 deletions

View File

@ -1,65 +1,3 @@
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>
* Version 0.2.5
* Defer configuration of interfaces until after daemonized; fixes an
issue where ndppd would fail to set ALLMULTI on the interface
properly.
* Fix a cast so ndppd can be compiled on GCC 6.
* Fix so ndppd changes working directory to / and umask to 0 once
daemonized.
2015-10-13 Daniel Adolfsson <daniel@priv.nu>
* Version 0.2.4
* Fix an issue where ndppd daemonizes too early.
* Fix to make sure the right pid is written to the pidfile.
2012-09-21 Daniel Adolfsson <daniel@priv.nu>
* Version 0.2.3
@ -72,7 +10,7 @@
* New "auto" configuration to detect outgoing interface, for forwarding
Neighbor Solicitation Messages.
* Improved logging.
* Bug fixes related to memory management.

View File

@ -9,17 +9,11 @@ CXX ?= g++
GZIP ?= /bin/gzip
MANDIR ?= ${DESTDIR}${PREFIX}/share/man
SBINDIR ?= ${DESTDIR}${PREFIX}/sbin
PKG_CONFIG ?= pkg-config
LIBS =
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
ifdef WITH_ND_NETLINK
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`
OBJ = ${OBJ} src/nd-netlink.o
endif
src/rule.o src/session.o src/conf.o src/route.o
all: ndppd ndppd.1.gz ndppd.conf.5.gz
@ -37,13 +31,10 @@ ndppd.conf.5.gz:
${GZIP} < ndppd.conf.5 > ndppd.conf.5.gz
ndppd: ${OBJS}
${CXX} -o ndppd ${LDFLAGS} ${OBJS} ${LIBS}
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 ndppd ${LDFLAGS} ${LIBS} ${OBJS}
.cc.o:
${CXX} -c ${CPPFLAGS} $(CXXFLAGS) -o $@ $<
${CXX} -c $(CXXFLAGS) -o $@ $<
clean:
rm -f ndppd ndppd.conf.5.gz ndppd.1.gz ${OBJS} nd-proxy
rm -f ndppd ndppd.conf.5.gz ndppd.1.gz ${OBJS}

23
README
View File

@ -1,24 +1,24 @@
ndppd - NDP Proxy Daemon
Version 0.2.5
Version 0.2.2
------------------------------------------------------------------------
1. Legal
------------------------------------------------------------------------
ndppd - NDP Proxy Daemon
Copyright (C) 2011-2016 Daniel Adolfsson <daniel@priv.nu>
Copyright (C) 2011 Daniel Adolfsson <daniel@priv.nu>
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/>.
@ -93,7 +93,7 @@
If you want to enable debugging, you can type:
make DEBUG=1 all
Note that this version of the binary is much bigger, and the daemon
produces a lot of messages.
@ -101,8 +101,8 @@
5. Usage
------------------------------------------------------------------------
Read through 'ndppd.conf-dist' for guidelines and examples how to
configure the daemon.
Read through 'ndppd.conf.example' for guidelines how to configure
the daemon.
Usage: ndppd [-d] [-c <config>] [-p <pidfile>]
@ -115,7 +115,7 @@
-d Daemonize the process, putting it in the background.
Also enables syslogging.
-v Increase logging verbosity. Can be used several times in
order to increase even further.
@ -125,9 +125,10 @@
Contact : Daniel Adolfsson <daniel@priv.nu>
Website : https://github.com/DanielAdolfsson/ndppd
Website : http://www.priv.nu/projects/ndppd
Git : git://github.com/DanielAdolfsson/ndppd.git
Git : git://github.com/Tuhox/ndppd.git
https://github.com/Tuhox/ndppd
If you want to report a bug, you can either send me a mail directly,
or submit an issue on github.com.

View File

@ -1,12 +0,0 @@
# NDPPD
***ndppd***, or ***NDP Proxy Daemon***, is a daemon that proxies *neighbor discovery* messages. It listens for *neighbor solicitations* on a
specified interface and responds with *neighbor advertisements* - as described in **RFC 4861** (section 7.2).
## Current status
Version 0.x is in maintenance, and is being replaced by `1.0-devel` which you can find [here](https://github.com/DanielAdolfsson/ndppd/tree/1.0-devel). `1.0` is not yet stable enough to be used in production, and I currently have no estimate when it is. Feel free to try it out if you like.
Latest stable release is 0.2.5:
- [Download](https://github.com/DanielAdolfsson/ndppd/releases/tag/0.2.5)
- [README](https://github.com/DanielAdolfsson/ndppd/blob/0.2.5/README)

11
debian/changelog vendored Normal file
View File

@ -0,0 +1,11 @@
ndppd (0.2.3-1) unstable; urgency=low
* New release (0.2.3).
-- Daniel Adolfsson <daniel@priv.nu> Fri, 21 Sep 2012 14:42:48 +0200
ndppd (0.2.2-1) unstable; urgency=low
* New Package. Closes: #644932.
-- Daniel Adolfsson <daniel@priv.nu> Fri, 03 Feb 2012 14:48:48 +0100

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
7

20
debian/control vendored Normal file
View File

@ -0,0 +1,20 @@
Source: ndppd
Section: net
Priority: extra
Maintainer: Daniel Adolfsson <daniel@priv.nu>
Build-Depends: debhelper (>= 7.0.50~)
Standards-Version: 3.9.2
Homepage: http://www.priv.nu/projects/ndppd
Vcs-Git: git://github.com/Tuhox/ndppd.git
Vcs-Browser: https://github.com/Tuhox/ndppd/tree/debian
Package: ndppd
Architecture: linux-any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: daemon that proxies IPv6 NDP messages
ndppd is a daemon that proxies certain IPv6 NDP messages between two or
more interfaces. It currently supports proxying Neighbor Solicitation
Messages and Neighbor Advertisement messages in order to allow IPv6
routing without relying on Linux "proxy_ndp".
.
The daemon is partially compliant with (experimental) RFC4389.

22
debian/copyright vendored Normal file
View File

@ -0,0 +1,22 @@
Format: http://anonscm.debian.org/viewvc/dep/web/deps/dep5.mdwn?revision=202
Upstream-Name: ndppd
Source: https://github.com/Tuhox/ndppd
Files: *
Copyright: 2011, Daniel Adolfsson <daniel@priv.nu>
License: GPL-3+
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 package 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/>.
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".

3
debian/docs vendored Normal file
View File

@ -0,0 +1,3 @@
README
ndppd.conf-dist

123
debian/init.d vendored Normal file
View File

@ -0,0 +1,123 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: ndppd
# Required-Start: $network $local_fs $remote_fs
# Required-Stop: $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: NDP Proxy Daemon
# Description: nppd, or NDP Proxy Daemon, is a daemon that proxies NDP
# (Neighbor Discovery Protocol) messages between interfaces.
### END INIT INFO
# Author: Daniel Adolfsson <daniel.adolfsson@tuhox.com>
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC=ndppd
NAME=ndppd
DAEMON=/usr/sbin/ndppd
PIDFILE=/var/run/$NAME.pid
DAEMON_ARGS="-d -p $PIDFILE"
SCRIPTNAME=/etc/init.d/$NAME
# Exit if the package is not installed
[ -x $DAEMON ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
. /lib/lsb/init-functions
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
$DAEMON_ARGS || return 2
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
exit 0

6
debian/rules vendored Executable file
View File

@ -0,0 +1,6 @@
#!/usr/bin/make -f
export PREFIX=/usr
%:
dh $@

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (quilt)

2
debian/watch vendored Normal file
View File

@ -0,0 +1,2 @@
version=3
http://www.tuhox.com/ndppd/files/ndppd_(.+).tar.gz

View File

@ -1,550 +0,0 @@
/**
* @file nd-proxy.c
*
* Copyright 2016, Allied Telesis Labs New Zealand, Ltd
*
* +---------+
* If A | | If B
* A-----------------| PROXY |-----------------B
* | |
* +---------+
* IPv6: A IPv6: PA IPv6: PB IPv6: B
* L2: a L2: pa L2: pb L2: b
*
* RS/RA proxy
* RS
* -------------------->
* L3src=A, L3dst=AllR L3src=A, L3dst=AllR
* L2src=a, L2dst=allr, SLL=a L2src=pb, L2dst=allr, SLL=pb
*
* RA
* <--------------------
* L3src=B, L3dst=AllN L3src=B, L3dst=AllN
* L2src=pa, L2dst=alln, SLL=pa L2src=b, L2dst=alln, SLL=b
*
* 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 <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <syslog.h>
#include <glib.h>
#include <glib-unix.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/ether.h>
#include <netpacket/packet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <linux/filter.h>
/* Mode */
#define PROXY_RS (1 << 0)
#define PROXY_RA (1 << 1)
#define PROXY_NS (1 << 2)
#define PROXY_NA (1 << 3)
#define PROXY_RD (1 << 4)
/* Debug macros */
#define DEBUG(fmt, args...) if (debug) printf (fmt, ## args)
#define ERROR(fmt, args...) \
{ \
syslog(LOG_ERR, fmt, ## args); \
fprintf(stderr, fmt, ## args); \
}
/* Proxy interface */
typedef struct _iface_t {
char *name;
uint32_t flags;
int ifindex;
uint8_t hwaddr[ETH_ALEN];
int fd;
guint src;
} iface_t;
/* Globals */
static bool debug = false;
static GList *ifaces = NULL;
/* Find the specified option in the ICMPv6 message */
static struct nd_opt_hdr *
find_option(struct icmp6_hdr *icmp6_hdr, int len, uint8_t type)
{
struct nd_opt_hdr *nd_opt;
int icmp_hlen;
/* Each ND type has a different offest to the options */
switch (icmp6_hdr->icmp6_type) {
case ND_ROUTER_SOLICIT:
icmp_hlen = sizeof(struct nd_router_solicit);
break;
case ND_ROUTER_ADVERT:
icmp_hlen = sizeof(struct nd_router_advert);
break;
case ND_NEIGHBOR_SOLICIT:
icmp_hlen = sizeof(struct nd_neighbor_solicit);
break;
case ND_NEIGHBOR_ADVERT:
icmp_hlen = sizeof(struct nd_neighbor_advert);
break;
case ND_REDIRECT:
icmp_hlen = sizeof(struct nd_redirect);
break;
default:
return NULL;
}
/* Find the option */
nd_opt = (struct nd_opt_hdr *)((uint8_t *)icmp6_hdr + icmp_hlen);
len -= icmp_hlen;
while (len > 0) {
int opt_len = nd_opt->nd_opt_len * 8;
if (nd_opt->nd_opt_type == type)
return nd_opt;
nd_opt = (struct nd_opt_hdr *)((uint8_t *)nd_opt +
sizeof(struct nd_opt_hdr) + opt_len);
len -= (sizeof(struct nd_opt_hdr) + opt_len);
}
return NULL;
}
/* Update the SLLA option in the packet (and checksum) */
static void
update_slla_option(struct icmp6_hdr *icmp6_hdr, int len, uint8_t *mac)
{
struct nd_opt_hdr *nd_opt;
/* Find the "source link-layer address" option */
nd_opt = find_option(icmp6_hdr, len, ND_OPT_SOURCE_LINKADDR);
/* Update the slla if we found it */
if (nd_opt) {
/* Option data is the mac address - it is always 16-bit aligned */
uint8_t *slla = (uint8_t *)nd_opt + sizeof(struct nd_opt_hdr);
/* Update ICMPv6 header checksum based on the old and new mac adddress */
uint16_t *omac = (uint16_t *)slla;
uint16_t *nmac = (uint16_t *)mac;
int i;
for (i = 0; i < ETH_ALEN / 2; i++) {
uint16_t hc_complement = ~ntohs(icmp6_hdr->icmp6_cksum);
uint16_t m_complement = ~ntohs(omac[i]);
uint16_t m_prime = ntohs(nmac[i]);
uint32_t sum = hc_complement + m_complement + m_prime;
while (sum >> 16) {
sum = (sum & 0xffff) + (sum >> 16);
}
icmp6_hdr->icmp6_cksum = htons(~((uint16_t)sum));
}
/* Copy the outgoing interface's hw addr into the
* "source link-layer address" option in the pkt. */
memcpy(slla, mac, ETH_ALEN);
}
}
/* Proxying of both RS and RA */
static void
proxy_rsra(iface_t *iface, uint8_t *msg, int len)
{
struct ether_header *eth_hdr;
struct ip6_hdr *ip6;
struct icmp6_hdr *icmp6_hdr;
struct sockaddr_ll socket_address;
/* Parse the packet */
eth_hdr = (struct ether_header *)msg;
ip6 = (struct ip6_hdr *)(msg + sizeof(struct ether_header));
icmp6_hdr = (struct icmp6_hdr *)(msg + sizeof(struct ether_header) +
sizeof(struct ip6_hdr));
DEBUG("Tx(%s): %s\n", iface->name,
icmp6_hdr->icmp6_type == ND_ROUTER_SOLICIT ?
"ND_ROUTER_SOLICIT" : "ND_ROUTER_ADVERT");
/* Avoid proxying spoofed packets */
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) {
DEBUG("Tx(%s): Ignoring RS/RA from spoofed address\n", iface->name);
return;
}
/* RS should be sent to "All Routers Address" FF02::2 */
/* RA should be sent to "All Nodes Address" FF02::1 */
/* Can only proxy to multicast L2 destinations 33:33:.. */
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
(eth_hdr->ether_dhost[0] != 0x33 && eth_hdr->ether_dhost[1] != 0x33)) {
DEBUG("Tx(%s): Ignoring RS/RA to non-multicast address\n", iface->name);
return;
}
/* Copy the outgoing interface's hw addr into the
* "source link-layer address" option in the pkt */
update_slla_option(icmp6_hdr, len - ((uint8_t *)icmp6_hdr - msg), iface->hwaddr);
/* Copy the outgoing interface's hw addr into the
* MAC source address in the pkt. */
memcpy((uint8_t *)(eth_hdr->ether_shost), iface->hwaddr, ETH_ALEN);
/* Send the packet */
socket_address.sll_ifindex = iface->ifindex;
socket_address.sll_halen = ETH_ALEN;
memcpy((uint8_t *)socket_address.sll_addr, iface->hwaddr, ETH_ALEN);
if (sendto (iface->fd, msg, len, 0, (struct sockaddr *)&socket_address,
sizeof(struct sockaddr_ll)) < 0) {
ERROR("Tx(%s): Failed to send packet\n", iface->name);
return;
}
}
static gboolean
handle_fd(gint fd, GIOCondition condition, gpointer data)
{
iface_t *iface = (iface_t *)data;
struct msghdr mhdr;
struct iovec iov;
struct sockaddr_ll t_saddr;
uint8_t msg[4096];
int size = 4096;
int len;
/* Receive a packet */
iov.iov_len = size;
iov.iov_base = (caddr_t)msg;
memset(&mhdr, 0, sizeof(mhdr));
mhdr.msg_name = (caddr_t)&t_saddr;
mhdr.msg_namelen = sizeof(struct sockaddr);
mhdr.msg_iov = &iov;
mhdr.msg_iovlen = 1;
if ((len = recvmsg(fd, &mhdr, 0)) < 0) {
DEBUG("Rx(%s):Interface has gone away\n", iface->name);
return true;
}
/* Check we have at least the icmp header */
if ((size_t) len < (ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))) {
ERROR("Rx(%s): Ignoring short packet (%d bytes)\n", iface->name, len);
return true;
}
struct icmp6_hdr *icmp6_hdr = (struct icmp6_hdr *)(msg + ETH_HLEN + sizeof(struct ip6_hdr));
uint8_t icmp6_type = icmp6_hdr->icmp6_type;
switch (icmp6_type) {
case ND_ROUTER_SOLICIT:
DEBUG("Rx(%s): ND_ROUTER_SOLICIT\n", iface->name);
if (iface->flags & PROXY_RS) {
GList *iter;
for (iter = ifaces; iter; iter = g_list_next(iter)) {
iface_t *oiface = (iface_t *)iter->data;
if (oiface != iface)
proxy_rsra(oiface, msg, len);
}
}
break;
case ND_ROUTER_ADVERT:
DEBUG("Rx(%s): ND_ROUTER_ADVERT\n", iface->name);
if (iface->flags & PROXY_RA) {
GList *iter;
for (iter = ifaces; iter; iter = g_list_next(iter)) {
iface_t *oiface = (iface_t *)iter->data;
if (oiface != iface)
proxy_rsra(oiface, msg, len);
}
}
break;
case ND_NEIGHBOR_SOLICIT:
case ND_NEIGHBOR_ADVERT:
case ND_REDIRECT:
default:
DEBUG("Rx(%s): ignoring ICMPv6 packets of type %d\n", iface->name, icmp6_type);
break;
}
return true;
}
static char *flags_to_string(uint32_t flags)
{
static char sbuffer[256];
sbuffer[0] = '\0';
if (flags == 0)
sprintf(sbuffer, "no packets");
if (flags & PROXY_NS)
sprintf(sbuffer + strlen(sbuffer), "%sNS",
strlen(sbuffer) ? "," : "");
if (flags & PROXY_NA)
sprintf(sbuffer + strlen(sbuffer), "%sNA",
strlen(sbuffer) ? "," : "");
if (flags & PROXY_RS)
sprintf(sbuffer + strlen(sbuffer), "%sRS",
strlen(sbuffer) ? "," : "");
if (flags & PROXY_RA)
sprintf(sbuffer + strlen(sbuffer), "%sRA",
strlen(sbuffer) ? "," : "");
if (flags & PROXY_RD)
sprintf(sbuffer + strlen(sbuffer), "%sRD",
strlen(sbuffer) ? "," : "");
return sbuffer;
}
static struct sock_filter bpf_filter[] = {
/* Load the ether_type. */
BPF_STMT(BPF_LD | BPF_H | BPF_ABS,
offsetof(struct ether_header, ether_type)),
/* Bail if it's* not* ETHERTYPE_IPV6. */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 9),
/* Load the next header type. */
BPF_STMT(BPF_LD | BPF_B | BPF_ABS,
sizeof(struct ether_header) + offsetof(struct ip6_hdr,
ip6_nxt)),
/* Bail if it's* not* IPPROTO_ICMPV6. */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 7),
/* Load the ICMPv6 type. */
BPF_STMT(BPF_LD | BPF_B | BPF_ABS,
sizeof(struct ether_header) + sizeof(struct ip6_hdr) +
offsetof(struct icmp6_hdr, icmp6_type)),
/* Bail if it's* not* ND */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_SOLICIT, 4, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 3, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_SOLICIT, 2, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_ADVERT, 1, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_REDIRECT, 0, 1),
/* Keep packet. */
BPF_STMT(BPF_RET | BPF_K, (u_int32_t)-1),
/* Drop packet. */
BPF_STMT(BPF_RET | BPF_K, 0)
};
static void iface_open(gpointer data, gpointer user)
{
iface_t *iface = (iface_t *)data;
struct sockaddr_ll lladdr;
struct sock_fprog fprog;
struct ifreq ifr;
int on = 1;
int fd;
DEBUG("Open(%s): %s\n", iface->name, flags_to_string(iface->flags));
/* Check the interface exists by getting its ifindex */
iface->ifindex = if_nametoindex(iface->name);
if (!iface->ifindex) {
ERROR("Open(%s): Could not find interface\n", iface->name);
exit(-1);
}
/* Create raw socket for tx/rx of IPv6 packets */
if ((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IPV6))) < 0) {
ERROR("Open(%s): Unable to create socket\n", iface->name);
exit(-1);
}
/* Bind the socket to the specified interface */
memset(&lladdr, 0, sizeof(struct sockaddr_ll));
lladdr.sll_family = AF_PACKET;
lladdr.sll_protocol = htons(ETH_P_IPV6);
lladdr.sll_ifindex = iface->ifindex;
if (bind(fd, (struct sockaddr *)&lladdr, sizeof(struct sockaddr_ll)) < 0) {
close(fd);
ERROR("Open(%s): Failed to bind to interface\n", iface->name);
exit(-1);
}
/* Set the socket non-blocking */
if (ioctl(fd, FIONBIO, (char *)&on) < 0) {
close(fd);
ERROR("Open(%s): Failed to make interface non-blocking\n", iface->name);
exit(-1);
}
/* Setup a filter to only receive ND packets */
fprog.len = sizeof(bpf_filter) / sizeof(bpf_filter[0]);
fprog.filter = bpf_filter;
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) {
close(fd);
ERROR("Open(%s): Failed to set filter for ND packets\n", iface->name);
exit(-1);
}
/* Enable all multicast for this interface */
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, iface->name, IFNAMSIZ - 1);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
close(fd);
ERROR("Open(%s): Failed to get flags for interface\n", iface->name);
exit(-1);
}
ifr.ifr_flags |= IFF_ALLMULTI;
if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
close(fd);
ERROR("Open(%s): Failed to set flags for interface\n", iface->name);
exit(-1);
}
/* Get the hwaddr of the interface */
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, iface->name, IFNAMSIZ - 1);
if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
ERROR("Open(%s): Failed to get interface hwaddr\n", iface->name);
exit(-1);
}
memcpy(iface->hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
/* Watch for packets */
iface->fd = fd;
iface->src = g_unix_fd_add(fd, G_IO_IN, handle_fd, iface);
}
static void iface_close(gpointer data)
{
iface_t *iface = (iface_t *)data;
DEBUG("Close(%s)\n", iface->name);
g_source_remove(iface->src);
close(iface->fd);
free(iface->name);
free(iface);
}
static iface_t *parse_interface(char *desc)
{
char *name = NULL;
uint32_t flags = PROXY_RS | PROXY_RA;
char *pflags = strchr(desc, ':');
if (pflags) {
char *token = strtok(pflags + 1, ",");
flags = 0;
while (token != NULL) {
if (strcmp("NS", token) == 0)
flags |= PROXY_NS;
else if (strcmp("NA", token) == 0)
flags |= PROXY_NA;
else if (strcmp("RA", token) == 0)
flags |= PROXY_RA;
else if (strcmp("RS", token) == 0)
flags |= PROXY_RS;
else if (strcmp("RD", token) == 0)
flags |= PROXY_RD;
else
return NULL;
token = strtok(NULL, ",");
}
name = strndup(desc, pflags - desc);
} else {
name = strdup(desc);
}
iface_t *iface = (iface_t *)g_malloc0(sizeof(iface_t));
iface->name = name;
iface->flags = flags;
iface->fd = -1;
iface->src = 0;
return iface;
}
static gboolean termination_handler(gpointer arg1)
{
GMainLoop *loop = (GMainLoop *) arg1;
g_main_loop_quit(loop);
return false;
}
void help(char *app_name)
{
printf("Usage: %s [-h] [-b] [-d] -i <interface>[:<type>[,<type>]..]\n"
" -h show this help\n"
" -b background mode\n"
" -d enable verbose debug\n"
" -i proxy [NS,NA,RS,RA,RD] messages received on <interface>\n"
"\n" "e.g %s -i eth1:RS -i eth2:RA\n", app_name, app_name);
}
int main(int argc, char *argv[])
{
int i = 0;
bool background = false;
GMainLoop *loop = NULL;
/* Parse options */
while ((i = getopt(argc, argv, "hdbi:")) != -1) {
switch (i) {
case 'd':
debug = true;
background = false;
break;
case 'b':
background = true;
break;
case 'i':
{
iface_t *iface = parse_interface(optarg);
if (!iface) {
help(argv[0]);
ERROR("ERROR: Invalid interface specification (%s)\n", optarg);
return 0;
}
ifaces = g_list_prepend(ifaces, iface);
break;
}
case '?':
case 'h':
default:
help(argv[0]);
return 0;
}
}
/* Check required */
if (g_list_length(ifaces) < 2) {
help(argv[0]);
ERROR("ERROR: Require at least 2 interfaces.\n");
return 0;
}
/* Daemonize */
if (background && fork() != 0) {
/* Parent */
return 0;
}
/* Main loop instance */
loop = g_main_loop_new(NULL, true);
/* Handle SIGTERM/SIGINT/SIGPIPE gracefully */
g_unix_signal_add(SIGINT, termination_handler, loop);
g_unix_signal_add(SIGTERM, termination_handler, loop);
/* Startup */
g_list_foreach(ifaces, iface_open, NULL);
/* Loop while not terminated */
g_main_loop_run(loop);
/* Shutdown */
g_list_free_full(ifaces, iface_close);
/* Free the glib main loop */
if (loop)
g_main_loop_unref(loop);
return 0;
}

View File

@ -1,21 +0,0 @@
#!/bin/sh
# kFreeBSD do not accept scripts as interpreters, using #!/bin/sh and sourcing.
if [ true != "$INIT_D_SCRIPT_SOURCED" ] ; then
set "$0" "$@"; INIT_D_SCRIPT_SOURCED=true . /lib/init/init-d-script
fi
### BEGIN INIT INFO
# Provides: ndppd
# Required-Start: $remote_fs $syslog $network
# Required-Stop: $remote_fs $syslog $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: ndppd init script
# Description: NDP Proxy Daemon init script
### END INIT INFO
# Author: Torben Nehmer <torben+ndppd@nehmer.net>
DESC="NDP Proxy Daemon"
PIDFILE=/run/ndppd.pid
DAEMON=/usr/local/sbin/ndppd
DAEMON_ARGS="-d -p $PIDFILE"

View File

@ -4,12 +4,6 @@
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>
# 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).
@ -28,36 +22,6 @@ proxy eth0 {
# invalidating the entry, in milliseconds. Default value is '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>
# Controls how long a valid or invalid entry remains in the cache, in
@ -91,13 +55,6 @@ proxy eth0 {
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
# method, it defaulted to 'static'. For compatibility reasons we choose
# to keep this behavior - for now (it may be removed in a future version).

View File

@ -48,20 +48,6 @@ Controls how long
.B ndppd
will cache an entry. This is in milliseconds, and the default value
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>"
Controls how long
.B ndppd

View File

@ -1,11 +0,0 @@
[Unit]
Description=NDP Proxy Daemon
After=network.target
[Service]
ExecStart=/usr/sbin/ndppd -d -p /var/run/ndppd/ndppd.pid
Type=forking
PIDFile=/var/run/ndppd/ndppd.pid
[Install]
WantedBy=multi-user.target

View File

@ -15,8 +15,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <string>
#include <vector>
#include <fstream>
#include <list>
#include <map>
#include <cstring>
@ -29,16 +27,9 @@
#include "ndppd.h"
#include "address.h"
#include "route.h"
NDPPD_NS_BEGIN
std::list<ptr<route> > address::_addresses;
int address::_ttl;
int address::_c_ttl;
address::address()
{
reset();
@ -57,19 +48,6 @@ address::address(const address& addr)
_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)
{
parse_string(str);
@ -132,21 +110,6 @@ bool address::operator!=(const address& addr) const
((_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()
{
_addr.s6_addr32[0] = 0;
@ -336,93 +299,7 @@ bool address::is_multicast() 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;
}
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

View File

@ -16,7 +16,6 @@
#pragma once
#include <string>
#include <list>
#include <netinet/ip6.h>
#include "ndppd.h"
@ -25,24 +24,15 @@ NDPPD_NS_BEGIN
class iface;
class route;
class address {
public:
address();
address(const address& addr);
address(const ptr<address>& addr);
address(const std::string& str);
address(const char* str);
address(const in6_addr& addr);
address(const in6_addr& addr, const in6_addr& mask);
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();
@ -56,8 +46,6 @@ public:
bool operator!=(const address& addr) const;
void reset();
bool is_empty() const;
const std::string to_string() const;
@ -72,22 +60,8 @@ public:
bool is_multicast() 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:
static int _ttl;
static int _c_ttl;
static std::list<ptr<route> > _addresses;
struct in6_addr _addr, _mask;
};

View File

@ -34,13 +34,11 @@
#include <linux/filter.h>
#include <errno.h>
#include <string>
#include <vector>
#include <map>
#include "ndppd.h"
#include "route.h"
NDPPD_NS_BEGIN
@ -66,19 +64,13 @@ iface::~iface()
if (_prev_allmulti >= 0) {
allmulti(_prev_allmulti);
}
if (_prev_promiscuous >= 0) {
promiscuous(_prev_promiscuous);
}
close(_pfd);
}
_map_dirty = true;
_serves.clear();
_parents.clear();
}
ptr<iface> iface::open_pfd(const std::string& name, bool promiscuous)
ptr<iface> iface::open_pfd(const std::string& name)
{
int fd = 0;
@ -155,7 +147,7 @@ ptr<iface> iface::open_pfd(const std::string& name, bool promiscuous)
// Bail if it's* not* ND_NEIGHBOR_SOLICIT.
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_SOLICIT, 0, 1),
// Keep packet.
BPF_STMT(BPF_RET | BPF_K, (u_int32_t)-1),
BPF_STMT(BPF_RET | BPF_K, -1),
// Drop packet.
BPF_STMT(BPF_RET | BPF_K, 0)
};
@ -176,13 +168,6 @@ ptr<iface> iface::open_pfd(const std::string& name, bool promiscuous)
// Eh. Allmulti.
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;
@ -227,29 +212,23 @@ ptr<iface> iface::open_ifd(const std::string& name)
if (ioctl(fd, SIOCGIFHWADDR,& ifr) < 0) {
close(fd);
logger::error()
<< "Failed to detect link-layer address for interface '"
<< name << "'";
logger::error() << "Failed to detect link-layer address for interface '" << name << "'";
return ptr<iface>();
}
logger::debug()
<< "fd=" << fd << ", hwaddr="
<< ether_ntoa((const struct ether_addr* )&ifr.ifr_hwaddr.sa_data);
logger::debug() << "fd=" << fd << ", hwaddr=" << ether_ntoa((const struct ether_addr* )&ifr.ifr_hwaddr.sa_data);;
// Set max hops.
int hops = 255;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
sizeof(hops)) < 0) {
if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,& hops, sizeof(hops)) < 0) {
close(fd);
logger::error() << "iface::open_ifd() failed IPV6_MULTICAST_HOPS";
return ptr<iface>();
}
if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops,
sizeof(hops)) < 0) {
if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,& hops, sizeof(hops)) < 0) {
close(fd);
logger::error() << "iface::open_ifd() failed IPV6_UNICAST_HOPS";
return ptr<iface>();
@ -259,11 +238,9 @@ ptr<iface> iface::open_ifd(const std::string& name)
int on = 1;
if (ioctl(fd, FIONBIO, (char*)&on) < 0) {
if (ioctl(fd, FIONBIO, (char* )&on) < 0) {
close(fd);
logger::error()
<< "Failed to switch to non-blocking on interface '"
<< name << "'";
logger::error() << "Failed to switch to non-blocking on interface '" << name << "'";
return ptr<iface>();
}
@ -271,7 +248,7 @@ ptr<iface> iface::open_ifd(const std::string& name)
struct icmp6_filter filter;
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT,& filter);
if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER,& filter, sizeof(filter)) < 0) {
logger::error() << "Failed to set filter";
@ -301,7 +278,7 @@ ptr<iface> iface::open_ifd(const std::string& name)
return ifa;
}
ssize_t iface::read(int fd, struct sockaddr* saddr, ssize_t saddr_size, uint8_t* msg, size_t size)
ssize_t iface::read(int fd, struct sockaddr* saddr, uint8_t* msg, size_t size)
{
struct msghdr mhdr;
struct iovec iov;
@ -315,21 +292,18 @@ ssize_t iface::read(int fd, struct sockaddr* saddr, ssize_t saddr_size, uint8_t*
memset(&mhdr, 0, sizeof(mhdr));
mhdr.msg_name = (caddr_t)saddr;
mhdr.msg_namelen = saddr_size;
mhdr.msg_namelen = sizeof(struct sockaddr);
mhdr.msg_iov =& iov;
mhdr.msg_iovlen = 1;
if ((len = recvmsg(fd,& mhdr, 0)) < 0)
{
logger::error() << "iface::read() failed! error=" << logger::err() << ", ifa=" << name();
return -1;
}
logger::debug() << "iface::read() ifa=" << name() << ", len=" << len;
if (len < sizeof(struct icmp6_hdr))
return -1;
logger::debug() << "iface::read() len=" << len;
return len;
}
@ -353,16 +327,12 @@ ssize_t iface::write(int fd, const address& daddr, const uint8_t* msg, size_t si
mhdr.msg_iov =& iov;
mhdr.msg_iovlen = 1;
logger::debug() << "iface::write() ifa=" << name() << ", daddr=" << daddr.to_string() << ", len="
<< size;
logger::debug() << "iface::write() daddr=" << daddr.to_string() << ", len=" << size;
int len;
if ((len = sendmsg(fd,& mhdr, 0)) < 0)
{
logger::error() << "iface::write() failed! error=" << logger::err() << ", ifa=" << name() << ", daddr=" << daddr.to_string();
return -1;
}
return len;
}
@ -373,28 +343,20 @@ ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr)
uint8_t msg[256];
ssize_t len;
if ((len = read(_pfd, (struct sockaddr*)&t_saddr, sizeof(struct sockaddr_ll), msg, sizeof(msg))) < 0) {
logger::warning() << "iface::read_solicit() failed: " << logger::err();
if ((len = read(_pfd, (struct sockaddr* )&t_saddr, msg, sizeof(msg))) < 0)
return -1;
}
struct ip6_hdr* ip6h =
(struct ip6_hdr* )(msg + ETH_HLEN);
struct nd_neighbor_solicit* ns =
(struct nd_neighbor_solicit*)(msg + ETH_HLEN + sizeof(struct ip6_hdr));
(struct nd_neighbor_solicit* )(msg + ETH_HLEN + sizeof( struct ip6_hdr));
taddr = ns->nd_ns_target;
daddr = ip6h->ip6_dst;
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()
<< ", daddr=" << daddr.to_string() << ", taddr=" << taddr.to_string() << ", len=" << len;
logger::debug() << "iface::read_solicit() saddr=" << saddr.to_string() << ", daddr=" << daddr.to_string() << ", len=" << len;
return len;
}
@ -418,8 +380,7 @@ ssize_t iface::write_solicit(const address& taddr)
memcpy(&ns->nd_ns_target,& taddr.const_addr(), sizeof(struct in6_addr));
memcpy(buf + sizeof(struct nd_neighbor_solicit) + sizeof(struct nd_opt_hdr),
&hwaddr, 6);
memcpy(buf + sizeof(struct nd_neighbor_solicit) + sizeof(struct nd_opt_hdr),& hwaddr, 6);
// FIXME: Alright, I'm lazy.
static address multicast("ff02::1:ff00:0000");
@ -432,11 +393,9 @@ ssize_t iface::write_solicit(const address& taddr)
daddr.addr().s6_addr[14] = taddr.const_addr().s6_addr[14];
daddr.addr().s6_addr[15] = taddr.const_addr().s6_addr[15];
logger::debug() << "iface::write_solicit() taddr=" << taddr.to_string()
<< ", daddr=" << daddr.to_string();
logger::debug() << "iface::write_solicit() taddr=" << taddr.to_string() << ", daddr=" << daddr.to_string();
return write(_ifd, daddr, (uint8_t* )buf, sizeof(struct nd_neighbor_solicit)
+ sizeof(struct nd_opt_hdr) + 6);
return write(_ifd, daddr, (uint8_t* )buf, sizeof(struct nd_neighbor_solicit) + sizeof(struct nd_opt_hdr) + 6);
}
ssize_t iface::write_advert(const address& daddr, const address& taddr, bool router)
@ -455,15 +414,13 @@ ssize_t iface::write_advert(const address& daddr, const address& taddr, bool rou
opt->nd_opt_len = 1;
na->nd_na_type = ND_NEIGHBOR_ADVERT;
na->nd_na_flags_reserved = (daddr.is_multicast() ? 0 : ND_NA_FLAG_SOLICITED) | (router ? ND_NA_FLAG_ROUTER : 0);
na->nd_na_flags_reserved = ND_NA_FLAG_SOLICITED | (router ? ND_NA_FLAG_ROUTER : 0);
memcpy(&na->nd_na_target,& taddr.const_addr(), sizeof(struct in6_addr));
memcpy(buf + sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr),
&hwaddr, 6);
memcpy(buf + sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr),& hwaddr, 6);
logger::debug() << "iface::write_advert() daddr=" << daddr.to_string()
<< ", taddr=" << taddr.to_string();
logger::debug() << "iface::write_advert() daddr=" << daddr.to_string() << ", taddr=" << taddr.to_string();
return write(_ifd, daddr, (uint8_t* )buf, sizeof(struct nd_neighbor_advert) +
sizeof(struct nd_opt_hdr) + 6);
@ -474,22 +431,11 @@ ssize_t iface::read_advert(address& saddr, address& taddr)
struct sockaddr_in6 t_saddr;
uint8_t msg[256];
ssize_t len;
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();
if ((len = read(_ifd, (struct sockaddr* )&t_saddr, msg, sizeof(msg))) < 0)
return -1;
}
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)
return -1;
@ -501,79 +447,6 @@ ssize_t iface::read_advert(address& saddr, address& taddr)
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()
{
_pollfds.resize(_map.size()* 2);
@ -596,6 +469,22 @@ 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()
{
for (std::map<std::string, weak_ptr<iface> >::iterator it = _map.begin();
@ -625,7 +514,6 @@ int iface::poll_all()
int len;
if ((len = ::poll(&_pollfds[0], _pollfds.size(), 50)) < 0) {
logger::error() << "Failed to poll interfaces: " << logger::err();
return -1;
}
@ -647,107 +535,43 @@ int iface::poll_all()
bool is_pfd = i++ % 2;
ptr<iface> ifa = i_it->second;
if (f_it->revents & POLLERR) {
logger::error() << "Error polling interface " << ifa->_name.c_str();
return -1;
}
if (!(f_it->revents & POLLIN)) {
continue;
}
ptr<iface> ifa = i_it->second;
address saddr, daddr, taddr;
ssize_t size;
if (is_pfd) {
size = ifa->read_solicit(saddr, daddr, taddr);
if (size < 0) {
if (ifa->read_solicit(saddr, daddr, taddr) < 0) {
logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str();
continue;
}
if (size == 0) {
logger::debug() << "iface::read_solicit() loopback received and ignored";
continue;
}
// Process any local addresses for interfaces that we are proxying
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 (!saddr.is_unicast() || !daddr.is_multicast()) {
continue;
}
// If it was not handled then write an error message
if (handled == false) {
logger::debug() << " - solicit was ignored";
if (ifa->_pr) {
ifa->_pr->handle_solicit(saddr, daddr, taddr);
}
} else {
size = ifa->read_advert(saddr, taddr);
if (size < 0) {
if (ifa->read_advert(saddr, taddr) < 0) {
logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str();
continue;
}
if (size == 0) {
logger::debug() << "iface::read_advert() loopback received and ignored";
continue;
}
// Process the NDP advert
bool handled = false;
for (std::list<weak_ptr<proxy> >::iterator pit = ifa->parents_begin(); pit != ifa->parents_end(); pit++) {
ptr<proxy> pr = (*pit);
if (!pr || !pr->ifa()) {
continue;
for (std::list<weak_ptr<session> >::iterator s_it = ifa->_sessions.begin();
s_it != ifa->_sessions.end(); s_it++) {
assert(!s_it->is_null());
const ptr<session> sess = *s_it;
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";
}
}
}
@ -759,9 +583,7 @@ int iface::allmulti(int state)
{
struct ifreq ifr;
logger::debug()
<< "iface::allmulti() state="
<< state << ", _name=\"" << _name << "\"";
logger::debug() << "iface::allmulti() state=" << state << ", _name=\"" << _name << "\"";
state = !!state;
@ -770,11 +592,10 @@ int iface::allmulti(int state)
strncpy(ifr.ifr_name, _name.c_str(), IFNAMSIZ);
if (ioctl(_pfd, SIOCGIFFLAGS, &ifr) < 0) {
logger::error() << "Failed to get allmulti: " << logger::err();
return -1;
}
int old_state = !!(ifr.ifr_flags &IFF_ALLMULTI);
int old_state = !!(ifr.ifr_flags & IFF_ALLMULTI);
if (state == old_state) {
return old_state;
@ -787,46 +608,6 @@ int iface::allmulti(int state)
}
if (ioctl(_pfd, SIOCSIFFLAGS, &ifr) < 0) {
logger::error() << "Failed to set allmulti: " << logger::err();
return -1;
}
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;
}
@ -838,34 +619,14 @@ const std::string& iface::name() const
return _name;
}
void iface::add_serves(const ptr<proxy>& pr)
void iface::pr(const ptr<proxy>& pr)
{
_serves.push_back(pr);
_pr = pr;
}
std::list<weak_ptr<proxy> >::iterator iface::serves_begin()
const ptr<proxy>& iface::pr() const
{
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();
return _pr;
}
NDPPD_NS_END

View File

@ -38,13 +38,13 @@ public:
static ptr<iface> open_ifd(const std::string& name);
static ptr<iface> open_pfd(const std::string& name, bool promiscuous);
static ptr<iface> open_pfd(const std::string& name);
static int poll_all();
ssize_t read(int fd, struct sockaddr* saddr, ssize_t saddr_size, uint8_t* msg, size_t size);
static ssize_t read(int fd, struct sockaddr* saddr, uint8_t* msg, size_t size);
ssize_t write(int fd, const address& daddr, const uint8_t* msg, size_t size);
static 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.
ssize_t write_solicit(const address& taddr);
@ -57,31 +57,21 @@ public:
// Reads a NB_NEIGHBOR_ADVERT message from the _ifd socket;
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.
const std::string& name() const;
std::list<weak_ptr<proxy> >::iterator serves_begin();
std::list<weak_ptr<proxy> >::iterator serves_end();
void add_serves(const ptr<proxy>& proxy);
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;
// Adds a session to be monitored for ND_NEIGHBOR_ADVERT messages.
void add_session(const ptr<session>& se);
void remove_session(const ptr<session>& se);
void pr(const ptr<proxy>& pr);
const ptr<proxy>& pr() const;
private:
static std::map<std::string, weak_ptr<iface> > _map;
static bool _map_dirty;
@ -106,16 +96,15 @@ private:
// Previous state of ALLMULTI for the interface.
int _prev_allmulti;
// Previous state of PROMISC for the interface
int _prev_promiscuous;
// Name of this interface.
std::string _name;
std::list<weak_ptr<proxy> > _serves;
std::list<weak_ptr<proxy> > _parents;
// An array of sessions that are monitoring this interface for
// ND_NEIGHBOR_ADVERT messages.
std::list<weak_ptr<session> > _sessions;
weak_ptr<proxy> _pr;
// The link-layer address of this interface.
struct ether_addr hwaddr;
@ -123,10 +112,6 @@ private:
// Turns on/off ALLMULTI for this interface - returns the previous state
// or -1 if there was an error.
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.
iface();

View File

@ -18,7 +18,6 @@
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <iostream>
#include <sstream>
@ -80,28 +79,6 @@ std::string logger::format(const std::string& fmt, ...)
return buf;
}
// alpine has a broken definition for strerr_r
// see https://stackoverflow.com/questions/41953104/strerror-r-is-incorrectly-declared-on-alpine-linux
std::string logger::err_str(int result, char *buff, int err)
{
if (result)
sprintf(buff, "unknown error: %d", err);
return buff;
}
std::string logger::err_str(char *result, char *buff, int err)
{
return result;
}
std::string logger::err()
{
char buf[2048];
return logger::err_str(strerror_r(errno, buf, sizeof(buf)), buf, errno);
}
logger logger::error()
{
return logger(LOG_ERR);

View File

@ -71,10 +71,7 @@ public:
static logger debug();
static logger notice();
static std::string err();
private:
int _pri;
std::stringstream _ss;
@ -92,9 +89,7 @@ private:
static int _max_pri;
// fixes for strerr_r on alpine
static std::string err_str(int result, char *buff, int err);
static std::string err_str(char *result, char *buff, int err);
};
NDPPD_NS_END

View File

@ -1,275 +0,0 @@
//
//@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

View File

@ -1,28 +0,0 @@
//
// @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

View File

@ -24,7 +24,6 @@
#include <getopt.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@ -33,29 +32,20 @@
using namespace ndppd;
static int daemonize()
int daemonize()
{
pid_t pid = fork();
if (pid < 0) {
logger::error() << "Failed to fork during daemonize: " << logger::err();
if (pid < 0)
return -1;
}
if (pid > 0)
exit(0);
umask(0);
pid_t sid = setsid();
if (sid < 0) {
logger::error() << "Failed to setsid during daemonize: " << logger::err();
return -1;
}
if (chdir("/") < 0) {
logger::error() << "Failed to change path during daemonize: " << logger::err();
if (sid < 0)
return -1;
}
close(STDIN_FILENO);
close(STDOUT_FILENO);
@ -64,12 +54,17 @@ static int daemonize()
return 0;
}
static ptr<conf> load_config(const std::string& path)
bool configure(const std::string& path)
{
ptr<conf> cf, x_cf;
if (!(cf = conf::load(path)))
return (conf*)NULL;
return false;
if (!(x_cf = cf->find("route-ttl")))
route::ttl(30000);
else
route::ttl(*x_cf);
std::vector<ptr<conf> >::const_iterator p_it;
@ -80,94 +75,13 @@ static ptr<conf> load_config(const std::string& path)
if (pr_cf->empty()) {
logger::error() << "'proxy' section is missing interface name";
return (conf*)NULL;
}
std::vector<ptr<conf> >::const_iterator r_it;
std::vector<ptr<conf> > rules(pr_cf->find_all("rule"));
for (r_it = rules.begin(); r_it != rules.end(); r_it++) {
ptr<conf> ru_cf =* r_it;
if (ru_cf->empty()) {
logger::error() << "'rule' is missing an IPv6 address/net";
return (conf*)NULL;
}
address addr(*ru_cf);
if (x_cf = ru_cf->find("iface")) {
if (ru_cf->find("static") || ru_cf->find("auto")) {
logger::error()
<< "Only one of 'iface', 'auto' and 'static' may "
<< "be specified.";
return (conf*)NULL;
}
if ((const std::string&)*x_cf == "") {
logger::error() << "'iface' expected an interface name";
return (conf*)NULL;
}
} else if (ru_cf->find("static")) {
if (ru_cf->find("auto")) {
logger::error()
<< "Only one of 'iface', 'auto' and 'static' may "
<< "be specified.";
return (conf*)NULL;
}
if (addr.prefix() <= 120) {
logger::warning()
<< "Low prefix length (" << addr.prefix()
<< " <= 120) when using 'static' method";
}
} else if (!ru_cf->find("auto")) {
logger::error()
<< "You must specify either 'iface', 'auto' or "
<< "'static'";
return (conf*)NULL;
}
}
}
return cf;
}
static bool configure(ptr<conf>& cf)
{
ptr<conf> x_cf;
if (!(x_cf = cf->find("route-ttl")))
route::ttl(30000);
else
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> > proxies(cf->find_all("proxy"));
for (p_it = proxies.begin(); p_it != proxies.end(); p_it++) {
ptr<conf> pr_cf = *p_it;
if (pr_cf->empty()) {
return false;
}
bool promiscuous = false;
if (!(x_cf = pr_cf->find("promiscuous")))
promiscuous = false;
else
promiscuous = *x_cf;
ptr<proxy> pr = proxy::open(*pr_cf, promiscuous);
if (!pr || pr.is_null() == true) {
ptr<proxy> pr = proxy::open(*pr_cf);
if (!pr) {
logger::error() << "Configuration failed for proxy '" << (const std::string& )*pr_cf << "'";
return false;
}
@ -175,31 +89,11 @@ static bool configure(ptr<conf>& cf)
pr->router(true);
else
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")))
pr->ttl(30000);
else
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")))
pr->timeout(500);
@ -213,78 +107,44 @@ static bool configure(ptr<conf>& cf)
for (r_it = rules.begin(); r_it != rules.end(); r_it++) {
ptr<conf> ru_cf =* r_it;
address addr(*ru_cf);
bool autovia = false;
if (!(x_cf = ru_cf->find("autovia")))
autovia = false;
else
autovia = *x_cf;
if (ru_cf->empty()) {
logger::error() << "'rule' is missing an IPv6 address/net";
return false;
}
if (x_cf = ru_cf->find("iface"))
{
ptr<iface> ifa = iface::open_ifd(*x_cf);
if (!ifa || ifa.is_null() == true) {
return false;
address addr(*ru_cf);
if (x_cf = ru_cf->find("iface")) {
if ((const std::string& )*x_cf == "") {
logger::error() << "'iface' expected an interface name";
} else {
pr->add_rule(addr, iface::open_ifd(*x_cf));
}
ifa->add_parent(pr);
myrules.push_back(pr->add_rule(addr, ifa, autovia));
} else if (ru_cf->find("auto")) {
myrules.push_back(pr->add_rule(addr, true));
pr->add_rule(addr, true);
} else {
myrules.push_back(pr->add_rule(addr, false));
if (!ru_cf->find("static")) {
logger::warning()
<< "## I'm going for 'static' since you didn't specify any method. Please fix this" << logger::endl
<< "## as it's not going to be supported in future versions of ndppd. (See 'man ndppd.conf')";
}
if (addr.prefix() <= 120) {
logger::warning()
<< "Low prefix length (" << addr.prefix() << " <= 120) when using 'static' method";
}
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;
}
static bool running = true;
bool running = true;
static void exit_ndppd(int sig)
void exit_ndppd(int sig)
{
logger::error() << "Shutting down...";
running = 0;
@ -341,26 +201,15 @@ int main(int argc, char* argv[], char* env[])
}
}
logger::notice()
<< "ndppd (NDP Proxy Daemon) version " NDPPD_VERSION << logger::endl
<< "Using configuration file '" << config_path << "'";
// Load configuration.
ptr<conf> cf = load_config(config_path);
if (cf.is_null())
return -1;
if (daemon) {
logger::syslog(true);
if (daemonize() < 0)
if (daemonize() < 0) {
logger::error() << "Failed to daemonize process";
return 1;
}
}
if (!configure(cf))
return -1;
if (!pidfile.empty()) {
std::ofstream pf;
pf.open(pidfile.c_str(), std::ios::out | std::ios::trunc);
@ -368,16 +217,23 @@ int main(int argc, char* argv[], char* env[])
pf.close();
}
logger::notice()
<< "ndppd (NDP Proxy Daemon) version " NDPPD_VERSION << logger::endl
<< "Using configuration file '" << config_path << "'";
// Load configuration.
if (!configure(config_path))
return -1;
//route::load("/proc/net/ipv6_route");
// Time stuff.
struct timeval t1, t2;
gettimeofday(&t1, 0);
#ifdef WITH_ND_NETLINK
netlink_setup();
#endif
while (running) {
if (iface::poll_all() < 0) {
if (running) {
@ -396,19 +252,10 @@ int main(int argc, char* argv[], char* env[])
t1.tv_sec = t2.tv_sec;
t1.tv_usec = t2.tv_usec;
if (rule::any_auto())
route::update(elapsed_time);
if (rule::any_iface())
address::update(elapsed_time);
route::update(elapsed_time);
session::update_all(elapsed_time);
}
#ifdef WITH_ND_NETLINK
netlink_teardown();
#endif
logger::notice() << "Bye";
return 0;

View File

@ -21,7 +21,7 @@
#define NDPPD_NS_BEGIN namespace ndppd {
#define NDPPD_NS_END }
#define NDPPD_VERSION "0.2.4"
#define NDPPD_VERSION "0.2.3"
#include <assert.h>
@ -35,4 +35,3 @@
#include "proxy.h"
#include "session.h"
#include "rule.h"
#include "nd-netlink.h"

View File

@ -26,88 +26,72 @@
#include "session.h"
NDPPD_NS_BEGIN
static address all_nodes = address("ff02::1");
std::list<ptr<proxy> > proxy::_list;
proxy::proxy() :
_router(true), _ttl(30000), _deadtime(3000), _timeout(500), _autowire(false), _keepalive(true), _promiscuous(false), _retries(3)
_router(true), _ttl(30000), _timeout(500)
{
}
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> proxy::create(const ptr<iface>& ifa)
{
ptr<proxy> pr(new proxy());
pr->_ptr = pr;
pr->_ifa = ifa;
pr->_promiscuous = promiscuous;
_list.push_back(pr);
ifa->add_serves(pr);
ifa->pr(pr);
logger::debug() << "proxy::create() if=" << ifa->name();
return pr;
}
ptr<proxy> proxy::open(const std::string& ifname, bool promiscuous)
ptr<proxy> proxy::open(const std::string& ifname)
{
ptr<iface> ifa = iface::open_pfd(ifname, promiscuous);
ptr<iface> ifa = iface::open_pfd(ifname);
if (!ifa) {
return ptr<proxy>();
}
return create(ifa, promiscuous);
return create(ifa);
}
ptr<session> proxy::find_or_create_session(const address& taddr)
void proxy::handle_solicit(const address& saddr, const address& daddr,
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
// find one with the same target address.
for (std::list<ptr<session> >::iterator sit = _sessions.begin();
sit != _sessions.end(); sit++) {
if ((*sit)->taddr() == taddr)
return (*sit);
if ((*sit)->taddr() == taddr) {
switch ((*sit)->status()) {
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
// a matching rule instead, and then set up a new session.
ptr<session> se;
for (std::list<ptr<rule> >::iterator it = _rules.begin();
it != _rules.end(); it++) {
ptr<rule> ru = *it;
@ -116,9 +100,9 @@ ptr<session> proxy::find_or_create_session(const address& taddr)
if (ru->addr() == taddr) {
if (!se) {
se = session::create(_ptr, taddr, _autowire, _keepalive, _retries);
se = session::create(_ptr, saddr, daddr, taddr);
}
if (ru->is_auto()) {
ptr<route> rt = route::find(taddr);
@ -127,100 +111,30 @@ ptr<session> proxy::find_or_create_session(const address& taddr)
} else {
ptr<iface> ifa = rt->ifa();
if (ifa && (ifa != ru->daughter())) {
if (ifa && (ifa != ru->ifa())) {
se->add_iface(ifa);
}
}
} else if (!ru->daughter()) {
} else if (!ru->ifa()) {
// This rule doesn't have an interface, and thus we'll consider
// it "static" and immediately send the response.
se->handle_advert();
return se;
return;
} else {
ptr<iface> ifa = ru->daughter();
se->add_iface(ifa);
#ifdef WITH_ND_NETLINK
if (if_addr_find(ifa->name(), &taddr.const_addr())) {
logger::debug() << "Sending NA out " << ifa->name();
se->add_iface(_ifa);
se->handle_advert();
}
#endif
se->add_iface((*it)->ifa());
}
}
}
if (se) {
_sessions.push_back(se);
}
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);
}
se->send_solicit();
}
}
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> proxy::add_rule(const address& addr, const ptr<iface>& ifa)
{
ptr<rule> ru(rule::create(_ptr, addr, ifa));
ru->autovia(autovia);
_rules.push_back(ru);
return ru;
}
@ -232,16 +146,6 @@ ptr<rule> proxy::add_rule(const address& addr, bool aut)
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)
{
_sessions.remove(se);
@ -252,11 +156,6 @@ const ptr<iface>& proxy::ifa() const
return _ifa;
}
bool proxy::promiscuous() const
{
return _promiscuous;
}
bool proxy::router() const
{
return _router;
@ -267,36 +166,6 @@ void proxy::router(bool 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
{
return _ttl;
@ -307,16 +176,6 @@ void proxy::ttl(int val)
_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
{
return _timeout;

View File

@ -29,50 +29,25 @@ class iface;
class rule;
class proxy {
public:
static ptr<proxy> create(const ptr<iface>& ifa, bool promiscuous);
static ptr<proxy> find_aunt(const std::string& ifname, const address& taddr);
public:
static ptr<proxy> create(const ptr<iface>& ifa);
static ptr<proxy> open(const std::string& ifn, bool promiscuous);
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);
static ptr<proxy> open(const std::string& ifn);
void handle_solicit(const address& saddr, const address& daddr,
const address& taddr);
void remove_session(const ptr<session>& se);
ptr<rule> add_rule(const address& addr, const ptr<iface>& ifa, bool autovia);
ptr<rule> add_rule(const address& addr, const ptr<iface>& ifa);
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;
bool promiscuous() const;
bool router() const;
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;
@ -81,10 +56,6 @@ public:
int ttl() const;
void ttl(int val);
int deadtime() const;
void deadtime(int val);
private:
static std::list<ptr<proxy> > _list;
@ -96,18 +67,10 @@ private:
std::list<ptr<rule> > _rules;
std::list<ptr<session> > _sessions;
bool _promiscuous;
bool _router;
bool _autowire;
int _retries;
bool _keepalive;
int _ttl, _deadtime, _timeout;
int _ttl, _timeout;
proxy();
};

View File

@ -70,8 +70,6 @@ protected:
void acquire(void* ptr)
{
if (!ptr) return;
_ref = new ptr_ref();
_ref->ptr = (T*)ptr;
_ref->wc = !!_weak;

View File

@ -81,8 +81,6 @@ std::string route::token(const char* str)
void route::load(const std::string& path)
{
// Hack to make sure the interfaces are not freed prematurely.
std::list<ptr<route> > tmp_routes(_routes);
_routes.clear();
logger::debug() << "reading routes";
@ -105,19 +103,19 @@ void route::load(const std::string& path)
unsigned char pfx;
if (route::hexdec(buf, (unsigned char* )&addr.addr(), 16) != 16) {
if (hexdec(buf, (unsigned char* )&addr.addr(), 16) != 16) {
// TODO: Warn here?
continue;
}
if (route::hexdec(buf + 33,& pfx, 1) != 1) {
if (hexdec(buf + 33,& pfx, 1) != 1) {
// TODO: Warn here?
continue;
}
addr.prefix((int)pfx);
route::create(addr, route::token(buf + 141));
route::create(addr, token(buf + 141));
}
} catch (std::ifstream::failure e) {
logger::warning() << "Failed to parse IPv6 routing data from '" << path << "'";

View File

@ -44,12 +44,6 @@ public:
const address& addr() const;
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:
static int _ttl;
@ -62,8 +56,14 @@ private:
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;
route(const address& addr, const std::string& ifname);
};
NDPPD_NS_END

View File

@ -16,7 +16,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>
#include "ndppd.h"
#include "rule.h"
@ -25,14 +24,6 @@
NDPPD_NS_BEGIN
std::vector<interface> interfaces;
bool rule::_any_aut = false;
bool rule::_any_iface = false;
bool rule::_any_static = false;
rule::rule()
{
}
@ -42,22 +33,11 @@ ptr<rule> rule::create(const ptr<proxy>& pr, const address& addr, const ptr<ifac
ptr<rule> ru(new rule());
ru->_ptr = ru;
ru->_pr = pr;
ru->_daughter = ifa;
ru->_ifa = ifa;
ru->_addr = addr;
ru->_aut = false;
_any_iface = true;
unsigned int ifindex;
ifindex = if_nametoindex(pr->ifa()->name().c_str());
#ifdef WITH_ND_NETLINK
if_add_to_list(ifindex, pr->ifa());
#endif
ifindex = if_nametoindex(ifa->name().c_str());
#ifdef WITH_ND_NETLINK
if_add_to_list(ifindex, ifa);
#endif
logger::debug() << "rule::create() if=" << pr->ifa()->name() << ", slave=" << ifa->name() << ", addr=" << addr;
logger::debug() << "rule::create() if=" << pr->ifa()->name() << ", addr=" << addr;
return ru;
}
@ -69,10 +49,6 @@ ptr<rule> rule::create(const ptr<proxy>& pr, const address& addr, bool aut)
ru->_pr = pr;
ru->_addr = addr;
ru->_aut = aut;
_any_aut = _any_aut || aut;
if (aut == false)
_any_static = true;
logger::debug()
<< "rule::create() if=" << pr->ifa()->name().c_str() << ", addr=" << addr
@ -86,9 +62,9 @@ const address& rule::addr() const
return _addr;
}
ptr<iface> rule::daughter() const
ptr<iface> rule::ifa() const
{
return _daughter;
return _ifa;
}
bool rule::is_auto() const
@ -96,31 +72,6 @@ bool rule::is_auto() const
return _aut;
}
bool rule::autovia() const
{
return _autovia;
}
void rule::autovia(bool val)
{
_autovia = val;
}
bool rule::any_auto()
{
return _any_aut;
}
bool rule::any_iface()
{
return _any_iface;
}
bool rule::any_static()
{
return _any_static;
}
bool rule::check(const address& addr) const
{
return _addr == addr;

View File

@ -18,7 +18,6 @@
#include <string>
#include <vector>
#include <map>
#include <list>
#include <sys/poll.h>
@ -37,57 +36,24 @@ public:
const address& addr() const;
ptr<iface> daughter() const;
ptr<iface> ifa() const;
bool is_auto() const;
bool check(const address& addr) const;
static bool any_auto();
static bool any_static();
static bool any_iface();
bool autovia() const;
void autovia(bool val);
private:
weak_ptr<rule> _ptr;
weak_ptr<proxy> _pr;
ptr<iface> _daughter;
ptr<iface> _ifa;
address _addr;
bool _aut;
static bool _any_aut;
static bool _any_static;
static bool _any_iface;
bool _autovia;
rule();
};
class interface {
public:
// List of IPv6 addresses on this interface
std::list<address> addresses;
// Index of this interface
int ifindex;
// Name of this interface.
std::string _name;
};
extern std::vector<interface> interfaces;
NDPPD_NS_END

View File

@ -14,7 +14,6 @@
// 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 <algorithm>
#include <sstream>
#include "ndppd.h"
#include "proxy.h"
@ -25,8 +24,6 @@ NDPPD_NS_BEGIN
std::list<weak_ptr<session> > session::_sessions;
static address all_nodes = address("ff02::1");
void session::update_all(int elapsed_time)
{
for (std::list<weak_ptr<session> >::iterator it = _sessions.begin();
@ -43,54 +40,10 @@ void session::update_all(int elapsed_time)
}
switch (se->_status) {
case session::WAITING:
if (se->_fails < se->_retries) {
logger::debug() << "session will keep trying [taddr=" << se->_taddr << "]";
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);
}
logger::debug() << "session is now invalid";
se->_status = session::INVALID;
se->_ttl = se->_pr->ttl();
break;
default:
@ -102,34 +55,30 @@ void session::update_all(int elapsed_time)
session::~session()
{
logger::debug() << "session::~session() this=" << logger::format("%x", this);
if (_wired == true) {
for (std::list<ptr<iface> >::iterator it = _ifaces.begin();
for (std::list<ptr<iface> >::iterator it = _ifaces.begin();
it != _ifaces.end(); it++) {
handle_auto_unwire((*it)->name());
}
(*it)->remove_session(_ptr);
}
}
ptr<session> session::create(const ptr<proxy>& pr, const address& taddr, bool auto_wire, bool keepalive, int retries)
ptr<session> session::create(const ptr<proxy>& pr, const address& saddr,
const address& daddr, const address& taddr)
{
ptr<session> se(new session());
se->_ptr = se;
se->_pr = pr;
se->_taddr = taddr;
se->_autowire = auto_wire;
se->_keepalive = keepalive;
se->_retries = retries;
se->_wired = false;
se->_ttl = pr->ttl();
se->_touched = false;
se->_ptr = se;
se->_pr = pr;
se->_saddr = saddr;
se->_taddr = taddr;
se->_daddr = daddr;
se->_ttl = pr->timeout();
_sessions.push_back(se);
logger::debug()
<< "session::create() pr=" << logger::format("%x", (proxy* )pr) << ", proxy=" << ((pr->ifa()) ? pr->ifa()->name() : "null")
<< ", taddr=" << taddr << " =" << logger::format("%x", (session* )se);
<< "session::create() pr=" << logger::format("%x", (proxy* )pr) << ", saddr=" << saddr
<< ", daddr=" << daddr << ", taddr=" << taddr << " =" << logger::format("%x", (session* )se);
return se;
}
@ -139,19 +88,10 @@ void session::add_iface(const ptr<iface>& ifa)
if (std::find(_ifaces.begin(), _ifaces.end(), ifa) != _ifaces.end())
return;
ifa->add_session(_ptr);
_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()
{
logger::debug() << "session::send_solicit() (_ifaces.size() = " << _ifaces.size() << ")";
@ -163,162 +103,17 @@ void session::send_solicit()
}
}
void session::touch()
void session::send_advert()
{
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();
}
}
_pr->ifa()->write_advert(_saddr, _taddr, _pr->router());
}
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()
{
logger::debug()
<< "session::handle_advert() taddr=" << _taddr << ", ttl=" << _pr->ttl();
if (_status != VALID) {
_status = VALID;
logger::debug() << "session is active [taddr=" << _taddr << "]";
}
_status = VALID;
_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();
}
send_advert();
}
const address& session::taddr() const
@ -326,34 +121,14 @@ const address& session::taddr() const
return _taddr;
}
bool session::autowire() const
const address& session::saddr() const
{
return _autowire;
return _saddr;
}
bool session::keepalive() const
const address& session::daddr() const
{
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;
return _daddr;
}
int session::status() const

View File

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