diff --git a/NEWS b/NEWS index 4cdde35..e64dd61 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ +WrapSix 0.2.1 +============= +- custom configuration files and some autodetection + WrapSix 0.2.0 ============= - - completely rewritten from scratch, but without DNS64 for now - dropped all external dependencies - cleaner design & implementation with emphasis on CPU & memory efficiency diff --git a/src/config.c b/src/config.c index fb69b2f..87b716b 100644 --- a/src/config.c +++ b/src/config.c @@ -16,11 +16,16 @@ * along with this program. If not, see . */ -#include -#include -#include +#include /* inet_pton */ +#include /* struct ifaddrs, getifaddrs, freeifaddrs */ +#include /* getnameinfo, NI_NUMERICHOST */ +#include /* FILE, fopen, getc, feof, fclose, perror */ +#include /* exit */ +#include /* strcmp, strncpy */ #include "config.h" +#include "ipv4.h" +#include "ipv6.h" #include "log.h" #include "wrapper.h" @@ -36,9 +41,10 @@ } #define C_DEFAULT_MTU 1280 -#define C_DEFAULT_INTERFACE "eth0" #define C_DEFAULT_PREFIX "64:ff9b::" +void cfg_guess_interface(char *cinterface); + /** * Configuration file parser. * @@ -48,6 +54,12 @@ * locally to init other things) * @param init 0 or 1, whether or not to initialize * configuration with defaults + * + * @return 0 for success + * @return 1 for failure in case of some syntax error when reloading + * configuration + * @return exit with code 1 in case of some syntax error when initializing + * configuration */ int cfg_parse(const char *config_file, unsigned short *cmtu, struct s_cfg_opts *oto, unsigned char init) @@ -61,15 +73,14 @@ int cfg_parse(const char *config_file, unsigned short *cmtu, /* set defaults */ *cmtu = C_DEFAULT_MTU; - /* TODO: get automatically the first available interface */ - strncpy(oto->interface, C_DEFAULT_INTERFACE, sizeof(oto->interface)); + oto->interface[0] = '\0'; + cfg_guess_interface(oto->interface); strncpy(oto->prefix, C_DEFAULT_PREFIX, sizeof(oto->prefix)); oto->ipv4_address[0] = '\0'; f = fopen(config_file, "r"); if (f == NULL) { - /* set defaults */ log_warn("Configuration file %s doesn't exist, using defaults", config_file); @@ -181,3 +192,136 @@ int cfg_parse(const char *config_file, unsigned short *cmtu, return 0; } + +/** + * Configures host IP addresses for usage in ICMP error messages. If some or + * both is not available, defaults are used. + * + * As a default in case of IPv4 is used IPv4 address assigned to WrapSix in + * configuration, in case of IPv6 is used made up address from default NAT64 + * prefix -- WrapSix itself cannot act as a host so it doesn't matter. + * + * @param cinterface Name of the interface WrapSix sits on + * @param ipv6_addr Where to save host IPv6 address + * @param ipv4_addr Where to save host IPv4 address + * @param default_ipv4_addr IPv4 address assigned to WrapSix + * + * @return 0 for success + * @return 1 for failure + */ +int cfg_host_ips(char *cinterface, struct s_ipv6_addr *ipv6_addr, + struct s_ipv4_addr *ipv4_addr, char *default_ipv4_addr) +{ + struct ifaddrs *ifaddr, *ifa; + /* 0x01 IPv6 from WrapSix' interface + * 0x02 IPv4 from WrapSix' interface + * 0x04 IPv6 from another interface + * 0x08 IPv4 from another interface + */ + char found = 0; + char ip_text[40]; + + if (getifaddrs(&ifaddr) == -1) { + perror("getifaddrs"); + return 1; + } + + /* Walk through linked list, maintaining head pointer so we can free + * list later */ + + /* first try to get addresses from the interface */ + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) { + continue; + } + + if (!strcmp(ifa->ifa_name, cinterface)) { + if (ifa->ifa_addr->sa_family == AF_INET6 && + !(found & 0x01)) { + found |= 0x01; + getnameinfo(ifa->ifa_addr, + sizeof(struct sockaddr_in6), + ip_text, sizeof(ip_text), NULL, 0, + NI_NUMERICHOST); + inet_pton(AF_INET6, ip_text, ipv6_addr); + } else if (ifa->ifa_addr->sa_family == AF_INET && + !(found & 0x02)) { + found |= 0x02; + getnameinfo(ifa->ifa_addr, + sizeof(struct sockaddr_in), + ip_text, sizeof(ip_text), NULL, 0, + NI_NUMERICHOST); + inet_pton(AF_INET, ip_text, ipv4_addr); + } + } else { /* look for addresses on other interfaces too */ + if (ifa->ifa_addr->sa_family == AF_INET6 && + !(found & 0x05)) { + found |= 0x04; + getnameinfo(ifa->ifa_addr, + sizeof(struct sockaddr_in6), + ip_text, sizeof(ip_text), NULL, 0, + NI_NUMERICHOST); + inet_pton(AF_INET6, ip_text, ipv6_addr); + } else if (ifa->ifa_addr->sa_family == AF_INET && + !(found & 0x0a)) { + found |= 0x08; + getnameinfo(ifa->ifa_addr, + sizeof(struct sockaddr_in), + ip_text, sizeof(ip_text), NULL, 0, + NI_NUMERICHOST); + inet_pton(AF_INET, ip_text, ipv4_addr); + } + } + + if ((found & 0x03) == 0x03) { + break; + } + } + + /* no IPv4 address -> use default */ + if (!(found & 0x0a)) { + inet_pton(AF_INET, default_ipv4_addr, ipv4_addr); + } + + /* IPv6 default? huh... but we can't work without host IPv6 address */ + if (!(found & 0x05)) { + /* FUN: try to decode it ;c) */ + inet_pton(AF_INET6, "64:ff9b::E7ad:514", ipv6_addr); + } + + freeifaddrs(ifaddr); + + return 0; +} + +/** + * Gets name of first non-loopback network interface. + * + * @param cinterface Where to save the interface name + */ +void cfg_guess_interface(char *cinterface) +{ + struct ifaddrs *ifaddr, *ifa; + + if (getifaddrs(&ifaddr) == -1) { + perror("getifaddrs"); + return; + } + + /* Walk through linked list, maintaining head pointer so we can free + * list later */ + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) { + continue; + } + + /* skip loopback */ + if (strcmp(ifa->ifa_name, "lo")) { + strncpy(cinterface, ifa->ifa_name, sizeof(cinterface)); + break; + } + } + + freeifaddrs(ifaddr); +} diff --git a/src/config.h b/src/config.h index 0e2b56c..54a2fde 100644 --- a/src/config.h +++ b/src/config.h @@ -19,6 +19,9 @@ #ifndef CONFIG_H #define CONFIG_H +#include "ipv4.h" +#include "ipv6.h" + struct s_cfg_opts { char interface[128]; char prefix[128]; @@ -27,5 +30,7 @@ struct s_cfg_opts { int cfg_parse(const char *config_file, unsigned short *cmtu, struct s_cfg_opts *oto, unsigned char init); +int cfg_host_ips(char *cinterface, struct s_ipv6_addr *ipv6_addr, + struct s_ipv4_addr *ipv4_addr, char *default_ipv4_addr); #endif /* CONFIG_H */ diff --git a/src/wrapper.c b/src/wrapper.c index 7cf6458..377b01f 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -43,11 +43,6 @@ #include "transmitter.h" #include "wrapper.h" -/* +++ CONFIGURATION +++ */ -#define HOST_IPV6_ADDR "fd77::1:0:1" -#define HOST_IPV4_ADDR "192.168.0.19" -/* --- CONFIGURATION --- */ - unsigned short mtu; struct ifreq interface; @@ -88,6 +83,22 @@ int main(int argc, char **argv) log_info(" MTU %d", mtu); log_info(" IPv4 address %s", cfg.ipv4_address); + /* get host IP addresses */ + if (cfg_host_ips(cfg.interface, &host_ipv6_addr, &host_ipv4_addr, + cfg.ipv4_address)) { + log_error("Unable to get host IP addresses"); + return 1; + } + /* using block because of the temporary variable */ + { + char ip_text[40]; + + inet_ntop(AF_INET, &host_ipv4_addr, ip_text, sizeof(ip_text)); + log_info(" host IPv4 address %s", ip_text); + inet_ntop(AF_INET6, &host_ipv6_addr, ip_text, sizeof(ip_text)); + log_info(" host IPv6 address %s", ip_text); + } + /* initialize the socket for sniffing */ if ((sniff_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) { @@ -149,12 +160,6 @@ int main(int argc, char **argv) /* compute binary IPv4 address of WrapSix */ inet_pton(AF_INET, cfg.ipv4_address, &wrapsix_ipv4_addr); - /* compute binary IPv6 address of WrapSix host */ - inet_pton(AF_INET6, HOST_IPV6_ADDR, &host_ipv6_addr); - - /* compute binary IPv4 address of WrapSix host */ - inet_pton(AF_INET, HOST_IPV4_ADDR, &host_ipv4_addr); - /* initiate sending socket */ if (transmission_init()) { log_error("Unable to initiate sending socket"); diff --git a/wrapsix.conf b/wrapsix.conf index cdbf68f..ea39146 100644 --- a/wrapsix.conf +++ b/wrapsix.conf @@ -9,9 +9,9 @@ # Don't enter information about prefix length (/96). #prefix 64:ff9b:: -# WrapSix automatically detects first interface and uses it. Use this option to -# run WrapSix on different interace. -interface eth0 +# WrapSix automatically detects first (non-loopback) interface and uses it. Use +# this option to run WrapSix on different interace. +#interface eth0 # Unassigned IPv4 address for sole usage by WrapSix. Even the OS mustn't have it # assigned.