Commit d3ef9682 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[trac878] simple DHCPv6 echo server implemented:

 - Added Addr6 (IPv6 address wrapper)
 - Added Pkt6 (generic IPv6 packet class)
 - Added IfaceMgr (provides information about available interfaces,
   opens sockets, sends and receives packets)
 - Added Dhcpv6Srv - a dummy echo server for now
parent f5cc3a37
......@@ -19,7 +19,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
CLEANFILES = *.gcno *.gcda spec_config.h
man_MANS = b10-dhcp6.8
EXTRA_DIST = $(man_MANS) dhcp6.spec
EXTRA_DIST = $(man_MANS) dhcp6.spec interfaces.txt
#if ENABLE_MAN
#b10-dhcp6.8: b10-dhcp6.xml
......@@ -31,8 +31,8 @@ spec_config.h: spec_config.h.pre
BUILT_SOURCES = spec_config.h
pkglibexec_PROGRAMS = b10-dhcp6
b10_dhcp6_SOURCES = main.cc
b10_dhcp6_SOURCES += dhcp6.h
b10_dhcp6_SOURCES = main.cc addr6.cc iface_mgr.cc pkt6.cc dhcp6_srv.cc
b10_dhcp6_SOURCES += addr6.h iface_mgr.h pkt6.h dhcp6_srv.h dhcp6.h
b10_dhcp6_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
......@@ -48,5 +48,4 @@ b10_dhcp6_LDADD += $(SQLITE_LIBS)
# TODO: config.h.in is wrong because doesn't honor pkgdatadir
# and can't use @datadir@ because doesn't expand default ${prefix}
b10_dhcp6dir = $(pkgdatadir)
b10_dhcp6_DATA = dhcp6.spec
b10_dhcp6_DATA = dhcp6.spec interfaces.txt
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <string.h>
#include <arpa/inet.h>
#include <ostream>
#include "dhcp6/addr6.h"
std::ostream & isc::operator << (std::ostream & out, const isc::Addr6& addr) {
out << addr.getPlain();
return out;
}
using namespace std;
using namespace isc;
Addr6::Addr6(const char *addr, bool plain /*=false*/) {
if (plain) {
inet_pton(AF_INET6, addr, addr_);
} else {
memcpy(addr_, addr, 16);
}
}
Addr6::Addr6() {
memset(addr_, 0, 16);
}
Addr6::Addr6(in6_addr* addr) {
memcpy(addr_, addr, 16);
}
Addr6::Addr6(sockaddr_in6* addr) {
memcpy(addr_, &addr->sin6_addr, 16);
}
bool Addr6::linkLocal() const {
if ( ( (unsigned char)addr_[0]==0xfe) &&
( (unsigned char)addr_[1]==0x80) ) {
return true;
} else {
return false;
}
}
bool Addr6::multicast() const {
if ( (unsigned char)addr_[0]==0xff) {
return true;
} else {
return false;
}
}
std::string Addr6::getPlain() const {
char buf[MAX_ADDRESS_STRING_LEN];
inet_ntop(AF_INET6, addr_, buf, MAX_ADDRESS_STRING_LEN);
return string(buf);
}
bool Addr6::operator==(const Addr6& other) const {
if (!memcmp(addr_, other.addr_, 16)) {
return true;
} else {
return false;
}
// return !memcmp() would be shorter, but less readable
}
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef ADDR6_H
#define ADDR6_H
#include <ostream>
#include <string>
#include <list>
namespace isc {
static const int MAX_ADDRESS_STRING_LEN =
sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255");
class Addr6 {
public:
Addr6(const char * addr, bool plain=false);
Addr6(struct in6_addr* addr);
Addr6(struct sockaddr_in6 * addr);
Addr6();
inline const char * get() const { return addr_; }
std::string getPlain() const;
char * get() { return addr_; }
bool operator==(const Addr6& other) const;
bool linkLocal() const;
bool multicast() const;
// no dtor necessary (no allocations done)
private:
char addr_[MAX_ADDRESS_STRING_LEN];
};
std::ostream & operator << (std::ostream & out, const Addr6& addr);
typedef std::list<Addr6> Addr6Lst;
};
#endif
/* dhcp6.h
DHCPv6 Protocol structures... */
/*
* Copyright (c) 2006-2011 by Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Internet Systems Consortium, Inc.
* 950 Charter Street
* Redwood City, CA 94063
* <info@isc.org>
* https://www.isc.org/
*/
// Copyright (C) 2006-2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef DHCP6_H
#define DHCP6_H
/* DHCPv6 Option codes: */
......@@ -136,8 +126,11 @@ extern const int dhcpv6_type_name_max;
/*
* DHCPv6 well-known multicast addressess, from section 5.1 of RFC 3315
*/
#define All_DHCP_Relay_Agents_and_Servers "FF02::1:2"
#define All_DHCP_Servers "FF05::1:3"
#define ALL_DHCP_RELAY_AGENTS_AND_SERVERS "FF02::1:2"
#define ALL_DHCP_SERVERS "FF05::1:3"
#define DHCP6_CLIENT_PORT 546
#define DHCP6_SERVER_PORT 547
/*
* DHCPv6 Retransmission Constants (RFC3315 section 5.5, RFC 5007)
......@@ -171,29 +164,6 @@ extern const int dhcpv6_type_name_max;
#define LQ6_MAX_RT 10
#define LQ6_MAX_RC 5
/*
* Normal packet format, defined in section 6 of RFC 3315
*/
struct dhcpv6_packet {
unsigned char msg_type;
unsigned char transaction_id[3];
unsigned char options[FLEXIBLE_ARRAY_MEMBER];
};
/* Offset into DHCPV6 Reply packets where Options spaces commence. */
#define REPLY_OPTIONS_INDEX 4
/*
* Relay packet format, defined in section 7 of RFC 3315
*/
struct dhcpv6_relay_packet {
unsigned char msg_type;
unsigned char hop_count;
unsigned char link_address[16];
unsigned char peer_address[16];
unsigned char options[FLEXIBLE_ARRAY_MEMBER];
};
/* Leasequery query-types (RFC 5007) */
#define LQ6QT_BY_ADDRESS 1
......@@ -211,3 +181,4 @@ struct dhcpv6_relay_packet {
#define IRT_DEFAULT 86400
#define IRT_MINIMUM 600
#endif
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "dhcp6/addr6.h"
#include "dhcp6/pkt6.h"
#include "dhcp6/iface_mgr.h"
#include "dhcp6/dhcp6_srv.h"
using namespace std;
using namespace isc;
Dhcpv6Srv::Dhcpv6Srv() {
cout << "Initialization" << endl;
}
bool Dhcpv6Srv::run() {
while (true) {
Pkt6 * pkt;
pkt = IfaceMgr::instance().receive();
if (pkt) {
Addr6 client = pkt->remoteAddr;
cout << "Received " << pkt->dataLen_ << " bytes, echoing back."
<< endl;
IfaceMgr::instance().send(*pkt);
delete pkt;
}
sleep(1);
}
return true;
}
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef DHCPV6_SRV_H
#define DHCPV6_SRV_H
#include <iostream>
#include "dhcp6/addr6.h"
namespace isc {
class Dhcpv6Srv {
private:
// defined private on purpose. We don't want to have more than
// one copy
Dhcpv6Srv(const Dhcpv6Srv& src);
Dhcpv6Srv& operator=(const Dhcpv6Srv& src);
public:
// default constructor
Dhcpv6Srv();
~Dhcpv6Srv();
bool run();
protected:
bool shutdown;
};
};
#endif // DHCP6_SRV_H
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <sstream>
#include <fstream>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include "addr6.h"
#include "dhcp6/iface_mgr.h"
#include "dhcp6/dhcp6.h"
using namespace std;
using namespace isc;
// IfaceMgr is a singleton implementation
IfaceMgr * IfaceMgr::instance_ = 0;
void IfaceMgr::instanceCreate() {
if (instance_) {
// XXX: throw exception here
return;
}
instance_ = new IfaceMgr();
}
IfaceMgr& IfaceMgr::instance() {
if (instance_ == 0)
instanceCreate();
return *instance_;
}
IfaceMgr::Iface::Iface()
: name_(""), ifindex_(0), macLen_(0) {
memset(mac_, 0, 20);
}
IfaceMgr::Iface::Iface(const std::string name, int ifindex)
:name_(name), ifindex_(ifindex), macLen_(0) {
memset(mac_, 0, 20);
}
std::string IfaceMgr::Iface::getFullName() const {
ostringstream tmp;
tmp << name_ << "/" << ifindex_;
return tmp.str();
}
std::string IfaceMgr::Iface::getPlainMac() const {
ostringstream tmp;
for (int i=0; i<macLen_; i++) {
tmp.fill('0');
tmp.width(2);
tmp << (hex) << (int) mac_[i];
if (i<macLen_-1) {
tmp << ":";
}
}
return tmp.str();
}
IfaceMgr::IfaceMgr() {
control_buf_len_ = CMSG_SPACE(sizeof(struct in6_pktinfo));
control_buf_ = (char*) new char[control_buf_len_];
cout << "IfaceMgr initialization." << endl;
detectIfaces();
openSockets();
}
IfaceMgr::~IfaceMgr() {
if (control_buf_) {
delete [] control_buf_;
control_buf_ = 0;
control_buf_len_ = 0;
}
}
void IfaceMgr::detectIfaces() {
string ifaceName, linkLocal;
// XXX: do the actual detection
cout << "Interface detection is not implemented yet. "
<< "Reading interfaces.txt file instead." << endl;
cout << "Please use format: interface-name link-local-address" << endl;
ifstream interfaces("interfaces.txt");
interfaces >> ifaceName;
interfaces >> linkLocal;
cout << "Detected interface " << ifaceName << "/" << linkLocal << endl;
Iface iface(ifaceName, if_nametoindex( ifaceName.c_str() ) );
Addr6 addr(linkLocal.c_str(), true);
iface.addrs_.push_back(addr);
ifaces_.push_back(iface);
interfaces.close();
}
bool IfaceMgr::openSockets() {
int sock;
for (IfaceLst::iterator iface=ifaces_.begin();
iface!=ifaces_.end();
++iface) {
for (Addr6Lst::iterator addr=iface->addrs_.begin();
addr!=iface->addrs_.end();
++addr) {
sock = openSocket(iface->name_, *addr,
DHCP6_SERVER_PORT, false);
if (sock<0) {
cout << "Failed to open unicast socket." << endl;
return false;
}
sendsock_ = sock;
sock = openSocket(iface->name_,
Addr6(ALL_DHCP_RELAY_AGENTS_AND_SERVERS, true),
DHCP6_SERVER_PORT, true);
if (sock<0) {
cout << "Failed to open multicast socket." << endl;
return false;
}
recvsock_ = sock;
}
}
return true;
}
void IfaceMgr::printIfaces() {
for (IfaceLst::const_iterator iface=ifaces_.begin();
iface!=ifaces_.end();
++iface) {
cout << "Detected interface " << iface->getFullName() << endl;
cout << " " << iface->addrs_.size() << " addr(s):" << endl;
for (Addr6Lst::const_iterator addr=iface->addrs_.begin();
addr != iface->addrs_.end();
++addr) {
cout << " " << *addr << endl;
}
cout << " mac: " << iface->getPlainMac() << endl;
}
}
IfaceMgr::Iface* IfaceMgr::getIface(int ifindex) {
for (IfaceLst::iterator iface=ifaces_.begin();
iface!=ifaces_.end();
++iface) {
if (iface->ifindex_ == ifindex)
return &(*iface);
}
return 0; // not found
}
IfaceMgr::Iface* IfaceMgr::getIface(const std::string &ifname) {
for (IfaceLst::iterator iface=ifaces_.begin();
iface!=ifaces_.end();
++iface) {
if (iface->name_ == ifname)
return &(*iface);
}
return 0; // not found
}
int IfaceMgr::openSocket(const std::string &ifname,
const Addr6 &addr,
int port,
bool mcast) {
struct sockaddr_storage name;
int name_len;
struct sockaddr_in6 *addr6;
cout << "Creating socket on " << ifname << "/" << addr << "/port="
<< port << endl;
memset(&name, 0, sizeof(name));
addr6 = (struct sockaddr_in6 *)&name;
addr6->sin6_family = AF_INET6;
addr6->sin6_port = htons(port);
addr6->sin6_scope_id = if_nametoindex(ifname.c_str());
memcpy(&addr6->sin6_addr,
addr.get(),
sizeof(addr6->sin6_addr));
#ifdef HAVE_SA_LEN
addr6->sin6_len = sizeof(*addr6);
#endif
name_len = sizeof(*addr6);
// XXX: use sockcreator once it becomes available
// make a socket
int sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock < 0) {
cout << "Failed to create UDP6 socket." << endl;
return -1;
}
/* Set the REUSEADDR option so that we don't fail to start if
we're being restarted. */
int flag = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&flag, sizeof(flag)) < 0) {
cout << "Can't set SO_REUSEADDR option on dhcpv6 socket." << endl;
return -1;
}
if (bind(sock, (struct sockaddr *)&name, name_len) < 0) {
cout << "Failed to bind socket " << sock << " to " << addr.getPlain()
<< "/port=" << port << endl;
return -1;
}
#ifdef IPV6_RECVPKTINFO
/* RFC3542 - a new way */
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
&flag, sizeof(flag)) != 0) {
cout << "setsockopt: IPV6_RECVPKTINFO failed." << endl;
return -1;
}
#else
/* RFC2292 - an old way */
if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
&flag, sizeof(flag)) != 0) {
cout << "setsockopt: IPV6_PKTINFO: failed." << endl;
return -1;
}
#endif
// multicast stuff
if (mcast /*addr.multicast()*/) {
// both mcast (ALL_DHCP_RELAY_AGENTS_AND_SERVERS and ALL_DHCP_SERVERS)
// are link and site-scoped, so there is no sense to join those them
// with global addressed.
if ( !joinMcast( sock, ifname,
string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
close(sock);
return -1;
}
}
cout << "Created socket " << sock << " on " << ifname << "/" << addr
<< "/port=" << port << endl;
return sock;
}
bool IfaceMgr::joinMcast(int sock, const std::string& ifname,
const std::string & mcast) {
struct ipv6_mreq mreq;
if (inet_pton(AF_INET6, mcast.c_str(),