ndppd/src/iface.cc

617 lines
14 KiB
C++
Raw Normal View History

2011-09-13 21:26:12 +02:00
// ndppd - NDP Proxy Daemon
// Copyright (C) 2011 Daniel Adolfsson <daniel.adolfsson@tuhox.com>
2011-09-13 21:26:12 +02:00
//
// 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/>.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/ether.h>
#include <netpacket/packet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
2011-09-16 17:06:36 +02:00
#include <linux/filter.h>
2011-09-13 21:26:12 +02:00
#include <string>
#include <vector>
#include <map>
#include "ndppd.h"
__NDPPD_NS_BEGIN
std::map<std::string, strong_ptr<iface> > iface::_map;
2011-09-13 21:26:12 +02:00
std::vector<struct pollfd> iface::_pollfds;
2011-09-17 01:10:23 +02:00
iface::iface() :
_ifd(-1), _pfd(-1)
{
}
2011-09-13 21:26:12 +02:00
iface::~iface()
{
2011-09-17 01:10:23 +02:00
DBG("iface::~iface()");
if(_ifd >= 0)
close(_ifd);
if(_pfd >= 0)
{
allmulti(_prev_allmulti);
close(_pfd);
}
2011-09-13 21:26:12 +02:00
}
strong_ptr<iface> iface::open_pfd(const std::string& name)
2011-09-13 21:26:12 +02:00
{
int fd;
std::map<std::string, strong_ptr<iface> >::iterator it = _map.find(name);
2011-09-16 17:06:36 +02:00
strong_ptr<iface> ifa;
2011-09-16 17:06:36 +02:00
if(it != _map.end())
{
2011-09-17 01:10:23 +02:00
if(it->second->_pfd >= 0)
2011-09-16 17:06:36 +02:00
return it->second;
ifa = it->second;
}
else
{
// We need an _ifs, so let's set one up.
ifa = open_ifd(name);
}
if(ifa.is_null())
return strong_ptr<iface>();
2011-09-16 17:06:36 +02:00
// Create a socket.
if((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IPV6))) < 0)
{
ERR("Unable to create socket");
return strong_ptr<iface>();
2011-09-16 17:06:36 +02:00
}
// Bind to the specified interface.
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, name.c_str(), IFNAMSIZ - 1);
ifr.ifr_name[IFNAMSIZ - 1] = '\0';
if(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0)
{
close(fd);
ERR("Failed to bind to interface '%s'", name.c_str());
return strong_ptr<iface>();
2011-09-16 17:06:36 +02:00
}
// Switch to non-blocking mode.
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
int on = 1;
if(ioctl(fd, FIONBIO, (char *)&on) < 0)
{
close(fd);
ERR("Failed to switch to non-blocking on interface '%s'", name.c_str());
return strong_ptr<iface>();
2011-09-16 17:06:36 +02:00
}
// Set up filter.
struct sock_fprog fprog;
static const struct sock_filter filter[] =
{
// Load the ether_type.
BPF_STMT(BPF_LD | BPF_H | BPF_ABS,
offsetof(struct ether_header, ether_type)),
// Bail if it's *not* ETHERTYPE_IPV6.
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 5),
// Load the next header type.
BPF_STMT(BPF_LD | BPF_B | BPF_ABS,
sizeof(struct ether_header) + offsetof(struct ip6_hdr, ip6_nxt)),
// Bail if it's *not* IPPROTO_ICMPV6.
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
// Load the ICMPv6 type.
BPF_STMT(BPF_LD | BPF_B | BPF_ABS,
sizeof(struct ether_header) + sizeof(ip6_hdr) + offsetof(struct icmp6_hdr, icmp6_type)),
// Bail if it's *not* ND_NEIGHBOR_SOLICIT.
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_SOLICIT, 0, 1),
// Keep packet.
BPF_STMT(BPF_RET | BPF_K, -1),
// Drop packet.
BPF_STMT(BPF_RET | BPF_K, 0)
};
fprog.filter = (struct sock_filter *)filter;
fprog.len = 8;
if(setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0)
{
ERR("Failed to set filter");
return strong_ptr<iface>();
2011-09-16 17:06:36 +02:00
}
// Set up an instance of 'iface'.
ifa->_pfd = fd;
fixup_pollfds();
return ifa;
}
strong_ptr<iface> iface::open_ifd(const std::string& name)
2011-09-16 17:06:36 +02:00
{
int fd;
2011-09-13 21:26:12 +02:00
std::map<std::string, strong_ptr<iface> >::iterator it = _map.find(name);
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
if((it != _map.end()) && it->second->_ifd)
return it->second;
2011-09-13 21:26:12 +02:00
// Create a socket.
if((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
{
ERR("Unable to create socket");
return strong_ptr<iface>();
2011-09-13 21:26:12 +02:00
}
// Bind to the specified interface.
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, name.c_str(), IFNAMSIZ - 1);
ifr.ifr_name[IFNAMSIZ - 1] = '\0';
if(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0)
{
close(fd);
ERR("Failed to bind to interface '%s'", name.c_str());
return strong_ptr<iface>();
2011-09-13 21:26:12 +02:00
}
// Detect the link-layer address.
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, name.c_str(), IFNAMSIZ - 1);
ifr.ifr_name[IFNAMSIZ - 1] = '\0';
if(ioctl(fd, SIOCGIFHWADDR, &ifr) < 0)
{
close(fd);
ERR("Failed to detect link-layer address for interface '%s'", name.c_str());
return strong_ptr<iface>();
2011-09-13 21:26:12 +02:00
}
DBG("fd=%d, hwaddr=%s", fd, ether_ntoa((const struct ether_addr *)&ifr.ifr_hwaddr.sa_data));
2011-09-16 17:06:36 +02:00
// Set max hops.
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
int hops = 255;
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
if(setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)) < 0)
2011-09-13 21:26:12 +02:00
{
close(fd);
2011-09-16 17:06:36 +02:00
ERR("iface::open_ifd() failed IPV6_MULTICAST_HOPS");
return strong_ptr<iface>();
2011-09-13 21:26:12 +02:00
}
2011-09-17 23:38:08 +02:00
if(setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, sizeof(hops)) < 0)
{
close(fd);
ERR("iface::open_ifd() failed IPV6_UNICAST_HOPS");
return strong_ptr<iface>();
}
2011-09-16 17:06:36 +02:00
// Switch to non-blocking mode.
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
int on = 1;
if(ioctl(fd, FIONBIO, (char *)&on) < 0)
2011-09-13 21:26:12 +02:00
{
2011-09-16 17:06:36 +02:00
close(fd);
ERR("Failed to switch to non-blocking on interface '%s'", name.c_str());
return strong_ptr<iface>();
2011-09-13 21:26:12 +02:00
}
// Set up filter.
struct icmp6_filter filter;
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
if(setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) < 0)
{
ERR("Failed to set filter");
return strong_ptr<iface>();
2011-09-13 21:26:12 +02:00
}
// Set up an instance of 'iface'.
strong_ptr<iface> ifa;
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
if(it == _map.end())
{
ifa = new iface();
2011-09-13 21:26:12 +02:00
ifa->_name = name;
2011-09-17 01:10:23 +02:00
ifa->_ptr = ifa;
2011-09-16 17:06:36 +02:00
2011-09-17 01:10:23 +02:00
_map[name] = ifa;
2011-09-16 17:06:36 +02:00
}
else
{
ifa = it->second;
}
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
ifa->_ifd = fd;
memcpy(&ifa->hwaddr, ifr.ifr_hwaddr.sa_data, sizeof(struct ether_addr));
2011-09-13 21:26:12 +02:00
fixup_pollfds();
return ifa;
}
ssize_t iface::read(int fd, struct sockaddr *saddr, uint8_t *msg, size_t size)
2011-09-13 21:26:12 +02:00
{
struct msghdr mhdr;
struct iovec iov;
char cbuf[256];
int len;
if(!msg || (size < 0))
return -1;
iov.iov_len = size;
2011-09-16 17:06:36 +02:00
iov.iov_base = (caddr_t)msg;
2011-09-13 21:26:12 +02:00
memset(&mhdr, 0, sizeof(mhdr));
mhdr.msg_name = (caddr_t)saddr;
mhdr.msg_namelen = sizeof(struct sockaddr);
2011-09-13 21:26:12 +02:00
mhdr.msg_iov = &iov;
mhdr.msg_iovlen = 1;
2011-09-16 17:06:36 +02:00
if((len = recvmsg(fd, &mhdr, 0)) < 0)
2011-09-13 21:26:12 +02:00
return -1;
if(len < sizeof(struct icmp6_hdr))
return -1;
DBG("iface::read() len=%d", len);
2011-09-13 21:26:12 +02:00
return len;
}
2011-09-16 17:06:36 +02:00
ssize_t iface::write(int fd, const address& daddr, const uint8_t *msg, size_t size)
2011-09-13 21:26:12 +02:00
{
struct sockaddr_in6 daddr_tmp;
struct msghdr mhdr;
struct iovec iov;
memset(&daddr_tmp, 0, sizeof(struct sockaddr_in6));
daddr_tmp.sin6_family = AF_INET6;
daddr_tmp.sin6_port = htons(IPPROTO_ICMPV6); // Needed?
2011-09-14 10:53:21 +02:00
memcpy(&daddr_tmp.sin6_addr, &daddr.const_addr(), sizeof(struct in6_addr));
2011-09-13 21:26:12 +02:00
iov.iov_len = size;
iov.iov_base = (caddr_t)msg;
memset(&mhdr, 0, sizeof(mhdr));
mhdr.msg_name = (caddr_t)&daddr_tmp;
mhdr.msg_namelen = sizeof(sockaddr_in6);
mhdr.msg_iov = &iov;
mhdr.msg_iovlen = 1;
DBG("iface::write() daddr=%s, len=%d", daddr.to_string().c_str(), size);
2011-09-13 21:26:12 +02:00
int len;
2011-09-16 17:06:36 +02:00
if((len = sendmsg(fd, &mhdr, 0)) < 0)
return -1;
return len;
}
ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr)
{
struct sockaddr_ll t_saddr;
2011-09-16 17:06:36 +02:00
uint8_t msg[256];
ssize_t len;
if((len = read(_pfd, (struct sockaddr *)&t_saddr, msg, sizeof(msg))) < 0)
2011-09-13 21:26:12 +02:00
return -1;
2011-09-17 01:10:23 +02:00
struct ip6_hdr *ip6h =
2011-09-16 17:06:36 +02:00
(struct ip6_hdr *)(msg + ETH_HLEN);
2011-09-17 01:10:23 +02:00
struct icmp6_hdr *icmph =
2011-09-16 17:06:36 +02:00
(struct icmp6_hdr *)(msg + ETH_HLEN + sizeof( struct ip6_hdr));
struct nd_neighbor_solicit *ns =
(struct nd_neighbor_solicit *)(msg + ETH_HLEN + sizeof( struct ip6_hdr));
taddr = ns->nd_ns_target;
daddr = ip6h->ip6_dst;
saddr = ip6h->ip6_src;
2011-09-17 01:10:23 +02:00
DBG("iface::read_solicit() saddr=%s, daddr=%s, taddr=%s, len=%d",
saddr.to_string().c_str(), daddr.to_string().c_str(),
2011-09-17 01:10:23 +02:00
taddr.to_string().c_str(), len);
2011-09-13 21:26:12 +02:00
return len;
}
2011-09-14 10:53:21 +02:00
ssize_t iface::write_solicit(const address& taddr)
2011-09-13 21:26:12 +02:00
{
2011-09-16 17:06:36 +02:00
char buf[128];
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
memset(buf, 0, sizeof(buf));
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
struct nd_neighbor_solicit *ns =
(struct nd_neighbor_solicit *)&buf[0];
struct nd_opt_hdr *opt =
(struct nd_opt_hdr *)&buf[sizeof(struct nd_neighbor_solicit)];
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
opt->nd_opt_len = 1;
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
memcpy(&ns->nd_ns_target, &taddr.const_addr(), sizeof(struct in6_addr));
memcpy(buf + sizeof(struct nd_neighbor_solicit) + sizeof(struct nd_opt_hdr), &hwaddr, 6);
// FIXME: Alright, I'm lazy.
static address multicast("ff02::1:ff00:0000");
address daddr;
2011-09-13 21:26:12 +02:00
daddr = multicast;
2011-09-14 10:53:21 +02:00
daddr.addr().s6_addr[13] = taddr.const_addr().s6_addr[13];
daddr.addr().s6_addr[14] = taddr.const_addr().s6_addr[14];
daddr.addr().s6_addr[15] = taddr.const_addr().s6_addr[15];
2011-09-13 21:26:12 +02:00
DBG("iface::write_solicit() taddr=%s, daddr=%s",
taddr.to_string().c_str(), daddr.to_string().c_str());
2011-09-16 17:06:36 +02:00
return write(_ifd, daddr, (uint8_t *)buf, sizeof(struct nd_neighbor_solicit) + sizeof(struct nd_opt_hdr) + 6);
2011-09-13 21:26:12 +02:00
}
2011-09-14 10:53:21 +02:00
ssize_t iface::write_advert(const address& daddr, const address& taddr)
{
2011-09-16 17:06:36 +02:00
char buf[128];
2011-09-14 13:36:56 +02:00
memset(buf, 0, sizeof(buf));
struct nd_neighbor_advert *na =
(struct nd_neighbor_advert *)&buf[0];
struct nd_opt_hdr *opt =
(struct nd_opt_hdr *)&buf[sizeof(struct nd_neighbor_advert)];
opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
opt->nd_opt_len = 1;
na->nd_na_type = ND_NEIGHBOR_ADVERT;
2011-09-17 23:38:08 +02:00
na->nd_na_flags_reserved = ND_NA_FLAG_SOLICITED; // | ND_NA_FLAG_ROUTER;
2011-09-14 10:53:21 +02:00
2011-09-14 13:36:56 +02:00
memcpy(&na->nd_na_target, &taddr.const_addr(), sizeof(struct in6_addr));
2011-09-14 10:53:21 +02:00
2011-09-14 13:36:56 +02:00
memcpy(buf + sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr), &hwaddr, 6);
2011-09-16 17:06:36 +02:00
DBG("iface::write_advert() daddr=%s, taddr=%s",
daddr.to_string().c_str(), taddr.to_string().c_str());
return write(_ifd, daddr, (uint8_t *)buf, sizeof(struct nd_neighbor_advert) +
sizeof(struct nd_opt_hdr) + 6);
2011-09-14 10:53:21 +02:00
}
2011-09-16 17:06:36 +02:00
ssize_t iface::read_advert(address& saddr, address& taddr)
2011-09-13 21:26:12 +02:00
{
struct sockaddr_in6 t_saddr;
2011-09-13 21:26:12 +02:00
uint8_t msg[256];
2011-09-16 17:06:36 +02:00
ssize_t len;
2011-09-13 21:26:12 +02:00
if((len = read(_ifd, (struct sockaddr *)&t_saddr, msg, sizeof(msg))) < 0)
2011-09-13 21:26:12 +02:00
return -1;
saddr = t_saddr.sin6_addr;
2011-09-16 17:06:36 +02:00
if(((struct icmp6_hdr *)msg)->icmp6_type != ND_NEIGHBOR_ADVERT)
2011-09-13 21:26:12 +02:00
return -1;
2011-09-16 17:06:36 +02:00
taddr = ((struct nd_neighbor_solicit *)msg)->nd_ns_target;
2011-09-13 21:26:12 +02:00
2011-09-17 01:10:23 +02:00
DBG("iface::read_advert() saddr=%s, taddr=%s, len=%d",
saddr.to_string().c_str(), taddr.to_string().c_str(), len);
2011-09-16 17:06:36 +02:00
return len;
}
2011-09-13 21:26:12 +02:00
void iface::fixup_pollfds()
{
2011-09-16 17:06:36 +02:00
_pollfds.resize(_map.size() * 2);
2011-09-13 21:26:12 +02:00
int i = 0;
DBG("iface::fixup_pollfds() _map.size()=%d", _map.size());
for(std::map<std::string, strong_ptr<iface> >::iterator it = _map.begin();
2011-09-14 13:36:56 +02:00
it != _map.end(); it++)
2011-09-13 21:26:12 +02:00
{
2011-09-17 01:10:23 +02:00
_pollfds[i].fd = it->second->_ifd;
_pollfds[i].events = POLLIN;
_pollfds[i].revents = 0;
i++;
_pollfds[i].fd = it->second->_pfd;
2011-09-13 21:26:12 +02:00
_pollfds[i].events = POLLIN;
_pollfds[i].revents = 0;
i++;
}
}
void iface::remove_session(const strong_ptr<session>& se)
2011-09-13 21:26:12 +02:00
{
2011-09-14 10:53:21 +02:00
_sessions.remove(se);
2011-09-13 21:26:12 +02:00
}
void iface::add_session(const strong_ptr<session>& se)
2011-09-13 21:26:12 +02:00
{
_sessions.push_back(se);
}
int iface::poll_all()
{
if(_pollfds.size() == 0)
{
::sleep(1);
return 0;
}
2011-09-17 01:10:23 +02:00
assert(_pollfds.size() == _map.size() * 2);
2011-09-13 21:26:12 +02:00
int len;
2011-09-17 01:10:23 +02:00
if((len = ::poll(&_pollfds[0], _pollfds.size(), 50)) < 0)
2011-09-13 21:26:12 +02:00
return -1;
if(len == 0)
return 0;
2011-09-17 01:10:23 +02:00
std::map<std::string, strong_ptr<iface> >::iterator i_it = _map.begin();
2011-09-13 21:26:12 +02:00
2011-09-16 17:06:36 +02:00
int i = 0;
2011-09-17 01:10:23 +02:00
for(std::vector<struct pollfd>::iterator f_it = _pollfds.begin();
f_it != _pollfds.end(); f_it++)
2011-09-13 21:26:12 +02:00
{
2011-09-17 01:10:23 +02:00
assert(i_it != _map.end());
if(i && !(i % 2))
i_it++;
bool is_pfd = i++ % 2;
2011-09-16 17:06:36 +02:00
2011-09-14 13:36:56 +02:00
if(!(f_it->revents & POLLIN))
2011-09-13 21:26:12 +02:00
continue;
strong_ptr<iface> ifa = i_it->second;
2011-09-13 21:26:12 +02:00
address saddr, daddr, taddr;
2011-09-17 01:10:23 +02:00
if(is_pfd)
2011-09-16 17:06:36 +02:00
{
if(ifa->read_solicit(saddr, daddr, taddr) < 0)
{
ERR("Failed to read from interface '%s'", ifa->_name.c_str());
continue;
}
if(!saddr.is_unicast() || !daddr.is_multicast())
continue;
ifa->_pr->handle_solicit(saddr, daddr, taddr);
2011-09-16 17:06:36 +02:00
}
else
{
if(ifa->read_advert(saddr, taddr) < 0)
{
ERR("Failed to read from interface '%s'", ifa->_name.c_str());
continue;
}
for(std::list<weak_ptr<session> >::iterator s_it = ifa->_sessions.begin();
2011-09-16 17:06:36 +02:00
s_it != ifa->_sessions.end(); s_it++)
{
if(((*s_it)->taddr() == taddr) && ((*s_it)->status() == session::WAITING))
{
(*s_it)->handle_advert();
break;
}
}
}
2011-09-13 21:26:12 +02:00
}
return 0;
}
2011-09-16 17:06:36 +02:00
int iface::allmulti(int state)
{
struct ifreq ifr;
DBG("iface::allmulti() state=%d, _name=\"%s\"",
state, _name.c_str());
state = !!state;
strncpy(ifr.ifr_name, _name.c_str(), IFNAMSIZ);
if(ioctl(_pfd, SIOCGIFFLAGS, &ifr) < 0)
return -1;
int old_state = !!(ifr.ifr_flags & IFF_ALLMULTI);
if(state == old_state)
return old_state;
if(state)
ifr.ifr_flags |= IFF_ALLMULTI;
else
ifr.ifr_flags &= ~IFF_ALLMULTI;
if(ioctl(_pfd, SIOCSIFFLAGS, &ifr) < 0)
return -1;
return old_state;
}
2011-09-13 21:26:12 +02:00
const std::string& iface::name() const
{
return _name;
}
void iface::pr(const strong_ptr<proxy>& pr)
{
_pr = pr;
}
const strong_ptr<proxy>& iface::pr() const
{
return _pr;
}
2011-09-13 21:26:12 +02:00
__NDPPD_NS_END