next up previous
Next: 5 Interacting with the Up: Simulation Environment Overview 15-441 Previous: 3 Building and running


4 The pbuf structure

A packet sent or received by an application is processed by several different layers in the network stack. In real BSD-style implementations, an mbuf structure is used for passing the packet between the different layers. In projects 2 and 3, you will be using a pbuf structure for building and passing packets between network stack layers. The pbuf structure is simplified version of the BSD mbuf.

The definition of the pbuf structure is the following:

    struct p_hdr {
            struct  pbuf *ph_next;    /* next buffer in chain */
            struct  pbuf *ph_nextpkt; /* next chain in queue/record */
            caddr_t ph_data;          /* location of data */
            int     ph_len;           /* amount of data in this mbuf */
            int     ph_type;          /* type of data in this mbuf */
            int     ph_flags;         /* flags; see below */
    };

    struct pbuf {
            struct p_hdr p_hdr;
            char         p_databuf[PHLEN];
    };
    #define p_next    p_hdr.ph_next
    #define p_nextpkt p_hdr.ph_nextpkt
    #define p_data    p_hdr.ph_data
    #define p_len     p_hdr.ph_len
    #define p_type    p_hdr.ph_type
    #define p_flags   p_hdr.ph_flags
    #define p_dat     p_databuf

pbuf's must be allocated and deallocated using the routines p_get() and p_free() declared in
$PDIR/include/pbuf.h. Since a pbuf contains less than 512 bytes of data (PHLEN is defined as 512 minus header length), an MTU-sized packet (1500 bytes in your projects) will consist of 4 pbuf structures linked together by the p_next field in each pbuf -- this is called a pbuf chain. The p_nextpkt field can be used to link multiple packets together on a queue. By convention, only the first pbuf in a pbuf chain should be used to link to another pbuf chain (through p_nextpkt).

Figure 3: A 48-byte IP packet spread out over 2 pbuf structures. There is a 20-byte IP header, an 8-byte UDP header, and 20-bytes of user data. The IP header starts at the beginning of the first pbuf's p_databuf, while the UDP header and data bytes start in the middle of the second pbuf's p_databuf. Placing data in the middle of p_databuf and modifying p_data to point to it is a clever way to leave space for headers, or to push and pop headers, without requiring additional pbufs.

\includegraphics[height=2.5in, keepaspectratio]{fig-pbufs.eps}

The field p_data points to the location where the packet data starts within the p_databuf[PHLEN] buffer. Why implement pbufs this way? Suppose your transport layer has built a UDP packet with 20 bytes of data and an 8-byte UDP header. Before this packet gets sent on the wire, it will have to go through netowrk and link layer processing. If you place the data at the beginning of the pbuf, the network layer will have to allocate a new pbuf in which to store the 20-byte IP header and prepend this pbuf to the packet. However, if you were clever enough to leave 20 bytes of space at the beginning of the p_databuf[] buffer, you could simply subtract 20 from the value of p_data and then copy the 20-byte IP header to the address indicated by this pointer. An example of a packet consisting of multiple pbuf structures is shown in Figure 3.

The field p_len is the length of data contained in the pbuf; it is not the total length of the packet. p_type is managed by the pbuf allocation code and p_flags is presently not used at all by the kernel.

In addition to the functions p_get() and p_free() mentioned above, there are some other functions that you may find useful for manipulating pbuf's. They are defined in $PDIR/include/pbuf.h. Some examples of these are:


next up previous
Next: 5 Interacting with the Up: Simulation Environment Overview 15-441 Previous: 3 Building and running