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();
+ }
}
Kind regards,
Dimitris