The Internet Super Server inetd

The internet super server, or
inetd(8), is
available on all Unix(like) systems, providing many of the basic network
services available. This chapter describes the relationship between the daemon
and several of the config files in the /etc/ directory.

Overview

In this document we will look at a simple definition of
inetd(8), how
several files that relate to
inetd(8) work
(not that these files are not related to other software), how to add a service
to inetd(8)
and some considerations both to use
inetd(8) for a
particular service and times when a service might be better off running outside
of inetd(8).

What is inetd?

In traditional Unix scenarios, one server (daemon) process watches for
connections on a particular port, and handles incoming requests. Now if a
machine offers many services, many daemon processes would be needed, mostly
running idle but still wasting resources like memory. The internet super server,
inetd, is an approach to this problem. It listens on a number of ports, and when
it receives a request it then determines which program to run to handle the
request and starts an instance of that program.

In the above diagram you can see the general idea. The
inetd(8)
process receives a request and then starts the appropriate server process. What
inetd(8) is
doing is software multiplexing. An important note here, regarding security: On
many other UNIX-like systems, a package called tcpwrappers is used as a security
enhancement for
inetd(8). On
NetBSD the tcpwrapper functionality is built into
inetd(8) using
libwrap.

Configuring inetd - /etc/inetd.conf

The operation of
inetd(8) is
controlled by its own config file, surprisingly named /etc/inetd.conf, see
inetd.conf(5).
The inetd.conf file basically provides enabling and mapping of services the
systems administrator would like to have multiplexed through
inetd(8),
indicating which program should be started for incoming requests on which port.

inetd.conf(5)
is an ascii file containing one service per line, and several fields per line.
The basic field layout is:

service-name: The service name indicates the port
inetd(8)
should listen on. It is either a decimal number, or a name matching a service
name given in /etc/services.

socket-type: The communications socket type, the different types are
stream for a TCP stream, dgram for an UDP service, raw for a raw
socket, rdm for reliably delivered message and "seqpacketfor a sequenced
packet socket. The most common socket types arestreamanddgram`.

protocol: The protocol used, mostly tcp, tcp6, udp and udp6 for
stream-oriented services via the Transmission Control Protocol, or
datagram-oriented services via the User Datagram Protocol. It is worth noting
that tcp and udp mean they use the default (currently IPv4), tcp4
specifically means communication via IPv4 only, and tcp6 and udp6 are
IPv6-only. In addition to those, protocols based on Remote Procedure Calls
(RPC) can be specified as either rpc/tcp or rpc/udp.

wait/nowait: This field tells
inetd(8) if
it should wait for a server program to return or to continue processing new
connections immediately. Many connections to server processes require answers
after data transfers are complete, where other types can keep transmitting on
a connection continuously, the latter is a nowait and the former wait. In
most cases, this entry corresponds to the socket-type, for example a
streaming connection would (most of the time) have a nowait value in this
field.

user[:group]: This field gives the user name and optionally a group name
that the server process which
inetd(8)
starts up runs as.

server-program: This field is the full path of the program that gets
started.

program-arguments: This field contains the argument vector argv[] of the
program started, including the program name and additional arguments the
systems administrator may need to specify for the server program that is
started.

That is all a lot to digest and there are other things the systems administrator
can do with some of the fields. Here is a sample line from an inetd.conf file:

ftp stream tcp nowait root /usr/libexec/ftpd ftpd -ll

From the left, the service-name is ftp, socket-type is stream, protocol is
tcp,
inetd(8) won't
wait for the server process to terminate (nowait), the process runs as user
root, path is /usr/libexec/ftpd and program name and arguments are
ftpd -ll. Notice in the last field, the program name is different from the
service-name.

Services - /etc/services

The next file to consider is the service name data base that can be found in
/etc/services. This file basically contains information mapping a service name
to a port number. The format of the /etc/services file is:

service-name port-number/protocol-name [aliases]

service-name is the name of the service, port-number is the port number
assigned to the service, protocol-name is either tcp or udp, and if alias
names for a port are needed, they can be added as aliases, separated by white
spaces. Comments may be added after a hash mark (#).

Let's take a look at the ssh entries as an example:

ssh 22/tcp # Secure Shell
ssh 22/udp

As we can see, from the left, the service name is ssh, the port number is 22,
the protocols are both tcp and udp. Notice that there is a separate entry for
every protocol a service can use (even on the same port).

Protocols - /etc/protocols

Another file read by
inetd(8) is
/etc/protocols. This file has the information pertaining to DARPA Internet
protocols. The format of the protocols name data base is:

protocol-name number [aliases]

where protocol-name describes the payload of an IP packet, e.g. tcp or
udp. number is the official protocol number assigned by IANA, and optional
alias names can be added after that.

Let's look at the seventh entry in the /etc/protocols db as an example:

tcp 6 TCP # transmission control protocol

Starting from the left, we see that the protocol name is tcp, the number is 6
and the only aliases listed is TCP, belonging to the Transmission Control
Protocol as indicated by the comment in that line.

Remote Procedure Calls (RPC) - /etc/rpc

The rpc program number data base used by services with the rpc protocol type
in
inetd.conf(5)
is kept in /etc/rpc and contains name mappings to rpc program numbers. The
format of the file is:

server-name program-number aliases

For example, here is the nfs entry:

nfs 100003 nfsprog

Allowing and denying hosts - /etc/hosts.allow, /etc/hosts.deny

As mentioned above, NetBSD's
inetd(8) has
the tcpwrapper package built in via the libwrap library. As such,
inetd(8) can
allow or deny access to each service on a more fine-grained base than just
allowing a service to everyone, or not enabling it at all. The access control is
defined in the files /etc/hosts.allow and /etc/hosts.deny, see the
hosts_access(5)
manpage.

Each of the two files contains several lines that describe access restrictions
for a certain server. Access is allowed if permission is given in
/etc/hosts.allow. If the service is not listened in /etc/hosts.allow but in
/etc/hosts.deny, it is denied. If a service is listed in neither file, it is
allowed, giving standard
inetd(8)
behaviour.

Each line in /etc/hosts.allow and /etc/hosts.deny contains a service either
by name (as given in the field for argv[0] in /etc/inetd.conf, e.g. ftpd
instead of ftp), or the special service ALL which obviously applies to all
services. Following the service name is - separated by a colon - a number of
access restrictions, which can be hostnames, domains, single IP addresses, whole
IP subnets or some other restrictions, please check
hosts_access(5)
for all the details.

An example configuration that is mostly open but denies access to services to a
certain host and all machines from a certain domain would look like this:

# /etc/hostname.deny:
ALL: some.host.name, .some.domain

Another example that would be mostly closed, denying access to all but very few
machines would need entries in both /etc/hosts.allow and /etc/hosts.deny.
The entry for /etc/hosts.deny would be:

Adding a Service

Many times a systems administrator will find that they need to add a service to
their system that is not already in
inetd(8) or
they may wish to move a service to it because it does not get very much traffic.
This is usually pretty simple, so as an example we will look at adding a version
of POP3 on a NetBSD system.

In this case we have retrieved and installed the cucipop package, which can be
found in pkgsrc/mail/cucipop. This server is pretty simple to use, the only
oddities are different path locations. Since it is POP3 we know it is a stream
oriented connection with nowait. Running as root will be fine, the only item
that is different is the location of the program and the name of the program
itself.

So the first half of the new entry in /etc/inetd.conf looks like this:

pop3 stream tcp nowait root

After installation, pkgsrc deposited cucipop in /usr/pkg/sbin/cucipop. So with
the next field we have:

pop3 stream tcp nowait root /usr/pkg/sbin/cucipop

Last, we want to use the Berkeley mailbox format, so our server program must be
called with the -Y option. This leaves the entire entry looking like so:

pop3 stream tcp nowait root /usr/pkg/sbin/cucipop cucipop -Y

We have added the service named pop3 to /etc/inetd.conf. Next item to check
is that the system can map the service name to a port number in /etc/services:

The pop3 entries here are of interest, i.e. they are already contained in the
/etc/services file shipped with NetBSD.

Now, to have
inetd(8) use
the new entry, we simply restart it using the rc script:

# sh /etc/rc.d/inetd restart

All done, in most cases, the software you are using has documentation that will
specify the entry, in the off case it does not, sometimes it helps to try and
find something similar to the server program you will be adding. A classic
example of this is a MUD server which has built-in telnet. You can pretty much
borrow the telnet entry and change parts where needed.

When to use or not to use inetd

The decision to add or move a service into or out of
inetd(8) is
usually based on server load. As an example, on most systems the telnet daemon
does not require as many new connections as say a mail server. Most of the time
the administrator has to feel out if a service should be moved.

A good example I have seen is mail services such as smtp and pop. I had setup a
mail server in which pop3 was in
inetd(8) and
exim was running in standalone, I mistakenly assumed it would run fine since
there was a low amount of users, namely myself and a diagnostic account. The
server was also setup to act as a backup MX and relay in case another heavily
used one went down. When I ran some tests I discovered a huge time lag for pop
connections remotely. This was because of my steady fetching of mail and the
diagnostic user constantly mailing diagnostics back and forth. In the end I had
to move the pop3 service out of
inetd(8).

The reason for moving the service is actually quite interesting. When a
particular service becomes heavily used, of course, it causes a load on the
system. In the case of a service that runs within the
inetd(8) meta
daemon the effects of a heavily loaded service can also harm other services that
use inetd(8).
If the multiplexor is getting too many requests for one particular service, it
will begin to affect the performance of other services that use
inetd(8). The
fix, in a situation like that, is to make the offending service run outside of
inetd(8) so
the response time of both the service and
inetd(8) will
increase.

Other Resources

Following is some additional reading and information about topics covered in
this document.