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 1, 2023
1 parent 3d11756 commit 3a1548f
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 20 deletions.
61 changes: 54 additions & 7 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 @@ -269,8 +274,8 @@ static int handle_subnet(struct parsedfile *config, int lineno, char *value)
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 +284,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 +323,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 @@ -488,7 +534,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 +556,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 +569,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 +587,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 @@ -183,6 +183,103 @@ 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 @@ -282,8 +379,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 @@ -362,7 +461,21 @@ 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
10 changes: 6 additions & 4 deletions tnat64.conf.complex.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@ 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

15 changes: 12 additions & 3 deletions validateconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,19 @@ 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);
}
}
else {
printf("NAT64 prefix: ERROR! None specified\n");
}


/* If this is the default servers and it has reachnets, thats stupid */
Expand Down

0 comments on commit 3a1548f

Please sign in to comment.