pfmod - STREAMS Packet Filter Module


#include <sys/pfmod.h>

ioctl(fd, IPUSH, "pfmod");


pfmod is a STREAMS module that subjects messages arriving on its read
queue to a packet filter and passes only those messages that the filter
accepts on to its upstream neighbor. Such filtering can be very useful
for user-level protocol implementations and for networking monitoring
programs that wish to view only specific types of events.

Read-side Behavior
pfmod applies the current packet filter to all M_DATA and M_PROTO
messages arriving on its read queue. The module prepares these messages
for examination by first skipping over all leading M_PROTO message blocks
to arrive at the beginning of the message's data portion. If there is no
data portion, pfmod accepts the message and passes it along to its
upstream neighbor. Otherwise, the module ensures that the part of the
message's data that the packet filter might examine lies in contiguous
memory, calling the pullupmsg(9F) utility routine if necessary to force
contiguity. (Note: this action destroys any sharing relationships that
the subject message might have had with other messages.) Finally, it
applies the packet filter to the message's data, passing the entire
message upstream to the next module if the filter accepts, and discarding
the message otherwise. See PACKET FILTERS below for details on how the
filter works.

If there is no packet filter yet in effect, the module acts as if the
filter exists but does nothing, implying that all incoming messages are
accepted. The ioctls section below describes how to associate a packet
filter with an instance of pfmod.

pfmod passes all other messages through unaltered to its upper neighbor.

Write-side Behavior
pfmod intercepts M_IOCTL messages for the ioctl described below. The
module passes all other messages through unaltered to its lower neighbor.


pfmod responds to the following ioctl.

This ioctl directs the module to replace its current packet
filter, if any, with the filter specified by the struct
packetfilt pointer named by its final argument. This
structure is defined in <sys/pfmod.h> as:

struct packetfilt {
uchar_t Pf_Priority; /* priority of filter */
uchar_t Pf_FilterLen; /* length of filter cmd list */
ushort_t Pf_Filter[ENMAXFILTERS]; /* filter command list */

The Pf_Priority field is included only for compatibility with other
packet filter implementations and is otherwise ignored. The packet filter
itself is specified in the Pf_Filter array as a sequence of two-byte
commands, with the Pf_FilterLen field giving the number of commands in
the sequence. This implementation restricts the maximum number of
commands in a filter (ENMAXFILTERS) to 255. The next section describes
the available commands and their semantics.


A packet filter consists of the filter command list length (in units of
ushort_ts), and the filter command list itself. (The priority field
mentioned above is ignored in this implementation.) Each filter command
list specifies a sequence of actions that operate on an internal stack of
ushort_ts ("shortwords") or an offset register. The offset register is
initially zero. Each shortword of the command list specifies an action
and a binary operator. Using _n_ as shorthand for the next shortword of
the instruction stream and _%oreg_ for the offset register, the list of
actions is:

ENF_PUSHLIT 2 Push _n_ on the stack.
ENF_PUSHZERO 1 Push zero on the stack.
ENF_PUSHONE 1 Push one on the stack.
ENF_PUSHFFFF 1 Push 0xFFFF on the stack.
ENF_PUSHFF00 1 Push 0xFF00 on the stack.
ENF_PUSH00FF 1 Push 0x00FF on the stack.
ENF_LOAD_OFFSET 2 Load _n_ into _%oreg_.
ENF_BRTR 2 Branch forward _n_ shortwords if
the top element of the stack is
ENF_BRFL 2 Branch forward _n_ shortwords if
the top element of the stack is zero.
ENF_POP 1 Pop the top element from the stack.
ENF_PUSHWORD+m 1 Push the value of shortword (_m_ +
_%oreg_) of the packet onto the stack.

The binary operators can be from the set {ENF_EQ, ENF_NEQ, ENF_LT,
ENF_LE, ENF_GT,ENF_GE, ENF_AND, ENF_OR, ENF_XOR} which operate on the top
two elements of the stack and replace them with its result.

When both an action and operator are specified in the same shortword, the
action is performed followed by the operation.

The binary operator can also be from the set {ENF_COR, ENF_CAND,
ENF_CNOR, ENF_CNAND}. These are "short-circuit" operators, in that they
terminate the execution of the filter immediately if the condition they
are checking for is found, and continue otherwise. All pop two elements
from the stack and compare them for equality; ENF_CAND returns false if
the result is false; ENF_COR returns true if the result is true;
ENF_CNAND returns true if the result is false; ENF_CNOR returns false if
the result is true. Unlike the other binary operators, these four do not
leave a result on the stack, even if they continue.

The short-circuit operators should be used when possible, to reduce the
amount of time spent evaluating filters. When they are used, you should
also arrange the order of the tests so that the filter will succeed or
fail as soon as possible; for example, checking the IP destination field
of a UDP packet is more likely to indicate failure than the packet type

The special action ENF_NOPUSH and the special operator ENF_NOP can be
used to only perform the binary operation or to only push a value on the
stack. Since both are (conveniently) defined to be zero, indicating only
an action actually specifies the action followed by ENF_NOP, and
indicating only an operation actually specifies ENF_NOPUSH followed by
the operation.

After executing the filter command list, a non-zero value (true) left on
top of the stack (or an empty stack) causes the incoming packet to be
accepted and a zero value (false) causes the packet to be rejected. (If
the filter exits as the result of a short-circuit operator, the top-of-
stack value is ignored.) Specifying an undefined operation or action in
the command list or performing an illegal operation or action (such as
pushing a shortword offset past the end of the packet or executing a
binary operator with fewer than two shortwords on the stack) causes a
filter to reject the packet.


The packet filter module is not dependent on any particular device driver
or module but is commonly used with datalink drivers such as the Ethernet
driver. If the underlying datalink driver supports the Data Link
Provider Interface (DLPI) message set, the appropriate STREAMS DLPI
messages must be issued to attach the stream to a particular hardware
device and bind a datalink address to the stream before the underlying
driver will route received packets upstream. Refer to the DLPI Version 2
specification for details on this interface.

The reverse ARP daemon program may use code similar to the following
fragment to construct a filter that rejects all but RARP packets. That
is, it accepts only packets whose Ethernet type field has the value
ETHERTYPE_REVARP. The filter works whether a VLAN is configured or not.

struct ether_header eh; /* used only for offset values */
struct packetfilt pf;
register ushort_t *fwp = pf.Pf_Filter;
ushort_t offset;
int fd;
* Push packet filter streams module.
if (ioctl(fd, I_PUSH, "pfmod") < 0)

* Set up filter. Offset is the displacement of the Ethernet
* type field from the beginning of the packet in units of
* ushort_ts.
offset = ((uint_t) &eh.ether_type - (uint_t) &eh.ether_dhost) /
sizeof (us_short);
*fwp++ = ENF_PUSHWORD + offset;
*fwp++ = ENF_PUSHLIT;
*fwp++ = htons(ETHERTYPE_VLAN);
*fwp++ = ENF_EQ;
*fwp++ = ENF_BRFL;
*fwp++ = 3; /* If this isn't ethertype VLAN, don't change oreg */
*fwp++ = 2; /* size of the VLAN tag in words */
*fwp++ = ENF_POP;
*fwp++ = ENF_PUSHWORD + offset;
*fwp++ = ENF_PUSHLIT;
*fwp++ = htons(ETHERTYPE_REVARP);
*fwp++ = ENF_EQ;
pf.Pf_FilterLen = fwp - &pf.PF_Filter[0];

This filter can be abbreviated by taking advantage of the ability to
combine actions and operations:

*fwp++ = ENF_PUSHWORD + offset;
*fwp++ = ENF_PUSHLIT | ENF_EQ;
*fwp++ = htons(ETHERTYPE_REVARP);
*fwp++ = htons(ETHERTYPE_VLAN);
*fwp++ = ENF_BRFL | ENF_NOP;
*fwp++ = 3;
*fwp++ = 2;
*fwp++ = ENF_POP | ENF_NOP;
*fwp++ = ENF_PUSHWORD + offset;
*fwp++ = ENF_PUSHLIT | ENF_EQ;
*fwp++ = htons(ETHERTYPE_REVARP);


bufmod(4M), dlpi(4P), pullupmsg(9F)

June 18, 2006 PFMOD(4M)