AF_INET6 link-local addresses in getaddrinfo

Florian Weimer fweimer@redhat.com
Thu Nov 21 12:22:12 GMT 2024


* Ronan Pigott:

> I work on systemd-resolved, which offers an nss hosts module that
> implements both DNS and mDNS via systemd-resolved. mDNS is widely used
> where traditional DNS service is not available, and it is very common
> to recieve answers with link-local addresses in ipv6 networks. The NSS
> interface currently doesn't adequately support this use case, because
> nss_gethostbyname3_r does not support the necessary scope id, and
> nss_gethostbyname4_r is necessarily dual-stack, and is therefore never
> selected by getaddrinfo when the requested address family is
> AF_INET6. As a result, AF_INET6 lookups are broken in many cases
> because they omit the required scope id of link-local addresses even
> if it was determined correctly by the nss service.

It's on my project list:

  [11] New NSS module API
  <https://inbox.sourceware.org/libc-alpha/875xqypkgx.fsf@oldenburg3.str.redhat.com/>

Rather than adding gethostbyname5_r, I'm leaning towards a more general 

  struct nss_query_result
  {
    uint32_t size;
    uint32_t flags;
    uint64_t expire_epoch; /* CLOCK_REALTIME seconds.  */
    void *data;
  };

  enum nss_query_type
   {
     nss_query_type_string = 000,
     nss_query_type_uint64_t = 001,
     nss_query_type_sockaddr = 002,
   };

  enum nss_query_result_type
    {
      nss_query_result_type_passwd = 000 << 6,
      nss_query_result_type_sockaddr = 001 << 6,
      nss_query_result_type_string_array = 002 << 6,
    };

  enum nss_query_database
    {
      nss_query_database_passwd = 000 << 12;
      nss_query_database_hosts = 001 << 12;
    };
     
  enum nss_query
    {
       nss_query_pwnam = ((000 << 18)
                          | nss_query_type_string 
                          | nss_query_result_type_passwd
                          | nss_query_database_passwd),
       nss_query_pwbyuid = ((001 << 18)
                            | nss_query_type_uint64_t 
                            | nss_query_result_type_passwd
                            | nss_query_database_passwd),
       nss_query_hostbyname = ((002 << 18)
                             | nss_query_type_string 
                             | nss_query_result_type_sockaddr
                             | nss_query_database_hosts),
       nss_query_hostbyaddr = ((003 << 18)
                             | nss_query_type_sockaddr 
                             | nss_query_result_type_string_array
                             | nss_query_database_hosts),
     };

  enum nss_status
  _nss_MODULE_query (enum nss_query, const void *query, const void *hints,
                     struct nss_query_result *);

  void _nss_MODULE_free (enum nss_query, void *);

(Maybe just an enum with the queries and separately documented types
would be sufficient.)

With this, getpwuid (uid) would roughly be translated as

  uint64_t 
  struct nss_query_result result = { .size = sizeof (result), };
  _nss_files_query (nss_query_pwbyuid, &uid, NULL, &result);
  return result.data;

Of course with code for handling multiple service modules, error
checking, and it's likely necessary to copy the data.  But there is no
need anymore for an ERANGE retry loop.

And getpwnam (name) would be:

  struct nss_query_result result = { .size = sizeof (result), };
  _nss_files_query (nss_query_pwbyuid, name, NULL, &result);
  return result.data;

Translating getaddrinfo will be more complicated because I think it
makes sense to maintain the name lookup/service lookup split. So

  getaddrinfo (name, service, hints, res)

would turn into:

  struct nss_query_result result = { .size = sizeof (result), };
  _nss_dns_query (nss_query_hostbyname, name, hints, &result);
  struct sockaddr **sa_array = result.data;
  *res = NULL;
  for (size_t i = 0; sa_array[i] != NULL, i++)
    {
      /* Build list of struct addrinfo objects in *res, using
         data from the services lookup.  */
    }
  _nss_dns_free (nss_query_hostbyname, sa_array);

I don't think the memory savings from using the existing struct
gaih_addrtuple are worth it.  We'd be in the same situation again if it
turns out there is a compelling use for sin6_flowinfo after all.

But maybe it's required to pass down the service name as well, and let
the service module expand the protocol list?  Then something like SRV
lookups could be implemented.  But struct addrinfo as returned from
getaddrinfo would not be able to capture additional data, so I'm not
sure how useful this is going to be.

The _nss_MODULE_free functions do not need to actually deallocate
anything immediately.  For example, they could use reference counting on
internal data structures.

Would this work for the systemd modules?  What about avahi?

Thanks,
Florian



More information about the Libc-alpha mailing list