#include <stdio.h>
#include <stddef.h>
#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/isa_defs.h>
#include <sys/socket.h>
#include <sys/vlan.h>
#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <inet/ip.h>
#include <inet/ip6.h>
#include <netdb.h>
#include <rpc/rpc.h>
#include <setjmp.h>
#include <sys/pfmod.h>
#include "snoop.h"
#include "snoop_vlan.h"
extern struct Pf_ext_packetfilt pf;
static ushort_t *pfp;
jmp_buf env;
#define EQ(val) (strcmp(token, val) == 0)
#define IPV4_ONLY 0
#define IPV6_ONLY 1
#define IPV4_AND_IPV6 2
typedef struct {
int transport_protocol;
int network_protocol;
int offset;
} transport_table_t;
typedef struct network_table {
char *nmt_name;
int nmt_val;
} network_table_t;
static network_table_t ether_network_mapping_table[] = {
{ "pup", ETHERTYPE_PUP },
{ "ip", ETHERTYPE_IP },
{ "arp", ETHERTYPE_ARP },
{ "rarp", ETHERTYPE_REVARP },
{ "at", ETHERTYPE_AT },
{ "aarp", ETHERTYPE_AARP },
{ "vlan", ETHERTYPE_VLAN },
{ "ip6", ETHERTYPE_IPV6 },
{ "slow", ETHERTYPE_SLOW },
{ "ppoed", ETHERTYPE_PPPOED },
{ "ppoes", ETHERTYPE_PPPOES },
{ "NULL", -1 }
};
static network_table_t ib_network_mapping_table[] = {
{ "pup", ETHERTYPE_PUP },
{ "ip", ETHERTYPE_IP },
{ "arp", ETHERTYPE_ARP },
{ "rarp", ETHERTYPE_REVARP },
{ "at", ETHERTYPE_AT },
{ "aarp", ETHERTYPE_AARP },
{ "vlan", ETHERTYPE_VLAN },
{ "ip6", ETHERTYPE_IPV6 },
{ "slow", ETHERTYPE_SLOW },
{ "ppoed", ETHERTYPE_PPPOED },
{ "ppoes", ETHERTYPE_PPPOES },
{ "NULL", -1 }
};
static network_table_t ipnet_network_mapping_table[] = {
{ "ip", (DL_IPNETINFO_VERSION << 8 | AF_INET) },
{ "ip6", (DL_IPNETINFO_VERSION << 8 | AF_INET6) },
{ "NULL", -1 }
};
static transport_table_t ether_transport_mapping_table[] = {
{IPPROTO_TCP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_TCP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_UDP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_UDP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_OSPF, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_OSPF, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_SCTP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_SCTP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_ICMP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_ICMPV6, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_ENCAP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_ESP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_ESP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_AH, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_AH, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{-1, 0, 0}
};
static transport_table_t ipnet_transport_mapping_table[] = {
{IPPROTO_TCP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_TCP, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_UDP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_UDP, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_OSPF, (DL_IPNETINFO_VERSION << 8 | AF_INET),
IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_OSPF, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_SCTP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_SCTP, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_ICMP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_ICMPV6, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_ENCAP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_ESP, (DL_IPNETINFO_VERSION << 8 | AF_INET),
IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_ESP, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_AH, (DL_IPNETINFO_VERSION << 8 | AF_INET),
IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_AH, (DL_IPNETINFO_VERSION << 8 | AF_INET6),
IPV6_TYPE_HEADER_OFFSET},
{-1, 0, 0}
};
static transport_table_t ib_transport_mapping_table[] = {
{IPPROTO_TCP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_TCP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_UDP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_UDP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_OSPF, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_OSPF, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_SCTP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_SCTP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_ICMP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_ICMPV6, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_ENCAP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_ESP, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_ESP, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{IPPROTO_AH, ETHERTYPE_IP, IPV4_TYPE_HEADER_OFFSET},
{IPPROTO_AH, ETHERTYPE_IPV6, IPV6_TYPE_HEADER_OFFSET},
{-1, 0, 0}
};
typedef struct datalink {
uint_t dl_type;
void (*dl_match_fn)(uint_t datatype);
transport_table_t *dl_trans_map_tbl;
network_table_t *dl_net_map_tbl;
int dl_link_header_len;
int dl_link_type_offset;
int dl_link_dest_offset;
int dl_link_src_offset;
int dl_link_addr_len;
} datalink_t;
datalink_t dl;
#define IPV4_SRCADDR_OFFSET (dl.dl_link_header_len + 12)
#define IPV4_DSTADDR_OFFSET (dl.dl_link_header_len + 16)
#define IPV6_SRCADDR_OFFSET (dl.dl_link_header_len + 8)
#define IPV6_DSTADDR_OFFSET (dl.dl_link_header_len + 24)
#define IPNET_SRCZONE_OFFSET 16
#define IPNET_DSTZONE_OFFSET 20
static int inBrace = 0, inBraceOR = 0;
static int foundOR = 0;
extern void next();
static void pf_expression();
static void pf_check_vlan_tag(uint_t offset);
static void pf_clear_offset_register();
static void pf_emit_load_offset(uint_t offset);
static void pf_match_ethertype(uint_t ethertype);
static void pf_match_ipnettype(uint_t type);
static void pf_match_ibtype(uint_t type);
static void pf_check_transport_protocol(uint_t transport_protocol);
static void pf_compare_value_mask_generic(int offset, uint_t len,
uint_t val, int mask, uint_t op);
static void pf_matchfn(const char *name);
static void *last_offset_operation = (void*)pf_clear_offset_register;
static void
pf_emit(ushort_t x)
{
if (pfp > &pf.Pf_Filter[PF_MAXFILTERS - 1])
longjmp(env, 1);
*pfp++ = x;
}
static void
pf_codeprint(ushort_t *code, int len)
{
ushort_t *pc;
ushort_t *plast = code + len;
int op, action;
if (len > 0) {
printf("Kernel Filter:\n");
}
for (pc = code; pc < plast; pc++) {
printf("\t%3d: ", pc - code);
op = *pc & 0xfc00;
action = *pc & 0x3ff;
switch (action) {
case ENF_PUSHLIT:
printf("PUSHLIT ");
break;
case ENF_PUSHZERO:
printf("PUSHZERO ");
break;
#ifdef ENF_PUSHONE
case ENF_PUSHONE:
printf("PUSHONE ");
break;
#endif
#ifdef ENF_PUSHFFFF
case ENF_PUSHFFFF:
printf("PUSHFFFF ");
break;
#endif
#ifdef ENF_PUSHFF00
case ENF_PUSHFF00:
printf("PUSHFF00 ");
break;
#endif
#ifdef ENF_PUSH00FF
case ENF_PUSH00FF:
printf("PUSH00FF ");
break;
#endif
case ENF_LOAD_OFFSET:
printf("LOAD_OFFSET ");
break;
case ENF_BRTR:
printf("BRTR ");
break;
case ENF_BRFL:
printf("BRFL ");
break;
case ENF_POP:
printf("POP ");
break;
}
if (action >= ENF_PUSHWORD)
printf("PUSHWORD %d ", action - ENF_PUSHWORD);
switch (op) {
case ENF_EQ:
printf("EQ ");
break;
case ENF_LT:
printf("LT ");
break;
case ENF_LE:
printf("LE ");
break;
case ENF_GT:
printf("GT ");
break;
case ENF_GE:
printf("GE ");
break;
case ENF_AND:
printf("AND ");
break;
case ENF_OR:
printf("OR ");
break;
case ENF_XOR:
printf("XOR ");
break;
case ENF_COR:
printf("COR ");
break;
case ENF_CAND:
printf("CAND ");
break;
case ENF_CNOR:
printf("CNOR ");
break;
case ENF_CNAND:
printf("CNAND ");
break;
case ENF_NEQ:
printf("NEQ ");
break;
}
if (action == ENF_PUSHLIT ||
action == ENF_LOAD_OFFSET ||
action == ENF_BRTR ||
action == ENF_BRFL) {
pc++;
printf("\n\t%3d: %d (0x%04x)", pc - code, *pc, *pc);
}
printf("\n");
}
}
static void
pf_compare_value(int offset, uint_t len, uint_t val)
{
if (offset == -1)
pr_err("filter option unsupported on media");
switch (len) {
case 1:
pf_emit(ENF_PUSHWORD + offset / 2);
#if defined(_BIG_ENDIAN)
if (offset % 2)
#else
if (!(offset % 2))
#endif
{
#ifdef ENF_PUSH00FF
pf_emit(ENF_PUSH00FF | ENF_AND);
#else
pf_emit(ENF_PUSHLIT | ENF_AND);
pf_emit(0x00FF);
#endif
pf_emit(ENF_PUSHLIT | ENF_EQ);
pf_emit(val);
} else {
#ifdef ENF_PUSHFF00
pf_emit(ENF_PUSHFF00 | ENF_AND);
#else
pf_emit(ENF_PUSHLIT | ENF_AND);
pf_emit(0xFF00);
#endif
pf_emit(ENF_PUSHLIT | ENF_EQ);
pf_emit(val << 8);
}
break;
case 2:
pf_emit(ENF_PUSHWORD + offset / 2);
pf_emit(ENF_PUSHLIT | ENF_EQ);
pf_emit((ushort_t)val);
break;
case 4:
pf_emit(ENF_PUSHWORD + offset / 2);
pf_emit(ENF_PUSHLIT | ENF_EQ);
#if defined(_BIG_ENDIAN)
pf_emit(val >> 16);
#elif defined(_LITTLE_ENDIAN)
pf_emit(val & 0xffff);
#else
#error One of _BIG_ENDIAN and _LITTLE_ENDIAN must be defined
#endif
pf_emit(ENF_PUSHWORD + (offset / 2) + 1);
pf_emit(ENF_PUSHLIT | ENF_EQ);
#if defined(_BIG_ENDIAN)
pf_emit(val & 0xffff);
#else
pf_emit(val >> 16);
#endif
pf_emit(ENF_AND);
break;
}
}
static void
pf_compare_value_v6(int offset, uint_t len, struct in6_addr val)
{
int i;
for (i = 0; i < len; i += 2) {
pf_emit(ENF_PUSHWORD + offset / 2 + i / 2);
pf_emit(ENF_PUSHLIT | ENF_EQ);
pf_emit(*(uint16_t *)&val.s6_addr[i]);
if (i != 0)
pf_emit(ENF_AND);
}
}
static void
pf_compare_value_mask(int offset, uint_t len, uint_t val, int mask)
{
pf_compare_value_mask_generic(offset, len, val, mask, ENF_EQ);
}
static void
pf_compare_value_mask_neq(int offset, uint_t len, uint_t val, int mask)
{
pf_compare_value_mask_generic(offset, len, val, mask, ENF_NEQ);
}
static void
pf_compare_value_mask_generic(int offset, uint_t len, uint_t val, int mask,
uint_t op)
{
if (offset == -1)
pr_err("filter option unsupported on media");
switch (len) {
case 1:
pf_emit(ENF_PUSHWORD + offset / 2);
#if defined(_BIG_ENDIAN)
if (offset % 2)
#else
if (!offset % 2)
#endif
{
pf_emit(ENF_PUSHLIT | ENF_AND);
pf_emit(mask & 0x00ff);
pf_emit(ENF_PUSHLIT | op);
pf_emit(val);
} else {
pf_emit(ENF_PUSHLIT | ENF_AND);
pf_emit((mask << 8) & 0xff00);
pf_emit(ENF_PUSHLIT | op);
pf_emit(val << 8);
}
break;
case 2:
pf_emit(ENF_PUSHWORD + offset / 2);
pf_emit(ENF_PUSHLIT | ENF_AND);
pf_emit(htons((ushort_t)mask));
pf_emit(ENF_PUSHLIT | op);
pf_emit(htons((ushort_t)val));
break;
case 4:
pf_emit(ENF_PUSHWORD + offset / 2);
pf_emit(ENF_PUSHLIT | ENF_AND);
pf_emit(htons((ushort_t)((mask >> 16) & 0xffff)));
pf_emit(ENF_PUSHLIT | op);
pf_emit(htons((ushort_t)((val >> 16) & 0xffff)));
pf_emit(ENF_PUSHWORD + (offset / 2) + 1);
pf_emit(ENF_PUSHLIT | ENF_AND);
pf_emit(htons((ushort_t)(mask & 0xffff)));
pf_emit(ENF_PUSHLIT | op);
pf_emit(htons((ushort_t)(val & 0xffff)));
pf_emit(ENF_AND);
break;
}
}
static void
pf_compare_zoneid(int offset, uint32_t val)
{
int i;
for (i = 0; i < sizeof (uint32_t) / 2; i ++) {
pf_emit(ENF_PUSHWORD + offset / 2 + i);
pf_emit(ENF_PUSHLIT | ENF_EQ);
pf_emit(((uint16_t *)&val)[i]);
if (i != 0)
pf_emit(ENF_AND);
}
}
static void
pf_ipaddr_match(enum direction which, char *hostname, int inet_type)
{
bool_t found_host;
uint_t *addr4ptr;
uint_t addr4;
struct in6_addr *addr6ptr;
int h_addr_index;
struct hostent *hp = NULL;
int error_num = 0;
boolean_t first = B_TRUE;
int pass = 0;
int i;
int addr4offset;
int addr6offset;
found_host = 0;
if (tokentype == ADDR_IP) {
hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
if (hp == NULL) {
if (error_num == TRY_AGAIN) {
pr_err("could not resolve %s (try again later)",
hostname);
} else {
pr_err("could not resolve %s", hostname);
}
}
inet_type = IPV4_ONLY;
} else if (tokentype == ADDR_IP6) {
hp = getipnodebyname(hostname, AF_INET6, 0, &error_num);
if (hp == NULL) {
if (error_num == TRY_AGAIN) {
pr_err("could not resolve %s (try again later)",
hostname);
} else {
pr_err("could not resolve %s", hostname);
}
}
inet_type = IPV6_ONLY;
} else if (tokentype == ALPHA) {
switch (inet_type) {
case IPV4_ONLY:
hp = getipnodebyname(hostname, AF_INET, 0, &error_num);
if (hp != NULL) {
found_host = 1;
}
break;
case IPV6_ONLY:
hp = getipnodebyname(hostname, AF_INET6, 0, &error_num);
if (hp != NULL) {
found_host = 1;
}
break;
case IPV4_AND_IPV6:
hp = getipnodebyname(hostname, AF_INET6,
AI_ALL | AI_V4MAPPED, &error_num);
if (hp != NULL) {
found_host = 1;
}
break;
default:
found_host = 0;
}
if (!found_host) {
if (error_num == TRY_AGAIN) {
pr_err("could not resolve %s (try again later)",
hostname);
} else {
pr_err("could not resolve %s", hostname);
}
}
} else {
pr_err("unknown token type: %s", hostname);
}
if (hp == NULL)
return;
switch (which) {
case TO:
addr4offset = IPV4_DSTADDR_OFFSET;
addr6offset = IPV6_DSTADDR_OFFSET;
break;
case FROM:
addr4offset = IPV4_SRCADDR_OFFSET;
addr6offset = IPV6_SRCADDR_OFFSET;
break;
case ANY:
addr4offset = -1;
addr6offset = -1;
break;
}
if (hp->h_addrtype == AF_INET) {
pf_matchfn("ip");
if (dl.dl_type == DL_ETHER)
pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
h_addr_index = 0;
addr4ptr = (uint_t *)hp->h_addr_list[h_addr_index];
while (addr4ptr != NULL) {
if (addr4offset == -1) {
pf_compare_value(IPV4_SRCADDR_OFFSET, 4,
*addr4ptr);
if (h_addr_index != 0)
pf_emit(ENF_OR);
pf_compare_value(IPV4_DSTADDR_OFFSET, 4,
*addr4ptr);
pf_emit(ENF_OR);
} else {
pf_compare_value(addr4offset, 4,
*addr4ptr);
if (h_addr_index != 0)
pf_emit(ENF_OR);
}
addr4ptr = (uint_t *)hp->h_addr_list[++h_addr_index];
}
pf_emit(ENF_AND);
} else {
h_addr_index = 0;
addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
first = B_TRUE;
while (addr6ptr != NULL) {
if (IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
if (first) {
pf_matchfn("ip");
if (dl.dl_type == DL_ETHER) {
pf_check_vlan_tag(
ENCAP_ETHERTYPE_OFF/2);
}
pass++;
}
IN6_V4MAPPED_TO_INADDR(addr6ptr,
(struct in_addr *)&addr4);
if (addr4offset == -1) {
pf_compare_value(IPV4_SRCADDR_OFFSET, 4,
addr4);
if (!first)
pf_emit(ENF_OR);
pf_compare_value(IPV4_DSTADDR_OFFSET, 4,
addr4);
pf_emit(ENF_OR);
} else {
pf_compare_value(addr4offset, 4,
addr4);
if (!first)
pf_emit(ENF_OR);
}
if (first)
first = B_FALSE;
}
addr6ptr = (struct in6_addr *)
hp->h_addr_list[++h_addr_index];
}
if (!first) {
pf_emit(ENF_AND);
}
h_addr_index = 0;
addr6ptr = (struct in6_addr *)hp->h_addr_list[h_addr_index];
first = B_TRUE;
while (addr6ptr != NULL) {
if (!IN6_IS_ADDR_V4MAPPED(addr6ptr)) {
if (first) {
pf_matchfn("ip6");
if (dl.dl_type == DL_ETHER) {
pf_check_vlan_tag(
ENCAP_ETHERTYPE_OFF / 2);
}
pass++;
}
if (addr6offset == -1) {
pf_compare_value_v6(IPV6_SRCADDR_OFFSET,
16, *addr6ptr);
if (!first)
pf_emit(ENF_OR);
pf_compare_value_v6(IPV6_DSTADDR_OFFSET,
16, *addr6ptr);
pf_emit(ENF_OR);
} else {
pf_compare_value_v6(addr6offset, 16,
*addr6ptr);
if (!first)
pf_emit(ENF_OR);
}
if (first)
first = B_FALSE;
}
addr6ptr = (struct in6_addr *)
hp->h_addr_list[++h_addr_index];
}
if (!first) {
pf_emit(ENF_AND);
}
if (pass == 2) {
pf_emit(ENF_OR);
}
}
freehostent(hp);
}
static void
pf_compare_address(int offset, uint_t len, uchar_t *addr)
{
uint32_t val;
uint16_t sval;
boolean_t didone = B_FALSE;
if (offset == -1)
pr_err("filter option unsupported on media");
while (len > 0) {
if (len >= 4) {
(void) memcpy(&val, addr, 4);
pf_compare_value(offset, 4, val);
addr += 4;
offset += 4;
len -= 4;
} else if (len >= 2) {
(void) memcpy(&sval, addr, 2);
pf_compare_value(offset, 2, sval);
addr += 2;
offset += 2;
len -= 2;
} else {
pf_compare_value(offset++, 1, *addr++);
len--;
}
if (didone)
pf_emit(ENF_AND);
didone = B_TRUE;
}
}
static void
pf_etheraddr_match(enum direction which, char *hostname)
{
struct ether_addr e, *ep = NULL;
if (isxdigit(*hostname))
ep = ether_aton(hostname);
if (ep == NULL) {
if (ether_hostton(hostname, &e))
if (!arp_for_ether(hostname, &e))
pr_err("cannot obtain ether addr for %s",
hostname);
ep = &e;
}
pf_clear_offset_register();
switch (which) {
case TO:
pf_compare_address(dl.dl_link_dest_offset, dl.dl_link_addr_len,
(uchar_t *)ep);
break;
case FROM:
pf_compare_address(dl.dl_link_src_offset, dl.dl_link_addr_len,
(uchar_t *)ep);
break;
case ANY:
pf_compare_address(dl.dl_link_dest_offset, dl.dl_link_addr_len,
(uchar_t *)ep);
pf_compare_address(dl.dl_link_src_offset, dl.dl_link_addr_len,
(uchar_t *)ep);
pf_emit(ENF_OR);
break;
}
}
static void
pf_netaddr_match(enum direction which, char *netname)
{
uint_t addr;
uint_t mask = 0xff000000;
struct netent *np;
if (isdigit(*netname)) {
addr = inet_network(netname);
} else {
np = getnetbyname(netname);
if (np == NULL)
pr_err("net %s not known", netname);
addr = np->n_net;
}
if (addr) {
while ((addr & ~mask) != 0) {
mask |= (mask >> 8);
}
}
pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
switch (which) {
case TO:
pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask);
break;
case FROM:
pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask);
break;
case ANY:
pf_compare_value_mask(IPV4_SRCADDR_OFFSET, 4, addr, mask);
pf_compare_value_mask(IPV4_DSTADDR_OFFSET, 4, addr, mask);
pf_emit(ENF_OR);
break;
}
}
static void
pf_match_zone(enum direction which, uint32_t zoneid)
{
if (dl.dl_type != DL_IPNET)
pr_err("zone filter option unsupported on media");
switch (which) {
case TO:
pf_compare_zoneid(IPNET_DSTZONE_OFFSET, zoneid);
break;
case FROM:
pf_compare_zoneid(IPNET_SRCZONE_OFFSET, zoneid);
break;
case ANY:
pf_compare_zoneid(IPNET_SRCZONE_OFFSET, zoneid);
pf_compare_zoneid(IPNET_DSTZONE_OFFSET, zoneid);
pf_emit(ENF_OR);
break;
}
}
static void
pf_emit_load_offset(uint_t offset)
{
pf_emit(ENF_LOAD_OFFSET | ENF_NOP);
pf_emit(offset);
}
static void
pf_clear_offset_register()
{
if (last_offset_operation != (void*)pf_clear_offset_register) {
pf_emit_load_offset(0);
last_offset_operation = (void*)pf_clear_offset_register;
}
}
static void
pf_check_vlan_tag(uint_t offset)
{
static uint_t last_offset = 0;
if ((interface->mac_type == DL_ETHER ||
interface->mac_type == DL_CSMACD) &&
(last_offset_operation != (void*)pf_check_vlan_tag ||
last_offset != offset)) {
pf_clear_offset_register();
pf_compare_value(dl.dl_link_type_offset, 2,
htons(ETHERTYPE_VLAN));
pf_emit(ENF_BRFL | ENF_NOP);
pf_emit(3);
pf_emit_load_offset(offset);
pf_emit(ENF_POP | ENF_NOP);
last_offset_operation = (void*)pf_check_vlan_tag;
last_offset = offset;
}
}
static void
pf_match_ethertype(uint_t ethertype)
{
if (ethertype == ETHERTYPE_VLAN)
pf_clear_offset_register();
else
pf_check_vlan_tag(2);
pf_compare_value(dl.dl_link_type_offset, 2, htons(ethertype));
}
static void
pf_match_ipnettype(uint_t type)
{
pf_compare_value(dl.dl_link_type_offset, 2, htons(type));
}
static void
pf_match_ibtype(uint_t type)
{
pf_compare_value(dl.dl_link_type_offset, 2, htons(type));
}
static void
pf_check_transport_protocol(uint_t transport_protocol)
{
int i;
uint_t number_of_matches = 0;
for (i = 0; dl.dl_trans_map_tbl[i].transport_protocol != -1; i++) {
if (transport_protocol ==
(uint_t)dl.dl_trans_map_tbl[i].transport_protocol) {
number_of_matches++;
dl.dl_match_fn(dl.dl_trans_map_tbl[i].network_protocol);
pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
pf_compare_value(dl.dl_trans_map_tbl[i].offset +
dl.dl_link_header_len, 1,
transport_protocol);
pf_emit(ENF_AND);
if (number_of_matches > 1) {
pf_emit(ENF_OR);
}
}
}
}
static void
pf_matchfn(const char *proto)
{
int i;
for (i = 0; dl.dl_net_map_tbl[i].nmt_val != -1; i++) {
if (strcmp(proto, dl.dl_net_map_tbl[i].nmt_name) == 0) {
dl.dl_match_fn(dl.dl_net_map_tbl[i].nmt_val);
break;
}
}
}
static void
pf_primary()
{
for (;;) {
if (tokentype == FIELD)
break;
if (EQ("ip")) {
pf_matchfn("ip");
opstack++;
next();
break;
}
if (EQ("ip6")) {
pf_matchfn("ip6");
opstack++;
next();
break;
}
if (EQ("pppoe")) {
pf_matchfn("pppoe");
pf_match_ethertype(ETHERTYPE_PPPOES);
pf_emit(ENF_OR);
opstack++;
next();
break;
}
if (EQ("pppoed")) {
pf_matchfn("pppoed");
opstack++;
next();
break;
}
if (EQ("pppoes")) {
pf_matchfn("pppoes");
opstack++;
next();
break;
}
if (EQ("arp")) {
pf_matchfn("arp");
opstack++;
next();
break;
}
if (EQ("vlan")) {
pf_matchfn("vlan");
pf_compare_value_mask_neq(VLAN_ID_OFFSET, 2,
0, VLAN_ID_MASK);
pf_emit(ENF_AND);
opstack++;
next();
break;
}
if (EQ("vlan-id")) {
next();
if (tokentype != NUMBER)
pr_err("VLAN ID expected");
pf_matchfn("vlan-id");
pf_compare_value_mask(VLAN_ID_OFFSET, 2, tokenval,
VLAN_ID_MASK);
pf_emit(ENF_AND);
opstack++;
next();
break;
}
if (EQ("rarp")) {
pf_matchfn("rarp");
opstack++;
next();
break;
}
if (EQ("tcp")) {
pf_check_transport_protocol(IPPROTO_TCP);
opstack++;
next();
break;
}
if (EQ("udp")) {
pf_check_transport_protocol(IPPROTO_UDP);
opstack++;
next();
break;
}
if (EQ("ospf")) {
pf_check_transport_protocol(IPPROTO_OSPF);
opstack++;
next();
break;
}
if (EQ("sctp")) {
pf_check_transport_protocol(IPPROTO_SCTP);
opstack++;
next();
break;
}
if (EQ("icmp")) {
pf_check_transport_protocol(IPPROTO_ICMP);
opstack++;
next();
break;
}
if (EQ("icmp6")) {
pf_check_transport_protocol(IPPROTO_ICMPV6);
opstack++;
next();
break;
}
if (EQ("ip-in-ip")) {
pf_check_transport_protocol(IPPROTO_ENCAP);
opstack++;
next();
break;
}
if (EQ("esp")) {
pf_check_transport_protocol(IPPROTO_ESP);
opstack++;
next();
break;
}
if (EQ("ah")) {
pf_check_transport_protocol(IPPROTO_AH);
opstack++;
next();
break;
}
if (EQ("(")) {
inBrace++;
next();
pf_expression();
if (EQ(")")) {
if (inBrace)
inBraceOR--;
inBrace--;
next();
}
break;
}
if (EQ("to") || EQ("dst")) {
dir = TO;
next();
continue;
}
if (EQ("from") || EQ("src")) {
dir = FROM;
next();
continue;
}
if (EQ("ether")) {
eaddr = 1;
next();
continue;
}
if (EQ("inet")) {
next();
if (EQ("host"))
next();
if (tokentype != ALPHA && tokentype != ADDR_IP)
pr_err("host/IPv4 addr expected after inet");
pf_ipaddr_match(dir, token, IPV4_ONLY);
opstack++;
next();
break;
}
if (EQ("inet6")) {
next();
if (EQ("host"))
next();
if (tokentype != ALPHA && tokentype != ADDR_IP6)
pr_err("host/IPv6 addr expected after inet6");
pf_ipaddr_match(dir, token, IPV6_ONLY);
opstack++;
next();
break;
}
if (EQ("proto")) {
next();
if (tokentype != NUMBER)
pr_err("IP proto type expected");
pf_check_vlan_tag(ENCAP_ETHERTYPE_OFF/2);
pf_compare_value(
IPV4_TYPE_HEADER_OFFSET + dl.dl_link_header_len, 1,
tokenval);
opstack++;
next();
break;
}
if (EQ("broadcast")) {
pf_clear_offset_register();
pf_compare_value(dl.dl_link_dest_offset, 4, 0xffffffff);
opstack++;
next();
break;
}
if (EQ("multicast")) {
pf_clear_offset_register();
pf_compare_value_mask(
dl.dl_link_dest_offset, 1, 0x01, 0x01);
opstack++;
next();
break;
}
if (EQ("ethertype")) {
next();
if (tokentype != NUMBER)
pr_err("ether type expected");
pf_match_ethertype(tokenval);
opstack++;
next();
break;
}
if (EQ("net") || EQ("dstnet") || EQ("srcnet")) {
if (EQ("dstnet"))
dir = TO;
else if (EQ("srcnet"))
dir = FROM;
next();
pf_netaddr_match(dir, token);
dir = ANY;
opstack++;
next();
break;
}
if (EQ("zone")) {
next();
if (tokentype != NUMBER)
pr_err("zoneid expected after inet");
pf_match_zone(dir, BE_32((uint32_t)(tokenval)));
opstack++;
next();
break;
}
if (EQ("and") || EQ("or") ||
EQ("not") || EQ("decnet") || EQ("apple") ||
EQ("length") || EQ("less") || EQ("greater") ||
EQ("port") || EQ("srcport") || EQ("dstport") ||
EQ("rpc") || EQ("gateway") || EQ("nofrag") ||
EQ("bootp") || EQ("dhcp") || EQ("dhcp6") ||
EQ("slp") || EQ("ldap")) {
break;
}
if (EQ("host") || EQ("between") ||
tokentype == ALPHA ||
tokentype == ADDR_IP ||
tokentype == ADDR_IP6 ||
tokentype == ADDR_ETHER) {
if (EQ("host") || EQ("between"))
next();
if (eaddr || tokentype == ADDR_ETHER) {
pf_etheraddr_match(dir, token);
} else if (tokentype == ALPHA) {
pf_ipaddr_match(dir, token, IPV4_AND_IPV6);
} else if (tokentype == ADDR_IP) {
pf_ipaddr_match(dir, token, IPV4_ONLY);
} else {
pf_ipaddr_match(dir, token, IPV6_ONLY);
}
dir = ANY;
eaddr = 0;
opstack++;
next();
break;
}
break;
}
}
static void
pf_alternation()
{
int s = opstack;
pf_primary();
for (;;) {
if (EQ("and"))
next();
pf_primary();
if (opstack != s + 2)
break;
pf_emit(ENF_AND);
opstack--;
}
}
static void
pf_expression()
{
pf_alternation();
while (EQ("or") || EQ(",")) {
if (inBrace)
inBraceOR++;
else
foundOR++;
next();
pf_alternation();
pf_emit(ENF_OR);
opstack--;
}
}
int
pf_compile(char *e, int print)
{
char *argstr;
char *sav_str, *ptr, *sav_ptr;
int inBr = 0, aheadOR = 0;
argstr = strdup(e);
sav_str = e;
tkp = argstr;
dir = ANY;
pfp = &pf.Pf_Filter[0];
if (setjmp(env)) {
return (0);
}
if (interface->mac_type == DL_ETHER) {
dl.dl_type = DL_ETHER;
dl.dl_match_fn = pf_match_ethertype;
dl.dl_trans_map_tbl = ether_transport_mapping_table;
dl.dl_net_map_tbl = ether_network_mapping_table;
dl.dl_link_header_len = 14;
dl.dl_link_type_offset = 12;
dl.dl_link_dest_offset = 0;
dl.dl_link_src_offset = 6;
dl.dl_link_addr_len = 6;
}
if (interface->mac_type == DL_IB) {
dl.dl_type = DL_IB;
dl.dl_link_header_len = 4;
dl.dl_link_type_offset = 0;
dl.dl_link_dest_offset = dl.dl_link_src_offset = -1;
dl.dl_link_addr_len = 20;
dl.dl_match_fn = pf_match_ibtype;
dl.dl_trans_map_tbl = ib_transport_mapping_table;
dl.dl_net_map_tbl = ib_network_mapping_table;
}
if (interface->mac_type == DL_IPNET) {
dl.dl_type = DL_IPNET;
dl.dl_link_header_len = 24;
dl.dl_link_type_offset = 0;
dl.dl_link_dest_offset = dl.dl_link_src_offset = -1;
dl.dl_link_addr_len = -1;
dl.dl_match_fn = pf_match_ipnettype;
dl.dl_trans_map_tbl = ipnet_transport_mapping_table;
dl.dl_net_map_tbl = ipnet_network_mapping_table;
}
next();
pf_expression();
if (tokentype != EOL) {
if (foundOR || inBraceOR) {
return (0);
} else {
sav_ptr = (char *)((uintptr_t)sav_str +
(uintptr_t)sav_tkp - (uintptr_t)argstr);
ptr = sav_ptr;
while (*ptr != '\0') {
switch (*ptr) {
case '(':
inBr++;
break;
case ')':
inBr--;
break;
case 'o':
case 'O':
if ((*(ptr + 1) == 'R' ||
*(ptr + 1) == 'r') && !inBr)
aheadOR = 1;
break;
case ',':
if (!inBr)
aheadOR = 1;
break;
}
ptr++;
}
if (!aheadOR) {
pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0];
pf.Pf_Priority = 5;
if (print) {
pf_codeprint(&pf.Pf_Filter[0],
pf.Pf_FilterLen);
}
compile(sav_ptr, print);
return (2);
} else
return (0);
}
}
pf.Pf_FilterLen = pfp - &pf.Pf_Filter[0];
pf.Pf_Priority = 5;
if (print) {
pf_codeprint(&pf.Pf_Filter[0], pf.Pf_FilterLen);
}
return (1);
}