Subject: bin/8640: Relay loop in faithd(8)
To: None <gnats-bugs@gnats.netbsd.org>
From: Feico Dillema <dillema@acm.org>
List: netbsd-bugs
Date: 10/17/1999 15:50:54
>Number:         8640
>Category:       bin
>Synopsis:       faithd enters relay loop for IPv4 connections
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Oct 17 15:03:00 1999
>Last-Modified:
>Originator:     Feico Dillema
>Organization:
University of Tromso
>Release:        Sun Oct 17 1999
>Environment:
System: NetBSD server6 1.4K NetBSD 1.4K (PASTA.v6) #2: Tue Sep 28 01:46:29 CEST 1999 root@server6:/usr/src/sys/arch/i386/compile/PASTA.v6 i386

>Description:

When faithd(8) is used to relay connections *and* to
accept connections for local service it will enter
a relay loop if the target address is an IPv4 address.

>How-To-Repeat:

Example:
	say, machine has local IPv4 address A.B.C.D,
	and a faithd is started on it for e.g. telnet as:

	faithd telnet /usr/libexec/telnetd telnetd

Now a `telnet A.B.C.D` to this machine makes faithd enter a relay loop
due to fact that it sees an incoming connection for the IPV4MAPPED 
address: ::ffff:A.B.C.D. Instead of starting a local telnetd to serve
the request, it translates ::ffff:A.B.C.D into IPv4 address A.B.C.D 
and relays the connection this address. This relayed connection to
A.B.C.D is comes into faithd again as ::ffff:A.B.C.D, which repeats
the relay process untill the kernel runs out of proc-table entries.

>Fix:

The following little patch adds support for IPv4 Mapped Addresses
to src/usr.sbin/faith/faithd.c and avoids the relay loop:

--- faithd.c.orig       Sun Oct 17 23:41:04 1999
+++ faithd.c    Sun Oct 17 23:42:06 1999
@@ -508,14 +508,14 @@
        struct sockaddr_in *sin4;
 
        for (p = myaddrs; p; p = p->next) {
+               struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
+               struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
                sin6 = (struct sockaddr_in6 *)p->addr;
                sin4 = (struct sockaddr_in *)p->addr;
 
                /* ugly! */
                if (p->addr->sa_len == dst->sa_len
                 && p->addr->sa_family == dst->sa_family) {
-                       struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
-                       struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
 
                        switch (dst->sa_family) {
                        case AF_INET6:
@@ -526,6 +526,18 @@
                        case AF_INET:
                                if (sin4->sin_addr.s_addr == dst4->sin_addr.s_addr)
                                return 0;
+                               break;
+                       }
+               }
+               else { /* probably even uglier ? */
+
+                        switch (dst->sa_family) {
+                        case AF_INET6:
+                                if (IN6_IS_ADDR_V4MAPPED(&(dst6->sin6_addr))) 
+                                        if (sin4->sin_addr.s_addr == dst6->sin6_addr.s6_addr32[3])
+                                                return 0;      
+                                break;
+                       default:
                                break;
                        }
                }
>Audit-Trail:
>Unformatted: