From c9104217968bebf95dd50e17e5596b3671efd608 Mon Sep 17 00:00:00 2001 From: "Tristan B. Velloza Kildaire" Date: Sun, 22 Dec 2024 15:27:16 +0200 Subject: [PATCH] Add IPv6 support (command-line and tuntap) (#16) * tncattach - Added `set_ipv6`, `ipv6_addr` and `netmask_v6` globals - Added `--ipv6` option - If `n` is specified (i.e. `--noipv6`) then bail out if the user specified IPv6 addressing with `--ipv6 ` - Added `6` parsing, this is for when we have an `--ipv6` and want to set the address * tncattach - Removed global `netmask_v6` which is never used * TAP - Added initial code that I have been working on * TAP - Removed old (bad) code `trySixSet2(...)` * TAP - Clean up * TAP - Clean up * TAP - Added device type checl * TAP - Randomize the remaining octets of the link-local address * TAP - Seed random number generatro based off of current time * TAP - Removed TODO * TAP_ - Cleaned up a little bit - The `mtu` (on Luinux) must be `1280` of greater, else IPv6 won't work (and the address will not be allowed to be added) * TAP - More clean up * TAP - Moved mtu check for Ipv6 to be earlier * TAP - Added missing `cleanup()` calls * TAP - Aded newline * TAP - Now link-local and normal v6 can be requested independently tncattach - Added `--ll` mode to add link-local * TAP - More cleanup * TAP - Removed code to generate a link-local address * tncattach - Added error handling for mtu with IPv6 support * TAP - Removed uneeded imports * TAP - Cleaned up * Cleaned up * TAP - Removed duplicate import * TAP - Removed error checking code from there * TAP - Cleaned up - Remove dneed for `link_local_v6` tncattach - Removed `link_local_v6` * TAP - Close control socket when done * tncattach - Removed debug print * TAP - Cleaned up * TAP - Cleaned up * TAP - Cleaned up * TAP - Added check for bad open * TAP - Added another check * tncattach - Now parse IPv6 address and prefix in opt-args TAP - Removed parsing from here * Fixed mtu stuff * tncattach - Typo fix * Work * tncattach - WHen parsing the `prefixPart_s` (the prefix length), only continue if the text is a number, if not then bail out with an error * tncattach - Only allow prefix length of between 0 to 128 * TAP - Cleaned up * TAP - Cleaned up * tncattach - Cleaned up --- TAP.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++- TAP.h | 1 + tncattach.c | 103 +++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 202 insertions(+), 12 deletions(-) diff --git a/TAP.c b/TAP.c index 5b0477f..289fd76 100644 --- a/TAP.c +++ b/TAP.c @@ -5,15 +5,72 @@ char tap_name[IFNAMSIZ]; extern bool verbose; extern bool noipv6; extern bool set_ipv4; +extern bool set_ipv6; +extern bool set_linklocal; extern bool set_netmask; extern bool noup; extern int mtu; extern int device_type; extern char if_name[IFNAMSIZ]; extern char* ipv4_addr; +extern char* ipv6_addr; +extern long ipv6_prefixLen; extern char* netmask; extern void cleanup(); + +void trySixSet +( + int interfaceIndex, + struct in6_addr address, + int prefixLen +) +{ + char ip_str[INET6_ADDRSTRLEN+1]; + inet_ntop(AF_INET6, &address, ip_str, INET6_ADDRSTRLEN+1); + + printf + ( + "Adding IPv6 address of '%s/%d' to interface at if_index %d\n", + ip_str, + prefixLen, + interfaceIndex + ); + + int inet6 = socket(AF_INET6, SOCK_DGRAM, 0); + if(inet6 < 0) + { + printf("Error opening control socket for adding IPv6 address to interface\n"); + cleanup(); + exit(1); + } + + struct in6_ifreq paramReq; + memset(¶mReq, 0, sizeof(struct in6_ifreq)); + paramReq.ifr6_ifindex = interfaceIndex; + paramReq.ifr6_prefixlen = prefixLen; + paramReq.ifr6_addr = address; + + + // Try add the address + if(ioctl(inet6, SIOCSIFADDR, ¶mReq) < 0) + { + printf + ( + "There was an errror assigning address '%s/%d' to if_index %d\n", + ip_str, + prefixLen, + interfaceIndex + ); + cleanup(); + close(inet6); + exit(1); + } + + printf("Address '%s/%d' added\n", ip_str, prefixLen); + close(inet6); +} + int open_tap(void) { struct ifreq ifr; int fd = open("/dev/net/tun", O_RDWR); @@ -43,10 +100,11 @@ int open_tap(void) { exit(1); } else { strcpy(if_name, ifr.ifr_name); + int inet = socket(AF_INET, SOCK_DGRAM, 0); if (inet == -1) { - perror("Could not open AF_INET socket"); + perror("Could not open control socket"); cleanup(); exit(1); } else { @@ -182,6 +240,54 @@ int open_tap(void) { } } } + + if(set_ipv6 || set_linklocal) + { + // Firstly, obtain the interface index by `ifr_name` + int inet6 = socket(AF_INET6, SOCK_DGRAM, 0); + if(inet6 < 0) + { + printf("Error opening control socket for adding IPv6 address to interface\n"); + cleanup(); + exit(1); + } + + if(ioctl(inet6, SIOCGIFINDEX, &ifr) < 0) + { + printf("Could not get interface index for interface '%s'\n", ifr.ifr_name); + close(inet6); + cleanup(); + exit(1); + } + + // if link-local was NOT requested and interface + // has been up'd -> then kernel would have added + // a link-local already, this removes it + if(!set_linklocal & !noup) + { + // TODO: Get all addresses that start with fe80 + } + // Else it could have been no-up; hence you will have to remove + // the link-local yourself + // Other else is link-local was requested, then we don't care (whether + // up'd or not as it will inevitably be added by the kernel) + + // Convert ASCII IPv6 address to ABI structure + struct in6_addr six_addr_itself; + memset(&six_addr_itself, 0, sizeof(struct in6_addr)); + if(inet_pton(AF_INET6, ipv6_addr, &six_addr_itself) < 0) + { + printf("Error parsing IPv6 address '%s'\n", ipv6_addr); + close(inet6); + cleanup(); + exit(1); + } + + // Add user's requested address + trySixSet(ifr.ifr_ifindex, six_addr_itself, ipv6_prefixLen); + + close(inet6); + } } } } @@ -195,4 +301,4 @@ int open_tap(void) { int close_tap(int tap_fd) { return close(tap_fd); -} \ No newline at end of file +} diff --git a/TAP.h b/TAP.h index eeb6a14..07f003a 100644 --- a/TAP.h +++ b/TAP.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "Constants.h" int open_tap(void); diff --git a/tncattach.c b/tncattach.c index f372daf..7d825b9 100644 --- a/tncattach.c +++ b/tncattach.c @@ -34,11 +34,16 @@ bool noipv6 = false; bool noup = false; bool daemonize = false; bool set_ipv4 = false; +bool set_ipv6 = false; +bool set_linklocal = false; bool set_netmask = false; bool kiss_over_tcp = false; char* ipv4_addr; char* netmask; +char* ipv6_addr; +long ipv6_prefixLen; + char* tcp_host; int tcp_port; @@ -269,15 +274,17 @@ static struct argp_option options[] = { { "mtu", 'm', "MTU", 0, "Specify interface MTU", 1}, { "ethernet", 'e', 0, 0, "Create a full ethernet device", 2}, { "ipv4", 'i', "IP_ADDRESS", 0, "Configure an IPv4 address on interface", 3}, - { "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC", 4}, - { "noup", 1, 0, 0, "Only create interface, don't bring it up", 5}, - { "kisstcp", 'T', 0, 0, "Use KISS over TCP instead of serial port", 6}, - { "tcphost", 'H', "TCP_HOST", 0, "Host to connect to when using KISS over TCP", 7}, - { "tcpport", 'P', "TCP_PORT", 0, "TCP port when using KISS over TCP", 8}, - { "interval", 't', "SECONDS", 0, "Maximum interval between station identifications", 9}, - { "id", 's', "CALLSIGN", 0, "Station identification data", 10}, - { "daemon", 'd', 0, 0, "Run tncattach as a daemon", 11}, - { "verbose", 'v', 0, 0, "Enable verbose output", 12}, + { "ipv6", '6', "IP6_ADDRESS", 0, "Configure an IPv6 address on interface", 4}, + { "ll", 'l', 0, 0, "Add a link-local Ipv6 address", 5}, + { "noipv6", 'n', 0, 0, "Filter IPv6 traffic from reaching TNC", 6}, + { "noup", 1, 0, 0, "Only create interface, don't bring it up", 7}, + { "kisstcp", 'T', 0, 0, "Use KISS over TCP instead of serial port", 8}, + { "tcphost", 'H', "TCP_HOST", 0, "Host to connect to when using KISS over TCP", 9}, + { "tcpport", 'P', "TCP_PORT", 0, "TCP port when using KISS over TCP", 10}, + { "interval", 't', "SECONDS", 0, "Maximum interval between station identifications", 11}, + { "id", 's', "CALLSIGN", 0, "Station identification data", 12}, + { "daemon", 'd', 0, 0, "Run tncattach as a daemon", 13}, + { "verbose", 'v', 0, 0, "Enable verbose output", 14}, { 0 } }; @@ -285,6 +292,7 @@ static struct argp_option options[] = { struct arguments { char *args[N_ARGS]; char *ipv4; + char *ipv6; char *id; bool valid_id; int id_interval; @@ -296,6 +304,9 @@ struct arguments { bool verbose; bool set_ipv4; bool set_netmask; + bool set_ipv6; + bool link_local_v6; + bool set_netmask_v6; bool noipv6; bool noup; bool kiss_over_tcp; @@ -321,6 +332,13 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { printf("Error: Invalid MTU specified\r\n\r\n"); argp_usage(state); } + + if((arguments->set_ipv6 || arguments->link_local_v6) && arguments->mtu < 1280) + { + printf("IPv6 and/or link-local IPv6 was requested, but the MTU provided is lower than 1280\n"); + exit(EXIT_FAILURE); + } + break; case 't': @@ -466,9 +484,70 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { } break; + case '6': + if(arguments->noipv6) + { + perror("Sorry, but you had noipv6 set yet want to use ipv6?\n"); + exit(EXIT_FAILURE); + } + + char* ipPart_s = strtok(arg, "/"); + char* prefixPart_s = strtok(NULL, "/"); + printf("ipPart_s: %s\n", ipPart_s); + + if(!prefixPart_s) + { + printf("No prefix length was provided\n"); + exit(1); + } + printf("prefixPart_s: %s\n", prefixPart_s); + + long prefixLen_l = strtol(prefixPart_s, NULL, 10); // TODO: Add handling here for errors (using errno) + + if(prefixLen_l == 0) { + printf("Prefix length '%s' is not numeric\n", prefixPart_s); + exit(EXIT_FAILURE); + } + else if(!(prefixLen_l >= 0 && prefixLen_l <= 128)) + { + printf("Prefix length '%s' is not within valid range of 0-128\n", prefixPart_s); + exit(EXIT_FAILURE); + } + + arguments->ipv6 = ipPart_s; + + arguments->set_ipv6 = true; + + // Copy across global IPv6 address + ipv6_addr = malloc(strlen(arguments->ipv6)+1); + strcpy(ipv6_addr, arguments->ipv6); + + // Set global IPv6 prefix length + ipv6_prefixLen = prefixLen_l; + + printf("MTU was %d, setting to minimum of %d as is required for IPv6\n", arguments->mtu, 1280); + arguments->mtu = 1280; + break; + + case 'l': + if(arguments->noipv6) + { + perror("Sorry, but you had noipv6 set yet want to use ipv6 link-local?\n"); + exit(EXIT_FAILURE); + } + arguments->link_local_v6 = true; + + printf("MTU was %d, setting to minimum of %d as is required for IPv6\n", arguments->mtu, 1280); + arguments->mtu = 1280; + break; case 'n': arguments->noipv6 = true; + if(arguments->set_ipv6) + { + printf("Requested no IPv6 yet you have set the IPv6 to '%s'\n", arguments->ipv6); + exit(1); + } break; case 'd': @@ -559,6 +638,9 @@ int main(int argc, char **argv) { arguments.verbose = false; arguments.set_ipv4 = false; arguments.set_netmask = false; + arguments.set_ipv6 = false; + arguments.link_local_v6 = false; + arguments.set_netmask_v6 = false; arguments.noipv6 = false; arguments.daemon = false; arguments.noup = false; @@ -586,6 +668,7 @@ int main(int argc, char **argv) { if (arguments.noipv6) noipv6 = true; if (arguments.set_ipv4) set_ipv4 = true; if (arguments.set_netmask) set_netmask = true; + if (arguments.set_ipv6) set_ipv6 = true; if (arguments.noup) noup = true; mtu = arguments.mtu; @@ -632,4 +715,4 @@ int main(int argc, char **argv) { read_loop(); return 0; -} \ No newline at end of file +}