This appendix describes the compatibility between io-net and io-pkt.
For information about converting drivers, see the “Porting an io-net driver to io-pkt” technote.
The previous generation of the QNX Neutrino networking stack (io-net) was designed based on a modular approach. It served its purpose in the past by allowing users to separate protocols and drivers, but this came at the expense of incurring a significant amount of overhead when converting to a particular protocol's domain from io-net and vice versa.
Note that io-pkt and the new utilities and daemons aren't backward-compatible with io-net.
Both io-net and io-pkt can co-exist on the same system. The updated socket library provided with io-pkt is compatible with io-net. This lets you run both io-net and io-pkt simultaneously.
The reverse isn't true; if you use the io-net version of the socket library with io-pkt, unresolved symbols will occur when you attempt to use the io-pkt configuration utilities (e.g. ifconfig). |
We've updated the following binaries for io-pkt:
The following io-net binaries are known to have compatibility issues with io-pkt:
/* * Example demonstrating a common pitfall with SIOCGIFCONF handling. */ #include <sys/sockio.h> #include <net/if.h> #include <malloc.h> #include <stdlib.h> #include <err.h> #include <ifaddrs.h> void gifconf(int); void gifaddrs(int); int main(void) { int s; if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) err(EXIT_FAILURE, "socket"); gifconf(s); /* Old code often used SIOCGIFCONF ioctl */ gifaddrs(s); /* New code should use getifaddrs() */ close(s); return 0; } void gifconf(int s) { struct ifconf ifc; struct ifreq *inext, *iend, *icur; size_t size; size = 4096; ifc.ifc_len = size; if ((ifc.ifc_buf = malloc(ifc.ifc_len)) == NULL) err(EXIT_FAILURE, "malloc"); if (ioctl(s, SIOCGIFCONF, &ifc) == -1) err(EXIT_FAILURE, "SIOCGIFCONF"); if (ifc.ifc_len >= size) { /* realloc and try again */ errx(EXIT_FAILURE, "SIOCGIFCONF: buf too small"); } inext = ifc.ifc_req; iend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); for (;;) { icur = inext; #if 0 /* * Broken code. This would happen to work for most cases * because previously: * * sizeof(struct sockaddr) + IFNAMSIZ == sizeof(struct ifreq) * * Under this scenario the two 'if' cases in the working * case below work out to the same thing. */ inext = (struct ifreq *) ((char *)inext + inext->ifr_addr.sa_len + IFNAMSIZ); #else /* This will work against old / new libsocket */ if (inext->ifr_addr.sa_len + IFNAMSIZ > sizeof(struct ifreq)) inext = (struct ifreq *) ((char *)inext + inext->ifr_addr.sa_len + IFNAMSIZ); else inext++; #endif if (inext > iend) break; /* process icur */ } free(ifc.ifc_buf); } void gifaddrs(int s) { struct ifaddrs *ifaddrs, *ifap; if (getifaddrs(&ifaddrs) == -1) err(EXIT_FAILURE, "getifaddrs"); for (ifap = ifaddrs; ifap != NULL; ifap = ifap->ifa_next) { continue; } freeifaddrs(ifaddrs); }
If you compile the test case against the old headers, the SIOCGIFCONF ioctl() will produce the expected results in all environments: old / new stack, old / new libc. If you compile it against the io-pkt headers, the SIOCGIFCONF command will work only with the io-pkt stack and the 6.4 libc. In either case, there's no dependency on any version of libsocket.
The getifaddrs() call will work everywhere and is recommended for new code.
You can run both io-net and io-pkt simultaneously on the same target if the relevant utilities and daemons for each stack are present. Here are some specific issues you should be aware of:
io-pkt -d pcnet pci=0 io-net -i1 -dpcnet pci=1 -ptcpip prefix=/alt
Note that the io-net versions of the utilities must be present on the target (assumed, for this example, to have been placed in a separate directory) and run with the io-net stack. For example:
SOCK=/alt /io-net/ifconfig en0 192.168.1.2 SOCK=/alt /io-net/inetd
The io-pkt networking stack doesn't support the following features:
We recommend that you use pfil hooks to rewrite your io-net filter to work in io-pkt. Here are the basic steps:
struct _iopkt_lsm_entry IOPKT_LSM_ENTRY_SYM(mod) = IOPKT_LSM_ENTRY_SYM_INIT(mod_entry);
Information about the interface that the packet was received on is contained in the ifp pointer (defined in usr/include/net/if.h). For example, the external interface name is in ifp->if_xname; you can use this to determine where the packet came from.
The cell, endpoint, and iface parameters are essentially wrapped up in the ifp pointer.
You can find information about using mbuf buffers in many places on the web. The header file that covers the io-pkt mbuf support is in $QNX_TARGET/usr/include/sys/mbuf.h.
In io-pkt, all buffer allocation is handled explicitly by the stack middleware. This means that any element requiring a buffer goes directly to the middleware to get it, and anything freeing a buffer (e.g. the driver) puts it directly back into the middleware buffer pools. This makes things easier to deal with, because you now no longer have to track and manage your own buffer pools as an endpoint. As soon as the code is finished with the buffer, it simply performs an m_freem() of the buffer which places it back in the general pool.
There is one downside to the global buffer implementation. You can't create a thread using pthread_create() that allocates or frees a buffer from the stack, because the thread has to modify internal stack structures. The locking implemented within io-pkt is optimized to reduce thread context-switch times, and this means that non-stack threads can't lock-protect these structures. Instead, the stack provides its own thread-creation function (defined in trunk/sys/nw_thread.h):
int nw_pthread_create(pthread_t *tidp, pthread_attr_t *attrp, void *(*funcp)(void *), void *arg, int flags, int (*init_func)(void *), void *init_arg);
The first four arguments are the standard arguments to pthread_create(); the last three are specific to io-pkt. You can find a fairly simplistic example of how to use this function in net/ppp_tty.c.