STREAMS Programming Guide

Building a Multiplexer

The example in this section builds a protocol multiplexer with the multiplexing configuration shown in Figure 13–1. To free users from the need to know about the underlying protocol structure, a user-level daemon process is built to maintain the multiplexing configuration. Users can then access the transport protocol directly by opening the transport protocol (TP) driver device node.

An internetworking protocol driver (IP) routes data from a single upper stream to one of two lower streams. This driver supports two STREAMS connections beneath it. These connections are to two distinct networks; one for the IEEE 802.3 standard through the 802.3 driver, and another to the IEEE 802.4 standard through the 802.4 driver. The TP driver multiplexes upper streams over a single stream to the IP driver.

Figure 13–1 Protocol Multiplexer

Diagram shows the multiplexing used in the code example in this

Example 13–1 shows how this daemon process sets up the protocol multiplexer. The necessary declarations and initialization for the daemon program follow.

Example 13–1 Protocol Daemon

#include <fcntl.h>
#include <stropts.h>
		int	fd_802_4,
		/* daemon-ize this process */

		switch (fork()) {
			case 0:
			case -1:
				perror("fork failed");

This multilevel multiplexed stream configuration is built from the bottom up. The example begins by first constructing the IP multiplexer. This multiplexing device driver is treated like any other software driver. It owns a node in the Solaris file system and is opened just like any other STREAMS device driver.

The first step is to open the multiplexing driver and the 802.4 driver, thus creating separate streams above each driver as shown in Figure 13–2. The stream to the 802.4 driver may now be connected below the multiplexing IP driver using the I_LINK ioctl(2).

Figure 13–2 Streams Before Link

Diagram continues the example. It shows the streams created after
the drivers are opened.

The sequence of instructions to this point is:

	if ((fd_802_4 = open("/dev/802_4", O_RDWR)) < 0) {
			perror("open of /dev/802_4 failed");
		if ((fd_ip = open("/dev/ip", O_RDWR)) < 0) {
			perror("open of /dev/ip failed");
		/* now link 802.4 to underside of IP */
		if (ioctl(fd_ip, I_LINK, fd_802_4) < 0) {
			perror("I_LINK ioctl failed");

I_LINK takes two file descriptors as arguments. The first file descriptor, fd_ip, is the stream connected to the multiplexing driver, and the second file descriptor, fd_802_4, is the stream to be connected below the multiplexer. The complete stream to the 802.4 driver is connected below the IP driver. The stream head's queues of the 802.4 driver are used by the IP driver to manage the lower half of the multiplexer.

I_LINK returns an integer value, muxid, which is used by the multiplexing driver to identify the stream just connected below it. muxid is ignored in the example, but it is useful for dismantling a multiplexer or routing data through the multiplexer. Its significance is discussed in Dismantling a Multiplexer.

The following sequence of system calls continues building the Internetworking Protocol multiplexer (IP):

	if ((fd_802_3 = open("/dev/802_3", O_RDWR)) < 0) {
			perror("open of /dev/802_3 failed");
		if (ioctl(fd_ip, I_LINK, fd_802_3) < 0) {
			perror("I_LINK ioctl failed");

The stream above the multiplexing driver used to establish the lower connections is the controlling stream and has special significance when dismantling the multiplexing configuration. This is illustrated in Dismantling a Multiplexer. The stream referenced by fd_ip is the controlling stream for the IP multiplexer.

The order in which the streams in the multiplexing configuration are opened is unimportant. If intermediate modules in the stream are necessary between the IP driver and media drivers, these modules must be added to the streams associated with the media drivers (using I_PUSH) before the media drivers are attached below the multiplexer.

The number of streams that can be linked to a multiplexer is restricted by the design of the particular multiplexer. The manual page describing each driver describes such restrictions (see SunOS Reference Manual, Intro(7)). However, only one I_LINK operation is allowed for each lower stream; a single stream cannot be linked below two multiplexers simultaneously.

Continuing with the example, the IP driver is now linked below the transport protocol (TP) multiplexing driver. As seen in Figure 13–1, only one link is supported below the transport driver. This link is formed by the following sequence of system calls:

	if ((fd_tp = open("/dev/tp", O_RDWR)) < 0) {
			perror("open of /dev/tp failed");
		if (ioctl(fd_tp, I_LINK, fd_ip) < 0) {
			perror("I_LINK ioctl failed");

Because the controlling stream of the IP multiplexer has been linked below the TP multiplexer, the controlling stream for the new multilevel multiplexer configuration is the stream above the TP multiplexer.

At this point, the file descriptors associated with the lower drivers can be closed without affecting the operation of the multiplexer. If these file descriptors are not closed, all subsequent read(2), write(2), ioctl(2), poll(2), getmsg(2), and putmsg(2) calls issued to them fail. That is because I_LINK associates the stream head of each linked stream with the multiplexer, so the user may not access that stream directly for the duration of the link.

The following sequence of system calls completes the daemon example:

		/* Hold multiplexer open forever or at least til this process
      is terminated by an external UNIX signal */

The transport driver supports several simultaneous streams. These streams are multiplexed over the single stream connected to the IP multiplexer. The mechanism for establishing multiple streams above the transport multiplexer is actually a by-product of the way in which streams are created between a user process and a driver. By opening different minor devices of a STREAMS driver, separate streams will be connected to that driver. The driver must be designed with the intelligence to route data from the single lower stream to the appropriate upper stream.

The daemon process maintains the multiplexed stream configuration through an open stream (the controlling stream) to the transport driver. Meanwhile, other users can access the services of the transport protocol by opening new streams to the transport driver; they are freed from the need for any unnecessary knowledge of the underlying protocol configurations and subnetworks that support the transport service.

Multilevel multiplexing configurations should be assembled from the bottom up. That is because the passing of ioctl(2) through the multiplexer is determined by the nature of the multiplexing driver and cannot generally be relied on.