/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program 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 .
*/
#include "interface/tuntap/TUNMessageType.h"
#include "interface/tuntap/NDPServer.h"
#include "util/Bits.h"
#include "util/Checksum.h"
#include "util/Identity.h"
#include "wire/Message.h"
#include "wire/Ethernet.h"
#include "wire/Headers.h"
#include "wire/NDPHeader.h"
#include
struct NDPServer_pvt
{
struct NDPServer pub;
struct Iface external;
struct Log* log;
uint8_t localMac[Ethernet_ADDRLEN];
Identity
};
#define MULTICAST_ADDR "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x00\x00\x08"
// ff 02 00 00 00 00 00 00 00 00 00 01 ff 00 00 02 870099
#define UNICAST_ADDR "\xfe\x80\0\0\0\0\0\0\0\0\0\0\0\0\0\x08"
//#define UNICAST_ADDR "\xfd\x80\0\0\0\0\0\0\0\0\0\0\0\0\0\x08"
#define ALL_ROUTERS "\xff\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\x02"
static bool isNeighborSolicitation(struct Message* msg, struct NDPServer_pvt* ns)
{
if (msg->length < Headers_IP6Header_SIZE + NDPHeader_NeighborSolicitation_SIZE) {
return false;
}
struct Headers_IP6Header* ip6 = (struct Headers_IP6Header*) msg->bytes;
struct NDPHeader_NeighborSolicitation* sol = (struct NDPHeader_NeighborSolicitation*) &ip6[1];
if (sol->oneThirtyFive != 135 || sol->zero != 0) {
Log_debug(ns->log, "wrong type/code for neighbor solicitation");
return false;
}
if (//Bits_memcmp(ip6->destinationAddr, UNICAST_ADDR, 16) ||
Bits_memcmp(ip6->destinationAddr, MULTICAST_ADDR, 13))
{
Log_debug(ns->log, "wrong address for neighbor solicitation");
return false;
}
/*if (Bits_memcmp(sol->targetAddr, UNICAST_ADDR, 16)) {
Log_debug(ns->log, "Soliciting the wrong neighbor");
return false;
}*/
return true;
}
static Iface_DEFUN answerNeighborSolicitation(struct Message* msg, struct NDPServer_pvt* ns)
{
struct Headers_IP6Header ip6;
Message_pop(msg, &ip6, Headers_IP6Header_SIZE, NULL);
struct NDPHeader_NeighborSolicitation sol;
Message_pop(msg, &sol, NDPHeader_NeighborSolicitation_SIZE, NULL);
if (msg->length) {
/* Right now we ignore any ICMP options. Windows will send them. */
Log_debug(ns->log, "%d extra bytes (ICMP options?) in neighbor solicitation",
msg->length);
}
struct NDPHeader_MacOpt macOpt = {
.type = NDPHeader_MacOpt_type_TARGET,
.one = 1
};
Bits_memcpy(macOpt.mac, ns->localMac, Ethernet_ADDRLEN);
Message_push(msg, &macOpt, sizeof(struct NDPHeader_MacOpt), NULL);
struct NDPHeader_NeighborAdvert na = {
.oneThirtySix = 136,
.zero = 0,
.checksum = 0,
.bits = NDPHeader_NeighborAdvert_bits_ROUTER
| NDPHeader_NeighborAdvert_bits_SOLICITED
| NDPHeader_NeighborAdvert_bits_OVERRIDE
};
Bits_memcpy(na.targetAddr, sol.targetAddr, 16);
Message_push(msg, &na, sizeof(struct NDPHeader_NeighborAdvert), NULL);
Bits_memcpy(ip6.destinationAddr, ip6.sourceAddr, 16);
Bits_memcpy(ip6.sourceAddr, sol.targetAddr, 16);
ip6.hopLimit = 255;
ip6.payloadLength_be = Endian_hostToBigEndian16(msg->length);
struct NDPHeader_RouterAdvert* adv = (struct NDPHeader_RouterAdvert*) msg->bytes;
adv->checksum = Checksum_icmp6(ip6.sourceAddr, msg->bytes, msg->length);
Message_push(msg, &ip6, sizeof(struct Headers_IP6Header), NULL);
TUNMessageType_push(msg, Ethernet_TYPE_IP6, NULL);
Log_debug(ns->log, "Sending neighbor advert");
return Iface_next(&ns->external, msg);
}
static Iface_DEFUN receiveMessage(struct Message* msg, struct Iface* external)
{
struct NDPServer_pvt* ns = Identity_containerOf(external, struct NDPServer_pvt, external);
if (msg->length > Headers_IP6Header_SIZE + 4) {
uint16_t ethertype = TUNMessageType_pop(msg, NULL);
if (ethertype != Ethernet_TYPE_IP6) {
} else if (isNeighborSolicitation(msg, ns)) {
//TODO(cjdns, Kubuxu): Filtering basing on cjdns network and tunnels.
return answerNeighborSolicitation(msg, ns);
}
TUNMessageType_push(msg, ethertype, NULL);
}
return Iface_next(&ns->pub.internal, msg);
}
static Iface_DEFUN sendMessage(struct Message* msg, struct Iface* internal)
{
struct NDPServer_pvt* ns = Identity_containerOf(internal, struct NDPServer_pvt, pub.internal);
return Iface_next(&ns->external, msg);
}
struct NDPServer* NDPServer_new(struct Iface* external,
struct Log* log,
uint8_t localMac[Ethernet_ADDRLEN],
struct Allocator* alloc)
{
struct NDPServer_pvt* out = Allocator_calloc(alloc, sizeof(struct NDPServer_pvt), 1);
Identity_set(out);
out->external.send = receiveMessage;
Iface_plumb(&out->external, external);
out->pub.internal.send = sendMessage;
out->log = log;
Bits_memcpy(out->localMac, localMac, Ethernet_ADDRLEN);
return &out->pub;
}