Skip to content

Commit

Permalink
Support for NAT64 prefixes other than /96 (RFC6052)
Browse files Browse the repository at this point in the history
  • Loading branch information
Leseratte10 committed Jan 22, 2023
1 parent 1309924 commit 53a7c0f
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 31 deletions.
77 changes: 61 additions & 16 deletions parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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]);
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
Expand All @@ -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:
Expand Down Expand Up @@ -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)
{
Expand All @@ -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
{
Expand All @@ -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, &currentcontext->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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -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];
Expand All @@ -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;
Expand All @@ -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;
Expand Down
6 changes: 4 additions & 2 deletions parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
};

Expand All @@ -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
119 changes: 116 additions & 3 deletions tnat64.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
6 changes: 6 additions & 0 deletions tnat64.conf.5
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ The prefix of IPv6 address of the NAT64 (e.g. "nat64_prefix = 64:ff9b::"). Only
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.

.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
An IP/subnet pair specifying a network which may be accessed directly without
Expand Down
13 changes: 7 additions & 6 deletions tnat64.conf.complex.example
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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

2 changes: 1 addition & 1 deletion tnat64.conf.simple.example
Original file line number Diff line number Diff line change
Expand Up @@ -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

Loading

0 comments on commit 53a7c0f

Please sign in to comment.