/* 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 (Message_getLength(msg) < Headers_IP6Header_SIZE + NDPHeader_NeighborSolicitation_SIZE) { return false; } struct Headers_IP6Header* ip6 = (struct Headers_IP6Header*) msg->msgbytes; 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; Er_assert(Message_epop(msg, &ip6, Headers_IP6Header_SIZE)); struct NDPHeader_NeighborSolicitation sol; Er_assert(Message_epop(msg, &sol, NDPHeader_NeighborSolicitation_SIZE)); if (Message_getLength(msg)) { /* Right now we ignore any ICMP options. Windows will send them. */ Log_debug(ns->log, "%d extra bytes (ICMP options?) in neighbor solicitation", Message_getLength(msg)); } struct NDPHeader_MacOpt macOpt = { .type = NDPHeader_MacOpt_type_TARGET, .one = 1 }; Bits_memcpy(macOpt.mac, ns->localMac, Ethernet_ADDRLEN); Er_assert(Message_epush(msg, &macOpt, sizeof(struct NDPHeader_MacOpt))); 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); Er_assert(Message_epush(msg, &na, sizeof(struct NDPHeader_NeighborAdvert))); Bits_memcpy(ip6.destinationAddr, ip6.sourceAddr, 16); Bits_memcpy(ip6.sourceAddr, sol.targetAddr, 16); ip6.hopLimit = 255; ip6.payloadLength_be = Endian_hostToBigEndian16(Message_getLength(msg)); struct NDPHeader_RouterAdvert* adv = (struct NDPHeader_RouterAdvert*) msg->msgbytes; adv->checksum = Checksum_icmp6_be(ip6.sourceAddr, msg->msgbytes, Message_getLength(msg)); Er_assert(Message_epush(msg, &ip6, sizeof(struct Headers_IP6Header))); Er_assert(TUNMessageType_push(msg, Ethernet_TYPE_IP6)); 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 (Message_getLength(msg) > Headers_IP6Header_SIZE + 4) { uint16_t ethertype = Er_assert(TUNMessageType_pop(msg)); if (ethertype != Ethernet_TYPE_IP6) { } else if (isNeighborSolicitation(msg, ns)) { //TODO(cjdns, Kubuxu): Filtering basing on cjdns network and tunnels. return answerNeighborSolicitation(msg, ns); } Er_assert(TUNMessageType_push(msg, ethertype)); } 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; }