← 输入框|github.com|刷新
Skip to content

Using IPVersion.All leads to OSError: [Errno 101] Network is unreachable logged #1357

Open

Description

@agners

When using AsyncZeroconf(ip_version=IPVersion.All) this can lead to the following warning being logged:

WARNING Error with socket 66 (('::1', 5353, 0, 0))): [Errno 101] Network is unreachable
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/asyncio/selector_events.py", line 1196, in sendto
    self._sock.sendto(data, addr)
OSError: [Errno 101] Network is unreachable

It seems that listening to the IPv6 loopback on it's own isn't problematic, but when trying to send to that socket, it leads to the above error. The problematic socket is created via get_all_addresses_v6(), which returns the loopback interface with the ::1 address (the full tuple being (('::1', 0, 0), 1)).

This then leads to a socket with the follow options created:

import socket
import struct
s = socket.socket(family=socket.AF_INET6, type=socket.SOCK_DGRAM)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 255)
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True)
s.bind(('::2', 5353, 0, 0))
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, struct.pack('@I', 1))

When this socket then is used, the stack trace appears:

s.sendto(b"Hello", ('ff02::fb', 5353, 0, 0))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 101] Network is unreachable

The relevant option seem to be the IPv6 specific binding to the interface index IPV6_MULTICAST_IF, in this case 1 for the loopback interface.

Activity

  1. agners commented on Feb 7, 2024

    @agners
    CollaboratorAuthor

    The problem here really is the missing multicast support flag on the loopback interface

    $ ip addr                                                                                                                                                                                                                                                                                             
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host noprefixroute
           valid_lft forever preferred_lft forever
    

    E.g. a Ethernet interface has the flag:

    2: eno2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    

    On IPv4 it is probably not a problem since the socket option IPV6_MULTICAST_IF to bind to a specific interface index is IPv6 specific.

    I found that e.g. the Matter SDK's minimal mDNS implementation simply skips loopback (see https://github.com/project-chip/connectedhomeip/blob/v1.2.0.1/src/lib/dnssd/minimal_mdns/AddressPolicy_DefaultImpl.cpp#L41-L53).

    However, it seems we can learn that from the interface flags instead. But it would require a change in the ifaddr library.

  2. bdraco commented on Feb 7, 2024

    @bdraco
    Member

    Looks like macos has MULTICAST on lo0

    lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    	options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
    	inet 127.0.0.1 netmask 0xff000000
    	inet6 ::1 prefixlen 128 
    	inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
    	nd6 options=201<PERFORMNUD,DAD>
    
  3. bdraco commented on Feb 7, 2024

    @bdraco
    Member

    linux 4.4.x

    lo        Link encap:Local Loopback
              inet addr:127.0.0.1  Mask:255.0.0.0
              inet6 addr: ::1/128 Scope:Host
              UP LOOPBACK RUNNING  MTU:65536  Metric:1
              RX packets:24830216 errors:0 dropped:0 overruns:0 frame:0
              TX packets:24830216 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1 
              RX bytes:3042660938 (2.8 GiB)  TX bytes:3042660938 (2.8 GiB)
    
  4. bdraco commented on Feb 7, 2024

    @bdraco
    Member
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host 
           valid_lft forever preferred_lft forever
    
  5. bdraco commented on Feb 7, 2024

    @bdraco
    Member

    In HA we already explicitly exclude loopback

            zc_args["interfaces"] = [
                str(source_ip)
                for source_ip in await network.async_get_enabled_source_ips(hass)
                if not source_ip.is_loopback
                and not (isinstance(source_ip, IPv6Address) and source_ip.is_global)
                and not (
                    isinstance(source_ip, IPv6Address)
                    and zc_args["ip_version"] == IPVersion.V4Only
                )
                and not (
                    isinstance(source_ip, IPv4Address)
                    and zc_args["ip_version"] == IPVersion.V6Only
                )
            ]
    
  6. bdraco commented on Feb 7, 2024

    @bdraco
    Member

    There is even a docstring that loopback doesn't work.

    Someone might need it though so I think we can change InterfaceChoice.All to exclude loopback, and add InterfaceChoice.AllWithLoopback

  7. agners commented on Feb 7, 2024

    @agners
    CollaboratorAuthor

    There is a iflags, I think it would be the better indication.

    Other interfaces might have that restriction too. E.g. a manually created dummy device:

    sudo ip link add name loop1 type dummy
    sudo ip addr add ::2 dev loop1
    
  8. agners commented on Feb 7, 2024

    @agners
    CollaboratorAuthor

    This would allow to filter the interfaces smarter on our end: ifaddr/ifaddr#59

  9. bdraco commented on Feb 7, 2024

    @bdraco
    Member

    That would be better but realistically we don't have that information unless your PR gets merged

  10. agners commented on Apr 16, 2025

    @agners
    CollaboratorAuthor
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OSError: [Errno 101] Network is unreachable
    

    It seems that this is more related to the routing table, e.g. adding a route like this makes the error go away:

    ip -6 route add table local multicast ff00::/8 dev lo proto kernel metric 255 pref medium
    

    There is also an explicit flag which allows to turn on/off multicast on the loopback interface (by default it is off):

    ip link set lo multicast on 
    

    With ifaddr/ifaddr#59 ifaddr will return the current state of that flag per interface.

    However, in my testing using Arch Linux with Linux 6.14.2, even with multicast set to on, multicast is not working on the loopback interface (I don't see multicast packets from the same or other processes). It does work with a dummy interface though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions