[Previous] [Contents] [Index] [Next]

The Virtual Packet Interface

This chapter includes:

The Virtual Packet Interface (VPI) is a network interface built into the TCP/IP stack. You can use this interface to implement your own device driver to support custom hardware, or you can write your own packet filter to monitor and control IP traffic.

To use the VPI, your application must link to the virtual packet library functions using these steps:

  1. Register an interface using vp_attach().
  2. Call vp_getpkt() to get a packet (if it's ready); or vp_putpkt() to give a packet to the stack.
  3. Close the interface using vp_detach().

Virtual Packet Library

The virtual packet library consists of these functions:

vp_attach()
Attach to a Virtual Packet Interface.
vp_detach()
Detach your application from the stack.
vp_getpkt()
Get a packet from the stack.
vp_ifrname()
Return the interface name.
vp_ktom()
Convert a TCP/IP stack address to an application address.
vp_mclget()
Get a cluster.
vp_mfree()
Free a single mbuf.
vp_mfreem()
Free all the mbufs in the chain.
vp_mget()
Allocate an mbuf.
vp_mgethdr()
Allocate an mbuf and initialize it as a packet header.
vp_mtok()
Convert an application address to a TCP/IP stack address.
vp_putpkt()
Send a packet to the stack.
vp_unit()
Return the unit number of the given vpid_t.

For descriptions of these functions, see the TCP/IP Libraries chapter.

Using the VPI as a network interface

The slattach utility attaches serial lines as network interfaces. It's also a good example of how to use the VPI.


Note: You'll find the complete source for this example in /usr/demo/src/socket/slattach.

slattach.c

The slattach.c program does the following:

if_sl.c

Both the slattach() and slstart() functions are in if_sl.c. It's the main part of how to use the VPI. Let's look at the slattach() function first:

slattach() example

int slattach(int fd, int flags)
{
    register struct sl_softc *sc = &slsoftc;
    struct ifreq ifr;
    struct _dev_info_entry di;

    memset(sc, 0, sizeof(*sc));

    sc->sc_ttyfd = fd;
    sc->sc_flags = flags | SC_AUTOCOMP;


    if ((sc->inProxy = qnx_proxy_attach(0, 0, 0, -1)) == -1) {
         syslog(LOG_ERR, "qnx_proxy_attach(): %m");
         return -1;
    }

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
         syslog(LOG_ERR, "socket(): %m");
         return -1;
    }

    if ((sc->vpid = vp_attach(sockfd, VP_NI, sc->inProxy)) == (vpid_t) -1)
         return -1;

    if (!vp_ifrname(sc->vpid, ifr.ifr_name)) {
        syslog(LOG_ERR, "vp_ifrname(): %m");
        return -1;
    }

    ifr.ifr_flags = (short)(IFF_POINTOPOINT | IFF_MULTICAST | IFF_RUNNING);
    if (ioctl(sockfd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
        syslog(LOG_ERR, "ioctl (SIOCSIFFLAGS): %m");
        return -1;
    }

    ifr.ifr_mtu = SLMTU;
    if (ioctl(sockfd, SIOCSIFMTU, (caddr_t) &ifr) < 0) {
        syslog(LOG_ERR, "ioctl (SIOCSIFFLAGS): %m");
        return -1;
    }

    if (slinit(sc) == 0)
        return -1;

    sc->sc_unit = vp_unit(sc->vpid);

    /* find the nid of the dev -- device could be remote */
    if (dev_info(sc->sc_ttyfd, &di) == -1)
        return -1;

    /* device input proxy */
    sc->sc_rxvid = qnx_proxy_rem_attach(
                          di.nid, 
                          sc->sc_rxmid = qnx_proxy_attach(0, 0, 0, -1));

    /* arm the device */
    dev_arm(fd, sc->sc_rxvid, _DEV_EVENT_RXRDY);

    return sc->sc_unit;
}

In the above example, the slattach() function:

  1. Prepares a proxy.
  2. Calls socket() to connect to the TCP/IP stack.
  3. Calls vp_attach() with the proxy to register a VPI as a network interface.
  4. Calls vp_ifrname() to get the registered interface name.
  5. Forms an ifreq structure to set up the flags and the maximum transmission unit (mtu) of the interface. Since this is a serial line, the IFF_POINTOPOINT flag is set up.
  6. Calls vp_unit() to collect the interfaces unit number.
  7. Prepares another proxy and bundles it with the serial-line device using dev_arm().

Note: Your application must set up the correct interface flag so that the TCP/IP stack knows which packet to give to the interface.

slstart()

Here's the code for the slstart() function:

/* 
 * Start output on interface. Get another datagram
 * to send from the interface queue and map it to
 * the interface before starting output.
 */
void slstart(sigset_t *sigs){
    pid_t pid;

    for (;;) {
            pid = Receive(0, 0, 0);
            if (pid == -1 && errno != EINTR) {
                    syslog(LOG_ERR, "Receive: %m");
                    continue;
            }

            if (pid == slsoftc.inProxy)
              sloutput();
            else if (pid == slsoftc.sc_rxmid)
              slbegin();
    }
}

The slstart() function determines how a proxy is triggered (the Receive()), and then calls the appropriate function.

If the proxy is triggered by: Then:
TCP/IP stack There's a packet ready for the interface to send out. The sloutput() function is called.
device Characters have come in from the serial line. The slbegin() function is called to collect the incoming characters.

sloutput()

The sloutput() function:

  1. Calls vp_getpkt() to get a packet from the stack. The packet returned by vp_getpkt() is packaged in a mbuf chain. As well, vp_getpkt() might return a ni_pktinfo structure that indicates the destination's IP address where the packet should go. In SLIP, it has only one destination, so sloutput() doesn't take this information.

    For details about the mbuf chain, see Unix Network Programming: Networking APIs: Sockets and XTI by W. Richard Stevens (Prentice-Hall, ISBN 0-13-490012-X).

  2. Checks the IP protocol to see if it requires compression.
  3. Sends out the whole packet within a SLIP FRAME.
  4. Calls vp_mfree() to free the mbuf.

Note: To reduce the cost of copying memory between the TCP/IP stack and your application, the TCP/IP stack uses shared memory to store all the mbufs and mclusters. Therefore, your application must allocate and free the mbufs by calling the appropriate virtual packet library function(s):

slbegin()

If data comes in from the serial line at the same time that slbegin() is called, slstart() uses dev_read() to read the data and slinput() to decode the data and put it in an mbuf chain. After a whole package is received, it calls vp_putpkt() to send it back to the TCP/IP stack.

The IP address is set by ifconfig after slattach is started.

Address conversion functions

Since shared memory is used, the address of an mbuf (pointers in an mbuf structure) in your application may differ from the mbuf in the TCP/IP stack. There are two functions in the virtual packet library that do address conversions between the TCP/IP stack and your application:


Note: If you use the virtual packet functions mentioned in the sloutput() example above, the library takes care of all the address conversions.

Using the VPI as an IP Filter

You can register a VPI as an IP Filter. An IP filter is a program that keeps a list of rules for inbound and/or outbound IP packets.

When you register yourself as a filter, the TCP/IP stack passes all of the incoming and outgoing IP packets to you. You can then decide if you want to drop them (block) or return them (pass through) to the TCP/IP Stack. You can also change the source and/or destination address in the packet (this is called Network Address Translation), and return it to the TCP/IP stack. The stack then passes the packet to the right interface (outbound) or to the right application (inbound).


Note: We recommend that you enable the TCP/IP Stack's gateway support. For more information, see the -d option of Tcpip.

The steps to use a VP interface as an IP filter are similar to the steps taken when using it as a network interface:

  1. Prepare a proxy.
  2. Call vp_attach() with the proxy and set the type to VP_FILTER to register a VPI as an IP filter.
  3. When the proxy is triggered, call vp_getpkt() to get a packet from the stack. In the case of an IP Filter, the ni_fltinfo structure is also returned. The structure contains information on:
  4. After you check the packet with your rule list, you can either drop it and call vp_mfree() to free the mbuf, or call vp_putpkt() to return it to the stack.

You can register multiple VPIs as IP filters. The first registered filter gets the packet first. After it returns the packet to the stack, the packet is passed on to the second filter, and so forth. After the packet passes though all of the filters, the stack passes it to the right interface (outbound) or to the right application (inbound).

If you register yourself as an IP filter, you can also pass extra flags to the stack with vp_putpkt(). This flag may be used to change the direction of a packet (i.e. turn an inbound packet to an outbound packet), or tell the stack to pass the packet to other filters.


[Previous] [Contents] [Index] [Next]