Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for NAT64 prefix sizes other than /96 (RFC6052) #6

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 84 additions & 23 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 @@ -403,18 +447,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);
Expand Down Expand Up @@ -472,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 @@ -494,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 @@ -507,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 @@ -524,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
129 changes: 126 additions & 3 deletions tnat64.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,112 @@ 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;
}

// 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;

}

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 +413,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 +507,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
Loading