922 lines
25 KiB
C
922 lines
25 KiB
C
/*
|
|
* (C) 2007-09 - Luca Deri <deri@ntop.org>
|
|
* Richard Andrews <andrews@ntop.org>
|
|
*
|
|
* 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/>
|
|
*
|
|
* Code contributions courtesy of:
|
|
* Richard Andrews <bbmaj7@yahoo.com.au>
|
|
* Massimo Torquati <torquati@ntop.org>
|
|
*
|
|
*/
|
|
|
|
#include "n2n.h"
|
|
|
|
#include "minilzo.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#if defined(DEBUG)
|
|
# define PURGE_REGISTRATION_FREQUENCY 60
|
|
# define REGISTRATION_TIMEOUT 120
|
|
#else /* #if defined(DEBUG) */
|
|
# define PURGE_REGISTRATION_FREQUENCY 60
|
|
# define REGISTRATION_TIMEOUT (60*5)
|
|
#endif /* #if defined(DEBUG) */
|
|
|
|
|
|
char broadcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
|
char multicast_addr[6] = { 0x01, 0x00, 0x05, 0x00, 0x00, 0x00 }; /* First 3 bytes are meaningful */
|
|
|
|
/* ************************************** */
|
|
|
|
static void print_header( const char * msg, const struct n2n_packet_header * hdr )
|
|
{
|
|
ipstr_t buf;
|
|
ipstr_t buf2;
|
|
|
|
traceEvent(TRACE_INFO, "%s hdr: public_ip=(%d)%s:%d, private_ip=(%d)%s:%d", msg,
|
|
hdr->public_ip.family,
|
|
intoa(ntohl(hdr->public_ip.addr_type.v4_addr), buf, sizeof(buf)),
|
|
ntohs(hdr->public_ip.port),
|
|
hdr->private_ip.family,
|
|
intoa(ntohl(hdr->private_ip.addr_type.v4_addr), buf2, sizeof(buf2)),
|
|
ntohs(hdr->private_ip.port)
|
|
);
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
extern void sockaddr_in2peer_addr(struct sockaddr_in *in, struct peer_addr *out) {
|
|
out->family = (u_int8_t)in->sin_family;
|
|
out->port = in->sin_port;
|
|
out->addr_type.v4_addr = in->sin_addr.s_addr;
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
extern void peer_addr2sockaddr_in(const struct peer_addr *in, struct sockaddr_in *out) {
|
|
out->sin_family = in->family;
|
|
out->sin_port = in->port;
|
|
out->sin_addr.s_addr = in->addr_type.v4_addr;
|
|
}
|
|
|
|
/* ************************************** */
|
|
|
|
static
|
|
int marshall_peer_addr( u_int8_t * buf, size_t * offset, const struct peer_addr * s )
|
|
{
|
|
/* RA: I'm pretty sure that this is broken. There is no guarantee that the
|
|
* peer_addr structure is packed. This will always work between like hosts but
|
|
* is almost certainly broken between different host types. */
|
|
memcpy( buf + *offset, s, sizeof(struct peer_addr));
|
|
*offset += sizeof(struct peer_addr);
|
|
|
|
return sizeof(struct peer_addr); /* bytes written */
|
|
}
|
|
|
|
/* ************************************** */
|
|
|
|
static
|
|
int marshall_uint32( u_int8_t * buf, size_t * offset, u_int32_t val )
|
|
{
|
|
buf[*offset + 0] = ((val >> 24) & 0xff);
|
|
buf[*offset + 1] = ((val >> 16) & 0xff);
|
|
buf[*offset + 2] = ((val >> 8) & 0xff);
|
|
buf[*offset + 3] = ((val ) & 0xff);
|
|
|
|
*offset += 4;
|
|
return 4;
|
|
}
|
|
|
|
/* ************************************** */
|
|
|
|
int marshall_n2n_packet_header( u_int8_t * buf, const struct n2n_packet_header * hdr )
|
|
{
|
|
size_t offset = 0;
|
|
|
|
print_header( "Marshalling ", hdr );
|
|
|
|
*(buf+offset) = hdr->version;
|
|
++offset;
|
|
|
|
*(buf+offset) = hdr->msg_type;
|
|
++offset;
|
|
|
|
*(buf+offset) = hdr->ttl;
|
|
++offset;
|
|
|
|
*(buf+offset) = hdr->sent_by_supernode;
|
|
++offset;
|
|
|
|
memcpy( buf+offset, hdr->community_name, COMMUNITY_LEN );
|
|
offset += COMMUNITY_LEN;
|
|
|
|
memcpy( buf+offset, hdr->src_mac, 6 );
|
|
offset += 6;
|
|
|
|
memcpy( buf+offset, hdr->dst_mac, 6 );
|
|
offset += 6;
|
|
|
|
marshall_peer_addr( buf, &offset, &(hdr->public_ip) );
|
|
marshall_peer_addr( buf, &offset, &(hdr->private_ip) );
|
|
|
|
*(buf+offset) = (hdr->pkt_type & 0xff);
|
|
++offset;
|
|
|
|
marshall_uint32( buf, &offset, hdr->sequence_id );
|
|
marshall_uint32( buf, &offset, hdr->crc );
|
|
|
|
return offset;
|
|
}
|
|
|
|
/* ************************************** */
|
|
|
|
static
|
|
int unmarshall_peer_addr( struct peer_addr * s, size_t * offset,
|
|
const u_int8_t * buf )
|
|
{
|
|
memcpy(s, buf + *offset, sizeof(struct peer_addr));
|
|
*offset += sizeof(struct peer_addr);
|
|
return (sizeof(struct peer_addr)); /* bytes written */
|
|
}
|
|
|
|
/* ************************************** */
|
|
|
|
static
|
|
int unmarshall_uint32( u_int32_t * val, size_t * offset, const u_int8_t * buf )
|
|
{
|
|
*val = ( (buf[*offset + 0] & 0xff) << 24 );
|
|
*val |= ( (buf[*offset + 1] & 0xff) << 16 );
|
|
*val |= ( (buf[*offset + 2] & 0xff) << 8 );
|
|
*val |= ( (buf[*offset + 3] & 0xff) );
|
|
|
|
*offset += 4;
|
|
return 4;
|
|
}
|
|
|
|
/* ************************************** */
|
|
|
|
int unmarshall_n2n_packet_header( struct n2n_packet_header * hdr, const u_int8_t * buf )
|
|
{
|
|
size_t offset=0;
|
|
|
|
hdr->version = *(buf + offset);
|
|
++offset;
|
|
|
|
hdr->msg_type = *(buf + offset);
|
|
++offset;
|
|
|
|
hdr->ttl = *(buf + offset);
|
|
++offset;
|
|
|
|
hdr->sent_by_supernode = *(buf + offset);
|
|
++offset;
|
|
|
|
memcpy( hdr->community_name, (buf + offset), COMMUNITY_LEN );
|
|
offset += COMMUNITY_LEN;
|
|
|
|
memcpy( hdr->src_mac, (buf + offset), 6 );
|
|
offset += 6;
|
|
|
|
memcpy( hdr->dst_mac, (buf + offset), 6 );
|
|
offset += 6;
|
|
|
|
unmarshall_peer_addr( &(hdr->public_ip), &offset, buf );
|
|
unmarshall_peer_addr( &(hdr->private_ip), &offset, buf );
|
|
|
|
hdr->pkt_type = (*(buf + offset) & 0xff); /* Make sure only 8 bits are copied. */
|
|
++offset;
|
|
|
|
unmarshall_uint32( &(hdr->sequence_id), &offset, buf );
|
|
unmarshall_uint32( &(hdr->crc), &offset, buf );
|
|
|
|
print_header( "Unmarshalled ", hdr );
|
|
|
|
return offset;
|
|
}
|
|
|
|
/* ************************************** */
|
|
|
|
SOCKET open_socket(int local_port, int udp_sock, int server_mode) {
|
|
SOCKET sock_fd;
|
|
struct sockaddr_in local_address;
|
|
int sockopt = 1;
|
|
|
|
if((sock_fd = socket(PF_INET, udp_sock ? SOCK_DGRAM : SOCK_STREAM, 0)) < 0) {
|
|
traceEvent(TRACE_ERROR, "Unable to create socket [%s][%d]\n",
|
|
strerror(errno), sock_fd);
|
|
return(-1);
|
|
}
|
|
|
|
#ifndef WIN32
|
|
/* fcntl(sock_fd, F_SETFL, O_NONBLOCK); */
|
|
#endif
|
|
|
|
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sizeof(sockopt));
|
|
|
|
memset(&local_address, 0, sizeof(local_address));
|
|
local_address.sin_family = AF_INET;
|
|
local_address.sin_port = htons(local_port);
|
|
local_address.sin_addr.s_addr = INADDR_ANY;
|
|
if(bind(sock_fd, (struct sockaddr*) &local_address, sizeof(local_address)) == -1) {
|
|
traceEvent(TRACE_ERROR, "Bind error [%s]\n", strerror(errno));
|
|
return(-1);
|
|
}
|
|
|
|
if((!udp_sock) && server_mode) {
|
|
if(listen(sock_fd, 255) == -1) {
|
|
traceEvent(TRACE_ERROR, "Listen error [%s]\n", strerror(errno));
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
return(sock_fd);
|
|
}
|
|
|
|
/* ************************************** */
|
|
|
|
int connect_socket(int sock_fd, struct peer_addr* _dest) {
|
|
char *http_header;
|
|
int len, rc;
|
|
struct sockaddr_in dest;
|
|
|
|
peer_addr2sockaddr_in(_dest, &dest);
|
|
|
|
/* FIX: add IPv6 support */
|
|
rc = connect(sock_fd, (struct sockaddr*)&dest, sizeof(struct sockaddr_in));
|
|
|
|
if(rc == -1) {
|
|
traceEvent(TRACE_WARNING, "connect() error [%s]\n", strerror(errno));
|
|
return(-1);
|
|
}
|
|
|
|
/* Send dummy http header */
|
|
http_header = "GET / HTTP/1.0\r\n\r\n";
|
|
len = strlen(http_header);
|
|
rc = send(sock_fd, http_header, len, 0);
|
|
|
|
return((rc == len) ? 0 : -1);
|
|
}
|
|
|
|
|
|
/* *********************************************** */
|
|
|
|
void send_packet(n2n_sock_info_t * sinfo,
|
|
char *packet, size_t *packet_len,
|
|
const struct peer_addr *remote_peer, u_int8_t compress_data) {
|
|
int data_sent_len;
|
|
|
|
data_sent_len = unreliable_sendto(sinfo,
|
|
packet, packet_len, remote_peer, compress_data);
|
|
|
|
if(data_sent_len != *packet_len)
|
|
traceEvent(TRACE_WARNING,
|
|
"sendto() [sent=%d][attempted_to_send=%d] [%s]\n",
|
|
data_sent_len, *packet_len, strerror(errno));
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
int traceLevel = 2 /* NORMAL */;
|
|
int useSyslog = 0, syslog_opened = 0;
|
|
|
|
#define N2N_TRACE_DATESIZE 32
|
|
void traceEvent(int eventTraceLevel, char* file, int line, char * format, ...) {
|
|
va_list va_ap;
|
|
|
|
if(eventTraceLevel <= traceLevel) {
|
|
char buf[2048];
|
|
char out_buf[640];
|
|
char theDate[N2N_TRACE_DATESIZE];
|
|
char *extra_msg = "";
|
|
time_t theTime = time(NULL);
|
|
#ifdef WIN32
|
|
int i;
|
|
#endif
|
|
|
|
/* We have two paths - one if we're logging, one if we aren't
|
|
* Note that the no-log case is those systems which don't support it (WIN32),
|
|
* those without the headers !defined(USE_SYSLOG)
|
|
* those where it's parametrically off...
|
|
*/
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
strftime(theDate, N2N_TRACE_DATESIZE, "%d/%b/%Y %H:%M:%S", localtime(&theTime));
|
|
|
|
va_start (va_ap, format);
|
|
vsnprintf(buf, sizeof(buf)-1, format, va_ap);
|
|
va_end(va_ap);
|
|
|
|
if(eventTraceLevel == 0 /* TRACE_ERROR */)
|
|
extra_msg = "ERROR: ";
|
|
else if(eventTraceLevel == 1 /* TRACE_WARNING */)
|
|
extra_msg = "WARNING: ";
|
|
|
|
while(buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0';
|
|
|
|
#ifndef WIN32
|
|
if(useSyslog) {
|
|
if(!syslog_opened) {
|
|
openlog("n2n", LOG_PID, LOG_DAEMON);
|
|
syslog_opened = 1;
|
|
}
|
|
|
|
snprintf(out_buf, sizeof(out_buf), "%s%s", extra_msg, buf);
|
|
syslog(LOG_INFO, out_buf);
|
|
} else {
|
|
snprintf(out_buf, sizeof(out_buf), "%s [%11s:%4d] %s%s", theDate, file, line, extra_msg, buf);
|
|
printf("%s\n", out_buf);
|
|
fflush(stdout);
|
|
}
|
|
#else
|
|
/* this is the WIN32 code */
|
|
for(i=strlen(file)-1; i>0; i--) if(file[i] == '\\') { i++; break; };
|
|
snprintf(out_buf, sizeof(out_buf), "%s [%11s:%4d] %s%s", theDate, &file[i], line, extra_msg, buf);
|
|
printf("%s\n", out_buf);
|
|
fflush(stdout);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
/* addr should be in network order. Things are so much simpler that way. */
|
|
char* intoa(u_int32_t /* host order */ addr, char* buf, u_short buf_len) {
|
|
char *cp, *retStr;
|
|
u_int byte;
|
|
int n;
|
|
|
|
cp = &buf[buf_len];
|
|
*--cp = '\0';
|
|
|
|
n = 4;
|
|
do {
|
|
byte = addr & 0xff;
|
|
*--cp = byte % 10 + '0';
|
|
byte /= 10;
|
|
if (byte > 0) {
|
|
*--cp = byte % 10 + '0';
|
|
byte /= 10;
|
|
if (byte > 0)
|
|
*--cp = byte + '0';
|
|
}
|
|
*--cp = '.';
|
|
addr >>= 8;
|
|
} while (--n > 0);
|
|
|
|
/* Convert the string to lowercase */
|
|
retStr = (char*)(cp+1);
|
|
|
|
return(retStr);
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
char* macaddr_str(const char *mac, char *buf, int buf_len) {
|
|
snprintf(buf, buf_len, "%02X:%02X:%02X:%02X:%02X:%02X",
|
|
mac[0] & 0xFF, mac[1] & 0xFF, mac[2] & 0xFF,
|
|
mac[3] & 0xFF, mac[4] & 0xFF, mac[5] & 0xFF);
|
|
return(buf);
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
void fill_standard_header_fields(n2n_sock_info_t * sinfo,
|
|
struct n2n_packet_header *hdr, char *src_mac) {
|
|
socklen_t len = sizeof(hdr->private_ip);
|
|
memset(hdr, 0, N2N_PKT_HDR_SIZE);
|
|
hdr->version = N2N_PKT_VERSION;
|
|
hdr->crc = 0; // FIX
|
|
if(src_mac != NULL) memcpy(hdr->src_mac, src_mac, 6);
|
|
getsockname(sinfo->sock, (struct sockaddr*)&hdr->private_ip, &len);
|
|
hdr->public_ip.family = AF_INET;
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
void send_ack(n2n_sock_info_t * sinfo,
|
|
u_int16_t last_rcvd_seq_id,
|
|
struct n2n_packet_header *header,
|
|
struct peer_addr *remote_peer,
|
|
char *src_mac) {
|
|
|
|
/* marshalling double-checked. */
|
|
struct n2n_packet_header hdr;
|
|
u_int8_t pkt[ N2N_PKT_HDR_SIZE ];
|
|
size_t len = sizeof(hdr);
|
|
size_t len2;
|
|
int compress_data = N2N_COMPRESSION_ENABLED;
|
|
|
|
fill_standard_header_fields(sinfo, &hdr, src_mac);
|
|
hdr.msg_type = MSG_TYPE_ACK_RESPONSE;
|
|
hdr.sequence_id = last_rcvd_seq_id;
|
|
memcpy(hdr.community_name, header->community_name, COMMUNITY_LEN);
|
|
|
|
len2=marshall_n2n_packet_header( pkt, &hdr );
|
|
assert( len2 == len );
|
|
|
|
send_packet(sinfo, (char*)pkt, &len, remote_peer, compress_data);
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
u_int8_t is_multi_broadcast(char *dest_mac) {
|
|
return(((!memcmp(broadcast_addr, dest_mac, 6))
|
|
|| (!memcmp(multicast_addr, dest_mac, 3))) ? 1 : 0);
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
/* http://www.faqs.org/rfcs/rfc908.html */
|
|
|
|
u_int receive_data(n2n_sock_info_t * sinfo,
|
|
char *packet, size_t packet_len,
|
|
struct peer_addr *from, u_int8_t *discarded_pkt,
|
|
char *tun_mac_addr, u_int8_t decompress_data,
|
|
struct n2n_packet_header *hdr) {
|
|
socklen_t fromlen = sizeof(struct sockaddr_in);
|
|
int len;
|
|
char *payload, *pkt_type;
|
|
macstr_t src_mac_buf;
|
|
macstr_t dst_mac_buf;
|
|
ipstr_t ip_buf;
|
|
ipstr_t from_ip_buf;
|
|
|
|
if(sinfo->is_udp_socket) {
|
|
struct sockaddr_in _from;
|
|
len = recvfrom(sinfo->sock, packet, packet_len, 0, (struct sockaddr*)&_from, &fromlen);
|
|
sockaddr_in2peer_addr(&_from, from);
|
|
} else {
|
|
len = recv(sinfo->sock, packet, 4, 0);
|
|
if(len == 4) {
|
|
packet[4] = '\0';
|
|
len = atoi(packet);
|
|
len = recv(sinfo->sock, packet, len, 0);
|
|
} else {
|
|
traceEvent(TRACE_WARNING, "Unable to receive n2n packet length");
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
unmarshall_n2n_packet_header(hdr, (u_int8_t *)packet);
|
|
|
|
payload = &packet[N2N_PKT_HDR_SIZE];
|
|
|
|
if(len < 0) {
|
|
#ifdef WIN32
|
|
if(WSAGetLastError() != WSAECONNRESET /* http://support.microsoft.com/kb/263823 */ ) {
|
|
traceEvent(TRACE_WARNING, "recvfrom returned %d [err=%d]", len, WSAGetLastError());
|
|
}
|
|
#endif
|
|
return(0);
|
|
} else if(len > MIN_COMPRESSED_PKT_LEN) {
|
|
#define N2N_DECOMPRESS_BUFSIZE 2048
|
|
char decompressed[N2N_DECOMPRESS_BUFSIZE];
|
|
int rc;
|
|
lzo_uint decompressed_len=N2N_DECOMPRESS_BUFSIZE;
|
|
size_t insize = len-N2N_PKT_HDR_SIZE;
|
|
|
|
if(decompress_data) {
|
|
rc = lzo1x_decompress_safe((u_char*)&packet[N2N_PKT_HDR_SIZE],
|
|
insize,
|
|
(u_char*)decompressed, &decompressed_len, NULL);
|
|
|
|
if(rc == LZO_E_OK)
|
|
{
|
|
traceEvent(TRACE_INFO, "%u bytes decompressed into %u", insize, decompressed_len);
|
|
}
|
|
else
|
|
{
|
|
traceEvent(TRACE_WARNING, "Failed to decompress %u byte packet. LZO error=%d", insize, rc );
|
|
return -1;
|
|
}
|
|
|
|
if(packet_len > decompressed_len) {
|
|
memcpy(&packet[N2N_PKT_HDR_SIZE], decompressed, decompressed_len);
|
|
len = decompressed_len+N2N_PKT_HDR_SIZE;
|
|
} else {
|
|
traceEvent(TRACE_WARNING, "Uncompressed packet is too large [decompressed_len=%d]",
|
|
decompressed_len);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
(*discarded_pkt) = 0;
|
|
|
|
if(!hdr->sent_by_supernode) {
|
|
memcpy( &packet[offsetof(struct n2n_packet_header, public_ip)], from, sizeof(struct sockaddr_in) );
|
|
}
|
|
|
|
switch(hdr->pkt_type) {
|
|
case packet_unreliable_data:
|
|
pkt_type = "unreliable data";
|
|
break;
|
|
case packet_reliable_data:
|
|
pkt_type = "reliable data";
|
|
break;
|
|
case packet_ping:
|
|
pkt_type = "ping";
|
|
break;
|
|
case packet_pong:
|
|
pkt_type = "pong";
|
|
break;
|
|
default:
|
|
pkt_type = "???";
|
|
}
|
|
|
|
traceEvent(TRACE_INFO, "+++ Received %s packet [rcvd_from=%s:%d][msg_type=%s][seq_id=%d]",
|
|
pkt_type,
|
|
intoa(ntohl(from->addr_type.v4_addr), from_ip_buf, sizeof(from_ip_buf)),
|
|
ntohs(from->port), msg_type2str(hdr->msg_type),
|
|
hdr->sequence_id);
|
|
traceEvent(TRACE_INFO, " [src_mac=%s][dst_mac=%s][original_sender=%s:%d]",
|
|
macaddr_str(hdr->src_mac, src_mac_buf, sizeof(src_mac_buf)),
|
|
macaddr_str(hdr->dst_mac, dst_mac_buf, sizeof(dst_mac_buf)),
|
|
intoa(ntohl(hdr->public_ip.addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(hdr->public_ip.port));
|
|
|
|
#ifdef HANDLE_RETRANSMISSION
|
|
if((hdr->pkt_type == packet_reliable_data)
|
|
&& (hdr->msg_type == MSG_TYPE_PACKET)) {
|
|
(*discarded_pkt) = handle_ack(sock_fd, is_udp_socket, hdr,
|
|
&payload[6], payload, from, tun_mac_addr);
|
|
} else
|
|
(*discarded_pkt) = 0;
|
|
#endif
|
|
} else
|
|
traceEvent(TRACE_WARNING, "Receive error [%s] or pkt too short [len=%d]\n",
|
|
strerror(errno), len);
|
|
|
|
return(len);
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
#if 0
|
|
static u_int32_t queue_packet(struct send_hash_entry *scan,
|
|
char *packet,
|
|
u_int16_t packet_len) {
|
|
struct packet_list *pkt = (struct packet_list*)malloc(sizeof(struct packet_list));
|
|
|
|
if(pkt == NULL) {
|
|
traceEvent(TRACE_ERROR, "Not enough memory!");
|
|
return(0);
|
|
}
|
|
|
|
if((pkt->packet = (char*)malloc(packet_len)) == NULL) {
|
|
traceEvent(TRACE_ERROR, "Not enough memory!");
|
|
return(0);
|
|
}
|
|
|
|
memcpy(pkt->packet, packet, packet_len);
|
|
pkt->packet_len = packet_len;
|
|
pkt->seq_id = scan->last_seq_id;
|
|
pkt->next = scan->unacked_packet_list;
|
|
scan->unacked_packet_list = pkt;
|
|
scan->num_unacked_pkts++;
|
|
return(pkt->seq_id);
|
|
}
|
|
#endif
|
|
|
|
/* *********************************************** */
|
|
|
|
/* Work-memory needed for compression. Allocate memory in units
|
|
* of `lzo_align_t' (instead of `char') to make sure it is properly aligned.
|
|
*/
|
|
|
|
#define HEAP_ALLOC(var,size) \
|
|
lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]
|
|
|
|
static HEAP_ALLOC(wrkmem,LZO1X_1_MEM_COMPRESS);
|
|
|
|
/* ******************************************************* */
|
|
|
|
u_int send_data(n2n_sock_info_t * sinfo,
|
|
char *packet, size_t *packet_len,
|
|
const struct peer_addr *to, u_int8_t compress_data) {
|
|
char compressed[1650];
|
|
int rc;
|
|
lzo_uint compressed_len=0;
|
|
struct sockaddr_in destsock;
|
|
|
|
if(*packet_len < N2N_PKT_HDR_SIZE) {
|
|
traceEvent(TRACE_WARNING, "The packet about to be sent is too short [len=%d]\n", *packet_len);
|
|
return(-1);
|
|
}
|
|
|
|
memcpy(compressed, packet, N2N_PKT_HDR_SIZE);
|
|
|
|
peer_addr2sockaddr_in(to, &destsock);
|
|
|
|
if(compress_data) {
|
|
rc = lzo1x_1_compress((u_char*)&packet[N2N_PKT_HDR_SIZE],
|
|
*packet_len - N2N_PKT_HDR_SIZE,
|
|
(u_char*)&compressed[N2N_PKT_HDR_SIZE],
|
|
&compressed_len, wrkmem);
|
|
|
|
if ( 0 == compressed_len )
|
|
{
|
|
traceEvent(TRACE_WARNING, "failed to compress %u bytes.", (*packet_len - N2N_PKT_HDR_SIZE) );
|
|
return -1;
|
|
}
|
|
|
|
compressed_len += N2N_PKT_HDR_SIZE;
|
|
|
|
traceEvent(TRACE_INFO, "%u bytes compressed into %u", *packet_len, compressed_len);
|
|
/* *packet_len = compressed_len; */
|
|
|
|
if(sinfo->is_udp_socket) {
|
|
rc = sendto(sinfo->sock, compressed, compressed_len, 0,
|
|
(struct sockaddr*)&destsock, sizeof(struct sockaddr_in));
|
|
} else {
|
|
char send_len[5];
|
|
|
|
/* 4 bytes packet length */
|
|
snprintf(send_len, sizeof(send_len), "%04d", (int)compressed_len);
|
|
if((rc = send(sinfo->sock, send_len, 4, 0)) != 4)
|
|
return(-1);
|
|
if((rc = send(sinfo->sock, compressed, compressed_len, 0)) != compressed_len) {
|
|
traceEvent(TRACE_WARNING, "send error [%d][%s]",
|
|
errno, strerror(errno));
|
|
}
|
|
}
|
|
} else {
|
|
compressed_len = *packet_len;
|
|
if(sinfo->is_udp_socket)
|
|
rc = sendto(sinfo->sock, packet, compressed_len, 0,
|
|
(struct sockaddr*)&destsock, sizeof(struct sockaddr_in));
|
|
else {
|
|
char send_len[5];
|
|
|
|
/* 4 bytes packet length */
|
|
snprintf(send_len, sizeof(send_len), "%04d", (int)compressed_len);
|
|
if((rc = send(sinfo->sock, send_len, 4, 0)) != 4)
|
|
return(-1);
|
|
rc = send(sinfo->sock, compressed, compressed_len, 0);
|
|
}
|
|
|
|
if(rc == -1) {
|
|
ipstr_t ip_buf;
|
|
|
|
traceEvent(TRACE_WARNING, "sendto() failed while attempting to send data to %s:%d",
|
|
intoa(ntohl(to->addr_type.v4_addr), ip_buf, sizeof(ip_buf)),
|
|
ntohs(to->port));
|
|
}
|
|
}
|
|
|
|
if ( rc >= 0) {
|
|
traceEvent(TRACE_INFO, "### Tx N2N Msg -> network");
|
|
}
|
|
|
|
if(rc == compressed_len)
|
|
return(*packet_len); /* fake just to avoid warnings */
|
|
else
|
|
return(rc);
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
u_int reliable_sendto(n2n_sock_info_t * sinfo,
|
|
char *packet, size_t *packet_len,
|
|
const struct peer_addr *to, u_int8_t compress_data) {
|
|
/* char *payload = &packet[N2N_PKT_HDR_SIZE]; */
|
|
struct n2n_packet_header hdr_storage;
|
|
struct n2n_packet_header *hdr = &hdr_storage;
|
|
macstr_t src_mac_buf;
|
|
macstr_t dst_mac_buf;
|
|
|
|
/* REVISIT: efficiency of unmarshal + re-marshal just to change a couple of bits. */
|
|
unmarshall_n2n_packet_header( hdr, (u_int8_t *)packet );
|
|
|
|
/* hdr->sequence_id = (hdr->msg_type == MSG_TYPE_PACKET) ? mac2sequence((u_char*)payload, packet, *packet_len) : 0; */
|
|
hdr->sequence_id = 0;
|
|
hdr->pkt_type = packet_reliable_data;
|
|
|
|
traceEvent(TRACE_INFO, "Sent reliable packet [msg_type=%s][seq_id=%d][src_mac=%s][dst_mac=%s]",
|
|
msg_type2str(hdr->msg_type), hdr->sequence_id,
|
|
macaddr_str(&packet[6], src_mac_buf, sizeof(src_mac_buf)),
|
|
macaddr_str(packet, dst_mac_buf, sizeof(dst_mac_buf)));
|
|
|
|
marshall_n2n_packet_header( (u_int8_t *)packet, hdr );
|
|
|
|
return(send_data(sinfo, packet, packet_len, to, compress_data));
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
/* unreliable_sendto is passed a fully marshalled, packet. Its purpose is to set
|
|
* the unreliable flags but leave the rest of the packet untouched. */
|
|
u_int unreliable_sendto(n2n_sock_info_t * sinfo,
|
|
char *packet, size_t *packet_len,
|
|
const struct peer_addr *to, u_int8_t compress_data) {
|
|
struct n2n_packet_header hdr_storage;
|
|
struct n2n_packet_header *hdr = &hdr_storage;
|
|
macstr_t src_mac_buf;
|
|
macstr_t dst_mac_buf;
|
|
|
|
/* REVISIT: efficiency of unmarshal + re-marshal just to change a couple of bits. */
|
|
unmarshall_n2n_packet_header( hdr, (u_int8_t *)packet );
|
|
|
|
hdr->sequence_id = 0; /* Unreliable messages have 0 as sequence number */
|
|
hdr->pkt_type = packet_unreliable_data;
|
|
|
|
traceEvent(TRACE_INFO, "Sent unreliable packet [msg_type=%s][seq_id=%d][src_mac=%s][dst_mac=%s]",
|
|
msg_type2str(hdr->msg_type), hdr->sequence_id,
|
|
macaddr_str(hdr->src_mac, src_mac_buf, sizeof(src_mac_buf)),
|
|
macaddr_str(hdr->dst_mac, dst_mac_buf, sizeof(dst_mac_buf)));
|
|
|
|
marshall_n2n_packet_header( (u_int8_t *)packet, hdr );
|
|
|
|
return(send_data(sinfo, packet, packet_len, to, compress_data));
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
char* msg_type2str(u_short msg_type) {
|
|
switch(msg_type) {
|
|
case MSG_TYPE_REGISTER: return("MSG_TYPE_REGISTER");
|
|
case MSG_TYPE_DEREGISTER: return("MSG_TYPE_DEREGISTER");
|
|
case MSG_TYPE_PACKET: return("MSG_TYPE_PACKET");
|
|
case MSG_TYPE_REGISTER_ACK: return("MSG_TYPE_REGISTER_ACK");
|
|
case MSG_TYPE_ACK_RESPONSE: return("MSG_TYPE_ACK_RESPONSE");
|
|
}
|
|
|
|
return("???");
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
void hexdump(char *buf, u_int len) {
|
|
u_int i;
|
|
|
|
for(i=0; i<len; i++) {
|
|
if((i > 0) && ((i % 16) == 0)) printf("\n");
|
|
printf("%02X ", buf[i] & 0xFF);
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
void print_n2n_version() {
|
|
printf("Welcome to n2n v.%s for %s\n"
|
|
"Built on %s\n"
|
|
"Copyright 2007-08 - http://www.ntop.org\n\n",
|
|
version, osName, buildDate);
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Find the peer entry in list with mac_addr equal to mac.
|
|
*
|
|
* Does not modify the list.
|
|
*
|
|
* @return NULL if not found; otherwise pointer to peer entry.
|
|
*/
|
|
struct peer_info * find_peer_by_mac( struct peer_info * list, const char * mac )
|
|
{
|
|
while(list != NULL)
|
|
{
|
|
if( 0 == memcmp(mac, list->mac_addr, 6) )
|
|
{
|
|
return list;
|
|
}
|
|
list = list->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/** Return the number of elements in the list.
|
|
*
|
|
*/
|
|
size_t peer_list_size( const struct peer_info * list )
|
|
{
|
|
size_t retval=0;
|
|
|
|
while ( list )
|
|
{
|
|
++retval;
|
|
list = list->next;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/** Add new to the head of list. If list is NULL; create it.
|
|
*
|
|
* The item new is added to the head of the list. New is modified during
|
|
* insertion. list takes ownership of new.
|
|
*/
|
|
void peer_list_add( struct peer_info * * list,
|
|
struct peer_info * new )
|
|
{
|
|
new->next = *list;
|
|
new->last_seen = time(NULL);
|
|
*list = new;
|
|
}
|
|
|
|
|
|
size_t purge_expired_registrations( struct peer_info ** peer_list ) {
|
|
static time_t last_purge = 0;
|
|
time_t now = time(NULL);
|
|
size_t num_reg = 0;
|
|
|
|
if((now - last_purge) < PURGE_REGISTRATION_FREQUENCY) return 0;
|
|
|
|
traceEvent(TRACE_INFO, "Purging old registrations");
|
|
|
|
num_reg = purge_peer_list( peer_list, now-REGISTRATION_TIMEOUT );
|
|
|
|
last_purge = now;
|
|
traceEvent(TRACE_INFO, "Remove %ld registrations", num_reg);
|
|
|
|
return num_reg;
|
|
}
|
|
|
|
/** Purge old items from the peer_list and return the number of items that were removed. */
|
|
size_t purge_peer_list( struct peer_info ** peer_list,
|
|
time_t purge_before )
|
|
{
|
|
struct peer_info *scan;
|
|
struct peer_info *prev;
|
|
size_t retval=0;
|
|
|
|
scan = *peer_list;
|
|
prev = NULL;
|
|
while(scan != NULL)
|
|
{
|
|
if(scan->last_seen < purge_before)
|
|
{
|
|
struct peer_info *next = scan->next;
|
|
|
|
if(prev == NULL)
|
|
{
|
|
*peer_list = next;
|
|
}
|
|
else
|
|
{
|
|
prev->next = next;
|
|
}
|
|
|
|
++retval;
|
|
free(scan);
|
|
scan = next;
|
|
}
|
|
else
|
|
{
|
|
prev = scan;
|
|
scan = scan->next;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static u_int8_t hex2byte( const char * s )
|
|
{
|
|
char tmp[3];
|
|
tmp[0]=s[0];
|
|
tmp[1]=s[1];
|
|
tmp[2]=0; /* NULL term */
|
|
|
|
return((u_int8_t)strtol( s, NULL, 16 ));
|
|
}
|
|
|
|
extern int str2mac( u_int8_t * outmac /* 6 bytes */, const char * s )
|
|
{
|
|
size_t i;
|
|
|
|
/* break it down as one case for the first "HH", the 5 x through loop for
|
|
* each ":HH" where HH is a two hex nibbles in ASCII. */
|
|
|
|
*outmac=hex2byte(s);
|
|
++outmac;
|
|
s+=2; /* don't skip colon yet - helps generalise loop. */
|
|
|
|
for (i=1; i<6; ++i )
|
|
{
|
|
s+=1;
|
|
*outmac=hex2byte(s);
|
|
++outmac;
|
|
s+=2;
|
|
}
|
|
|
|
return 0; /* ok */
|
|
}
|