2013-09-16 09:53:25 +00:00
|
|
|
/* This file is part of Netsukuku
|
|
|
|
* (c) Copyright 2005 Andrea Lo Pumo aka AlpT <alpt@freaknet.org>
|
|
|
|
*
|
|
|
|
* This source code is free software; you can redistribute it and/or
|
2014-05-03 19:10:51 +00:00
|
|
|
* modify it under the terms of the GNU General Public License as published
|
2013-09-16 09:53:25 +00:00
|
|
|
* by the Free Software Foundation; either version 2 of the License,
|
|
|
|
* or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This source code 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.
|
|
|
|
* Please refer to the GNU Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Public License along with
|
|
|
|
* this source code; if not, write to:
|
|
|
|
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "includes.h"
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "ipv6-gmp.h"
|
|
|
|
#include "libnetlink.h"
|
|
|
|
#include "ll_map.h"
|
|
|
|
#include "inet.h"
|
|
|
|
#include "endianness.h"
|
|
|
|
|
|
|
|
|
2014-05-03 19:10:51 +00:00
|
|
|
/*
|
2013-09-16 09:53:25 +00:00
|
|
|
* inet_ntohl: Converts each element of `data' from network to host order. If
|
|
|
|
* `family' is equal to AF_INET6, the array is swapped too (on big endian
|
|
|
|
* machine).
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
void
|
|
|
|
inet_ntohl(u_int * data, int family)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
2014-09-17 12:41:24 +00:00
|
|
|
if (family == AF_INET) {
|
|
|
|
data[0] = ntohl(data[0]);
|
2013-09-16 09:53:25 +00:00
|
|
|
} else {
|
|
|
|
int i;
|
|
|
|
swap_ints(MAX_IP_INT, data, data);
|
2014-09-17 12:41:24 +00:00
|
|
|
for (i = 0; i < MAX_IP_INT; i++)
|
|
|
|
data[i] = ntohl(data[i]);
|
2013-09-16 09:53:25 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-05-03 19:10:51 +00:00
|
|
|
/*
|
2013-09-16 09:53:25 +00:00
|
|
|
* inet_htonl: Converts each element of `data' from host to network order. If
|
|
|
|
* `family' is equal to AF_INET6, the array is swapped too (on big endian
|
|
|
|
* machine).
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
void
|
|
|
|
inet_htonl(u_int * data, int family)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
2014-09-17 12:41:24 +00:00
|
|
|
if (family == AF_INET) {
|
|
|
|
data[0] = htonl(data[0]);
|
2013-09-16 09:53:25 +00:00
|
|
|
} else {
|
|
|
|
int i;
|
|
|
|
swap_ints(MAX_IP_INT, data, data);
|
2014-09-17 12:41:24 +00:00
|
|
|
for (i = 0; i < MAX_IP_INT; i++)
|
|
|
|
data[i] = htonl(data[i]);
|
2013-09-16 09:53:25 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* inet_setip_raw: fills the `ip' inet_prefix struct with `data' and `family'.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_setip_raw(inet_prefix * ip, u_int * data, int family)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
ip->family = family;
|
2013-09-16 09:53:25 +00:00
|
|
|
setzero(ip->data, sizeof(ip->data));
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (family == AF_INET) {
|
|
|
|
ip->data[0] = data[0];
|
|
|
|
ip->len = 4;
|
|
|
|
} else if (family == AF_INET6) {
|
2013-09-16 09:53:25 +00:00
|
|
|
memcpy(ip->data, data, sizeof(ip->data));
|
2014-09-17 12:41:24 +00:00
|
|
|
ip->len = 16;
|
2014-05-03 19:10:51 +00:00
|
|
|
} else
|
2013-09-16 09:53:25 +00:00
|
|
|
fatal(ERROR_MSG "family not supported", ERROR_POS);
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
ip->bits = ip->len << 3; /* bits=len*8 */
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* inet_setip: fills the `ip' inet_prefix struct with `data' and `family'.
|
|
|
|
* Note that it does a network to host order conversion on `data'.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_setip(inet_prefix * ip, u_int * data, int family)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
inet_setip_raw(ip, data, family);
|
|
|
|
inet_ntohl(ip->data, ip->family);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_setip_bcast(inet_prefix * ip, int family)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
if (family == AF_INET) {
|
|
|
|
u_int data[MAX_IP_INT] = { 0, 0, 0, 0 };
|
|
|
|
data[0] = INADDR_BROADCAST;
|
2013-09-16 09:53:25 +00:00
|
|
|
inet_setip(ip, data, family);
|
2014-09-17 12:41:24 +00:00
|
|
|
} else if (family == AF_INET6) {
|
|
|
|
u_int data[MAX_IP_INT] = IPV6_ADDR_BROADCAST;
|
2013-09-16 09:53:25 +00:00
|
|
|
inet_setip(ip, data, family);
|
2014-05-03 19:10:51 +00:00
|
|
|
} else
|
2013-09-16 09:53:25 +00:00
|
|
|
fatal(ERROR_MSG "family not supported", ERROR_POS);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_setip_anyaddr(inet_prefix * ip, int family)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
if (family == AF_INET) {
|
|
|
|
u_int data[MAX_IP_INT] = { 0, 0, 0, 0 };
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
data[0] = INADDR_ANY;
|
2013-09-16 09:53:25 +00:00
|
|
|
inet_setip(ip, data, family);
|
2014-09-17 12:41:24 +00:00
|
|
|
} else if (family == AF_INET6) {
|
|
|
|
struct in6_addr ipv6 = IN6ADDR_ANY_INIT;
|
|
|
|
inet_setip(ip, (u_int *) (&ipv6), family);
|
2014-05-03 19:10:51 +00:00
|
|
|
} else
|
2013-09-16 09:53:25 +00:00
|
|
|
fatal(ERROR_MSG "family not supported", ERROR_POS);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_setip_loopback(inet_prefix * ip, int family)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
if (family == AF_INET) {
|
|
|
|
u_int data[MAX_IP_INT] = { 0, 0, 0, 0 };
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
data[0] = LOOPBACK_IP;
|
2013-09-16 09:53:25 +00:00
|
|
|
inet_setip(ip, data, family);
|
|
|
|
inet_htonl(ip->data, ip->family);
|
2014-09-17 12:41:24 +00:00
|
|
|
} else if (family == AF_INET6) {
|
|
|
|
u_int data[MAX_IP_INT] = LOOPBACK_IPV6;
|
2013-09-16 09:53:25 +00:00
|
|
|
inet_setip(ip, data, family);
|
2014-05-03 19:10:51 +00:00
|
|
|
} else
|
2013-09-16 09:53:25 +00:00
|
|
|
fatal(ERROR_MSG "family not supported", ERROR_POS);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-03 19:10:51 +00:00
|
|
|
/*
|
2013-09-16 09:53:25 +00:00
|
|
|
* inet_setip_localaddr: Restrict the `ip' to a local private class changing the
|
2014-05-03 19:10:51 +00:00
|
|
|
* first byte of the `ip'. `class' specifies what restricted class is currently
|
2013-09-16 09:53:25 +00:00
|
|
|
* being used (10.x.x.x or 172.16.x.x). In ipv6 the site local class is the
|
|
|
|
* default.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_setip_localaddr(inet_prefix * ip, int family, int class)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
if (family == AF_INET) {
|
|
|
|
if (class == RESTRICTED_10)
|
2013-09-16 09:53:25 +00:00
|
|
|
ip->data[0] = NTK_RESTRICTED_10_MASK(ip->data[0]);
|
2014-05-03 19:10:51 +00:00
|
|
|
else
|
2013-09-16 09:53:25 +00:00
|
|
|
ip->data[0] = NTK_RESTRICTED_172_MASK(ip->data[0]);
|
2014-09-17 12:41:24 +00:00
|
|
|
} else if (family == AF_INET6) {
|
2013-09-16 09:53:25 +00:00
|
|
|
ip->data[0] = NTK_RESTRICTED_IPV6_MASK(ip->data[0]);
|
2014-05-03 19:10:51 +00:00
|
|
|
} else
|
2013-09-16 09:53:25 +00:00
|
|
|
fatal(ERROR_MSG "family not supported", ERROR_POS);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* inet_is_ip_local: verifies if `ip' is a local address. If it is, 1 is
|
2014-05-03 19:10:51 +00:00
|
|
|
* returned. `class' specifies what restricted class is currently
|
2013-09-16 09:53:25 +00:00
|
|
|
* being used (10.x.x.x or 172.16.x.x). In ipv6 the site local class is the
|
|
|
|
* default.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_is_ip_local(inet_prefix * ip, int class)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
if (ip->family == AF_INET) {
|
|
|
|
if (class == RESTRICTED_10)
|
2013-09-16 09:53:25 +00:00
|
|
|
return ip->data[0] == NTK_RESTRICTED_10_MASK(ip->data[0]);
|
|
|
|
else
|
|
|
|
return ip->data[0] == NTK_RESTRICTED_172_MASK(ip->data[0]);
|
2014-09-17 12:41:24 +00:00
|
|
|
} else if (ip->family == AF_INET6)
|
2013-09-16 09:53:25 +00:00
|
|
|
return ip->data[0] == NTK_RESTRICTED_IPV6_MASK(ip->data[0]);
|
|
|
|
else
|
|
|
|
fatal(ERROR_MSG "family not supported", ERROR_POS);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
void
|
|
|
|
inet_copy(inet_prefix * dst, inet_prefix * src)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
memcpy(dst, src, sizeof(inet_prefix));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* inet_copy_ipdata_raw: copies `ip'->data in `dst_data'.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
void
|
|
|
|
inet_copy_ipdata_raw(u_int * dst_data, inet_prefix * ip)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
memcpy(dst_data, ip->data, MAX_IP_SZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* inet_copy_ipdata: copies `ip'->data in `dst_data' and converts it in network
|
|
|
|
* order.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
void
|
|
|
|
inet_copy_ipdata(u_int * dst_data, inet_prefix * ip)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
inet_prefix tmp_ip;
|
|
|
|
|
|
|
|
inet_copy(&tmp_ip, ip);
|
|
|
|
inet_htonl(tmp_ip.data, tmp_ip.family);
|
|
|
|
memcpy(dst_data, tmp_ip.data, MAX_IP_SZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pack_inet_prefix: packs the `ip' inet_prefix struct and stores it in
|
|
|
|
* `pack', which must be INET_PREFIX_PACK_SZ bytes big. `pack' will be in
|
|
|
|
* network order.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
void
|
|
|
|
pack_inet_prefix(inet_prefix * ip, char *pack)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
char *buf;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
buf = pack;
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
memcpy(buf, &ip->family, sizeof(u_char));
|
2014-09-17 12:41:24 +00:00
|
|
|
buf += sizeof(u_char);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
memcpy(buf, &ip->len, sizeof(u_short));
|
2014-09-17 12:41:24 +00:00
|
|
|
buf += sizeof(u_short);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
memcpy(buf, &ip->bits, sizeof(u_char));
|
2014-09-17 12:41:24 +00:00
|
|
|
buf += sizeof(u_char);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
memcpy(buf, ip->data, MAX_IP_SZ);
|
2014-09-17 12:41:24 +00:00
|
|
|
inet_htonl((u_int *) buf, ip->family);
|
|
|
|
buf += MAX_IP_SZ;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
ints_host_to_network(pack, inet_prefix_iinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* unpack_inet_prefix: restores in `ip' the inet_prefix struct contained in `pack'.
|
|
|
|
* Note that `pack' will be modified during the restoration.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
void
|
|
|
|
unpack_inet_prefix(inet_prefix * ip, char *pack)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
char *buf;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
buf = pack;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
ints_network_to_host(pack, inet_prefix_iinfo);
|
|
|
|
|
|
|
|
memcpy(&ip->family, buf, sizeof(u_char));
|
2014-09-17 12:41:24 +00:00
|
|
|
buf += sizeof(u_char);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
memcpy(&ip->len, buf, sizeof(u_short));
|
2014-09-17 12:41:24 +00:00
|
|
|
buf += sizeof(u_short);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
memcpy(&ip->bits, buf, sizeof(u_char));
|
2014-09-17 12:41:24 +00:00
|
|
|
buf += sizeof(u_char);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
memcpy(ip->data, buf, MAX_IP_SZ);
|
|
|
|
inet_ntohl(ip->data, ip->family);
|
2014-09-17 12:41:24 +00:00
|
|
|
buf += MAX_IP_SZ;
|
2013-09-16 09:53:25 +00:00
|
|
|
}
|
|
|
|
|
2014-05-03 19:10:51 +00:00
|
|
|
/*
|
2013-09-16 09:53:25 +00:00
|
|
|
* inet_addr_match: without hesitating this function was robbed from iproute2.
|
|
|
|
* It compares a->data wih b->data matching `bits'# bits.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_addr_match(const inet_prefix * a, const inet_prefix * b, int bits)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
const uint32_t *a1 = a->data;
|
|
|
|
const uint32_t *a2 = b->data;
|
|
|
|
int words = bits >> 0x05;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
bits &= 0x1f;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (words)
|
|
|
|
if (memcmp(a1, a2, words << 2))
|
|
|
|
return -1;
|
2013-09-16 09:53:25 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (bits) {
|
|
|
|
uint32_t w1, w2;
|
|
|
|
uint32_t mask;
|
2013-09-16 09:53:25 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
w1 = a1[words];
|
|
|
|
w2 = a2[words];
|
2013-09-16 09:53:25 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
mask = htonl((0xffffffff) << (0x20 - bits));
|
2013-09-16 09:53:25 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if ((w1 ^ w2) & mask)
|
|
|
|
return 1;
|
|
|
|
}
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
ipv6_addr_type(inet_prefix addr)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
int type;
|
|
|
|
u_int st;
|
|
|
|
|
|
|
|
st = htonl(addr.data[0]);
|
|
|
|
|
|
|
|
if ((st & htonl(0xFF000000)) == htonl(0xFF000000)) {
|
|
|
|
type = IPV6_ADDR_MULTICAST;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
switch ((st & htonl(0x00FF0000))) {
|
|
|
|
case __constant_htonl(0x00010000):
|
|
|
|
type |= IPV6_ADDR_LOOPBACK;
|
|
|
|
break;
|
2013-09-16 09:53:25 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
case __constant_htonl(0x00020000):
|
|
|
|
type |= IPV6_ADDR_LINKLOCAL;
|
|
|
|
break;
|
2013-09-16 09:53:25 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
case __constant_htonl(0x00050000):
|
|
|
|
type |= IPV6_ADDR_SITELOCAL;
|
|
|
|
break;
|
2013-09-16 09:53:25 +00:00
|
|
|
};
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
type = IPV6_ADDR_UNICAST;
|
|
|
|
|
|
|
|
/* Consider all addresses with the first three bits different of
|
|
|
|
000 and 111 as finished.
|
|
|
|
*/
|
|
|
|
if ((st & htonl(0xE0000000)) != htonl(0x00000000) &&
|
2014-09-17 12:41:24 +00:00
|
|
|
(st & htonl(0xE0000000)) != htonl(0xE0000000))
|
2013-09-16 09:53:25 +00:00
|
|
|
return type;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
if ((st & htonl(0xFFC00000)) == htonl(0xFE800000))
|
|
|
|
return (IPV6_ADDR_LINKLOCAL | type);
|
|
|
|
|
|
|
|
if ((st & htonl(0xFFC00000)) == htonl(0xFEC00000))
|
|
|
|
return (IPV6_ADDR_SITELOCAL | type);
|
|
|
|
|
|
|
|
if ((addr.data[0] | addr.data[1]) == 0) {
|
|
|
|
if (addr.data[2] == 0) {
|
|
|
|
if (addr.data[3] == 0)
|
|
|
|
return IPV6_ADDR_ANY;
|
|
|
|
|
|
|
|
if (htonl(addr.data[3]) == htonl(0x00000001))
|
|
|
|
return (IPV6_ADDR_LOOPBACK | type);
|
|
|
|
|
|
|
|
return (IPV6_ADDR_COMPATv4 | type);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (htonl(addr.data[2]) == htonl(0x0000ffff))
|
|
|
|
return IPV6_ADDR_MAPPED;
|
|
|
|
}
|
|
|
|
|
|
|
|
st &= htonl(0xFF000000);
|
|
|
|
if (st == 0)
|
|
|
|
return IPV6_ADDR_RESERVED;
|
|
|
|
st &= htonl(0xFE000000);
|
|
|
|
if (st == htonl(0x02000000))
|
|
|
|
return IPV6_ADDR_RESERVED; /* for NSAP */
|
|
|
|
if (st == htonl(0x04000000))
|
|
|
|
return IPV6_ADDR_RESERVED; /* for IPX */
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* inet_validate_ip: returns 0 is `ip' a valid IP which can be set by
|
|
|
|
* Netsukuku to a network interface
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_validate_ip(inet_prefix ip)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
int type, ipv4;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (ip.family == AF_INET) {
|
|
|
|
ipv4 = htonl(ip.data[0]);
|
|
|
|
if (MULTICAST(ipv4) || BADCLASS(ipv4) || ZERONET(ipv4)
|
2013-09-16 09:53:25 +00:00
|
|
|
|| LOOPBACK(ipv4) || NTK_PRIVATE_C(ipv4) ||
|
|
|
|
(!restricted_mode && NTK_PRIVATE_B(ipv4)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
} else if (ip.family == AF_INET6) {
|
|
|
|
type = ipv6_addr_type(ip);
|
|
|
|
if ((type & IPV6_ADDR_MULTICAST) || (type & IPV6_ADDR_RESERVED) ||
|
|
|
|
(type & IPV6_ADDR_LOOPBACK))
|
2013-09-16 09:53:25 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (is_bufzero((char *) ip.data, MAX_IP_SZ))
|
2013-09-16 09:53:25 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*\
|
|
|
|
*
|
2014-05-03 19:10:51 +00:00
|
|
|
* * * Conversion functions... * *
|
|
|
|
*
|
2013-09-16 09:53:25 +00:00
|
|
|
\*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ipraw_to_str: It returns the string which represents the given ip in host
|
|
|
|
* order.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
const char *
|
|
|
|
ipraw_to_str(u_int ip[MAX_IP_INT], int family)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
struct in_addr src;
|
|
|
|
struct in6_addr src6;
|
|
|
|
static char dst[INET_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN];
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (family == AF_INET) {
|
|
|
|
src.s_addr = htonl(ip[0]);
|
2013-09-16 09:53:25 +00:00
|
|
|
inet_ntop(family, &src, dst, INET_ADDRSTRLEN);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
return dst;
|
2014-09-17 12:41:24 +00:00
|
|
|
} else if (family == AF_INET6) {
|
2013-09-16 09:53:25 +00:00
|
|
|
inet_htonl(ip, family);
|
|
|
|
memcpy(&src6, ip, MAX_IP_SZ);
|
|
|
|
inet_ntop(family, &src6, dst6, INET6_ADDRSTRLEN);
|
|
|
|
|
|
|
|
return dst6;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* inet_to_str: returns the string rapresentation of `ip'
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
const char *
|
|
|
|
inet_to_str(inet_prefix ip)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
return ipraw_to_str(ip.data, ip.family);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* str_to_inet: it converts the IP address string contained in `src' and
|
|
|
|
* terminated by a `\0' char to an inet_prefix struct. The result is stored in
|
|
|
|
* `ip'. On error -1 is returned.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
str_to_inet(const char *src, inet_prefix * ip)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
struct in_addr dst;
|
|
|
|
struct in6_addr dst6;
|
2014-09-17 12:41:24 +00:00
|
|
|
int family, res;
|
2013-09-16 09:53:25 +00:00
|
|
|
u_int *data;
|
|
|
|
|
|
|
|
setzero(ip, sizeof(inet_prefix));
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (strstr(src, ":")) {
|
|
|
|
family = AF_INET6;
|
|
|
|
data = (u_int *) & dst6;
|
2013-09-16 09:53:25 +00:00
|
|
|
} else {
|
2014-09-17 12:41:24 +00:00
|
|
|
family = AF_INET;
|
|
|
|
data = (u_int *) & dst;
|
2013-09-16 09:53:25 +00:00
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if ((res = inet_pton(family, src, (void *) data)) < 0) {
|
2014-05-03 19:10:51 +00:00
|
|
|
debug(DBG_NORMAL, ERROR_MSG "error -> %s.",
|
2014-09-17 12:41:24 +00:00
|
|
|
ERROR_FUNC, strerror(errno));
|
2013-09-16 09:53:25 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!res) {
|
|
|
|
debug(DBG_NORMAL, ERROR_MSG "impossible to convert \"%s\":"
|
2014-09-17 12:41:24 +00:00
|
|
|
" invalid address.", ERROR_FUNC, src);
|
2013-09-16 09:53:25 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
inet_setip(ip, data, family);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* inet_to_sockaddr: Converts a inet_prefix struct to a sockaddr struct
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_to_sockaddr(inet_prefix * ip, u_short port, struct sockaddr *dst,
|
|
|
|
socklen_t * dstlen)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
port = htons(port);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (ip->family == AF_INET) {
|
2013-09-16 09:53:25 +00:00
|
|
|
struct sockaddr_in sin;
|
2014-09-17 12:41:24 +00:00
|
|
|
setzero(&sin, sizeof(struct sockaddr_in));
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
sin.sin_family = ip->family;
|
|
|
|
sin.sin_port = port;
|
|
|
|
sin.sin_addr.s_addr = htonl(ip->data[0]);
|
|
|
|
memcpy(dst, &sin, sizeof(struct sockaddr_in));
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (dstlen)
|
|
|
|
*dstlen = sizeof(struct sockaddr_in);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
} else if (ip->family == AF_INET6) {
|
2013-09-16 09:53:25 +00:00
|
|
|
struct sockaddr_in6 sin6;
|
2014-09-17 12:41:24 +00:00
|
|
|
setzero(&sin6, sizeof(struct sockaddr_in6));
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
sin6.sin6_family = ip->family;
|
|
|
|
sin6.sin6_port = port;
|
|
|
|
sin6.sin6_flowinfo = 0;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
memcpy(&sin6.sin6_addr, ip->data, MAX_IP_SZ);
|
2014-09-17 12:41:24 +00:00
|
|
|
inet_htonl((u_int *) & sin6.sin6_addr, ip->family);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
memcpy(dst, &sin6, sizeof(struct sockaddr_in6));
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (dstlen)
|
|
|
|
*dstlen = sizeof(struct sockaddr_in6);
|
2013-09-16 09:53:25 +00:00
|
|
|
} else
|
|
|
|
fatal(ERROR_MSG "family not supported", ERROR_POS);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
sockaddr_to_inet(struct sockaddr *ip, inet_prefix * dst, u_short * port)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
u_short po;
|
|
|
|
char *p;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
setzero(dst, sizeof(inet_prefix));
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
dst->family = ip->sa_family;
|
2013-09-16 09:53:25 +00:00
|
|
|
memcpy(&po, &ip->sa_data, sizeof(u_short));
|
2014-09-17 12:41:24 +00:00
|
|
|
if (port)
|
|
|
|
*port = ntohs(po);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (ip->sa_family == AF_INET)
|
|
|
|
p = (char *) ip->sa_data + sizeof(u_short);
|
|
|
|
else if (ip->sa_family == AF_INET6)
|
|
|
|
p = (char *) ip->sa_data + sizeof(u_short) + sizeof(int);
|
2013-09-16 09:53:25 +00:00
|
|
|
else {
|
|
|
|
error(ERROR_MSG "family not supported", ERROR_POS);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
inet_setip(dst, (u_int *) p, ip->sa_family);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*\
|
|
|
|
*
|
|
|
|
* * * Socket operations * *
|
|
|
|
*
|
|
|
|
\*/
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
new_socket(int sock_type)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
int sockfd;
|
2014-09-17 12:41:24 +00:00
|
|
|
if ((sockfd = socket(sock_type, SOCK_STREAM, 0)) == -1) {
|
2013-09-16 09:53:25 +00:00
|
|
|
error("Socket SOCK_STREAM creation failed: %s", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
2014-09-17 12:41:24 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
return sockfd;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
new_dgram_socket(int sock_type)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
int sockfd;
|
2014-09-17 12:41:24 +00:00
|
|
|
if ((sockfd = socket(sock_type, SOCK_DGRAM, 0)) == -1) {
|
2013-09-16 09:53:25 +00:00
|
|
|
error("Socket SOCK_DGRAM creation failed: %s", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sockfd;
|
|
|
|
}
|
|
|
|
|
2014-05-03 19:10:51 +00:00
|
|
|
/*
|
2013-09-16 09:53:25 +00:00
|
|
|
* inet_close
|
|
|
|
*
|
|
|
|
* It closes the `*sk' socket and sets it to zero.
|
|
|
|
* It always returns 0;
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_close(int *sk)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
close(*sk);
|
2014-09-17 12:41:24 +00:00
|
|
|
return (*sk = 0);
|
2013-09-16 09:53:25 +00:00
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
inet_getpeername(int sk, inet_prefix * ip, short *port)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
struct sockaddr_storage saddr_sto;
|
2014-09-17 12:41:24 +00:00
|
|
|
struct sockaddr *sa = (struct sockaddr *) &saddr_sto;
|
2013-09-16 09:53:25 +00:00
|
|
|
socklen_t alen;
|
|
|
|
|
|
|
|
alen = sizeof(saddr_sto);
|
|
|
|
setzero(sa, alen);
|
2014-09-17 12:41:24 +00:00
|
|
|
if (getpeername(sk, sa, &alen) == -1) {
|
2013-09-16 09:53:25 +00:00
|
|
|
error("Cannot getpeername: %s", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sockaddr_to_inet(sa, ip, port);
|
|
|
|
}
|
|
|
|
|
2014-05-03 19:10:51 +00:00
|
|
|
/*
|
2013-09-16 09:53:25 +00:00
|
|
|
* join_ipv6_multicast: It adds the membership to the IPV6_ADDR_BROADCAST
|
2014-05-03 19:10:51 +00:00
|
|
|
* multicast group. The device with index `idx' will be used.
|
2013-09-16 09:53:25 +00:00
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
join_ipv6_multicast(int socket, int idx)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
struct ipv6_mreq mreq6;
|
|
|
|
const int addr[MAX_IP_INT] = IPV6_ADDR_BROADCAST;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
setzero(&mreq6, sizeof(struct ipv6_mreq));
|
2014-09-17 12:41:24 +00:00
|
|
|
memcpy(&mreq6.ipv6mr_multiaddr, addr, sizeof(struct in6_addr));
|
|
|
|
mreq6.ipv6mr_interface = idx;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (setsockopt(socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
|
|
|
|
sizeof(mreq6)) < 0) {
|
2013-09-16 09:53:25 +00:00
|
|
|
error("Cannot set IPV6_JOIN_GROUP: %s", strerror(errno));
|
2014-09-17 12:41:24 +00:00
|
|
|
close(socket);
|
2013-09-16 09:53:25 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
set_multicast_if(int socket, int idx)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
/* man ipv6 */
|
|
|
|
|
|
|
|
if (setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF,
|
2014-09-17 12:41:24 +00:00
|
|
|
&idx, sizeof(int)) < 0) {
|
2013-09-16 09:53:25 +00:00
|
|
|
error("set_multicast_if(): cannot set IPV6_MULTICAST_IF: %s",
|
2014-09-17 12:41:24 +00:00
|
|
|
strerror(errno));
|
2013-09-16 09:53:25 +00:00
|
|
|
close(socket);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
set_nonblock_sk(int fd)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
|
2014-05-03 19:10:51 +00:00
|
|
|
error("set_nonblock_sk(): cannot set O_NONBLOCK: %s",
|
2014-09-17 12:41:24 +00:00
|
|
|
strerror(errno));
|
2013-09-16 09:53:25 +00:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
unset_nonblock_sk(int fd)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
if (fcntl(fd, F_SETFL, 0) < 0) {
|
2014-05-03 19:10:51 +00:00
|
|
|
error("unset_nonblock_sk(): cannot unset O_NONBLOCK: %s",
|
2014-09-17 12:41:24 +00:00
|
|
|
strerror(errno));
|
2013-09-16 09:53:25 +00:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
set_reuseaddr_sk(int socket)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
int reuseaddr = 1, ret;
|
2013-09-16 09:53:25 +00:00
|
|
|
/*
|
|
|
|
* SO_REUSEADDR: <<Go ahead and reuse that port even if it is in
|
|
|
|
* TIME_WAIT state.>>
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
ret =
|
|
|
|
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
|
|
|
|
sizeof(int));
|
|
|
|
if (ret < 0)
|
2013-09-16 09:53:25 +00:00
|
|
|
error("setsockopt SO_REUSEADDR: %s", strerror(errno));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
set_bindtodevice_sk(int socket, char *dev)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
struct ifreq ifr;
|
2014-09-17 12:41:24 +00:00
|
|
|
int ret = 0;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
setzero(&ifr, sizeof(ifr));
|
2014-09-17 12:41:24 +00:00
|
|
|
strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
ret =
|
|
|
|
setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, dev,
|
|
|
|
strlen(dev) + 1);
|
|
|
|
if (ret < 0)
|
2013-09-16 09:53:25 +00:00
|
|
|
error("setsockopt SO_BINDTODEVICE: %s", strerror(errno));
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
return ret;
|
2013-09-16 09:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-05-03 19:10:51 +00:00
|
|
|
* `loop': 0 = disable, 1 = enable (default)
|
2013-09-16 09:53:25 +00:00
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
set_multicast_loop_sk(int family, int socket, u_char loop)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
int ret = 0;
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* <<The IPV6_MULTICAST_LOOP option gives the sender explicit control
|
|
|
|
* over whether or not subsequent datagrams are looped bac.>>
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
if (family == AF_INET6)
|
|
|
|
ret =
|
|
|
|
setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop,
|
|
|
|
sizeof(loop));
|
|
|
|
if (ret < 0)
|
2013-09-16 09:53:25 +00:00
|
|
|
error("setsockopt IP_MULTICAST_LOOP: %s", strerror(errno));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
set_broadcast_sk(int socket, int family, inet_prefix * host, short port,
|
|
|
|
int dev_idx)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
struct sockaddr_storage saddr_sto;
|
2014-09-17 12:41:24 +00:00
|
|
|
struct sockaddr *sa = (struct sockaddr *) &saddr_sto;
|
2013-09-16 09:53:25 +00:00
|
|
|
socklen_t alen;
|
2014-09-17 12:41:24 +00:00
|
|
|
int broadcast = 1;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (family == AF_INET) {
|
2013-09-16 09:53:25 +00:00
|
|
|
if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &broadcast,
|
2014-09-17 12:41:24 +00:00
|
|
|
sizeof(broadcast)) < 0) {
|
|
|
|
error("Cannot set SO_BROADCAST to socket: %s",
|
|
|
|
strerror(errno));
|
2013-09-16 09:53:25 +00:00
|
|
|
close(socket);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-09-17 12:41:24 +00:00
|
|
|
} else if (family == AF_INET6) {
|
|
|
|
if (join_ipv6_multicast(socket, dev_idx) < 0)
|
2013-09-16 09:53:25 +00:00
|
|
|
return -1;
|
2014-09-17 12:41:24 +00:00
|
|
|
if (set_multicast_loop_sk(family, socket, 0) < 0)
|
2013-09-16 09:53:25 +00:00
|
|
|
return -1;
|
|
|
|
set_multicast_if(socket, dev_idx);
|
|
|
|
} else
|
|
|
|
fatal(ERROR_MSG "family not supported", ERROR_POS);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
/* What's my name ? */
|
|
|
|
alen = sizeof(saddr_sto);
|
|
|
|
setzero(sa, alen);
|
|
|
|
if (getsockname(socket, sa, &alen) == -1) {
|
|
|
|
error("Cannot getsockname: %s", strerror(errno));
|
|
|
|
close(socket);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
/* Let's bind it! */
|
2014-09-17 12:41:24 +00:00
|
|
|
if (bind(socket, sa, alen) < 0) {
|
2013-09-16 09:53:25 +00:00
|
|
|
error("Cannot bind the broadcast socket: %s", strerror(errno));
|
|
|
|
close(socket);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
unset_broadcast_sk(int socket, int family)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
int broadcast = 0;
|
|
|
|
if (family == AF_INET) {
|
|
|
|
if (setsockopt
|
|
|
|
(socket, SOL_SOCKET, SO_BROADCAST, &broadcast,
|
|
|
|
sizeof(broadcast)) < 0) {
|
|
|
|
error("Cannot unset broadcasting: %s", strerror(errno));
|
2013-09-16 09:53:25 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
set_keepalive_sk(int socket)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
int on = 1;
|
2013-09-16 09:53:25 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, (void *) &on,
|
|
|
|
sizeof(on)) < 0) {
|
2013-09-16 09:53:25 +00:00
|
|
|
error("Cannot set keepalive socket: %s", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
unset_keepalive_sk(int socket)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-17 12:41:24 +00:00
|
|
|
int off = 0;
|
2013-09-16 09:53:25 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, (void *) &off,
|
|
|
|
sizeof(off)) < 0) {
|
2013-09-16 09:53:25 +00:00
|
|
|
error("Cannot unset keepalive socket: %s", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
set_tos_sk(int socket, int lowdelay)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
int tos = lowdelay ? IPTOS_LOWDELAY : IPTOS_THROUGHPUT;
|
|
|
|
|
|
|
|
/* Only for Ipv4 */
|
|
|
|
if (setsockopt(socket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
|
|
|
|
error("setsockopt IP_TOS %d: %s", tos, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*\
|
|
|
|
*
|
2014-05-03 19:10:51 +00:00
|
|
|
* * * Connection functions * *
|
2013-09-16 09:53:25 +00:00
|
|
|
*
|
|
|
|
\*/
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
new_tcp_conn(inet_prefix * host, short port, char *dev)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
int sk;
|
|
|
|
socklen_t sa_len;
|
|
|
|
struct sockaddr_storage saddr_sto;
|
2014-09-17 12:41:24 +00:00
|
|
|
struct sockaddr *sa = (struct sockaddr *) &saddr_sto;
|
2013-09-16 09:53:25 +00:00
|
|
|
const char *ntop;
|
2014-09-17 12:41:24 +00:00
|
|
|
ntop = inet_to_str(*host);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (inet_to_sockaddr(host, port, sa, &sa_len)) {
|
|
|
|
error("Cannot new_tcp_connect(): %d Family not supported",
|
|
|
|
host->family);
|
2013-09-16 09:53:25 +00:00
|
|
|
ERROR_FINISH(sk, -1, finish);
|
|
|
|
}
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if ((sk = new_socket(host->family)) == -1)
|
2013-09-16 09:53:25 +00:00
|
|
|
ERROR_FINISH(sk, -1, finish);
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (dev) /* if `dev' is not null bind the socket to it */
|
|
|
|
if (set_bindtodevice_sk(sk, dev) < 0)
|
2013-09-16 09:53:25 +00:00
|
|
|
ERROR_FINISH(sk, -1, finish);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
if (connect(sk, sa, sa_len) == -1) {
|
|
|
|
error("Cannot tcp_connect() to %s: %s", ntop, strerror(errno));
|
|
|
|
ERROR_FINISH(sk, -1, finish);
|
|
|
|
}
|
2014-09-17 12:41:24 +00:00
|
|
|
finish:
|
2013-09-16 09:53:25 +00:00
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
new_udp_conn(inet_prefix * host, short port, char *dev)
|
2014-05-03 19:10:51 +00:00
|
|
|
{
|
2013-09-16 09:53:25 +00:00
|
|
|
int sk;
|
|
|
|
socklen_t sa_len;
|
|
|
|
struct sockaddr_storage saddr_sto;
|
2014-09-17 12:41:24 +00:00
|
|
|
struct sockaddr *sa = (struct sockaddr *) &saddr_sto;
|
2013-09-16 09:53:25 +00:00
|
|
|
const char *ntop;
|
2014-09-17 12:41:24 +00:00
|
|
|
ntop = inet_to_str(*host);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (inet_to_sockaddr(host, port, sa, &sa_len)) {
|
|
|
|
error("Cannot new_udp_connect(): %d Family not supported",
|
|
|
|
host->family);
|
2013-09-16 09:53:25 +00:00
|
|
|
ERROR_FINISH(sk, -1, finish);
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if ((sk = new_dgram_socket(host->family)) == -1)
|
2013-09-16 09:53:25 +00:00
|
|
|
ERROR_FINISH(sk, -1, finish);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (dev) /* if `dev' is not null bind the socket to it */
|
|
|
|
if (set_bindtodevice_sk(sk, dev) < 0)
|
2013-09-16 09:53:25 +00:00
|
|
|
ERROR_FINISH(sk, -1, finish);
|
|
|
|
|
|
|
|
if (connect(sk, sa, sa_len) == -1) {
|
|
|
|
error("Cannot connect to %s: %s", ntop, strerror(errno));
|
|
|
|
ERROR_FINISH(sk, -1, finish);
|
|
|
|
}
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
finish:
|
2013-09-16 09:53:25 +00:00
|
|
|
return sk;
|
|
|
|
}
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
int
|
|
|
|
new_bcast_conn(inet_prefix * host, short port, int dev_idx)
|
2014-05-03 19:10:51 +00:00
|
|
|
{
|
2013-09-16 09:53:25 +00:00
|
|
|
struct sockaddr_storage saddr_sto;
|
2014-09-17 12:41:24 +00:00
|
|
|
struct sockaddr *sa = (struct sockaddr *) &saddr_sto;
|
2013-09-16 09:53:25 +00:00
|
|
|
socklen_t alen;
|
|
|
|
int sk;
|
|
|
|
const char *ntop;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if ((sk = new_dgram_socket(host->family)) == -1)
|
2013-09-16 09:53:25 +00:00
|
|
|
return -1;
|
2014-09-17 12:41:24 +00:00
|
|
|
sk = set_broadcast_sk(sk, host->family, host, port, dev_idx);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
/*
|
2014-05-03 19:10:51 +00:00
|
|
|
* Connect
|
2013-09-16 09:53:25 +00:00
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
if (inet_to_sockaddr(host, port, sa, &alen)) {
|
2013-09-16 09:53:25 +00:00
|
|
|
error("set_broadcast_sk: %d Family not supported", host->family);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (host->family == AF_INET6) {
|
|
|
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
|
2013-09-16 09:53:25 +00:00
|
|
|
sin6->sin6_scope_id = dev_idx;
|
|
|
|
}
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (set_bindtodevice_sk(sk, (char *) ll_index_to_name(dev_idx)) < 0)
|
2013-09-16 09:53:25 +00:00
|
|
|
return -1;
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (connect(sk, sa, alen) == -1) {
|
|
|
|
ntop = inet_to_str(*host);
|
2014-05-03 19:10:51 +00:00
|
|
|
error("Cannot connect to the broadcast (%s): %s", ntop,
|
2014-09-17 12:41:24 +00:00
|
|
|
strerror(errno));
|
2013-09-16 09:53:25 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*\
|
|
|
|
*
|
|
|
|
* * * Recv/Send functions * *
|
|
|
|
*
|
|
|
|
\*/
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
ssize_t
|
|
|
|
inet_recv(int s, void *buf, size_t len, int flags)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
ssize_t err;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if ((err = recv(s, buf, len, flags)) == -1) {
|
|
|
|
switch (errno) {
|
|
|
|
default:
|
|
|
|
/* Probably connection was closed */
|
|
|
|
debug(DBG_NORMAL, "inet_recv: Cannot recv(): %s",
|
|
|
|
strerror(errno));
|
|
|
|
return err;
|
|
|
|
break;
|
2013-09-16 09:53:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2014-05-03 19:10:51 +00:00
|
|
|
/*
|
2013-09-16 09:53:25 +00:00
|
|
|
* inet_recv_timeout
|
2014-05-03 19:10:51 +00:00
|
|
|
*
|
2013-09-16 09:53:25 +00:00
|
|
|
* is the same as inet_recv() but if no reply is received for `timeout'
|
|
|
|
* seconds it returns -1.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
ssize_t
|
|
|
|
inet_recv_timeout(int s, void *buf, size_t len, int flags, u_int timeout)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
struct timeval timeout_t;
|
|
|
|
fd_set fdset;
|
|
|
|
int ret;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
MILLISEC_TO_TV(timeout * 1000, timeout_t);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
FD_ZERO(&fdset);
|
|
|
|
FD_SET(s, &fdset);
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
ret = select(s + 1, &fdset, NULL, NULL, &timeout_t);
|
2013-09-16 09:53:25 +00:00
|
|
|
if (ret == -1) {
|
|
|
|
error(ERROR_MSG "select error: %s", ERROR_FUNC, strerror(errno));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FD_ISSET(s, &fdset) ? inet_recv(s, buf, len, flags) : -1;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
ssize_t
|
|
|
|
inet_recvfrom(int s, void *buf, size_t len, int flags,
|
|
|
|
struct sockaddr * from, socklen_t * fromlen)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
ssize_t err;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if ((err = recvfrom(s, buf, len, flags, from, fromlen)) < 0) {
|
|
|
|
switch (errno) {
|
|
|
|
default:
|
|
|
|
error("inet_recvfrom: Cannot recv(): %s", strerror(errno));
|
|
|
|
return err;
|
|
|
|
break;
|
2013-09-16 09:53:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2014-05-03 19:10:51 +00:00
|
|
|
/*
|
2013-09-16 09:53:25 +00:00
|
|
|
* inet_recvfrom_timeout: is the same as inet_recvfrom() but if no reply is
|
|
|
|
* received for `timeout' seconds it returns -1.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
ssize_t
|
|
|
|
inet_recvfrom_timeout(int s, void *buf, size_t len, int flags,
|
|
|
|
struct sockaddr * from, socklen_t * fromlen,
|
|
|
|
u_int timeout)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
struct timeval timeout_t;
|
|
|
|
fd_set fdset;
|
|
|
|
int ret;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
MILLISEC_TO_TV(timeout * 1000, timeout_t);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
FD_ZERO(&fdset);
|
|
|
|
FD_SET(s, &fdset);
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
ret = select(s + 1, &fdset, NULL, NULL, &timeout_t);
|
2013-09-16 09:53:25 +00:00
|
|
|
if (ret == -1) {
|
|
|
|
error(ERROR_MSG "select error: %s", ERROR_FUNC, strerror(errno));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (FD_ISSET(s, &fdset))
|
2013-09-16 09:53:25 +00:00
|
|
|
return inet_recvfrom(s, buf, len, flags, from, fromlen);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
ssize_t
|
|
|
|
inet_send(int s, const void *msg, size_t len, int flags)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
ssize_t err;
|
|
|
|
|
|
|
|
if((err=send(s, msg, len, flags)) < 0) {
|
2014-05-03 19:10:51 +00:00
|
|
|
switch(errno)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
2014-09-21 23:15:48 +00:00
|
|
|
/* This divides the length of a packet if it is too large to send,
|
|
|
|
* it then sends the first half, And then the second half.
|
|
|
|
* If it is too large to send again, When it tries to send the first half
|
|
|
|
* it will just come back here to repeat the process as needed. */
|
2013-09-16 09:53:25 +00:00
|
|
|
case EMSGSIZE:
|
|
|
|
inet_send(s, msg, len/2, flags);
|
2014-05-03 19:10:51 +00:00
|
|
|
err=inet_send(s, (const char *)msg+(len/2),
|
2013-09-16 09:53:25 +00:00
|
|
|
len-(len/2), flags);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
error("inet_send: Cannot send(): %s", strerror(errno));
|
|
|
|
return err;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* inet_send_timeout: is the same as inet_send() but if the packet isn't sent
|
|
|
|
* in `timeout' seconds it timeouts and returns -1.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
ssize_t
|
|
|
|
inet_send_timeout(int s, const void *msg, size_t len, int flags,
|
|
|
|
u_int timeout)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
struct timeval timeout_t;
|
|
|
|
fd_set fdset;
|
|
|
|
int ret;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
MILLISEC_TO_TV(timeout * 1000, timeout_t);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
FD_ZERO(&fdset);
|
|
|
|
FD_SET(s, &fdset);
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
ret = select(s + 1, NULL, &fdset, NULL, &timeout_t);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
if (ret == -1) {
|
|
|
|
error(ERROR_MSG "select error: %s", ERROR_FUNC, strerror(errno));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (FD_ISSET(s, &fdset))
|
2013-09-16 09:53:25 +00:00
|
|
|
return inet_send(s, msg, len, flags);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
ssize_t
|
|
|
|
inet_sendto(int s, const void *msg, size_t len, int flags,
|
|
|
|
const struct sockaddr * to, socklen_t tolen)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
ssize_t err;
|
2014-10-26 14:28:07 +00:00
|
|
|
char * ipv4_addr;
|
2014-09-17 12:41:24 +00:00
|
|
|
if ((err = sendto(s, msg, len, flags, to, tolen)) == -1) {
|
|
|
|
switch (errno) {
|
|
|
|
case EMSGSIZE:
|
2014-10-21 09:31:41 +00:00
|
|
|
inet_sendto(s, msg, len/2, flags, to, tolen);
|
|
|
|
err=inet_sendto(s, (const char *)msg+(len/2),
|
|
|
|
len-(len/2), flags, to, tolen);
|
2014-09-17 12:41:24 +00:00
|
|
|
break;
|
|
|
|
case EFAULT:
|
2014-10-26 13:58:14 +00:00
|
|
|
error("To Family is: %hu "
|
2014-10-26 14:21:35 +00:00
|
|
|
"To Data is: %s "
|
|
|
|
"and: %s", to->sa_family,
|
|
|
|
inet_ntop(to->sa_family,
|
|
|
|
&(((struct sockaddr_in*)to)->sin_addr),
|
|
|
|
ipv4_addr, sizeof ipv4_addr),
|
|
|
|
strerror(errno));
|
2014-09-17 12:41:24 +00:00
|
|
|
default:
|
|
|
|
error("inet_sendto: Cannot send(): %s", strerror(errno));
|
|
|
|
return err;
|
|
|
|
break;
|
2013-09-16 09:53:25 +00:00
|
|
|
}
|
2014-10-21 08:20:46 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* inet_sendto_timeout: is the same as inet_sendto() but if the packet isn't sent
|
|
|
|
* in `timeout' seconds it timeouts and returns -1.
|
|
|
|
*/
|
2014-09-17 12:41:24 +00:00
|
|
|
ssize_t
|
|
|
|
inet_sendto_timeout(int s, const void *msg, size_t len, int flags,
|
|
|
|
const struct sockaddr * to, socklen_t tolen,
|
|
|
|
u_int timeout)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
struct timeval timeout_t;
|
|
|
|
fd_set fdset;
|
|
|
|
int ret;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
MILLISEC_TO_TV(timeout * 1000, timeout_t);
|
2014-05-03 19:10:51 +00:00
|
|
|
|
2013-09-16 09:53:25 +00:00
|
|
|
FD_ZERO(&fdset);
|
|
|
|
FD_SET(s, &fdset);
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
ret = select(s + 1, NULL, &fdset, NULL, &timeout_t);
|
2013-09-16 09:53:25 +00:00
|
|
|
|
|
|
|
if (ret == -1) {
|
|
|
|
error(ERROR_MSG "select error: %s", ERROR_FUNC, strerror(errno));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if (FD_ISSET(s, &fdset))
|
2013-09-16 09:53:25 +00:00
|
|
|
return inet_sendto(s, msg, len, flags, to, tolen);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
ssize_t
|
|
|
|
inet_sendfile(int out_fd, int in_fd, off_t * offset, size_t count)
|
2013-09-16 09:53:25 +00:00
|
|
|
{
|
|
|
|
ssize_t err;
|
|
|
|
|
2014-09-17 12:41:24 +00:00
|
|
|
if ((err = sendfile(out_fd, in_fd, offset, count)) == -1)
|
2013-09-16 09:53:25 +00:00
|
|
|
error("inet_sendfile: Cannot sendfile(): %s", strerror(errno));
|
|
|
|
return err;
|
|
|
|
}
|