LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Go Back   LinuxQuestions.org > Blogs > Rod3775
User Name
Password

Notices


Rate this Entry

iPhones, Linux and Service Discovery

Posted 05-14-2017 at 12:25 PM by Rod3775

Service Discovery

Service discovery refers to the process of getting additional information about resources in a distributed (e.g. networked) system. iPhones are particularly dependent on service discovery. This blog concerns the process of discovering services (specifically remote printer sevices) and IP addresses (e.g name service) and the various ways this can be done.

<Short digression>:

IP network messages can be sent in one of three ways, referred to later. Here
is quick reminder about these ways:

Unicast - Messages contain an explicit IP address and a port number,
and the message is routed/received based on the address given. The
receiver sends the message to the local process(if any) monitoring
that port.
Broadcast- Messages contain an explicit IP address on the local network,
usually ending in ".255" These messages are received by all devices
on the local network. They are not routed outside the local
network. The receivers sends the message to the local process(if
any) monitoring that port.
Multicast- Messages contain a specific, non-local IP address (hopefully)
recognized as a multicast address by routers, which "magically"
forward the message to appropriate receivers, which may be anywhere
in the world. The message also contains a port number, and if a
receiver gets the message, it sends it to the local process(if any)
monitoring that port.

All routers implement unicast and broadcast. Most residential routers do not implement multicast, or implement it for only certain multicast mechanisms (e.g. uPnP/SSDP). They may also broadcast multicast messages on the local network (only).

<End digression>

Traditionally, there was no service detection in Unix, and name service used a local file (/etc/hosts) to map between names and IP addresses. /etc/hosts was created by hand for the (small) local networks that existed using coax cable Ethernet.

The next step up was ISC (Berkeley) name service. In this scheme, which we call DNS, a local or remote daemon is accessed via port 53 and returns name->IP or IP->name mappings using automatically-maintained local tables. The big difference is that the daemon (named) has built-in to its tables the IP address of a more "senior" machine that also contains a name server, and if the local tables do not yield an answer, the "senior" nameserver is queried. The local nameserver retains (for some selectable period of time) the answer, and subsequent requests are answered using the local copy. This process, called "caching", speeds up name resolution, but also creates the possibility of "stale" answers. If a name is moved to another IP address, it takes a while for this information to propagate through the worldwide name server system, but eventually, the stale entries get replaced. Note that there is no concept of "service", except via the artifice of multiple names for (maybe) the same IP address (ftp.x.com; mail.x.com, www.x.com, etc). This breaks reverse resolution, since the IP address might return any (or none) of these, but reverse resolution is not commonly needed. Since each local name server contains the IP address of its "senior partner", remote requests can be sent to the that specific partner via unicast network protocols. One may inquire as to how this "senior" address is obtained, and the answer is two-fold. If the IP address of the requestor is "built-in" (static), the senior name server IP address can also be built in. If the IP address of the requestor is obtained via DHCP, the senior name server address is (almost) always sent also.

An intermediate step called NetBIOS (no longer used much) was developed by Microsoft for local office networks. In this scheme, there was a designated registrar which held both names and services whose IP address was obtained via the broadcast network mechanism, and was queried for name and service information. Each machine using NetBIOS was responsible for registering itself and its services. [For name service, ISC "named" actually has such a mechanism for names, but it is rarely used.]

The Simple Network Management Protocol, or SNMP, is conceptually similar to NetBIOS, but is Internet-standard, rather than Microsoft-centric. It uses agents on connected devices to provide information about themselves. Theoretically, service discovery can be performed by querying the "manager", but most home networks don't have one. The agents can be queried sequentially by scanning each IP in the network, but this is impractical.

Microsoft created a protocol called UPnP or Simple Service Discovery Protocal (SSDP) that used multicasting to find devices, and unicasting to retrieve device information. Support is spotty - Apple and CUPS don't support it, although many printing devices do. Some routers do it, some don't.

The latest incarnation is called DNS-SD, or mDNS, and is considerably different. IN DNS-SD, each attached computer transmits a set of multicast messages on port 5353 (sound familiar?) at frequent intervals "advertising" its name and the services it offers. Apple chose to use DNS-SD, trademarked as "BonJour", for connection by their iPhones, and CUPS (Common Unix Printing Service), a public-domain Apple product, can also use it. (An earlier version of service discovery was originally embedded in CUPS and used broadcast, but has been discontinued. It also had severe limitations.)

The way DNS-SD is implemented in practice is that each device that wants to provide service discovery in a wireless environment has, in addition to its wireless station (STA) in the local network, its own access point (AP) on its own, home-grown network. If an iPhone or other requestor wants to find out about services, it must connect directly to that AP. Each such connection requires its own wireless NIC (radio, etc) and only accesses the single device. Since the connection is direct, there is no router involved and multicast "works". Apple makes money on this deal, since BonJour is a licensed product. Contemporary printer manufacturers are almost compelled to license it since wireless printers and iPhones are all the rage.

So, in order to use Linux printers with iPhones, Linux must implement some version of DNS-SD that is compatible with BonJour. The Linux daemon that transmits/receives service discovery and dynamic name service is called Avahi. But in order to use it, the box running Avahi must have, in addition to its connection to the normal local network, an AP that iPhones can use to connect to it and get service discovery.

In a home network, which is the more common case, the printers are fairly stable, and print queues for them can be added to a local CUPS server without service discovery. Most DHCP setups try to assign the same IP to new devices if they have been on recently (like in the last day). So even in a DHCP network, addresses are pretty stable, and actual IP addresses can be used in CUPS queues (e.g. ipp://192.168.1.8) and won't have to change. Many DHCPs, both in computers and in routers, allow for reserved or fixed addresses for devices with known MAC (the "real" hardware address in the device) addresses. This is even safer. So a CUPS server can build a print queue "database" that holds this information. A Python utility called "airprint-generate", available from github.com/tjfontaine can parse this file and build a services database for Avahi that will be used to generate DNS-SD advertisments when necessary. These describe the printers in terms of their CUPS queue names (on the server) and the CUPS server name. Avahi sends this out on the AP (and maybe on the local LAN, but it may never get through the router, so who cares).

So an iPhone or an Avahi-enabled client(e.g. another CUPS) can pick up this service data and use it to send print data (in this case) to the AP. In theory this should work. But the form of the service data contains the name of the print queue and the NAME of the server, not its IP address. Since the server is also hosting hostap, the software that creates an AP, its IP address is almost surely 192.168.x.1 (where x is chosen to not collide with the the IP addresses of the local network). However, the NAME of the server is "<hostname>.local" in the ".local" domain and there is no name server to resolve this back to an IP address. So the CUPS server gets a legitimate print request aimed at itself based on service discovery data sent out by Avahi, but referring to <hostname>.local. The local DNS name server (if there even is one) does not know about ".local", which is purely a DNS-SD creation, and CUPS does not acknowledge the print request.

Here we get into the arcane history of name resolution. Because name resolution is such a common requirement, routines to "do" name resolution have been part of the C library (glibc) forever.There are also command-line tools to do name resolution. But there is no requirement that the C library calls be used, DNS can be queried directly through port 53, and the file /etc/hosts is world-readable. If an application uses the C library routines, that's nice, but there is no way of knowing! Furthermore, applications that require a great deal of name resolution are free to keep answers they get from somewhere, sometime, and never go back to the well to check if the answers are still valid. The various name service sources (etc/hosts, named, and Avahi) are either completely static or make an attempt to keep updated. But applications are under no such constraint, and worse yet, caching of answers is not even at the application level - it's done in the C library!

In an attempt to allow some control over the name resolution process, the C library implements a mechanism called "Name Service Switch" or NSS. A file is created called "/etc/nsswitch.conf". Each line refers to a kind of name service (mostly not internet related) and gives a list of sources from which this particular kind of name resolution can be satisfied. We are interested in the "hosts" category, and the options are "files" - the /etc/hosts file in this case; "dns" - the DNS daemon 'named'; or "mdns" (of which there are several variants). In all cases, "mdns" uses the Avahi daemon as described above. But because of the multicasting, Avahi doesn't retrieve much outside the local network, unless configured to browse specific external sites, and is quite slow. The "recommended" nsswitch setup for Avahi is "hosts: files mdns dns". But if Avahi isn't running, timeouts (if they happen at all) are quite long - after all, your multicast could theoretically be going all around the world, and unlike DNS, there is no intermediate caching. And it affects EVERY host name resolution request in the ENTIRE system. Web browsing becomes impossible. So locating the IP address of "<hostname>.local" on the server via mdns is both slow and fraught with reliability issues. Even if it's running, how can Avahi tell when it's gotten all the answers it's going to get? Answer: Wait a long time and see what happens!

If Avahi is not installed (and it is an add-on for my Slackware system), then the nsswitch "hosts" entry merely contains "files dns" DNS is not going to help here, but adding an entry in the server's /etc/hosts file of the form "192.168.x.1 <hostname>.local" will resolve the name that CUPS needs without going to DNS, and the print request from a remote client works just fine. In this case, the server uses Avahi for its service advertisement role only, and NOT for mDNS name resolution. However, the remote client is going to need "mdns" because that's the only way IT can resolve <hostname>.local!

*** I have a separate blog post about setting up the CUPS server itself. It involves recompiling the CUPS source, since the default on Slackware doesn't include Avahi/mDNS support. Also some bug fixing involving cups-filters.

So once all the software is running, the CUPS configuration sequence is:
1) Create the printer queues on the server using fixed printer IP addresses
2) Use the airprint-generate utility to convert the CUPS printers.conf file into
an Avahi .service file
3) Place the Avahi .service file in the /etc/avahi/services directory on the server
4) Add an entry to /etc/hosts on the server to resolve <hostname>.local

Note that Avahi must run on both the server and on the client. Only the client needs "mdns" in its nsswitch.conf file. Only name resolution is done through the C library. Service discovery is done directly by Avahi when requested by CUPS, and the server Avahi already knows about the printers from step (3) above!

One last issue is that service discovery exposes the existence of system resources to outside users. That's what it's supposed to do, but perhaps only certain resources should be exposed to outside users. In my present application, I'd like to expose AirPrint printers (via Avahi and CUPS) to the AirPrint access point network, but not the underlying printers themselves, which have internal websites and all sorts of configuration options that could be misused. On the base network, this stuff needs to be exposed so the local CUPS server has access to it. This is an exercise in firewalling, since the server has no control over the outside user's system or what the actual printers choose to expose.

For my specific case, the solution was the following:

1) Set the following in iptables (eth0 is the base network; wlan0 is the AirPrint AP)
iptables -A FORWARD -i eth0 ! -o eth0 -j DROP
iptables -A FORWARD -i wlan0 ! -o wlan0 -j DROP
This prevents mDNS information (and everything else!) on one interface from getting to the other. The two nets can communicate to applications on the server, but not across the server.

This can be tightened down even further by restricting the ports available on the wireless network:
iptables -A INPUT -i wlan0 -p udp --sport 5353 -j ACCEPT
iptables -A INPUT -i wlan0 -p udp --dport 5353 -j ACCEPT
iptables -A INPUT -i wlan0 -p tcp --dport 631 -j ACCEPT
iptables -A INPUT -i wlan0 -p tcp --sport 631 -j ACCEPT
iptables -A INPUT -i wlan0 -j REJECT
Then everything except mDNS traffic (Avahi) and ipp traffic (CUPS) will be restricted. No telnet, no http, no ftp, even if the CUPS server provides such things to its base network. [The reason for using REJECT rather than DROP is that it sends a "courtesy" message to the sender of a blocked message that short-circuits its timeout - particularly if the sender is trying to find a name server throught port 53.]

2) In avahi-daemon.conf on the server, set
disable-user-service-publishing=yes
This prevents the server's CUPS from registering its own knowledge of printers with Avahi. Only the information in the Avahi services/printer.service file will be advertised. If this was built with airprint-generate.py, it will contain only the AirPrint information. And this is all the client Avahi and client CUPS will see.
Posted in Uncategorized
Views 1837 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 01:56 PM.

Main Menu
Advertisement
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration