On Wed, Nov 12, 2025 at 4:03 PM Christopher Schultz <
[email protected]> wrote:

> Dimitris,
>
> On 11/11/25 5:46 PM, Dimitris Soumis wrote:
> > On Tue, Nov 11, 2025 at 9:47 PM Christopher Schultz <
> > [email protected]> wrote:
> >
> >> Mark,
> >>
> >> On 11/11/25 1:17 PM, Christopher Schultz wrote:
> >>> Mark,
> >>>
> >>> On 11/11/25 10:53 AM, Christopher Schultz wrote:
> >>>> Mark,
> >>>>
> >>>> On 11/10/25 10:08 AM, Mark Thomas wrote:
> >>>>> On 10/11/2025 15:01, Christopher Schultz wrote:
> >>>>>> Mark,
> >>>>>>
> >>>>>> On 11/10/25 9:48 AM, Mark Thomas wrote:
> >>>>>>> On 10/11/2025 14:15, Christopher Schultz wrote:
> >>>>>>>> All,
> >>>>>>>>
> >>>>>>>> I've been looking into this, and I think the problem is that all
> >>>>>>>> of the link-local interfaces detected during this test are not
> >>>>>>>> actually routable:
> >>>>>>>>
> >>>>>>>> fe80:0:0:0:2609:93a5:c4ac:15ea%utun7
> >>>>>>>> fe80:0:0:0:926b:f264:7ec7:283e%utun6
> >>>>>>>> fe80:0:0:0:4ab0:1734:75f8:c236%utun5
> >>>>>>>> fe80:0:0:0:edc2:bbe0:ab0e:db71%utun4
> >>>>>>>> fe80:0:0:0:898:f4ff:feb6:6b87%llw0
> >>>>>>>> fe80:0:0:0:898:f4ff:feb6:6b87%awdl0
> >>>>>>>> fe80:0:0:0:ce81:b1c:bd2c:69e%utun3
> >>>>>>>> fe80:0:0:0:96b7:f229:b97f:3887%utun2
> >>>>>>>> fe80:0:0:0:c055:a248:e218:ba6b%utun1
> >>>>>>>> fe80:0:0:0:310c:b555:9669:572a%utun0
> >>>>>>>>
> >>>>>>>> So even though Tomcat can bind to them, and you can create a URL
> >>>>>>>> to try to connect, the OS will never route the packets as
> expected.
> >>>>>>>>
> >>>>>>>> When checking all available interfaces on my machine, they all
> >>>>>>>> seem to be IPv6 and all seem to be .. unusable to Tomcat?
> >>>>>>>>
> >>>>>>>> If I hard-code the interface "fe80::1%lo0" into the unit test, it
> >>>>>>>> passes immediately and successfully. But when left to its own
> >>>>>>>> devices, the test will randomly pick one of the other interfaces
> >>>>>>>> and hang.
> >>>>>>>>
> >>>>>>>> I think the testing environment is being sandboxed by the OS and
> >>>>>>>> so it doesn't even see those other interfaces that I know exist.
> >>>>>>>> Instead, I only see these useless tunneling interfaces. For
> >>>>>>>> example, "lo0" doesn't show up, and "en0" doesn't show up -- the
> >>>>>>>> primary useful interfaces on this machine.
> >>>>>>>>
> >>>>>>>> So I wonder if we need some kind of workaround for MacOS.
> >>>>>>>
> >>>>>>> I think we need a more general work-around. Seeing a number of
> >>>>>>> those were tunnels reminded me of the change we made to the unlock
> >>>>>>> accept code to skip point to point interfaces.
> >>>>>>>
> >>>>>>> Something like:
> >>>>>>>
> >>>>>>> diff --git a/test/org/apache/catalina/startup/
> >>>>>>> TestStartupIPv6Connectors.java b/test/org/apache/catalina/startup/
> >>>>>>> TestStartupIPv6Connectors.java
> >>>>>>> index c3c4b9a..2212e39 100644
> >>>>>>> ---
> a/test/org/apache/catalina/startup/TestStartupIPv6Connectors.java
> >>>>>>> +++
> b/test/org/apache/catalina/startup/TestStartupIPv6Connectors.java
> >>>>>>> @@ -68,6 +68,9 @@
> >>>>>>>            while (interfaces.hasMoreElements()) {
> >>>>>>>                NetworkInterface interf = interfaces.nextElement();
> >>>>>>>                Enumeration<InetAddress> addresses =
> >>>>>>> interf.getInetAddresses();
> >>>>>>> +            if (interf.isPointToPoint()) {
> >>>>>>> +                continue;
> >>>>>>> +            }
> >>>>>>>                while (addresses.hasMoreElements()) {
> >>>>>>>                    InetAddress address = addresses.nextElement();
> >>>>>>>                    if (address instanceof Inet6Address
> inet6Address) {
> >>>>>>
> >>>>>> +1
> >>>>>>
> >>>>>> This is one of the patches I had considered, but when I started
> >>>>>> adding debug code to print stuff out, I had a in it and it wasn't
> >>>>>> printing the %en interface (which would work) so I thought it would
> >>>>>> always fail.
> >>>>>>
> >>>>>> I've fixed it and I think we basically have the same patch at this
> >>>>>> point.
> >>>>>>
> >>>>>> But it still doesn't work for me, since %lo0 doesn't appear in the
> >>>>>> list. %en0 not does appear
> >>>>
> >>>> This should have been "%en0 DOES appear..."
> >>>>
> >>>>> , but it's not link-local and so it doesn't
> >>>>>> get chosen for this. Instead, the test chooses the %awdl0 interface
> >>>>>> which is equally as useless for this test.
> >>>>>
> >>>>> What is that interface? Is there some characteristic we can use to
> >>>>> filter it out as well? If the test can't find any interfaces, it will
> >>>>> skip the test.
> >>>>
> >>>> I think awdl0 has something to do with AirPlay.
> >>>>
> >>>>>> ChatGPT says that I need to allow Java more privileges -- like "Full
> >>>>>> Disk Access" in order to get access to the various real network
> >>>>>> interfaces. I was hoping for a solution that didn't require that
> >>>>>> because while it will work for me, it won't likely work for anyone
> >>>>>> else who just happens to want to run the unit tests.
> >>>>>>
> >>>>>> We might have to implement an allow-list, block-list, etc.
> >>>>>
> >>>>> Yuck. I'd much prefer a general solution if we can find one.
> >>>>
> >>>> I spent some time chatting with AI and it didn't have any better ideas
> >>>> other than a combination allow-list + deny-list to skip known-bad
> >>>> interfaces (like %utun*) and include known-good interfaces (like %lo*
> >>>> and %en*).
> >>>>
> >>>> I would also very much like to find a better solution.
> >>>>
> >>>> If I run the test with your patch, the test is skipped. Which is good,
> >>>> I suppose. But it means the test isn't being run. I will give more
> >>>> permissions to the JVM and see if it can see all interfaces and,
> >>>> therefore, allow this test to run.
> >>>
> >>> Something is still weird.
> >>>
> >>> I wrote a short program to just dump out all the details of the network
> >>> interfaces so I could compare before/after giving privileges and the
> >>> before-state shows *everything*.
> >>>
> >>> So I'll need to dig more into why the test isn't seeing these
> interfaces
> >>> in my environment.
> >>
> >> This is a face-palm; I had completely forgotten that the test runs
> >> through interfaces until it finds what it's looking for: both link-local
> >> and "global" addresses. But it doesn't have any preference for which
> >> one(s) of those it chooses.
> >>
> >> So it can detect-and-choose more than one link-local address, for
> >> example, and the last one wins. Since we don't really care about which
> >> link-local interface is chosen, it doesn't matter to us, either. But it
> >> also doesn't have a fixed order in which it traverses the interface
> >> list: it just looks in the order in which the JVM handed-out the
> >> interfaces.
> >>
> >> So in my environment, the llw0 and awdl0 interfaces are listed first,
> >> and are both acceptable as link-local addresses. Then later it sees the
> >> %en0 interface -- which is "global" and stops. If it had continued
> >> through the list, it would have seen the %lo0 interface which would work
> >> much better.
> >>
> >> Here are all the interfaces and their flags on this system, in the order
> >> in which Java ends up seeing them:
> >>
> >> Name   Addr                                        U L V P M n s l m
> >> utun7  fe80:0:0:0:2609:93a5:c4ac:15ea%utun7        Y N N Y Y Y N N N
> >> utun6  fe80:0:0:0:926b:f264:7ec7:283e%utun6        Y N N Y Y Y N N N
> >> utun5  fe80:0:0:0:4ab0:1734:75f8:c236%utun5        Y N N Y Y Y N N N
> >> utun4  fe80:0:0:0:edc2:bbe0:ab0e:db71%utun4        Y N N Y Y Y N N N
> >> llw0   fe80:0:0:0:6473:baff:fe97:b9d%llw0          Y N N N Y Y N N N
> >> awdl0  fe80:0:0:0:6473:baff:fe97:b9d%awdl0         Y N N N Y Y N N N
> >> utun3  fe80:0:0:0:ce81:b1c:bd2c:69e%utun3          Y N N Y Y Y N N N
> >> utun2  fe80:0:0:0:96b7:f229:b97f:3887%utun2        Y N N Y Y Y N N N
> >> utun1  fe80:0:0:0:c055:a248:e218:ba6b%utun1        Y N N Y Y Y N N N
> >> utun0  fe80:0:0:0:310c:b555:9669:572a%utun0        Y N N Y Y Y N N N
> >> en0    fd00:0:0:0:10ce:a902:a735:c03a%en0          Y N N N Y N N N N
> >> en0    2600:4040:452e:500:18cb:a7f7:f0ac:e9f0%en0  Y N N N Y N N N N
> >> en0    2600:4040:452e:500:142c:94b5:4311:f3f1%en0  Y N N N Y N N N N
> >> en0    fe80:0:0:0:1033:a61d:8b9c:3289%en0          Y N N N Y Y N N N
> >> en0    192.168.50.225                              Y N N N Y N Y N N
> >> lo0    fe80:0:0:0:0:0:0:1%lo0                      Y Y N N Y Y N N N
> >> lo0    0:0:0:0:0:0:0:1%lo0                         Y Y N N Y N N Y N
> >> lo0    127.0.0.1                                   Y Y N N Y N N Y N
> >>
> >> The key for the flags is:
> >> U - Up
> >> L - Loop-back (interface)
> >> V - Virtual
> >> P - P-to-P
> >> M - Multi-cast (interface)
> >> n - Link-local
> >> s - Site-Local
> >> l - Loop-back (address)
> >> m - Multi-cast (address)
> >>
> >> I think the only usable link-local interface in my environment will be
> >> these:
> >>
> >> Name   Addr                                        U L V P M n s l m
> >> en0    fe80:0:0:0:1033:a61d:8b9c:3289%en0          Y N N N Y Y N N N
> >> lo0    fe80:0:0:0:0:0:0:1%lo0                      Y Y N N Y Y N N N
> >>
> >> Both of those have the link-local flag set to TRUE, but these do as
> well:
> >>
> >> Name   Addr                                        U L V P M n s l m
> >> utun7  fe80:0:0:0:2609:93a5:c4ac:15ea%utun7        Y N N Y Y Y N N N
> >> utun6  fe80:0:0:0:926b:f264:7ec7:283e%utun6        Y N N Y Y Y N N N
> >> utun5  fe80:0:0:0:4ab0:1734:75f8:c236%utun5        Y N N Y Y Y N N N
> >> utun4  fe80:0:0:0:edc2:bbe0:ab0e:db71%utun4        Y N N Y Y Y N N N
> >> llw0   fe80:0:0:0:6473:baff:fe97:b9d%llw0          Y N N N Y Y N N N
> >> awdl0  fe80:0:0:0:6473:baff:fe97:b9d%awdl0         Y N N N Y Y N N N
> >> utun3  fe80:0:0:0:ce81:b1c:bd2c:69e%utun3          Y N N Y Y Y N N N
> >> utun2  fe80:0:0:0:96b7:f229:b97f:3887%utun2        Y N N Y Y Y N N N
> >> utun1  fe80:0:0:0:c055:a248:e218:ba6b%utun1        Y N N Y Y Y N N N
> >> utun0  fe80:0:0:0:310c:b555:9669:572a%utun0        Y N N Y Y Y N N N
> >>
> >> Further removing the point-to-point ones leaves these:
> >>
> >> Name   Addr                                        U L V P M n s l m
> >> llw0   fe80:0:0:0:6473:baff:fe97:b9d%llw0          Y N N N Y Y N N N
> >> awdl0  fe80:0:0:0:6473:baff:fe97:b9d%awdl0         Y N N N Y Y N N N
> >>
> >> Maybe we want link-local PLUS interface-loopback?
> >>
> >> I think that gets us only these:
> >> Name   Addr                                        U L V P M n s l m
> >> lo0    fe80:0:0:0:0:0:0:1%lo0                      Y Y N N Y Y N N N
> >>
> >> This patch (relative to the previous release) chooses %lo0 for the
> >> link-local IPv6 address and %en0 for the global IPv6 address. And then
> >> both unit tests pass.
> >>
> >> @@ -67,16 +67,22 @@ public class TestStartupIPv6Connectors extends
> >> TomcatBaseTest {
> >>            Enumeration<NetworkInterface> interfaces =
> >> NetworkInterface.getNetworkInterfaces();
> >>            while (interfaces.hasMoreElements()) {
> >>                NetworkInterface interf = interfaces.nextElement();
> >> +            if(interf.isPointToPoint()) {
> >> +                continue;
> >> +            }
> >>                Enumeration<InetAddress> addresses =
> >> interf.getInetAddresses();
> >>                while (addresses.hasMoreElements()) {
> >>                    InetAddress address = addresses.nextElement();
> >> +                System.out.println("Detected interface " + address);
> >>                    if (address instanceof Inet6Address) {
> >>                        Inet6Address inet6Address = (Inet6Address)
> address;
> >> -                    if (inet6Address.isLinkLocalAddress()) {
> >> +                    if (inet6Address.isLinkLocalAddress() &&
> >> interf.isLoopback()) {
> >>                            linklocalAddress =
> inet6Address.getHostAddress();
> >> +                        System.out.println("Chose " + address + " as
> >> link-local address");
> >>                        }
> >>                        if (!inet6Address.isAnyLocalAddress() &&
> >> !inet6Address.isLoopbackAddress() && !inet6Address.isLinkLocalAddress()
> >> && !inet6Address.isMulticastAddress()) {
> >>                            globalAddress =
> inet6Address.getHostAddress();
> >> +                        System.out.println("Chose global address: " +
> >> address);
> >>                        }
> >>                        if (linklocalAddress != null && globalAddress !=
> >> null) {
> >>                            return;
> >>
> >> Obviously the System.outs will be removed.
> >>
> >> WDYT?
> >>
> >> -chris
> >>
> >>
> >> ---------------------------------------------------------------------
> >> To unsubscribe, e-mail: [email protected]
> >> For additional commands, e-mail: [email protected]
> >>
> >>
> > Kind of late to the discussion, but the link-local IPv6 to be on the
> > loopback interface is not the default on Linux and the test gets
> skipped. I
> > propose the following patch:
> >
> > --- a/test/org/apache/catalina/startup/TestStartupIPv6Connectors.java
> > +++ b/test/org/apache/catalina/startup/TestStartupIPv6Connectors.java
> > @@ -64,28 +64,62 @@ public class TestStartupIPv6Connectors extends
> > TomcatBaseTest {
> >
> >       @BeforeClass
> >       public static void initializeTestIpv6Addresses() throws Exception {
> > +        Inet6Address possibleLinkLocalLoopback = null;
> > +        Inet6Address possibleLinkLocalOnGlobal = null;
> > +        Inet6Address possibleLinkLocalAny = null;
> > +
> >           Enumeration<NetworkInterface> interfaces =
> > NetworkInterface.getNetworkInterfaces();
> >           while (interfaces.hasMoreElements()) {
> >               NetworkInterface interf = interfaces.nextElement();
> >               Enumeration<InetAddress> addresses =
> interf.getInetAddresses();
> > -            if (interf.isPointToPoint()) {
> > +            if (!interf.isUp() || interf.isVirtual() ||
> > interf.isPointToPoint()) {
> >                   continue;
> >               }
> > +            boolean globalOnInterface = false;
> >               while (addresses.hasMoreElements()) {
> >                   InetAddress address = addresses.nextElement();
> >                   if (address instanceof Inet6Address inet6Address) {
> > -                    if (inet6Address.isLinkLocalAddress()) {
> > -                        linklocalAddress =
> inet6Address.getHostAddress();
> > -                    }
> >                       if (!inet6Address.isAnyLocalAddress() &&
> > !inet6Address.isLoopbackAddress() && !inet6Address.isLinkLocalAddress()
> &&
> > !inet6Address.isMulticastAddress()) {
> > -                        globalAddress = inet6Address.getHostAddress();
> > +                        globalOnInterface = true;
> > +                        if (!interf.isLoopback()) {
> > +                            globalAddress =
> inet6Address.getHostAddress();
> > +                            break;
> > +                        }
> >                       }
> > -                    if (linklocalAddress != null && globalAddress !=
> null)
> > {
> > -                        return;
> > +                }
> > +            }
> > +
> > +            // Second pass to get link-local results with specific order
> > +            addresses = interf.getInetAddresses();
> > +            while (addresses.hasMoreElements()) {
> > +                InetAddress address = addresses.nextElement();
> > +                if (address instanceof Inet6Address inet6Address) {
> > +                    if (inet6Address.isLinkLocalAddress()) {
> > +                        if (interf.isLoopback()) {
> > +                            // Best option for mac
> > +                            possibleLinkLocalLoopback = inet6Address;
> > +                        } else if (globalOnInterface &&
> > possibleLinkLocalOnGlobal == null) {
> > +                            // link-local on an interface that also has
> a
> > global IPv6 (e.g. en0)
> > +                            possibleLinkLocalOnGlobal = inet6Address;
> > +                        } else if (possibleLinkLocalAny == null) {
> > +                            possibleLinkLocalAny = inet6Address;
> > +                        }
> >                       }
> >                   }
> >               }
> >           }
> > +
> > +        if (possibleLinkLocalLoopback != null) {
> > +            linklocalAddress =
> possibleLinkLocalLoopback.getHostAddress();
> > +        } else if (possibleLinkLocalOnGlobal != null) {
> > +            linklocalAddress =
> possibleLinkLocalOnGlobal.getHostAddress();
> > +        } else if (possibleLinkLocalAny != null) {
> > +            linklocalAddress = possibleLinkLocalAny.getHostAddress();
> > +        }
> >       }
>
> Looks good to me. Mark, does this look like Windows will have any
> issues? I suspect you wouldn't have +1'd if it did.
>
We can harden it more by also skipping interfaces that don't support
multicast as it can be disabled by firewall rules.
-            if (!interf.isUp() || interf.isVirtual() ||
interf.isPointToPoint()) {
+            if (!interf.isUp() || interf.isVirtual() ||
interf.isPointToPoint() || !interf.supportsMulticast()) {

>
> I can also post my interface info class source if anyone is interested
> in what their system interfaces loo like to Windows.
>
This could help identify possible edge cases.

>
> -chris
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>
>

Reply via email to