Compare commits
65 Commits
debian
...
burble.dn4
Author | SHA1 | Date | |
---|---|---|---|
6cfacd05d8 | |||
|
e01d67a864 | ||
|
2eb55c8e2b | ||
|
b7571fdaa7 | ||
|
97754c0a53 | ||
|
b67f560309 | ||
|
caef398f85 | ||
|
13473e5259 | ||
|
eb81b8f2d6 | ||
|
8ed532809f | ||
|
6ef7a57b2e | ||
|
a008fad5f4 | ||
|
4ca86ef88e | ||
|
e8e3739126 | ||
|
9341e7f0b7 | ||
|
1f50dc5b5a | ||
|
a690d128e7 | ||
|
89130bf099 | ||
|
f6ef3fe494 | ||
|
f2d5804959 | ||
|
0fd1dc0308 | ||
|
5c56cd3a6d | ||
|
4f11b9a4f0 | ||
|
890d1a7821 | ||
|
4316aa58dd | ||
|
b6b3487152 | ||
|
1a691255e3 | ||
|
2f10118a5c | ||
|
55ad4b11f0 | ||
|
701476417a | ||
|
2ceb687bdc | ||
|
f8ea51ab5e | ||
|
7e0948bf96 | ||
|
e11d1943e9 | ||
|
37181c81ca | ||
|
68240908bc | ||
|
aaaed4fce9 | ||
|
8ed35f841f | ||
|
ca91245d3f | ||
|
f37e8eb33d | ||
|
eec9657b28 | ||
|
b30b654871 | ||
|
8a8f7b065c | ||
|
3caf569a1f | ||
|
ce3815d954 | ||
|
ab7b2aa3bb | ||
|
a35def5b52 | ||
|
df16c053b0 | ||
|
cf244c10f7 | ||
|
8afd356df3 | ||
|
771f9ca29d | ||
|
9709759270 | ||
|
9499745013 | ||
|
cb23cdbeac | ||
|
96ab05dade | ||
|
e49b71c0b7 | ||
|
f19fa4be6a | ||
|
b8b2d401b9 | ||
|
4a837c511c | ||
|
e6f2e0a52e | ||
|
baec0bf028 | ||
|
87f989026d | ||
|
e2e44ec5ed | ||
|
6b08f63fff | ||
|
00da8bf7ba |
62
ChangeLog
62
ChangeLog
@ -1,3 +1,65 @@
|
|||||||
|
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>
|
2012-09-21 Daniel Adolfsson <daniel@priv.nu>
|
||||||
|
|
||||||
* Version 0.2.3
|
* Version 0.2.3
|
||||||
|
17
Makefile
17
Makefile
@ -9,12 +9,18 @@ CXX ?= g++
|
|||||||
GZIP ?= /bin/gzip
|
GZIP ?= /bin/gzip
|
||||||
MANDIR ?= ${DESTDIR}${PREFIX}/share/man
|
MANDIR ?= ${DESTDIR}${PREFIX}/share/man
|
||||||
SBINDIR ?= ${DESTDIR}${PREFIX}/sbin
|
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 \
|
OBJS = src/logger.o src/ndppd.o src/iface.o src/proxy.o src/address.o \
|
||||||
src/rule.o src/session.o src/conf.o src/route.o
|
src/rule.o src/session.o src/conf.o src/route.o
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
all: ndppd ndppd.1.gz ndppd.conf.5.gz
|
all: ndppd ndppd.1.gz ndppd.conf.5.gz
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
@ -31,10 +37,13 @@ ndppd.conf.5.gz:
|
|||||||
${GZIP} < ndppd.conf.5 > ndppd.conf.5.gz
|
${GZIP} < ndppd.conf.5 > ndppd.conf.5.gz
|
||||||
|
|
||||||
ndppd: ${OBJS}
|
ndppd: ${OBJS}
|
||||||
${CXX} -o ndppd ${LDFLAGS} ${LIBS} ${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`
|
||||||
|
|
||||||
.cc.o:
|
.cc.o:
|
||||||
${CXX} -c $(CXXFLAGS) -o $@ $<
|
${CXX} -c ${CPPFLAGS} $(CXXFLAGS) -o $@ $<
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f ndppd ndppd.conf.5.gz ndppd.1.gz ${OBJS}
|
rm -f ndppd ndppd.conf.5.gz ndppd.1.gz ${OBJS} nd-proxy
|
||||||
|
13
README
13
README
@ -1,13 +1,13 @@
|
|||||||
ndppd - NDP Proxy Daemon
|
ndppd - NDP Proxy Daemon
|
||||||
|
|
||||||
Version 0.2.2
|
Version 0.2.5
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
1. Legal
|
1. Legal
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
ndppd - NDP Proxy Daemon
|
ndppd - NDP Proxy Daemon
|
||||||
Copyright (C) 2011 Daniel Adolfsson <daniel@priv.nu>
|
Copyright (C) 2011-2016 Daniel Adolfsson <daniel@priv.nu>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -101,8 +101,8 @@
|
|||||||
5. Usage
|
5. Usage
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
Read through 'ndppd.conf.example' for guidelines how to configure
|
Read through 'ndppd.conf-dist' for guidelines and examples how to
|
||||||
the daemon.
|
configure the daemon.
|
||||||
|
|
||||||
Usage: ndppd [-d] [-c <config>] [-p <pidfile>]
|
Usage: ndppd [-d] [-c <config>] [-p <pidfile>]
|
||||||
|
|
||||||
@ -125,10 +125,9 @@
|
|||||||
|
|
||||||
Contact : Daniel Adolfsson <daniel@priv.nu>
|
Contact : Daniel Adolfsson <daniel@priv.nu>
|
||||||
|
|
||||||
Website : http://www.priv.nu/projects/ndppd
|
Website : https://github.com/DanielAdolfsson/ndppd
|
||||||
|
|
||||||
Git : git://github.com/Tuhox/ndppd.git
|
Git : git://github.com/DanielAdolfsson/ndppd.git
|
||||||
https://github.com/Tuhox/ndppd
|
|
||||||
|
|
||||||
If you want to report a bug, you can either send me a mail directly,
|
If you want to report a bug, you can either send me a mail directly,
|
||||||
or submit an issue on github.com.
|
or submit an issue on github.com.
|
||||||
|
12
README.md
Normal file
12
README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# 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)
|
550
nd-proxy.c
Normal file
550
nd-proxy.c
Normal file
@ -0,0 +1,550 @@
|
|||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
21
ndppd-init-debian-jessi
Executable file
21
ndppd-init-debian-jessi
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
#!/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"
|
@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
route-ttl 30000
|
route-ttl 30000
|
||||||
|
|
||||||
|
# address-ttl <integer> (NEW)
|
||||||
|
# This tells 'ndppd' how often to reload the IP address file /proc/net/if_inet6
|
||||||
|
# Default value is '30000' (30 seconds).
|
||||||
|
|
||||||
|
address-ttl 30000
|
||||||
|
|
||||||
# proxy <interface>
|
# proxy <interface>
|
||||||
# This sets up a listener, that will listen for any Neighbor Solicitation
|
# This sets up a listener, that will listen for any Neighbor Solicitation
|
||||||
# messages, and respond to them according to a set of rules (see below).
|
# messages, and respond to them according to a set of rules (see below).
|
||||||
@ -23,6 +29,36 @@ proxy eth0 {
|
|||||||
|
|
||||||
timeout 500
|
timeout 500
|
||||||
|
|
||||||
|
# autowire <yes|no|true|false>
|
||||||
|
# Controls whether ndppd will automatically create host entries
|
||||||
|
# in the routing tables when it receives Neighbor Advertisements on a
|
||||||
|
# listening interface. The the default value is no.
|
||||||
|
# Note: Autowire will ignore all rules with 'auto' or 'static' given it
|
||||||
|
# is expected that the routes are already defined for these paths
|
||||||
|
|
||||||
|
autowire no
|
||||||
|
|
||||||
|
# keepalive <yes|no|true|false>
|
||||||
|
# Controls whether ndppd will automatically attempt to keep routing
|
||||||
|
# sessions alive by actively sending out NDP Solicitations before the the
|
||||||
|
# session is expired. The the default value is yes.
|
||||||
|
|
||||||
|
keepalive yes
|
||||||
|
|
||||||
|
# retries <integer>
|
||||||
|
# Number of times a NDP Solicitation will be sent out before the daemon
|
||||||
|
# considers a route unreachable. The default value is 3
|
||||||
|
|
||||||
|
retries 3
|
||||||
|
|
||||||
|
# promiscuous <yes|no|true|false>
|
||||||
|
# Controls whether ndppd will put the proxy listening interface into promiscuous
|
||||||
|
# mode and hence will react to inbound and outbound NDP commands. This is
|
||||||
|
# required for machines behind the gateway to talk to each other in more
|
||||||
|
# complex topology scenarios. The the default value is no.
|
||||||
|
|
||||||
|
promiscuous no
|
||||||
|
|
||||||
# ttl <integer>
|
# ttl <integer>
|
||||||
# Controls how long a valid or invalid entry remains in the cache, in
|
# Controls how long a valid or invalid entry remains in the cache, in
|
||||||
# milliseconds. Default value is '30000' (30 seconds).
|
# milliseconds. Default value is '30000' (30 seconds).
|
||||||
@ -55,6 +91,13 @@ proxy eth0 {
|
|||||||
|
|
||||||
auto
|
auto
|
||||||
|
|
||||||
|
# autovia <yes|no|true|false>
|
||||||
|
# Any addresses updated using NDP advertisments will use a gateway to
|
||||||
|
# route traffic on this particular interface (only works wiith the iface
|
||||||
|
# rule type). Default is no
|
||||||
|
|
||||||
|
autovia no
|
||||||
|
|
||||||
# Note that before version 0.2.2 of 'ndppd', if you didn't choose a
|
# Note that before version 0.2.2 of 'ndppd', if you didn't choose a
|
||||||
# method, it defaulted to 'static'. For compatibility reasons we choose
|
# method, it defaulted to 'static'. For compatibility reasons we choose
|
||||||
# to keep this behavior - for now (it may be removed in a future version).
|
# to keep this behavior - for now (it may be removed in a future version).
|
||||||
|
14
ndppd.conf.5
14
ndppd.conf.5
@ -48,6 +48,20 @@ Controls how long
|
|||||||
.B ndppd
|
.B ndppd
|
||||||
will cache an entry. This is in milliseconds, and the default value
|
will cache an entry. This is in milliseconds, and the default value
|
||||||
is 30000 (30 seconds).
|
is 30000 (30 seconds).
|
||||||
|
.IP "autowire <yes|no>"
|
||||||
|
Controls whether
|
||||||
|
.B ndppd
|
||||||
|
will automatically create host entries in the routing tables when
|
||||||
|
.B ndppd receives Neighbor Advertisements on a listening interface.
|
||||||
|
The default value is no.
|
||||||
|
.IP "promiscuous <yes|no>"
|
||||||
|
Controls whether
|
||||||
|
.B ndppd
|
||||||
|
will put the proxy listening interface into promiscuous mode and
|
||||||
|
hence will react to inbound and outbound NDP commands. This is
|
||||||
|
required for machines behind the gateway to talk to each other in
|
||||||
|
more complex topology scenarios.
|
||||||
|
The the default value is no.
|
||||||
.IP "timeout <value>"
|
.IP "timeout <value>"
|
||||||
Controls how long
|
Controls how long
|
||||||
.B ndppd
|
.B ndppd
|
||||||
|
11
ndppd.service
Normal file
11
ndppd.service
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[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
|
123
src/address.cc
123
src/address.cc
@ -15,6 +15,8 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <fstream>
|
||||||
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -27,9 +29,16 @@
|
|||||||
|
|
||||||
#include "ndppd.h"
|
#include "ndppd.h"
|
||||||
#include "address.h"
|
#include "address.h"
|
||||||
|
#include "route.h"
|
||||||
|
|
||||||
NDPPD_NS_BEGIN
|
NDPPD_NS_BEGIN
|
||||||
|
|
||||||
|
std::list<ptr<route> > address::_addresses;
|
||||||
|
|
||||||
|
int address::_ttl;
|
||||||
|
|
||||||
|
int address::_c_ttl;
|
||||||
|
|
||||||
address::address()
|
address::address()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
@ -48,6 +57,19 @@ address::address(const address& addr)
|
|||||||
_mask.s6_addr32[3] = addr._mask.s6_addr32[3];
|
_mask.s6_addr32[3] = addr._mask.s6_addr32[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
address::address(const ptr<address>& addr)
|
||||||
|
{
|
||||||
|
_addr.s6_addr32[0] = addr->_addr.s6_addr32[0];
|
||||||
|
_addr.s6_addr32[1] = addr->_addr.s6_addr32[1];
|
||||||
|
_addr.s6_addr32[2] = addr->_addr.s6_addr32[2];
|
||||||
|
_addr.s6_addr32[3] = addr->_addr.s6_addr32[3];
|
||||||
|
|
||||||
|
_mask.s6_addr32[0] = addr->_mask.s6_addr32[0];
|
||||||
|
_mask.s6_addr32[1] = addr->_mask.s6_addr32[1];
|
||||||
|
_mask.s6_addr32[2] = addr->_mask.s6_addr32[2];
|
||||||
|
_mask.s6_addr32[3] = addr->_mask.s6_addr32[3];
|
||||||
|
}
|
||||||
|
|
||||||
address::address(const std::string& str)
|
address::address(const std::string& str)
|
||||||
{
|
{
|
||||||
parse_string(str);
|
parse_string(str);
|
||||||
@ -110,6 +132,21 @@ bool address::operator!=(const address& addr) const
|
|||||||
((_addr.s6_addr32[3] ^ addr._addr.s6_addr32[3]) & _mask.s6_addr32[3]));
|
((_addr.s6_addr32[3] ^ addr._addr.s6_addr32[3]) & _mask.s6_addr32[3]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool address::is_empty() const
|
||||||
|
{
|
||||||
|
if (_addr.s6_addr32[0] == 0 &&
|
||||||
|
_addr.s6_addr32[1] == 0 &&
|
||||||
|
_addr.s6_addr32[2] == 0 &&
|
||||||
|
_addr.s6_addr32[3] == 0 &&
|
||||||
|
_mask.s6_addr32[0] == 0xffffffff &&
|
||||||
|
_mask.s6_addr32[1] == 0xffffffff &&
|
||||||
|
_mask.s6_addr32[2] == 0xffffffff &&
|
||||||
|
_mask.s6_addr32[3] == 0xffffffff)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void address::reset()
|
void address::reset()
|
||||||
{
|
{
|
||||||
_addr.s6_addr32[0] = 0;
|
_addr.s6_addr32[0] = 0;
|
||||||
@ -299,7 +336,93 @@ bool address::is_multicast() const
|
|||||||
|
|
||||||
bool address::is_unicast() const
|
bool address::is_unicast() const
|
||||||
{
|
{
|
||||||
|
if (_addr.s6_addr32[2] == 0 &&
|
||||||
|
_addr.s6_addr32[3] == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
return _addr.s6_addr[0] != 0xff;
|
return _addr.s6_addr[0] != 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void address::add(const address& addr, const std::string& ifname)
|
||||||
|
{
|
||||||
|
ptr<route> rt(new route(addr, ifname));
|
||||||
|
// logger::debug() << "address::create() addr=" << addr << ", ifname=" << ifname;
|
||||||
|
_addresses.push_back(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<ptr<route> >::iterator address::addresses_begin()
|
||||||
|
{
|
||||||
|
return _addresses.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<ptr<route> >::iterator address::addresses_end()
|
||||||
|
{
|
||||||
|
return _addresses.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void address::load(const std::string& path)
|
||||||
|
{
|
||||||
|
// Hack to make sure the addresses are not freed prematurely.
|
||||||
|
std::list<ptr<route> > tmp_addresses(_addresses);
|
||||||
|
_addresses.clear();
|
||||||
|
|
||||||
|
logger::debug() << "reading IP addresses";
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::ifstream ifs;
|
||||||
|
ifs.exceptions(std::ifstream::badbit | std::ifstream::failbit);
|
||||||
|
ifs.open(path.c_str(), std::ios::in);
|
||||||
|
ifs.exceptions(std::ifstream::badbit);
|
||||||
|
|
||||||
|
while (!ifs.eof()) {
|
||||||
|
char buf[1024];
|
||||||
|
ifs.getline(buf, sizeof(buf));
|
||||||
|
|
||||||
|
if (ifs.gcount() < 53) {
|
||||||
|
if (ifs.gcount() > 0)
|
||||||
|
logger::debug() << "skipping entry (size=" << ifs.gcount() << ")";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
address addr;
|
||||||
|
|
||||||
|
if (route::hexdec(buf, (unsigned char* )&addr.addr(), 16) != 16) {
|
||||||
|
logger::warning() << "failed to load address (" << buf << ")";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr.prefix(128);
|
||||||
|
|
||||||
|
std::string iface = route::token(buf + 45);
|
||||||
|
|
||||||
|
address::add(addr, iface);
|
||||||
|
|
||||||
|
logger::debug() << "found local addr=" << addr << ", iface=" << iface;
|
||||||
|
}
|
||||||
|
} catch (std::ifstream::failure e) {
|
||||||
|
logger::warning() << "Failed to parse IPv6 address data from '" << path << "'";
|
||||||
|
logger::error() << e.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger::debug() << "completed IP addresses load";
|
||||||
|
}
|
||||||
|
|
||||||
|
void address::update(int elapsed_time)
|
||||||
|
{
|
||||||
|
if ((_c_ttl -= elapsed_time) <= 0) {
|
||||||
|
load("/proc/net/if_inet6");
|
||||||
|
_c_ttl = _ttl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int address::ttl()
|
||||||
|
{
|
||||||
|
return _ttl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void address::ttl(int ttl)
|
||||||
|
{
|
||||||
|
_ttl = ttl;
|
||||||
|
}
|
||||||
|
|
||||||
NDPPD_NS_END
|
NDPPD_NS_END
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <list>
|
||||||
#include <netinet/ip6.h>
|
#include <netinet/ip6.h>
|
||||||
|
|
||||||
#include "ndppd.h"
|
#include "ndppd.h"
|
||||||
@ -24,16 +25,25 @@ NDPPD_NS_BEGIN
|
|||||||
|
|
||||||
class iface;
|
class iface;
|
||||||
|
|
||||||
|
class route;
|
||||||
|
|
||||||
class address {
|
class address {
|
||||||
public:
|
public:
|
||||||
address();
|
address();
|
||||||
address(const address& addr);
|
address(const address& addr);
|
||||||
|
address(const ptr<address>& addr);
|
||||||
address(const std::string& str);
|
address(const std::string& str);
|
||||||
address(const char* str);
|
address(const char* str);
|
||||||
address(const in6_addr& addr);
|
address(const in6_addr& addr);
|
||||||
address(const in6_addr& addr, const in6_addr& mask);
|
address(const in6_addr& addr, const in6_addr& mask);
|
||||||
address(const in6_addr& addr, int prefix);
|
address(const in6_addr& addr, int prefix);
|
||||||
|
|
||||||
|
static void update(int elapsed_time);
|
||||||
|
|
||||||
|
static int ttl();
|
||||||
|
|
||||||
|
static void ttl(int ttl);
|
||||||
|
|
||||||
struct in6_addr& addr();
|
struct in6_addr& addr();
|
||||||
|
|
||||||
const struct in6_addr& const_addr() const;
|
const struct in6_addr& const_addr() const;
|
||||||
@ -47,6 +57,8 @@ public:
|
|||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
bool is_empty() const;
|
||||||
|
|
||||||
const std::string to_string() const;
|
const std::string to_string() const;
|
||||||
|
|
||||||
bool parse_string(const std::string& str);
|
bool parse_string(const std::string& str);
|
||||||
@ -61,7 +73,21 @@ public:
|
|||||||
|
|
||||||
operator std::string() const;
|
operator std::string() const;
|
||||||
|
|
||||||
|
static void add(const address& addr, const std::string& ifname);
|
||||||
|
|
||||||
|
static void load(const std::string& path);
|
||||||
|
|
||||||
|
static std::list<ptr<route> >::iterator addresses_begin();
|
||||||
|
|
||||||
|
static std::list<ptr<route> >::iterator addresses_end();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static int _ttl;
|
||||||
|
|
||||||
|
static int _c_ttl;
|
||||||
|
|
||||||
|
static std::list<ptr<route> > _addresses;
|
||||||
|
|
||||||
struct in6_addr _addr, _mask;
|
struct in6_addr _addr, _mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
359
src/iface.cc
359
src/iface.cc
@ -34,11 +34,13 @@
|
|||||||
|
|
||||||
#include <linux/filter.h>
|
#include <linux/filter.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "ndppd.h"
|
#include "ndppd.h"
|
||||||
|
#include "route.h"
|
||||||
|
|
||||||
NDPPD_NS_BEGIN
|
NDPPD_NS_BEGIN
|
||||||
|
|
||||||
@ -64,13 +66,19 @@ iface::~iface()
|
|||||||
if (_prev_allmulti >= 0) {
|
if (_prev_allmulti >= 0) {
|
||||||
allmulti(_prev_allmulti);
|
allmulti(_prev_allmulti);
|
||||||
}
|
}
|
||||||
|
if (_prev_promiscuous >= 0) {
|
||||||
|
promiscuous(_prev_promiscuous);
|
||||||
|
}
|
||||||
close(_pfd);
|
close(_pfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
_map_dirty = true;
|
_map_dirty = true;
|
||||||
|
|
||||||
|
_serves.clear();
|
||||||
|
_parents.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr<iface> iface::open_pfd(const std::string& name)
|
ptr<iface> iface::open_pfd(const std::string& name, bool promiscuous)
|
||||||
{
|
{
|
||||||
int fd = 0;
|
int fd = 0;
|
||||||
|
|
||||||
@ -147,7 +155,7 @@ ptr<iface> iface::open_pfd(const std::string& name)
|
|||||||
// Bail if it's* not* ND_NEIGHBOR_SOLICIT.
|
// Bail if it's* not* ND_NEIGHBOR_SOLICIT.
|
||||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_SOLICIT, 0, 1),
|
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_SOLICIT, 0, 1),
|
||||||
// Keep packet.
|
// Keep packet.
|
||||||
BPF_STMT(BPF_RET | BPF_K, -1),
|
BPF_STMT(BPF_RET | BPF_K, (u_int32_t)-1),
|
||||||
// Drop packet.
|
// Drop packet.
|
||||||
BPF_STMT(BPF_RET | BPF_K, 0)
|
BPF_STMT(BPF_RET | BPF_K, 0)
|
||||||
};
|
};
|
||||||
@ -169,6 +177,13 @@ ptr<iface> iface::open_pfd(const std::string& name)
|
|||||||
// Eh. Allmulti.
|
// Eh. Allmulti.
|
||||||
ifa->_prev_allmulti = ifa->allmulti(1);
|
ifa->_prev_allmulti = ifa->allmulti(1);
|
||||||
|
|
||||||
|
// Eh. Promiscuous
|
||||||
|
if (promiscuous == true) {
|
||||||
|
ifa->_prev_promiscuous = ifa->promiscuous(1);
|
||||||
|
} else {
|
||||||
|
ifa->_prev_promiscuous = -1;
|
||||||
|
}
|
||||||
|
|
||||||
_map_dirty = true;
|
_map_dirty = true;
|
||||||
|
|
||||||
return ifa;
|
return ifa;
|
||||||
@ -212,23 +227,29 @@ ptr<iface> iface::open_ifd(const std::string& name)
|
|||||||
|
|
||||||
if (ioctl(fd, SIOCGIFHWADDR,& ifr) < 0) {
|
if (ioctl(fd, SIOCGIFHWADDR,& ifr) < 0) {
|
||||||
close(fd);
|
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>();
|
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.
|
// Set max hops.
|
||||||
|
|
||||||
int hops = 255;
|
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);
|
close(fd);
|
||||||
logger::error() << "iface::open_ifd() failed IPV6_MULTICAST_HOPS";
|
logger::error() << "iface::open_ifd() failed IPV6_MULTICAST_HOPS";
|
||||||
return ptr<iface>();
|
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);
|
close(fd);
|
||||||
logger::error() << "iface::open_ifd() failed IPV6_UNICAST_HOPS";
|
logger::error() << "iface::open_ifd() failed IPV6_UNICAST_HOPS";
|
||||||
return ptr<iface>();
|
return ptr<iface>();
|
||||||
@ -238,9 +259,11 @@ ptr<iface> iface::open_ifd(const std::string& name)
|
|||||||
|
|
||||||
int on = 1;
|
int on = 1;
|
||||||
|
|
||||||
if (ioctl(fd, FIONBIO, (char* )&on) < 0) {
|
if (ioctl(fd, FIONBIO, (char*)&on) < 0) {
|
||||||
close(fd);
|
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>();
|
return ptr<iface>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +271,7 @@ ptr<iface> iface::open_ifd(const std::string& name)
|
|||||||
|
|
||||||
struct icmp6_filter filter;
|
struct icmp6_filter filter;
|
||||||
ICMP6_FILTER_SETBLOCKALL(&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) {
|
if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER,& filter, sizeof(filter)) < 0) {
|
||||||
logger::error() << "Failed to set filter";
|
logger::error() << "Failed to set filter";
|
||||||
@ -278,7 +301,7 @@ ptr<iface> iface::open_ifd(const std::string& name)
|
|||||||
return ifa;
|
return ifa;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t iface::read(int fd, struct sockaddr* saddr, uint8_t* msg, size_t size)
|
ssize_t iface::read(int fd, struct sockaddr* saddr, ssize_t saddr_size, uint8_t* msg, size_t size)
|
||||||
{
|
{
|
||||||
struct msghdr mhdr;
|
struct msghdr mhdr;
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
@ -292,18 +315,21 @@ ssize_t iface::read(int fd, struct sockaddr* saddr, uint8_t* msg, size_t size)
|
|||||||
|
|
||||||
memset(&mhdr, 0, sizeof(mhdr));
|
memset(&mhdr, 0, sizeof(mhdr));
|
||||||
mhdr.msg_name = (caddr_t)saddr;
|
mhdr.msg_name = (caddr_t)saddr;
|
||||||
mhdr.msg_namelen = sizeof(struct sockaddr);
|
mhdr.msg_namelen = saddr_size;
|
||||||
mhdr.msg_iov =& iov;
|
mhdr.msg_iov =& iov;
|
||||||
mhdr.msg_iovlen = 1;
|
mhdr.msg_iovlen = 1;
|
||||||
|
|
||||||
if ((len = recvmsg(fd,& mhdr, 0)) < 0)
|
if ((len = recvmsg(fd,& mhdr, 0)) < 0)
|
||||||
|
{
|
||||||
|
logger::error() << "iface::read() failed! error=" << logger::err() << ", ifa=" << name();
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger::debug() << "iface::read() ifa=" << name() << ", len=" << len;
|
||||||
|
|
||||||
if (len < sizeof(struct icmp6_hdr))
|
if (len < sizeof(struct icmp6_hdr))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
logger::debug() << "iface::read() len=" << len;
|
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,12 +353,16 @@ ssize_t iface::write(int fd, const address& daddr, const uint8_t* msg, size_t si
|
|||||||
mhdr.msg_iov =& iov;
|
mhdr.msg_iov =& iov;
|
||||||
mhdr.msg_iovlen = 1;
|
mhdr.msg_iovlen = 1;
|
||||||
|
|
||||||
logger::debug() << "iface::write() daddr=" << daddr.to_string() << ", len=" << size;
|
logger::debug() << "iface::write() ifa=" << name() << ", daddr=" << daddr.to_string() << ", len="
|
||||||
|
<< size;
|
||||||
|
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
if ((len = sendmsg(fd,& mhdr, 0)) < 0)
|
if ((len = sendmsg(fd,& mhdr, 0)) < 0)
|
||||||
|
{
|
||||||
|
logger::error() << "iface::write() failed! error=" << logger::err() << ", ifa=" << name() << ", daddr=" << daddr.to_string();
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@ -343,20 +373,28 @@ ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr)
|
|||||||
uint8_t msg[256];
|
uint8_t msg[256];
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
if ((len = read(_pfd, (struct sockaddr* )&t_saddr, msg, sizeof(msg))) < 0)
|
if ((len = read(_pfd, (struct sockaddr*)&t_saddr, sizeof(struct sockaddr_ll), msg, sizeof(msg))) < 0) {
|
||||||
|
logger::warning() << "iface::read_solicit() failed: " << logger::err();
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
struct ip6_hdr* ip6h =
|
struct ip6_hdr* ip6h =
|
||||||
(struct ip6_hdr* )(msg + ETH_HLEN);
|
(struct ip6_hdr* )(msg + ETH_HLEN);
|
||||||
|
|
||||||
struct nd_neighbor_solicit* ns =
|
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;
|
taddr = ns->nd_ns_target;
|
||||||
daddr = ip6h->ip6_dst;
|
daddr = ip6h->ip6_dst;
|
||||||
saddr = ip6h->ip6_src;
|
saddr = ip6h->ip6_src;
|
||||||
|
|
||||||
logger::debug() << "iface::read_solicit() saddr=" << saddr.to_string() << ", daddr=" << daddr.to_string() << ", len=" << len;
|
// 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;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@ -380,7 +418,8 @@ ssize_t iface::write_solicit(const address& taddr)
|
|||||||
|
|
||||||
memcpy(&ns->nd_ns_target,& taddr.const_addr(), sizeof(struct in6_addr));
|
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.
|
// FIXME: Alright, I'm lazy.
|
||||||
static address multicast("ff02::1:ff00:0000");
|
static address multicast("ff02::1:ff00:0000");
|
||||||
@ -393,9 +432,11 @@ ssize_t iface::write_solicit(const address& taddr)
|
|||||||
daddr.addr().s6_addr[14] = taddr.const_addr().s6_addr[14];
|
daddr.addr().s6_addr[14] = taddr.const_addr().s6_addr[14];
|
||||||
daddr.addr().s6_addr[15] = taddr.const_addr().s6_addr[15];
|
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)
|
ssize_t iface::write_advert(const address& daddr, const address& taddr, bool router)
|
||||||
@ -414,13 +455,15 @@ ssize_t iface::write_advert(const address& daddr, const address& taddr, bool rou
|
|||||||
opt->nd_opt_len = 1;
|
opt->nd_opt_len = 1;
|
||||||
|
|
||||||
na->nd_na_type = ND_NEIGHBOR_ADVERT;
|
na->nd_na_type = ND_NEIGHBOR_ADVERT;
|
||||||
na->nd_na_flags_reserved = ND_NA_FLAG_SOLICITED | (router ? ND_NA_FLAG_ROUTER : 0);
|
na->nd_na_flags_reserved = (daddr.is_multicast() ? 0 : ND_NA_FLAG_SOLICITED) | (router ? ND_NA_FLAG_ROUTER : 0);
|
||||||
|
|
||||||
memcpy(&na->nd_na_target,& taddr.const_addr(), sizeof(struct in6_addr));
|
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) +
|
return write(_ifd, daddr, (uint8_t* )buf, sizeof(struct nd_neighbor_advert) +
|
||||||
sizeof(struct nd_opt_hdr) + 6);
|
sizeof(struct nd_opt_hdr) + 6);
|
||||||
@ -432,11 +475,22 @@ ssize_t iface::read_advert(address& saddr, address& taddr)
|
|||||||
uint8_t msg[256];
|
uint8_t msg[256];
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
if ((len = read(_ifd, (struct sockaddr* )&t_saddr, msg, sizeof(msg))) < 0)
|
memset(&t_saddr, 0, sizeof(struct sockaddr_in6));
|
||||||
|
t_saddr.sin6_family = AF_INET6;
|
||||||
|
t_saddr.sin6_port = htons(IPPROTO_ICMPV6); // Needed?
|
||||||
|
|
||||||
|
if ((len = read(_ifd, (struct sockaddr* )&t_saddr, sizeof(struct sockaddr_in6), msg, sizeof(msg))) < 0) {
|
||||||
|
logger::warning() << "iface::read_advert() failed: " << logger::err();
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
saddr = t_saddr.sin6_addr;
|
saddr = t_saddr.sin6_addr;
|
||||||
|
|
||||||
|
// Ignore packets sent from this machine
|
||||||
|
if (iface::is_local(saddr) == true) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (((struct icmp6_hdr* )msg)->icmp6_type != ND_NEIGHBOR_ADVERT)
|
if (((struct icmp6_hdr* )msg)->icmp6_type != ND_NEIGHBOR_ADVERT)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -447,6 +501,79 @@ ssize_t iface::read_advert(address& saddr, address& taddr)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool iface::is_local(const address& addr)
|
||||||
|
{
|
||||||
|
// Check if the address is for an interface we own that is attached to
|
||||||
|
// one of the slave interfaces
|
||||||
|
for (std::list<ptr<route> >::iterator ad = address::addresses_begin(); ad != address::addresses_end(); ad++)
|
||||||
|
{
|
||||||
|
if ((*ad)->addr() == addr)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iface::handle_local(const address& saddr, const address& taddr)
|
||||||
|
{
|
||||||
|
// Check if the address is for an interface we own that is attached to
|
||||||
|
// one of the slave interfaces
|
||||||
|
for (std::list<ptr<route> >::iterator ad = address::addresses_begin(); ad != address::addresses_end(); ad++)
|
||||||
|
{
|
||||||
|
if ((*ad)->addr() == taddr)
|
||||||
|
{
|
||||||
|
// Loop through all the serves that are using this iface to respond to NDP solicitation requests
|
||||||
|
for (std::list<weak_ptr<proxy> >::iterator pit = serves_begin(); pit != serves_end(); pit++) {
|
||||||
|
ptr<proxy> pr = (*pit);
|
||||||
|
if (!pr) continue;
|
||||||
|
|
||||||
|
for (std::list<ptr<rule> >::iterator it = pr->rules_begin(); it != pr->rules_end(); it++) {
|
||||||
|
ptr<rule> ru = *it;
|
||||||
|
|
||||||
|
if (ru->daughter() && ru->daughter()->name() == (*ad)->ifname())
|
||||||
|
{
|
||||||
|
logger::debug() << "proxy::handle_solicit() found local taddr=" << taddr;
|
||||||
|
write_advert(saddr, taddr, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iface::handle_reverse_advert(const address& saddr, const std::string& ifname)
|
||||||
|
{
|
||||||
|
if (!saddr.is_unicast())
|
||||||
|
return;
|
||||||
|
|
||||||
|
logger::debug()
|
||||||
|
<< "proxy::handle_reverse_advert()";
|
||||||
|
|
||||||
|
// Loop through all the parents that forward new NDP soliciation requests to this interface
|
||||||
|
for (std::list<weak_ptr<proxy> >::iterator pit = parents_begin(); pit != parents_end(); pit++) {
|
||||||
|
ptr<proxy> parent = (*pit);
|
||||||
|
if (!parent || !parent->ifa()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the reverse path on any proxies that are dealing
|
||||||
|
// with the reverse direction (this helps improve connectivity and
|
||||||
|
// latency in a full duplex setup)
|
||||||
|
for (std::list<ptr<rule> >::iterator it = parent->rules_begin(); it != parent->rules_end(); it++) {
|
||||||
|
ptr<rule> ru = *it;
|
||||||
|
|
||||||
|
if (ru->addr() == saddr &&
|
||||||
|
ru->daughter()->name() == ifname)
|
||||||
|
{
|
||||||
|
logger::debug() << " - generating artifical advertisement: " << ifname;
|
||||||
|
parent->handle_stateless_advert(saddr, saddr, ifname, ru->autovia());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void iface::fixup_pollfds()
|
void iface::fixup_pollfds()
|
||||||
{
|
{
|
||||||
_pollfds.resize(_map.size()* 2);
|
_pollfds.resize(_map.size()* 2);
|
||||||
@ -469,22 +596,6 @@ void iface::fixup_pollfds()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void iface::remove_session(const ptr<session>& se)
|
|
||||||
{
|
|
||||||
for (std::list<weak_ptr<session> >::iterator it = _sessions.begin();
|
|
||||||
it != _sessions.end(); it++) {
|
|
||||||
if (*it == se) {
|
|
||||||
_sessions.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void iface::add_session(const ptr<session>& se)
|
|
||||||
{
|
|
||||||
_sessions.push_back(se);
|
|
||||||
}
|
|
||||||
|
|
||||||
void iface::cleanup()
|
void iface::cleanup()
|
||||||
{
|
{
|
||||||
for (std::map<std::string, weak_ptr<iface> >::iterator it = _map.begin();
|
for (std::map<std::string, weak_ptr<iface> >::iterator it = _map.begin();
|
||||||
@ -514,6 +625,7 @@ int iface::poll_all()
|
|||||||
int len;
|
int len;
|
||||||
|
|
||||||
if ((len = ::poll(&_pollfds[0], _pollfds.size(), 50)) < 0) {
|
if ((len = ::poll(&_pollfds[0], _pollfds.size(), 50)) < 0) {
|
||||||
|
logger::error() << "Failed to poll interfaces: " << logger::err();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,44 +647,108 @@ int iface::poll_all()
|
|||||||
|
|
||||||
bool is_pfd = i++ % 2;
|
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)) {
|
if (!(f_it->revents & POLLIN)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr<iface> ifa = i_it->second;
|
|
||||||
|
|
||||||
address saddr, daddr, taddr;
|
address saddr, daddr, taddr;
|
||||||
|
ssize_t size;
|
||||||
|
|
||||||
if (is_pfd) {
|
if (is_pfd) {
|
||||||
if (ifa->read_solicit(saddr, daddr, taddr) < 0) {
|
size = ifa->read_solicit(saddr, daddr, taddr);
|
||||||
|
if (size < 0) {
|
||||||
logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str();
|
logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (size == 0) {
|
||||||
if (!saddr.is_unicast() || !daddr.is_multicast()) {
|
logger::debug() << "iface::read_solicit() loopback received and ignored";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ifa->_pr) {
|
// Process any local addresses for interfaces that we are proxying
|
||||||
ifa->_pr->handle_solicit(saddr, daddr, taddr);
|
if (ifa->handle_local(saddr, taddr) == true) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have to handle all the parents who may be interested in
|
||||||
|
// the reverse path towards the one who sent this solicit.
|
||||||
|
// In fact, the parent need to know the source address in order
|
||||||
|
// to respond to NDP Solicitations
|
||||||
|
ifa->handle_reverse_advert(saddr, ifa->name());
|
||||||
|
|
||||||
|
// Loop through all the proxies that are using this iface to respond to NDP solicitation requests
|
||||||
|
bool handled = false;
|
||||||
|
for (std::list<weak_ptr<proxy> >::iterator pit = ifa->serves_begin(); pit != ifa->serves_end(); pit++) {
|
||||||
|
ptr<proxy> pr = (*pit);
|
||||||
|
if (!pr) continue;
|
||||||
|
|
||||||
|
// Process the solicitation request by relating it to other
|
||||||
|
// interfaces or lookup up any statics routes we have configured
|
||||||
|
handled = true;
|
||||||
|
pr->handle_solicit(saddr, taddr, ifa->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it was not handled then write an error message
|
||||||
|
if (handled == false) {
|
||||||
|
logger::debug() << " - solicit was ignored";
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (ifa->read_advert(saddr, taddr) < 0) {
|
size = ifa->read_advert(saddr, taddr);
|
||||||
|
if (size < 0) {
|
||||||
logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str();
|
logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (size == 0) {
|
||||||
|
logger::debug() << "iface::read_advert() loopback received and ignored";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (std::list<weak_ptr<session> >::iterator s_it = ifa->_sessions.begin();
|
// Process the NDP advert
|
||||||
s_it != ifa->_sessions.end(); s_it++) {
|
bool handled = false;
|
||||||
assert(!s_it->is_null());
|
for (std::list<weak_ptr<proxy> >::iterator pit = ifa->parents_begin(); pit != ifa->parents_end(); pit++) {
|
||||||
|
ptr<proxy> pr = (*pit);
|
||||||
|
if (!pr || !pr->ifa()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const ptr<session> sess = *s_it;
|
// 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 ((sess->taddr() == taddr) && (sess->status() == session::WAITING)) {
|
if (ru->addr() == taddr &&
|
||||||
sess->handle_advert();
|
ru->daughter() &&
|
||||||
|
ru->daughter()->name() == ifa->name())
|
||||||
|
{
|
||||||
|
is_relevant = true;
|
||||||
|
autovia = ru->autovia();
|
||||||
break;
|
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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,7 +759,9 @@ int iface::allmulti(int state)
|
|||||||
{
|
{
|
||||||
struct ifreq ifr;
|
struct ifreq ifr;
|
||||||
|
|
||||||
logger::debug() << "iface::allmulti() state=" << state << ", _name=\"" << _name << "\"";
|
logger::debug()
|
||||||
|
<< "iface::allmulti() state="
|
||||||
|
<< state << ", _name=\"" << _name << "\"";
|
||||||
|
|
||||||
state = !!state;
|
state = !!state;
|
||||||
|
|
||||||
@ -592,10 +770,11 @@ int iface::allmulti(int state)
|
|||||||
strncpy(ifr.ifr_name, _name.c_str(), IFNAMSIZ);
|
strncpy(ifr.ifr_name, _name.c_str(), IFNAMSIZ);
|
||||||
|
|
||||||
if (ioctl(_pfd, SIOCGIFFLAGS, &ifr) < 0) {
|
if (ioctl(_pfd, SIOCGIFFLAGS, &ifr) < 0) {
|
||||||
|
logger::error() << "Failed to get allmulti: " << logger::err();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int old_state = !!(ifr.ifr_flags & IFF_ALLMULTI);
|
int old_state = !!(ifr.ifr_flags &IFF_ALLMULTI);
|
||||||
|
|
||||||
if (state == old_state) {
|
if (state == old_state) {
|
||||||
return old_state;
|
return old_state;
|
||||||
@ -608,6 +787,46 @@ int iface::allmulti(int state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ioctl(_pfd, SIOCSIFFLAGS, &ifr) < 0) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,14 +838,34 @@ const std::string& iface::name() const
|
|||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void iface::pr(const ptr<proxy>& pr)
|
void iface::add_serves(const ptr<proxy>& pr)
|
||||||
{
|
{
|
||||||
_pr = pr;
|
_serves.push_back(pr);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ptr<proxy>& iface::pr() const
|
std::list<weak_ptr<proxy> >::iterator iface::serves_begin()
|
||||||
{
|
{
|
||||||
return _pr;
|
return _serves.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<weak_ptr<proxy> >::iterator iface::serves_end()
|
||||||
|
{
|
||||||
|
return _serves.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void iface::add_parent(const ptr<proxy>& pr)
|
||||||
|
{
|
||||||
|
_parents.push_back(pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<weak_ptr<proxy> >::iterator iface::parents_begin()
|
||||||
|
{
|
||||||
|
return _parents.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<weak_ptr<proxy> >::iterator iface::parents_end()
|
||||||
|
{
|
||||||
|
return _parents.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
NDPPD_NS_END
|
NDPPD_NS_END
|
||||||
|
41
src/iface.h
41
src/iface.h
@ -38,13 +38,13 @@ public:
|
|||||||
|
|
||||||
static ptr<iface> open_ifd(const std::string& name);
|
static ptr<iface> open_ifd(const std::string& name);
|
||||||
|
|
||||||
static ptr<iface> open_pfd(const std::string& name);
|
static ptr<iface> open_pfd(const std::string& name, bool promiscuous);
|
||||||
|
|
||||||
static int poll_all();
|
static int poll_all();
|
||||||
|
|
||||||
static ssize_t read(int fd, struct sockaddr* saddr, uint8_t* msg, size_t size);
|
ssize_t read(int fd, struct sockaddr* saddr, ssize_t saddr_size, uint8_t* msg, size_t size);
|
||||||
|
|
||||||
static ssize_t write(int fd, const address& daddr, const uint8_t* msg, size_t size);
|
ssize_t write(int fd, const address& daddr, const uint8_t* msg, size_t size);
|
||||||
|
|
||||||
// Writes a NB_NEIGHBOR_SOLICIT message to the _ifd socket.
|
// Writes a NB_NEIGHBOR_SOLICIT message to the _ifd socket.
|
||||||
ssize_t write_solicit(const address& taddr);
|
ssize_t write_solicit(const address& taddr);
|
||||||
@ -58,20 +58,30 @@ public:
|
|||||||
// Reads a NB_NEIGHBOR_ADVERT message from the _ifd socket;
|
// Reads a NB_NEIGHBOR_ADVERT message from the _ifd socket;
|
||||||
ssize_t read_advert(address& saddr, address& taddr);
|
ssize_t read_advert(address& saddr, address& taddr);
|
||||||
|
|
||||||
|
bool handle_local(const address& saddr, const address& taddr);
|
||||||
|
|
||||||
|
bool is_local(const address& addr);
|
||||||
|
|
||||||
|
void handle_reverse_advert(const address& saddr, const std::string& ifname);
|
||||||
|
|
||||||
// Returns the name of the interface.
|
// Returns the name of the interface.
|
||||||
const std::string& name() const;
|
const std::string& name() const;
|
||||||
|
|
||||||
// Adds a session to be monitored for ND_NEIGHBOR_ADVERT messages.
|
std::list<weak_ptr<proxy> >::iterator serves_begin();
|
||||||
void add_session(const ptr<session>& se);
|
|
||||||
|
|
||||||
void remove_session(const ptr<session>& se);
|
std::list<weak_ptr<proxy> >::iterator serves_end();
|
||||||
|
|
||||||
void pr(const ptr<proxy>& pr);
|
void add_serves(const ptr<proxy>& proxy);
|
||||||
|
|
||||||
const ptr<proxy>& pr() const;
|
std::list<weak_ptr<proxy> >::iterator parents_begin();
|
||||||
|
|
||||||
|
std::list<weak_ptr<proxy> >::iterator parents_end();
|
||||||
|
|
||||||
|
void add_parent(const ptr<proxy>& parent);
|
||||||
|
|
||||||
|
static std::map<std::string, weak_ptr<iface> > _map;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::map<std::string, weak_ptr<iface> > _map;
|
|
||||||
|
|
||||||
static bool _map_dirty;
|
static bool _map_dirty;
|
||||||
|
|
||||||
@ -97,14 +107,15 @@ private:
|
|||||||
// Previous state of ALLMULTI for the interface.
|
// Previous state of ALLMULTI for the interface.
|
||||||
int _prev_allmulti;
|
int _prev_allmulti;
|
||||||
|
|
||||||
|
// Previous state of PROMISC for the interface
|
||||||
|
int _prev_promiscuous;
|
||||||
|
|
||||||
// Name of this interface.
|
// Name of this interface.
|
||||||
std::string _name;
|
std::string _name;
|
||||||
|
|
||||||
// An array of sessions that are monitoring this interface for
|
std::list<weak_ptr<proxy> > _serves;
|
||||||
// ND_NEIGHBOR_ADVERT messages.
|
|
||||||
std::list<weak_ptr<session> > _sessions;
|
|
||||||
|
|
||||||
weak_ptr<proxy> _pr;
|
std::list<weak_ptr<proxy> > _parents;
|
||||||
|
|
||||||
// The link-layer address of this interface.
|
// The link-layer address of this interface.
|
||||||
struct ether_addr hwaddr;
|
struct ether_addr hwaddr;
|
||||||
@ -113,6 +124,10 @@ private:
|
|||||||
// or -1 if there was an error.
|
// or -1 if there was an error.
|
||||||
int allmulti(int state);
|
int allmulti(int state);
|
||||||
|
|
||||||
|
// Turns on/off PROMISC for this interface - returns the previous state
|
||||||
|
// or -1 if there was an error
|
||||||
|
int promiscuous(int state);
|
||||||
|
|
||||||
// Constructor.
|
// Constructor.
|
||||||
iface();
|
iface();
|
||||||
};
|
};
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <cerrno>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@ -79,6 +80,28 @@ std::string logger::format(const std::string& fmt, ...)
|
|||||||
return buf;
|
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()
|
logger logger::error()
|
||||||
{
|
{
|
||||||
return logger(LOG_ERR);
|
return logger(LOG_ERR);
|
||||||
|
@ -71,7 +71,10 @@ public:
|
|||||||
static logger debug();
|
static logger debug();
|
||||||
static logger notice();
|
static logger notice();
|
||||||
|
|
||||||
|
static std::string err();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
int _pri;
|
int _pri;
|
||||||
|
|
||||||
std::stringstream _ss;
|
std::stringstream _ss;
|
||||||
@ -89,7 +92,9 @@ private:
|
|||||||
|
|
||||||
static int _max_pri;
|
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
|
NDPPD_NS_END
|
||||||
|
275
src/nd-netlink.cc
Normal file
275
src/nd-netlink.cc
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
//
|
||||||
|
//@file nd-netlink.cc
|
||||||
|
//
|
||||||
|
// Copyright 2016, Allied Telesis Labs New Zealand, Ltd
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netlink/route/addr.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include "ndppd.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
NDPPD_NS_BEGIN
|
||||||
|
|
||||||
|
pthread_mutex_t cs_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
struct in6_addr*
|
||||||
|
address_create_ipv6(struct in6_addr *local)
|
||||||
|
{
|
||||||
|
struct in6_addr *addr = (struct in6_addr *)calloc(1, sizeof(struct in6_addr));
|
||||||
|
memcpy(addr, local, sizeof(struct in6_addr));
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void if_add_to_list(int ifindex, const ptr<iface>& ifa)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
pthread_mutex_lock (&cs_mutex);
|
||||||
|
for (std::vector<interface>::iterator it = interfaces.begin();
|
||||||
|
it != interfaces.end(); it++) {
|
||||||
|
if ((*it).ifindex == ifindex) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
logger::debug() << "rule::add_iface() if=" << ifa->name();
|
||||||
|
interface anInterface;
|
||||||
|
anInterface._name = ifa->name();
|
||||||
|
anInterface.ifindex = ifindex;
|
||||||
|
interfaces.push_back(anInterface);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock (&cs_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
if_addr_add(int ifindex, struct in6_addr *iaddr)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock (&cs_mutex);
|
||||||
|
for (std::vector<interface>::iterator it = interfaces.begin();
|
||||||
|
it != interfaces.end(); it++) {
|
||||||
|
if ((*it).ifindex == ifindex) {
|
||||||
|
address addr = address(*iaddr);
|
||||||
|
logger::debug() << "Adding addr " << addr.to_string();
|
||||||
|
std::list<address>::iterator it_addr;
|
||||||
|
it_addr = std::find((*it).addresses.begin(), (*it).addresses.end(), addr);
|
||||||
|
if (it_addr == (*it).addresses.end()) {
|
||||||
|
(*it).addresses.push_back(addr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(iaddr);
|
||||||
|
pthread_mutex_unlock (&cs_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
if_addr_del(int ifindex, struct in6_addr *iaddr)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock (&cs_mutex);
|
||||||
|
for (std::vector<interface>::iterator it = interfaces.begin();
|
||||||
|
it != interfaces.end(); it++) {
|
||||||
|
if ((*it).ifindex == ifindex) {
|
||||||
|
address addr = address(*iaddr);
|
||||||
|
logger::debug() << "Deleting addr " << addr.to_string();
|
||||||
|
(*it).addresses.remove(addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(iaddr);
|
||||||
|
pthread_mutex_unlock (&cs_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
if_addr_find(std::string iface, const struct in6_addr *iaddr)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
pthread_mutex_lock (&cs_mutex);
|
||||||
|
for (std::vector<interface>::iterator it = interfaces.begin();
|
||||||
|
it != interfaces.end(); it++) {
|
||||||
|
if (iface.compare((*it)._name) == 0) {
|
||||||
|
address addr = address(*iaddr);
|
||||||
|
std::list<address>::iterator it_addr;
|
||||||
|
it_addr = std::find((*it).addresses.begin(), (*it).addresses.end(), addr);
|
||||||
|
if (it_addr != (*it).addresses.end()) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock (&cs_mutex);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nl_msg_newaddr(struct nlmsghdr *hdr)
|
||||||
|
{
|
||||||
|
struct ifaddrmsg *ifaddr =
|
||||||
|
(struct ifaddrmsg *)(((char *) hdr) + (sizeof(struct nlmsghdr)));
|
||||||
|
// parse the attributes
|
||||||
|
struct nlattr *attrs[IFA_MAX + 1];
|
||||||
|
struct nlattr *s = (struct nlattr *)(((char *) ifaddr) + (sizeof(struct ifaddrmsg)));
|
||||||
|
int len = nlmsg_datalen(hdr) - sizeof(struct ifinfomsg);
|
||||||
|
memset(&attrs, '\0', sizeof(attrs));
|
||||||
|
nla_parse(attrs, IFA_MAX, s, len, NULL);
|
||||||
|
|
||||||
|
struct in6_addr* addr = NULL;
|
||||||
|
|
||||||
|
if (ifaddr->ifa_family == AF_INET6) {
|
||||||
|
addr = address_create_ipv6((struct in6_addr *)nla_data(attrs[IFA_ADDRESS]));
|
||||||
|
if_addr_add(ifaddr->ifa_index, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nl_msg_deladdr(struct nlmsghdr *hdr)
|
||||||
|
{
|
||||||
|
struct ifaddrmsg *ifaddr =
|
||||||
|
(struct ifaddrmsg *)(((char *) hdr) + (sizeof(struct nlmsghdr)));
|
||||||
|
// parse the attributes
|
||||||
|
struct nlattr *attrs[IFA_MAX + 1];
|
||||||
|
struct nlattr *s = (struct nlattr *)(((char *) ifaddr) + (sizeof(struct ifaddrmsg)));
|
||||||
|
int len = nlmsg_datalen(hdr) - sizeof(struct ifinfomsg);
|
||||||
|
memset(&attrs, '\0', sizeof(attrs));
|
||||||
|
nla_parse(attrs, IFA_MAX, s, len, NULL);
|
||||||
|
|
||||||
|
struct in6_addr* addr = NULL;
|
||||||
|
|
||||||
|
if (ifaddr->ifa_family == AF_INET6) {
|
||||||
|
addr = address_create_ipv6((struct in6_addr *)nla_data(attrs[IFA_ADDRESS]));
|
||||||
|
if_addr_del(ifaddr->ifa_index, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
new_addr(struct nl_object *obj, void *p)
|
||||||
|
{
|
||||||
|
struct rtnl_addr *addr = (struct rtnl_addr *) obj;
|
||||||
|
struct nl_addr *local = rtnl_addr_get_local(addr);
|
||||||
|
int family = rtnl_addr_get_family(addr);
|
||||||
|
int ifindex = rtnl_addr_get_ifindex(addr);
|
||||||
|
struct in6_addr* in_addr = NULL;
|
||||||
|
|
||||||
|
char ipstr[INET6_ADDRSTRLEN];
|
||||||
|
inet_ntop(family, nl_addr_get_binary_addr(local), ipstr, INET6_ADDRSTRLEN);
|
||||||
|
|
||||||
|
switch (family) {
|
||||||
|
case AF_INET:
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
in_addr = address_create_ipv6((struct in6_addr *)nl_addr_get_binary_addr(local));
|
||||||
|
if_addr_add(ifindex, in_addr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger::error() << "Unknown message family: " << family;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nl_msg_handler(struct nl_msg *msg, void *arg)
|
||||||
|
{
|
||||||
|
logger::debug() << "nl_msg_handler";
|
||||||
|
struct nlmsghdr *hdr = nlmsg_hdr(msg);
|
||||||
|
|
||||||
|
switch (hdr->nlmsg_type) {
|
||||||
|
case RTM_NEWADDR:
|
||||||
|
nl_msg_newaddr(hdr);
|
||||||
|
break;
|
||||||
|
case RTM_DELADDR:
|
||||||
|
nl_msg_deladdr(hdr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger::error() << "Unknown message type: " << hdr->nlmsg_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
netlink_monitor(void *p)
|
||||||
|
{
|
||||||
|
struct nl_sock *sock = (struct nl_sock *) p;
|
||||||
|
struct nl_cache *addr_cache;
|
||||||
|
|
||||||
|
// get all the current addresses
|
||||||
|
if (rtnl_addr_alloc_cache(sock, &addr_cache) < 0) {
|
||||||
|
perror("rtnl_addr_alloc_cache");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add existing addresses
|
||||||
|
nl_cache_foreach(addr_cache, new_addr, NULL);
|
||||||
|
// destroy the cache
|
||||||
|
nl_cache_free(addr_cache);
|
||||||
|
|
||||||
|
// switch to notification mode
|
||||||
|
// disable sequence checking
|
||||||
|
nl_socket_disable_seq_check(sock);
|
||||||
|
// set the callback we want
|
||||||
|
nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, nl_msg_handler, NULL);
|
||||||
|
|
||||||
|
// subscribe to the IPv6 address change callbacks
|
||||||
|
nl_socket_add_memberships(sock, RTNLGRP_IPV6_IFADDR, 0);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
nl_recvmsgs_default(sock);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pthread_t monitor_thread;
|
||||||
|
static struct nl_sock *monitor_sock;
|
||||||
|
struct nl_sock *control_sock;
|
||||||
|
|
||||||
|
bool
|
||||||
|
netlink_setup()
|
||||||
|
{
|
||||||
|
// create a netlink socket
|
||||||
|
control_sock = nl_socket_alloc();
|
||||||
|
nl_connect(control_sock, NETLINK_ROUTE);
|
||||||
|
|
||||||
|
// create a thread to run the netlink monitor in
|
||||||
|
// create a netlink socket
|
||||||
|
monitor_sock = nl_socket_alloc();
|
||||||
|
nl_connect(monitor_sock, NETLINK_ROUTE);
|
||||||
|
// increase the recv buffer size to capture all notifications
|
||||||
|
nl_socket_set_buffer_size(monitor_sock, 2048000, 0);
|
||||||
|
|
||||||
|
pthread_create(&monitor_thread, NULL, netlink_monitor, monitor_sock);
|
||||||
|
pthread_setname_np(monitor_thread, "netlink");
|
||||||
|
if (pthread_setschedprio(monitor_thread, -10) < 0)
|
||||||
|
{
|
||||||
|
logger::warning() << "setschedprio: " << strerror(errno);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
netlink_teardown()
|
||||||
|
{
|
||||||
|
void *res = 0;
|
||||||
|
pthread_cancel(monitor_thread);
|
||||||
|
pthread_join(monitor_thread, &res);
|
||||||
|
nl_socket_free(monitor_sock);
|
||||||
|
nl_socket_free(control_sock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
NDPPD_NS_END
|
28
src/nd-netlink.h
Normal file
28
src/nd-netlink.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// @file nd-netlink.h
|
||||||
|
//
|
||||||
|
// Copyright 2016, Allied Telesis Labs New Zealand, Ltd
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
NDPPD_NS_BEGIN
|
||||||
|
|
||||||
|
bool netlink_teardown();
|
||||||
|
bool netlink_setup();
|
||||||
|
bool if_addr_find(std::string iface, const struct in6_addr *iaddr);
|
||||||
|
void if_add_to_list(int ifindex, const ptr<iface>& ifa);
|
||||||
|
|
||||||
|
NDPPD_NS_END
|
251
src/ndppd.cc
251
src/ndppd.cc
@ -24,6 +24,7 @@
|
|||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -32,20 +33,29 @@
|
|||||||
|
|
||||||
using namespace ndppd;
|
using namespace ndppd;
|
||||||
|
|
||||||
int daemonize()
|
static int daemonize()
|
||||||
{
|
{
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
if (pid < 0)
|
logger::error() << "Failed to fork during daemonize: " << logger::err();
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
pid_t sid = setsid();
|
umask(0);
|
||||||
|
|
||||||
if (sid < 0)
|
pid_t sid = setsid();
|
||||||
|
if (sid < 0) {
|
||||||
|
logger::error() << "Failed to setsid during daemonize: " << logger::err();
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chdir("/") < 0) {
|
||||||
|
logger::error() << "Failed to change path during daemonize: " << logger::err();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
close(STDIN_FILENO);
|
close(STDIN_FILENO);
|
||||||
close(STDOUT_FILENO);
|
close(STDOUT_FILENO);
|
||||||
@ -54,17 +64,12 @@ int daemonize()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool configure(const std::string& path)
|
static ptr<conf> load_config(const std::string& path)
|
||||||
{
|
{
|
||||||
ptr<conf> cf, x_cf;
|
ptr<conf> cf, x_cf;
|
||||||
|
|
||||||
if (!(cf = conf::load(path)))
|
if (!(cf = conf::load(path)))
|
||||||
return false;
|
return (conf*)NULL;
|
||||||
|
|
||||||
if (!(x_cf = cf->find("route-ttl")))
|
|
||||||
route::ttl(30000);
|
|
||||||
else
|
|
||||||
route::ttl(*x_cf);
|
|
||||||
|
|
||||||
std::vector<ptr<conf> >::const_iterator p_it;
|
std::vector<ptr<conf> >::const_iterator p_it;
|
||||||
|
|
||||||
@ -75,13 +80,94 @@ bool configure(const std::string& path)
|
|||||||
|
|
||||||
if (pr_cf->empty()) {
|
if (pr_cf->empty()) {
|
||||||
logger::error() << "'proxy' section is missing interface name";
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr<proxy> pr = proxy::open(*pr_cf);
|
bool promiscuous = false;
|
||||||
|
if (!(x_cf = pr_cf->find("promiscuous")))
|
||||||
|
promiscuous = false;
|
||||||
|
else
|
||||||
|
promiscuous = *x_cf;
|
||||||
|
|
||||||
if (!pr) {
|
ptr<proxy> pr = proxy::open(*pr_cf, promiscuous);
|
||||||
logger::error() << "Configuration failed for proxy '" << (const std::string& )*pr_cf << "'";
|
if (!pr || pr.is_null() == true) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,11 +176,31 @@ bool configure(const std::string& path)
|
|||||||
else
|
else
|
||||||
pr->router(*x_cf);
|
pr->router(*x_cf);
|
||||||
|
|
||||||
|
if (!(x_cf = pr_cf->find("autowire")))
|
||||||
|
pr->autowire(false);
|
||||||
|
else
|
||||||
|
pr->autowire(*x_cf);
|
||||||
|
|
||||||
|
if (!(x_cf = pr_cf->find("keepalive")))
|
||||||
|
pr->keepalive(true);
|
||||||
|
else
|
||||||
|
pr->keepalive(*x_cf);
|
||||||
|
|
||||||
|
if (!(x_cf = pr_cf->find("retries")))
|
||||||
|
pr->retries(3);
|
||||||
|
else
|
||||||
|
pr->retries(*x_cf);
|
||||||
|
|
||||||
if (!(x_cf = pr_cf->find("ttl")))
|
if (!(x_cf = pr_cf->find("ttl")))
|
||||||
pr->ttl(30000);
|
pr->ttl(30000);
|
||||||
else
|
else
|
||||||
pr->ttl(*x_cf);
|
pr->ttl(*x_cf);
|
||||||
|
|
||||||
|
if (!(x_cf = pr_cf->find("deadtime")))
|
||||||
|
pr->deadtime(pr->ttl());
|
||||||
|
else
|
||||||
|
pr->deadtime(*x_cf);
|
||||||
|
|
||||||
if (!(x_cf = pr_cf->find("timeout")))
|
if (!(x_cf = pr_cf->find("timeout")))
|
||||||
pr->timeout(500);
|
pr->timeout(500);
|
||||||
else
|
else
|
||||||
@ -107,44 +213,78 @@ bool configure(const std::string& path)
|
|||||||
for (r_it = rules.begin(); r_it != rules.end(); r_it++) {
|
for (r_it = rules.begin(); r_it != rules.end(); r_it++) {
|
||||||
ptr<conf> ru_cf =* r_it;
|
ptr<conf> ru_cf =* r_it;
|
||||||
|
|
||||||
if (ru_cf->empty()) {
|
address addr(*ru_cf);
|
||||||
logger::error() << "'rule' is missing an IPv6 address/net";
|
|
||||||
|
bool autovia = false;
|
||||||
|
if (!(x_cf = ru_cf->find("autovia")))
|
||||||
|
autovia = false;
|
||||||
|
else
|
||||||
|
autovia = *x_cf;
|
||||||
|
|
||||||
|
if (x_cf = ru_cf->find("iface"))
|
||||||
|
{
|
||||||
|
ptr<iface> ifa = iface::open_ifd(*x_cf);
|
||||||
|
if (!ifa || ifa.is_null() == true) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
address addr(*ru_cf);
|
ifa->add_parent(pr);
|
||||||
|
|
||||||
if (x_cf = ru_cf->find("iface")) {
|
myrules.push_back(pr->add_rule(addr, ifa, autovia));
|
||||||
if ((const std::string& )*x_cf == "") {
|
|
||||||
logger::error() << "'iface' expected an interface name";
|
|
||||||
} else {
|
|
||||||
pr->add_rule(addr, iface::open_ifd(*x_cf));
|
|
||||||
}
|
|
||||||
} else if (ru_cf->find("auto")) {
|
} else if (ru_cf->find("auto")) {
|
||||||
pr->add_rule(addr, true);
|
myrules.push_back(pr->add_rule(addr, true));
|
||||||
} else {
|
} else {
|
||||||
if (!ru_cf->find("static")) {
|
myrules.push_back(pr->add_rule(addr, false));
|
||||||
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) {
|
// Print out all the topology
|
||||||
logger::warning()
|
for (std::map<std::string, weak_ptr<iface> >::iterator i_it = iface::_map.begin(); i_it != iface::_map.end(); i_it++) {
|
||||||
<< "Low prefix length (" << addr.prefix() << " <= 120) when using 'static' method";
|
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() << " }";
|
||||||
}
|
}
|
||||||
|
|
||||||
pr->add_rule(addr, false);
|
logger::debug() << " }";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger::debug() << " " << "parents {";
|
||||||
|
for (std::list<weak_ptr<proxy> >::iterator pit = ifa->parents_begin(); pit != ifa->parents_end(); pit++) {
|
||||||
|
ptr<proxy> pr = (*pit);
|
||||||
|
|
||||||
|
logger::debug() << " " << "parent " << logger::format("%x", pr.get_pointer()) << ";";
|
||||||
}
|
}
|
||||||
|
logger::debug() << " }";
|
||||||
|
|
||||||
|
logger::debug() << "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool running = true;
|
static bool running = true;
|
||||||
|
|
||||||
void exit_ndppd(int sig)
|
static void exit_ndppd(int sig)
|
||||||
{
|
{
|
||||||
logger::error() << "Shutting down...";
|
logger::error() << "Shutting down...";
|
||||||
running = 0;
|
running = 0;
|
||||||
@ -201,14 +341,25 @@ 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) {
|
if (daemon) {
|
||||||
logger::syslog(true);
|
logger::syslog(true);
|
||||||
|
|
||||||
if (daemonize() < 0) {
|
if (daemonize() < 0)
|
||||||
logger::error() << "Failed to daemonize process";
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (!configure(cf))
|
||||||
|
return -1;
|
||||||
|
|
||||||
if (!pidfile.empty()) {
|
if (!pidfile.empty()) {
|
||||||
std::ofstream pf;
|
std::ofstream pf;
|
||||||
@ -217,23 +368,16 @@ int main(int argc, char* argv[], char* env[])
|
|||||||
pf.close();
|
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.
|
// Time stuff.
|
||||||
|
|
||||||
struct timeval t1, t2;
|
struct timeval t1, t2;
|
||||||
|
|
||||||
gettimeofday(&t1, 0);
|
gettimeofday(&t1, 0);
|
||||||
|
|
||||||
|
#ifdef WITH_ND_NETLINK
|
||||||
|
netlink_setup();
|
||||||
|
#endif
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
if (iface::poll_all() < 0) {
|
if (iface::poll_all() < 0) {
|
||||||
if (running) {
|
if (running) {
|
||||||
@ -252,10 +396,19 @@ int main(int argc, char* argv[], char* env[])
|
|||||||
t1.tv_sec = t2.tv_sec;
|
t1.tv_sec = t2.tv_sec;
|
||||||
t1.tv_usec = t2.tv_usec;
|
t1.tv_usec = t2.tv_usec;
|
||||||
|
|
||||||
|
if (rule::any_auto())
|
||||||
route::update(elapsed_time);
|
route::update(elapsed_time);
|
||||||
|
|
||||||
|
if (rule::any_iface())
|
||||||
|
address::update(elapsed_time);
|
||||||
|
|
||||||
session::update_all(elapsed_time);
|
session::update_all(elapsed_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_ND_NETLINK
|
||||||
|
netlink_teardown();
|
||||||
|
#endif
|
||||||
|
|
||||||
logger::notice() << "Bye";
|
logger::notice() << "Bye";
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#define NDPPD_NS_BEGIN namespace ndppd {
|
#define NDPPD_NS_BEGIN namespace ndppd {
|
||||||
#define NDPPD_NS_END }
|
#define NDPPD_NS_END }
|
||||||
|
|
||||||
#define NDPPD_VERSION "0.2.3"
|
#define NDPPD_VERSION "0.2.4"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
@ -35,3 +35,4 @@
|
|||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "rule.h"
|
#include "rule.h"
|
||||||
|
#include "nd-netlink.h"
|
||||||
|
205
src/proxy.cc
205
src/proxy.cc
@ -27,71 +27,87 @@
|
|||||||
|
|
||||||
NDPPD_NS_BEGIN
|
NDPPD_NS_BEGIN
|
||||||
|
|
||||||
|
static address all_nodes = address("ff02::1");
|
||||||
|
|
||||||
std::list<ptr<proxy> > proxy::_list;
|
std::list<ptr<proxy> > proxy::_list;
|
||||||
|
|
||||||
proxy::proxy() :
|
proxy::proxy() :
|
||||||
_router(true), _ttl(30000), _timeout(500)
|
_router(true), _ttl(30000), _deadtime(3000), _timeout(500), _autowire(false), _keepalive(true), _promiscuous(false), _retries(3)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr<proxy> proxy::create(const ptr<iface>& ifa)
|
ptr<proxy> proxy::find_aunt(const std::string& ifname, const address& taddr)
|
||||||
|
{
|
||||||
|
for (std::list<ptr<proxy> >::iterator sit = _list.begin();
|
||||||
|
sit != _list.end(); sit++)
|
||||||
|
{
|
||||||
|
ptr<proxy> pr = (*sit);
|
||||||
|
|
||||||
|
bool has_addr = false;
|
||||||
|
for (std::list<ptr<rule> >::iterator it = pr->_rules.begin(); it != pr->_rules.end(); it++) {
|
||||||
|
ptr<rule> ru = *it;
|
||||||
|
|
||||||
|
if (ru->addr() == taddr) {
|
||||||
|
has_addr = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_addr == false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pr->ifa() && pr->ifa()->name() == ifname)
|
||||||
|
return pr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr<proxy>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr<proxy> proxy::create(const ptr<iface>& ifa, bool promiscuous)
|
||||||
{
|
{
|
||||||
ptr<proxy> pr(new proxy());
|
ptr<proxy> pr(new proxy());
|
||||||
pr->_ptr = pr;
|
pr->_ptr = pr;
|
||||||
pr->_ifa = ifa;
|
pr->_ifa = ifa;
|
||||||
|
pr->_promiscuous = promiscuous;
|
||||||
|
|
||||||
_list.push_back(pr);
|
_list.push_back(pr);
|
||||||
|
|
||||||
ifa->pr(pr);
|
ifa->add_serves(pr);
|
||||||
|
|
||||||
logger::debug() << "proxy::create() if=" << ifa->name();
|
logger::debug() << "proxy::create() if=" << ifa->name();
|
||||||
|
|
||||||
return pr;
|
return pr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr<proxy> proxy::open(const std::string& ifname)
|
ptr<proxy> proxy::open(const std::string& ifname, bool promiscuous)
|
||||||
{
|
{
|
||||||
ptr<iface> ifa = iface::open_pfd(ifname);
|
ptr<iface> ifa = iface::open_pfd(ifname, promiscuous);
|
||||||
|
|
||||||
if (!ifa) {
|
if (!ifa) {
|
||||||
return ptr<proxy>();
|
return ptr<proxy>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return create(ifa);
|
return create(ifa, promiscuous);
|
||||||
}
|
}
|
||||||
|
|
||||||
void proxy::handle_solicit(const address& saddr, const address& daddr,
|
ptr<session> proxy::find_or_create_session(const address& taddr)
|
||||||
const address& taddr)
|
|
||||||
{
|
{
|
||||||
logger::debug()
|
|
||||||
<< "proxy::handle_solicit() saddr=" << saddr.to_string()
|
|
||||||
<< ", taddr=" << taddr.to_string();
|
|
||||||
|
|
||||||
// Let's check this proxy's list of sessions to see if we can
|
// Let's check this proxy's list of sessions to see if we can
|
||||||
// find one with the same target address.
|
// find one with the same target address.
|
||||||
|
|
||||||
for (std::list<ptr<session> >::iterator sit = _sessions.begin();
|
for (std::list<ptr<session> >::iterator sit = _sessions.begin();
|
||||||
sit != _sessions.end(); sit++) {
|
sit != _sessions.end(); sit++) {
|
||||||
|
|
||||||
if ((*sit)->taddr() == taddr) {
|
if ((*sit)->taddr() == taddr)
|
||||||
switch ((*sit)->status()) {
|
return (*sit);
|
||||||
case session::WAITING:
|
|
||||||
case session::INVALID:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case session::VALID:
|
|
||||||
(*sit)->send_advert();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
ptr<session> se;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since we couldn't find a session that matched, we'll try to find
|
// Since we couldn't find a session that matched, we'll try to find
|
||||||
// a matching rule instead, and then set up a new session.
|
// a matching rule instead, and then set up a new session.
|
||||||
|
|
||||||
ptr<session> se;
|
|
||||||
|
|
||||||
for (std::list<ptr<rule> >::iterator it = _rules.begin();
|
for (std::list<ptr<rule> >::iterator it = _rules.begin();
|
||||||
it != _rules.end(); it++) {
|
it != _rules.end(); it++) {
|
||||||
ptr<rule> ru = *it;
|
ptr<rule> ru = *it;
|
||||||
@ -100,7 +116,7 @@ void proxy::handle_solicit(const address& saddr, const address& daddr,
|
|||||||
|
|
||||||
if (ru->addr() == taddr) {
|
if (ru->addr() == taddr) {
|
||||||
if (!se) {
|
if (!se) {
|
||||||
se = session::create(_ptr, saddr, daddr, taddr);
|
se = session::create(_ptr, taddr, _autowire, _keepalive, _retries);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ru->is_auto()) {
|
if (ru->is_auto()) {
|
||||||
@ -111,30 +127,100 @@ void proxy::handle_solicit(const address& saddr, const address& daddr,
|
|||||||
} else {
|
} else {
|
||||||
ptr<iface> ifa = rt->ifa();
|
ptr<iface> ifa = rt->ifa();
|
||||||
|
|
||||||
if (ifa && (ifa != ru->ifa())) {
|
if (ifa && (ifa != ru->daughter())) {
|
||||||
se->add_iface(ifa);
|
se->add_iface(ifa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!ru->ifa()) {
|
} else if (!ru->daughter()) {
|
||||||
// This rule doesn't have an interface, and thus we'll consider
|
// This rule doesn't have an interface, and thus we'll consider
|
||||||
// it "static" and immediately send the response.
|
// it "static" and immediately send the response.
|
||||||
se->handle_advert();
|
se->handle_advert();
|
||||||
return;
|
return se;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
se->add_iface((*it)->ifa());
|
|
||||||
|
ptr<iface> ifa = ru->daughter();
|
||||||
|
se->add_iface(ifa);
|
||||||
|
|
||||||
|
#ifdef WITH_ND_NETLINK
|
||||||
|
if (if_addr_find(ifa->name(), &taddr.const_addr())) {
|
||||||
|
logger::debug() << "Sending NA out " << ifa->name();
|
||||||
|
se->add_iface(_ifa);
|
||||||
|
se->handle_advert();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (se) {
|
if (se) {
|
||||||
_sessions.push_back(se);
|
_sessions.push_back(se);
|
||||||
se->send_solicit();
|
}
|
||||||
|
|
||||||
|
return se;
|
||||||
|
}
|
||||||
|
|
||||||
|
void proxy::handle_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via)
|
||||||
|
{
|
||||||
|
// If a session exists then process the advert in the context of the session
|
||||||
|
for (std::list<ptr<session> >::iterator s_it = _sessions.begin();
|
||||||
|
s_it != _sessions.end(); s_it++)
|
||||||
|
{
|
||||||
|
const ptr<session> sess = *s_it;
|
||||||
|
|
||||||
|
if ((sess->taddr() == taddr)) {
|
||||||
|
sess->handle_advert(saddr, ifname, use_via);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr<rule> proxy::add_rule(const address& addr, const ptr<iface>& ifa)
|
void proxy::handle_stateless_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via)
|
||||||
|
{
|
||||||
|
logger::debug()
|
||||||
|
<< "proxy::handle_stateless_advert() proxy=" << (ifa() ? ifa()->name() : "null") << ", taddr=" << taddr.to_string() << ", ifname=" << ifname;
|
||||||
|
|
||||||
|
ptr<session> se = find_or_create_session(taddr);
|
||||||
|
if (!se) return;
|
||||||
|
|
||||||
|
if (_autowire == true && se->status() == session::WAITING) {
|
||||||
|
se->handle_auto_wire(saddr, ifname, use_via);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void proxy::handle_solicit(const address& saddr, const address& taddr, const std::string& ifname)
|
||||||
|
{
|
||||||
|
logger::debug()
|
||||||
|
<< "proxy::handle_solicit()";
|
||||||
|
|
||||||
|
// Otherwise find or create a session to scan for this address
|
||||||
|
ptr<session> se = find_or_create_session(taddr);
|
||||||
|
if (!se) return;
|
||||||
|
|
||||||
|
// Touching the session will cause an NDP advert to be transmitted to all
|
||||||
|
// the daughters
|
||||||
|
se->touch();
|
||||||
|
|
||||||
|
// If our session is confirmed then we can respoond with an advert otherwise
|
||||||
|
// subscribe so that if it does become active we can notify everyone
|
||||||
|
if (saddr != taddr) {
|
||||||
|
switch (se->status()) {
|
||||||
|
case session::WAITING:
|
||||||
|
case session::INVALID:
|
||||||
|
se->add_pending(saddr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case session::VALID:
|
||||||
|
case session::RENEWING:
|
||||||
|
se->send_advert(saddr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr<rule> proxy::add_rule(const address& addr, const ptr<iface>& ifa, bool autovia)
|
||||||
{
|
{
|
||||||
ptr<rule> ru(rule::create(_ptr, addr, ifa));
|
ptr<rule> ru(rule::create(_ptr, addr, ifa));
|
||||||
|
ru->autovia(autovia);
|
||||||
_rules.push_back(ru);
|
_rules.push_back(ru);
|
||||||
return ru;
|
return ru;
|
||||||
}
|
}
|
||||||
@ -146,6 +232,16 @@ ptr<rule> proxy::add_rule(const address& addr, bool aut)
|
|||||||
return ru;
|
return ru;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::list<ptr<rule> >::iterator proxy::rules_begin()
|
||||||
|
{
|
||||||
|
return _rules.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<ptr<rule> >::iterator proxy::rules_end()
|
||||||
|
{
|
||||||
|
return _rules.end();
|
||||||
|
}
|
||||||
|
|
||||||
void proxy::remove_session(const ptr<session>& se)
|
void proxy::remove_session(const ptr<session>& se)
|
||||||
{
|
{
|
||||||
_sessions.remove(se);
|
_sessions.remove(se);
|
||||||
@ -156,6 +252,11 @@ const ptr<iface>& proxy::ifa() const
|
|||||||
return _ifa;
|
return _ifa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool proxy::promiscuous() const
|
||||||
|
{
|
||||||
|
return _promiscuous;
|
||||||
|
}
|
||||||
|
|
||||||
bool proxy::router() const
|
bool proxy::router() const
|
||||||
{
|
{
|
||||||
return _router;
|
return _router;
|
||||||
@ -166,6 +267,36 @@ void proxy::router(bool val)
|
|||||||
_router = val;
|
_router = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool proxy::autowire() const
|
||||||
|
{
|
||||||
|
return _autowire;
|
||||||
|
}
|
||||||
|
|
||||||
|
void proxy::autowire(bool val)
|
||||||
|
{
|
||||||
|
_autowire = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
int proxy::retries() const
|
||||||
|
{
|
||||||
|
return _retries;
|
||||||
|
}
|
||||||
|
|
||||||
|
void proxy::retries(int val)
|
||||||
|
{
|
||||||
|
_retries = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool proxy::keepalive() const
|
||||||
|
{
|
||||||
|
return _keepalive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void proxy::keepalive(bool val)
|
||||||
|
{
|
||||||
|
_keepalive = val;
|
||||||
|
}
|
||||||
|
|
||||||
int proxy::ttl() const
|
int proxy::ttl() const
|
||||||
{
|
{
|
||||||
return _ttl;
|
return _ttl;
|
||||||
@ -176,6 +307,16 @@ void proxy::ttl(int val)
|
|||||||
_ttl = (val >= 0) ? val : 30000;
|
_ttl = (val >= 0) ? val : 30000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int proxy::deadtime() const
|
||||||
|
{
|
||||||
|
return _deadtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void proxy::deadtime(int val)
|
||||||
|
{
|
||||||
|
_deadtime = (val >= 0) ? val : 30000;
|
||||||
|
}
|
||||||
|
|
||||||
int proxy::timeout() const
|
int proxy::timeout() const
|
||||||
{
|
{
|
||||||
return _timeout;
|
return _timeout;
|
||||||
|
49
src/proxy.h
49
src/proxy.h
@ -30,25 +30,50 @@ class rule;
|
|||||||
|
|
||||||
class proxy {
|
class proxy {
|
||||||
public:
|
public:
|
||||||
static ptr<proxy> create(const ptr<iface>& ifa);
|
static ptr<proxy> create(const ptr<iface>& ifa, bool promiscuous);
|
||||||
|
|
||||||
static ptr<proxy> open(const std::string& ifn);
|
static ptr<proxy> find_aunt(const std::string& ifname, const address& taddr);
|
||||||
|
|
||||||
void handle_solicit(const address& saddr, const address& daddr,
|
static ptr<proxy> open(const std::string& ifn, bool promiscuous);
|
||||||
const address& taddr);
|
|
||||||
|
ptr<session> find_or_create_session(const address& taddr);
|
||||||
|
|
||||||
|
void handle_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via);
|
||||||
|
|
||||||
|
void handle_stateless_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via);
|
||||||
|
|
||||||
|
void handle_solicit(const address& saddr, const address& taddr, const std::string& ifname);
|
||||||
|
|
||||||
void remove_session(const ptr<session>& se);
|
void remove_session(const ptr<session>& se);
|
||||||
|
|
||||||
ptr<rule> add_rule(const address& addr, const ptr<iface>& ifa);
|
ptr<rule> add_rule(const address& addr, const ptr<iface>& ifa, bool autovia);
|
||||||
|
|
||||||
ptr<rule> add_rule(const address& addr, bool aut = false);
|
ptr<rule> add_rule(const address& addr, bool aut = false);
|
||||||
|
|
||||||
|
std::list<ptr<rule> >::iterator rules_begin();
|
||||||
|
|
||||||
|
std::list<ptr<rule> >::iterator rules_end();
|
||||||
|
|
||||||
const ptr<iface>& ifa() const;
|
const ptr<iface>& ifa() const;
|
||||||
|
|
||||||
|
bool promiscuous() const;
|
||||||
|
|
||||||
bool router() const;
|
bool router() const;
|
||||||
|
|
||||||
void router(bool val);
|
void router(bool val);
|
||||||
|
|
||||||
|
bool autowire() const;
|
||||||
|
|
||||||
|
void autowire(bool val);
|
||||||
|
|
||||||
|
int retries() const;
|
||||||
|
|
||||||
|
void retries(int val);
|
||||||
|
|
||||||
|
bool keepalive() const;
|
||||||
|
|
||||||
|
void keepalive(bool val);
|
||||||
|
|
||||||
int timeout() const;
|
int timeout() const;
|
||||||
|
|
||||||
void timeout(int val);
|
void timeout(int val);
|
||||||
@ -57,6 +82,10 @@ public:
|
|||||||
|
|
||||||
void ttl(int val);
|
void ttl(int val);
|
||||||
|
|
||||||
|
int deadtime() const;
|
||||||
|
|
||||||
|
void deadtime(int val);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::list<ptr<proxy> > _list;
|
static std::list<ptr<proxy> > _list;
|
||||||
|
|
||||||
@ -68,9 +97,17 @@ private:
|
|||||||
|
|
||||||
std::list<ptr<session> > _sessions;
|
std::list<ptr<session> > _sessions;
|
||||||
|
|
||||||
|
bool _promiscuous;
|
||||||
|
|
||||||
bool _router;
|
bool _router;
|
||||||
|
|
||||||
int _ttl, _timeout;
|
bool _autowire;
|
||||||
|
|
||||||
|
int _retries;
|
||||||
|
|
||||||
|
bool _keepalive;
|
||||||
|
|
||||||
|
int _ttl, _deadtime, _timeout;
|
||||||
|
|
||||||
proxy();
|
proxy();
|
||||||
};
|
};
|
||||||
|
@ -70,6 +70,8 @@ protected:
|
|||||||
|
|
||||||
void acquire(void* ptr)
|
void acquire(void* ptr)
|
||||||
{
|
{
|
||||||
|
if (!ptr) return;
|
||||||
|
|
||||||
_ref = new ptr_ref();
|
_ref = new ptr_ref();
|
||||||
_ref->ptr = (T*)ptr;
|
_ref->ptr = (T*)ptr;
|
||||||
_ref->wc = !!_weak;
|
_ref->wc = !!_weak;
|
||||||
|
@ -81,6 +81,8 @@ std::string route::token(const char* str)
|
|||||||
|
|
||||||
void route::load(const std::string& path)
|
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();
|
_routes.clear();
|
||||||
|
|
||||||
logger::debug() << "reading routes";
|
logger::debug() << "reading routes";
|
||||||
@ -103,19 +105,19 @@ void route::load(const std::string& path)
|
|||||||
|
|
||||||
unsigned char pfx;
|
unsigned char pfx;
|
||||||
|
|
||||||
if (hexdec(buf, (unsigned char* )&addr.addr(), 16) != 16) {
|
if (route::hexdec(buf, (unsigned char* )&addr.addr(), 16) != 16) {
|
||||||
// TODO: Warn here?
|
// TODO: Warn here?
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hexdec(buf + 33,& pfx, 1) != 1) {
|
if (route::hexdec(buf + 33,& pfx, 1) != 1) {
|
||||||
// TODO: Warn here?
|
// TODO: Warn here?
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr.prefix((int)pfx);
|
addr.prefix((int)pfx);
|
||||||
|
|
||||||
route::create(addr, token(buf + 141));
|
route::create(addr, route::token(buf + 141));
|
||||||
}
|
}
|
||||||
} catch (std::ifstream::failure e) {
|
} catch (std::ifstream::failure e) {
|
||||||
logger::warning() << "Failed to parse IPv6 routing data from '" << path << "'";
|
logger::warning() << "Failed to parse IPv6 routing data from '" << path << "'";
|
||||||
|
12
src/route.h
12
src/route.h
@ -45,6 +45,12 @@ public:
|
|||||||
|
|
||||||
ptr<iface> ifa();
|
ptr<iface> ifa();
|
||||||
|
|
||||||
|
route(const address& addr, const std::string& ifname);
|
||||||
|
|
||||||
|
static size_t hexdec(const char* str, unsigned char* buf, size_t size);
|
||||||
|
|
||||||
|
static std::string token(const char* str);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int _ttl;
|
static int _ttl;
|
||||||
|
|
||||||
@ -56,14 +62,8 @@ private:
|
|||||||
|
|
||||||
ptr<iface> _ifa;
|
ptr<iface> _ifa;
|
||||||
|
|
||||||
static size_t hexdec(const char* str, unsigned char* buf, size_t size);
|
|
||||||
|
|
||||||
static std::string token(const char* str);
|
|
||||||
|
|
||||||
static std::list<ptr<route> > _routes;
|
static std::list<ptr<route> > _routes;
|
||||||
|
|
||||||
route(const address& addr, const std::string& ifname);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
NDPPD_NS_END
|
NDPPD_NS_END
|
||||||
|
57
src/rule.cc
57
src/rule.cc
@ -16,6 +16,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
#include "ndppd.h"
|
#include "ndppd.h"
|
||||||
#include "rule.h"
|
#include "rule.h"
|
||||||
@ -24,6 +25,14 @@
|
|||||||
|
|
||||||
NDPPD_NS_BEGIN
|
NDPPD_NS_BEGIN
|
||||||
|
|
||||||
|
std::vector<interface> interfaces;
|
||||||
|
|
||||||
|
bool rule::_any_aut = false;
|
||||||
|
|
||||||
|
bool rule::_any_iface = false;
|
||||||
|
|
||||||
|
bool rule::_any_static = false;
|
||||||
|
|
||||||
rule::rule()
|
rule::rule()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -33,11 +42,22 @@ ptr<rule> rule::create(const ptr<proxy>& pr, const address& addr, const ptr<ifac
|
|||||||
ptr<rule> ru(new rule());
|
ptr<rule> ru(new rule());
|
||||||
ru->_ptr = ru;
|
ru->_ptr = ru;
|
||||||
ru->_pr = pr;
|
ru->_pr = pr;
|
||||||
ru->_ifa = ifa;
|
ru->_daughter = ifa;
|
||||||
ru->_addr = addr;
|
ru->_addr = addr;
|
||||||
ru->_aut = false;
|
ru->_aut = false;
|
||||||
|
_any_iface = true;
|
||||||
|
unsigned int ifindex;
|
||||||
|
|
||||||
logger::debug() << "rule::create() if=" << pr->ifa()->name() << ", addr=" << addr;
|
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;
|
||||||
|
|
||||||
return ru;
|
return ru;
|
||||||
}
|
}
|
||||||
@ -49,6 +69,10 @@ ptr<rule> rule::create(const ptr<proxy>& pr, const address& addr, bool aut)
|
|||||||
ru->_pr = pr;
|
ru->_pr = pr;
|
||||||
ru->_addr = addr;
|
ru->_addr = addr;
|
||||||
ru->_aut = aut;
|
ru->_aut = aut;
|
||||||
|
_any_aut = _any_aut || aut;
|
||||||
|
|
||||||
|
if (aut == false)
|
||||||
|
_any_static = true;
|
||||||
|
|
||||||
logger::debug()
|
logger::debug()
|
||||||
<< "rule::create() if=" << pr->ifa()->name().c_str() << ", addr=" << addr
|
<< "rule::create() if=" << pr->ifa()->name().c_str() << ", addr=" << addr
|
||||||
@ -62,9 +86,9 @@ const address& rule::addr() const
|
|||||||
return _addr;
|
return _addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr<iface> rule::ifa() const
|
ptr<iface> rule::daughter() const
|
||||||
{
|
{
|
||||||
return _ifa;
|
return _daughter;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rule::is_auto() const
|
bool rule::is_auto() const
|
||||||
@ -72,6 +96,31 @@ bool rule::is_auto() const
|
|||||||
return _aut;
|
return _aut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool rule::autovia() const
|
||||||
|
{
|
||||||
|
return _autovia;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rule::autovia(bool val)
|
||||||
|
{
|
||||||
|
_autovia = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rule::any_auto()
|
||||||
|
{
|
||||||
|
return _any_aut;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rule::any_iface()
|
||||||
|
{
|
||||||
|
return _any_iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rule::any_static()
|
||||||
|
{
|
||||||
|
return _any_static;
|
||||||
|
}
|
||||||
|
|
||||||
bool rule::check(const address& addr) const
|
bool rule::check(const address& addr) const
|
||||||
{
|
{
|
||||||
return _addr == addr;
|
return _addr == addr;
|
||||||
|
38
src/rule.h
38
src/rule.h
@ -18,6 +18,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
|
||||||
@ -36,24 +37,57 @@ public:
|
|||||||
|
|
||||||
const address& addr() const;
|
const address& addr() const;
|
||||||
|
|
||||||
ptr<iface> ifa() const;
|
ptr<iface> daughter() const;
|
||||||
|
|
||||||
bool is_auto() const;
|
bool is_auto() const;
|
||||||
|
|
||||||
bool check(const address& addr) 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:
|
private:
|
||||||
weak_ptr<rule> _ptr;
|
weak_ptr<rule> _ptr;
|
||||||
|
|
||||||
weak_ptr<proxy> _pr;
|
weak_ptr<proxy> _pr;
|
||||||
|
|
||||||
ptr<iface> _ifa;
|
ptr<iface> _daughter;
|
||||||
|
|
||||||
address _addr;
|
address _addr;
|
||||||
|
|
||||||
bool _aut;
|
bool _aut;
|
||||||
|
|
||||||
|
static bool _any_aut;
|
||||||
|
|
||||||
|
static bool _any_static;
|
||||||
|
|
||||||
|
static bool _any_iface;
|
||||||
|
|
||||||
|
bool _autovia;
|
||||||
|
|
||||||
rule();
|
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
|
NDPPD_NS_END
|
||||||
|
265
src/session.cc
265
src/session.cc
@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "ndppd.h"
|
#include "ndppd.h"
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
@ -24,6 +25,8 @@ NDPPD_NS_BEGIN
|
|||||||
|
|
||||||
std::list<weak_ptr<session> > session::_sessions;
|
std::list<weak_ptr<session> > session::_sessions;
|
||||||
|
|
||||||
|
static address all_nodes = address("ff02::1");
|
||||||
|
|
||||||
void session::update_all(int elapsed_time)
|
void session::update_all(int elapsed_time)
|
||||||
{
|
{
|
||||||
for (std::list<weak_ptr<session> >::iterator it = _sessions.begin();
|
for (std::list<weak_ptr<session> >::iterator it = _sessions.begin();
|
||||||
@ -40,10 +43,54 @@ void session::update_all(int elapsed_time)
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (se->_status) {
|
switch (se->_status) {
|
||||||
|
|
||||||
case session::WAITING:
|
case session::WAITING:
|
||||||
logger::debug() << "session is now invalid";
|
if (se->_fails < se->_retries) {
|
||||||
|
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->_status = session::INVALID;
|
||||||
se->_ttl = se->_pr->ttl();
|
se->_ttl = se->_pr->deadtime();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case session::RENEWING:
|
||||||
|
logger::debug() << "session is became invalid [taddr=" << se->_taddr << "]";
|
||||||
|
|
||||||
|
if (se->_fails < se->_retries) {
|
||||||
|
se->_ttl = se->_pr->timeout();
|
||||||
|
se->_fails++;
|
||||||
|
|
||||||
|
// Send another solicit
|
||||||
|
se->send_solicit();
|
||||||
|
} else {
|
||||||
|
se->_pr->remove_session(se);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case session::VALID:
|
||||||
|
if (se->touched() == true ||
|
||||||
|
se->keepalive() == true)
|
||||||
|
{
|
||||||
|
logger::debug() << "session is renewing [taddr=" << se->_taddr << "]";
|
||||||
|
se->_status = session::RENEWING;
|
||||||
|
se->_ttl = se->_pr->timeout();
|
||||||
|
se->_fails = 0;
|
||||||
|
se->_touched = false;
|
||||||
|
|
||||||
|
// Send another solicit to make sure the route is still valid
|
||||||
|
se->send_solicit();
|
||||||
|
} else {
|
||||||
|
se->_pr->remove_session(se);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -56,29 +103,33 @@ session::~session()
|
|||||||
{
|
{
|
||||||
logger::debug() << "session::~session() this=" << logger::format("%x", this);
|
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++) {
|
it != _ifaces.end(); it++) {
|
||||||
(*it)->remove_session(_ptr);
|
handle_auto_unwire((*it)->name());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr<session> session::create(const ptr<proxy>& pr, const address& saddr,
|
ptr<session> session::create(const ptr<proxy>& pr, const address& taddr, bool auto_wire, bool keepalive, int retries)
|
||||||
const address& daddr, const address& taddr)
|
|
||||||
{
|
{
|
||||||
ptr<session> se(new session());
|
ptr<session> se(new session());
|
||||||
|
|
||||||
se->_ptr = se;
|
se->_ptr = se;
|
||||||
se->_pr = pr;
|
se->_pr = pr;
|
||||||
se->_saddr = saddr;
|
|
||||||
se->_taddr = taddr;
|
se->_taddr = taddr;
|
||||||
se->_daddr = daddr;
|
se->_autowire = auto_wire;
|
||||||
se->_ttl = pr->timeout();
|
se->_keepalive = keepalive;
|
||||||
|
se->_retries = retries;
|
||||||
|
se->_wired = false;
|
||||||
|
se->_ttl = pr->ttl();
|
||||||
|
se->_touched = false;
|
||||||
|
|
||||||
_sessions.push_back(se);
|
_sessions.push_back(se);
|
||||||
|
|
||||||
logger::debug()
|
logger::debug()
|
||||||
<< "session::create() pr=" << logger::format("%x", (proxy* )pr) << ", saddr=" << saddr
|
<< "session::create() pr=" << logger::format("%x", (proxy* )pr) << ", proxy=" << ((pr->ifa()) ? pr->ifa()->name() : "null")
|
||||||
<< ", daddr=" << daddr << ", taddr=" << taddr << " =" << logger::format("%x", (session* )se);
|
<< ", taddr=" << taddr << " =" << logger::format("%x", (session* )se);
|
||||||
|
|
||||||
return se;
|
return se;
|
||||||
}
|
}
|
||||||
@ -88,10 +139,19 @@ void session::add_iface(const ptr<iface>& ifa)
|
|||||||
if (std::find(_ifaces.begin(), _ifaces.end(), ifa) != _ifaces.end())
|
if (std::find(_ifaces.begin(), _ifaces.end(), ifa) != _ifaces.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ifa->add_session(_ptr);
|
|
||||||
_ifaces.push_back(ifa);
|
_ifaces.push_back(ifa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void session::add_pending(const address& addr)
|
||||||
|
{
|
||||||
|
for (std::list<ptr<address> >::iterator ad = _pending.begin(); ad != _pending.end(); ad++) {
|
||||||
|
if (addr == (*ad))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pending.push_back(new address(addr));
|
||||||
|
}
|
||||||
|
|
||||||
void session::send_solicit()
|
void session::send_solicit()
|
||||||
{
|
{
|
||||||
logger::debug() << "session::send_solicit() (_ifaces.size() = " << _ifaces.size() << ")";
|
logger::debug() << "session::send_solicit() (_ifaces.size() = " << _ifaces.size() << ")";
|
||||||
@ -103,17 +163,162 @@ void session::send_solicit()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void session::send_advert()
|
void session::touch()
|
||||||
{
|
{
|
||||||
_pr->ifa()->write_advert(_saddr, _taddr, _pr->router());
|
if (_touched == false)
|
||||||
|
{
|
||||||
|
_touched = true;
|
||||||
|
|
||||||
|
if (status() == session::WAITING || status() == session::INVALID) {
|
||||||
|
_ttl = _pr->timeout();
|
||||||
|
|
||||||
|
logger::debug() << "session is now probing [taddr=" << _taddr << "]";
|
||||||
|
|
||||||
|
send_solicit();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void session::send_advert(const address& daddr)
|
||||||
|
{
|
||||||
|
_pr->ifa()->write_advert(daddr, _taddr, _pr->router());
|
||||||
|
}
|
||||||
|
|
||||||
|
void session::handle_auto_wire(const address& saddr, const std::string& ifname, bool use_via)
|
||||||
|
{
|
||||||
|
if (_wired == true && (_wired_via.is_empty() || _wired_via == saddr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
logger::debug()
|
||||||
|
<< "session::handle_auto_wire() taddr=" << _taddr << ", ifname=" << ifname;
|
||||||
|
|
||||||
|
if (use_via == true &&
|
||||||
|
_taddr != saddr &&
|
||||||
|
saddr.is_unicast() == true &&
|
||||||
|
saddr.is_multicast() == false)
|
||||||
|
{
|
||||||
|
std::stringstream route_cmd;
|
||||||
|
route_cmd << "ip";
|
||||||
|
route_cmd << " " << "-6";
|
||||||
|
route_cmd << " " << "route";
|
||||||
|
route_cmd << " " << "replace";
|
||||||
|
route_cmd << " " << std::string(saddr);
|
||||||
|
route_cmd << " " << "dev";
|
||||||
|
route_cmd << " " << ifname;
|
||||||
|
|
||||||
|
logger::debug()
|
||||||
|
<< "session::system(" << route_cmd.str() << ")";
|
||||||
|
|
||||||
|
system(route_cmd.str().c_str());
|
||||||
|
|
||||||
|
_wired_via = saddr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_wired_via.reset();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::stringstream route_cmd;
|
||||||
|
route_cmd << "ip";
|
||||||
|
route_cmd << " " << "-6";
|
||||||
|
route_cmd << " " << "route";
|
||||||
|
route_cmd << " " << "replace";
|
||||||
|
route_cmd << " " << std::string(_taddr);
|
||||||
|
if (_wired_via.is_empty() == false) {
|
||||||
|
route_cmd << " " << "via";
|
||||||
|
route_cmd << " " << std::string(_wired_via);
|
||||||
|
}
|
||||||
|
route_cmd << " " << "dev";
|
||||||
|
route_cmd << " " << ifname;
|
||||||
|
|
||||||
|
logger::debug()
|
||||||
|
<< "session::system(" << route_cmd.str() << ")";
|
||||||
|
|
||||||
|
system(route_cmd.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
_wired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void session::handle_auto_unwire(const std::string& ifname)
|
||||||
|
{
|
||||||
|
logger::debug()
|
||||||
|
<< "session::handle_auto_unwire() taddr=" << _taddr << ", ifname=" << ifname;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::stringstream route_cmd;
|
||||||
|
route_cmd << "ip";
|
||||||
|
route_cmd << " " << "-6";
|
||||||
|
route_cmd << " " << "route";
|
||||||
|
route_cmd << " " << "flush";
|
||||||
|
route_cmd << " " << std::string(_taddr);
|
||||||
|
if (_wired_via.is_empty() == false) {
|
||||||
|
route_cmd << " " << "via";
|
||||||
|
route_cmd << " " << std::string(_wired_via);
|
||||||
|
}
|
||||||
|
route_cmd << " " << "dev";
|
||||||
|
route_cmd << " " << ifname;
|
||||||
|
|
||||||
|
logger::debug()
|
||||||
|
<< "session::system(" << route_cmd.str() << ")";
|
||||||
|
|
||||||
|
system(route_cmd.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_wired_via.is_empty() == false) {
|
||||||
|
std::stringstream route_cmd;
|
||||||
|
route_cmd << "ip";
|
||||||
|
route_cmd << " " << "-6";
|
||||||
|
route_cmd << " " << "route";
|
||||||
|
route_cmd << " " << "flush";
|
||||||
|
route_cmd << " " << std::string(_wired_via);
|
||||||
|
route_cmd << " " << "dev";
|
||||||
|
route_cmd << " " << ifname;
|
||||||
|
|
||||||
|
logger::debug()
|
||||||
|
<< "session::system(" << route_cmd.str() << ")";
|
||||||
|
|
||||||
|
system(route_cmd.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
_wired = false;
|
||||||
|
_wired_via.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void session::handle_advert(const address& saddr, const std::string& ifname, bool use_via)
|
||||||
|
{
|
||||||
|
if (_autowire == true && _status == WAITING) {
|
||||||
|
handle_auto_wire(saddr, ifname, use_via);
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_advert();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void session::handle_advert()
|
void session::handle_advert()
|
||||||
{
|
{
|
||||||
_status = VALID;
|
logger::debug()
|
||||||
_ttl = _pr->ttl();
|
<< "session::handle_advert() taddr=" << _taddr << ", ttl=" << _pr->ttl();
|
||||||
|
|
||||||
send_advert();
|
if (_status != VALID) {
|
||||||
|
_status = VALID;
|
||||||
|
|
||||||
|
logger::debug() << "session is active [taddr=" << _taddr << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
_ttl = _pr->ttl();
|
||||||
|
_fails = 0;
|
||||||
|
|
||||||
|
if (!_pending.empty()) {
|
||||||
|
for (std::list<ptr<address> >::iterator ad = _pending.begin();
|
||||||
|
ad != _pending.end(); ad++) {
|
||||||
|
ptr<address> addr = (*ad);
|
||||||
|
logger::debug() << " - forward to " << addr;
|
||||||
|
|
||||||
|
send_advert(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pending.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const address& session::taddr() const
|
const address& session::taddr() const
|
||||||
@ -121,14 +326,34 @@ const address& session::taddr() const
|
|||||||
return _taddr;
|
return _taddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const address& session::saddr() const
|
bool session::autowire() const
|
||||||
{
|
{
|
||||||
return _saddr;
|
return _autowire;
|
||||||
}
|
}
|
||||||
|
|
||||||
const address& session::daddr() const
|
bool session::keepalive() const
|
||||||
{
|
{
|
||||||
return _daddr;
|
return _keepalive;
|
||||||
|
}
|
||||||
|
|
||||||
|
int session::retries() const
|
||||||
|
{
|
||||||
|
return _retries;
|
||||||
|
}
|
||||||
|
|
||||||
|
int session::fails() const
|
||||||
|
{
|
||||||
|
return _fails;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool session::wired() const
|
||||||
|
{
|
||||||
|
return _wired;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool session::touched() const
|
||||||
|
{
|
||||||
|
return _touched;
|
||||||
}
|
}
|
||||||
|
|
||||||
int session::status() const
|
int session::status() const
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "ndppd.h"
|
#include "ndppd.h"
|
||||||
|
|
||||||
@ -32,14 +33,30 @@ private:
|
|||||||
|
|
||||||
address _saddr, _daddr, _taddr;
|
address _saddr, _daddr, _taddr;
|
||||||
|
|
||||||
|
bool _autowire;
|
||||||
|
|
||||||
|
bool _keepalive;
|
||||||
|
|
||||||
|
bool _wired;
|
||||||
|
|
||||||
|
address _wired_via;
|
||||||
|
|
||||||
|
bool _touched;
|
||||||
|
|
||||||
// An array of interfaces this session is monitoring for
|
// An array of interfaces this session is monitoring for
|
||||||
// ND_NEIGHBOR_ADVERT on.
|
// ND_NEIGHBOR_ADVERT on.
|
||||||
std::list<ptr<iface> > _ifaces;
|
std::list<ptr<iface> > _ifaces;
|
||||||
|
|
||||||
|
std::list<ptr<address> > _pending;
|
||||||
|
|
||||||
// The remaining time in miliseconds the object will stay in the
|
// The remaining time in miliseconds the object will stay in the
|
||||||
// interface's session array or cache.
|
// interface's session array or cache.
|
||||||
int _ttl;
|
int _ttl;
|
||||||
|
|
||||||
|
int _fails;
|
||||||
|
|
||||||
|
int _retries;
|
||||||
|
|
||||||
int _status;
|
int _status;
|
||||||
|
|
||||||
static std::list<weak_ptr<session> > _sessions;
|
static std::list<weak_ptr<session> > _sessions;
|
||||||
@ -48,6 +65,7 @@ public:
|
|||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
WAITING, // Waiting for an advert response.
|
WAITING, // Waiting for an advert response.
|
||||||
|
RENEWING, // Renewing;
|
||||||
VALID, // Valid;
|
VALID, // Valid;
|
||||||
INVALID // Invalid;
|
INVALID // Invalid;
|
||||||
};
|
};
|
||||||
@ -57,24 +75,45 @@ public:
|
|||||||
// Destructor.
|
// Destructor.
|
||||||
~session();
|
~session();
|
||||||
|
|
||||||
static ptr<session> create(const ptr<proxy>& pr, const address& saddr,
|
static ptr<session> create(const ptr<proxy>& pr, const address& taddr, bool autowire, bool keepalive, int retries);
|
||||||
const address& daddr, const address& taddr);
|
|
||||||
|
|
||||||
void add_iface(const ptr<iface>& ifa);
|
void add_iface(const ptr<iface>& ifa);
|
||||||
|
|
||||||
|
void add_pending(const address& addr);
|
||||||
|
|
||||||
const address& taddr() const;
|
const address& taddr() const;
|
||||||
|
|
||||||
const address& daddr() const;
|
const address& daddr() const;
|
||||||
|
|
||||||
const address& saddr() const;
|
const address& saddr() const;
|
||||||
|
|
||||||
|
bool autowire() const;
|
||||||
|
|
||||||
|
int retries() const;
|
||||||
|
|
||||||
|
int fails() const;
|
||||||
|
|
||||||
|
bool keepalive() const;
|
||||||
|
|
||||||
|
bool wired() const;
|
||||||
|
|
||||||
|
bool touched() const;
|
||||||
|
|
||||||
int status() const;
|
int status() const;
|
||||||
|
|
||||||
void status(int val);
|
void status(int val);
|
||||||
|
|
||||||
void handle_advert();
|
void handle_advert();
|
||||||
|
|
||||||
void send_advert();
|
void handle_advert(const address& saddr, const std::string& ifname, bool use_via);
|
||||||
|
|
||||||
|
void handle_auto_wire(const address& saddr, const std::string& ifname, bool use_via);
|
||||||
|
|
||||||
|
void handle_auto_unwire(const std::string& ifname);
|
||||||
|
|
||||||
|
void touch();
|
||||||
|
|
||||||
|
void send_advert(const address& daddr);
|
||||||
|
|
||||||
void send_solicit();
|
void send_solicit();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user