2013-05-01 14:52:55 +02:00
|
|
|
#include "capwap.h"
|
|
|
|
#include "capwap_network.h"
|
2013-05-27 21:33:23 +02:00
|
|
|
#include "capwap_protocol.h"
|
2013-05-01 14:52:55 +02:00
|
|
|
#include <linux/netlink.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include <net/if_arp.h>
|
2014-12-27 18:45:09 +01:00
|
|
|
#include <arpa/inet.h>
|
2013-05-01 14:52:55 +02:00
|
|
|
#include <ifaddrs.h>
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* */
|
|
|
|
#define CAPWAP_ROUTE_NOT_FOUND 0
|
|
|
|
#define CAPWAP_ROUTE_LOCAL_ADDRESS 1
|
|
|
|
#define CAPWAP_ROUTE_VIA_ADDRESS 2
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
/* Prepare socket to bind */
|
2014-09-10 21:58:23 +02:00
|
|
|
static int capwap_configure_socket(int sock, int socketfamily, const char* bindinterface) {
|
2013-05-01 14:52:55 +02:00
|
|
|
int flag;
|
|
|
|
|
|
|
|
ASSERT(sock >= 0);
|
|
|
|
ASSERT((socketfamily == AF_INET) || (socketfamily == AF_INET6));
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* Retrieve information into recvfrom local address */
|
|
|
|
if (socketfamily == AF_INET) {
|
|
|
|
#ifdef IP_PKTINFO
|
|
|
|
flag = 1;
|
|
|
|
if (setsockopt(sock, SOL_IP, IP_PKTINFO, &flag, sizeof(int))) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_ERR, "Unable set IP_PKTINFO to socket '%d'", errno);
|
2014-09-10 21:58:23 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#elif defined IP_RECVDSTADDR
|
|
|
|
flag = 1;
|
|
|
|
if (setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &flag, sizeof(int))) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_ERR, "Unable set IP_RECVDSTADDR to socket '%d'", errno);
|
2014-09-10 21:58:23 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#error "No method of getting the destination ip address supported"
|
|
|
|
#endif
|
|
|
|
} else if (socketfamily == AF_INET6) {
|
|
|
|
flag = 1;
|
|
|
|
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &flag, sizeof(int))) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_ERR, "Unable set IPV6_RECVPKTINFO to socket '%d'", errno);
|
2013-05-01 14:52:55 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reuse address */
|
|
|
|
flag = 1;
|
|
|
|
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int))) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_ERR, "Unable set SO_REUSEADDR to socket");
|
2013-05-01 14:52:55 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Broadcast */
|
2014-09-10 21:58:23 +02:00
|
|
|
flag = 1;
|
|
|
|
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(int))) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_ERR, "Unable set SO_BROADCAST to socket");
|
2014-09-10 21:58:23 +02:00
|
|
|
return -1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Bind to interface */
|
|
|
|
if ((bindinterface != NULL) && (bindinterface[0] != 0)) {
|
|
|
|
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, bindinterface, strlen(bindinterface) + 1)) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_ERR, "Unable set SO_BINDTODEVICE to socket %d", errno);
|
2013-05-01 14:52:55 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable checksum */
|
|
|
|
if (socketfamily == AF_INET) {
|
|
|
|
flag = 1;
|
2014-09-10 21:58:23 +02:00
|
|
|
if (setsockopt(sock, SOL_SOCKET, SO_NO_CHECK, &flag, sizeof(int))) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_ERR, "Unable set SO_NO_CHECK to socket");
|
2013-05-01 14:52:55 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Listen socket */
|
2014-09-10 21:58:23 +02:00
|
|
|
static int capwap_prepare_bind_socket(struct capwap_network* net) {
|
|
|
|
int sock;
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
ASSERT(net != NULL);
|
2014-09-10 21:58:23 +02:00
|
|
|
ASSERT((net->localaddr.ss.ss_family == AF_INET) || (net->localaddr.ss.ss_family == AF_INET6));
|
|
|
|
|
|
|
|
/* Create socket */
|
|
|
|
sock = socket(net->localaddr.ss.ss_family, SOCK_DGRAM, IPPROTO_UDP);
|
|
|
|
if (sock < 0) {
|
|
|
|
return -1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* Prepare binding */
|
|
|
|
if (capwap_configure_socket(sock, net->localaddr.ss.ss_family, net->bindiface)) {
|
|
|
|
close(sock);
|
|
|
|
return -1;
|
|
|
|
}
|
2013-05-01 14:52:55 +02:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* Binding */
|
|
|
|
if (bind(sock, &net->localaddr.sa, sizeof(union sockaddr_capwap))) {
|
|
|
|
close(sock);
|
|
|
|
return -1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* Retrieve port */
|
|
|
|
if (!CAPWAP_GET_NETWORK_PORT(&net->localaddr)) {
|
|
|
|
union sockaddr_capwap sockinfo;
|
|
|
|
socklen_t sockinfolen = sizeof(union sockaddr_capwap);
|
2013-05-01 14:52:55 +02:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
if (getsockname(sock, &sockinfo.sa, &sockinfolen) < 0) {
|
|
|
|
close(sock);
|
|
|
|
return -1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
/* */
|
|
|
|
CAPWAP_COPY_NETWORK_PORT(&net->localaddr, &sockinfo);
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
/* */
|
|
|
|
net->socket = sock;
|
|
|
|
return 0;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* */
|
|
|
|
int capwap_bind_sockets(struct capwap_network* net) {
|
|
|
|
int result;
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
ASSERT(net != NULL);
|
2014-09-10 21:58:23 +02:00
|
|
|
ASSERT((net->localaddr.ss.ss_family == AF_INET) || (net->localaddr.ss.ss_family == AF_INET6));
|
|
|
|
|
|
|
|
/* */
|
|
|
|
result = capwap_prepare_bind_socket(net);
|
|
|
|
if (result && net->localaddr.ss.ss_family == AF_INET6) {
|
|
|
|
uint16_t port = net->localaddr.sin6.sin6_port;
|
|
|
|
|
|
|
|
net->localaddr.ss.ss_family = AF_INET;
|
|
|
|
net->localaddr.sin.sin_port = port;
|
|
|
|
|
|
|
|
result = capwap_prepare_bind_socket(net);
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
return result;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2016-03-02 15:13:56 +01:00
|
|
|
/* */
|
|
|
|
int capwap_connect_socket(struct capwap_network* net, union sockaddr_capwap *peeraddr)
|
|
|
|
{
|
|
|
|
if (net->socket < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return connect(net->socket, &peeraddr->sa, sizeof(peeraddr->ss));
|
|
|
|
}
|
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Close socket */
|
|
|
|
void capwap_close_sockets(struct capwap_network* net) {
|
|
|
|
ASSERT(net != NULL);
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
if (net->socket >= 0) {
|
|
|
|
shutdown(net->socket, SHUT_RDWR);
|
|
|
|
close(net->socket);
|
|
|
|
net->socket = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-02 15:13:56 +01:00
|
|
|
/* */
|
|
|
|
int capwap_getsockname(struct capwap_network* net, union sockaddr_capwap *addr)
|
|
|
|
{
|
|
|
|
socklen_t addrlen = sizeof(addr->ss);
|
|
|
|
|
|
|
|
if (net->socket < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return getsockname(net->socket, &addr->sa, &addrlen);
|
|
|
|
}
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* */
|
|
|
|
int capwap_ipv4_mapped_ipv6(union sockaddr_capwap* addr) {
|
2016-02-08 17:33:00 +01:00
|
|
|
uint32_t inetaddr;
|
|
|
|
uint16_t inetport;
|
|
|
|
uint32_t* inet6addr;
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
ASSERT(addr != NULL);
|
|
|
|
|
|
|
|
/* */
|
2016-02-08 17:33:00 +01:00
|
|
|
inet6addr = (uint32_t *)&addr->sin6.sin6_addr.s6_addr[0];
|
2014-09-10 21:58:23 +02:00
|
|
|
if (addr->ss.ss_family == AF_INET) {
|
|
|
|
inetaddr = addr->sin.sin_addr.s_addr;
|
|
|
|
inetport = addr->sin.sin_port;
|
|
|
|
|
|
|
|
/* Convert into IPv4 mapped IPv6 */
|
2016-02-08 17:33:00 +01:00
|
|
|
addr->sin6.sin6_family = AF_INET6;
|
2014-09-10 21:58:23 +02:00
|
|
|
inet6addr[0] = 0;
|
|
|
|
inet6addr[1] = 0;
|
|
|
|
inet6addr[2] = htonl(0xffff);
|
|
|
|
inet6addr[3] = inetaddr;
|
|
|
|
addr->sin6.sin6_port = inetport;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
} else if ((addr->ss.ss_family == AF_INET6) && (IN6_IS_ADDR_V4MAPPED(&addr->sin6.sin6_addr))) {
|
|
|
|
inetaddr = inet6addr[3];
|
|
|
|
inetport = addr->sin6.sin6_port;
|
|
|
|
|
|
|
|
/* Convert into IPv4 */
|
|
|
|
addr->sin.sin_family = AF_INET;
|
|
|
|
addr->sin.sin_addr.s_addr = inetaddr;
|
|
|
|
addr->sin.sin_port = inetport;
|
|
|
|
|
|
|
|
return 1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
return 0;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare ip address */
|
2014-09-10 21:58:23 +02:00
|
|
|
int capwap_compare_ip(union sockaddr_capwap* addr1, union sockaddr_capwap* addr2) {
|
2013-05-01 14:52:55 +02:00
|
|
|
ASSERT(addr1 != NULL);
|
|
|
|
ASSERT(addr2 != NULL);
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
if (addr1->ss.ss_family != addr2->ss.ss_family) {
|
|
|
|
return -1;
|
|
|
|
}
|
2013-05-01 14:52:55 +02:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* */
|
|
|
|
if (addr1->ss.ss_family == AF_INET) {
|
|
|
|
if ((addr1->sin.sin_addr.s_addr == addr2->sin.sin_addr.s_addr) && (addr1->sin.sin_port == addr2->sin.sin_port)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else if (addr1->ss.ss_family == AF_INET6) {
|
|
|
|
if (!memcmp(&addr1->sin6.sin6_addr, &addr2->sin6.sin6_addr, sizeof(struct in6_addr)) && (addr1->sin6.sin6_port == addr2->sin6.sin6_port)) {
|
|
|
|
return 0;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
return -1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2013-11-02 19:08:05 +01:00
|
|
|
/* Receive packet from fd */
|
2016-03-30 10:39:04 +02:00
|
|
|
ssize_t capwap_recvfrom(int sock, void* buffer, size_t len,
|
|
|
|
union sockaddr_capwap* fromaddr,
|
|
|
|
union sockaddr_capwap* toaddr)
|
|
|
|
{
|
|
|
|
ssize_t r = 0;
|
2013-11-02 19:08:05 +01:00
|
|
|
char cbuf[256];
|
2016-03-30 10:39:04 +02:00
|
|
|
struct iovec iov = {
|
|
|
|
.iov_base = buffer,
|
|
|
|
.iov_len = len
|
|
|
|
};
|
|
|
|
struct msghdr msgh = {
|
|
|
|
.msg_control = cbuf,
|
|
|
|
.msg_controllen = sizeof(cbuf),
|
|
|
|
.msg_name = &fromaddr->ss,
|
|
|
|
.msg_namelen = sizeof(struct sockaddr_storage),
|
|
|
|
.msg_iov = &iov,
|
|
|
|
.msg_iovlen = 1,
|
|
|
|
.msg_flags = 0
|
|
|
|
};
|
|
|
|
struct cmsghdr* cmsg;
|
2013-11-02 19:08:05 +01:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
ASSERT(sock >= 0);
|
2013-11-02 19:08:05 +01:00
|
|
|
ASSERT(buffer != NULL);
|
2016-03-30 10:39:04 +02:00
|
|
|
ASSERT(len > 0);
|
2014-09-10 21:58:23 +02:00
|
|
|
ASSERT(fromaddr != NULL);
|
2013-11-02 19:08:05 +01:00
|
|
|
|
|
|
|
/* Receive packet with recvmsg */
|
2016-03-30 10:39:04 +02:00
|
|
|
do {
|
|
|
|
r = recvmsg(sock, &msgh, MSG_DONTWAIT);
|
|
|
|
} while (r < 0 && errno == EINTR);
|
|
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
if (errno != EAGAIN)
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_WARNING, "Unable to recv packet, recvmsg return %zd with error %d", r, errno);
|
2016-03-30 10:39:04 +02:00
|
|
|
return r;
|
2014-09-10 21:58:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if IPv4 is mapped into IPv6 */
|
|
|
|
if (fromaddr->ss.ss_family == AF_INET6) {
|
2014-12-27 18:45:09 +01:00
|
|
|
if (!capwap_ipv4_mapped_ipv6(fromaddr)) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_WARNING, "Receive packet with invalid fromaddr");
|
2014-12-27 18:45:09 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
}
|
2013-11-02 19:08:05 +01:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* */
|
|
|
|
if (toaddr) {
|
2013-11-02 19:08:05 +01:00
|
|
|
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
|
2013-05-01 14:52:55 +02:00
|
|
|
#ifdef IP_PKTINFO
|
2013-11-02 19:08:05 +01:00
|
|
|
if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
|
2016-02-04 14:59:20 +01:00
|
|
|
struct in_pktinfo *pi = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
2014-09-10 21:58:23 +02:00
|
|
|
toaddr->sin.sin_family = AF_INET;
|
2016-02-04 14:59:20 +01:00
|
|
|
memcpy(&toaddr->sin.sin_addr, &pi->ipi_addr, sizeof(struct in_addr));
|
2013-11-02 19:08:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2013-05-01 14:52:55 +02:00
|
|
|
#elif defined IP_RECVDSTADDR
|
2013-11-02 19:08:05 +01:00
|
|
|
if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
|
2014-09-10 21:58:23 +02:00
|
|
|
toaddr->sin.sin_family = AF_INET;
|
|
|
|
memcpy(&toaddr->sin.sin_addr, (struct in_addr*)CMSG_DATA(cmsg), sizeof(struct in_addr));
|
2013-11-02 19:08:05 +01:00
|
|
|
break;
|
|
|
|
}
|
2013-05-01 14:52:55 +02:00
|
|
|
#endif
|
2013-11-02 19:08:05 +01:00
|
|
|
if ((cmsg->cmsg_level == IPPROTO_IPV6) && ((cmsg->cmsg_type == IPV6_PKTINFO) || (cmsg->cmsg_type == IPV6_RECVPKTINFO))) {
|
2016-02-04 14:59:20 +01:00
|
|
|
struct in6_pktinfo *pi6 = (struct in6_pktinfo*) CMSG_DATA(cmsg);
|
2014-09-10 21:58:23 +02:00
|
|
|
toaddr->sin6.sin6_family = AF_INET6;
|
2016-02-04 14:59:20 +01:00
|
|
|
memcpy(&toaddr->sin6.sin6_addr, &pi6->ipi6_addr, sizeof(struct in6_addr));
|
2013-11-02 19:08:05 +01:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* Check if IPv4 is mapped into IPv6 */
|
|
|
|
if (fromaddr->ss.ss_family == AF_INET) {
|
|
|
|
if (!capwap_ipv4_mapped_ipv6(toaddr)) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_WARNING, "Receive packet with invalid toaddr");
|
2014-09-10 21:58:23 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:45:09 +01:00
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
|
|
char strfromaddr[INET6_ADDRSTRLEN];
|
|
|
|
char strtoaddr[INET6_ADDRSTRLEN];
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_DEBUG, "Receive packet from %s:%d to %s with size %zd",
|
2016-03-30 10:39:04 +02:00
|
|
|
capwap_address_to_string(fromaddr, strfromaddr, INET6_ADDRSTRLEN),
|
|
|
|
(int)CAPWAP_GET_NETWORK_PORT(fromaddr),
|
|
|
|
capwap_address_to_string(toaddr, strtoaddr, INET6_ADDRSTRLEN), r);
|
2014-12-27 18:45:09 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-03-30 10:39:04 +02:00
|
|
|
return r;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
void capwap_network_init(struct capwap_network* net) {
|
|
|
|
ASSERT(net != NULL);
|
|
|
|
|
2013-05-28 22:22:16 +02:00
|
|
|
/* */
|
|
|
|
memset(net, 0, sizeof(struct capwap_network));
|
|
|
|
|
|
|
|
/* */
|
2014-09-10 21:58:23 +02:00
|
|
|
net->localaddr.ss.ss_family = AF_UNSPEC;
|
|
|
|
net->socket = -1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
int capwap_network_set_pollfd(struct capwap_network* net, struct pollfd* fds, int fdscount) {
|
|
|
|
ASSERT(net != NULL);
|
2014-06-07 22:37:19 +02:00
|
|
|
ASSERT(fdscount >= 0);
|
|
|
|
|
|
|
|
/* */
|
2014-09-10 21:58:23 +02:00
|
|
|
if (!fds) {
|
|
|
|
return (!fdscount ? 1 : -1);
|
|
|
|
} else if (fdscount < 1) {
|
2013-05-01 14:52:55 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2013-11-02 19:08:05 +01:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Configure fds array */
|
2014-09-10 21:58:23 +02:00
|
|
|
fds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
|
|
|
|
fds[0].fd = net->socket;
|
2013-11-02 19:08:05 +01:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
return 1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
2014-09-10 21:58:23 +02:00
|
|
|
int capwap_sendto(int sock, void* buffer, int size, union sockaddr_capwap* toaddr) {
|
2014-12-27 18:45:09 +01:00
|
|
|
int result;
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
ASSERT(sock >= 0);
|
|
|
|
ASSERT(buffer != NULL);
|
|
|
|
ASSERT(size > 0);
|
2014-09-10 21:58:23 +02:00
|
|
|
ASSERT(toaddr != NULL);
|
2013-05-01 14:52:55 +02:00
|
|
|
|
2014-12-27 18:45:09 +01:00
|
|
|
do {
|
2014-09-10 21:58:23 +02:00
|
|
|
result = sendto(sock, buffer, size, 0, &toaddr->sa, sizeof(union sockaddr_capwap));
|
2014-12-27 18:45:09 +01:00
|
|
|
if ((result < 0) && (errno != EAGAIN) && (errno != EINTR)) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_WARNING, "Unable to send packet, sendto return %d with error %d", result, errno);
|
2014-12-27 18:45:09 +01:00
|
|
|
return -errno;
|
|
|
|
} else if ((result > 0) && (result != size)) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_WARNING, "Unable to send packet, mismatch sendto size %d - %d", size, result);
|
2014-12-27 18:45:09 +01:00
|
|
|
return -ENETRESET;
|
2013-05-27 21:33:23 +02:00
|
|
|
}
|
2014-12-27 18:45:09 +01:00
|
|
|
} while (result < 0);
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
|
|
char strtoaddr[INET6_ADDRSTRLEN];
|
2016-04-29 08:32:49 +02:00
|
|
|
log_printf(LOG_DEBUG, "Sent packet to %s:%d with result %d",
|
|
|
|
capwap_address_to_string(toaddr, strtoaddr, INET6_ADDRSTRLEN),
|
|
|
|
(int)CAPWAP_GET_NETWORK_PORT(toaddr), result);
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2014-12-27 18:45:09 +01:00
|
|
|
#endif
|
2013-05-01 14:52:55 +02:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
return result;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2013-05-27 21:33:23 +02:00
|
|
|
/* */
|
2014-09-10 21:58:23 +02:00
|
|
|
int capwap_sendto_fragmentpacket(int sock, struct capwap_list* fragmentlist, union sockaddr_capwap* toaddr) {
|
2014-12-27 18:45:09 +01:00
|
|
|
int err;
|
2013-05-27 21:33:23 +02:00
|
|
|
struct capwap_list_item* item;
|
|
|
|
|
|
|
|
ASSERT(sock >= 0);
|
|
|
|
ASSERT(fragmentlist != NULL);
|
2014-09-10 21:58:23 +02:00
|
|
|
ASSERT(toaddr != NULL);
|
2013-05-27 21:33:23 +02:00
|
|
|
|
|
|
|
item = fragmentlist->first;
|
|
|
|
while (item) {
|
|
|
|
struct capwap_fragment_packet_item* fragmentpacket = (struct capwap_fragment_packet_item*)item->item;
|
|
|
|
ASSERT(fragmentpacket != NULL);
|
|
|
|
ASSERT(fragmentpacket->offset > 0);
|
|
|
|
|
2014-12-27 18:45:09 +01:00
|
|
|
err = capwap_sendto(sock, fragmentpacket->buffer, fragmentpacket->offset, toaddr);
|
|
|
|
if (err <= 0) {
|
2016-03-30 14:47:57 +02:00
|
|
|
log_printf(LOG_WARNING, "Unable to send fragment, sentto return error %d", err);
|
2013-05-27 21:33:23 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
|
|
|
item = item->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Convert string into address */
|
2014-09-10 21:58:23 +02:00
|
|
|
int capwap_address_from_string(const char* ip, union sockaddr_capwap* sockaddr) {
|
2013-07-15 18:48:47 +02:00
|
|
|
char* pos;
|
|
|
|
char* buffer;
|
2013-05-01 14:52:55 +02:00
|
|
|
struct addrinfo hints;
|
|
|
|
struct addrinfo* info = NULL;
|
|
|
|
char* service = NULL;
|
2013-07-15 18:48:47 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
ASSERT(ip != NULL);
|
2014-09-10 21:58:23 +02:00
|
|
|
ASSERT(sockaddr != NULL);
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
/* Init */
|
2014-09-10 21:58:23 +02:00
|
|
|
memset(sockaddr, 0, sizeof(union sockaddr_capwap));
|
2013-07-15 18:48:47 +02:00
|
|
|
if (!*ip) {
|
2013-05-01 14:52:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-15 18:48:47 +02:00
|
|
|
/* */
|
|
|
|
buffer = capwap_duplicate_string(ip);
|
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* */
|
|
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
hints.ai_flags = 0;
|
2013-07-15 18:48:47 +02:00
|
|
|
|
|
|
|
/* Parsing address */
|
|
|
|
pos = buffer;
|
2013-05-01 14:52:55 +02:00
|
|
|
if (*pos == '[') {
|
|
|
|
char* temp = pos + 1;
|
|
|
|
|
|
|
|
pos = temp;
|
|
|
|
hints.ai_family = AF_INET6;
|
|
|
|
hints.ai_flags |= AI_NUMERICHOST;
|
2013-07-15 18:48:47 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
temp = strchr(temp, ']');
|
|
|
|
if (!temp) {
|
2013-07-15 18:48:47 +02:00
|
|
|
capwap_free(buffer);
|
2013-05-01 14:52:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2013-07-15 18:48:47 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
*temp = 0;
|
|
|
|
if (*(temp + 1) == ':') {
|
|
|
|
service = temp + 2;
|
|
|
|
hints.ai_flags |= AI_NUMERICSERV;
|
|
|
|
} else if (*(temp + 1)) {
|
2013-07-15 18:48:47 +02:00
|
|
|
capwap_free(buffer);
|
2013-05-01 14:52:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
char* temp = strchr(pos, ':');
|
|
|
|
if (temp) {
|
|
|
|
*temp = 0;
|
|
|
|
service = temp + 1;
|
|
|
|
hints.ai_flags |= AI_NUMERICSERV;
|
|
|
|
}
|
|
|
|
}
|
2013-07-15 18:48:47 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Parsing address */
|
|
|
|
if (getaddrinfo(pos, service, &hints, &info)) {
|
2013-07-15 18:48:47 +02:00
|
|
|
capwap_free(buffer);
|
2013-05-01 14:52:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy address */
|
2014-09-10 21:58:23 +02:00
|
|
|
memcpy(&sockaddr->ss, info->ai_addr, info->ai_addrlen);
|
2013-07-15 18:48:47 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
freeaddrinfo(info);
|
2013-07-15 18:48:47 +02:00
|
|
|
capwap_free(buffer);
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:45:09 +01:00
|
|
|
/* Convert address to string */
|
|
|
|
const char* capwap_address_to_string(union sockaddr_capwap* sockaddr, char* ip, int len) {
|
|
|
|
ASSERT(sockaddr != NULL);
|
|
|
|
ASSERT(ip != NULL);
|
|
|
|
ASSERT(len > 0);
|
|
|
|
|
|
|
|
if ((sockaddr->ss.ss_family == AF_INET) && (len >= INET_ADDRSTRLEN)) {
|
|
|
|
if (!inet_ntop(AF_INET, &sockaddr->sin.sin_addr, ip, len)) {
|
|
|
|
*ip = 0;
|
|
|
|
}
|
|
|
|
} else if ((sockaddr->ss.ss_family == AF_INET6) && (len >= INET6_ADDRSTRLEN)) {
|
|
|
|
if (!inet_ntop(AF_INET6, &sockaddr->sin6.sin6_addr, ip, len)) {
|
|
|
|
*ip = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
*ip = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ip;
|
|
|
|
}
|
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Get macaddress from interface */
|
|
|
|
int capwap_get_macaddress_from_interface(const char* interface, char* macaddress) {
|
|
|
|
int sock;
|
|
|
|
struct ifreq ifr;
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
ASSERT(interface != NULL);
|
|
|
|
ASSERT(macaddress != NULL);
|
|
|
|
|
|
|
|
sock = socket(PF_PACKET, SOCK_RAW, 0);
|
|
|
|
if (sock < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&ifr, 0, sizeof(struct ifreq));
|
|
|
|
strcpy(ifr.ifr_name, interface);
|
|
|
|
|
|
|
|
if (!ioctl(sock, SIOCGIFHWADDR, &ifr)) {
|
2013-06-09 17:29:50 +02:00
|
|
|
result = ((ifr.ifr_hwaddr.sa_family == ARPHRD_EUI64) ? MACADDRESS_EUI64_LENGTH : MACADDRESS_EUI48_LENGTH);
|
2013-05-01 14:52:55 +02:00
|
|
|
memcpy(macaddress, ifr.ifr_hwaddr.sa_data, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
close(sock);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
2014-09-10 21:58:23 +02:00
|
|
|
static void capwap_get_network_address(union sockaddr_capwap* addr, union sockaddr_capwap* network, unsigned long bitsmask) {
|
2013-05-01 14:52:55 +02:00
|
|
|
unsigned long i;
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
ASSERT(addr != NULL);
|
|
|
|
ASSERT(network != NULL);
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
memcpy(network, addr, sizeof(union sockaddr_capwap));
|
2013-05-01 14:52:55 +02:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
if (addr->ss.ss_family == AF_INET) {
|
2013-05-01 14:52:55 +02:00
|
|
|
unsigned long mask = 0xffffffff;
|
|
|
|
|
|
|
|
for (i = bitsmask; i < 32; i++) {
|
|
|
|
mask <<= 1;
|
|
|
|
}
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
network->sin.sin_addr.s_addr &= htonl(mask);
|
2013-05-01 14:52:55 +02:00
|
|
|
} else {
|
|
|
|
unsigned long pos = bitsmask / 8;
|
|
|
|
unsigned long delta = bitsmask % 8;
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
if (!delta) {
|
2014-09-10 21:58:23 +02:00
|
|
|
pos -= 1; /* Optimize for all bits of pos equal 0 */
|
2013-05-01 14:52:55 +02:00
|
|
|
} else {
|
|
|
|
unsigned char mask = 0xff;
|
|
|
|
|
|
|
|
for (i = delta; i < 8; i++) {
|
|
|
|
mask <<= 1;
|
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
network->sin6.sin6_addr.s6_addr[pos] &= mask;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
for (i = pos + 1; i < 16; i++) {
|
2014-09-10 21:58:23 +02:00
|
|
|
network->sin6.sin6_addr.s6_addr[i] = 0;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
2014-09-10 21:58:23 +02:00
|
|
|
static int capwap_get_routeaddress(union sockaddr_capwap* localaddr, union sockaddr_capwap* peeraddr, char* iface, unsigned char table) {
|
|
|
|
int result = CAPWAP_ROUTE_NOT_FOUND;
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
int foundgateway = 0;
|
|
|
|
unsigned char gatewaytable = 0;
|
|
|
|
unsigned long gatewaymetric = 0;
|
2014-09-10 21:58:23 +02:00
|
|
|
union sockaddr_capwap gateway;
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
int nlsock;
|
|
|
|
struct sockaddr_nl nllocal;
|
|
|
|
socklen_t nllocaladdrlen;
|
|
|
|
int sndbuf = 32768;
|
|
|
|
int rcvbuf = 32768;
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
struct {
|
|
|
|
struct nlmsghdr nlh;
|
|
|
|
struct rtgenmsg g;
|
|
|
|
} req;
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
ASSERT(localaddr != NULL);
|
|
|
|
ASSERT(peeraddr != NULL);
|
|
|
|
|
|
|
|
/* */
|
|
|
|
memset(localaddr, 0, sizeof(union sockaddr_capwap));
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
/* Open netlink route socket */
|
|
|
|
nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
|
|
|
if (nlsock < 0) {
|
2014-09-10 21:58:23 +02:00
|
|
|
return CAPWAP_ROUTE_NOT_FOUND;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* Configure socket */
|
2013-05-01 14:52:55 +02:00
|
|
|
if (setsockopt(nlsock, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) < 0) {
|
|
|
|
close(nlsock);
|
2014-09-10 21:58:23 +02:00
|
|
|
return CAPWAP_ROUTE_NOT_FOUND;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (setsockopt(nlsock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) < 0) {
|
|
|
|
close(nlsock);
|
2014-09-10 21:58:23 +02:00
|
|
|
return CAPWAP_ROUTE_NOT_FOUND;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Bind */
|
|
|
|
memset(&nllocal, 0, sizeof(struct sockaddr_nl));
|
|
|
|
nllocal.nl_family = AF_NETLINK;
|
|
|
|
if (bind(nlsock, (struct sockaddr*)&nllocal, sizeof(struct sockaddr_nl)) < 0) {
|
|
|
|
close(nlsock);
|
2014-09-10 21:58:23 +02:00
|
|
|
return CAPWAP_ROUTE_NOT_FOUND;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check bind */
|
|
|
|
nllocaladdrlen = sizeof(struct sockaddr_nl);
|
|
|
|
if (getsockname(nlsock, (struct sockaddr*)&nllocal, &nllocaladdrlen) < 0) {
|
|
|
|
close(nlsock);
|
2014-09-10 21:58:23 +02:00
|
|
|
return CAPWAP_ROUTE_NOT_FOUND;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((nllocaladdrlen != sizeof(struct sockaddr_nl)) || (nllocal.nl_family != AF_NETLINK)) {
|
|
|
|
close(nlsock);
|
2014-09-10 21:58:23 +02:00
|
|
|
return CAPWAP_ROUTE_NOT_FOUND;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Send request */
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
req.nlh.nlmsg_len = sizeof(req);
|
|
|
|
req.nlh.nlmsg_type = RTM_GETROUTE;
|
|
|
|
req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
|
|
|
|
req.nlh.nlmsg_pid = 0;
|
|
|
|
req.nlh.nlmsg_seq = 0;
|
|
|
|
req.g.rtgen_family = AF_UNSPEC;
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
if (send(nlsock, (void*)&req, sizeof(req), 0) == sizeof(req)) {
|
2014-09-10 21:58:23 +02:00
|
|
|
int end = 0;
|
2013-05-01 14:52:55 +02:00
|
|
|
struct sockaddr_nl nladdr;
|
|
|
|
struct iovec iov;
|
|
|
|
char buf[16384];
|
|
|
|
struct msghdr msg = {
|
|
|
|
.msg_name = &nladdr,
|
|
|
|
.msg_namelen = sizeof(struct sockaddr_nl),
|
|
|
|
.msg_iov = &iov,
|
|
|
|
.msg_iovlen = 1,
|
|
|
|
};
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
iov.iov_base = buf;
|
2014-09-10 21:58:23 +02:00
|
|
|
while ((result == CAPWAP_ROUTE_NOT_FOUND) && !end) {
|
2013-05-01 14:52:55 +02:00
|
|
|
int status;
|
|
|
|
struct nlmsghdr *h;
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Receive response */
|
|
|
|
iov.iov_len = sizeof(buf);
|
|
|
|
status = recvmsg(nlsock, &msg, 0);
|
|
|
|
if (status < 0) {
|
2014-09-10 21:58:23 +02:00
|
|
|
if ((errno == EINTR) || (errno == EAGAIN)) {
|
2013-05-01 14:52:55 +02:00
|
|
|
continue;
|
2014-09-10 21:58:23 +02:00
|
|
|
}
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
break;
|
2014-09-10 21:58:23 +02:00
|
|
|
} else if (!status) {
|
2013-05-01 14:52:55 +02:00
|
|
|
break;
|
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Parsing message */
|
|
|
|
h = (struct nlmsghdr*)buf;
|
|
|
|
while (NLMSG_OK(h, status)) {
|
|
|
|
if ((h->nlmsg_pid == nllocal.nl_pid) && (h->nlmsg_seq == 0)) {
|
|
|
|
if ((h->nlmsg_type == NLMSG_DONE) || (h->nlmsg_type == NLMSG_ERROR)) {
|
|
|
|
end = 1;
|
|
|
|
break;
|
|
|
|
} else if (h->nlmsg_type == RTM_NEWROUTE) {
|
|
|
|
struct rtmsg* r = NLMSG_DATA(h);
|
|
|
|
int len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));
|
|
|
|
|
|
|
|
/* Accept only address IPv4 or IPv6 from main table route */
|
2014-09-10 21:58:23 +02:00
|
|
|
if ((len >= 0) && (!table || (r->rtm_table == table)) && (peeraddr->ss.ss_family == r->rtm_family)) {
|
2013-05-01 14:52:55 +02:00
|
|
|
struct rtattr* tb[RTA_MAX + 1];
|
|
|
|
struct rtattr* rta = RTM_RTA(r);
|
|
|
|
int addrsize = ((r->rtm_family == AF_INET) ? sizeof(struct in_addr) : sizeof(struct in6_addr));
|
|
|
|
int defaultgateway = 0;
|
|
|
|
int destmask = r->rtm_dst_len;
|
|
|
|
char ifname[IFNAMSIZ + 1];
|
|
|
|
|
|
|
|
/* Parsing rtattr */
|
|
|
|
memset(tb, 0, sizeof(struct rtattr*) * (RTA_MAX + 1));
|
|
|
|
while (RTA_OK(rta, len)) {
|
|
|
|
if (rta->rta_type <= RTA_MAX) {
|
|
|
|
tb[rta->rta_type] = rta;
|
|
|
|
}
|
|
|
|
|
|
|
|
rta = RTA_NEXT(rta, len);
|
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Get device name */
|
|
|
|
if (tb[RTA_OIF]) {
|
|
|
|
if (!if_indextoname(*(int*)RTA_DATA(tb[RTA_OIF]), ifname)) {
|
|
|
|
ifname[0] = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ifname[0] = 0;
|
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
if (!iface || !strcmp(iface, ifname)) {
|
|
|
|
union sockaddr_capwap destaddr;
|
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Destination network */
|
2014-09-10 21:58:23 +02:00
|
|
|
memset(&destaddr, 0, sizeof(union sockaddr_capwap));
|
|
|
|
destaddr.ss.ss_family = r->rtm_family;
|
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
if (tb[RTA_DST]) {
|
2014-09-10 21:58:23 +02:00
|
|
|
memcpy(((r->rtm_family == AF_INET) ? (void*)&destaddr.sin.sin_addr : (void*)&destaddr.sin6.sin6_addr), RTA_DATA(tb[RTA_DST]), addrsize);
|
2013-05-01 14:52:55 +02:00
|
|
|
} else if (!r->rtm_dst_len) {
|
|
|
|
defaultgateway = 1;
|
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Check network */
|
|
|
|
if (defaultgateway) {
|
|
|
|
if (tb[RTA_GATEWAY]) {
|
|
|
|
unsigned long metric = (tb[RTA_PRIORITY] ? *(unsigned long*)RTA_DATA(tb[RTA_PRIORITY]) : 0);
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
if ((gatewaytable < r->rtm_table) || ((gatewaytable == r->rtm_table) && (gatewaymetric > metric))) {
|
2013-05-01 14:52:55 +02:00
|
|
|
foundgateway = 1;
|
|
|
|
gatewaytable = r->rtm_table;
|
|
|
|
gatewaymetric = metric;
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
/* */
|
|
|
|
memset(&gateway, 0, sizeof(union sockaddr_capwap));
|
|
|
|
|
|
|
|
gateway.ss.ss_family = r->rtm_family;
|
|
|
|
memcpy(((r->rtm_family == AF_INET) ? (void*)&gateway.sin.sin_addr : (void*)&gateway.sin6.sin6_addr), RTA_DATA(tb[RTA_GATEWAY]), addrsize);
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (tb[RTA_PREFSRC]) {
|
2014-09-10 21:58:23 +02:00
|
|
|
int equal = 0;
|
|
|
|
union sockaddr_capwap peernetwork;
|
|
|
|
union sockaddr_capwap destnework;
|
|
|
|
|
|
|
|
/* Get subnet */
|
|
|
|
capwap_get_network_address(peeraddr, &peernetwork, destmask);
|
|
|
|
capwap_get_network_address(&destaddr, &destnework, destmask);
|
|
|
|
|
|
|
|
/* Compare subnet */
|
|
|
|
if (peernetwork.ss.ss_family == AF_INET) {
|
|
|
|
if (peernetwork.sin.sin_addr.s_addr == destnework.sin.sin_addr.s_addr) {
|
|
|
|
equal = 1;
|
|
|
|
}
|
|
|
|
} else if (peernetwork.ss.ss_family == AF_INET6) {
|
|
|
|
if (!memcmp(&peernetwork.sin6.sin6_addr, &destnework.sin6.sin6_addr, sizeof(struct in6_addr))) {
|
|
|
|
equal = 1;
|
|
|
|
}
|
|
|
|
}
|
2013-11-02 19:08:05 +01:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
if (equal) {
|
2013-05-01 14:52:55 +02:00
|
|
|
result = CAPWAP_ROUTE_LOCAL_ADDRESS;
|
2014-09-10 21:58:23 +02:00
|
|
|
localaddr->ss.ss_family = r->rtm_family;
|
|
|
|
memcpy(((r->rtm_family == AF_INET) ? (void*)&localaddr->sin.sin_addr : (void*)&localaddr->sin6.sin6_addr), RTA_DATA(tb[RTA_PREFSRC]), addrsize);
|
2013-11-02 19:08:05 +01:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-11-02 19:08:05 +01:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* Next */
|
2013-05-01 14:52:55 +02:00
|
|
|
h = NLMSG_NEXT(h, status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* */
|
2014-09-10 21:58:23 +02:00
|
|
|
if ((result == CAPWAP_ROUTE_NOT_FOUND) && foundgateway) {
|
2013-05-01 14:52:55 +02:00
|
|
|
result = CAPWAP_ROUTE_VIA_ADDRESS;
|
2014-09-10 21:58:23 +02:00
|
|
|
memcpy(localaddr, &gateway, sizeof(union sockaddr_capwap));
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2013-11-02 19:08:05 +01:00
|
|
|
/* */
|
2013-05-01 14:52:55 +02:00
|
|
|
close(nlsock);
|
2014-09-10 21:58:23 +02:00
|
|
|
return result;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Get interface flags */
|
|
|
|
static short capwap_get_interface_flags(char* iface) {
|
|
|
|
int sock;
|
|
|
|
struct ifreq req;
|
2013-11-02 19:08:05 +01:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
ASSERT(iface != NULL);
|
|
|
|
ASSERT(iface[0] != 0);
|
2013-11-02 19:08:05 +01:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
|
|
|
if (sock < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(req.ifr_name, iface);
|
2013-11-02 19:08:05 +01:00
|
|
|
if (ioctl(sock, SIOCGIFFLAGS, &req) < 0) {
|
|
|
|
req.ifr_flags = 0;
|
|
|
|
}
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
close(sock);
|
|
|
|
return req.ifr_flags;
|
|
|
|
}
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Return local address from remote address */
|
2014-09-10 21:58:23 +02:00
|
|
|
int capwap_network_get_localaddress(union sockaddr_capwap* localaddr, union sockaddr_capwap* peeraddr, char* iface) {
|
2013-05-01 14:52:55 +02:00
|
|
|
int result;
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
ASSERT(localaddr != NULL);
|
|
|
|
ASSERT(peeraddr != NULL);
|
|
|
|
|
|
|
|
/* */
|
|
|
|
memset(localaddr, 0, sizeof(union sockaddr_capwap));
|
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Check output interface */
|
2014-09-10 21:58:23 +02:00
|
|
|
if (iface && !*iface) {
|
|
|
|
iface = NULL;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
/* Check Loopback address */
|
|
|
|
if (peeraddr->ss.ss_family == AF_INET) {
|
|
|
|
if (peeraddr->sin.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
|
|
|
|
if (iface && ((capwap_get_interface_flags(iface) & IFF_LOOPBACK) != IFF_LOOPBACK)) {
|
|
|
|
return -1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
/* Loopback */
|
|
|
|
localaddr->ss.ss_family = AF_INET;
|
|
|
|
localaddr->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
return 0;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
} else if (peeraddr->ss.ss_family == AF_INET6) {
|
|
|
|
if (!memcmp(&peeraddr->sin6.sin6_addr, &in6addr_loopback, sizeof(struct in6_addr))) {
|
|
|
|
if (iface && ((capwap_get_interface_flags(iface) & IFF_LOOPBACK) != IFF_LOOPBACK)) {
|
|
|
|
return -1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
localaddr->ss.ss_family = AF_INET6;
|
|
|
|
memcpy(&localaddr->sin6.sin6_addr, &in6addr_loopback, sizeof(struct in6_addr));
|
|
|
|
return 0;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
} else {
|
|
|
|
return -1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Get address */
|
2014-09-10 21:58:23 +02:00
|
|
|
result = capwap_get_routeaddress(localaddr, peeraddr, iface, RT_TABLE_MAIN);
|
2013-05-01 14:52:55 +02:00
|
|
|
if (result == CAPWAP_ROUTE_NOT_FOUND) {
|
2014-09-10 21:58:23 +02:00
|
|
|
return -1;
|
2013-05-01 14:52:55 +02:00
|
|
|
} else if (result == CAPWAP_ROUTE_VIA_ADDRESS) {
|
2014-09-10 21:58:23 +02:00
|
|
|
union sockaddr_capwap tempaddr;
|
|
|
|
|
|
|
|
result = capwap_get_routeaddress(&tempaddr, localaddr, iface, RT_TABLE_MAIN);
|
|
|
|
if (result != CAPWAP_ROUTE_LOCAL_ADDRESS) {
|
|
|
|
return -1;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
memcpy(localaddr, &tempaddr, sizeof(union sockaddr_capwap));
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2014-09-10 21:58:23 +02:00
|
|
|
|
|
|
|
return 0;
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Retrieve interface list */
|
|
|
|
void capwap_interface_list(struct capwap_network* net, struct capwap_list* list) {
|
|
|
|
struct ifaddrs* ifaddrlist;
|
|
|
|
struct ifaddrs* ifcurrentpos;
|
2013-05-28 22:22:16 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
ASSERT(net != NULL);
|
|
|
|
ASSERT(list != NULL);
|
|
|
|
|
|
|
|
/* Get interface list */
|
2013-05-28 22:22:16 +02:00
|
|
|
if (getifaddrs(&ifaddrlist) != 0) {
|
|
|
|
return;
|
|
|
|
}
|
2013-05-01 14:52:55 +02:00
|
|
|
|
|
|
|
/* */
|
|
|
|
for (ifcurrentpos = ifaddrlist; ifcurrentpos != NULL; ifcurrentpos = ifcurrentpos->ifa_next) {
|
2013-05-27 21:33:23 +02:00
|
|
|
struct capwap_list_item* item;
|
2014-09-10 21:58:23 +02:00
|
|
|
union sockaddr_capwap* addr;
|
2013-05-27 21:33:23 +02:00
|
|
|
|
|
|
|
/* No loopback interface */
|
|
|
|
if ((ifcurrentpos->ifa_flags & IFF_LOOPBACK) != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Only IPv4 and IPv6 */
|
|
|
|
if ((ifcurrentpos->ifa_addr == NULL) || ((ifcurrentpos->ifa_addr->sa_family != AF_INET) && (ifcurrentpos->ifa_addr->sa_family != AF_INET6))) {
|
|
|
|
continue;
|
|
|
|
}
|
2013-05-27 21:33:23 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Filter family */
|
2014-09-10 21:58:23 +02:00
|
|
|
if (net->localaddr.ss.ss_family != ifcurrentpos->ifa_addr->sa_family) {
|
2013-05-01 14:52:55 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Filter interface */
|
2014-09-10 21:58:23 +02:00
|
|
|
if (*net->bindiface && strcmp(net->bindiface, ifcurrentpos->ifa_name)) {
|
2013-05-01 14:52:55 +02:00
|
|
|
continue;
|
|
|
|
}
|
2013-05-28 22:22:16 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Add local address */
|
2014-09-10 21:58:23 +02:00
|
|
|
item = capwap_itemlist_create(sizeof(union sockaddr_capwap));
|
|
|
|
addr = (union sockaddr_capwap*)item->item;
|
2013-05-28 22:22:16 +02:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
memset(addr, 0, sizeof(union sockaddr_capwap));
|
|
|
|
addr->ss.ss_family = ifcurrentpos->ifa_addr->sa_family;
|
2013-05-28 22:22:16 +02:00
|
|
|
|
2014-09-10 21:58:23 +02:00
|
|
|
if (addr->ss.ss_family == AF_INET) {
|
|
|
|
memcpy(&addr->sin.sin_addr, &((struct sockaddr_in*)ifcurrentpos->ifa_addr)->sin_addr, sizeof(struct in_addr));
|
|
|
|
addr->sin.sin_port = htons(CAPWAP_CONTROL_PORT);
|
|
|
|
} else if (addr->ss.ss_family == AF_INET6) {
|
|
|
|
memcpy(&addr->sin6.sin6_addr, &((struct sockaddr_in6*)ifcurrentpos->ifa_addr)->sin6_addr, sizeof(struct in6_addr));
|
|
|
|
addr->sin6.sin6_port = htons(CAPWAP_CONTROL_PORT);
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2013-05-28 22:22:16 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Add address */
|
|
|
|
capwap_itemlist_insert_after(list, NULL, item);
|
|
|
|
}
|
2013-05-28 22:22:16 +02:00
|
|
|
|
2013-05-01 14:52:55 +02:00
|
|
|
/* Free */
|
2014-09-10 21:58:23 +02:00
|
|
|
freeifaddrs(ifaddrlist);
|
2013-05-01 14:52:55 +02:00
|
|
|
}
|
2013-09-30 17:36:43 +02:00
|
|
|
|
2013-10-06 22:36:29 +02:00
|
|
|
/* */
|
2014-04-02 22:40:04 +02:00
|
|
|
char* capwap_printf_macaddress(char* buffer, const uint8_t* macaddress, int type) {
|
2013-09-30 17:36:43 +02:00
|
|
|
if (type == MACADDRESS_EUI48_LENGTH) {
|
|
|
|
sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", macaddress[0], macaddress[1], macaddress[2], macaddress[3], macaddress[4], macaddress[5]);
|
|
|
|
} else if (type == MACADDRESS_EUI64_LENGTH) {
|
|
|
|
sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", macaddress[0], macaddress[1], macaddress[2], macaddress[3], macaddress[4], macaddress[5], macaddress[6], macaddress[7]);
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
2013-10-06 22:36:29 +02:00
|
|
|
|
|
|
|
/* */
|
2014-04-02 22:40:04 +02:00
|
|
|
int capwap_scanf_macaddress(uint8_t* macaddress, const char* buffer, int type) {
|
2013-10-06 22:36:29 +02:00
|
|
|
if (type == MACADDRESS_EUI48_LENGTH) {
|
|
|
|
if (sscanf(buffer, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddress[0], &macaddress[1], &macaddress[2], &macaddress[3], &macaddress[4], &macaddress[5]) != 6) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else if (type == MACADDRESS_EUI64_LENGTH) {
|
|
|
|
if (sscanf(buffer, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddress[0], &macaddress[1], &macaddress[2], &macaddress[3], &macaddress[4], &macaddress[5], &macaddress[6], &macaddress[7]) != 8) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|