Selectively removes upstream data messages on a Stream.
#include <stropts.h>
#include <sys/pfmod.h>
ioctl(fd, I_PUSH, "pfmod");
The pfmod module implements a programmable packet filter facility that may be pushed over any stream. Every data message that pfmod receives on its read side is subjected to a filter program. If the filter program accepts a message, it will be passed along upstream, and will otherwise be freed. If no filter program has been set (as is the case when pfmod is first pushed), all messages are accepted. Non-data messages (for example, M_FLUSH, M_PCPROTO, M_IOCACK) are never examined and always accepted. The write side is not filtered.
Data messages are defined as either M_PROTO or M_DATA. If an M_PROTO message is received, pfmod will skip over all the leading blocks until it finds an M_DATA block. If none is found, the message is accepted. The M_DATA portion of the message is then made contiguous with pullupmsg(), if necessary, to ensure the data area referenced by the filter program can be accessed in a single mblk_t.
The following ioctls are defined for this module. All other ioctls are passed downstream without examination.
PFIOCSETF
Install a new filter program, replacing any previous program. It uses the following data structure:
typedef struct packetfilt {
uchar Pf_Priority;
uchar Pf_FilterLen;
ushort Pf_Filter[MAXFILTERS];
} pfilter_t;
Pf_Priority is currently ignored, and should be set to zero. Pf_FilterLen indicates the number of shortwords in the Pf_Filter array. Pf_Filter is an array of shortwords that comprise the filter program. See "Filters" for details on how to write filter programs.
This ioctl may be issued either transparently or as an I_STR. It will return 0 on success, or -1 on failure, and set errno to one of:
Value | Description |
---|---|
ERANGE | The length of the M_IOCTL message data was not exactly size of (pfilter_t). The data structure is not variable length, although the filter program is. |
EFAULT | The ioctl argument points out of bounds. |
A filter program consists of a linear array of shortword instructions. These instructions operate upon a stack of shortwords. Flow of control is strictly linear; there are no branches or loops. When the filter program completes, the top of the stack is examined. If it is non-zero, or if the stack is empty, the packet being examined is passed upstream (accepted), otherwise the packet is freed (rejected).
Instructions are composed of three portions: push command PF_CMD(), argument PF_ARG(), and operation PF_OP(). Each instruction optionally pushes a shortword onto the stack, then optionally performs a binary operation on the top two elements on the stack, leaving its result on the stack. If there are not at least two elements on the stack, the operation will immediately fail and the packet will be rejected. The argument portion is used only by certain push commands, as documented below.
The following push commands are defined:
Command | Description |
---|---|
PF_NOPUSH | Nothing is pushed onto the stack. |
PF_PUSHZERO | Pushes 0x0000. |
PF_PUSHONE | Pushes 0x0001. |
PF_PUSHFFFF | Pushes 0xffff. |
PF_PUSHFF00 | Pushes 0xff00. |
PF_PUSH00FF | Pushes 0x00ff. |
PF_PUSHLIT | Pushes the next shortword in the filter program as literal data. Execution resumes with the next shortword after the literal data. |
PF_PUSHWORD+N | Pushes shortword N of the message onto the data stack. N must be in the range 0-255, as enforced by the macro PF_ARG(). |
The following operations are defined. Each operation pops the top two elements from the stack, and pushes the result of the operation onto the stack. The operations below are described in terms of v1 and v2. The top of stack is popped into v2, then the new top of stack is popped into v1. The result of v1 op v2 is then pushed onto the stack.
Operation | Description |
---|---|
PF_NOP | The stack is unchanged; nothing is popped. |
PF_EQ | v1 == v2 |
PF_NEQ | v1 != v2 |
PF_LT | v1 < v2 |
PF_LE | v1 <= v2 |
PF_GT | v1 > v2 |
PF_GE | v1 >= v2 |
PF_AND | v1 & v2; bitwise |
PF_OR | v1 | v2; bitwise |
PF_XOR | v1 ^ v2; bitwise |
The remaining operations are "short-circuit" operations. If the condition checked for is found, then the filter program terminates immediately, either accepting or rejecting the packet as specified, without examining the top of stack. If the condition is not found, the filter program continues. These operators do not push any result onto the stack.
Operation | Description |
---|---|
PF_COR | If v1 == v2, accept. |
PF_CNOR | If v1 == v2, reject. |
PF_CAND | If v1 != v2, reject. |
PF_CNAND | If v1 != v2, accept. |
If an unknown push command or operation is specified, the filter program terminates immediately and the packet is rejected.
Before using pfmod, it must be loaded into the kernel. This may be accomplished with the strload command, using the following syntax:
strload -m pfmod
This command will load the pfmod into the kernel and make it available to I_PUSH. Note that attempting to I_PUSH pfmod before loading it will result in an EINVAL error code.
The following program fragment will push pfmod on a stream, then program it to only accept messages with an Ethertype of 0x8137. This example assumes the stream is a promiscuous DLPI ethernet stream (see dlpi for details).
#include <stddef.h>
#include <sys/types.h>
#include <netinet/if_ether.h>
#define scale(x) ((x)/sizeof(ushort))
setfilter(int fd)
{
pfilter_t filter;
ushort *fp, offset;
if (ioctl(fd, I_PUSH, "pfmod"))
return -1;
offset = scale(offsetof(struct ether_header, ether_type));
fp = filter.Pf_Filter;
/* the filter program */
*fp++ = PF_PUSHLIT;
*fp++ = 0x8137;
*fp++ = PF_PUSHWORD + offset;
*fp++ = PF_EQ;
filter.Pf_FilterLen = fp - filter.Pf_Filter;
if (ioctl(fd, PFIOCSETF, &filter))
return -1;
return 0;
}
This program may be shortened by combining the operation with the push command:
*fp++ = PF_PUSHLIT;
*fp++ = 0x8137;
*fp++ = (PF_PUSHWORD + offset) | PF_EQ;
The following filter will accept 802.3 frames addressed to either the Netware raw sap 0xff or the 802.2 sap 0xe0:
offset = scale(offsetof(struct ie3_hdr, llc));
*fp++ = PF_PUSHWORD + offset; /* get ssap, dsap */
*fp++ = PF_PUSH00FF | PF_AND; /* keep only dsap */
*fp++ = PF_PUSH00FF | PF_COR; /* is dsap == 0xff? */
*fp++ = PF_PUSHWORD + offset; /* get ssap, dsap again */
*fp++ = PF_PUSH00FF | PF_AND; /* keep only dsap */
*fp++ = PF_PUSHLIT | PF_CAND; /* is dsap == 0xe0? */
*fp++ = 0x00e0;
Note the use of PF_COR in this example. If the dsap is 0xff, then the frame is accepted immediately, without continuing the filter program.