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:
The virtual packet library consists of these functions:
For descriptions of these functions, see the TCP/IP Libraries chapter.
The slattach utility attaches serial lines as network interfaces. It's also a good example of how to use the VPI.
You'll find the complete source for this example in /usr/demo/src/socket/slattach. |
The slattach.c program does the following:
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:
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:
Your application must set up the correct interface flag so that the TCP/IP stack knows which packet to give to the interface. |
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. |
The sloutput() function:
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).
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): |
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.
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:
If you use the virtual packet functions mentioned in the sloutput() example above, the library takes care of all the address conversions. |
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).
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:
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.