Skip to content

Commit

Permalink
Add Linux nfconntrack IPv6 masquerading support
Browse files Browse the repository at this point in the history
  • Loading branch information
janikrabe committed Apr 6, 2018
1 parent 5e5a7b3 commit 787f0d5
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 54 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
2018-04-06 Janik Rabe <oidentd@janikrabe.com>

* Add Linux IPv6 masquerading support.

2018-04-04 Janik Rabe <oidentd@janikrabe.com>

* Add build information to `--version` output.
Expand Down
2 changes: 1 addition & 1 deletion KERNEL_SUPPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
| FreeBSD 1-3 | Yes | Yes | ipf only | No | Yes | No |
| FreeBSD 4 | Yes | Yes | ipf only | No | NAT only | If no NAT |
| FreeBSD 5+ | Yes | Yes | ipf only | No | NAT only | If no NAT |
| Linux | Yes | Yes | Yes | No | No | No |
| Linux | Yes | Yes | Yes | Yes | No | No |
| NetBSD 1-4 | Yes | Yes | ipf only | No | Yes | No |
| NetBSD 5+ | Yes | Yes | No | No | No | Yes |
| OpenBSD 2.0 - 2.3 | Yes | Yes | ipf only | No | Yes | No |
Expand Down
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Unreleased changes

* Added Linux IPv6 masquerading support.
* Added build information to `--version` output.

Changes in version 2.2.3
Expand Down
153 changes: 100 additions & 53 deletions src/kernel/linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -465,13 +465,6 @@ bool masq( int sock,
{
char buf[1024];

/*
** There's no masq support for IPv6 yet.
*/

if (faddr->ss_family != AF_INET)
return false;

lport = ntohs(lport);
fport = ntohs(fport);

Expand Down Expand Up @@ -527,22 +520,26 @@ static int masq_ct_line(char *line,
in_port_t masq_lport;
in_port_t masq_fport;
char user[MAX_ULEN];
in_addr_t localm;
in_addr_t remotem;
in_addr_t localn;
in_addr_t remoten;
struct sockaddr_storage ss;
struct sockaddr_storage localm_ss;
struct sockaddr_storage remotem_ss;
struct sockaddr_storage localn_ss;
struct sockaddr_storage remoten_ss;
int ret;

if (conntrack == CT_MASQFILE) {
in_addr_t localm4;
in_addr_t remotem4;
u_int32_t mport_temp;
u_int32_t nport_temp;
u_int32_t masq_lport_temp;
u_int32_t masq_fport_temp;

if (faddr->ss_family != AF_INET)
return -1;

ret = sscanf(line, "%15s %X:%X %X:%X %X %X %*d %*d %*u",
proto, &localm, &masq_lport_temp,
&remotem, &masq_fport_temp, &mport_temp, &nport_temp);
proto, &localm4, &masq_lport_temp,
&remotem4, &masq_fport_temp, &mport_temp, &nport_temp);

if (ret != 7)
return 1;
Expand All @@ -552,17 +549,27 @@ static int masq_ct_line(char *line,
masq_lport = (in_port_t) masq_lport_temp;
masq_fport = (in_port_t) masq_fport_temp;

sin_setv4(localm4, &localm_ss);
sin_setv4(remotem4, &remotem_ss);

/* Assume local NAT. */
remoten = localm;
localn = remotem;
sin_setv4(localm4, &remoten_ss);
sin_setv4(remotem4, &localn_ss);
} else if (conntrack == CT_IPCONNTRACK) {
int l1, l2, l3, l4, r1, r2, r3, r4;
int nl1, nl2, nl3, nl4, nr1, nr2, nr3, nr4;
in_addr_t localm4;
in_addr_t remotem4;
in_addr_t localn4;
in_addr_t remoten4;
u_int32_t nport_temp;
u_int32_t mport_temp;
u_int32_t masq_lport_temp;
u_int32_t masq_fport_temp;

if (faddr->ss_family != AF_INET)
return -1;

ret = sscanf(line,
"%15s %*d %*d ESTABLISHED src=%d.%d.%d.%d dst=%d.%d.%d.%d sport=%d dport=%d src=%d.%d.%d.%d dst=%d.%d.%d.%d sport=%d dport=%d",
proto, &l1, &l2, &l3, &l4, &r1, &r2, &r3, &r4,
Expand All @@ -588,57 +595,99 @@ static int masq_ct_line(char *line,
nport = (in_port_t) nport_temp;
mport = (in_port_t) mport_temp;

localm = l1 << 24 | l2 << 16 | l3 << 8 | l4;
remotem = r1 << 24 | r2 << 16 | r3 << 8 | r4;
localm4 = l1 << 24 | l2 << 16 | l3 << 8 | l4;
remotem4 = r1 << 24 | r2 << 16 | r3 << 8 | r4;

localn4 = nl1 << 24 | nl2 << 16 | nl3 << 8 | nl4;
remoten4 = nr1 << 24 | nr2 << 16 | nr3 << 8 | nr4;

localn = nl1 << 24 | nl2 << 16 | nl3 << 8 | nl4;
remoten = nr1 << 24 | nr2 << 16 | nr3 << 8 | nr4;
sin_setv4(localm4, &localm_ss);
sin_setv4(remotem4, &remotem_ss);
sin_setv4(localn4, &localn_ss);
sin_setv4(remoten4, &remoten_ss);
#if LIBNFCT_SUPPORT
} else if (conntrack == CT_NFCONNTRACK || conntrack == CT_LIBNFCT) {
#else
} else if (conntrack == CT_NFCONNTRACK) {
#endif
int l1, l2, l3, l4, r1, r2, r3, r4;
int nl1, nl2, nl3, nl4, nr1, nr2, nr3, nr4;
char ml[MAX_IPLEN];
char mr[MAX_IPLEN];
char nl[MAX_IPLEN];
char nr[MAX_IPLEN];
in_addr_t localm4;
in_addr_t remotem4;
in_addr_t localn4;
in_addr_t remoten4;
struct in6_addr localm6;
struct in6_addr remotem6;
struct in6_addr localn6;
struct in6_addr remoten6;
u_int32_t nport_temp;
u_int32_t mport_temp;
u_int32_t masq_lport_temp;
u_int32_t masq_fport_temp;

ret = sscanf(line,
"%15s %*d %15s %*d %*d ESTABLISHED src=%d.%d.%d.%d dst=%d.%d.%d.%d sport=%d dport=%d packets=%*d bytes=%*d src=%d.%d.%d.%d dst=%d.%d.%d.%d sport=%d dport=%d",
family, proto, &l1, &l2, &l3, &l4, &r1, &r2, &r3, &r4,
&masq_lport_temp, &masq_fport_temp,
&nl1, &nl2, &nl3, &nl4, &nr1, &nr2, &nr3, &nr4,
"%15s %*d %15s %*d %*d ESTABLISHED src=%45s dst=%45s sport=%d dport=%d packets=%*d bytes=%*d src=%45s dst=%45s sport=%d dport=%d",
family, proto, ml, mr,
&masq_lport_temp, &masq_fport_temp, nl, nr,
&nport_temp, &mport_temp);

/* Added to handle /proc/sys/net/netfilter/nf_conntrack_acct = 0 */
if (ret != 22) {
if (ret != 10) {
ret = sscanf(line,
"%15s %*d %15s %*d %*d ESTABLISHED src=%d.%d.%d.%d dst=%d.%d.%d.%d sport=%d dport=%d src=%d.%d.%d.%d dst=%d.%d.%d.%d sport=%d dport=%d",
family, proto, &l1, &l2, &l3, &l4, &r1, &r2, &r3, &r4,
&masq_lport_temp, &masq_fport_temp,
&nl1, &nl2, &nl3, &nl4, &nr1, &nr2, &nr3, &nr4,
"%15s %*d %15s %*d %*d ESTABLISHED src=%45s dst=%45s sport=%d dport=%d src=%45s dst=%45s sport=%d dport=%d",
family, proto, ml, mr,
&masq_lport_temp, &masq_fport_temp, nl, nr,
&nport_temp, &mport_temp);
}

if (ret != 22)
if (ret != 10)
return 1;

if (strcasecmp(family, "ipv4"))
return 1;
switch (faddr->ss_family) {
case AF_INET:
if (strcasecmp(family, "ipv4"))
return 1;

if (inet_pton(AF_INET, ml, &localm4) < 0 ||
inet_pton(AF_INET, mr, &remotem4) < 0 ||
inet_pton(AF_INET, nl, &localn4) < 0 ||
inet_pton(AF_INET, nr, &remoten4) < 0)
return 1;

sin_setv4(localm4, &localm_ss);
sin_setv4(remotem4, &remotem_ss);
sin_setv4(localn4, &localn_ss);
sin_setv4(remoten4, &remoten_ss);

break;
case AF_INET6:
if (strcasecmp(family, "ipv6"))
return 1;

if (inet_pton(AF_INET6, ml, &localm6) < 0 ||
inet_pton(AF_INET6, mr, &remotem6) < 0 ||
inet_pton(AF_INET6, nl, &localn6) < 0 ||
inet_pton(AF_INET6, nr, &remoten6) < 0)
return 1;

sin_setv6(&localm6, &localm_ss);
sin_setv6(&remotem6, &remotem_ss);
sin_setv6(&localn6, &localn_ss);
sin_setv6(&remoten6, &remoten_ss);

break;
default:
debug("masq_ct_line: bad address family %d", faddr->ss_family);
return -1;
}

masq_lport = (in_port_t) masq_lport_temp;
masq_fport = (in_port_t) masq_fport_temp;

nport = (in_port_t) nport_temp;
mport = (in_port_t) mport_temp;

localm = l1 << 24 | l2 << 16 | l3 << 8 | l4;
remotem = r1 << 24 | r2 << 16 | r3 << 8 | r4;

localn = nl1 << 24 | nl2 << 16 | nl3 << 8 | nl4;
remoten = nr1 << 24 | nr2 << 16 | nr3 << 8 | nr4;
} else
return -1;

Expand All @@ -652,19 +701,19 @@ static int masq_ct_line(char *line,
return 1;

/* Local NAT, don't forward or do masquerade entry lookup. */
if (localm == remoten) {
if (sin_equal(&localm_ss, &remoten_ss)) {
uid_t con_uid = MISSING_UID;
struct passwd *pw;
char suser[MAX_ULEN];
char ipbuf[MAX_IPLEN];

sin_setv4(htonl(remotem), &ss);
get_ip(faddr, ipbuf, sizeof(ipbuf));

if (con_uid == MISSING_UID && faddr->ss_family == AF_INET)
con_uid = get_user4(htons(masq_lport), htons(masq_fport), laddr, &ss);
con_uid = get_user4(htons(masq_lport), htons(masq_fport), laddr, &remotem_ss);

/* Add call to get_user6 when IPv6 NAT is supported. */
if (con_uid == MISSING_UID && faddr->ss_family == AF_INET6)
con_uid = get_user6(htons(masq_lport), htons(masq_fport), laddr, &remotem_ss);

if (con_uid == MISSING_UID)
return -1;
Expand All @@ -678,7 +727,7 @@ static int masq_ct_line(char *line,
return 0;
}

ret = get_ident(pw, masq_lport, masq_fport, laddr, &ss, suser, sizeof(suser));
ret = get_ident(pw, masq_lport, masq_fport, laddr, &remotem_ss, suser, sizeof(suser));
if (ret == -1) {
sockprintf(sock, "%d,%d:ERROR:%s\r\n",
lport, fport, ERROR("HIDDEN-USER"));
Expand All @@ -698,28 +747,26 @@ static int masq_ct_line(char *line,
return 0;
}

if (localn != ntohl(SIN4(faddr)->sin_addr.s_addr)) {
if (!sin_equal(&localn_ss, faddr)) {
if (!opt_enabled(PROXY))
return 1;

if (SIN4(faddr)->sin_addr.s_addr != SIN4(&proxy)->sin_addr.s_addr)
if (!sin_equal(faddr, &proxy))
return 1;

if (localn == SIN4(&proxy)->sin_addr.s_addr)
if (sin_equal(&localn_ss, &proxy))
return 1;
}

sin_setv4(htonl(localm), &ss);

ret = find_masq_entry(&ss, user, sizeof(user), os, sizeof(os));
ret = find_masq_entry(&localm_ss, user, sizeof(user), os, sizeof(os));

if (opt_enabled(FORWARD) && (ret != 0 || !opt_enabled(MASQ_OVERRIDE))) {
char ipbuf[MAX_IPLEN];

if (fwd_request(sock, lport, masq_lport, fport, masq_fport, &ss) == 0)
if (fwd_request(sock, lport, masq_lport, fport, masq_fport, &localm_ss) == 0)
return 0;

get_ip(&ss, ipbuf, sizeof(ipbuf));
get_ip(&localm_ss, ipbuf, sizeof(ipbuf));

debug("Forward to %s (%d %d) failed", ipbuf, masq_lport, fport);
}
Expand Down

0 comments on commit 787f0d5

Please sign in to comment.