From: Mark Wooding Date: Mon, 4 Jun 2018 01:56:44 +0000 (+0100) Subject: linux.c: Check for IPv4-mapped IPv6 addresses. X-Git-Tag: 1.0.5~1 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/yaid/commitdiff_plain/267505410875df7012ad8220c506602d7b173ac3 linux.c: Check for IPv4-mapped IPv6 addresses. Clients which use IPv4-mapping rather than proper IPv4 sockets get listed in `/proc/net/tcp6' rather than in `/proc/net/tcp'. Cope with this by searching the latter if we can't find the entry we want in the former. --- diff --git a/linux.c b/linux.c index 7508343..412fae8 100644 --- a/linux.c +++ b/linux.c @@ -347,6 +347,36 @@ done: return (rc); } +/* Convert the IPv4 socket address IN into the equivalent IPv4-mapped IPv6 + * address OUT. + */ +static void map_v4(struct socket *out, const struct socket *in) +{ + unsigned i; + in_addr_t a4 = ntohl(in->addr.ipv4.s_addr); + + for (i = 0; i < 10; i++) out->addr.ipv6.s6_addr[i] = 0; + for (i = 10; i < 12; i++) out->addr.ipv6.s6_addr[i] = 0xff; + for (i = 0; i < 4; i++) out->addr.ipv6.s6_addr[15 - i] = (a4 >> 8*i)&0xff; + out->port = in->port; +} + +/* Convert the IPv4-mapped IPv6 socket address IN into the equivalent IPv4 + * address OUT; return -1 if the IN address isn't actually IPv4-mapped. + */ +static int unmap_v4(struct socket *out, const struct socket *in) +{ + unsigned i; + in_addr_t a4 = 0; + + for (i = 0; i < 10; i++) if (in->addr.ipv6.s6_addr[i] != 0) return (-1); + for (i = 10; i < 12; i++) if (in->addr.ipv6.s6_addr[i] != 0xff) return (-1); + for (i = 0; i < 4; i++) a4 |= in->addr.ipv6.s6_addr[15 - i] << 8*i; + out->addr.ipv4.s_addr = htonl(a4); + out->port = in->port; + return (0); +} + /* Find out who is responsible for the connection described in the query Q. * Write the answer to Q. Errors are logged and reported via the query * structure. @@ -378,6 +408,20 @@ void identify(struct query *q) /* Search the main `tcp' table. */ if (search_tcp_file(q, gwp, q->ao, q->s)) goto done; + /* Oh, dear. If this is IPv4, then the entry might actually be in the IPv6 + * table, with weird addresses. So we must try again. + */ + if (q->ao->af == AF_INET) { + map_v4(&s[L], &q->s[L]); map_v4(&s[R], &q->s[R]); + if (search_tcp_file(q, gwp, &addroptab[ADDR_IPV6], s)) { + if (gwp && unmap_v4(&q->s[R], &s[R])) { + logmsg(q, LOG_ERR, "can't unmap NATted destination address"); + goto err_unk; + } + goto done; + } + } + /* If we opened the NAT table file, and we're using IPv4, then check to see * whether we should proxy the connection. At least the addresses in this * file aren't crazy.