#include <signal.h>
#include <errno.h>
+#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/ethernet.h>
static void handle_solicit(void *addr, void *data, size_t len,
- struct interface *iface);
+ struct interface *iface, void *dest);
static void handle_rtnetlink(void *addr, void *data, size_t len,
- struct interface *iface);
+ struct interface *iface, void *dest);
static struct ndp_neighbor* find_neighbor(struct in6_addr *addr, bool strict);
static void modify_neighbor(struct in6_addr *addr, struct interface *iface,
bool add);
send(rtnl_event.uloop.fd, &req2, sizeof(req2), MSG_DONTWAIT);
odhcpd_register(&rtnl_event);
-
- // Create socket for intercepting NDP
- int sock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- htons(ETH_P_ALL)); // ETH_P_ALL for ingress + egress
- if (sock < 0) {
- syslog(LOG_ERR, "Unable to open packet socket: %s",
- strerror(errno));
- return -1;
- }
-
- if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
- &bpf_prog, sizeof(bpf_prog))) {
- syslog(LOG_ERR, "Failed to set BPF: %s", strerror(errno));
- return -1;
- }
-
- ndp_event.uloop.fd = sock;
- odhcpd_register(&ndp_event);
-
// Open ICMPv6 socket
ping_socket = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
+ if (ping_socket < 0) {
+ syslog(LOG_ERR, "Unable to open raw socket: %s", strerror(errno));
+ return -1;
+ }
int val = 2;
setsockopt(ping_socket, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
memcpy(entry, iface->static_ndp, iface->static_ndp_len);
for (entry = strtok_r(entry, " ", &saveptr); entry; entry = strtok_r(NULL, " ", &saveptr)) {
+ char *sep;
struct ndp_neighbor *n = malloc(sizeof(*n));
if (!n) {
syslog(LOG_ERR, "Malloc failed for static NDP-prefix %s", entry);
n->iface = iface;
n->timeout = 0;
- char ipbuf[INET6_ADDRSTRLEN];
- if (sscanf(entry, "%45s/%hhu", ipbuf, &n->len) < 2
- || n->len > 128 || inet_pton(AF_INET6, ipbuf, &n->addr) != 1) {
+ sep = strchr(entry, '/');
+ if (!sep) {
+ free(n);
syslog(LOG_ERR, "Invalid static NDP-prefix %s", entry);
return -1;
}
+
+ *sep = 0;
+ n->len = atoi(sep + 1);
+ if (inet_pton(AF_INET6, entry, &n->addr) != 1 || n->len > 128) {
+ free(n);
+ syslog(LOG_ERR, "Invalid static NDP-prefix %s/%s", entry, sep + 1);
+ return -1;
+ }
list_add(&n->head, &neighbors);
}
}
}
+ bool enable_packet = false;
+ struct interface *i;
+ list_for_each_entry(i, &interfaces, head) {
+ if (i == iface && !enable)
+ continue;
+
+ if (i->ndp == RELAYD_RELAY)
+ enable_packet = true;
+ }
+
+ if (enable_packet && ndp_event.uloop.fd < 0) {
+ // Create socket for intercepting NDP
+ int sock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ htons(ETH_P_ALL)); // ETH_P_ALL for ingress + egress
+ if (sock < 0) {
+ syslog(LOG_ERR, "Unable to open packet socket: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
+ &bpf_prog, sizeof(bpf_prog))) {
+ syslog(LOG_ERR, "Failed to set BPF: %s", strerror(errno));
+ return -1;
+ }
+
+ ndp_event.uloop.fd = sock;
+ odhcpd_register(&ndp_event);
+ } else if (!enable_packet && ndp_event.uloop.fd >= 0) {
+ close(ndp_event.uloop.fd);
+ ndp_event.uloop.fd = -1;
+ }
+
return 0;
}
// Handle solicitations
static void handle_solicit(void *addr, void *data, size_t len,
- struct interface *iface)
+ struct interface *iface, _unused void *dest)
{
struct ip6_hdr *ip6 = data;
struct nd_neighbor_solicit *req = (struct nd_neighbor_solicit*)&ip6[1];
char ipbuf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &req->nd_ns_target, ipbuf, sizeof(ipbuf));
- syslog(LOG_NOTICE, "Got a NS for %s", ipbuf);
+ syslog(LOG_DEBUG, "Got a NS for %s", ipbuf);
uint8_t mac[6];
odhcpd_get_mac(iface, mac);
struct ndp_neighbor *n = find_neighbor(&req->nd_ns_target, false);
if (n && (n->iface || abs(n->timeout - now) < 5)) {
- syslog(LOG_NOTICE, "%s is on %s", ipbuf,
+ syslog(LOG_DEBUG, "%s is on %s", ipbuf,
(n->iface) ? n->iface->ifname : "<pending>");
if (!n->iface || n->iface == iface)
return;
--neighbor_count;
}
-
-static bool match_neighbor(struct ndp_neighbor *n, struct in6_addr *addr)
-{
- if (n->len <= 32)
- return ntohl(n->addr.s6_addr32[0]) >> (32 - n->len) ==
- ntohl(addr->s6_addr32[0]) >> (32 - n->len);
-
- if (n->addr.s6_addr32[0] != addr->s6_addr32[0])
- return false;
-
- if (n->len <= 64)
- return ntohl(n->addr.s6_addr32[1]) >> (64 - n->len) ==
- ntohl(addr->s6_addr32[1]) >> (64 - n->len);
-
- if (n->addr.s6_addr32[1] != addr->s6_addr32[1])
- return false;
-
- if (n->len <= 96)
- return ntohl(n->addr.s6_addr32[2]) >> (96 - n->len) ==
- ntohl(addr->s6_addr32[2]) >> (96 - n->len);
-
- if (n->addr.s6_addr32[2] != addr->s6_addr32[2])
- return false;
-
- return ntohl(n->addr.s6_addr32[3]) >> (128 - n->len) ==
- ntohl(addr->s6_addr32[3]) >> (128 - n->len);
-}
-
-
static struct ndp_neighbor* find_neighbor(struct in6_addr *addr, bool strict)
{
time_t now = time(NULL);
struct ndp_neighbor *n, *e;
list_for_each_entry_safe(n, e, &neighbors, head) {
- if ((!strict && match_neighbor(n, addr)) ||
+ if ((!strict && !odhcpd_bmemcmp(&n->addr, addr, n->len)) ||
(n->len == 128 && IN6_ARE_ADDR_EQUAL(&n->addr, addr)))
return n;
// Handler for neighbor cache entries from the kernel. This is our source
// to learn and unlearn hosts on interfaces.
static void handle_rtnetlink(_unused void *addr, void *data, size_t len,
- _unused struct interface *iface)
+ _unused struct interface *iface, _unused void *dest)
{
for (struct nlmsghdr *nh = data; NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len)) {
/* TODO: See if this is required for optimal operation
// Keep neighbor entries alive so we don't loose routes
+ */
if (add && (ndm->ndm_state & NUD_STALE))
ping6(addr, iface);
- */
}
}