Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

send NA using raw ETH_P_IPV6 packet API #86

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ PKG_CONFIG ?= pkg-config


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/solicitor.o src/conf.o src/route.o src/cksum.o

ifdef WITH_ND_NETLINK
LIBS = `${PKG_CONFIG} --libs glib-2.0 libnl-3.0 libnl-route-3.0` -pthread
Expand Down
4 changes: 2 additions & 2 deletions src/address.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ class address {

struct in6_addr& mask();

// Compare _a/_m against a._a.
/// Compare _a/_m against a._a. NON-PERMUTATIVE!
bool operator==(const address& addr) const;

/// NON-PERMUTATIVE!
bool operator!=(const address& addr) const;

void reset();
Expand Down
47 changes: 47 additions & 0 deletions src/cksum.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// ndppd - NDP Proxy Daemon
// Copyright (C) 2011 Daniel Adolfsson <[email protected]>
//
// 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/>.
//
// file author: Sergey E. Kolesnikov <[email protected]>
#include <cstdint>
#include <netinet/ip6.h>
#include "cksum.h"

NDPPD_NS_BEGIN

uint_fast32_t partial_sum(const void* data, int size)
{
const uint16_t* wend= (const uint16_t*) data + size / 2;
uint_fast32_t accum = 0;
for (const uint16_t* wdata=(const uint16_t*) data; wdata < wend; wdata++) {
accum += *wdata;
}
if (size & 1) {
accum += *((const uint8_t*) data + size - 1);
}
return accum;
}

uint16_t icmp6_sum(const ip6_hdr* hdr, const void* payload, int size)
{
uint_fast32_t accum = partial_sum(payload, size);
accum += partial_sum(&hdr->ip6_src, sizeof(struct in6_addr));
accum += partial_sum(&hdr->ip6_dst, sizeof(struct in6_addr));
accum += hdr->ip6_plen;
accum += htons(hdr->ip6_nxt); // calculated as 16-bit network order word
return ~((accum >> 16) + (accum & 0xffff));
}

NDPPD_NS_END
27 changes: 27 additions & 0 deletions src/cksum.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// ndppd - NDP Proxy Daemon
// Copyright (C) 2011 Daniel Adolfsson <[email protected]>
//
// 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/>.
//
// file author: Sergey E. Kolesnikov <[email protected]>
#pragma once
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include "ndppd.h"

NDPPD_NS_BEGIN

uint16_t icmp6_sum(const struct ip6_hdr* hdr, const void* payload, int size);

NDPPD_NS_END
65 changes: 52 additions & 13 deletions src/iface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

#include "ndppd.h"
#include "route.h"
#include "cksum.h"

NDPPD_NS_BEGIN

Expand Down Expand Up @@ -367,7 +368,7 @@ ssize_t iface::write(int fd, const address& daddr, const uint8_t* msg, size_t si
return len;
}

ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr)
ssize_t iface::read_solicit(ether_addr& shwaddr, address& saddr, address& daddr, address& taddr)
{
struct sockaddr_ll t_saddr;
uint8_t msg[256];
Expand All @@ -378,6 +379,8 @@ ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr)
return -1;
}

struct ether_header* ethh = (struct ether_header*) msg;

struct ip6_hdr* ip6h =
(struct ip6_hdr* )(msg + ETH_HLEN);

Expand All @@ -387,13 +390,21 @@ ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr)
taddr = ns->nd_ns_target;
daddr = ip6h->ip6_dst;
saddr = ip6h->ip6_src;
memcpy(&shwaddr, ethh->ether_shost, ETH_ALEN);

// Ignore packets sent from this machine
if (iface::is_local(saddr) == true) {
return 0;
}

char shwaddr_s[18];
char dhwaddr_s[18];
ether_ntoa_r(&shwaddr, shwaddr_s);
ether_ntoa_r((struct ether_addr*) &ethh->ether_dhost, dhwaddr_s);

logger::debug() << "iface::read_solicit() saddr=" << saddr.to_string()
<< ", shwaddr=" << shwaddr_s
<< ", dhwaddr=" << dhwaddr_s
<< ", daddr=" << daddr.to_string() << ", taddr=" << taddr.to_string() << ", len=" << len;

return len;
Expand Down Expand Up @@ -438,35 +449,62 @@ ssize_t iface::write_solicit(const address& taddr)
return write(_ifd, daddr, (uint8_t* )buf, sizeof(struct nd_neighbor_solicit)
+ sizeof(struct nd_opt_hdr) + 6);
}
const uint16_t plen = htons(0x20);
const uint16_t ether_type_ipv6 = htons(ETHERTYPE_IPV6);

ssize_t iface::write_advert(const address& daddr, const address& taddr, bool router)
ssize_t iface::write_advert(const ether_addr& dhwaddr, const address& daddr, const address& taddr, bool router)
{
char buf[128];
// buffer layout:
// 0x00 - ether_hdr
// 0x0e - ipv6_hdr
// 0x36 - nd_neighbor_advert

memset(buf, 0, sizeof(buf));

ether_header* ethh = (ether_header*) &buf[0];
ip6_hdr* ip6h = (ip6_hdr*) &buf[ETH_HLEN];
struct nd_neighbor_advert* na =
(struct nd_neighbor_advert* )&buf[0];
(struct nd_neighbor_advert*) &buf[ETH_HLEN + sizeof(ip6_hdr)];

struct nd_opt_hdr* opt =
(struct nd_opt_hdr* )&buf[sizeof(struct nd_neighbor_advert)];
(struct nd_opt_hdr* )&buf[ETH_HLEN + sizeof(ip6_hdr) + sizeof(struct nd_neighbor_advert)];

// cook packet
// ETHER
memcpy(&ethh->ether_shost, &this->hwaddr, ETH_ALEN);
memcpy(&ethh->ether_dhost, &dhwaddr, ETH_ALEN);
ethh->ether_type = ether_type_ipv6;

// IPv6 header
memcpy(&ip6h->ip6_src, &taddr.const_addr(), sizeof (in6_addr));
memcpy(&ip6h->ip6_dst, &daddr.const_addr(), sizeof (in6_addr));
ip6h->ip6_plen = plen;
ip6h->ip6_nxt = IPPROTO_ICMPV6;
ip6h->ip6_hops = 255;
ip6h->ip6_vfc = 0x60;

// Neighbor advert header

opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
opt->nd_opt_len = 1;

na->nd_na_type = ND_NEIGHBOR_ADVERT;
na->nd_na_flags_reserved = (daddr.is_multicast() ? 0 : ND_NA_FLAG_SOLICITED) | (router ? ND_NA_FLAG_ROUTER : 0);
na->nd_na_cksum = 0; // will set later

memcpy(&na->nd_na_target,& taddr.const_addr(), sizeof(struct in6_addr));

memcpy(buf + sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr),
memcpy(buf + ETH_HLEN + sizeof(ip6_hdr) + sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr),
&hwaddr, 6);
na->nd_na_cksum = icmp6_sum(ip6h, na, sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr) + 6);

logger::debug() << "iface::write_advert() daddr=" << daddr.to_string()
<< ", taddr=" << taddr.to_string();
<< ", taddr=" << taddr.to_string()
<< ", dhwaddr=" << ether_ntoa(&dhwaddr);

return write(_ifd, daddr, (uint8_t* )buf, sizeof(struct nd_neighbor_advert) +
sizeof(struct nd_opt_hdr) + 6);
return send(_pfd, buf, ETH_HLEN + sizeof (ip6_hdr) + sizeof(nd_neighbor_advert) +
sizeof(nd_opt_hdr) + 6, 0);
}

ssize_t iface::read_advert(address& saddr, address& taddr)
Expand Down Expand Up @@ -513,7 +551,7 @@ bool iface::is_local(const address& addr)
return false;
}

bool iface::handle_local(const address& saddr, const address& taddr)
bool iface::handle_local(const ether_addr& shwaddr, 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
Expand All @@ -532,7 +570,7 @@ bool iface::handle_local(const address& saddr, const address& taddr)
if (ru->daughter() && ru->daughter()->name() == (*ad)->ifname())
{
logger::debug() << "proxy::handle_solicit() found local taddr=" << taddr;
write_advert(saddr, taddr, false);
write_advert(shwaddr, saddr, taddr, false);
return true;
}
}
Expand Down Expand Up @@ -659,10 +697,11 @@ int iface::poll_all()
}

address saddr, daddr, taddr;
ether_addr shwaddr;
ssize_t size;

if (is_pfd) {
size = ifa->read_solicit(saddr, daddr, taddr);
size = ifa->read_solicit(shwaddr, saddr, daddr, taddr);
if (size < 0) {
logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str();
continue;
Expand All @@ -673,7 +712,7 @@ int iface::poll_all()
}

// Process any local addresses for interfaces that we are proxying
if (ifa->handle_local(saddr, taddr) == true) {
if (ifa->handle_local(shwaddr, saddr, taddr)) {
continue;
}

Expand All @@ -692,7 +731,7 @@ int iface::poll_all()
// 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());
pr->handle_solicit(shwaddr, saddr, taddr, ifa->name());
}

// If it was not handled then write an error message
Expand Down
8 changes: 4 additions & 4 deletions src/iface.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ class iface {
ssize_t write_solicit(const address& taddr);

// Writes a NB_NEIGHBOR_ADVERT message to the _ifd socket;
ssize_t write_advert(const address& daddr, const address& taddr, bool router);
ssize_t write_advert(const ether_addr& dhwaddr, const address& daddr, const address& taddr, bool router);

// Reads a NB_NEIGHBOR_SOLICIT message from the _pfd socket.
ssize_t read_solicit(address& saddr, address& daddr, address& taddr);
ssize_t read_solicit(ether_addr& shwaddr, address& saddr, address& daddr, address& taddr);

// Reads a NB_NEIGHBOR_ADVERT message from the _ifd socket;
ssize_t read_advert(address& saddr, address& taddr);
bool handle_local(const address& saddr, const address& taddr);

bool handle_local(const ether_addr& shwaddr, const address& saddr, const address& taddr);

bool is_local(const address& addr);

Expand Down
6 changes: 3 additions & 3 deletions src/proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ void proxy::handle_stateless_advert(const address& saddr, const address& taddr,
}
}

void proxy::handle_solicit(const address& saddr, const address& taddr, const std::string& ifname)
void proxy::handle_solicit(const ether_addr& shwaddr, const address& saddr, const address& taddr, const std::string& ifname)
{
logger::debug()
<< "proxy::handle_solicit()";
Expand All @@ -206,12 +206,12 @@ void proxy::handle_solicit(const address& saddr, const address& taddr, const std
switch (se->status()) {
case session::WAITING:
case session::INVALID:
se->add_pending(saddr);
se->add_pending(shwaddr, saddr);
break;

case session::VALID:
case session::RENEWING:
se->send_advert(saddr);
se->send_advert(shwaddr, saddr);
break;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ class proxy {
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 handle_solicit(const ether_addr& shwaddr, const address& saddr, const address& taddr, const std::string& ifname);

void remove_session(const ptr<session>& se);

Expand Down
20 changes: 10 additions & 10 deletions src/session.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,14 @@ void session::add_iface(const ptr<iface>& ifa)
_ifaces.push_back(ifa);
}

void session::add_pending(const address& addr)
void session::add_pending(const ether_addr &hwaddr, const address &addr)
{
for (std::list<ptr<address> >::iterator ad = _pending.begin(); ad != _pending.end(); ad++) {
if (addr == (*ad))
for (std::list<ptr<solicitor> >::iterator ad = _pending.begin(); ad != _pending.end(); ad++) {
if ((*ad)->is(addr, hwaddr))
return;
}

_pending.push_back(new address(addr));
_pending.push_back(new solicitor(addr, hwaddr));
}

void session::send_solicit()
Expand Down Expand Up @@ -179,9 +179,9 @@ void session::touch()
}
}

void session::send_advert(const address& daddr)
void session::send_advert(const ether_addr &dhwaddr, const address& daddr)
{
_pr->ifa()->write_advert(daddr, _taddr, _pr->router());
_pr->ifa()->write_advert(dhwaddr, daddr, _taddr, _pr->router());
}

void session::handle_auto_wire(const address& saddr, const std::string& ifname, bool use_via)
Expand Down Expand Up @@ -309,12 +309,12 @@ void session::handle_advert()
_fails = 0;

if (!_pending.empty()) {
for (std::list<ptr<address> >::iterator ad = _pending.begin();
for (std::list<ptr<solicitor> >::iterator ad = _pending.begin();
ad != _pending.end(); ad++) {
ptr<address> addr = (*ad);
logger::debug() << " - forward to " << addr;
ptr<solicitor> psolicitor = (*ad);
logger::debug() << " - forward to " << *psolicitor;

send_advert(addr);
send_advert(psolicitor->hwaddr(), psolicitor->addr());
}

_pending.clear();
Expand Down
Loading