Browse Source

merge remote-tracking branch 'upstream/dev' into dev

pull/218/head
Logan007 5 years ago
parent
commit
01f7825fd7
  1. 3
      .gitignore
  2. 27
      CMakeLists.txt
  3. 24
      Makefile.in
  4. 2
      README.md
  5. 3
      android/edge_android.c
  6. 15
      configure.seed
  7. 1
      doc/NEW_FEATURES.txt
  8. 22
      edge.8
  9. 22
      edge.c
  10. 165
      edge_utils.c
  11. 3
      example_edge_embed.c
  12. 16
      n2n.c
  13. 4
      n2n.h
  14. 2
      n2n_wire.h
  15. 2
      packages/rpm/Makefile.in
  16. 22
      packages/rpm/configure
  17. 11
      packages/rpm/configure.in
  18. 45
      tools/Makefile.in
  19. 0
      tools/benchmark.c
  20. 372
      tools/n2n_decode.c
  21. 240
      tuntap_linux.c
  22. 2
      tuntap_osx.c

3
.gitignore

@ -6,10 +6,11 @@ configure.ac
config.*
Makefile
autom4te.cache
benchmark
edge
example_edge_embed
supernode
tools/n2n-benchmark
tools/n2n-decode
build
packages/debian/debian/changelog
packages/debian/debian/control

27
CMakeLists.txt

@ -1,5 +1,6 @@
project(n2n)
cmake_minimum_required(VERSION 2.6)
include(CheckFunctionExists)
# N2n information
set(N2N_VERSION 2.5.1)
@ -16,6 +17,8 @@ if(NOT DEFINED N2N_OPTION_AES)
set(N2N_OPTION_AES ON)
endif(NOT DEFINED N2N_OPTION_AES)
add_definitions(-DCMAKE_BUILD)
add_definitions(-DGIT_RELEASE="" -DPACKAGE_VERSION="${N2N_VERSION}" -DPACKAGE_OSNAME="${CMAKE_SYSTEM}")
add_definitions(-DN2N_VERSION="${N2N_VERSION}" -DN2N_OSNAME="${N2N_OSNAME}")
if(N2N_OPTION_AES)
@ -79,15 +82,33 @@ target_link_libraries(edge n2n)
add_executable(supernode sn.c)
target_link_libraries(supernode n2n)
add_executable(benchmark benchmark.c)
target_link_libraries(benchmark n2n)
install(TARGETS edge supernode
RUNTIME DESTINATION sbin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
# Tools
include_directories(.)
add_executable(n2n-benchmark tools/benchmark.c)
target_link_libraries(n2n-benchmark n2n)
find_library(PCAP_LIB pcap)
if(PCAP_LIB)
add_executable(n2n-decode tools/n2n_decode.c)
target_link_libraries(n2n-decode n2n pcap)
install(TARGETS n2n-decode RUNTIME DESTINATION bin)
set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIB})
check_function_exists(pcap_set_immediate_mode HAVE_PCAP_IMMEDIATE_MODE)
IF(HAVE_PCAP_IMMEDIATE_MODE)
ADD_DEFINITIONS("-DHAVE_PCAP_IMMEDIATE_MODE")
ENDIF()
endif()
install(TARGETS n2n-benchmark RUNTIME DESTINATION bin)
# Documentation
if(DEFINED UNIX)
add_dependencies(n2n doc)

24
Makefile.in

@ -8,7 +8,7 @@ GIT_COMMITS=@GIT_COMMITS@
CC?=gcc
DEBUG?=-g3
#OPTIMIZATION?=-O2
WARN?=-Wall -Wshadow -Wpointer-arith -Wmissing-declarations -Wnested-externs
WARN?=-Wall
#Ultrasparc64 users experiencing SIGBUS should try the following gcc options
#(thanks to Robert Gibbon)
@ -66,22 +66,23 @@ APPS+=example_edge_embed
DOCS=edge.8.gz supernode.1.gz n2n.7.gz
all: $(APPS) $(DOCS) benchmark
.PHONY: steps build push all clean install tools
all: $(APPS) $(DOCS) tools
tools: $(N2N_LIB)
$(MAKE) -C $@
edge: edge.c $(N2N_LIB) n2n_wire.h n2n.h Makefile
$(CC) $(CFLAGS) edge.c $(N2N_LIB) $(LIBS_EDGE) -o edge
$(CC) $(CFLAGS) $< $(N2N_LIB) $(LIBS_EDGE) -o $@
supernode: sn.c $(N2N_LIB) n2n.h Makefile
$(CC) $(CFLAGS) sn.c $(N2N_LIB) $(LIBS_SN) -o supernode
benchmark: benchmark.c $(N2N_LIB) n2n_wire.h n2n.h Makefile
$(CC) $(CFLAGS) benchmark.c $(N2N_LIB) $(LIBS_EDGE) -o benchmark
$(CC) $(CFLAGS) $< $(N2N_LIB) $(LIBS_SN) -o $@
example_edge_embed: example_edge_embed.c $(N2N_LIB) n2n.h
$(CC) $(CFLAGS) example_edge_embed.c $(N2N_LIB) $(LIBS_EDGE) -o example_edge_embed
$(CC) $(CFLAGS) $< $(N2N_LIB) $(LIBS_EDGE) -o $@
.c.o: n2n.h n2n_transforms.h n2n_wire.h twofish.h Makefile
$(CC) $(CFLAGS) -c $<
$(CC) $(CFLAGS) -c $< -o $@
%.gz : %
gzip -c $< > $@
@ -91,7 +92,8 @@ $(N2N_LIB): $(N2N_OBJS)
# $(RANLIB) $@
clean:
rm -rf $(N2N_OBJS) $(N2N_LIB) $(APPS) $(DOCS) test *.dSYM *~
rm -rf $(N2N_OBJS) $(N2N_LIB) $(APPS) $(DOCS) test n2n-decode *.dSYM *~
$(MAKE) -C tools clean
install: edge supernode edge.8.gz supernode.1.gz n2n.7.gz
echo "MANDIR=$(MANDIR)"
@ -101,6 +103,7 @@ install: edge supernode edge.8.gz supernode.1.gz n2n.7.gz
$(INSTALL_DOC) edge.8.gz $(MAN8DIR)/
$(INSTALL_DOC) supernode.1.gz $(MAN1DIR)/
$(INSTALL_DOC) n2n.7.gz $(MAN7DIR)/
$(MAKE) -C tools install
# Docker builder section
DOCKER_IMAGE_NAME=ntop/supernode
@ -136,5 +139,4 @@ push:
echo "Please pass TARGET_ARCHITECTURE, see README.md."; \
fi
.PHONY: steps build push
# End Docker builder section

2
README.md

@ -133,7 +133,7 @@ two edge nodes, but it will now that edge A is talking with edge B.
Recently AES encryption support has been implemented, which increases both security and performance,
so it is recommended to enable it on all the edge nodes by specifying the `-A` option.
A benchmark of the encryption methods is available when compiled from source with `./benchmark`.
A benchmark of the encryption methods is available when compiled from source with `tools/n2n-benchmark`.
Contribution
------------

3
android/edge_android.c

@ -165,6 +165,9 @@ int start_edge(const n2n_edge_cmd_t* cmd)
traceLevel = traceLevel < 0 ? 0 : traceLevel; /* TRACE_ERROR */
traceLevel = traceLevel > 4 ? 4 : traceLevel; /* TRACE_DEBUG */
/* Random seed */
srand(time(NULL));
if (-1 == edge_init(&eee) )
{
traceEvent( TRACE_ERROR, "Failed in edge_init" );

15
configure.seed

@ -24,6 +24,19 @@ else
N2N_LIBS=-lcrypto
fi
AC_CHECK_LIB([pcap], [pcap_open_live], pcap=true)
if test x$pcap != x; then
AC_DEFINE([N2N_HAVE_PCAP], [], [Have PCAP library])
ADDITIONAL_TOOLS="$ADDITIONAL_TOOLS n2n-decode"
fi
AC_CHECK_LIB([pcap], [pcap_set_immediate_mode], pcap_immediate_mode=true)
if test x$pcap_immediate_mode != x; then
AC_DEFINE([HAVE_PCAP_IMMEDIATE_MODE], [], [Have pcap_immediate_mode])
fi
MACHINE=`uname -m`
SYSTEM=`uname -s`
@ -60,8 +73,10 @@ AC_SUBST(GIT_REVISION)
AC_SUBST(GIT_RELEASE)
AC_SUBST(N2N_DEFINES)
AC_SUBST(N2N_LIBS)
AC_SUBST(ADDITIONAL_TOOLS)
AC_CONFIG_HEADERS(config.h)
AC_CONFIG_FILES(Makefile)
AC_CONFIG_FILES(tools/Makefile)
AC_OUTPUT

1
doc/NEW_FEATURES.txt

@ -3,4 +3,3 @@ Between 2.0.x and 2.1.x
* Better ming Windows build support.
* Added -E flag to allow multicast ethernet traffic.

22
edge.8

@ -4,7 +4,7 @@ edge \- n2n edge node daemon
.SH SYNOPSIS
.B edge
[\-d <tun device>] \-a <tun IP address> \-c <community> {\-k <encrypt key>|\-K <keyfile>}
[\-s <netmask>] \-l <supernode host:port>
[\-s <netmask>] \-l <supernode host:port> [\-L <reg_ttl>]
[\-p <local port>] [\-u <UID>] [\-g <GID>] [-f] [\-m <MAC address>] [\-r] [\-v]
.SH DESCRIPTION
N2N is a peer-to-peer VPN system. Edge is the edge node daemon for n2n which
@ -111,6 +111,26 @@ are used in multicast ethernet and IPv6 neighbour discovery. If this option is
not present these multicast packets are discarded as most users do not need or
understand them.
.TP
\-L
set the TTL for the hole punching packet. This is an advanced flag to make
sure that the registration packet is dropped immediately when it goes out of
local nat so that it will not trigger some firewall behavior on target peer.
Actually, the registration packet is only expected to make local nat UDP hole
and is not expected to reach the target peer, see
https://tools.ietf.org/html/rfc5389. To achieve this, the flag should be set as
nat level + 1. For example, if we have 2 layer nat in local, we should set -L 3.
Usually we know exactly how much nat layers in local.
If we are not sure how much nat layers in local, we can use traceroute on
Linux to check. The following example shows a local single layer nat because on
second jump it shows a public ip address. In this case it should set -L 2.
$ /usr/sbin/traceroute -w1 8.8.8.8
traceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets
1 192.168.3.1 (192.168.3.1) 0.464 ms 0.587 ms 0.719 ms
2 112.65.17.217 (112.65.17.217) 5.269 ms 7.031 ms 8.666 ms
But this method does not always work due to various local network device policy.
.TP
\-v
more verbose logging (may be specified several times for more verbosity).
.SH ENVIRONMENT

22
edge.c

@ -140,10 +140,10 @@ static void help() {
"-l <supernode host:port>\n"
" "
"[-p <local port>] [-M <mtu>] "
#ifdef __linux__
#ifndef __APPLE__
"[-D] "
#endif
"[-r] [-E] [-v] [-i <reg_interval>] [-t <mgmt port>] [-A] [-h]\n\n");
"[-r] [-E] [-v] [-i <reg_interval>] [-L <reg_ttl>] [-t <mgmt port>] [-A] [-h]\n\n");
#if defined(N2N_CAN_NAME_IFACE)
printf("-d <tun device> | tun device name\n");
@ -155,6 +155,7 @@ static void help() {
printf("-s <netmask> | Edge interface netmask in dotted decimal notation (255.255.255.0).\n");
printf("-l <supernode host:port> | Supernode IP:port\n");
printf("-i <reg_interval> | Registration interval, for NAT hole punching (default 20 seconds)\n");
printf("-L <reg_ttl> | TTL for registration packet when UDP NAT hole punching through supernode (default 0 for not set )\n");
printf("-p <local port> | Fixed local UDP port.\n");
#ifndef WIN32
printf("-u <UID> | User ID (numeric) to use when privileges are dropped.\n");
@ -166,8 +167,8 @@ static void help() {
printf("-m <MAC address> | Fix MAC address for the TAP interface (otherwise it may be random)\n"
" | eg. -m 01:02:03:04:05:06\n");
printf("-M <mtu> | Specify n2n MTU of edge interface (default %d).\n", DEFAULT_MTU);
#ifdef __linux__
printf("-D | Enable PMTU discovery. PMTU discovery can reduce fragmentation but"
#ifndef __APPLE__
printf("-D | Enable PMTU discovery. PMTU discovery can reduce fragmentation but\n"
" | causes connections stall when not properly supported.\n");
#endif
printf("-r | Enable packet forwarding through n2n community.\n");
@ -257,7 +258,7 @@ static int setOption(int optkey, char *optargument, n2n_priv_config_t *ec, n2n_e
break;
}
#ifdef __linux__
#ifndef __APPLE__
case 'D' : /* enable PMTU discovery */
{
conf->disable_pmtu_discovery = 0;
@ -303,6 +304,10 @@ static int setOption(int optkey, char *optargument, n2n_priv_config_t *ec, n2n_e
conf->register_interval = atoi(optargument);
break;
case 'L': /* supernode registration interval */
conf->register_ttl = atoi(optarg);
break;
#if defined(N2N_CAN_NAME_IFACE)
case 'd': /* TUNTAP name */
{
@ -393,7 +398,7 @@ static int loadFromCLI(int argc, char *argv[], n2n_edge_conf_t *conf, n2n_priv_c
u_char c;
while((c = getopt_long(argc, argv,
"K:k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:S"
"k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:SDL:"
#ifdef N2N_HAVE_AES
"A"
#endif
@ -672,6 +677,9 @@ int main(int argc, char* argv[]) {
traceEvent(TRACE_NORMAL, "Starting n2n edge %s %s", PACKAGE_VERSION, PACKAGE_BUILDDATE);
/* Random seed */
srand(time(NULL));
if(0 == strcmp("dhcp", ec.ip_mode)) {
traceEvent(TRACE_NORMAL, "Dynamic IP address assignment enabled.");
@ -713,7 +721,7 @@ int main(int argc, char* argv[]) {
#ifndef WIN32
if((ec.userid != 0) || (ec.groupid != 0)) {
traceEvent(TRACE_NORMAL, "Interface up. Dropping privileges to uid=%d, gid=%d",
traceEvent(TRACE_NORMAL, "Dropping privileges to uid=%d, gid=%d",
(signed int)ec.userid, (signed int)ec.groupid);
/* Finished with the need for root privileges. Drop to unprivileged user. */

165
edge_utils.c

@ -144,6 +144,49 @@ static const char* transop_str(enum n2n_transform tr) {
/* ************************************** */
/** Destination 01:00:5E:00:00:00 - 01:00:5E:7F:FF:FF is multicast ethernet.
*/
static int is_ethMulticast(const void * buf, size_t bufsize) {
int retval = 0;
/* Match 01:00:5E:00:00:00 - 01:00:5E:7F:FF:FF */
if(bufsize >= sizeof(ether_hdr_t)) {
/* copy to aligned memory */
ether_hdr_t eh;
memcpy(&eh, buf, sizeof(ether_hdr_t));
if((0x01 == eh.dhost[0]) &&
(0x00 == eh.dhost[1]) &&
(0x5E == eh.dhost[2]) &&
(0 == (0x80 & eh.dhost[3])))
retval = 1; /* This is an ethernet multicast packet [RFC1112]. */
}
return retval;
}
/* ************************************** */
/** Destination MAC 33:33:0:00:00:00 - 33:33:FF:FF:FF:FF is reserved for IPv6
* neighbour discovery.
*/
static int is_ip6_discovery(const void * buf, size_t bufsize) {
int retval = 0;
if(bufsize >= sizeof(ether_hdr_t)) {
/* copy to aligned memory */
ether_hdr_t eh;
memcpy(&eh, buf, sizeof(ether_hdr_t));
if((0x33 == eh.dhost[0]) && (0x33 == eh.dhost[1]))
retval = 1; /* This is an IPv6 multicast packet [RFC2464]. */
}
return retval;
}
/* ************************************** */
/** Initialise an edge to defaults.
*
* This also initialises the NULL transform operation opstruct.
@ -171,10 +214,6 @@ n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *r
memcpy(&eee->device, dev, sizeof(*dev));
eee->start_time = time(NULL);
/* REVISIT: BbMaj7 : Should choose something with less predictability
* particularly for embedded targets with no real-time clock. */
srand(eee->start_time);
eee->known_peers = NULL;
eee->pending_peers = NULL;
eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS;
@ -388,9 +427,43 @@ static void register_with_new_peer(n2n_edge_t * eee,
HASH_COUNT(eee->pending_peers));
/* trace Sending REGISTER */
send_register(eee, &(scan->sock), mac);
if(from_supernode) {
/* UDP NAT hole punching through supernode. Send to peer first(punch local UDP hole)
* and then ask supernode to forward. Supernode then ask peer to ack. Some nat device
* drop and block ports with incoming UDP packet if out-come traffic does not exist.
* So we can alternatively set TTL so that the packet sent to peer never really reaches
* The register_ttl is basically nat level + 1. Set it to 1 means host like DMZ.
*/
if (eee->conf.register_ttl == 1) {
/* We are DMZ host or port is directly accessible. Just let peer to send back the ack */
#ifndef WIN32
} else if(eee->conf.register_ttl > 1) {
/* Setting register_ttl usually implies that the edge knows the internal net topology
* clearly, we can apply aggressive port prediction to support incoming Symmetric NAT
*/
int curTTL = 0;
socklen_t lenTTL = sizeof(int);
n2n_sock_t sock = scan->sock;
int alter = 16; /* TODO: set by command line or more reliable prediction method */
getsockopt(eee->udp_sock, IPPROTO_IP, IP_TTL, (void *)(char *)&curTTL, &lenTTL);
setsockopt(eee->udp_sock, IPPROTO_IP, IP_TTL,
(void *)(char *)&eee->conf.register_ttl,
sizeof(eee->conf.register_ttl));
for (; alter > 0; alter--, sock.port++)
{
send_register(eee, &sock, mac);
}
setsockopt(eee->udp_sock, IPPROTO_IP, IP_TTL, (void *)(char *)&curTTL, sizeof(curTTL));
#endif
} else { /* eee->conf.register_ttl <= 0 */
/* Normal STUN */
send_register(eee, &(scan->sock), mac);
}
send_register(eee, &(eee->supernode), mac);
} else {
/* P2P register, send directly */
send_register(eee, &(scan->sock), mac);
}
register_with_local_peers(eee);
@ -873,14 +946,20 @@ static int handle_PACKET(n2n_edge_t * eee,
rx_transop_id = (n2n_transform_t)pkt->transform;
if(rx_transop_id == eee->conf.transop_id) {
uint8_t is_multicast;
eth_payload = decodebuf;
eh = (ether_hdr_t*)eth_payload;
eth_size = eee->transop.rev(&eee->transop,
eth_payload, N2N_PKT_BUF_SIZE,
payload, psize, pkt->srcMac);
++(eee->transop.rx_cnt); /* stats */
is_multicast = (is_ip6_discovery(eth_payload, eth_size) || is_ethMulticast(eth_payload, eth_size));
if(!(eee->conf.allow_routing)) {
if(eee->conf.drop_multicast && is_multicast) {
traceEvent(TRACE_INFO, "Dropping RX multicast");
return(-1);
} else if((!eee->conf.allow_routing) && (!is_multicast)) {
/* Check if it is a routed packet */
if((ntohs(eh->type) == 0x0800) && (eth_size >= ETH_FRAMESIZE + IP4_MIN_SIZE)) {
uint32_t *dst = (uint32_t*)&eth_payload[ETH_FRAMESIZE + IP4_DSTOFFSET];
u_int8_t *dst_mac = (u_int8_t*)eth_payload;
@ -1058,49 +1137,6 @@ static void readFromMgmtSocket(n2n_edge_t * eee, int * keep_running) {
/* ************************************** */
/** Destination MAC 33:33:0:00:00:00 - 33:33:FF:FF:FF:FF is reserved for IPv6
* neighbour discovery.
*/
static int is_ip6_discovery(const void * buf, size_t bufsize) {
int retval = 0;
if(bufsize >= sizeof(ether_hdr_t)) {
/* copy to aligned memory */
ether_hdr_t eh;
memcpy(&eh, buf, sizeof(ether_hdr_t));
if((0x33 == eh.dhost[0]) && (0x33 == eh.dhost[1]))
retval = 1; /* This is an IPv6 multicast packet [RFC2464]. */
}
return retval;
}
/* ************************************** */
/** Destination 01:00:5E:00:00:00 - 01:00:5E:7F:FF:FF is multicast ethernet.
*/
static int is_ethMulticast(const void * buf, size_t bufsize) {
int retval = 0;
/* Match 01:00:5E:00:00:00 - 01:00:5E:7F:FF:FF */
if(bufsize >= sizeof(ether_hdr_t)) {
/* copy to aligned memory */
ether_hdr_t eh;
memcpy(&eh, buf, sizeof(ether_hdr_t));
if((0x01 == eh.dhost[0]) &&
(0x00 == eh.dhost[1]) &&
(0x5E == eh.dhost[2]) &&
(0 == (0x80 & eh.dhost[3])))
retval = 1; /* This is an ethernet multicast packet [RFC1112]. */
}
return retval;
}
/* ************************************** */
static int check_query_peer_info(n2n_edge_t *eee, time_t now, n2n_mac_t mac) {
struct peer_info *scan;
@ -1340,7 +1376,7 @@ static void readFromTAPSocket(n2n_edge_t * eee) {
)
)
{
traceEvent(TRACE_DEBUG, "Dropping multicast");
traceEvent(TRACE_INFO, "Dropping TX multicast");
}
else
{
@ -1465,6 +1501,16 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) {
if(is_valid_peer_sock(&pkt.sock))
orig_sender = &(pkt.sock);
if(!from_supernode) {
/* This is a P2P packet from the peer. We purge a pending
* registration towards the possibly nat-ted peer address as we now have
* a valid channel. We still use check_peer_registration_needed in
* handle_PACKET to double check this.
*/
traceEvent(TRACE_DEBUG, "Got P2P packet");
find_and_remove_peer(&eee->pending_peers, pkt.srcMac);
}
traceEvent(TRACE_INFO, "Rx PACKET from %s (sender=%s) [%u B]",
sock_to_cstr(sockbuf1, &sender),
sock_to_cstr(sockbuf2, orig_sender),
@ -1799,6 +1845,8 @@ void edge_term(n2n_edge_t * eee) {
/* ************************************** */
static int edge_init_sockets(n2n_edge_t *eee, int udp_local_port, int mgmt_port, uint8_t tos) {
int sockopt;
if(udp_local_port > 0)
traceEvent(TRACE_NORMAL, "Binding to local port %d", udp_local_port);
@ -1808,10 +1856,9 @@ static int edge_init_sockets(n2n_edge_t *eee, int udp_local_port, int mgmt_port,
return(-1);
}
#ifdef __linux__
if(tos) {
/* https://www.tucny.com/Home/dscp-tos */
int sockopt = tos;
sockopt = tos;
if(setsockopt(eee->udp_sock, IPPROTO_IP, IP_TOS, &sockopt, sizeof(sockopt)) == 0)
traceEvent(TRACE_NORMAL, "TOS set to 0x%x", tos);
@ -1819,14 +1866,14 @@ static int edge_init_sockets(n2n_edge_t *eee, int udp_local_port, int mgmt_port,
traceEvent(TRACE_ERROR, "Could not set TOS 0x%x[%d]: %s", tos, errno, strerror(errno));
}
if(eee->conf.disable_pmtu_discovery) {
int sockopt = 0;
#ifdef IP_PMTUDISC_DO
sockopt = (eee->conf.disable_pmtu_discovery) ? IP_PMTUDISC_DONT : IP_PMTUDISC_DO;
if(setsockopt(eee->udp_sock, IPPROTO_IP, IP_MTU_DISCOVER, &sockopt, sizeof(sockopt)) < 0)
traceEvent(TRACE_WARNING, "Could not disable PMTU discovery[%d]: %s", errno, strerror(errno));
else
traceEvent(TRACE_DEBUG, "PMTU discovery disabled");
}
if(setsockopt(eee->udp_sock, IPPROTO_IP, IP_MTU_DISCOVER, &sockopt, sizeof(sockopt)) < 0)
traceEvent(TRACE_WARNING, "Could not %s PMTU discovery[%d]: %s",
(eee->conf.disable_pmtu_discovery) ? "disable" : "enable", errno, strerror(errno));
else
traceEvent(TRACE_DEBUG, "PMTU discovery %s", (eee->conf.disable_pmtu_discovery) ? "disabled" : "enabled");
#endif
eee->udp_mgmt_sock = open_socket(mgmt_port, 0 /* bind LOOPBACK */);

3
example_edge_embed.c

@ -35,6 +35,9 @@ int main(int argc, char* argv[]) {
/* Increase tracelevel to see what's happening */
setTraceLevel(10);
/* Random seed */
srand(time(NULL));
/*
NOTE

16
n2n.c

@ -64,6 +64,7 @@ SOCKET open_socket(int local_port, int bind_any) {
static int traceLevel = 2 /* NORMAL */;
static int useSyslog = 0, syslog_opened = 0;
static FILE *traceFile = NULL;
int getTraceLevel() {
return(traceLevel);
@ -77,10 +78,17 @@ void setUseSyslog(int use_syslog) {
useSyslog= use_syslog;
}
void setTraceFile(FILE *f) {
traceFile = f;
}
#define N2N_TRACE_DATESIZE 32
void traceEvent(int eventTraceLevel, char* file, int line, char * format, ...) {
va_list va_ap;
if(traceFile == NULL)
traceFile = stdout;
if(eventTraceLevel <= traceLevel) {
char buf[1024];
char out_buf[1280];
@ -145,16 +153,16 @@ void traceEvent(int eventTraceLevel, char* file, int line, char * format, ...) {
}
__android_log_write(eventTraceLevel, "n2n", out_buf);
#else
printf("%s\n", out_buf);
fflush(stdout);
fprintf(traceFile, "%s\n", out_buf);
fflush(traceFile);
#endif /* #ifdef __ANDROID_NDK__ */
}
#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 [%s:%d] %s%s", theDate, &file[i], line, extra_msg, buf);
printf("%s\n", out_buf);
fflush(stdout);
fprintf(traceFile, "%s\n", out_buf);
fflush(traceFile);
#endif
}

4
n2n.h

@ -42,8 +42,10 @@
#undef N2N_HAVE_DAEMON
#undef N2N_HAVE_SETUID
#else
#ifndef CMAKE_BUILD
#include "config.h"
#endif
#endif
#define PACKAGE_BUILDDATE (__DATE__ " " __TIME__)
@ -215,6 +217,7 @@ typedef struct n2n_edge_conf {
uint8_t tos; /** TOS for sent packets */
char *encrypt_key;
int register_interval; /**< Interval for supernode registration, also used for UDP NAT hole punching. */
int register_ttl; /**< TTL for registration packet when UDP NAT hole punching through supernode. */
int local_port;
int mgmt_port;
} n2n_edge_conf_t;
@ -261,6 +264,7 @@ int n2n_transop_aes_cbc_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt);
/* Log */
void setTraceLevel(int level);
void setUseSyslog(int use_syslog);
void setTraceFile(FILE *f);
int getTraceLevel();
void traceEvent(int eventTraceLevel, char* file, int line, char * format, ...);

2
n2n_wire.h

@ -106,7 +106,9 @@ typedef struct n2n_auth
typedef struct n2n_common
{
/* NOTE: wire representation is different! */
/* int version; */
uint8_t ttl;
uint8_t pc;
uint16_t flags;

2
packages/rpm/Makefile.in

@ -10,7 +10,7 @@ all: clean pkg
pkg:
rpmbuild -bb ./n2n.spec
@./rpm-sign.exp $(HOME)/rpmbuild/RPMS/$(PLATFORM)/$(RPM_PKG)
@@RPM_SIGN_CMD@ $(HOME)/rpmbuild/RPMS/$(PLATFORM)/$(RPM_PKG)
@echo ""
@echo "Package contents:"
@rpm -qpl $(HOME)/rpmbuild/RPMS/$(PLATFORM)/$(RPM_PKG)

22
packages/rpm/configure

@ -583,10 +583,11 @@ PACKAGE_URL=''
ac_subst_vars='LTLIBOBJS
LIBOBJS
N2N_VERSION_SHORT
GIT_COMMITS
RPM_SIGN_CMD
DATE
EXTN
GIT_COMMITS
N2N_VERSION_SHORT
MACHINE
APP
target_alias
@ -1668,6 +1669,10 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
# NOTE: this file is not actually used. You need to edit configure as well!
N2N_VERSION_SHORT=`grep N2N_VERSION_SHORT ../../Makefile | head -1| cut -d "=" -f 2`
GIT_COMMITS=`grep GIT_COMMITS ../../Makefile | head -1| cut -d "=" -f 2`
MACHINE=`uname -m`
SHORT_MACHINE=`uname -m | cut -b1-3`
@ -1685,7 +1690,7 @@ else
if test $SHORT_MACHINE = "mip"; then
EXTN="mips"
EXTRA_DEPS=""
else
else
EXTN="i386"
fi
fi
@ -1693,10 +1698,17 @@ else
fi
APP=n2n
N2N_VERSION_SHORT=`grep N2N_VERSION_SHORT ../../Makefile | head -1| cut -d "=" -f 2`
GIT_COMMITS=`grep GIT_COMMITS ../../Makefile | head -1| cut -d "=" -f 2`
DATE=`date -R`
CENTOS_RELEASE=`cat /etc/centos-release | cut -d ' ' -f 3|cut -d '.' -f 1`
if test $CENTOS_RELEASE = "release"; then
CENTOS_RELEASE=`cat /etc/centos-release | cut -d ' ' -f 4|cut -d '.' -f 1`
fi
RPM_SIGN_CMD="rpm --addsign"
if test "$CENTOS_RELEASE" -ne 8; then
RPM_SIGN_CMD="./rpm-sign.exp"
fi

11
packages/rpm/configure.in

@ -31,12 +31,23 @@ fi
APP=n2n
DATE=`date -R`
CENTOS_RELEASE=`cat /etc/centos-release | cut -d ' ' -f 3|cut -d '.' -f 1`
if test $CENTOS_RELEASE = "release"; then
CENTOS_RELEASE=`cat /etc/centos-release | cut -d ' ' -f 4|cut -d '.' -f 1`
fi
RPM_SIGN_CMD="rpm --addsign"
if test "$CENTOS_RELEASE" -ne 8; then
RPM_SIGN_CMD="./rpm-sign.exp"
fi
AC_SUBST(APP)
AC_SUBST(MACHINE)
AC_SUBST(N2N_VERSION_SHORT)
AC_SUBST(GIT_COMMITS)
AC_SUBST(EXTN)
AC_SUBST(DATE)
AC_SUBST(RPM_SIGN_CMD)
AC_CONFIG_FILES(n2n.spec)
AC_CONFIG_FILES(../etc/systemd/system/edge.service)

45
tools/Makefile.in

@ -0,0 +1,45 @@
CC?=gcc
DEBUG?=-g3
#OPTIMIZATION?=-O2
WARN?=-Wall
INSTALL=install
INSTALL_PROG=$(INSTALL) -m755
MKDIR=mkdir -p
PREFIX?=$(DESTDIR)/usr
ifeq ($(OS),Darwin)
SBINDIR=$(PREFIX)/local/sbin
else
SBINDIR=$(PREFIX)/sbin
endif
LIBS_EDGE_OPT=@N2N_LIBS@
LIBS_EDGE+=$(LIBS_EDGE_OPT)
HEADERS=../n2n_wire.h ../n2n.h ../twofish.h ../n2n_transforms.h
CFLAGS+=-I..
LDFLAGS+=-L..
CFLAGS+=$(DEBUG) $(OPTIMIZATION) $(WARN)
N2N_LIB=../libn2n.a
TOOLS=n2n-benchmark
TOOLS+=@ADDITIONAL_TOOLS@
.PHONY: all clean install
all: $(TOOLS)
n2n-benchmark: benchmark.c $(N2N_LIB) $(HEADERS)
$(CC) $(CFLAGS) $< $(N2N_LIB) $(LIBS_EDGE) -o $@
n2n-decode: n2n_decode.c $(N2N_LIB) $(HEADERS)
$(CC) $(CFLAGS) $< $(N2N_LIB) $(LIBS_EDGE) -lpcap -o $@
.c.o: $(HEADERS) ../Makefile Makefile
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -rf $(TOOLS) $(N2N_LIB) *.o *.dSYM *~
install: $(TOOLS)
$(INSTALL_PROG) $(TOOLS) $(SBINDIR)/

0
benchmark.c → tools/benchmark.c

372
tools/n2n_decode.c

@ -0,0 +1,372 @@
/**
* (C) 2019 - ntop.org and contributors
*
* 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 see <http://www.gnu.org/licenses/>
*
*/
#include <pcap.h>
#include "n2n.h"
#define SNAPLEN 1500
#define TIMEOUT 200
/* *************************************************** */
static int aes_mode = 0;
static int running = 1;
static char *ifname = NULL;
static n2n_edge_conf_t conf;
static n2n_trans_op_t transop;
static pcap_t *handle;
static pcap_dumper_t *dumper;
/* *************************************************** */
static void help() {
fprintf(stderr, "n2n-decode -i ifname -k key -c community [-B bpf] [-w fname] [-v]"
#ifdef N2N_HAVE_AES
" [-A]"
#endif
"\n");
fprintf(stderr, "-i <ifname> | Specify the capture interface name.\n");
fprintf(stderr, "-c <community> | Specify the community.\n");
fprintf(stderr, "-k <key> | Specify the encryption key.\n");
#ifdef N2N_HAVE_AES
fprintf(stderr, "-A | Use AES CBC decryption (default=use twofish).\n");
#endif
fprintf(stderr, "-B <bpf> | Use set a BPF filter for the capture.\n");
fprintf(stderr, "-w <fname> | Write decoded PCAP to file.\n");
fprintf(stderr, "-v | Increase verbosity level.\n");
exit(0);
}
/* *************************************************** */
#ifdef WIN32
BOOL WINAPI term_handler(DWORD sig)
#else
static void term_handler(int sig)
#endif
{
static int called = 0;
if(called) {
traceEvent(TRACE_NORMAL, "Ok I am leaving now");
_exit(0);
} else {
traceEvent(TRACE_NORMAL, "Shutting down...");
called = 1;
}
running = 0;
#ifdef WIN32
return(TRUE);
#endif
}
/* *************************************************** */
static void write_packet(const u_char *packet, struct pcap_pkthdr *hdr) {
pcap_dump((unsigned char*)dumper, hdr, packet);
pcap_dump_flush(dumper);
}
/* *************************************************** */
static int decode_encrypted_packet(const u_char *packet, struct pcap_pkthdr *header,
n2n_PACKET_t *pkt, int encrypted_offset) {
uint8_t decoded_packet[encrypted_offset + N2N_PKT_BUF_SIZE];
int decoded_eth_size;
int transop_shift;
switch(pkt->transform) {
case N2N_TRANSFORM_ID_NULL:
/* Not encrypted, dump it */
write_packet(packet, header);
break;
case N2N_TRANSFORM_ID_TWOFISH:
if(aes_mode) {
traceEvent(TRACE_INFO, "Skipping twofish encrypted packet");
return(-1);
}
break;
case N2N_TRANSFORM_ID_AESCBC:
if(!aes_mode) {
traceEvent(TRACE_INFO, "Skipping AES encrypted packet");
return(-1);
}
break;
default:
traceEvent(TRACE_INFO, "Skipping unknown transform packet: %d", pkt->transform);
return(-2);
}
decoded_eth_size = transop.rev(&transop, decoded_packet+encrypted_offset, N2N_PKT_BUF_SIZE, packet + encrypted_offset,
header->caplen - encrypted_offset, pkt->srcMac);
transop_shift = (header->caplen - encrypted_offset) - decoded_eth_size;
if(transop_shift >= 0) {
int transform_id_offset = encrypted_offset - 2;
/* Copy the initial part of the packet */
memcpy(decoded_packet, packet, encrypted_offset);
/* Change the packet transform to NULL as there is now plaintext data */
*((u_int16_t*)(decoded_packet + transform_id_offset)) = htons(N2N_TRANSFORM_ID_NULL);
// TODO fix IP and UDP chechsums
write_packet(decoded_packet, header);
return(0);
}
traceEvent(TRACE_INFO, "Something was wrong in the decoding");
return(-3);
}
/* *************************************************** */
#define ETH_SIZE 14
#define UDP_SIZE 8
#define MIN_IP_SIZE 20
#define MIN_LEN (ETH_SIZE + UDP_SIZE + MIN_IP_SIZE + sizeof(n2n_common_t))
static int run_packet_loop() {
struct pcap_pkthdr header;
const u_char *packet;
traceEvent(TRACE_NORMAL, "Capturing packets on %s...", ifname);
while(running) {
n2n_common_t common;
n2n_PACKET_t pkt;
uint ipsize, common_offset;
size_t idx, rem;
memset(&common, 0, sizeof(common));
memset(&pkt, 0, sizeof(pkt));
packet = pcap_next(handle, &header);
if(!packet)
continue;
if(header.caplen < MIN_LEN) {
traceEvent(TRACE_INFO, "Skipping packet too small: size=%d", header.caplen);
continue;
}
if(ntohs(*(uint16_t*)(packet + 12)) != 0x0800) {
traceEvent(TRACE_INFO, "Skipping non IPv4 packet");
continue;
}
if(packet[ETH_SIZE + 9] != IPPROTO_UDP) {
traceEvent(TRACE_INFO, "Skipping non UDP packet");
continue;
}
ipsize = (packet[ETH_SIZE] & 0x0F) * 4;
common_offset = ETH_SIZE + ipsize + UDP_SIZE;
idx = common_offset;
rem = header.caplen - idx;
if(decode_common(&common, packet, &rem, &idx) == -1) {
traceEvent(TRACE_INFO, "Skipping packet, decode common failed");
continue;
}
if(strncmp((char*)conf.community_name, (char*)common.community, N2N_COMMUNITY_SIZE) != 0) {
traceEvent(TRACE_INFO, "Skipping packet with non-matching community");
continue;
}
switch(common.pc) {
case n2n_ping:
case n2n_register:
case n2n_deregister:
case n2n_register_ack:
case n2n_register_super:
case n2n_register_super_ack:
case n2n_register_super_nak:
case n2n_federation:
case n2n_peer_info:
case n2n_query_peer:
write_packet(packet, &header);
break;
case n2n_packet:
decode_PACKET(&pkt, &common, packet, &rem, &idx);
decode_encrypted_packet(packet, &header, &pkt, idx);
break;
default:
traceEvent(TRACE_INFO, "Skipping packet with unknown type: %d", common.pc);
continue;
}
}
return(0);
}
/* *************************************************** */
int main(int argc, char* argv[]) {
u_char c;
struct bpf_program fcode;
char *bpf_filter = NULL, *out_fname = NULL;
char errbuf[PCAP_ERRBUF_SIZE];
int rv = 0;
FILE *outf = stdout;
/* Trace to stderr to leave stdout for the PCAP dump if "-w -" is used */
setTraceFile(stderr);
/* Init configuration */
edge_init_conf_defaults(&conf);
while((c = getopt(argc, argv,
"k:i:B:w:c:v"
#ifdef N2N_HAVE_AES
"A"
#endif
)) != '?') {
if(c == 255) break;
switch(c) {
case 'c':
strncpy((char*)conf.community_name, optarg, sizeof(conf.community_name)-1);
break;
case 'i':
ifname = strdup(optarg);
break;
case 'k':
conf.encrypt_key = strdup(optarg);
break;
case 'B':
bpf_filter = strdup(optarg);
break;
#ifdef N2N_HAVE_AES
case 'A':
aes_mode = 1;
break;
#endif
case 'w':
if(strcmp(optarg, "-") != 0)
out_fname = strdup(optarg);
break;
case 'v': /* verbose */
setTraceLevel(getTraceLevel() + 1);
break;
default:
help();
}
}
if((ifname == NULL) || (conf.encrypt_key == NULL) || (conf.community_name[0] == '\0'))
help();
#ifdef N2N_HAVE_AES
if(aes_mode)
n2n_transop_aes_cbc_init(&conf, &transop);
else
#endif
n2n_transop_twofish_init(&conf, &transop);
if((handle = pcap_create(ifname, errbuf)) == NULL) {
traceEvent(TRACE_ERROR, "Cannot open device %s: %s", ifname, errbuf);
return(1);
}
if((pcap_set_timeout(handle, TIMEOUT) != 0) ||
(pcap_set_snaplen(handle, SNAPLEN) != 0)) {
traceEvent(TRACE_ERROR, "Error while setting timeout/snaplen");
return(1);
}
#ifdef HAVE_PCAP_IMMEDIATE_MODE
/* The timeout is not honored unless immediate mode is set.
* See https://github.com/mfontanini/libtins/issues/180 */
if(pcap_set_immediate_mode(handle, 1) != 0) {
traceEvent(TRACE_ERROR, "Could not set PCAP immediate mode");
return(1);
}
#endif
if(pcap_activate(handle) != 0) {
traceEvent(TRACE_ERROR, "pcap_activate failed: %s", pcap_geterr(handle));
}
if(pcap_datalink(handle) != DLT_EN10MB) {
traceEvent(TRACE_ERROR, "Device %s doesn't provide Ethernet headers - not supported", ifname);
return(2);
}
if(bpf_filter) {
bpf_u_int32 net, mask;
if(pcap_lookupnet(ifname, &net, &mask, errbuf) == -1) {
traceEvent(TRACE_WARNING, "Couldn't get netmask for device %s: %s", ifname, errbuf);
net = 0;
mask = 0;
}
if((pcap_compile(handle, &fcode, bpf_filter, 1, net) < 0)
|| (pcap_setfilter(handle, &fcode) < 0)) {
traceEvent(TRACE_ERROR, "Could not set BPF filter: %s", pcap_geterr(handle));
return(3);
}
}
if(out_fname) {
outf = fopen(out_fname, "wb");
if(outf == NULL) {
traceEvent(TRACE_ERROR, "Could not open %s for write[%d]: %s", errno, strerror(errno));
return(4);
}
}
dumper = pcap_dump_fopen(handle, outf);
if(dumper == NULL) {
traceEvent(TRACE_ERROR, "Could dump file: %s", pcap_geterr(handle));
return(5);
}
#ifdef __linux__
signal(SIGTERM, term_handler);
signal(SIGINT, term_handler);
#endif
#ifdef WIN32
SetConsoleCtrlHandler(term_handler, TRUE);
#endif
rv = run_packet_loop();
/* Cleanup */
pcap_close(handle);
if(conf.encrypt_key) free(conf.encrypt_key);
if(bpf_filter) free(bpf_filter);
if(ifname) free(ifname);
if(out_fname) {
fclose(outf);
free(out_fname);
}
return(rv);
}

240
tuntap_linux.c

@ -20,38 +20,76 @@
#ifdef __linux__
/* *************************************************** */
#include <net/if_arp.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
/* ********************************** */
static void read_mac(char *ifname, n2n_mac_t mac_addr) {
int _sock, res;
static int setup_ifname(int fd, const char *ifname, const char *ipaddr,
const char *netmask, uint8_t *mac, int mtu) {
struct ifreq ifr;
macstr_t mac_addr_buf;
memset (&ifr,0,sizeof(struct ifreq));
/* Dummy socket, just to make ioctls with */
_sock=socket(PF_INET, SOCK_DGRAM, 0);
strcpy(ifr.ifr_name, ifname);
res = ioctl(_sock,SIOCGIFHWADDR,&ifr);
if(res < 0) {
perror ("Get hw addr");
traceEvent(TRACE_ERROR, "Unable to read interfce %s MAC", ifname);
} else
memcpy(mac_addr, ifr.ifr_ifru.ifru_hwaddr.sa_data, 6);
traceEvent(TRACE_NORMAL, "Interface %s has MAC %s",
ifname,
macaddr_str(mac_addr_buf, mac_addr ));
close(_sock);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ-1] = '\0';
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
memcpy(ifr.ifr_hwaddr.sa_data, mac, 6);
if(ioctl(fd, SIOCSIFHWADDR, &ifr) == -1) {
traceEvent(TRACE_ERROR, "ioctl(SIOCSIFHWADDR) failed [%d]: %s", errno, strerror(errno));
return(-1);
}
ifr.ifr_addr.sa_family = AF_INET;
/* Interface Address */
inet_pton(AF_INET, ipaddr, &((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr);
if(ioctl(fd, SIOCSIFADDR, &ifr) == -1) {
traceEvent(TRACE_ERROR, "ioctl(SIOCSIFADDR) failed [%d]: %s", errno, strerror(errno));
return(-2);
}
/* Netmask */
if(netmask && (((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr.s_addr != 0)) {
inet_pton(AF_INET, netmask, &((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr);
if(ioctl(fd, SIOCSIFNETMASK, &ifr) == -1) {
traceEvent(TRACE_ERROR, "ioctl(SIOCSIFNETMASK, %s) failed [%d]: %s", netmask, errno, strerror(errno));
return(-3);
}
}
/* MTU */
ifr.ifr_mtu = mtu;
if(ioctl(fd, SIOCSIFMTU, &ifr) == -1) {
traceEvent(TRACE_ERROR, "ioctl(SIOCSIFMTU) failed [%d]: %s", errno, strerror(errno));
return(-4);
}
/* Set up and running */
if(ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
traceEvent(TRACE_ERROR, "ioctl(SIOCGIFFLAGS) failed [%d]: %s", errno, strerror(errno));
return(-5);
}
ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
if(ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
traceEvent(TRACE_ERROR, "ioctl(SIOCSIFFLAGS) failed [%d]: %s", errno, strerror(errno));
return(-6);
}
return(0);
}
/* ********************************** */
/** @brief Open and configure the TAP device for packet read/write.
*
* This routine creates the interface via the tuntap driver then uses ifconfig
* to configure address/mask and MTU.
* This routine creates the interface via the tuntap driver and then
* configures it.
*
* @param device - [inout] a device info holder object
* @param dev - user-defined name for the new iface,
@ -71,10 +109,15 @@ int tuntap_open(tuntap_dev *device,
const char * device_mac,
int mtu) {
char *tuntap_device = "/dev/net/tun";
#define N2N_LINUX_SYSTEMCMD_SIZE 128
char buf[N2N_LINUX_SYSTEMCMD_SIZE];
int ioctl_fd;
struct ifreq ifr;
int rc;
int nl_fd;
char nl_buf[8192]; /* >= 8192 to avoid truncation, see "man 7 netlink" */
struct iovec iov;
struct sockaddr_nl sa;
int up_and_running = 0;
struct msghdr msg;
device->fd = open(tuntap_device, O_RDWR);
if(device->fd < 0) {
@ -97,32 +140,95 @@ int tuntap_open(tuntap_dev *device,
/* Store the device name for later reuse */
strncpy(device->dev_name, ifr.ifr_name, MIN(IFNAMSIZ, N2N_IFNAMSIZ) );
if ( device_mac && device_mac[0] != '\0' )
{
/* Set the hw address before bringing the if up. */
snprintf(buf, sizeof(buf), "/sbin/ifconfig %s hw ether %s",
ifr.ifr_name, device_mac );
system(buf);
traceEvent(TRACE_INFO, "Setting MAC: %s", buf);
}
if(device_mac && device_mac[0]) {
/* Use the user-provided MAC */
str2mac(device->mac_addr, device_mac);
} else {
/* Set an explicit random MAC to know the exact MAC in use. Manually
* reading the MAC address is not safe as it may change internally
* also after the TAP interface UP status has been notified. */
int i;
if ( 0 == strncmp( "dhcp", address_mode, 5 ) )
{
snprintf(buf, sizeof(buf), "/sbin/ifconfig %s %s mtu %d up",
ifr.ifr_name, device_ip, mtu);
}
else
{
snprintf(buf, sizeof(buf), "/sbin/ifconfig %s %s netmask %s mtu %d up",
ifr.ifr_name, device_ip, device_mask, mtu);
for(i = 0; i < 6; i++)
device->mac_addr[i] = rand();
device->mac_addr[0] &= ~0x01; /* Clear multicast bit */
device->mac_addr[0] |= 0x02; /* Set locally-assigned bit */
}
/* Initialize Netlink socket */
if((nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
traceEvent(TRACE_ERROR, "netlink socket creation failed [%d]: %s", errno, strerror(errno));
return -1;
}
iov.iov_base = nl_buf;
iov.iov_len = sizeof(nl_buf);
memset(&sa, 0, sizeof(sa));
sa.nl_family = PF_NETLINK;
sa.nl_groups = RTMGRP_LINK;
sa.nl_pid = getpid();
msg.msg_name = &sa;
msg.msg_namelen = sizeof(sa);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* Subscribe to interface events */
if(bind(nl_fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
traceEvent(TRACE_ERROR, "netlink socket bind failed [%d]: %s", errno, strerror(errno));
return -1;
}
if((ioctl_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
traceEvent(TRACE_ERROR, "socket creation failed [%d]: %s", errno, strerror(errno));
close(nl_fd);
return -1;
}
if(setup_ifname(ioctl_fd, device->dev_name, device_ip, device_mask, device->mac_addr, mtu) < 0) {
close(nl_fd);
close(ioctl_fd);
close(device->fd);
return -1;
}
close(ioctl_fd);
/* Wait for the up and running notification */
traceEvent(TRACE_INFO, "Waiting for TAP interface to be up and running...");
while(!up_and_running) {
ssize_t len = recvmsg(nl_fd, &msg, 0);
struct nlmsghdr *nh;
for(nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
if(nh->nlmsg_type == NLMSG_ERROR) {
traceEvent(TRACE_DEBUG, "nh->nlmsg_type == NLMSG_ERROR");
break;
}
if(nh->nlmsg_type == NLMSG_DONE)
break;
if(nh->nlmsg_type == NETLINK_GENERIC) {
struct ifinfomsg *ifi = NLMSG_DATA(nh);
/* NOTE: skipping interface name check, assuming it's our TAP */
if((ifi->ifi_flags & IFF_UP) && (ifi->ifi_flags & IFF_RUNNING)) {
up_and_running = 1;
traceEvent(TRACE_INFO, "Interface is up and running");
break;
}
}
}
}
system(buf);
traceEvent(TRACE_INFO, "Bringing up: %s", buf);
close(nl_fd);
device->ip_addr = inet_addr(device_ip);
device->device_mask = inet_addr(device_mask);
read_mac(dev, device->mac_addr);
return(device->fd);
}
@ -149,34 +255,22 @@ void tuntap_close(struct tuntap_dev *tuntap) {
/* Fill out the ip_addr value from the interface. Called to pick up dynamic
* address changes. */
void tuntap_get_address(struct tuntap_dev *tuntap) {
FILE * fp=NULL;
ssize_t nread=0;
char buf[N2N_LINUX_SYSTEMCMD_SIZE];
/* Would rather have a more direct way to get the inet address but a netlink
* socket is overkill and probably less portable than ifconfig and sed. */
/* If the interface has no address (0.0.0.0) there will be no inet addr
* line and the returned string will be empty. */
snprintf( buf, sizeof(buf),
"/sbin/ifconfig %s | /bin/sed -e '/inet addr:/!d' -e 's/^.*inet addr://' -e 's/ .*$//'",
tuntap->dev_name);
fp = popen(buf, "r");
if (fp) {
memset(buf, 0, N2N_LINUX_SYSTEMCMD_SIZE); /* make sure buf is NULL terminated. */
nread = fread(buf, N2N_LINUX_SYSTEMCMD_SIZE-1, 1, fp);
fclose(fp);
fp = NULL;
traceEvent(TRACE_INFO, "ifconfig address = %s", buf);
if(nread > 0) {
buf[nread] = '\0';
tuntap->ip_addr = inet_addr(buf);
}
struct ifreq ifr;
int fd;
if((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
traceEvent(TRACE_ERROR, "socket creation failed [%d]: %s", errno, strerror(errno));
return;
}
}
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name, tuntap->dev_name, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ-1] = '\0';
if(ioctl(fd, SIOCGIFADDR, &ifr) != -1)
tuntap->ip_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
close(fd);
}
#endif /* #ifdef __linux__ */

2
tuntap_osx.c

@ -46,7 +46,7 @@ int tuntap_open(tuntap_dev *device /* ignored */,
}
if(device->fd < 0) {
traceEvent(TRACE_ERROR, "Unable to open tap device %s", tap_device);
traceEvent(TRACE_ERROR, "Unable to open any tap devices /dev/tap0 through /dev/tap254. Is this user properly authorized to access those descriptors?");
traceEvent(TRACE_ERROR, "Please read https://github.com/ntop/n2n/blob/dev/doc/n2n_on_MacOS.txt");
return(-1);
} else {

Loading…
Cancel
Save