snoop_filter.c revision b127ac411761a3d8d642d9342d9cac2785e1faaa
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <string.h>
#include <stddef.h>
#include <netinet/if_ether.h>
#include <netdb.h>
#include <snoop.h>
#include "snoop_vlan.h"
#define IPV4_ONLY 0
#define IPV6_ONLY 1
#define IPV4_AND_IPV6 2
/*
* The following constants represent the offsets in bytes from the beginning
* of the IP(v6) header of the source and destination IP(v6) addresses.
* These are useful when generating filter code.
*/
#define IPV4_SRCADDR_OFFSET 12
#define IPV4_DSTADDR_OFFSET 16
#define IPV6_SRCADDR_OFFSET 8
#define IPV6_DSTADDR_OFFSET 24
#define MASKED_IPV4_VERS 0x40
#define MASKED_IPV6_VERS 0x60
/*
* Coding the constant below is tacky, but the compiler won't let us
* be more clever. E.g., &((struct ip *)0)->ip_xxx
*/
/*
* for 8 octets of overhead, and the common AppleTalk DDP Ethernet
* header is another 4 octets.
*
* The following constants represents the offsets in bytes from the beginning
* of the Ethernet payload to various parts of the DDP header.
*/
#define AT_DST_NET_OFFSET 12
#define AT_SRC_NET_OFFSET 14
#define AT_DST_NODE_OFFSET 16
#define AT_SRC_NODE_OFFSET 17
/*
* Offset for the source and destination zoneid in the ipnet header.
*/
#define IPNET_SRCZONE_OFFSET 8
#define IPNET_DSTZONE_OFFSET 16
int eaddr; /* need ethernet addr */
int opstack; /* operand stack depth */
/*
* These are the operators of the user-level filter.
* STOP ends execution of the filter expression and
* returns the truth value at the top of the stack.
* OP_LOAD_OCTET, OP_LOAD_SHORT and OP_LOAD_LONG pop
* an offset value from the stack and load a value of
* an appropriate size from the packet (octet, short or
* long). The offset is computed from a base value that
* may be set via the OP_OFFSET operators.
* OP_EQ, OP_NE, OP_GT, OP_GE, OP_LT, OP_LE pop two values
* from the stack and return the result of their comparison.
* OP_AND, OP_OR, OP_XOR pop two values from the stack and
* do perform a bitwise operation on them - returning a result
* to the stack. OP_NOT inverts the bits of the value on the
* stack.
* OP_BRFL and OP_BRTR branch to an offset in the code array
* depending on the value at the top of the stack: true (not 0)
* or false (0).
* OP_ADD, OP_SUB, OP_MUL, OP_DIV and OP_REM pop two values
* from the stack and perform arithmetic.
* The OP_OFFSET operators change the base from which the
* OP_LOAD operators compute their offsets.
* OP_OFFSET_ZERO sets the offset to zero - beginning of packet.
* OP_OFFSET_LINK sets the base to the first octet after
* the link (DLC) header. OP_OFFSET_IP, OP_OFFSET_TCP,
* and OP_OFFSET_UDP do the same for those headers - they
* set the offset base to the *end* of the header - not the
* beginning. The OP_OFFSET_RPC operator is a bit unusual.
* It points the base at the cached RPC header. For the
* purposes of selection, RPC reply headers look like call
* headers except for the direction value.
* OP_OFFSET_ETHERTYPE sets base according to the following
* algorithm:
* if the packet is not VLAN tagged, then set base to
* the ethertype field in the ethernet header
* else set base to the ethertype field of the VLAN header
* OP_OFFSET_POP restores the offset base to the value prior
* to the most recent OP_OFFSET call.
*/
enum optype {
OP_STOP = 0,
};
static char *opnames[] = {
"STOP",
"LOAD_OCTET",
"LOAD_SHORT",
"LOAD_LONG",
"LOAD_CONST",
"LOAD_LENGTH",
"EQ",
"NE",
"GT",
"GE",
"LT",
"LE",
"AND",
"OR",
"XOR",
"NOT",
"BRFL",
"BRTR",
"ADD",
"SUB",
"MUL",
"DIV",
"REM",
"OFFSET_POP",
"OFFSET_ZERO",
"OFFSET_ETHER",
"OFFSET_IP",
"OFFSET_TCP",
"OFFSET_UDP",
"OFFSET_RPC",
"OP_OFFSET_SLP",
"OFFSET_ETHERTYPE",
""
};
#define MAXOPS 1024
#define MAXSS 64
extern struct hostent *lgetipnodebyname(const char *, int, int, int *);
static void alternation();
static void codeprint();
static void emitop();
static void emitval();
static void expression();
static void optimize();
static void ethertype_match();
/*
* Get a ushort from a possibly unaligned character buffer.
*
* INPUTS: buffer - where the data is. Must be at least
* sizeof(uint16_t) bytes long.
* OUPUTS: An unsigned short that contains the data at buffer.
* No calls to ntohs or htons are done on the data.
*/
static uint16_t
{
/*
* ntohs is used only as a cheap way to flip the bits
* around on a little endian platform. The value will
* still be in host order or network order, depending on
* the order it was in when it was passed in.
*/
}
/*
* Returns the ULP for an IPv4 or IPv6 packet
* Assumes that the packet has already been checked to verify
* that it's either IPv4 or IPv6
*
* XXX Will need to be updated for AH and ESP
* XXX when IPsec is supported for v6.
*/
static uchar_t
{
case IPV4_VERSION:
return (IP_PROTO_OF(ip));
case IPV6_VERSION:
do {
switch (nxt) {
/*
* XXX Add IPsec headers here when supported for v6
* XXX (the AH will have a different size...)
*/
case IPPROTO_HOPOPTS:
case IPPROTO_ROUTING:
case IPPROTO_FRAGMENT:
case IPPROTO_DSTOPTS:
break;
default:
break;
}
} while (not_done);
return (nxt);
default:
break; /* shouldn't get here... */
}
return (0);
}
/*
* Returns the total IP header length.
* For v4, this includes any options present.
* For v6, this is the length of the IPv6 header plus
* any extension headers present.
*
* XXX Will need to be updated for AH and ESP
* XXX when IPsec is supported for v6.
*/
static int
{
int hdr_len;
case IPV4_VERSION:
return (IP_HDR_LEN(ip));
case IPV6_VERSION:
do {
switch (nxt) {
/*
* XXX Add IPsec headers here when supported for v6
* XXX (the AH will have a different size...)
*/
case IPPROTO_HOPOPTS:
case IPPROTO_ROUTING:
case IPPROTO_FRAGMENT:
case IPPROTO_DSTOPTS:
break;
default:
break;
}
} while (not_done);
return (len);
default:
break;
}
return (0); /* not IP */
}
static void
{
printf("User filter:\n");
else
switch (*op) {
case OP_LOAD_CONST:
case OP_BRTR:
case OP_BRFL:
op++;
if ((int)*op < 0)
printf("\t%2d: 0x%08x (%d)\n",
else
printf("\t%2d: %d (0x%08x)\n",
}
}
printf("\n");
}
/*
* Take a pass through the generated code and optimize
* branches. A branch true (BRTR) that has another BRTR
* at its destination can use the address of the destination
* BRTR. A BRTR that points to a BRFL (branch false) should
* point to the address following the BRFL.
* A similar optimization applies to BRFL operators.
*/
static void
{
switch (*op) {
case OP_LOAD_CONST:
op++;
break;
case OP_BRTR:
op++;
*op += 2;
break;
case OP_BRFL:
op++;
*op += 2;
break;
}
}
}
/*
* RPC packets are tough to filter.
* While the call packet has all the interesting
* info: program number, version, procedure etc,
* the reply packet has none of this information.
* If we want to do useful filtering based on this
* information then we have to stash the information
* from the call packet, and use the XID in the reply
* to find the stashed info. The stashed info is
* kept in a circular lifo, assuming that a call packet
* will be followed quickly by its reply.
*/
struct xid_entry {
unsigned x_xid; /* The XID (32 bits) */
unsigned x_dir; /* CALL or REPLY */
unsigned x_rpcvers; /* Protocol version (2) */
unsigned x_prog; /* RPC program number */
unsigned x_vers; /* RPC version number */
unsigned x_proc; /* RPC procedure number */
};
static struct xid_entry *
{
struct xid_entry *x;
return (x);
return (x);
return (NULL);
}
static void
{
struct xid_entry *x;
return;
x = xe++;
}
/*
* SLP can multicast requests, and recieve unicast replies in which
* neither the source nor destination port is identifiable as a SLP
* port. Hence, we need to do as RPC does, and keep track of packets we
* are interested in. For SLP, however, we use ports, not XIDs, and
* a smaller cache size is more efficient since every incoming packet
* needs to be checked.
*/
#define SLP_CACHE_SIZE 64
static int slp_index = 0;
/*
* Returns the index of dport in the table if found, otherwise -1.
*/
static int
int i;
if (!dport)
return (0);
for (i = slp_index; i >= 0; i--)
return (i);
}
return (i);
}
return (-1);
}
/* avoid redundancy due to multicast retransmissions */
return;
if (slp_index == SLP_CACHE_SIZE)
slp_index = 0;
}
/*
* This routine takes a packet and returns true or false
* according to whether the filter expression selects it
* or not.
* We assume here that offsets for short and long values
* are even - we may die with an alignment error if the
* CPU doesn't support odd addresses. Note that long
* values are loaded as two shorts so that 32 bit word
* alignment isn't important.
*
* IPv6 is a bit stickier to handle than IPv4...
*/
int
{
int newrpc = 0;
int off, header_size;
*sp = 1;
case OP_LOAD_OCTET:
return (0); /* packet too short */
break;
case OP_LOAD_SHORT:
return (0); /* packet too short */
break;
case OP_LOAD_LONG:
return (0); /* packet too short */
/*
* Handle 3 possible alignments
*/
case 0:
break;
case 2:
break;
case 1:
case 3:
break;
}
break;
case OP_LOAD_CONST:
return (0);
break;
case OP_LOAD_LENGTH:
return (0);
break;
case OP_EQ:
return (0);
sp--;
break;
case OP_NE:
return (0);
sp--;
break;
case OP_GT:
return (0);
sp--;
break;
case OP_GE:
return (0);
sp--;
break;
case OP_LT:
return (0);
sp--;
break;
case OP_LE:
return (0);
sp--;
break;
case OP_AND:
return (0);
sp--;
break;
case OP_OR:
return (0);
sp--;
break;
case OP_XOR:
return (0);
sp--;
break;
case OP_NOT:
break;
case OP_BRFL:
op++;
if (!*sp)
break;
case OP_BRTR:
op++;
if (*sp)
break;
case OP_ADD:
return (0);
sp--;
break;
case OP_SUB:
return (0);
sp--;
break;
case OP_MUL:
return (0);
sp--;
break;
case OP_DIV:
return (0);
sp--;
break;
case OP_REM:
return (0);
sp--;
break;
case OP_OFFSET_POP:
return (0);
}
break;
case OP_OFFSET_ZERO:
return (0);
break;
case OP_OFFSET_LINK:
return (0);
/*
* If the offset exceeds the packet length,
* we should not be interested in this packet...
* Just return 0.
*/
return (0);
}
break;
case OP_OFFSET_IP:
return (0);
return (0); /* not IP */
}
return (0); /* bad pkt */
}
break;
case OP_OFFSET_TCP:
return (0);
return (0); /* not IP */
}
return (0);
}
break;
case OP_OFFSET_UDP:
return (0);
return (0); /* not IP */
}
return (0);
}
break;
case OP_OFFSET_RPC:
return (0);
return (0);
*(++sp) = 0;
break;
}
switch (ip_proto_of(ip)) {
case IPPROTO_UDP:
sizeof (struct udphdr));
break;
case IPPROTO_TCP:
/*
* Need to skip an extra 4 for the xdr_rec
* field.
*/
break;
}
/*
* We need to have at least 24 bytes of a RPC
* packet to look at to determine the validity
* of it.
*/
return (0);
*(++sp) = 0;
break;
}
/* align */
return (0);
*(++sp) = 0;
break;
}
newrpc = 1;
return (0);
*(++sp) = 1;
} else {
return (0);
}
break;
case OP_OFFSET_SLP:
return (0);
*(++sp) = 0;
break;
}
switch (ip_proto_of(ip)) {
case IPPROTO_UDP:
/* align */
break;
case IPPROTO_TCP:
/* align */
break;
}
return (0);
*(++sp) = 0;
break;
}
return (0);
*(++sp) = 1;
break;
return (0);
*(++sp) = 1;
break;
}
/* else fallthrough to reject */
}
return (0);
*(++sp) = 0;
break;
case OP_OFFSET_ETHERTYPE:
/*
* Set base to the location of the ethertype as
* appropriate for this link type. Note that it's
* not called "ethertype" for every link type, but
* we need to call it something.
*/
return (0);
/*
* Below, we adjust the offset for unusual
* link-layer headers that may have the protocol
* type in a variable location beyond what was set
* above.
*/
case DL_ETHER:
case DL_CSMACD:
/*
* If this is a VLAN-tagged packet, we need
* to point to the ethertype field in the
* VLAN header. Move past the ethertype
* field in the ethernet header.
*/
base += (ENCAP_ETHERTYPE_OFF);
break;
}
/* Went too far, drop the packet */
return (0);
}
break;
}
}
return (*sp);
}
static void
{
}
static void
{
if (offset >= 0)
switch (len) {
case 1:
break;
case 2:
break;
case 4:
break;
}
}
/*
* Emit code to compare a field in
* the packet against a constant value.
*/
static void
{
}
static void
{
}
static void
{
int i;
for (i = 0; i < len; i += 4) {
if (i != 0)
}
}
/*
* Same as above except do the comparison
* after and'ing a mask value. Useful
* for comparing IP network numbers
*/
static void
{
}
/*
* Compare two zoneid's. The arg val passed in is stored in network
* byte order.
*/
static void
{
int i;
for (i = 0; i < sizeof (uint64_t) / 4; i++) {
if (i != 0)
}
}
/* Emit an operator into the code array */
static void
{
pr_err("expression too long");
}
/*
* Remove n operators recently emitted into
* the code array. Used by alternation().
*/
static void
{
}
/*
* Same as emitop except that we're emitting
* a value that's not an operator.
*/
static void
{
pr_err("expression too long");
}
/*
* Used to chain forward branches together
* for later resolution by resolve_chain().
*/
static uint_t
chain(int p)
{
emitval(p);
return (pos);
}
/*
* Proceed backward through the code array
* following a chain of forward references.
* At each reference install the destination
* branch offset.
*/
static void
{
uint_t n;
while (p) {
n = oplist[p];
p = n;
}
}
char *token;
/*
* This is the scanner. Each call returns the next
* token in the filter expression. A token is either:
* EOL: The end of the line - no more tokens.
* ALPHA: A name that begins with a letter and contains
* letters or digits, hyphens or underscores.
* NUMBER: A number. The value can be represented as
* a decimal value (1234) or an octal value
* that begins with zero (066) or a hex value
* that begins with 0x or 0X (0xff).
* FIELD: A name followed by a left square bracket.
* ADDR_IP: An IP address. Any sequence of digits
* separated by dots e.g. 109.104.40.13
* ADDR_ETHER: An ethernet address. Any sequence of hex
* digits separated by colons e.g. 8:0:20:0:76:39
* SPECIAL: A special character e.g. ">" or "(". The scanner
* correctly handles digraphs - two special characters
* that constitute a single token e.g. "==" or ">=".
* ADDR_IP6: An IPv6 address.
*
* ADDR_AT: An AppleTalk Phase II address. A sequence of two numbers
* separated by a dot.
*
* The current token is maintained in "token" and and its
* type in "tokentype". If tokentype is NUMBER then the
* value is held in "tokenval".
*/
static const char *namechars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.";
static const char *numchars = "0123456789abcdefABCDEFXx:.";
void
next()
{
static int savechar;
char *p;
colons = 0;
double_colon = 0;
if (*tkp == '\0') {
}
if (*token == '\0') {
return;
}
/* A token containing ':' cannot be ALPHA type */
if (*p == ':') {
colons++;
if (*(p+1) == ':')
double_colon++;
}
}
if (*tkp == '[') {
*tkp++ = '\0';
}
} else
/*
* RFC1123 states that host names may now start with digits. Need
* to change parser to account for this. Also, need to distinguish
* between 1.2.3.4 and 1.2.3.a where the first case is an IP address
* and the second is a domain name. 333aaa needs to be distinguished
* from 0x333aaa. The first is a host name and the second is a number.
*
* The (colons > 1) conditional differentiates between ethernet
* and IPv6 addresses, and an expression of the form base[expr:size],
* which can only contain one ':' character.
*/
if (*p == '.')
dots++;
else if (isalpha(*p))
alphas = 1;
}
if (colons > 1) {
} else {
}
} else if (dots) {
} else
if (*tkp == '[') {
*tkp++ = '\0';
}
}
} else
/*
* With the above check, if there are more
* characters after the last digit, assume
* that it is not a number.
*/
p = tkp;
base = 10;
if (*tkp == '0') {
base = 8;
tkp++;
base = 16;
}
tkp = p;
} else if (base == 16) {
"0123456789abcdefABCDEF");
tkp = p;
} else
/*
* handles the case of 0x so an error message
* is not printed. Treats 0x as 0.
*/
if (size == 2) {
tokenval = 0;
} else {
}
} else {
}
} else {
if (*tkp == '[') {
*tkp++ = '\0';
}
}
} else {
tkp++;
tkp++;
}
*tkp = '\0';
}
typedef struct match_type {
char *m_name;
int m_offset;
int m_size;
int m_value;
int m_depend;
} match_type_t;
static match_type_t ether_match_types[] = {
/*
* Table initialized assuming Ethernet data link headers.
* m_offset is an offset beyond the offset op, which is why
* the offset is zero for when snoop needs to check an ethertype.
*/
0, 0, 0, 0, 0, 0
};
static match_type_t ipnet_match_types[] = {
/*
* Table initialized assuming Ethernet data link headers.
* m_offset is an offset beyond the offset op, which is why
* the offset is zero for when snoop needs to check an ethertype.
*/
-1, OP_OFFSET_ETHERTYPE,
-1, OP_OFFSET_ETHERTYPE,
0, 0, 0, 0, 0, 0
};
static void
{
/*
* Note: this code assumes the above dependencies are
* not cyclic. This *should* always be true.
*/
}
/*
* Generate code based on the keyword argument.
* This word is looked up in the match_types table
* and checks a field within the packet for a given
* value e.g. ether or ip type field. The match
* can also have a dependency on another entry e.g.
* "tcp" requires that the packet also be "ip".
*/
static int
comparison(char *s)
{
unsigned int i, n_checks = 0;
case DL_ETHER:
break;
case DL_IPNET:
break;
default:
return (0);
}
continue;
n_checks++;
if (n_checks > 1)
}
return (n_checks > 0);
}
/*
* Generate code to match an IP address. The address
* may be supplied either as a hostname or in dotted format.
* For source packets both the IP source address and ARP
* src are checked.
* Note: we don't check packet type here - whether IP or ARP.
* It's possible that we'll do an improper match.
*/
static void
{
int m = 0, n = 0;
int h_addr_index;
int error_num = 0;
/*
* The addr4offset and addr6offset variables simplify the code which
* generates the address comparison filter. With these two variables,
* duplicate code need not exist for the TO and FROM case.
* A value of -1 describes the ANY case (TO and FROM).
*/
int addr4offset;
int addr6offset;
found_host = 0;
freehp = 1;
}
pr_err("couldn't resolve %s (try again later)",
hostname);
} else {
}
}
freehp = 1;
}
pr_err("couldn't resolve %s (try again later)",
hostname);
} else {
}
}
} else {
/* Some hostname i.e. tokentype is ALPHA */
switch (inet_type) {
case IPV4_ONLY:
/* Only IPv4 address is needed */
&error_num);
freehp = 1;
}
found_host = 1;
}
break;
case IPV6_ONLY:
/* Only IPv6 address is needed */
&error_num);
&error_num);
freehp = 1;
}
found_host = 1;
}
break;
case IPV4_AND_IPV6:
/* Both IPv4 and IPv6 are needed */
freehp = 1;
}
found_host = 1;
}
break;
default:
found_host = 0;
}
if (!found_host) {
pr_err("could not resolve %s (try again later)",
hostname);
} else {
}
}
}
switch (which) {
case TO:
break;
case FROM:
break;
case ANY:
addr4offset = -1;
addr6offset = -1;
break;
}
/*
* The code below generates the filter.
*/
n = chain(n);
h_addr_index = 0;
if (addr4offset == -1) {
*addr4ptr);
m = chain(m);
*addr4ptr);
} else {
}
m = chain(m);
}
}
if (m != 0) {
resolve_chain(m);
}
resolve_chain(n);
} else {
/* first pass: IPv4 addresses */
h_addr_index = 0;
if (IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
if (first) {
n = chain(n);
} else {
m = chain(m);
}
if (addr4offset == -1) {
addr4);
m = chain(m);
addr4);
} else {
}
}
}
/* second pass: IPv6 addresses */
h_addr_index = 0;
if (!IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
if (first) {
/*
* bypass check for IPv6 addresses
* when we have an IPv4 packet
*/
if (n != 0) {
m = chain(m);
m = chain(m);
resolve_chain(n);
n = 0;
}
n = chain(n);
} else {
m = chain(m);
}
if (addr6offset == -1) {
16, *addr6ptr);
m = chain(m);
16, *addr6ptr);
} else {
*addr6ptr);
}
}
}
if (m != 0) {
resolve_chain(m);
}
resolve_chain(n);
}
/* only free struct hostent returned by getipnodebyname() */
if (freehp) {
}
}
/*
* Match on zoneid. The arg zone passed in is in network byte order.
*/
static void
{
switch (which) {
case TO:
break;
case FROM:
break;
case ANY:
}
}
/*
* Generate code to match an AppleTalk address. The address
* must be given as two numbers with a dot between
*
*/
static void
{
uint_t m, n;
switch (which) {
case TO:
m = chain(0);
resolve_chain(m);
break;
case FROM:
m = chain(0);
resolve_chain(m);
break;
case ANY:
m = chain(0);
resolve_chain(m);
n = chain(0);
m = chain(0);
resolve_chain(m);
resolve_chain(n);
break;
}
}
/*
* Compare ethernet addresses. The address may
* be provided either as a hostname or as a
* 6 octet colon-separated address.
*/
static void
{
int to_offset, from_offset;
int m;
/*
* is determinable; if not, retreat early.
*/
case DL_ETHER:
to_offset = 0;
break;
case DL_IB:
/*
* If an ethernet address is attempted to be used
* on an IPoIB interface, flag error. Link address
* based filtering is unsupported on IPoIB, so there
* is no ipibaddr_match() or parsing support for IPoIB
* 20 byte link addresses.
*/
pr_err("filter option unsupported on media");
break;
case DL_FDDI:
from_offset = 7;
to_offset = 1;
break;
default:
/*
* Where do we find "ether" address for FDDI & TR?
* XXX can improve? ~sparker
*/
load_const(1);
return;
}
if (ether_hostton(hostname, &e))
if (!arp_for_ether(hostname, &e))
pr_err("cannot obtain ether addr for %s",
hostname);
ep = &e;
}
switch (which) {
case TO:
m = chain(0);
resolve_chain(m);
break;
case FROM:
m = chain(0);
resolve_chain(m);
break;
case ANY:
m = chain(0);
resolve_chain(m);
break;
}
}
static void
ethertype_match(int val)
{
/*
* If the user is interested in ethertype VLAN,
* then we need to set the offset to the beginning of the packet.
* But if the user is interested in another ethertype,
* such as IPv4, then we need to take into consideration
* the fact that the packet might be VLAN tagged.
*/
if (val != ETHERTYPE_VLAN) {
/*
* OP_OFFSET_ETHERTYPE puts us at the ethertype
* field whether or not there is a VLAN tag,
* so ether_offset goes to zero if we get here.
*/
ether_offset = 0;
} else {
}
}
}
}
static void
ipnettype_match(int val)
{
}
/*
* Match a network address. The host part
* is masked out. The network address may
* be supplied either as a netname or in
* IP dotted format. The mask to be used
* for the comparison is assumed from the
* address format (see comment below).
*/
static void
{
uint_t m;
} else {
}
/*
* Left justify the address and figure
* out a mask based on the supplied address.
* Set the mask according to the number of zero
* low-order bytes.
* Note: this works only for whole octet masks.
*/
if (addr) {
}
}
switch (which) {
case TO:
break;
case FROM:
break;
case ANY:
m = chain(0);
resolve_chain(m);
break;
}
}
/*
* Match either a UDP or TCP port number.
* The port number may be provided either as
* the port number itself (2049).
*/
static void
{
} else {
}
switch (which) {
case TO:
break;
case FROM:
break;
case ANY:
m = chain(0);
resolve_chain(m);
break;
}
}
/*
* Generate code to match packets with a specific
* RPC program number. If the progname is a name
* as extra qualifiers.
*/
static void
{
uint_t m, n;
} else {
}
n = chain(0);
m = chain(0);
if (vers >= 0) {
m = chain(m);
}
if (proc >= 0) {
m = chain(m);
}
switch (which) {
case TO:
m = chain(m);
break;
case FROM:
m = chain(m);
break;
}
resolve_chain(m);
resolve_chain(n);
}
/*
* Generate code to parse a field specification
* and load the value of the field from the packet
* onto the operand stack.
* The field offset may be specified relative to the
* beginning of the ether header, IP header, UDP header,
* or TCP header. An optional size specification may
* be provided following a colon. If no size is given
* one byte is assumed e.g.
*
* ether[0] The first byte of the ether header
* ip[2:2] The second 16 bit field of the IP header
*/
static void
{
int size = 1;
int s;
if (EQ("ether"))
else
pr_err("invalid field type");
next();
s = opstack;
expression();
if (opstack != s + 1)
pr_err("invalid field offset");
opstack--;
if (*token == ':') {
next();
pr_err("field size expected");
pr_err("field size invalid");
next();
}
if (*token != ']')
pr_err("right bracket expected");
}
/*
* Check that the operand stack
* contains n arguments
*/
static void
checkstack(int numargs)
{
}
static void
primary()
{
int m, m2, s;
for (;;) {
load_field();
opstack++;
next();
break;
}
if (comparison(token)) {
opstack++;
next();
break;
}
next();
s = opstack;
primary();
checkstack(s + 1);
break;
}
if (EQ("(")) {
next();
s = opstack;
expression();
checkstack(s + 1);
if (!EQ(")"))
pr_err("right paren expected");
next();
}
next();
continue;
}
next();
continue;
}
if (EQ("ether")) {
eaddr = 1;
next();
continue;
}
if (EQ("proto")) {
next();
pr_err("IP proto type expected");
opstack++;
next();
continue;
}
if (EQ("broadcast")) {
/*
* Be tricky: FDDI ether dst address begins at
* byte one. Since the address is really six
* bytes long, this works for FDDI & ethernet.
* XXX - Token ring?
*/
pr_err("filter option unsupported on media");
opstack++;
next();
break;
}
if (EQ("multicast")) {
/* XXX Token ring? */
pr_err("filter option unsupported on media");
} else {
}
opstack++;
next();
break;
}
if (EQ("decnet")) {
/* XXX Token ring? */
load_const(0x6000);
m = chain(0);
load_const(0x6009);
resolve_chain(m);
} else {
load_const(0x6000);
m = chain(0);
load_const(0x6009);
resolve_chain(m);
}
opstack++;
next();
break;
}
if (EQ("vlan-id")) {
next();
pr_err("vlan id expected");
m = chain(0);
resolve_chain(m);
opstack++;
next();
break;
}
if (EQ("apple")) {
/*
* Appletalk also appears in 802.2
* packets, so check for the ethertypes
* at offset 12 and 20 in the MAC header.
*/
m = chain(0);
m = chain(m);
m = chain(m);
resolve_chain(m);
opstack++;
next();
break;
}
if (EQ("vlan")) {
opstack++;
next();
break;
}
m = chain(0);
m = chain(m);
compare_value(0, 4,
compare_value(0, 4,
resolve_chain(m);
opstack++;
next();
break;
}
if (EQ("dhcp6")) {
m = chain(0);
m = chain(m);
resolve_chain(m);
opstack++;
next();
break;
}
if (EQ("ethertype")) {
next();
pr_err("ether type expected");
opstack++;
next();
break;
}
if (EQ("pppoe")) {
opstack++;
next();
break;
}
if (EQ("inet")) {
next();
if (EQ("host"))
next();
opstack++;
next();
break;
}
if (EQ("inet6")) {
next();
if (EQ("host"))
next();
opstack++;
next();
break;
}
if (EQ("length")) {
opstack++;
next();
break;
}
if (EQ("less")) {
next();
pr_err("packet length expected");
opstack++;
next();
break;
}
if (EQ("greater")) {
next();
pr_err("packet length expected");
opstack++;
next();
break;
}
if (EQ("nofrag")) {
m = chain(0);
resolve_chain(m);
opstack++;
next();
break;
}
if (EQ("dstnet"))
else if (EQ("srcnet"))
next();
opstack++;
next();
break;
}
if (EQ("dstport"))
else if (EQ("srcport"))
next();
opstack++;
next();
break;
}
if (EQ("rpc")) {
char savetoken[32];
next();
next();
if (*token == ',') {
next();
pr_err("version number expected");
next();
}
if (*token == ',') {
next();
pr_err("proc number expected");
next();
}
opstack++;
break;
}
if (EQ("slp")) {
/* filter out TCP handshakes */
emitval(52);
emitval(2);
m = chain(0);
resolve_chain(m);
opstack++;
next();
break;
}
if (EQ("ldap")) {
opstack++;
next();
break;
}
break;
}
if (EQ("zone")) {
next();
pr_err("zoneid expected");
opstack++;
next();
break;
}
if (EQ("gateway")) {
next();
m = chain(0);
resolve_chain(m);
opstack++;
next();
}
tokentype == ADDR_ETHER) {
next();
} else {
}
eaddr = 0;
opstack++;
next();
break;
}
opstack++;
next();
break;
}
break; /* unknown token */
}
}
struct optable {
char *op_tok;
};
static struct optable
mulops[] = {
"*", OP_MUL,
"/", OP_DIV,
"%", OP_REM,
"&", OP_AND,
"", OP_STOP,
};
static struct optable
addops[] = {
"+", OP_ADD,
"-", OP_SUB,
"|", OP_OR,
"^", OP_XOR,
"", OP_STOP,
};
static struct optable
compareops[] = {
"==", OP_EQ,
"=", OP_EQ,
"!=", OP_NE,
">", OP_GT,
">=", OP_GE,
"<", OP_LT,
"<=", OP_LE,
"", OP_STOP,
};
/*
* Using the table, find the operator
* that corresponds to the token.
* Return 0 if not found.
*/
static int
{
}
return (0);
}
static void
expr_mul()
{
int op;
int s = opstack;
primary();
next();
primary();
checkstack(s + 2);
opstack--;
}
}
static void
expr_add()
{
expr_mul();
next();
expr_mul();
checkstack(s + 2);
opstack--;
}
}
static void
{
expr_add();
next();
expr_add();
checkstack(s + 2);
opstack--;
}
}
/*
* Alternation ("and") is difficult because
* an implied "and" is acknowledge between
* two adjacent primaries. Just keep calling
* the lower-level expression routine until
* no value is added to the opstack.
*/
static void
{
int m = 0;
int s = opstack;
expr_compare();
checkstack(s + 1);
for (;;) {
if (EQ("and"))
next();
m = chain(m);
expr_compare();
if (opstack != s + 2)
break;
opstack--;
}
unemit(2);
resolve_chain(m);
}
static void
{
int m = 0;
int s = opstack;
alternation();
m = chain(m);
next();
alternation();
checkstack(s + 2);
opstack--;
}
resolve_chain(m);
}
/*
* Take n args from the argv list
* and concatenate them into a single string.
*/
char *
{
int i, len;
char *str, *p;
/* First add the lengths of all the strings */
len = 0;
for (i = 0; i < argc; i++)
/* allocate the big string */
pr_err("no mem");
p = str;
/*
* Concat the strings into the big
* string using a space as separator
*/
for (i = 0; i < argc; i++) {
p += strlen(p);
*p++ = ' ';
}
*--p = '\0';
return (str);
}
/*
* Take the expression in the string "expr"
* and compile it into the code array.
* Print the generated code if the print
* arg is set.
*/
void
{
pr_err("no mem");
next();
expression();
pr_err("invalid expression");
if (print)
codeprint();
}
/*
* Lookup hostname in the arp cache.
*/
{
struct sockaddr_in *sin;
int error_num;
int s;
return (B_FALSE);
}
if (s < 0) {
return (B_FALSE);
}
close(s);
return (B_FALSE);
}
close(s);
return (B_TRUE);
}