Commit 4d305e38 authored by Jari Sundell's avatar Jari Sundell Committed by GitHub

Merge pull request #113 from rakshasa/ipv6

Ipv6
parents c97b462e 6f51b39d
Jari Sundell <jaris@ifi.uio.no>
Jari Sundell <sundell.software@gmail.com>
LibTorrent
Copyright (C) 2005-2014, Jari Sundell
LICENSE
GNU GPL, see COPYING. "libtorrent/src/utils/sha_fast.{cc,h}" is
......@@ -15,11 +17,11 @@ compiled if the user wishes to avoid using OpenSSL.
CONTACT
Jari Sundell
Skomakerveien 33
3185 Skoppum, NORWAY
Send bug reports, suggestions and patches to
<sundell.software@gmail.com> or to the mailinglist.
POLLING
"libtorrent/src/torrent/poll.h" provides an abstract class for
implementing any kind of polling the client wishes to use. Currently
epoll and select based polling is included.
......@@ -105,7 +105,6 @@ TORRENT_CHECK_EXECINFO()
TORRENT_CHECK_PTHREAD_SETNAME_NP()
TORRENT_MINCORE()
TORRENT_DISABLE_IPV6
TORRENT_DISABLE_INSTRUMENTATION
LIBTORRENT_LIBS="-ltorrent"
......@@ -126,9 +125,10 @@ AC_OUTPUT([
Makefile
src/Makefile
src/torrent/Makefile
src/torrent/peer/Makefile
src/torrent/data/Makefile
src/torrent/download/Makefile
src/torrent/net/Makefile
src/torrent/peer/Makefile
src/torrent/utils/Makefile
src/data/Makefile
src/dht/Makefile
......
This diff is collapsed.
......@@ -229,14 +229,3 @@ AC_DEFUN([TORRENT_ENABLE_INTERRUPT_SOCKET], [
]
)
])
AC_DEFUN([TORRENT_DISABLE_IPV6], [
AC_ARG_ENABLE(ipv6,
AC_HELP_STRING([--enable-ipv6], [enable ipv6 [[default=no]]]),
[
if test "$enableval" = "yes"; then
AC_DEFINE(RAK_USE_INET6, 1, enable ipv6 stuff)
fi
])
])
......@@ -15,6 +15,7 @@ libtorrent_la_LIBADD = \
torrent/libsub_torrent.la \
torrent/data/libsub_torrentdata.la \
torrent/download/libsub_torrentdownload.la \
torrent/net/libsub_torrentnet.la \
torrent/peer/libsub_torrentpeer.la \
torrent/utils/libsub_torrentutils.la \
data/libsub_data.la \
......
......@@ -54,8 +54,9 @@ DhtNode::DhtNode(const HashString& id, const rak::socket_address* sa) :
m_recentlyInactive(0),
m_bucket(NULL) {
if (sa->family() != rak::socket_address::af_inet)
throw resource_error("Address not af_inet");
if (sa->family() != rak::socket_address::af_inet &&
(sa->family() != rak::socket_address::af_inet6 || !sa->sa_inet6()->is_any()))
throw resource_error("Addres not af_inet or in6addr_any");
}
DhtNode::DhtNode(const std::string& id, const Object& cache) :
......@@ -84,8 +85,19 @@ DhtNode::store_compact(char* buffer) const {
Object*
DhtNode::store_cache(Object* container) const {
container->insert_key("i", m_socketAddress.sa_inet()->address_h());
container->insert_key("p", m_socketAddress.sa_inet()->port());
if (m_socketAddress.family() == rak::socket_address::af_inet6) {
// Currently, all we support is in6addr_any (checked in the constructor),
// which is effectively equivalent to this. Note that we need to specify
// int64_t explicitly here because a zero constant is special in C++ and
// thus we need an explicit match.
container->insert_key("i", int64_t(0));
container->insert_key("p", m_socketAddress.sa_inet6()->port());
} else {
container->insert_key("i", m_socketAddress.sa_inet()->address_h());
container->insert_key("p", m_socketAddress.sa_inet()->port());
}
container->insert_key("t", m_lastSeen);
return container;
}
......
......@@ -701,6 +701,14 @@ DhtServer::event_read() {
if (read < 0)
break;
// We can currently only process mapped-IPv4 addresses, not real IPv6.
// Translate them to an af_inet socket_address.
if (sa.family() == rak::socket_address::af_inet6)
sa = sa.sa_inet6()->normalize_address();
if (sa.family() != rak::socket_address::af_inet)
continue;
total += read;
// If it's not a valid bencode dictionary at all, it's probably not a DHT
......
......@@ -4,6 +4,8 @@ libsub_net_la_SOURCES = \
address_list.cc \
address_list.h \
data_buffer.h \
local_addr.cc \
local_addr.h \
listen.cc \
listen.h \
protocol_buffer.h \
......
......@@ -77,6 +77,16 @@ AddressList::parse_address_compact(raw_string s) {
std::back_inserter(*this));
}
void
AddressList::parse_address_compact_ipv6(const std::string& s) {
if (sizeof(const SocketAddressCompact6) != 18)
throw internal_error("ConnectionList::AddressList::parse_address_compact_ipv6(...) bad struct size.");
std::copy(reinterpret_cast<const SocketAddressCompact6*>(s.c_str()),
reinterpret_cast<const SocketAddressCompact6*>(s.c_str() + s.size() - s.size() % sizeof(SocketAddressCompact6)),
std::back_inserter(*this));
}
void
AddressList::parse_address_bencode(raw_list s) {
if (sizeof(const SocketAddressCompact) != 6)
......
......@@ -54,6 +54,7 @@ public:
void parse_address_compact(raw_string s);
void parse_address_compact(const std::string& s);
void parse_address_compact_ipv6(const std::string& s);
private:
static rak::socket_address parse_address(const Object& b);
......@@ -99,6 +100,26 @@ struct SocketAddressCompact {
const char* c_str() const { return reinterpret_cast<const char*>(this); }
} __attribute__ ((packed));
struct SocketAddressCompact6 {
SocketAddressCompact6() {}
SocketAddressCompact6(in6_addr a, uint16_t p) : addr(a), port(p) {}
SocketAddressCompact6(const rak::socket_address_inet6* sa) : addr(sa->address()), port(sa->port_n()) {}
operator rak::socket_address () const {
rak::socket_address sa;
sa.sa_inet6()->clear();
sa.sa_inet6()->set_port_n(port);
sa.sa_inet6()->set_address(addr);
return sa;
}
in6_addr addr;
uint16_t port;
const char* c_str() const { return reinterpret_cast<const char*>(this); }
} __attribute__ ((packed));
}
#endif
......@@ -61,7 +61,8 @@ Listen::open(uint16_t first, uint16_t last, int backlog, const rak::socket_addre
if (first == 0 || first > last)
throw input_error("Tried to open listening port with an invalid range.");
if (bindAddress->family() != rak::socket_address::af_inet &&
if (bindAddress->family() != 0 &&
bindAddress->family() != rak::socket_address::af_inet &&
bindAddress->family() != rak::socket_address::af_inet6)
throw input_error("Listening socket must be bound to an inet or inet6 address.");
......@@ -71,7 +72,13 @@ Listen::open(uint16_t first, uint16_t last, int backlog, const rak::socket_addre
throw resource_error("Could not allocate socket for listening.");
rak::socket_address sa;
sa.copy(*bindAddress, bindAddress->length());
// TODO: Temporary until we refactor:
if (bindAddress->family() == 0) {
sa.sa_inet6()->clear();
} else {
sa.copy(*bindAddress, bindAddress->length());
}
do {
sa.set_port(first);
......
// libTorrent - BitTorrent library
// Copyright (C) 2005-2007, Jari Sundell
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <jaris@ifi.uio.no>
//
// Skomakerveien 33
// 3185 Skoppum, NORWAY
#include "config.h"
#include <stdio.h>
#include <ifaddrs.h>
#include <rak/socket_address.h>
#include <sys/types.h>
#include <errno.h>
#ifdef __linux__
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#endif
#include "torrent/exceptions.h"
#include "socket_fd.h"
#include "local_addr.h"
namespace torrent {
#ifdef __linux__
namespace {
// IPv4 priority, from highest to lowest:
//
// 1. Everything else (global address)
// 2. Private address space (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
// 3. Empty/INADDR_ANY (0.0.0.0)
// 4. Link-local address (169.254.0.0/16)
// 5. Localhost (127.0.0.0/8)
int
get_priority_ipv4(const in_addr& addr) {
if ((addr.s_addr & htonl(0xff000000U)) == htonl(0x7f000000U)) {
return 5;
}
if ((addr.s_addr & htonl(0xffff0000U)) == htonl(0xa9fe0000U)) {
return 4;
}
if (addr.s_addr == htonl(0)) {
return 3;
}
if ((addr.s_addr & htonl(0xff000000U)) == htonl(0x0a000000U) ||
(addr.s_addr & htonl(0xfff00000U)) == htonl(0xac100000U) ||
(addr.s_addr & htonl(0xffff0000U)) == htonl(0xc0a80000U)) {
return 2;
}
return 1;
}
// IPv6 priority, from highest to lowest:
//
// 1. Global address (2000::/16 not in 6to4 or Teredo)
// 2. 6to4 (2002::/16)
// 3. Teredo (2001::/32)
// 4. Empty/INADDR_ANY (::)
// 5. Everything else (link-local, ULA, etc.)
int
get_priority_ipv6(const in6_addr& addr) {
const uint32_t *addr32 = reinterpret_cast<const uint32_t *>(addr.s6_addr);
if (addr32[0] == htonl(0) &&
addr32[1] == htonl(0) &&
addr32[2] == htonl(0) &&
addr32[3] == htonl(0)) {
return 4;
}
if (addr32[0] == htonl(0x20010000)) {
return 3;
}
if ((addr32[0] & htonl(0xffff0000)) == htonl(0x20020000)) {
return 2;
}
if ((addr32[0] & htonl(0xe0000000)) == htonl(0x20000000)) {
return 1;
}
return 5;
}
int
get_priority(const rak::socket_address& addr) {
switch (addr.family()) {
case AF_INET:
return get_priority_ipv4(addr.c_sockaddr_inet()->sin_addr);
case AF_INET6:
return get_priority_ipv6(addr.c_sockaddr_inet6()->sin6_addr);
default:
throw torrent::internal_error("Unknown address family given to compare");
}
}
}
// Linux-specific implementation that understands how to filter away
// understands how to filter away secondary addresses.
bool get_local_address(sa_family_t family, rak::socket_address *address) {
ifaddrs *ifaddrs;
if (getifaddrs(&ifaddrs)) {
return false;
}
rak::socket_address best_addr;
switch (family) {
case AF_INET:
best_addr.sa_inet()->clear();
break;
case AF_INET6:
best_addr.sa_inet6()->clear();
break;
default:
throw torrent::internal_error("Unknown address family given to get_local_address");
}
// The bottom bit of the priority is used to hold if the address is
// a secondary address (e.g. with IPv6 privacy extensions) or not;
// secondary addresses have lower priority (higher number).
int best_addr_pri = get_priority(best_addr) * 2;
// Get all the addresses via Linux' netlink interface.
int fd = ::socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd == -1) {
return false;
}
struct sockaddr_nl nladdr;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
if (::bind(fd, (sockaddr *)&nladdr, sizeof(nladdr))) {
::close(fd);
return false;
}
const int seq_no = 1;
struct {
nlmsghdr nh;
rtgenmsg g;
} req;
memset(&req, 0, sizeof(req));
req.nh.nlmsg_len = sizeof(req);
req.nh.nlmsg_type = RTM_GETADDR;
req.nh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
req.nh.nlmsg_pid = getpid();
req.nh.nlmsg_seq = seq_no;
req.g.rtgen_family = AF_UNSPEC;
int ret;
do {
ret = ::sendto(fd, &req, sizeof(req), 0, (sockaddr *)&nladdr, sizeof(nladdr));
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
::close(fd);
return false;
}
bool done = false;
do {
char buf[4096];
socklen_t len = sizeof(nladdr);
do {
ret = ::recvfrom(fd, buf, sizeof(buf), 0, (sockaddr *)&nladdr, &len);
} while (ret == -1 && errno == EINTR);
if (ret < 0) {
::close(fd);
return false;
}
for (const nlmsghdr *nlmsg = (const nlmsghdr *)buf;
NLMSG_OK(nlmsg, ret);
nlmsg = NLMSG_NEXT(nlmsg, ret)) {
if (nlmsg->nlmsg_seq != seq_no)
continue;
if (nlmsg->nlmsg_type == NLMSG_DONE) {
done = true;
break;
}
if (nlmsg->nlmsg_type == NLMSG_ERROR) {
::close(fd);
return false;
}
if (nlmsg->nlmsg_type != RTM_NEWADDR)
continue;
const ifaddrmsg *ifa = (const ifaddrmsg *)NLMSG_DATA(nlmsg);
if (ifa->ifa_family != family)
continue;
#ifdef IFA_F_OPTIMISTIC
if ((ifa->ifa_flags & IFA_F_OPTIMISTIC) != 0)
continue;
#endif
#ifdef IFA_F_DADFAILED
if ((ifa->ifa_flags & IFA_F_DADFAILED) != 0)
continue;
#endif
#ifdef IFA_F_DEPRECATED
if ((ifa->ifa_flags & IFA_F_DEPRECATED) != 0)
continue;
#endif
#ifdef IFA_F_TENTATIVE
if ((ifa->ifa_flags & IFA_F_TENTATIVE) != 0)
continue;
#endif
// Since there can be point-to-point links on the machine, we need to keep
// track of the addresses we've seen for this interface; if we see both
// IFA_LOCAL and IFA_ADDRESS for an interface, keep only the IFA_LOCAL.
rak::socket_address this_addr;
bool seen_addr = false;
int plen = IFA_PAYLOAD(nlmsg);
for (const rtattr *rta = IFA_RTA(ifa);
RTA_OK(rta, plen);
rta = RTA_NEXT(rta, plen)) {
if (rta->rta_type != IFA_LOCAL &&
rta->rta_type != IFA_ADDRESS) {
continue;
}
if (rta->rta_type == IFA_ADDRESS && seen_addr) {
continue;
}
seen_addr = true;
switch (ifa->ifa_family) {
case AF_INET:
this_addr.sa_inet()->clear();
this_addr.sa_inet()->set_address(*(const in_addr *)RTA_DATA(rta));
break;
case AF_INET6:
this_addr.sa_inet6()->clear();
this_addr.sa_inet6()->set_address(*(const in6_addr *)RTA_DATA(rta));
break;
}
}
if (!seen_addr)
continue;
int this_addr_pri = get_priority(this_addr) * 2;
if ((ifa->ifa_flags & IFA_F_SECONDARY) == IFA_F_SECONDARY) {
++this_addr_pri;
}
if (this_addr_pri < best_addr_pri) {
best_addr = this_addr;
best_addr_pri = this_addr_pri;
}
}
} while (!done);
::close(fd);
if (!best_addr.is_address_any()) {
*address = best_addr;
return true;
} else {
return false;
}
}
#else
// Generic POSIX variant.
bool
get_local_address(sa_family_t family, rak::socket_address *address) {
SocketFd sock;
if (!sock.open_datagram()) {
return false;
}
rak::socket_address dummy_dest;
dummy_dest.clear();
switch (family) {
case rak::socket_address::af_inet:
dummy_dest.set_address_c_str("4.0.0.0");
break;
case rak::socket_address::af_inet6:
dummy_dest.set_address_c_str("2001:700::");
break;
default:
throw internal_error("Unknown address family");
}
dummy_dest.set_port(80);
if (!sock.connect(dummy_dest)) {
sock.close();
return false;
}
bool ret = sock.getsockname(address);
sock.close();
return ret;
}
#endif
}
// libTorrent - BitTorrent library
// Copyright (C) 2005-2007, Jari Sundell
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <jaris@ifi.uio.no>
//
// Skomakerveien 33
// 3185 Skoppum, NORWAY
// A routine to get a local IP address that can be presented to a tracker.
// (Does not use UPnP etc., so will not understand NAT.)
// On a machine with multiple network cards, address selection can be a
// complex process, and in general what's selected is a source/destination
// address pair. However, this routine will give an approximation that will
// be good enough for most purposes and users.
#ifndef LIBTORRENT_NET_LOCAL_ADDR_H
#define LIBTORRENT_NET_LOCAL_ADDR_H
#include <unistd.h>
namespace rak {
class socket_address;
}
namespace torrent {
// Note: family must currently be rak::af_inet or rak::af_inet6
// (rak::af_unspec won't do); anything else will throw an exception.
// Returns false if no address of the given family could be found,
// either because there are none, or because something went wrong in
// the process (e.g., no free file descriptors).
bool get_local_address(sa_family_t family, rak::socket_address *address);
}
#endif /* LIBTORRENT_NET_LOCAL_ADDR_H */
......@@ -73,7 +73,12 @@ SocketDatagram::write_datagram(const void* buffer, unsigned int length, rak::soc
int r;
if (sa != NULL) {
r = ::sendto(m_fileDesc, buffer, length, 0, sa->sa_inet()->c_sockaddr(), sizeof(rak::socket_address_inet));
if (m_ipv6_socket && sa->family() == rak::socket_address::pf_inet) {
rak::socket_address_inet6 sa_mapped = sa->sa_inet()->to_mapped_address();
r = ::sendto(m_fileDesc, buffer, length, 0, sa_mapped.c_sockaddr(), sizeof(rak::socket_address_inet6));
} else {
r = ::sendto(m_fileDesc, buffer, length, 0, sa->c_sockaddr(), sa->length());
}
} else {
r = ::send(m_fileDesc, buffer, length, 0);
}
......
......@@ -70,7 +70,10 @@ SocketFd::set_priority(priority_type p) {
check_valid();
int opt = p;
return setsockopt(m_fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) == 0;
if (m_ipv6_socket)
return setsockopt(m_fd, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt)) == 0;
else
return setsockopt(m_fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) == 0;
}
bool
......@@ -112,12 +115,32 @@ SocketFd::get_error() const {
bool
SocketFd::open_stream() {
return (m_fd = socket(rak::socket_address::pf_inet, SOCK_STREAM, IPPROTO_TCP)) != -1;
m_fd = socket(rak::socket_address::pf_inet6, SOCK_STREAM, IPPROTO_TCP);
if (m_fd == -1) {
m_ipv6_socket = false;
return (m_fd = socket(rak::socket_address::pf_inet, SOCK_STREAM, IPPROTO_TCP)) != -1;
}
m_ipv6_socket = true;
int zero = 0;
return setsockopt(m_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) != -1;
}
bool
SocketFd::open_datagram() {
return (m_fd = socket(rak::socket_address::pf_inet, SOCK_DGRAM, 0)) != -1;
m_fd = socket(rak::socket_address::pf_inet6, SOCK_DGRAM, 0);
if (m_fd == -1) {
m_ipv6_socket = false;
return (m_fd = socket(rak::socket_address::pf_inet, SOCK_DGRAM, 0)) != -1;
}
m_ipv6_socket = true;
int zero = 0;
return setsockopt(m_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) != -1;
}
bool
......@@ -148,6 +171,11 @@ bool
SocketFd::bind(const rak::socket_address& sa) {
check_valid();
if (m_ipv6_socket && sa.family() == rak::socket_address::pf_inet) {
rak::socket_address_inet6 sa_mapped = sa.sa_inet()->to_mapped_address();
return !::bind(m_fd, sa_mapped.c_sockaddr(), sizeof(sa_mapped));
}
return !::bind(m_fd, sa.c_sockaddr(), sa.length());
}
......@@ -155,6 +183,11 @@ bool
SocketFd::bind(const rak::socket_address& sa, unsigned int length) {
check_valid();