From 42c6d24bcf44a6515436208a135fbc7da1bfa8dd Mon Sep 17 00:00:00 2001 From: Florian Bach Date: Sun, 1 Jan 2023 18:08:38 +0100 Subject: [PATCH 1/3] Allow a CIDR mask (/24) instead of subnetmask in config --- parser.c | 34 +++++++++++++++++++++++++--------- tnat64.conf.complex.example | 4 ++-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/parser.c b/parser.c index 7397930..03ee4d0 100644 --- a/parser.c +++ b/parser.c @@ -385,7 +385,7 @@ int HIDDENSYM make_netent(char *value, struct netent **ent) exit(1); } - show_msg(MSGDEBUG, "New network entry for %s\n", ip); + show_msg(MSGDEBUG, "New network entry for %s going to 0x%08x\n", ip, *ent); if (!startport) (*ent)->startport = 0; @@ -403,18 +403,34 @@ int HIDDENSYM make_netent(char *value, struct netent **ent) free(*ent); return (2); } + + // Check if there's a dot in the subnet string. + if (strstr(subnet, ".") != NULL) { + // There's a dot, so it's a netmask like 255.255.255.0 #ifdef HAVE_INET_ADDR - else if (((*ent)->localnet.s_addr = inet_addr(subnet)) == -1) - { + if (((*ent)->localnet.s_addr = inet_addr(subnet)) == -1) + { #elif defined(HAVE_INET_ATON) - else if (!(inet_aton(subnet, &((*ent)->localnet)))) - { + if (!(inet_aton(subnet, &((*ent)->localnet)))) + { #endif - /* Badly constructed subnet */ - free(*ent); - return (3); + /* Badly constructed subnet */ + free(*ent); + return (3); + } } - else if (((*ent)->localip.s_addr & (*ent)->localnet.s_addr) != (*ent)->localip.s_addr) + else { + // No dot, probably a mask like /24 + int mask_size = atoi(subnet); + if (mask_size < 0 || mask_size > 32) { + /* Bad mask */ + free (*ent); + return (3); + } + (*ent)->localnet.s_addr = htonl((0xFFFFFFFFULL << (32-mask_size)) & 0xFFFFFFFFULL); + } + + if (((*ent)->localip.s_addr & (*ent)->localnet.s_addr) != (*ent)->localip.s_addr) { /* Subnet and Ip != Ip */ free(*ent); diff --git a/tnat64.conf.complex.example b/tnat64.conf.complex.example index 24fcea1..29849f5 100644 --- a/tnat64.conf.complex.example +++ b/tnat64.conf.complex.example @@ -12,10 +12,10 @@ # the man pages, tnat64(8) and tnat64.conf(8) # Local networks -# For this example this machine can directly access 192.168.0.0/255.255.255.0 +# For this example this machine can directly access 192.168.0.0/24 # (192.168.0.*) and 10.0.0.0/255.0.0.0 (10.*) -local = 192.168.0.0/255.255.255.0 +local = 192.168.0.0/24 local = 10.0.0.0/255.0.0.0 # Paths From 098d040246dd092033d7557fab517b88854d2385 Mon Sep 17 00:00:00 2001 From: Florian Bach Date: Sun, 1 Jan 2023 18:08:38 +0100 Subject: [PATCH 2/3] Support for NAT64 prefixes other than /96 (RFC6052) --- parser.c | 77 ++++++++++++++++++----- parser.h | 6 +- tnat64.c | 119 +++++++++++++++++++++++++++++++++++- tnat64.conf.5 | 13 +++- tnat64.conf.complex.example | 13 ++-- tnat64.conf.simple.example | 2 +- validateconf.c | 39 +++++++++++- 7 files changed, 235 insertions(+), 34 deletions(-) diff --git a/parser.c b/parser.c index 03ee4d0..fc1d7d5 100644 --- a/parser.c +++ b/parser.c @@ -26,6 +26,7 @@ static int handle_endpath(struct parsedfile *, int, int, char *[]); static int handle_subnet(struct parsedfile *, int, char *); static int handle_local(struct parsedfile *, int, char *); static int handle_prefix(struct parsedfile *, int, char *); +static int handle_suffix(struct parsedfile *, int, char *); static int make_netent(char *value, struct netent **ent); int HIDDENSYM read_config(char *filename, struct parsedfile *config) @@ -126,6 +127,10 @@ static int handle_line(struct parsedfile *config, char *line, int lineno) { handle_prefix(config, lineno, words[2]); } + else if (!strcmp(words[0], "nat64_suffix")) + { + handle_suffix(config, lineno, words[2]); + } else if (!strcmp(words[0], "local")) { handle_local(config, lineno, words[2]); @@ -190,7 +195,7 @@ static int handle_path(struct parsedfile *config, int lineno, int nowords, char exit(-1); /* Initialize the structure */ - show_msg(MSGDEBUG, "New prefix structure from line %d in configuration file going " "to 0x%08x\n", lineno, newprefix); + show_msg(MSGDEBUG, "New prefix structure from line %d in configuration file\n", lineno); memset(newprefix, 0x0, sizeof(*newprefix)); newprefix->next = config->paths; newprefix->lineno = lineno; @@ -213,9 +218,9 @@ static int handle_endpath(struct parsedfile *config, int lineno, int nowords, ch currentcontext = &(config->defaultprefix); } - /* We could perform some checking on the validty of data in */ - /* the completed path here, but thats what verifyconf is */ - /* designed to do, no point in weighing down libtsocks */ + /* We could perform some checking on the validity of data */ + /* in the completed path here, but thats what verifyconf is */ + /* designed to do, no point in weighing down libtnat64. */ return (0); } @@ -241,8 +246,7 @@ static int handle_subnet(struct parsedfile *config, int lineno, char *value) return (0); break; case 4: - show_msg(MSGERR, "IP (%s) & ", inet_ntoa(ent->localip)); - show_msg(MSGERR, "SUBNET (%s) != IP on line %d in " "configuration file, ignored\n", inet_ntoa(ent->localnet), lineno); + show_msg(MSGERR, "IP (%s) & SUBNET (%s) != IP on line %d in " "configuration file, ignored\n", inet_ntoa(ent->localip), inet_ntoa(ent->localnet), lineno); return (0); break; case 5: @@ -270,7 +274,7 @@ static int handle_prefix(struct parsedfile *config, int lineno, char *value) { char *ip; - ip = strsplit(NULL, &value, " "); + ip = strsplit(NULL, &value, " /"); if (currentcontext->address == NULL) { @@ -279,6 +283,33 @@ static int handle_prefix(struct parsedfile *config, int lineno, char *value) { show_msg(MSGERR, "Cannot parse NAT64 prefix " "specified at line %d in " "configuration file\n", lineno); } + else { + // Check for prefix size: + currentcontext->prefix_size = 96; + + char *prefix_size_str = strsplit(NULL, &value, " /"); + if (prefix_size_str) { + int prefix_size_temp = atoi(prefix_size_str); + switch (prefix_size_temp) { + // RFC 6052 says that only these sizes are allowed. + case 32: + case 40: + case 48: + case 56: + case 64: + case 96: + case 128: + // also allow for the case /128, in which ALL traffic (to this network) + // will be sent to this single IPv6 address: + currentcontext->prefix_size = prefix_size_temp; + break; + default: + show_msg(MSGERR, "NAT64 prefix specified at " "line %d in configuration file " "has invalid prefix size %d, assuming /96\n", lineno, prefix_size_temp); + break; + } + } + } + } else { @@ -291,6 +322,20 @@ static int handle_prefix(struct parsedfile *config, int lineno, char *value) return (0); } +static int handle_suffix(struct parsedfile *config, int lineno, char *value) +{ + char *ip; + + ip = strsplit(NULL, &value, " "); + + if (!inet_pton(AF_INET6, ip, ¤tcontext->suffix)) + { + show_msg(MSGERR, "Cannot parse NAT64 suffix " "specified at line %d in " "configuration file\n", lineno); + } + + return (0); +} + static int handle_local(struct parsedfile *config, int lineno, char *value) { int rc; @@ -318,8 +363,7 @@ static int handle_local(struct parsedfile *config, int lineno, char *value) return (0); break; case 4: - show_msg(MSGERR, "IP (%s) & ", inet_ntoa(ent->localip)); - show_msg(MSGERR, "SUBNET (%s) != IP on line %d in " "configuration file, ignored\n", inet_ntoa(ent->localnet), lineno); + show_msg(MSGERR, "IP (%s) & SUBNET (%s) != IP on line %d in " "configuration file, ignored\n", inet_ntoa(ent->localip), inet_ntoa(ent->localnet), lineno); return (0); case 5: case 6: @@ -385,7 +429,7 @@ int HIDDENSYM make_netent(char *value, struct netent **ent) exit(1); } - show_msg(MSGDEBUG, "New network entry for %s going to 0x%08x\n", ip, *ent); + show_msg(MSGDEBUG, "New network entry for %s\n", ip); if (!startport) (*ent)->startport = 0; @@ -488,7 +532,7 @@ int HIDDENSYM pick_prefix(struct parsedfile *config, struct prefixent **ent, str { /* Go through all the prefixes looking for one */ /* with a path to this network */ - show_msg(MSGDEBUG, "Checking NAT64 prefix %s\n", ((*ent)->address ? (*ent)->address : "(No Address)")); + show_msg(MSGDEBUG, "Checking NAT64 prefix %s/%d\n", ((*ent)->address ? (*ent)->address : "(No Address)"), (*ent)->prefix_size); net = (*ent)->reachnets; while (net != NULL) { @@ -510,7 +554,7 @@ int HIDDENSYM pick_prefix(struct parsedfile *config, struct prefixent **ent, str return (0); } -int HIDDENSYM check_prefix(struct parsedfile *config, struct in6_addr * addr) +struct prefixent * check_prefix(struct parsedfile *config, struct in6_addr * addr) { struct prefixent *ent; char addrbuffer[INET6_ADDRSTRLEN]; @@ -523,13 +567,14 @@ int HIDDENSYM check_prefix(struct parsedfile *config, struct in6_addr * addr) while (ent != NULL) { /* Go through all the prefixes */ - show_msg(MSGDEBUG, "Checking NAT64 prefix %s\n", (ent->address ? ent->address : "(No Address)")); + show_msg(MSGDEBUG, "Checking NAT64 prefix %s/%d\n", (ent->address ? ent->address : "(No Address)"), ent->prefix_size); if ((ent->address)) { - if (!memcmp(addr, &(ent->prefix), NAT64PREFIXLEN)) + // prefix_size is in bits, divide by 8 to get bytes. + if (!memcmp(addr, &(ent->prefix), (ent->prefix_size / 8))) { show_msg(MSGDEBUG, "Match!\n"); - return 1; + return ent; } } ent = ent->next; @@ -540,7 +585,7 @@ int HIDDENSYM check_prefix(struct parsedfile *config, struct in6_addr * addr) if (!memcmp(addr, &(ent->prefix), NAT64PREFIXLEN)) { show_msg(MSGDEBUG, "Match!\n"); - return 1; + return ent; } return 0; diff --git a/parser.h b/parser.h index c558bea..d239543 100644 --- a/parser.h +++ b/parser.h @@ -13,7 +13,9 @@ struct prefixent int lineno; /* Line number in conf file this path started on */ char *address; /* IPv6 address prefix in textual form */ struct in6_addr prefix; /* the same, but in binary form */ - struct netent *reachnets; /* Linked list of nets from this preifx */ + struct in6_addr suffix; /* suffix to be appended to the address */ + int prefix_size; /* IPv6 prefix size (usually 96) */ + struct netent *reachnets; /* Linked list of nets from this prefix */ struct prefixent *next; /* Pointer to next prefix entry */ }; @@ -39,7 +41,7 @@ struct parsedfile int read_config(char *, struct parsedfile *); int is_local(struct parsedfile *, struct in_addr *); int pick_prefix(struct parsedfile *, struct prefixent **, struct in_addr *, uint16_t port); -int check_prefix(struct parsedfile *config, struct in6_addr * addr); +struct prefixent * check_prefix(struct parsedfile *config, struct in6_addr * addr); char *strsplit(char *separator, char **text, const char *search); #endif diff --git a/tnat64.c b/tnat64.c index fe29a80..116d660 100644 --- a/tnat64.c +++ b/tnat64.c @@ -196,6 +196,102 @@ int socket(SOCKET_SIGNATURE) } } +struct in6_addr put_ipv4_into_prefix(struct prefixent *prefix, struct in_addr ipv4_address) { + struct in6_addr retval; + + // Copy the prefix + memcpy(&retval, &(prefix->prefix), 16); + + // Determine the suffix size from the prefix size (RFC6052, 2.2) + int suffix_size = 0; + switch (prefix->prefix_size) { + case 32: + suffix_size = 7; break; + case 40: + suffix_size = 6; break; + case 48: + suffix_size = 5; break; + case 56: + suffix_size = 4; break; + case 64: + suffix_size = 3; break; + case 96: + case 128: + default: + suffix_size = 0; break; + } + + // Copy over the suffix: + for (int i = 0; i < suffix_size; i++) { + retval.s6_addr[15-i] = (prefix->suffix).s6_addr[15-i]; + } + + // Now add the IPv4 address at the correct location - overwriting the suffix if necessary. + + switch (prefix->prefix_size) { + case 32: + memcpy(retval.s6_addr+4, &ipv4_address.s_addr, 4); + break; + case 40: + memcpy(retval.s6_addr+5, &ipv4_address.s_addr, 3); + memcpy(retval.s6_addr+9, (char *)&ipv4_address.s_addr + 3, 1); + break; + case 48: + memcpy(retval.s6_addr+6, &ipv4_address.s_addr, 2); + memcpy(retval.s6_addr+9, (char *)&ipv4_address.s_addr + 2, 2); + break; + case 56: + memcpy(retval.s6_addr+7, &ipv4_address.s_addr, 1); + memcpy(retval.s6_addr+9, (char *)&ipv4_address.s_addr + 1, 3); + break; + case 64: + memcpy(retval.s6_addr+9, &ipv4_address.s_addr, 4); + break; + case 96: + memcpy(retval.s6_addr+12, &ipv4_address.s_addr, 4); + break; + case 128: + // No replacement necessary, route all IPv4 packets to this IPv6 address. + break; + } + + return retval; + +} + +struct in_addr get_ipv4_from_prefix(struct prefixent *prefix, struct in6_addr ipv6_address) { + struct in_addr retval = {}; + + switch (prefix->prefix_size) { + case 32: + memcpy(&retval.s_addr, ipv6_address.s6_addr + 4, 4); + return retval; + case 40: + memcpy( &retval.s_addr, ipv6_address.s6_addr + 5, 3); + memcpy((char *)&retval.s_addr + 3, ipv6_address.s6_addr + 9, 1); + return retval; + case 48: + memcpy( &retval.s_addr, ipv6_address.s6_addr + 6, 2); + memcpy((char *)&retval.s_addr + 2, ipv6_address.s6_addr + 9, 2); + return retval; + case 56: + memcpy( &retval.s_addr, ipv6_address.s6_addr + 7, 1); + memcpy((char *)&retval.s_addr + 1, ipv6_address.s6_addr + 9, 3); + return retval; + case 64: + memcpy(&retval.s_addr, ipv6_address.s6_addr + 9, 4); + return retval; + case 96: + memcpy(&retval.s_addr, ipv6_address.s6_addr + 12, 4); + return retval; + case 128: + default: + // IP unknown, just set 0.0.0.0 + memset(&retval.s_addr, 0, 4); + return retval; + } +} + int connect(CONNECT_SIGNATURE) { struct sockaddr_in *connaddr; @@ -307,8 +403,10 @@ int connect(CONNECT_SIGNATURE) dest_address6.sin6_port = connaddr->sin_port; dest_address6.sin6_flowinfo = 0; dest_address6.sin6_scope_id = 0; - memcpy(&dest_address6.sin6_addr, &path->prefix, sizeof(struct in6_addr)); - memcpy(&dest_address6.sin6_addr.s6_addr[12], &connaddr->sin_addr, sizeof(struct in_addr)); + + struct in6_addr dest = put_ipv4_into_prefix(path, connaddr->sin_addr); + memcpy(&dest_address6.sin6_addr, &dest, sizeof(struct in6_addr)); + if (inet_ntop(AF_INET6, &dest_address6.sin6_addr, addrbuffer, sizeof(addrbuffer))) { show_msg(MSGDEBUG, "Trying IPv6 address %s...\n", addrbuffer); @@ -399,7 +497,22 @@ int getpeername(GETPEERNAME_SIGNATURE) result = (struct sockaddr_in *)__addr; result->sin_family = AF_INET; result->sin_port = realpeer.sin6_port; - memcpy(&result->sin_addr, &realpeer.sin6_addr.s6_addr[12], sizeof(struct in_addr)); + + // Now that we're supporting different prefix lengths, we can't just take the lower 32 bits anymore. + // Call check_prefix, have that return the *actual* prefix, so we know the prefix length to extract the IPv4. + + struct prefixent * pfx = check_prefix(config, &realpeer.sin6_addr); + + // Extract the IPv4 from that prefix. + if (pfx) { + struct in_addr ipv4 = get_ipv4_from_prefix(pfx, realpeer.sin6_addr); + memcpy(&result->sin_addr, &ipv4, sizeof(struct in_addr)); + } + else { + // Can this even happen? Fallback so we don't crash. + memset(&result->sin_addr, 0, 4); + } + *__len = sizeof(struct sockaddr_in); return ret; } diff --git a/tnat64.conf.5 b/tnat64.conf.5 index 530e734..7ef4082 100644 --- a/tnat64.conf.5 +++ b/tnat64.conf.5 @@ -63,9 +63,16 @@ The following directives are used in the tnat64 configuration file: .TP .I nat64_prefix -The prefix of IPv6 address of the NAT64 (e.g. "nat64_prefix = 64:ff9b::"). Only one -NAT64 prefix may be specified per path block, or one outside a path -block (to define the default NAT64 prefix). The NAT64 prefix is always /96. +The prefix of IPv6 address of the NAT64 (e.g. "nat64_prefix = 64:ff9b::/96"). Only +one NAT64 prefix may be specified per path block, or one outside a path +block (to define the default NAT64 prefix). If the prefix size (/96) is omitted, +a /96 prefix is assumed. + +.TP +.I nat64_suffix +The suffix to be added to the NAT64 IPv6 address when using a NAT64 prefix larger +than /96 (RFC6052). The default value is "::" (0 / no suffix). This parameter +usually isn't necessary. .TP .I local diff --git a/tnat64.conf.complex.example b/tnat64.conf.complex.example index 29849f5..9eebebf 100644 --- a/tnat64.conf.complex.example +++ b/tnat64.conf.complex.example @@ -1,4 +1,4 @@ -# This is the configuration for libtsocks (transparent socks) +# This is the configuration for libtnat64 (transparent NAT64) # Lines beginning with # and blank lines are ignored # # The basic idea is to specify: @@ -21,17 +21,18 @@ local = 10.0.0.0/255.0.0.0 # Paths # For this example this machine needs to access 150.0.0.0/255.255.0.0 as # well as port 80 on the network 150.1.0.0/255.255.0.0 through -# the NAT64 with the prefix 2001:aaa:bbbb:0:ccc:616::/96 - +# the NAT64 with the prefix 2001:aaa:bbbb:cccc::/64, +# using a NAT64 suffix of ::aa:bbcc (see RFC6052). path { subnet = 150.0.0.0/255.255.0.0 subnet = 150.1.0.0:80/255.255.0.0 - nat64_prefix = 2001:aaa:bbbb:0:ccc:616:: + nat64_prefix = 2001:aaa:bbbb:cccc::/64 + nat64_suffix = ::aa:bbcc } # Default NAT64 prefix # For connections that aren't to the local subnets or to 150.0.0.0/255.255.0.0 -# the NAT64 with the prefix 64:ff9b::/96 should be used +# the NAT64 with the prefix 64:ff9b::/96 (with no suffix) should be used. -nat64_prefix = 64:ff9b:: +nat64_prefix = 64:ff9b::/96 diff --git a/tnat64.conf.simple.example b/tnat64.conf.simple.example index c74c372..c9a65ac 100644 --- a/tnat64.conf.simple.example +++ b/tnat64.conf.simple.example @@ -12,5 +12,5 @@ local = 192.168.0.0/255.255.255.0 # Otherwise we use the NAT64 -nat64_prefix = 64:ff9b:: +nat64_prefix = 64:ff9b::/96 diff --git a/validateconf.c b/validateconf.c index c0b6c2f..7a2a5e3 100644 --- a/validateconf.c +++ b/validateconf.c @@ -196,10 +196,43 @@ void show_prefix(struct parsedfile *config, struct prefixent *prefix, int def) struct netent *net; /* Show address */ - if (prefix->address != NULL) - printf("NAT64 prefix: %s\n", prefix->address); - else + if (prefix->address != NULL) { + printf("NAT64 prefix: %s/%d\n", prefix->address, prefix->prefix_size); + + char suffix_buffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(prefix->suffix), suffix_buffer, sizeof(suffix_buffer)); + + if (strcmp(suffix_buffer, "::") != 0) { + printf("NAT64 suffix: %s\n", suffix_buffer); + + + // Check if the NAT64 suffix is too large. + int suffix_size = 0; + switch(prefix->prefix_size) { + case 32: suffix_size = 7; break; + case 40: suffix_size = 6; break; + case 48: suffix_size = 5; break; + case 56: suffix_size = 4; break; + case 64: suffix_size = 3; break; + } + + int suffix_used_bytes = 0; + for (int i = 0; i < 16; i++) { + if ((prefix->suffix).s6_addr[15-i] != 0) suffix_used_bytes = (i + 1); + } + + if (suffix_used_bytes > suffix_size) { + fprintf(stderr, "Error: The specified NAT64 suffix (%d bytes) is larger than " + "the available space inside the NAT64 prefix (%d bytes). " + "The suffix will be truncated to fit. \n", suffix_used_bytes, suffix_size ); + } + + } + + } + else { printf("NAT64 prefix: ERROR! None specified\n"); + } /* If this is the default servers and it has reachnets, thats stupid */ From 284e496c5abe29f8e2c3e2b24826b5c7cd8a635e Mon Sep 17 00:00:00 2001 From: Florian Bach Date: Fri, 5 May 2023 15:04:10 +0200 Subject: [PATCH 3/3] Update validateconf Validate some more stuff, add useful return code. --- tnat64.c | 10 +++++++ validateconf.c | 74 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/tnat64.c b/tnat64.c index 116d660..fb19a03 100644 --- a/tnat64.c +++ b/tnat64.c @@ -255,6 +255,16 @@ struct in6_addr put_ipv4_into_prefix(struct prefixent *prefix, struct in_addr ip break; } + // Ensure that the 9th byte in the prefix is set to 0 as required by + // RFC 6052 section 2.2: + if (prefix->prefix_size < 128 && retval.s6_addr[8] != 0) { + retval.s6_addr[8] = 0; + if (prefix->prefix_size <= 64) + show_msg(MSGDEBUG, "Setting 8th bit in prefix to 0 (RFC6052 2.2)\n"); + else + show_msg(MSGWARN, "Setting 8th bit in prefix to 0 (RFC6052 2.2)\n"); + } + return retval; } diff --git a/validateconf.c b/validateconf.c index 7a2a5e3..4426d39 100644 --- a/validateconf.c +++ b/validateconf.c @@ -40,8 +40,8 @@ static const char *progname = "tnat64-validateconf"; /* Name for error ms #include #include -void show_prefix(struct parsedfile *, struct prefixent *, int); -void show_conf(struct parsedfile *config); +int show_prefix(struct parsedfile *, struct prefixent *, int); +int show_conf(struct parsedfile *config); void test_host(struct parsedfile *config, char *); int main(int argc, char *argv[]) @@ -90,8 +90,13 @@ int main(int argc, char *argv[]) /* If they specified a test host, test it, otherwise */ /* dump the configuration */ - if (!testhost) - show_conf(&config); + if (!testhost) { + int retval = show_conf(&config); + if (retval != 0) { + fprintf(stderr, "Found %d error(s)\n", retval); + exit(2); + } + } else test_host(&config, testhost); @@ -147,8 +152,11 @@ void test_host(struct parsedfile *config, char *host) return; } -void show_conf(struct parsedfile *config) +int show_conf(struct parsedfile *config) { + + int error_count = 0; + struct netent *net; struct prefixent *prefix; @@ -167,7 +175,7 @@ void show_conf(struct parsedfile *config) printf("=== Default NAT64 prefix configuration ===\n"); if ((config->defaultprefix).address != NULL) { - show_prefix(config, &(config->defaultprefix), 1); + error_count += show_prefix(config, &(config->defaultprefix), 1); } else { @@ -182,23 +190,36 @@ void show_conf(struct parsedfile *config) while (prefix != NULL) { printf("=== Path (line no %d in configuration file)" " ===\n", prefix->lineno); - show_prefix(config, prefix, 0); + error_count += show_prefix(config, prefix, 0); printf("\n"); prefix = prefix->next; } } - return; + return error_count; } -void show_prefix(struct parsedfile *config, struct prefixent *prefix, int def) +int show_prefix(struct parsedfile *config, struct prefixent *prefix, int def) { + int error_count = 0; struct netent *net; /* Show address */ if (prefix->address != NULL) { printf("NAT64 prefix: %s/%d\n", prefix->address, prefix->prefix_size); + if (prefix->prefix_size < 128 && (prefix->prefix).s6_addr[8] != 0) { + // RFC 6052 section 2.2 states that this byte in the NAT64 prefix MUST be 0. + show_msg(MSGERR, "NAT64 prefix specified is invalid - the 8th bit must be zero (RFC6052 2.2)\n"); + (prefix->prefix).s6_addr[8] = 0; + + char corrected_prefix_buffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &(prefix->prefix), corrected_prefix_buffer, sizeof(corrected_prefix_buffer)); + show_msg(MSGERR, "Corrected NAT64 prefix: %s/%d\n", corrected_prefix_buffer, prefix->prefix_size); + + error_count++; + } + char suffix_buffer[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &(prefix->suffix), suffix_buffer, sizeof(suffix_buffer)); @@ -218,13 +239,35 @@ void show_prefix(struct parsedfile *config, struct prefixent *prefix, int def) int suffix_used_bytes = 0; for (int i = 0; i < 16; i++) { - if ((prefix->suffix).s6_addr[15-i] != 0) suffix_used_bytes = (i + 1); + if ((prefix->suffix).s6_addr[15-i] != 0) { + suffix_used_bytes = (i + 1); + } } if (suffix_used_bytes > suffix_size) { - fprintf(stderr, "Error: The specified NAT64 suffix (%d bytes) is larger than " - "the available space inside the NAT64 prefix (%d bytes). " - "The suffix will be truncated to fit. \n", suffix_used_bytes, suffix_size ); + // Clear all bytes inside the suffix that would overwrite + // bytes in the prefix. + + for (int i = 0; i < (16-suffix_size); i++) { + (prefix->suffix).s6_addr[i] = 0; + } + + inet_ntop(AF_INET6, &(prefix->suffix), suffix_buffer, sizeof(suffix_buffer)); + + if (suffix_size > 0) { + fprintf(stderr, "Error: The specified NAT64 suffix (%d bytes) is larger than " + "the available space inside the NAT64 prefix (%d bytes).\n" + "The suffix will be truncated to fit - new suffix: %s \n", + suffix_used_bytes, suffix_size, suffix_buffer ); + } + else { + fprintf(stderr, "Error: The specified NAT64 prefix size (/%d) " + "does not allow for a NAT64 suffix.\n" + "Please choose a different prefix or remove the suffix.\n", + prefix->prefix_size); + } + + error_count++; } } @@ -232,6 +275,7 @@ void show_prefix(struct parsedfile *config, struct prefixent *prefix, int def) } else { printf("NAT64 prefix: ERROR! None specified\n"); + error_count++; } @@ -243,11 +287,13 @@ void show_prefix(struct parsedfile *config, struct prefixent *prefix, int def) fprintf(stderr, "Error: The default NAT64 prefix has " "specified networks it can be used to reach (subnet statements), " "these statements are ignored since the " "default NAT64 prefix will be used for any network " "which is not specified in a subnet statement " "for other prefixes\n"); + error_count++; } } else if (prefix->reachnets == NULL) { fprintf(stderr, "Error: No subnet statements specified for " "this NAT64 prefix, it will never be used\n"); + error_count++; } else { @@ -263,4 +309,6 @@ void show_prefix(struct parsedfile *config, struct prefixent *prefix, int def) net = net->next; } } + + return error_count; }