/** * (C) 2007-18 - 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 "n2n.h" #define N2N_NETMASK_STR_SIZE 16 /* dotted decimal 12 numbers + 3 dots */ #define N2N_MACNAMSIZ 18 /* AA:BB:CC:DD:EE:FF + NULL*/ #define N2N_IF_MODE_SIZE 16 /* static | dhcp */ /* *************************************************** */ /** maximum length of command line arguments */ #define MAX_CMDLINE_BUFFER_LENGTH 4096 /** maximum length of a line in the configuration file */ #define MAX_CONFFILE_LINE_LENGTH 1024 /* ******************************************************* */ /** Main structure type for edge. */ /* ************************************** */ /* parse the configuration file */ static int readConfFile(const char * filename, char * const linebuffer) { struct stat stats; FILE * fd; char * buffer = NULL; buffer = (char *)malloc(MAX_CONFFILE_LINE_LENGTH); if(!buffer) { traceEvent(TRACE_ERROR, "Unable to allocate memory"); return -1; } if(stat(filename, &stats)) { if(errno == ENOENT) traceEvent(TRACE_ERROR, "parameter file %s not found/unable to access\n", filename); else traceEvent(TRACE_ERROR, "cannot stat file %s, errno=%d\n",filename, errno); free(buffer); return -1; } fd = fopen(filename, "rb"); if(!fd) { traceEvent(TRACE_ERROR, "Unable to open parameter file '%s' (%d)...\n",filename,errno); free(buffer); return -1; } while(fgets(buffer, MAX_CONFFILE_LINE_LENGTH,fd)) { char * p = NULL; /* strip out comments */ p = strchr(buffer, '#'); if(p) *p ='\0'; /* remove \n */ p = strchr(buffer, '\n'); if(p) *p ='\0'; /* strip out heading spaces */ p = buffer; while(*p == ' ' && *p != '\0') ++p; if(p != buffer) strncpy(buffer,p,strlen(p)+1); /* strip out trailing spaces */ while(strlen(buffer) && buffer[strlen(buffer)-1]==' ') buffer[strlen(buffer)-1]= '\0'; /* check for nested @file option */ if(strchr(buffer, '@')) { traceEvent(TRACE_ERROR, "@file in file nesting is not supported\n"); free(buffer); return -1; } if((strlen(linebuffer)+strlen(buffer)+2)< MAX_CMDLINE_BUFFER_LENGTH) { strncat(linebuffer, " ", 1); strncat(linebuffer, buffer, strlen(buffer)); } else { traceEvent(TRACE_ERROR, "too many argument"); free(buffer); return -1; } } free(buffer); fclose(fd); return 0; } /* ************************************** */ /* Create the argv vector */ static char ** buildargv(int * effectiveargc, char * const linebuffer) { const int INITIAL_MAXARGC = 16; /* Number of args + NULL in initial argv */ int maxargc; int argc=0; char ** argv; char * buffer, * buff; *effectiveargc = 0; buffer = (char *)calloc(1, strlen(linebuffer)+2); if(!buffer) { traceEvent(TRACE_ERROR, "Unable to allocate memory"); return NULL; } strncpy(buffer, linebuffer,strlen(linebuffer)); maxargc = INITIAL_MAXARGC; argv = (char **)malloc(maxargc * sizeof(char*)); if(argv == NULL) { traceEvent(TRACE_ERROR, "Unable to allocate memory"); return NULL; } buff = buffer; while(buff) { char * p = strchr(buff,' '); if(p) { *p='\0'; argv[argc++] = strdup(buff); while(*++p == ' ' && *p != '\0'); buff=p; if(argc >= maxargc) { maxargc *= 2; argv = (char **)realloc(argv, maxargc * sizeof(char*)); if(argv == NULL) { traceEvent(TRACE_ERROR, "Unable to re-allocate memory"); free(buffer); return NULL; } } } else { argv[argc++] = strdup(buff); break; } } free(buffer); *effectiveargc = argc; return argv; } /* ************************************** */ static void help() { print_n2n_version(); printf("edge " #if defined(N2N_CAN_NAME_IFACE) "-d " #endif /* #if defined(N2N_CAN_NAME_IFACE) */ "-a [static:|dhcp:] " "-c " "[-k | -K ]\n" " " "[-s ] " #ifndef WIN32 "[-u -g ]" #endif /* #ifndef WIN32 */ #ifndef WIN32 "[-f]" #endif /* #ifndef WIN32 */ "[-m ] " "-l \n" " " "[-p ] [-M ] " "[-r] [-E] [-v] [-t ] [-b] [-h]\n\n"); #ifdef __linux__ printf("-d | tun device name\n"); #endif printf("-a | Set interface address. For DHCP use '-r -a dhcp:0.0.0.0'\n"); printf("-c | n2n community name the edge belongs to.\n"); printf("-k | Encryption key (ASCII) - also N2N_KEY=. Not with -K.\n"); printf("-K | Specify a key schedule file to load. Not with -k.\n"); printf("-s | Edge interface netmask in dotted decimal notation (255.255.255.0).\n"); printf("-l | Supernode IP:port\n"); printf("-b | Periodically resolve supernode IP\n"); printf(" | (when supernodes are running on dynamic IPs)\n"); printf("-p | Fixed local UDP port.\n"); #ifndef WIN32 printf("-u | User ID (numeric) to use when privileges are dropped.\n"); printf("-g | Group ID (numeric) to use when privileges are dropped.\n"); #endif /* ifndef WIN32 */ #ifndef WIN32 printf("-f | Do not fork and run as a daemon; rather run in foreground.\n"); #endif /* #ifndef WIN32 */ printf("-m | Fix MAC address for the TAP interface (otherwise it may be random)\n" " | eg. -m 01:02:03:04:05:06\n"); printf("-M | Specify n2n MTU of edge interface (default %d).\n", DEFAULT_MTU); printf("-r | Enable packet forwarding through n2n community.\n"); printf("-E | Accept multicast MAC addresses (default=drop).\n"); printf("-v | Make more verbose. Repeat as required.\n"); printf("-t | Management UDP Port (for multiple edges on a machine).\n"); printf("\nEnvironment variables:\n"); printf(" N2N_KEY | Encryption key (ASCII). Not with -K or -k.\n"); exit(0); } /* ************************************** */ #if defined(DUMMY_ID_00001) /* Disabled waiting for config option to enable it */ static char gratuitous_arp[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* Dest mac */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Src mac */ 0x08, 0x06, /* ARP */ 0x00, 0x01, /* Ethernet */ 0x08, 0x00, /* IP */ 0x06, /* Hw Size */ 0x04, /* Protocol Size */ 0x00, 0x01, /* ARP Request */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Src mac */ 0x00, 0x00, 0x00, 0x00, /* Src IP */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Target mac */ 0x00, 0x00, 0x00, 0x00 /* Target IP */ }; /* ************************************** */ /** Build a gratuitous ARP packet for a /24 layer 3 (IP) network. */ static int build_gratuitous_arp(char *buffer, uint16_t buffer_len) { if(buffer_len < sizeof(gratuitous_arp)) return(-1); memcpy(buffer, gratuitous_arp, sizeof(gratuitous_arp)); memcpy(&buffer[6], device.mac_addr, 6); memcpy(&buffer[22], device.mac_addr, 6); memcpy(&buffer[28], &device.ip_addr, 4); /* REVISIT: BbMaj7 - use a real netmask here. This is valid only by accident * for /24 IPv4 networks. */ buffer[31] = 0xFF; /* Use a faked broadcast address */ memcpy(&buffer[38], &device.ip_addr, 4); return(sizeof(gratuitous_arp)); } /* ************************************** */ /** Called from update_supernode_reg to periodically send gratuitous ARP * broadcasts. */ static void send_grat_arps(n2n_edge_t * eee,) { char buffer[48]; size_t len; traceEvent(TRACE_NORMAL, "Sending gratuitous ARP..."); len = build_gratuitous_arp(buffer, sizeof(buffer)); send_packet2net(eee, buffer, len); send_packet2net(eee, buffer, len); /* Two is better than one :-) */ } #endif /* #if defined(DUMMY_ID_00001) */ /* *********************************************** */ static const struct option long_options[] = { { "community", required_argument, NULL, 'c' }, { "supernode-list", required_argument, NULL, 'l' }, { "tun-device", required_argument, NULL, 'd' }, { "euid", required_argument, NULL, 'u' }, { "egid", required_argument, NULL, 'g' }, { "help" , no_argument, NULL, 'h' }, { "verbose", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } }; /* ***************************************************** */ /** Find the address and IP mode for the tuntap device. * * s is one of these forms: * * := | A.B.C.D * * | static: | dhcp: * * If the mode is present (colon required) then fill ip_mode with that value * otherwise do not change ip_mode. Fill ip_mode with everything after the * colon if it is present; or s if colon is not present. * * ip_add and ip_mode are NULL terminated if modified. * * return 0 on success and -1 on error */ static int scan_address(char * ip_addr, size_t addr_size, char * ip_mode, size_t mode_size, const char * s) { int retval = -1; char * p; if((NULL == s) || (NULL == ip_addr)) { return -1; } memset(ip_addr, 0, addr_size); p = strpbrk(s, ":"); if(p) { /* colon is present */ if(ip_mode) { size_t end=0; memset(ip_mode, 0, mode_size); end = MIN(p-s, (ssize_t)(mode_size-1)); /* ensure NULL term */ strncpy(ip_mode, s, end); strncpy(ip_addr, p+1, addr_size-1); /* ensure NULL term */ retval = 0; } } else { /* colon is not present */ strncpy(ip_addr, s, addr_size); } return retval; } /* ************************************** */ static void daemonize() { #ifndef WIN32 int childpid; traceEvent(TRACE_NORMAL, "Parent process is exiting (this is normal)"); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGQUIT, SIG_IGN); if((childpid = fork()) < 0) traceEvent(TRACE_ERROR, "Occurred while daemonizing (errno=%d)", errno); else { if(!childpid) { /* child */ int rc; //traceEvent(TRACE_NORMAL, "Bye bye: I'm becoming a daemon..."); rc = chdir("/"); if(rc != 0) traceEvent(TRACE_ERROR, "Error while moving to / directory"); setsid(); /* detach from the terminal */ fclose(stdin); fclose(stdout); /* fclose(stderr); */ /* * clear any inherited file mode creation mask */ //umask(0); /* * Use line buffered stdout */ /* setlinebuf (stdout); */ setvbuf(stdout, (char *)NULL, _IOLBF, 0); } else /* father */ exit(0); } #endif } /* *************************************************** */ /** Entry point to program from kernel. */ int main(int argc, char* argv[]) { int opt; int keep_on_running = 1; int local_port = 0 /* any port */; int mgmt_port = N2N_EDGE_MGMT_PORT; /* 5644 by default */ char tuntap_dev_name[N2N_IFNAMSIZ] = "edge0"; char ip_mode[N2N_IF_MODE_SIZE] = "static"; char ip_addr[N2N_NETMASK_STR_SIZE] = ""; char netmask[N2N_NETMASK_STR_SIZE] = "255.255.255.0"; int mtu = DEFAULT_MTU; int got_s = 0; #ifndef WIN32 uid_t userid = 0; /* root is the only guaranteed ID */ gid_t groupid = 0; /* root is the only guaranteed ID */ #endif char device_mac[N2N_MACNAMSIZ] = ""; char * encrypt_key = NULL; int i, effectiveargc = 0; char ** effectiveargv = NULL; char * linebuffer = NULL; n2n_edge_t eee; /* single instance for this program */ if(-1 == edge_init(&eee)) { traceEvent(TRACE_ERROR, "Failed in edge_init"); exit(1); } if(getenv("N2N_KEY")) { encrypt_key = strdup(getenv("N2N_KEY")); } #ifdef WIN32 tuntap_dev_name[0] = '\0'; #endif memset(&(eee.supernode), 0, sizeof(eee.supernode)); eee.supernode.family = AF_INET; linebuffer = (char *)malloc(MAX_CMDLINE_BUFFER_LENGTH); if(!linebuffer) { traceEvent(TRACE_ERROR, "Unable to allocate memory"); exit(1); } snprintf(linebuffer, MAX_CMDLINE_BUFFER_LENGTH, "%s",argv[0]); #ifdef WIN32 for(i=0; i < (int)strlen(linebuffer); i++) if(linebuffer[i] == '\\') linebuffer[i] = '/'; #endif for(i=1;i 0) { fprintf(stderr, "Error: -K and -k options are mutually exclusive.\n"); exit(1); } else { traceEvent(TRACE_DEBUG, "encrypt_key = '%s'\n", encrypt_key); encrypt_key = strdup(optarg); } break; } case 'r': /* enable packet routing across n2n endpoints */ { eee.allow_routing = 1; break; } case 'l': /* supernode-list */ { if(eee.sn_num < N2N_EDGE_NUM_SUPERNODES) { strncpy((eee.sn_ip_array[eee.sn_num]), optarg, N2N_EDGE_SN_HOST_SIZE); traceEvent(TRACE_DEBUG, "Adding supernode[%u] = %s\n", (unsigned int)eee.sn_num, (eee.sn_ip_array[eee.sn_num])); ++eee.sn_num; } else { fprintf(stderr, "Too many supernodes!\n"); exit(1); } break; } #if defined(N2N_CAN_NAME_IFACE) case 'd': /* TUNTAP name */ { strncpy(tuntap_dev_name, optarg, N2N_IFNAMSIZ); break; } #endif case 'b': { eee.re_resolve_supernode_ip = 1; break; } case 'p': { local_port = atoi(optarg); break; } case 't': { mgmt_port = atoi(optarg); break; } case 's': /* Subnet Mask */ { if(0 != got_s) { traceEvent(TRACE_WARNING, "Multiple subnet masks supplied."); } strncpy(netmask, optarg, N2N_NETMASK_STR_SIZE); got_s = 1; break; } case 'h': /* help */ { help(); break; } case 'v': /* verbose */ { ++traceLevel; /* do 2 -v flags to increase verbosity to DEBUG level*/ break; } } /* end switch */ } #ifndef WIN32 if(eee.daemon) { useSyslog = 1; /* traceEvent output now goes to syslog. */ daemonize(); } #endif /* #ifndef WIN32 */ traceEvent(TRACE_NORMAL, "Starting n2n edge %s %s", n2n_sw_version, n2n_sw_buildDate); for (i=0; i< N2N_EDGE_NUM_SUPERNODES; ++i) traceEvent(TRACE_NORMAL, "supernode %u => %s\n", i, (eee.sn_ip_array[i])); supernode2addr(&(eee.supernode), eee.sn_ip_array[eee.sn_idx]); for (i=0; i 0) traceEvent(TRACE_NORMAL, "Binding to local port %d", (signed int)local_port); if(encrypt_key) { if(edge_init_twofish(&eee, (uint8_t *)(encrypt_key), strlen(encrypt_key)) < 0) { fprintf(stderr, "Error: twofish setup failed.\n"); return(-1); } } else if(strlen(eee.keyschedule) > 0) { if(edge_init_keyschedule(&eee) != 0) { fprintf(stderr, "Error: keyschedule setup failed.\n"); return(-1); } } /* else run in NULL mode */ eee.udp_sock = open_socket(local_port, 1 /* bind ANY */); if(eee.udp_sock < 0) { traceEvent(TRACE_ERROR, "Failed to bind main UDP port %u", (signed int)local_port); return(-1); } eee.udp_mgmt_sock = open_socket(mgmt_port, 0 /* bind LOOPBACK */); if(eee.udp_mgmt_sock < 0) { traceEvent(TRACE_ERROR, "Failed to bind management UDP port %u", mgmt_port); return(-1); } traceEvent(TRACE_NORMAL, "edge started"); update_supernode_reg(&eee, time(NULL)); return run_edge_loop(&eee, &keep_on_running); } /* ************************************** */