diff --git a/.gitignore b/.gitignore
index 507459c..53002e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ Makefile
autom4te.cache
benchmark
edge
+n2n-decode
example_edge_embed
supernode
build
diff --git a/Makefile.in b/Makefile.in
index 030cc33..4f4a79b 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -63,10 +63,11 @@ endif
APPS=edge
APPS+=supernode
APPS+=example_edge_embed
+APPS+=benchmark
DOCS=edge.8.gz supernode.1.gz n2n.7.gz
-all: $(APPS) $(DOCS) benchmark
+all: $(APPS) $(DOCS)
edge: edge.c $(N2N_LIB) n2n_wire.h n2n.h Makefile
$(CC) $(CFLAGS) edge.c $(N2N_LIB) $(LIBS_EDGE) -o edge
@@ -77,6 +78,9 @@ supernode: sn.c $(N2N_LIB) n2n.h Makefile
benchmark: benchmark.c $(N2N_LIB) n2n_wire.h n2n.h Makefile
$(CC) $(CFLAGS) benchmark.c $(N2N_LIB) $(LIBS_EDGE) -o benchmark
+n2n-decode: n2n_decode.c $(N2N_LIB) n2n_wire.h n2n.h Makefile
+ $(CC) $(CFLAGS) n2n_decode.c $(N2N_LIB) $(LIBS_EDGE) -lpcap -o n2n-decode
+
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
@@ -91,7 +95,7 @@ $(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 *~
install: edge supernode edge.8.gz supernode.1.gz n2n.7.gz
echo "MANDIR=$(MANDIR)"
diff --git a/edge.c b/edge.c
index d65e4bf..7fed318 100644
--- a/edge.c
+++ b/edge.c
@@ -393,7 +393,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:S"
#ifdef N2N_HAVE_AES
"A"
#endif
diff --git a/n2n.c b/n2n.c
index 3559ea4..b3479ce 100644
--- a/n2n.c
+++ b/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
}
diff --git a/n2n.h b/n2n.h
index 388e741..79151ff 100644
--- a/n2n.h
+++ b/n2n.h
@@ -261,6 +261,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, ...);
diff --git a/n2n_decode.c b/n2n_decode.c
new file mode 100644
index 0000000..bfe1156
--- /dev/null
+++ b/n2n_decode.c
@@ -0,0 +1,348 @@
+/**
+ * (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
+ *
+ */
+
+#include
+#include "n2n.h"
+
+#define SNAPLEN 1500
+
+/* *************************************************** */
+
+static int aes_mode = 0;
+static int running = 1;
+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 | Specify the capture interface name.\n");
+ fprintf(stderr, "-c | Specify the community.\n");
+ fprintf(stderr, "-k | Specify the encryption key.\n");
+#ifdef N2N_HAVE_AES
+ fprintf(stderr, "-A | Use AES CBC decryption (default=use twofish).\n");
+#endif
+ fprintf(stderr, "-B | Use set a BPF filter for the capture.\n");
+ fprintf(stderr, "-w | 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, "Running loop");
+
+ // TODO handle timeout
+ while(running) {
+ n2n_common_t common = {0};
+ n2n_PACKET_t pkt = {0};
+ uint ipsize, common_offset;
+ size_t idx, rem;
+
+ packet = pcap_next(handle, &header);
+
+ 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, *ifname = 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));
+ 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);
+
+ handle = pcap_open_live(ifname, SNAPLEN, 1, 1000, errbuf);
+
+ if(handle == NULL) {
+ traceEvent(TRACE_ERROR, "Cannot open device %s: %s", ifname, errbuf);
+ return(1);
+ }
+
+ 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);
+}
diff --git a/n2n_wire.h b/n2n_wire.h
index daa9359..15aff9e 100644
--- a/n2n_wire.h
+++ b/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;