Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

5007 lines (4773 sloc) 154.166 kB
FreeBSD 8-STABLE patch. SVN rev 203554
Changelog:
* Fix dummynet with tags mask panic
* Fix dummynet+vnet panics
* Use dummynet fast io for layer2 packets to prevent reentering ipfw
* Fix ip_fw_pfil.c mismerge. Remove dummynet tags after processing
* Use malloc(M_WAITOK) in ipfw:add_table_entry
diff --git a/contrib/pf/man/pf.conf.5 b/contrib/pf/man/pf.conf.5
index 98c3d0e..3bf922c 100644
--- a/contrib/pf/man/pf.conf.5
+++ b/contrib/pf/man/pf.conf.5
@@ -123,6 +123,7 @@ and
rules and in the routing options of filter rules, but only for
.Ar round-robin
pools.
+Table entry can contain optional ethernet address (MAC address).
.Pp
Tables can be defined with any of the following
.Xr pfctl 8
@@ -1485,6 +1486,10 @@ The ruleset does not need to be reloaded.
This is especially useful with
.Ar nat .
.Pp
+Optional ethernet address (MAC address) can be assigned to addresses
+specified in CIDR notation (matching netblocks), as symbolic host names or
+interface names.
+.Pp
Ports can be specified either by number or by name.
For example, port 80 can be specified as
.Em www .
@@ -2044,6 +2049,10 @@ support these options, and
must be specified explicitly to apply options to a rule.
.Pp
.Bl -tag -width xxxx -compact
+.It Ar ether
+Enable layer 2 stateful filtering for a rule.
+Source and destination ethernet addresses (MAC addresses) are used to
+create a state entry and to check if packet matches any state entry.
.It Ar max Aq Ar number
Limits the number of concurrent states the rule may create.
When this limit is reached, further packets matching the rule that would
@@ -2742,6 +2751,9 @@ pass in on $ext_if proto tcp from any to any port { ssh, smtp, domain, \e
block in on $ext_if proto tcp from any os {"Windows 95", "Windows 98"} \e
to any port smtp
+pass in on $bridge_if proto tcp from 10.1.1.1 ether 00:11:11:11:11:11 \e
+ to ($int_if) ether 00:22:22:22:22:22 keep state (ether)
+
# IPv6
# pass in/out all IPv6 traffic: note that we have to enable this in two
# different ways, on both our physical interface and our tunnel
@@ -2842,7 +2854,7 @@ tableopts-list = tableopts-list tableopts | tableopts
tableopts = "persist" | "const" | "file" string |
"{" [ tableaddr-list ] "}"
tableaddr-list = tableaddr-list [ "," ] tableaddr-spec | tableaddr-spec
-tableaddr-spec = [ "!" ] tableaddr [ "/" mask-bits ]
+tableaddr-spec = [ "!" ] tableaddr [ "/" mask-bits ] [ "ether" ether-addr ]
tableaddr = hostname | ipv4-dotted-quad | ipv6-coloned-hex |
interface-name | "self"
@@ -2897,7 +2909,7 @@ host = [ "!" ] ( address [ "/" mask-bits ] | "\*(Lt" string "\*(Gt" )
redirhost = address [ "/" mask-bits ]
routehost = "(" interface-name [ address [ "/" mask-bits ] ] ")"
address = ( interface-name | "(" interface-name ")" | hostname |
- ipv4-dotted-quad | ipv6-coloned-hex )
+ ipv4-dotted-quad | ipv6-coloned-hex ) [ "ether" ether-addr ]
host-list = host [ [ "," ] host-list ]
redirhost-list = redirhost [ [ "," ] redirhost-list ]
routehost-list = routehost [ [ "," ] routehost-list ]
@@ -2930,7 +2942,7 @@ tos = "tos" ( "lowdelay" | "throughput" | "reliability" |
[ "0x" ] number )
state-opts = state-opt [ [ "," ] state-opts ]
-state-opt = ( "max" number | "no-sync" | timeout | sloppy |
+state-opt = ( "ether" | "max" number | "no-sync" | timeout | sloppy |
"source-track" [ ( "rule" | "global" ) ] |
"max-src-nodes" number | "max-src-states" number |
"max-src-conn" number |
diff --git a/contrib/pf/pfctl/parse.y b/contrib/pf/pfctl/parse.y
index c22a0b6..d0da5dc 100644
--- a/contrib/pf/pfctl/parse.y
+++ b/contrib/pf/pfctl/parse.y
@@ -128,7 +128,7 @@ enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK,
PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN,
PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES,
PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK,
- PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY };
+ PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, PF_STATE_OPT_ETHER };
enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE };
@@ -409,7 +409,7 @@ typedef struct {
%}
-%token PASS BLOCK SCRUB RETURN IN OS OUT LOG QUICK ON FROM TO FLAGS
+%token PASS BLOCK SCRUB RETURN IN OS OUT LOG QUICK ON ETHER FROM TO FLAGS
%token RETURNRST RETURNICMP RETURNICMP6 PROTO INET INET6 ALL ANY ICMPTYPE
%token ICMP6TYPE CODE KEEP MODULATE STATE PORT RDR NAT BINAT ARROW NODF
%token MINTTL ERROR ALLOWOPTS FASTROUTE FILENAME ROUTETO DUPTO REPLYTO NO LABEL
@@ -442,7 +442,7 @@ typedef struct {
%type <v.icmp> icmp6_list icmp6_item
%type <v.fromto> fromto
%type <v.peer> ipportspec from to
-%type <v.host> ipspec xhost host dynaddr host_list
+%type <v.host> ipspec ether xhost host dynaddr host_list
%type <v.host> redir_host_list redirspec
%type <v.host> route_host route_host_list routespec
%type <v.os> os xos os_list
@@ -1914,6 +1914,10 @@ pfrule : action dir logquick interface route af proto fromto
}
r.timeout[o->data.timeout.number] =
o->data.timeout.seconds;
+ break;
+ case PF_STATE_OPT_ETHER:
+ r.rule_flag |= PFRULE_ETHERSTATE;
+ break;
}
o = o->next;
free(p);
@@ -2479,12 +2483,41 @@ host_list : ipspec { $$ = $1; }
}
;
-xhost : not host {
+ether : /* empty */ { $$ = NULL; }
+ | ETHER ANY { $$ = NULL; }
+ | ETHER STRING {
+ $$ = host_ether($2);
+ free($2);
+ if ($$ == NULL) {
+ YYERROR;
+ }
+ }
+ ;
+
+xhost : not host ether {
struct node_host *n;
for (n = $2; n != NULL; n = n->next)
n->not = $1;
$$ = $2;
+ if ($3) {
+ for (n = $$; n != NULL; n = n->next) {
+ if (n->addr.type != PF_ADDR_ADDRMASK &&
+ n->addr.type != PF_ADDR_DYNIFTL) {
+ yyerror("ethernet address can "
+ "be specified only for "
+ "host or interface name");
+ free($3);
+ $3 = NULL;
+ YYERROR;
+ } else {
+ n->addr.addr_ether =
+ $3->addr.addr_ether;
+ }
+ }
+ if ($3)
+ free($3);
+ }
}
| not NOROUTE {
$$ = calloc(1, sizeof(struct node_host));
@@ -3206,6 +3239,14 @@ state_opt_item : MAXIMUM number {
$$->next = NULL;
$$->tail = $$;
}
+ | ETHER {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_ETHER;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
| sourcetrack {
$$ = calloc(1, sizeof(struct node_state_opt));
if ($$ == NULL)
@@ -4917,6 +4958,7 @@ lookup(char *s)
{ "drop", DROP},
{ "drop-ovl", FRAGDROP},
{ "dup-to", DUPTO},
+ { "ether", ETHER},
{ "fastroute", FASTROUTE},
{ "file", FILENAME},
{ "fingerprints", FINGERPRINTS},
diff --git a/contrib/pf/pfctl/pf_print_state.c b/contrib/pf/pfctl/pf_print_state.c
index 02a39b3..438c9fc 100644
--- a/contrib/pf/pfctl/pf_print_state.c
+++ b/contrib/pf/pfctl/pf_print_state.c
@@ -85,7 +85,8 @@ print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
return;
case PF_ADDR_ADDRMASK:
if (PF_AZERO(&addr->v.a.addr, AF_INET6) &&
- PF_AZERO(&addr->v.a.mask, AF_INET6))
+ PF_AZERO(&addr->v.a.mask, AF_INET6) &&
+ (addr->addr_ether.flags & PFAE_CHECK) == 0)
printf("any");
else {
char buf[48];
@@ -113,12 +114,37 @@ print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
/* mask if not _both_ address and mask are zero */
if (!(PF_AZERO(&addr->v.a.addr, AF_INET6) &&
- PF_AZERO(&addr->v.a.mask, AF_INET6))) {
+ PF_AZERO(&addr->v.a.mask, AF_INET6) &&
+ (addr->addr_ether.flags & PFAE_CHECK) == 0)) {
int bits = unmask(&addr->v.a.mask, af);
if (bits != (af == AF_INET ? 32 : 128))
printf("/%d", bits);
}
+
+ if ((addr->addr_ether.flags & PFAE_CHECK) != 0) {
+ putchar(' ');
+ print_addr_ether(&addr->addr_ether, 0);
+ }
+}
+
+void
+print_addr_ether(struct pf_addr_ether *addr, int verbose)
+{
+ u_int8_t *ea;
+
+ if ((addr->flags & PFAE_CHECK) == 0) {
+ if (verbose)
+ printf("ether any");
+ return;
+ }
+ if (addr->flags & PFAE_MULTICAST) {
+ printf("ether multicast");
+ } else {
+ ea = addr->octet;
+ printf("ether %02x:%02x:%02x:%02x:%02x:%02x",
+ ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
+ }
}
void
@@ -301,6 +327,29 @@ print_state(struct pf_state *s, int opts)
if (s->nat_src_node != NULL)
printf(", sticky-address");
printf("\n");
+ if (s->local_flags & PFSTATE_ETHER) {
+ sec = 0; /* reuse; left printed */
+
+ printf(" ");
+ if (s->lan.addr_ether.flags & PFAE_CHECK) {
+ print_addr_ether(&s->lan.addr_ether, 1);
+ if (s->direction == PF_OUT)
+ printf(" -> ");
+ else
+ printf(" <- ");
+ sec = 1;
+ }
+ if (sec == 0 ||
+ (s->gwy.addr_ether.flags & PFAE_CHECK)) {
+ print_addr_ether(&s->gwy.addr_ether, 1);
+ if (s->direction == PF_OUT)
+ printf(" -> ");
+ else
+ printf(" <- ");
+ }
+ print_addr_ether(&s->ext.addr_ether, 1);
+ printf("\n");
+ }
}
if (opts & PF_OPT_VERBOSE2) {
printf(" id: %016llx creatorid: %08x%s\n",
diff --git a/contrib/pf/pfctl/pfctl.c b/contrib/pf/pfctl/pfctl.c
index 21995f6..7fa86c8 100644
--- a/contrib/pf/pfctl/pfctl.c
+++ b/contrib/pf/pfctl/pfctl.c
@@ -1902,8 +1902,8 @@ pfctl_test_altqsupport(int dev, int opts)
if (ioctl(dev, DIOCGETALTQS, &pa)) {
if (errno == ENODEV) {
- if (!(opts & PF_OPT_QUIET))
- fprintf(stderr, "No ALTQ support in kernel\n"
+ if (opts & PF_OPT_VERBOSE)
+ fprintf(stderr, "No ALTQ support in kernel. "
"ALTQ related functions disabled\n");
return (0);
} else
diff --git a/contrib/pf/pfctl/pfctl.h b/contrib/pf/pfctl/pfctl.h
index 98a1bef..459c5a3 100644
--- a/contrib/pf/pfctl/pfctl.h
+++ b/contrib/pf/pfctl/pfctl.h
@@ -117,6 +117,7 @@ struct pf_altq *pfaltq_lookup(const char *);
char *rate2str(double);
void print_addr(struct pf_addr_wrap *, sa_family_t, int);
+void print_addr_ether(struct pf_addr_ether *, int);
void print_host(struct pf_state_host *, sa_family_t, int);
void print_seq(struct pf_state_peer *);
void print_state(struct pf_state *, int);
diff --git a/contrib/pf/pfctl/pfctl_parser.c b/contrib/pf/pfctl/pfctl_parser.c
index c9b2e11..a027f5c 100644
--- a/contrib/pf/pfctl/pfctl_parser.c
+++ b/contrib/pf/pfctl/pfctl_parser.c
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#include <net/pfvar.h>
+#include <net/ethernet.h>
#include <arpa/inet.h>
#include <stdio.h>
@@ -374,7 +375,9 @@ print_fromto(struct pf_rule_addr *src, pf_osfp_t osfp, struct pf_rule_addr *dst,
PF_AZERO(&dst->addr.v.a.mask, AF_INET6) &&
!src->neg && !dst->neg &&
!src->port_op && !dst->port_op &&
- osfp == PF_OSFP_ANY)
+ osfp == PF_OSFP_ANY &&
+ (src->addr.addr_ether.flags & PFAE_CHECK) == 0 &&
+ (dst->addr.addr_ether.flags & PFAE_CHECK) == 0)
printf(" all");
else {
printf(" from ");
@@ -878,6 +881,8 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
for (i = 0; !opts && i < PFTM_MAX; ++i)
if (r->timeout[i])
opts = 1;
+ if (r->rule_flag & PFRULE_ETHERSTATE)
+ opts = 1;
if (opts) {
printf(" (");
if (r->max_states) {
@@ -962,6 +967,12 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
"inv.timeout" : pf_timeouts[j].name,
r->timeout[i]);
}
+ if (r->rule_flag & PFRULE_ETHERSTATE) {
+ if (!opts)
+ printf(", ");
+ printf("ether");
+ opts = 0;
+ }
printf(")");
}
if (r->rule_flag & PFRULE_FRAGMENT)
@@ -1427,6 +1438,35 @@ host(const char *s)
}
struct node_host *
+host_ether(const char *s)
+{
+ struct pf_addr_ether *addr;
+ struct node_host *h = NULL;
+
+ if (strcmp(s, "any") == 0) {
+ return (NULL);
+ }
+
+ h = calloc(1, sizeof(*h));
+ if (h == NULL)
+ err(1, "host_ether: malloc");
+ addr = &h->addr.addr_ether;
+
+ if (strcmp(s, "multicast") == 0) {
+ addr->flags = PFAE_CHECK | PFAE_MULTICAST;
+ return (h);
+ }
+ if (!ether_aton_r(s, (struct ether_addr*)addr->octet)) {
+ fprintf(stderr, "can't parse ethernet address: %s\n", s);
+ free(h);
+ return (NULL);
+ }
+ addr->flags = PFAE_CHECK;
+
+ return (h);
+}
+
+struct node_host *
host_if(const char *s, int mask)
{
struct node_host *n, *h = NULL;
@@ -1614,16 +1654,39 @@ host_dns(const char *s, int v4mask, int v6mask)
int
append_addr(struct pfr_buffer *b, char *s, int test)
{
- char *r;
+ char *r, *rs, *s_ether, *p;
struct node_host *h, *n;
+ struct pf_addr_ether addr_ether;
int rv, not = 0;
for (r = s; *r == '!'; r++)
not = !not;
- if ((n = host(r)) == NULL) {
+ if ((rs = strdup(r)) == NULL)
+ err(1, "append_addr: strdup");
+ bzero(&addr_ether, sizeof (addr_ether));
+ if ((p = strstr(rs, "ether")) != NULL) {
+ s_ether = p + strlen("ether");
+ if (p > rs && isspace(*(p - 1)) && isspace(*s_ether++)) {
+ while (isspace(*s_ether))
+ s_ether++;
+ h = host_ether(s_ether);
+ if (h) {
+ addr_ether = h->addr.addr_ether;
+ free(h);
+ h = NULL;
+ }
+ for (p--; p >= rs && isspace(*p); p--)
+ *p = '\0';
+ }
+ }
+ if ((n = host(rs)) == NULL) {
errno = 0;
return (-1);
}
+ for (h = n; h != NULL; h = h->next)
+ h->addr.addr_ether = addr_ether;
+ h = NULL;
+ free(rs);
rv = append_addr_host(b, n, test, not);
do {
h = n;
@@ -1669,6 +1732,7 @@ append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not)
errno = EINVAL;
return (-1);
}
+ addr.pfra_ether = n->addr.addr_ether;
if (pfr_buf_add(b, &addr))
return (-1);
} while ((n = n->next) != NULL);
diff --git a/contrib/pf/pfctl/pfctl_parser.h b/contrib/pf/pfctl/pfctl_parser.h
index 4885203..48f9d85 100644
--- a/contrib/pf/pfctl/pfctl_parser.h
+++ b/contrib/pf/pfctl/pfctl_parser.h
@@ -296,6 +296,7 @@ void ifa_load(void);
struct node_host *ifa_exists(const char *);
struct node_host *ifa_lookup(const char *, int);
struct node_host *host(const char *);
+struct node_host *host_ether(const char *);
int append_addr(struct pfr_buffer *, char *, int);
int append_addr_host(struct pfr_buffer *,
diff --git a/contrib/pf/pfctl/pfctl_radix.c b/contrib/pf/pfctl/pfctl_radix.c
index 01ad475..3f62ee4 100644
--- a/contrib/pf/pfctl/pfctl_radix.c
+++ b/contrib/pf/pfctl/pfctl_radix.c
@@ -607,12 +607,20 @@ pfr_next_token(char buf[BUF_SIZE], FILE *fp)
do {
if (i < BUF_SIZE)
buf[i++] = next_ch;
- next_ch = fgetc(fp);
- } while (!feof(fp) && !isspace(next_ch));
+ /* leave only 1 space */
+ if (isspace(next_ch)) {
+ while (isspace(next_ch) && next_ch != '\n' && !feof(fp))
+ next_ch = fgetc(fp);
+ } else {
+ next_ch = fgetc(fp);
+ }
+ } while (!feof(fp) && next_ch != '\n');
if (i >= BUF_SIZE) {
errno = EINVAL;
return (-1);
}
+ if (i > 0 && isspace(buf[i-1]))
+ i--;
buf[i] = '\0';
return (1);
}
diff --git a/contrib/pf/pfctl/pfctl_table.c b/contrib/pf/pfctl/pfctl_table.c
index 3d54466..e8f9cc4 100644
--- a/contrib/pf/pfctl/pfctl_table.c
+++ b/contrib/pf/pfctl/pfctl_table.c
@@ -438,6 +438,8 @@ print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns)
printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf);
if (ad->pfra_net < hostnet)
printf("/%d", ad->pfra_net);
+ putchar(' ');
+ print_addr_ether(&ad->pfra_ether, 0);
if (rad != NULL && fback != PFR_FB_NONE) {
if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf))
errx(1, "print_addrx: strlcpy");
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index c372a08..3568a69 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -240,6 +240,27 @@ and will never send any requests.
If the Address Resolution Protocol is enabled,
the host will perform normally,
sending out requests and listening for replies.
+.It Cm l2tag
+Special tag containing source and destination layer 2 addresses will be
+attached to every packet passing through interface.
+Note that only incoming or outgoing packets may be tagged (but not both), it is
+interface dependant.
+.It Fl l2tag
+Disable special packet tagging with layer 2 addresses.
+.It Cm l2filter
+Perform layer 2 filtering of packets passing through interface.
+This option doesn't imply
+.Cm l2tag
+option.
+With
+.Cm l2filter
+specified packets are passed to firewall as they were received from wire.
+But
+.Cm l2tag
+just tags packet and usual layer 3 filtering is performed.
+.It Fl l2filter
+Disable layer 2 filtering.
+Higher level filtering will perform normally.
.It Cm broadcast
(Inet only.)
Specify the address to use to represent broadcasts to the
diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c
index f05374c..aedc894 100644
--- a/sbin/ifconfig/ifconfig.c
+++ b/sbin/ifconfig/ifconfig.c
@@ -825,7 +825,7 @@ setifname(const char *val, int dummy __unused, int s,
#define IFFBITS \
"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \
"\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \
-"\20MULTICAST\22PPROMISC\23MONITOR\24STATICARP"
+"\20MULTICAST\22PPROMISC\23MONITOR\24STATICARP\30L2FILTER\31L2TAG"
#define IFCAPBITS \
"\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING" \
@@ -1066,6 +1066,10 @@ static struct cmd basic_cmds[] = {
DEF_CMD("-monitor", -IFF_MONITOR, setifflags),
DEF_CMD("staticarp", IFF_STATICARP, setifflags),
DEF_CMD("-staticarp", -IFF_STATICARP, setifflags),
+ DEF_CMD("l2filter", IFF_L2FILTER, setifflags),
+ DEF_CMD("-l2filter", -IFF_L2FILTER, setifflags),
+ DEF_CMD("l2tag", IFF_L2TAG, setifflags),
+ DEF_CMD("-l2tag", -IFF_L2TAG, setifflags),
DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap),
DEF_CMD("-rxcsum", -IFCAP_RXCSUM, setifcap),
DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap),
diff --git a/sbin/ipfw/dummynet.c b/sbin/ipfw/dummynet.c
index 9e68e65..5d4fcce 100644
--- a/sbin/ipfw/dummynet.c
+++ b/sbin/ipfw/dummynet.c
@@ -33,6 +33,7 @@
#include <ctype.h>
#include <err.h>
#include <errno.h>
+#include <inttypes.h>
#include <libutil.h>
#include <netdb.h>
#include <stdio.h>
@@ -74,6 +75,10 @@ static struct _s_x dummynet_params[] = {
{ "profile", TOK_PIPE_PROFILE},
{ "burst", TOK_BURST},
{ "dummynet-params", TOK_NULL },
+ { "ether", TOK_ETHER },
+ { "dst-ether", TOK_ETHER_DST },
+ { "src-ether", TOK_ETHER_SRC },
+ { "tag", TOK_TAG },
{ NULL, 0 } /* terminator */
};
@@ -128,8 +133,7 @@ list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
for (l = 0; l < fs->rq_elements; l++) {
struct in_addr ina;
- /* XXX: Should check for IPv4 flows */
- if (IS_IP6_FLOW_ID(&(q[l].id)))
+ if (q[l].id.flow_type != IPFW_FLOW_IP4)
continue;
if (!index_printed) {
@@ -138,13 +142,16 @@ list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
printf("\n");
indexes++;
printf(" "
- "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
+ "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x "
+ "tag: 0x%04x\n",
fs->flow_mask.proto,
fs->flow_mask.src_ip, fs->flow_mask.src_port,
- fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
+ fs->flow_mask.dst_ip, fs->flow_mask.dst_port,
+ fs->flow_mask.tag);
printf("BKT Prot ___Source IP/port____ "
"____Dest. IP/port____ "
+ " Tag "
"Tot_pkt/bytes Pkt/Byte Drp\n");
}
@@ -160,6 +167,7 @@ list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
ina.s_addr = htonl(q[l].id.dst_ip);
printf("%15s/%-5d ",
inet_ntoa(ina), q[l].id.dst_port);
+ printf("%4d ", q[l].id.tag);
printf("%4llu %8llu %2u %4u %3u\n",
align_uint64(&q[l].tot_pkts),
align_uint64(&q[l].tot_bytes),
@@ -172,7 +180,7 @@ list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
/* Print IPv6 flows */
index_printed = 0;
for (l = 0; l < fs->rq_elements; l++) {
- if (!IS_IP6_FLOW_ID(&(q[l].id)))
+ if (q[l].id.flow_type != IPFW_FLOW_IP6)
continue;
if (!index_printed) {
@@ -187,11 +195,13 @@ list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port);
inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6),
buff, sizeof(buff) );
- printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port);
+ printf("%s/0x%04x ", buff, fs->flow_mask.dst_port);
+ printf("tag: 0x%04x\n", fs->flow_mask.tag);
printf("BKT ___Prot___ _flow-id_ "
"______________Source IPv6/port_______________ "
"_______________Dest. IPv6/port_______________ "
+ " Tag "
"Tot_pkt/bytes Pkt/Byte Drp\n");
}
printf("%3d ", q[l].hash_slot);
@@ -206,6 +216,7 @@ list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
printf(" %39s/%-5d ",
inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)),
q[l].id.dst_port);
+ printf("%4d ", q[l].id.tag);
printf(" %4llu %8llu %2u %4u %3u\n",
align_uint64(&q[l].tot_pkts),
align_uint64(&q[l].tot_bytes),
@@ -215,6 +226,44 @@ list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
align_uint64(&q[l].S),
align_uint64(&q[l].F));
}
+
+ /* Print ether flows */
+ index_printed = 0;
+ for (l = 0; l < fs->rq_elements; l++) {
+ if (q[l].id.flow_type != IPFW_FLOW_ETHER)
+ continue;
+
+ if (!index_printed) {
+ index_printed = 1;
+ if (indexes > 0) /* currently a no-op */
+ printf("\n");
+ indexes++;
+ printf("mask: ");
+ print_ether_addr(fs->flow_mask.src_ether.octet);
+ printf(" -> ");
+ print_ether_addr(fs->flow_mask.dst_ether.octet);
+ printf(" tag: 0x%04x", fs->flow_mask.tag);
+ printf("\n");
+ printf("BKT _Source Ether Addr_ "
+ "_Dest. Ether Addr__ "
+ " Tag "
+ "Tot_pkt/bytes Pkt/Byte Drp\n");
+ }
+
+ printf("%3d ", q[l].hash_slot);
+ print_ether_addr(q[l].id.src_ether.octet);
+ printf(" ");
+ print_ether_addr(q[l].id.dst_ether.octet);
+ printf(" ");
+ printf("%4d ", q[l].id.tag);
+ printf("%4llu %8llu %2u %4u %3u\n",
+ align_uint64(&q[l].tot_pkts),
+ align_uint64(&q[l].tot_bytes),
+ q[l].len, q[l].len_bytes, q[l].drops);
+ if (co.verbose)
+ printf(" S %20llu F %20llu\n",
+ align_uint64(&q[l].S), align_uint64(&q[l].F));
+ }
}
static void
@@ -694,6 +743,78 @@ load_extra_delays(const char *filename, struct dn_pipe *p)
strncpy(p->name, profile_name, sizeof(p->name));
}
+static void
+config_parse_mask(const char *arg, void *dst, int max_mask_len,
+ int *ac, char ***av)
+{
+ const int mask_capacity = 64;
+ uint64_t mask, mask_cp, max_mask;
+ int mask_len, n;
+ char *end;
+ u_char *p;
+
+ mask = 0ULL;
+ mask_len = 0;
+ if (ac == NULL || av == NULL || *ac < 1) {
+ mask_len = max_mask_len;
+ } else {
+ if (**av[0] == '/') {
+ mask_len = strtoul(*av[0] + 1, &end, 0);
+ if (mask_len == 0 || mask_len < 0 ||
+ mask_len > max_mask_len)
+ errx(EX_DATAERR, "%s: invalid mask length: %s",
+ arg, *av[0] + 1);
+ (*ac)--; (*av)++;
+ } else {
+ mask = strtoimax(*av[0], &end, 0);
+ if (end == *av[0])
+ return;
+ if (mask == 0ULL)
+ errx(EX_DATAERR, "%s: invalid mask: %s",
+ arg, *av[0]);
+ if (max_mask_len > mask_capacity)
+ errx(EX_DATAERR, "%s: invalid mask; "
+ "mask length expected: %s", arg, *av[0]);
+ (*ac)--; (*av)++;
+ }
+ }
+ if (max_mask_len <= mask_capacity) {
+ max_mask = (max_mask_len == mask_capacity) ? ~0ULL :
+ (1ULL << max_mask_len) - 1;
+
+ if (mask == 0ULL)
+ mask = (mask_len == mask_capacity) ? ~0ULL : max_mask &
+ ~((1ULL << (max_mask_len - mask_len)) - 1);
+ if (~max_mask & mask)
+ errx(EX_DATAERR, "%s mask must be %d bit",
+ arg, max_mask_len);
+ if (max_mask_len > 32) {
+ mask_cp = mask;
+ p = (u_char *) dst;
+ p += max_mask_len/8 - 1;
+ if (max_mask_len % 8 != 0)
+ p++;
+ for (; p >= (u_char *)dst; p--, mask_cp >>= 8) {
+ *p = (u_char)(mask_cp & 0xff);
+ }
+ } else if (max_mask_len > 16)
+ *((uint32_t *)dst) = (uint32_t)mask;
+ else if (max_mask_len > 8)
+ *((uint16_t *)dst) = (uint16_t)mask;
+ else
+ *((uint8_t *)dst) = (uint8_t)mask;
+ } else {
+ p = (u_char *) dst;
+ n = mask_len;
+ for (; n > 0; p++, n -= 8) {
+ if (n >= 8)
+ *p = 0xff;
+ else
+ *p = (u_char)(~((1 << (8 - n)) - 1));
+ }
+ }
+}
+
void
ipfw_config_pipe(int ac, char **av)
{
@@ -768,12 +889,7 @@ ipfw_config_pipe(int ac, char **av)
end = NULL;
while (ac >= 1) {
- uint32_t *p32 = NULL;
- uint16_t *p16 = NULL;
- uint32_t *p20 = NULL;
- struct in6_addr *pa6 = NULL;
- uint32_t a;
-
+ char *tok_str = *av;
tok = match_token(dummynet_params, *av);
ac--; av++;
switch(tok) {
@@ -790,78 +906,88 @@ ipfw_config_pipe(int ac, char **av)
n2mask(&(p.fs.flow_mask.src_ip6), 128);
p.fs.flow_mask.flow_id6 = ~0;
p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.dst_ether, 48,
+ NULL, NULL);
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.src_ether, 48,
+ NULL, NULL);
+ p.fs.flow_mask.tag = ~0;
goto end_mask;
case TOK_DSTIP:
- p32 = &p.fs.flow_mask.dst_ip;
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.dst_ip, 32, &ac, &av);
break;
case TOK_SRCIP:
- p32 = &p.fs.flow_mask.src_ip;
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.src_ip, 32, &ac, &av);
break;
case TOK_DSTIP6:
- pa6 = &(p.fs.flow_mask.dst_ip6);
+ config_parse_mask(tok_str,
+ &(p.fs.flow_mask.dst_ip6), 128,
+ &ac, &av);
break;
case TOK_SRCIP6:
- pa6 = &(p.fs.flow_mask.src_ip6);
+ config_parse_mask(tok_str,
+ &(p.fs.flow_mask.src_ip6), 128,
+ &ac, &av);
break;
case TOK_FLOWID:
- p20 = &p.fs.flow_mask.flow_id6;
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.flow_id6, 20, &ac, &av);
break;
case TOK_DSTPORT:
- p16 = &p.fs.flow_mask.dst_port;
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.dst_port, 16, &ac, &av);
break;
case TOK_SRCPORT:
- p16 = &p.fs.flow_mask.src_port;
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.src_port, 16, &ac, &av);
break;
case TOK_PROTO:
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.proto, 8, &ac, &av);
+ break;
+
+ case TOK_ETHER:
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.dst_ether, 48,
+ NULL, NULL);
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.src_ether, 48,
+ NULL, NULL);
+ break;
+
+ case TOK_ETHER_DST:
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.dst_ether, 48,
+ &ac, &av);
+ break;
+
+ case TOK_ETHER_SRC:
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.src_ether, 48,
+ &ac, &av);
+ break;
+
+ case TOK_TAG:
+ config_parse_mask(tok_str,
+ &p.fs.flow_mask.tag, 16, &ac, &av);
break;
default:
ac++; av--; /* backtrack */
goto end_mask;
}
- if (ac < 1)
- errx(EX_USAGE, "mask: value missing");
- if (*av[0] == '/') {
- a = strtoul(av[0]+1, &end, 0);
- if (pa6 == NULL)
- a = (a == 32) ? ~0 : (1 << a) - 1;
- } else
- a = strtoul(av[0], &end, 0);
- if (p32 != NULL)
- *p32 = a;
- else if (p16 != NULL) {
- if (a > 0xFFFF)
- errx(EX_DATAERR,
- "port mask must be 16 bit");
- *p16 = (uint16_t)a;
- } else if (p20 != NULL) {
- if (a > 0xfffff)
- errx(EX_DATAERR,
- "flow_id mask must be 20 bit");
- *p20 = (uint32_t)a;
- } else if (pa6 != NULL) {
- if (a > 128)
- errx(EX_DATAERR,
- "in6addr invalid mask len");
- else
- n2mask(pa6, a);
- } else {
- if (a > 0xFF)
- errx(EX_DATAERR,
- "proto mask must be 8 bit");
- p.fs.flow_mask.proto = (uint8_t)a;
- }
- if (a != 0)
- p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
- ac--; av++;
+ p.fs.flags_fs |= DN_HAVE_FLOW_MASK;
} /* end while, config masks */
end_mask:
break;
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index f8b0746..c961a75 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -45,9 +45,9 @@
.Cm set show
.Pp
.Nm
-.Cm table Ar number Cm add Ar addr Ns Oo / Ns Ar masklen Oc Op Ar value
+.Cm table Ar number Cm add Ar addr Ns Oo / Ns Ar masklen Oc Oo Cm ether Ar etheraddr Oc Op Ar value
.Nm
-.Cm table Ar number Cm delete Ar addr Ns Op / Ns Ar masklen
+.Cm table Ar number Cm delete Ar addr Ns Oo / Ns Ar masklen Oc Oo Cm ether Ar etheraddr Oc
.Nm
.Cm table
.Brq Ar number | all
@@ -343,9 +343,9 @@ You can use
to temporarily disable the firewall to regain access to the network,
allowing you to fix the problem.
.Sh PACKET FLOW
-A packet is checked against the active ruleset in multiple places
-in the protocol stack, under control of several sysctl variables.
-These places and variables are shown below, and it is important to
+A packet is checked against the active ruleset in multiple places in the
+protocol stack, under control of several sysctl variables and interface flags.
+These places and variables and flags are shown below, and it is important to
have this picture in mind in order to design a correct ruleset.
.Bd -literal -offset indent
^ to upper layers V
@@ -353,11 +353,12 @@ have this picture in mind in order to design a correct ruleset.
+----------->-----------+
^ V
[ip(6)_input] [ip(6)_output] net.inet(6).ip(6).fw.enable=1
+ | | (l2tag interface flag)
| |
^ V
- [ether_demux] [ether_output_frame] net.link.ether.ipfw=1
+ [ether_demux] [ether_output_frame] l2filter interface flag
| |
- +-->--[bdg_forward]-->--+ net.link.bridge.ipfw=1
+ +-->----[bridge]----->--+ l2filter interface flag
^ V
| to devices |
.Ed
@@ -381,13 +382,39 @@ is invoked from
or
.Cm ip6_input() .
.Pp
+Note that packets do
+.Em not
+contain IP header when invoked from
+.Cm ether_demux() , ether_output_frame()
+or
+.Cm bridge .
+.Pp
+In order to filter by both MAC and IP headers interface flag
+.Cm l2tag
+should be used.
+When enabled a special tag containing MAC header is appended to incoming
+packets. Tag is used when
+.Nm
+invoked from
+.Cm ip_input()
+or
+.Cm ip6_input() .
+Note that as a rule only incoming packets are tagged, but
+.Cm bridge
+appends tag to outgoing packets too.
+Therefore dynamic rules (like rules created by
+.Cm keep-state
+option) do not check specified MAC header options if there is no
+.Cm l2tag
+tag appended to packet.
+.Pp
Also note that each packet is always checked against the complete ruleset,
irrespective of the place where the check occurs, or the source of the packet.
If a rule contains some match patterns or actions which are not valid
for the place of invocation (e.g.\& trying to match a MAC header within
.Cm ip_input
or
-.Cm ip6_input ),
+.Cm ip6_input ) Ns ,
the match pattern will not match, but a
.Cm not
operator in front of such patterns
@@ -401,7 +428,7 @@ differentiate among the possible places.
.Cm skipto
rules can be useful here, as an example:
.Bd -literal -offset indent
-# packets from ether_demux or bdg_forward
+# packets from ether_demux or bridge
ipfw add 10 skipto 1000 all from any to any layer2 in
# packets from ip_input
ipfw add 10 skipto 2000 all from any to any not layer2 in
@@ -412,7 +439,7 @@ ipfw add 10 skipto 4000 all from any to any layer2 out
.Ed
.Pp
(yes, at the moment there is no way to differentiate between
-ether_demux and bdg_forward).
+ether_demux and bridge).
.Sh SYNTAX
In general, each keyword or argument must be provided as
a separate command line argument, with no leading or trailing
@@ -1163,6 +1190,19 @@ Everything following // is considered as a comment and stored in the rule.
You can have comment-only rules, which are listed as having a
.Cm count
action followed by the comment.
+.It Cm arp-op Ar arp-op
+Matches Address Resolution Protocol (ARP) packets whose
+.Em Operation
+field corresponds to one of those specified as argument.
+.Ar arp-op
+is specified in the same way as port numbers (i.e., one or more
+comma-separated single values or ranges). You can use symbolic names
+for known values such as
+.Em request , reply , rev_request , rev_reply , inv_request , inv_reply .
+Values can be entered as decimal or hexadecimal (if prefixed by 0x), and
+they are always printed as hexadecimal (unless the
+.Cm -N
+option is used, in which case symbolic resolution will be attempted).
.It Cm bridged
Alias for
.Cm layer2 .
@@ -1174,6 +1214,25 @@ input for delivery.
.It Cm diverted-output
Matches only packets going from a divert socket back outward to the IP
stack output for delivery.
+.It Cm dst-arp Ar dst-arp
+Matches Address Resolution Protocol (ARP) packets whose
+.Em Target protocol address (TPA)
+and optionally
+.Em Target hardware address (THA)
+fields correspond to entry in the lookup table
+.Ar dst-arp .
+See the
+.Sx LOOKUP TABLES
+section below for more information on lookup tables.
+.It Cm dst-ether Ar dst-ether
+Match packets with a given destination MAC address
+.Ar dst-ether Ns ,
+specified as the
+.Cm any
+keyword (matching any MAC address),
+.Cm muticast
+keyword (matching multicast MAC addresses), or six groups of hex digits
+separated by colons.
.It Cm dst-ip Ar ip-address
Matches IPv4 packets whose destination IP is one of the address(es)
specified as argument.
@@ -1185,6 +1244,19 @@ Matches IP packets whose destination port is one of the port(s)
specified as argument.
.It Cm established
Matches TCP packets that have the RST or ACK bits set.
+.It Cm ether-type Ar ether-type
+Matches packets whose Ethernet Type field
+corresponds to one of those specified as argument.
+.Ar ether-type
+is specified in the same way as
+.Cm port numbers
+(i.e., one or more comma-separated single values or ranges).
+You can use symbolic names for known values such as
+.Em vlan , ipv4, ipv6 .
+Values can be entered as decimal or hexadecimal (if prefixed by 0x),
+and they are always printed as hexadecimal (unless the
+.Cm -N
+option is used, in which case symbolic resolution will be attempted).
.It Cm ext6hdr Ar header
Matches IPv6 packets containing the extended header given by
.Ar header .
@@ -1389,57 +1461,6 @@ of source and destination addresses and ports can be
specified.
Currently,
only IPv4 flows are supported.
-.It Cm { MAC | mac } Ar dst-mac src-mac
-Match packets with a given
-.Ar dst-mac
-and
-.Ar src-mac
-addresses, specified as the
-.Cm any
-keyword (matching any MAC address), or six groups of hex digits
-separated by colons,
-and optionally followed by a mask indicating the significant bits.
-The mask may be specified using either of the following methods:
-.Bl -enum -width indent
-.It
-A slash
-.Pq /
-followed by the number of significant bits.
-For example, an address with 33 significant bits could be specified as:
-.Pp
-.Dl "MAC 10:20:30:40:50:60/33 any"
-.Pp
-.It
-An ampersand
-.Pq &
-followed by a bitmask specified as six groups of hex digits separated
-by colons.
-For example, an address in which the last 16 bits are significant could
-be specified as:
-.Pp
-.Dl "MAC 10:20:30:40:50:60&00:00:00:00:ff:ff any"
-.Pp
-Note that the ampersand character has a special meaning in many shells
-and should generally be escaped.
-.Pp
-.El
-Note that the order of MAC addresses (destination first,
-source second) is
-the same as on the wire, but the opposite of the one used for
-IP addresses.
-.It Cm mac-type Ar mac-type
-Matches packets whose Ethernet Type field
-corresponds to one of those specified as argument.
-.Ar mac-type
-is specified in the same way as
-.Cm port numbers
-(i.e., one or more comma-separated single values or ranges).
-You can use symbolic names for known values such as
-.Em vlan , ipv4, ipv6 .
-Values can be entered as decimal or hexadecimal (if prefixed by 0x),
-and they are always printed as hexadecimal (unless the
-.Cm -N
-option is used, in which case symbolic resolution will be attempted).
.It Cm proto Ar protocol
Matches packets with the corresponding IP protocol.
.It Cm recv | xmit | via Brq Ar ifX | Ar if Ns Cm * | Ar ipno | Ar any
@@ -1488,6 +1509,40 @@ interface.
Matches TCP packets that have the SYN bit set but no ACK bit.
This is the short form of
.Dq Li tcpflags\ syn,!ack .
+.It Cm state-options Ar spec
+Specifies options for dynamic rule creation by
+.Cm keep-state
+or
+.Cm limit .
+.Ar spec
+is comma separated list of options.
+The supported options are:
+.Bl -tag -width xxxxxxxx -compact
+.It Cm ether
+Enable layer 2 stateful filtering for a rule.
+Source and destination ethernet addresses (MAC addresses) are used to
+create a state entry (dynamic rule) and to check if packet matches any
+state entry.
+.El
+.It Cm src-arp Ar src-arp
+Matches Address Resolution Protocol (ARP) packets whose
+.Em Sender protocol address (SPA)
+and optionally
+.Em Sender hardware address (SHA)
+fields correspond to entry in the lookup table
+.Ar src-arp .
+See the
+.Sx LOOKUP TABLES
+section below for more information on lookup tables.
+.It Cm src-ether Ar src-ether
+Match packets with a given source MAC address
+.Ar src-ether Ns ,
+specified as the
+.Cm any
+keyword (matching any MAC address),
+.Cm muticast
+keyword (matching multicast MAC addresses), or six groups of hex digits
+separated by colons.
.It Cm src-ip Ar ip-address
Matches IPv4 packets whose source IP is one of the address(es)
specified as an argument.
@@ -1503,6 +1558,8 @@ Matches packets whose tags are included in
which is either a single value or a list of values or ranges
specified in the same way as
.Ar ports .
+.Cm any
+value matches any tag.
Tags can be applied to the packet using
.Cm tag
rule action parameter (see it's description for details on tags).
@@ -1644,6 +1701,8 @@ If
is not specified, it defaults to 32.
When looking up an IP address in a table, the most specific
entry will match.
+Optionally each entry specifies MAC address
+.Pq Cm ether Ar etheraddr Ns .
Associated with each entry is a 32-bit unsigned
.Ar value ,
which can optionally be checked by a rule matching code.
@@ -1777,6 +1836,13 @@ and
.Em dst
are used here only to denote the initial match addresses, but they
are completely equivalent afterwards).
+If rule specifies ethernet source or destination address it is also used
+by dynamic rule to match packets.
+But note that packets without
+.Cm l2tag
+appended to them match against such dynamic rules, because
+.Cm l2tag
+usually presents only in incoming or outgoing packets, but not in both.
Dynamic rules will be checked at the first
.Cm check-state, keep-state
or
@@ -2079,7 +2145,7 @@ sent to a different
.Em dynamic
pipe or queue.
A flow identifier is constructed by masking the IP addresses,
-ports and protocol types as specified with the
+ethrnet addresses, ports, protocol and tag types as specified with the
.Cm mask
options in the configuration of the pipe or queue.
For each different flow identifier, a new pipe or queue is created
@@ -2095,7 +2161,9 @@ are used, each flow will share the parent's pipe bandwidth evenly
with other flows generated by the same queue (note that other queues
with different weights might be connected to the same pipe).
.br
-Available mask specifiers are a combination of one or more of the following:
+Available mask specifiers are a combination of one or more of the following
+.Ns No ( Ar mask
+argument is optional):
.Pp
.Cm dst-ip Ar mask ,
.Cm dst-ip6 Ar mask ,
@@ -2104,11 +2172,20 @@ Available mask specifiers are a combination of one or more of the following:
.Cm dst-port Ar mask ,
.Cm src-port Ar mask ,
.Cm flow-id Ar mask ,
-.Cm proto Ar mask
+.Cm proto Ar mask ,
+.Cm ether ,
+.Cm src-ether Ar mask ,
+.Cm dst-ether Ar mask ,
+.Cm tag Ar mask
or
.Cm all ,
.Pp
where the latter means all bits in all fields are significant.
+.Cm ether
+is equivalent to specifying both
+.Cm src-ether
+and
+.Cm dst-ether .
.Pp
.It Cm noerror
When a packet is dropped by a
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 58094a1..0c9cb08 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -44,6 +44,8 @@
#include <net/ethernet.h>
#include <net/if.h> /* only IFNAMSIZ */
+#include <net/if_dl.h>
+#include <net/if_arp.h>
#include <netinet/in.h>
#include <netinet/in_systm.h> /* only n_short, n_long */
#include <netinet/ip.h>
@@ -143,6 +145,11 @@ static struct _s_x f_iptos[] = {
{ NULL, 0 }
};
+static struct _s_x f_stateopts[] = {
+ { "ether", IP_FW_STATEOPT_ETHER},
+ { NULL, 0 }
+};
+
static struct _s_x limit_masks[] = {
{"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
{"src-addr", DYN_SRC_ADDR},
@@ -157,6 +164,7 @@ static struct _s_x limit_masks[] = {
* This is only used in this code.
*/
#define IPPROTO_ETHERTYPE 0x1000
+#define IPPROTO_ARPOP 0x1001
static struct _s_x ether_types[] = {
/*
* Note, we cannot use "-:&/" in the names because they are field
@@ -184,6 +192,15 @@ static struct _s_x ether_types[] = {
{ "ns", 0x0600 },
{ NULL, 0 }
};
+static struct _s_x arp_ops[] = {
+ { "request", ARPOP_REQUEST },
+ { "reply", ARPOP_REPLY },
+ { "rev_request", ARPOP_REVREQUEST },
+ { "rev_reply", ARPOP_REVREPLY },
+ { "inv_request", ARPOP_INVREQUEST },
+ { "inv_reply", ARPOP_INVREPLY },
+ { NULL, 0 }
+};
static struct _s_x rule_actions[] = {
@@ -271,9 +288,13 @@ static struct _s_x rule_options[] = {
{ "dst-port", TOK_DSTPORT },
{ "src-port", TOK_SRCPORT },
{ "proto", TOK_PROTO },
- { "MAC", TOK_MAC },
- { "mac", TOK_MAC },
- { "mac-type", TOK_MACTYPE },
+ { "MAC", TOK_ETHER },
+ { "mac", TOK_ETHER },
+ { "ether", TOK_ETHER },
+ { "src-ether", TOK_ETHER_SRC },
+ { "dst-ether", TOK_ETHER_DST },
+ { "mac-type", TOK_ETHER_TYPE },
+ { "ether-type", TOK_ETHER_TYPE },
{ "verrevpath", TOK_VERREVPATH },
{ "versrcreach", TOK_VERSRCREACH },
{ "antispoof", TOK_ANTISPOOF },
@@ -290,6 +311,11 @@ static struct _s_x rule_options[] = {
{ "dst-ip6", TOK_DSTIP6},
{ "src-ipv6", TOK_SRCIP6},
{ "src-ip6", TOK_SRCIP6},
+ { "state-options", TOK_STATEOPTS },
+ { "state-opts", TOK_STATEOPTS },
+ { "arp-op", TOK_ARP_OP},
+ { "src-arp", TOK_ARP_SRC},
+ { "dst-arp", TOK_ARP_DST},
{ "//", TOK_COMMENT },
{ "not", TOK_NOT }, /* pseudo option */
@@ -463,6 +489,13 @@ print_port(int proto, uint16_t port)
printf("%s", s);
else
printf("0x%04x", port);
+ } else if (proto == IPPROTO_ARPOP) {
+ char const *s;
+
+ if (co.do_resolv && (s = match_value(arp_ops, port)) )
+ printf("%s", s);
+ else
+ printf("0x%04x", port);
} else {
struct servent *se = NULL;
if (co.do_resolv) {
@@ -483,7 +516,8 @@ static struct _s_x _port_name[] = {
{"ipid", O_IPID},
{"iplen", O_IPLEN},
{"ipttl", O_IPTTL},
- {"mac-type", O_MAC_TYPE},
+ {"ether-type", O_ETHER_TYPE},
+ {"arp-op", O_ARP_OP},
{"tcpdatalen", O_TCPDATALEN},
{"tagged", O_TAGGED},
{NULL, 0}
@@ -524,6 +558,7 @@ print_newports(ipfw_insn_u16 *cmd, int proto, int opcode)
* In particular:
* proto == -1 disables the protocol check;
* proto == IPPROTO_ETHERTYPE looks up an internal table
+ * proto == IPPROTO_ARPOP looks up an internal table
* proto == <some value in /etc/protocols> matches the values there.
* Returns *end == s in case the parameter is not found.
*/
@@ -565,6 +600,13 @@ strtoport(char *s, char **end, int base, int proto)
*end = s1;
return i;
}
+ } else if (proto == IPPROTO_ARPOP) {
+ i = match_token(arp_ops, buf);
+ free(buf);
+ if (i != -1) { /* found */
+ *end = s1;
+ return i;
+ }
} else {
struct protoent *pe = NULL;
struct servent *se;
@@ -817,25 +859,24 @@ print_ip(ipfw_insn_ip *cmd, char const *s)
}
/*
- * prints a MAC address/mask pair
+ * prints a ethernet (MAC) address/mask pair
*/
-static void
-print_mac(uint8_t *addr, uint8_t *mask)
+void
+print_ether_addr(u_char *ea)
{
- int l = contigmask(mask, 48);
+ printf("%02x:%02x:%02x:%02x:%02x:%02x",
+ ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
+}
- if (l == 0)
- printf(" any");
- else {
- printf(" %02x:%02x:%02x:%02x:%02x:%02x",
- addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
- if (l == -1)
- printf("&%02x:%02x:%02x:%02x:%02x:%02x",
- mask[0], mask[1], mask[2],
- mask[3], mask[4], mask[5]);
- else if (l < 48)
- printf("/%d", l);
- }
+static void
+print_ether(ipfw_ether_addr *addr)
+{
+ if ((addr->flags & IPFW_EA_CHECK) == 0)
+ printf("any");
+ else if (addr->flags & IPFW_EA_MULTICAST)
+ printf("multicast");
+ else
+ print_ether_addr(addr->octet);
}
static void
@@ -884,7 +925,7 @@ print_icmptypes(ipfw_insn_u32 *cmd)
* The first argument is the list of fields we have, the second is
* the list of fields we want to be printed.
*
- * Special cases if we have provided a MAC header:
+ * Special cases if we have provided a ethernet header:
* + if the rule does not contain IP addresses/ports, do not print them;
* + if the rule does not contain an IP proto, print "all" instead of "ip";
*
@@ -1272,16 +1313,17 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
if (cmd->len & F_NOT && cmd->opcode != O_IN)
printf(" not");
switch(cmd->opcode) {
- case O_MACADDR2: {
- ipfw_insn_mac *m = (ipfw_insn_mac *)cmd;
+ case O_ETHER_SRC:
+ printf(" src-ether ");
+ print_ether(&((ipfw_insn_ether *)cmd)->ether);
+ break;
- printf(" MAC");
- print_mac(m->addr, m->mask);
- print_mac(m->addr + 6, m->mask + 6);
- }
+ case O_ETHER_DST:
+ printf(" dst-ether ");
+ print_ether(&((ipfw_insn_ether *)cmd)->ether);
break;
- case O_MAC_TYPE:
+ case O_ETHER_TYPE:
print_newports((ipfw_insn_u16 *)cmd,
IPPROTO_ETHERTYPE, cmd->opcode);
break;
@@ -1295,6 +1337,24 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
printf(" fib %u", cmd->arg1 );
break;
+ case O_ARP_OP:
+ print_newports((ipfw_insn_u16 *)cmd,
+ IPPROTO_ARPOP, cmd->opcode);
+ break;
+
+ case O_ARP_SRC_LOOKUP:
+ case O_ARP_DST_LOOKUP:
+ printf(" %s-arp table(%u",
+ cmd->opcode == O_ARP_DST_LOOKUP ?
+ "dst" : "src",
+ ((ipfw_insn *)cmd)->arg1);
+ if (F_LEN((ipfw_insn *)cmd) ==
+ F_INSN_SIZE(ipfw_insn_u32))
+ printf(",%u",
+ *((ipfw_insn_u32 *)cmd)->d);
+ printf(")");
+ break;
+
case O_IN:
printf(cmd->len & F_NOT ? " out" : " in");
break;
@@ -1462,6 +1522,10 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
comment = (char *)(cmd + 1);
break;
+ case O_STATEOPTS:
+ print_flags("state-options", cmd, f_stateopts);
+ break;
+
case O_KEEP_STATE:
printf(" keep-state");
break;
@@ -1500,9 +1564,14 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
break;
case O_TAGGED:
- if (F_LEN(cmd) == 1)
- PRINT_UINT_ARG(" tagged ", cmd->arg1);
- else
+ if (F_LEN(cmd) == 1) {
+ if (cmd->arg1 != 0)
+ PRINT_UINT_ARG(" tagged ",
+ cmd->arg1);
+ else {
+ printf(" tagged any");
+ }
+ } else
print_newports((ipfw_insn_u16 *)cmd, 0,
O_TAGGED);
break;
@@ -1561,13 +1630,13 @@ show_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth)
else
printf(" proto %u", d->id.proto);
- if (d->id.addr_type == 4) {
+ if (d->id.flow_type == IPFW_FLOW_IP4) {
a.s_addr = htonl(d->id.src_ip);
printf(" %s %d", inet_ntoa(a), d->id.src_port);
a.s_addr = htonl(d->id.dst_ip);
printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port);
- } else if (d->id.addr_type == 6) {
+ } else if (d->id.flow_type == IPFW_FLOW_IP6) {
printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf,
sizeof(buf)), d->id.src_port);
printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf,
@@ -2253,50 +2322,26 @@ fill_iface(ipfw_insn_if *cmd, char *arg)
}
static void
-get_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
+get_ether_addr(const char *p, ipfw_ether_addr *addr)
{
- int i, l;
- char *ap, *ptr, *optr;
- struct ether_addr *mac;
- const char *macset = "0123456789abcdefABCDEF:";
+ struct ether_addr *ether;
+ const char *etherset = "0123456789abcdefABCDEF:";
+ bzero(addr, sizeof(*addr));
if (strcmp(p, "any") == 0) {
- for (i = 0; i < ETHER_ADDR_LEN; i++)
- addr[i] = mask[i] = 0;
return;
}
-
- optr = ptr = strdup(p);
- if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) {
- l = strlen(ap);
- if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL)
- errx(EX_DATAERR, "Incorrect MAC address");
- bcopy(mac, addr, ETHER_ADDR_LEN);
- } else
- errx(EX_DATAERR, "Incorrect MAC address");
-
- if (ptr != NULL) { /* we have mask? */
- if (p[ptr - optr - 1] == '/') { /* mask len */
- l = strtol(ptr, &ap, 10);
- if (*ap != 0 || l > ETHER_ADDR_LEN * 8 || l < 0)
- errx(EX_DATAERR, "Incorrect mask length");
- for (i = 0; l > 0 && i < ETHER_ADDR_LEN; l -= 8, i++)
- mask[i] = (l >= 8) ? 0xff: (~0) << (8 - l);
- } else { /* mask */
- l = strlen(ptr);
- if (strspn(ptr, macset) != l ||
- (mac = ether_aton(ptr)) == NULL)
- errx(EX_DATAERR, "Incorrect mask");
- bcopy(mac, mask, ETHER_ADDR_LEN);
- }
- } else { /* default mask: ff:ff:ff:ff:ff:ff */
- for (i = 0; i < ETHER_ADDR_LEN; i++)
- mask[i] = 0xff;
+ if (strcmp(p, "multicast") == 0) {
+ addr->flags = IPFW_EA_CHECK | IPFW_EA_MULTICAST;
+ return;
}
- for (i = 0; i < ETHER_ADDR_LEN; i++)
- addr[i] &= mask[i];
- free(optr);
+ if (strspn(p, etherset) != strlen(p) ||
+ (ether = ether_aton(p)) == NULL)
+ errx(EX_DATAERR, "Incorrect ethernet (MAC) address");
+
+ memcpy(addr->octet, ether, ETHER_ADDR_LEN);
+ addr->flags = IPFW_EA_CHECK;
}
/*
@@ -2359,31 +2404,44 @@ fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
* two microinstructions, and returns the pointer to the last one.
*/
static ipfw_insn *
-add_mac(ipfw_insn *cmd, int ac, char *av[])
+add_ether(ipfw_insn *cmd, int opcode, char *arg)
{
- ipfw_insn_mac *mac;
-
- if (ac < 2)
- errx(EX_DATAERR, "MAC dst src");
+ ipfw_insn_ether *ether;
- cmd->opcode = O_MACADDR2;
- cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
+ cmd->opcode = opcode;
+ cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_ether);
- mac = (ipfw_insn_mac *)cmd;
- get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */
- get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]),
- &(mac->mask[ETHER_ADDR_LEN])); /* src */
+ ether = (ipfw_insn_ether *)cmd;
+ get_ether_addr(arg, &ether->ether);
return cmd;
}
static ipfw_insn *
-add_mactype(ipfw_insn *cmd, int ac, char *av)
+add_ether_src(ipfw_insn *cmd, int ac, char *av[])
+{
+ if (ac < 1)
+ errx(EX_DATAERR, "src-ether src");
+
+ return add_ether(cmd, O_ETHER_SRC, av[0]);
+}
+
+static ipfw_insn *
+add_ether_dst(ipfw_insn *cmd, int ac, char *av[])
+{
+ if (ac < 1)
+ errx(EX_DATAERR, "dst-ether dst");
+
+ return add_ether(cmd, O_ETHER_DST, av[0]);
+}
+
+static ipfw_insn *
+add_ethertype(ipfw_insn *cmd, int ac, char *av)
{
if (ac < 1)
- errx(EX_DATAERR, "missing MAC type");
+ errx(EX_DATAERR, "missing ether-type argument");
if (strcmp(av, "any") != 0) { /* we have a non-null type */
fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
- cmd->opcode = O_MAC_TYPE;
+ cmd->opcode = O_ETHER_TYPE;
return cmd;
} else
return NULL;
@@ -3399,16 +3457,31 @@ read_options:
*av);
break;
- case TOK_MAC:
- if (add_mac(cmd, ac, av)) {
- ac -= 2; av += 2;
+ case TOK_ETHER:
+ if (ac >= 2 && add_ether_dst(cmd, ac, av)) {
+ /*
+ * XXX will not allocate next command here
+ */
+ av[0] = strdup("src-ether");
}
break;
- case TOK_MACTYPE:
- NEED1("missing mac type");
- if (!add_mactype(cmd, ac, *av))
- errx(EX_DATAERR, "invalid mac type %s", *av);
+ case TOK_ETHER_SRC:
+ if (add_ether_src(cmd, ac, av)) {
+ ac--; av++;
+ }
+ break;
+
+ case TOK_ETHER_DST:
+ if (add_ether_dst(cmd, ac, av)) {
+ ac--; av++;
+ }
+ break;
+
+ case TOK_ETHER_TYPE:
+ NEED1("missing ether type");
+ if (!add_ethertype(cmd, ac, *av))
+ errx(EX_DATAERR, "invalid ether type %s", *av);
ac--; av++;
break;
@@ -3460,8 +3533,9 @@ read_options:
if (!add_ports(cmd, *av, 0, O_TAGGED))
errx(EX_DATAERR, "tagged: invalid tag"
" list: %s", *av);
- }
- else {
+ } else if (strcmp("any", *av) == 0) {
+ fill_cmd(cmd, O_TAGGED, 0, 0);
+ } else {
uint16_t tag;
GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX,
@@ -3477,6 +3551,41 @@ read_options:
ac--; av++;
break;
+ case TOK_ARP_OP:
+ NEED1("missing arp operation");
+ if (strcmp(*av, "any") != 0) {
+ if (!fill_newports((ipfw_insn_u16 *)cmd, *av,
+ IPPROTO_ARPOP))
+ errx(EX_DATAERR, "invalid arp operation"
+ " %s", *av);
+ cmd->opcode = O_ARP_OP;
+ }
+ ac--; av++;
+ break;
+
+ case TOK_STATEOPTS:
+ NEED1("missing argument for state-options");
+ fill_flags(cmd, O_STATEOPTS, f_stateopts, *av);
+ if ((cmd->arg1 >> 8) & 0xff) /* clear flags specified */
+ errx(EX_DATAERR, "invalid state-options %s",
+ *av);
+ ac--; av++;
+ break;
+
+ case TOK_ARP_SRC:
+ case TOK_ARP_DST:
+ NEED1("missing lookup table argument");
+ fill_ip((ipfw_insn_ip *)cmd, *av);
+ if (cmd->opcode != O_IP_DST_LOOKUP) /* table */
+ errx(EX_USAGE, "invalid lookup table %s\n",
+ *av);
+ if (i == TOK_ARP_DST)
+ cmd->opcode = O_ARP_DST_LOOKUP;
+ else
+ cmd->opcode = O_ARP_SRC_LOOKUP;
+ ac--; av++;
+ break;
+
default:
errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
}
@@ -3720,7 +3829,7 @@ ipfw_table_handler(int ac, char *av[])
do_add = **av == 'a';
ac--; av++;
if (!ac)
- errx(EX_USAGE, "IP address required");
+ errx(EX_USAGE, "Address required");
p = strchr(*av, '/');
if (p) {
*p++ = '\0';
@@ -3729,9 +3838,22 @@ ipfw_table_handler(int ac, char *av[])
errx(EX_DATAERR, "bad width ``%s''", p);
} else
ent.masklen = 32;
- if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
- errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
- ac--; av++;
+ if (strcmp(*av, "ether") == 0 || strcmp(*av, "any") == 0) {
+ ent.addr = INADDR_ANY;
+ ent.masklen = 0;
+ if ((*av)[0] == 'a') { /* any */
+ ac--; av++;
+ }
+ } else {
+ if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
+ errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
+ ac--; av++;
+ }
+ bzero(&ent.ether_addr, sizeof(ent.ether_addr));
+ if (ac >= 2 && strcmp(*av, "ether") == 0) {
+ get_ether_addr(av[1], &ent.ether_addr);
+ ac-=2; av+=2;
+ }
if (do_add && ac) {
unsigned int tval;
/* isdigit is a bit of a hack here.. */
@@ -3804,20 +3926,37 @@ table_list(ipfw_table_entry ent, int need_header)
printf("---table(%d)---\n", tbl->tbl);
for (a = 0; a < tbl->cnt; a++) {
unsigned int tval;
+ char tval_buf[128];
+ char tether_buf[128];
tval = tbl->ent[a].value;
if (co.do_value_as_ip) {
- char tbuf[128];
- strncpy(tbuf, inet_ntoa(*(struct in_addr *)
- &tbl->ent[a].addr), 127);
- /* inet_ntoa expects network order */
- tval = htonl(tval);
- printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
- inet_ntoa(*(struct in_addr *)&tval));
+ /* inet_ntoa expects network order */
+ tval = htonl(tval);
+ strlcpy(tval_buf, inet_ntoa(*(struct in_addr *)
+ &tval), sizeof(tval_buf));
+ } else {
+ snprintf(tval_buf, sizeof(tval_buf), "%u", tval);
+ }
+ if (tbl->ent[a].ether_addr.flags & IPFW_EA_CHECK) {
+ uint8_t *x = (uint8_t *)&tbl->ent[a].ether_addr;
+ if (tbl->ent[a].ether_addr.flags & IPFW_EA_MULTICAST)
+ strlcpy(tether_buf, "ether multicast ",
+ sizeof(tether_buf));
+ else
+ snprintf(tether_buf, sizeof(tether_buf),
+ "ether %02x:%02x:%02x:%02x:%02x:%02x ",
+ x[0], x[1], x[2], x[3], x[4], x[5]);
} else {
- printf("%s/%u %u\n",
- inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
- tbl->ent[a].masklen, tval);
+ tether_buf[0] = 0;
}
+
+ if (tbl->ent[a].addr == INADDR_ANY && tbl->ent[a].masklen == 0)
+ printf("any");
+ else
+ printf("%s/%u",
+ inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
+ tbl->ent[a].masklen);
+ printf(" %s%s\n", tether_buf, tval_buf);
}
free(tbl);
}
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index d3ce7fb..7f99c73 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -134,8 +134,10 @@ enum tokens {
TOK_TCPACK,
TOK_TCPWIN,
TOK_ICMPTYPES,
- TOK_MAC,
- TOK_MACTYPE,
+ TOK_ETHER,
+ TOK_ETHER_SRC,
+ TOK_ETHER_DST,
+ TOK_ETHER_TYPE,
TOK_VERREVPATH,
TOK_VERSRCREACH,
TOK_ANTISPOOF,
@@ -186,6 +188,12 @@ enum tokens {
TOK_FIB,
TOK_SETFIB,
+
+ TOK_STATEOPTS,
+
+ TOK_ARP_OP,
+ TOK_ARP_SRC,
+ TOK_ARP_DST,
};
/*
* the following macro returns an error message if we run out of
@@ -247,6 +255,8 @@ void ipfw_flush(int force);
void ipfw_zero(int ac, char *av[], int optname);
void ipfw_list(int ac, char *av[], int show_counters);
+void print_ether_addr(u_char *ea);
+
/* altq.c */
void altq_set_enabled(int enabled);
u_int32_t altq_name_to_qid(const char *name);
diff --git a/sbin/ipfw/main.c b/sbin/ipfw/main.c
index 3916057..1d2ea07 100644
--- a/sbin/ipfw/main.c
+++ b/sbin/ipfw/main.c
@@ -48,7 +48,8 @@ help(void)
" redirect_port linkspec|redirect_proto linkspec}\n"
"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
"set N {show|list|zero|resetlog|delete} [N{,N}] | flush\n"
-"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
+"table N {add ip[/bits] [ether ETHERADDR] [value] |\n"
+" delete ip[/bits] [ether ETHERADDR] | flush | list}\n"
"table all {flush | list}\n"
"\n"
"RULE-BODY: check-state [PARAMS] | ACTION [PARAMS] ADDR [OPTION_LIST]\n"
@@ -63,17 +64,20 @@ help(void)
"IP6ADDR: [not] { any | me | me6 | ip6/bits | IP6LIST }\n"
"IP6LIST: { ip6 | ip6/bits }[,IP6LIST]\n"
"IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n"
+"ETHERADDR: { any | multicast | ether }\n"
"OPTION_LIST: OPTION [OPTION_LIST]\n"
-"OPTION: bridged | diverted | diverted-loopback | diverted-output |\n"
+"OPTION: arp-op LIST | bridged | diverted | diverted-loopback |\n"
+" {dst-arp|src-arp} table(t[,v]) | diverted-output |\n"
" {dst-ip|src-ip} IPADDR | {dst-ip6|src-ip6|dst-ipv6|src-ipv6} IP6ADDR |\n"
" {dst-port|src-port} LIST |\n"
" estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n"
" iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n"
" ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n"
" icmp6types LIST | ext6hdr LIST | flow-id N[,N] | fib FIB |\n"
-" mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
-" setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
-" tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
+" {src-ether|dst-ether} ETHERADDR | ether-type LIST | proto LIST |\n"
+" {recv|xmit|via} {IF|IPADDR} | setup | {tcpack|tcpseq|tcpwin} NN |\n"
+" tcpflags SPEC | tcpoptions SPEC | tcpdatalen LIST |\n"
+" verrevpath | versrcreach | antispoof\n"
);
exit(0);
diff --git a/share/man/man4/bridge.4 b/share/man/man4/bridge.4
index 7d45356..5933036 100644
--- a/share/man/man4/bridge.4
+++ b/share/man/man4/bridge.4
@@ -187,6 +187,14 @@ Set to
to only allow IP packets to pass (subject to firewall rules), set to
.Li 0
to unconditionally pass all non-IP Ethernet frames.
+.It Va net.link.bridge.pfil_layer2_arp
+Set to
+.Li 1
+to enable layer2 ARP filtering with
+.Xr pfil 9 ,
+set to
+.Li 0
+to disable it.
.It Va net.link.bridge.pfil_member
Set to
.Li 1
@@ -208,36 +216,6 @@ to additionally filter on the physical interface for locally destined packets.
Set to
.Li 0
to disable this feature.
-.It Va net.link.bridge.ipfw
-Set to
-.Li 1
-to enable layer2 filtering with
-.Xr ipfirewall 4 ,
-set to
-.Li 0
-to disable it.
-This needs to be enabled for
-.Xr dummynet 4
-support.
-When
-.Va ipfw
-is enabled,
-.Va pfil_bridge
-and
-.Va pfil_member
-will be disabled so that IPFW
-is not run twice; these can be re-enabled if desired.
-.It Va net.link.bridge.ipfw_arp
-Set to
-.Li 1
-to enable layer2 ARP filtering with
-.Xr ipfirewall 4 ,
-set to
-.Li 0
-to disable it.
-Requires
-.Va ipfw
-to be enabled.
.El
.Pp
ARP and REVARP packets are forwarded without being filtered and others
diff --git a/sys/contrib/pf/net/pf.c b/sys/contrib/pf/net/pf.c
index ebe73c2..25db596 100644
--- a/sys/contrib/pf/net/pf.c
+++ b/sys/contrib/pf/net/pf.c
@@ -342,6 +342,8 @@ struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = {
kif, &key, PF_LAN_EXT); \
if (*state == NULL || (*state)->timeout == PFTM_PURGE) \
return (PF_DROP); \
+ if (!pf_state_match_addr_ether(*state, pd, direction)) \
+ return (PF_DROP); \
if (direction == PF_OUT && \
(((*state)->rule.ptr->rt == PF_ROUTETO && \
(*state)->rule.ptr->direction == PF_OUT) || \
@@ -706,6 +708,35 @@ pf_find_state_all(struct pf_state_cmp *key, u_int8_t tree, int *more)
}
}
+static __inline int
+pf_state_match_addr_ether(struct pf_state *state, struct pf_pdesc *pd,
+ int direction)
+{
+ struct pf_addr_ether *src, *dst;
+
+#ifdef __FreeBSD__
+ if ((state->local_flags & PFSTATE_ETHER) == 0)
+ return (1);
+#else
+ if ((state->rule.ptr->rule_flag & PFRULE_ETHERSTATE) == 0)
+ return (1);
+#endif
+
+ if (direction == PF_IN) {
+ src = &state->ext.addr_ether;
+ dst = &state->gwy.addr_ether;
+ } else {
+ src = &state->lan.addr_ether;
+ dst = &state->ext.addr_ether;
+ }
+
+ if (pf_match_addr_ether(src, &pd->src_ether, 1) &&
+ pf_match_addr_ether(dst, &pd->dst_ether, 1))
+ return (1);
+
+ return (0);
+}
+
void
pf_init_threshold(struct pf_threshold *threshold,
u_int32_t limit, u_int32_t seconds)
@@ -1972,7 +2003,11 @@ pf_send_tcp(const struct pf_rule *r, sa_family_t af,
#endif
h->ip_ttl = ttl ? ttl : V_ip_defttl;
h->ip_sum = 0;
- if (eh == NULL) {
+ /*
+ * XXX Condition is always true due to broken route structs
+ * initialization in eh != NULL case.
+ */
+ if (1 || eh == NULL) {
#ifdef __FreeBSD__
PF_UNLOCK();
ip_output(m, (void *)NULL, (void *)NULL, 0,
@@ -2154,6 +2189,27 @@ pf_match_addr(u_int8_t n, struct pf_addr *a, struct pf_addr *m,
}
int
+pf_match_addr_ether(struct pf_addr_ether *want, struct pf_addr_ether *a,
+ int match_empty)
+{
+ static struct pf_addr_ether mask = {
+ .octet = { 0xff, 0xff, 0xff, 0xff, 0xff,0xff },
+ .flags = 0
+ };
+ if (want == NULL || (want->flags & PFAE_CHECK) == 0)
+ return (1);
+ if (a == NULL || (a->flags & PFAE_CHECK) == 0)
+ return (match_empty);
+
+ if (want->flags & PFAE_MULTICAST) {
+ return (ETHER_IS_MULTICAST(a->octet));
+ }
+#define EA_CMP(x) (*((u_int64_t*)(x)) & *((u_int64_t*)&mask))
+ return (EA_CMP(want) == EA_CMP(a));
+#undef EA_CMP
+}
+
+int
pf_match(u_int8_t op, u_int32_t a1, u_int32_t a2, u_int32_t p)
{
switch (op) {
@@ -3351,14 +3407,14 @@ pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction,
r = r->skip[PF_SKIP_AF].ptr;
else if (r->proto && r->proto != IPPROTO_TCP)
r = r->skip[PF_SKIP_PROTO].ptr;
- else if (PF_MISMATCHAW(&r->src.addr, saddr, af,
- r->src.neg, kif))
+ else if (PF_MISMATCHAW_L2(&r->src.addr, saddr, &pd->src_ether,
+ af, r->src.neg, kif))
r = r->skip[PF_SKIP_SRC_ADDR].ptr;
else if (r->src.port_op && !pf_match_port(r->src.port_op,
r->src.port[0], r->src.port[1], th->th_sport))
r = r->skip[PF_SKIP_SRC_PORT].ptr;
- else if (PF_MISMATCHAW(&r->dst.addr, daddr, af,
- r->dst.neg, NULL))
+ else if (PF_MISMATCHAW_L2(&r->dst.addr, daddr, &pd->dst_ether,
+ af, r->dst.neg, NULL))
r = r->skip[PF_SKIP_DST_ADDR].ptr;
else if (r->dst.port_op && !pf_match_port(r->dst.port_op,
r->dst.port[0], r->dst.port[1], th->th_dport))
@@ -3545,11 +3601,15 @@ cleanup:
s->proto = IPPROTO_TCP;
s->direction = direction;
s->af = af;
+ if (r->rule_flag & PFRULE_ETHERSTATE)
+ s->local_flags |= PFSTATE_ETHER;
if (direction == PF_OUT) {
PF_ACPY(&s->gwy.addr, saddr, af);
s->gwy.port = th->th_sport; /* sport */
+ s->gwy.addr_ether = pd->src_ether;
PF_ACPY(&s->ext.addr, daddr, af);
s->ext.port = th->th_dport;
+ s->ext.addr_ether = pd->dst_ether;
if (nr != NULL) {
PF_ACPY(&s->lan.addr, &pd->baddr, af);
s->lan.port = bport;
@@ -3560,8 +3620,10 @@ cleanup:
} else {
PF_ACPY(&s->lan.addr, daddr, af);
s->lan.port = th->th_dport;
+ s->lan.addr_ether = pd->dst_ether;
PF_ACPY(&s->ext.addr, saddr, af);
s->ext.port = th->th_sport;
+ s->ext.addr_ether = pd->src_ether;
if (nr != NULL) {
PF_ACPY(&s->gwy.addr, &pd->baddr, af);
s->gwy.port = bport;
@@ -3776,14 +3838,14 @@ pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction,
r = r->skip[PF_SKIP_AF].ptr;
else if (r->proto && r->proto != IPPROTO_UDP)
r = r->skip[PF_SKIP_PROTO].ptr;
- else if (PF_MISMATCHAW(&r->src.addr, saddr, af,
- r->src.neg, kif))
+ else if (PF_MISMATCHAW_L2(&r->src.addr, saddr, &pd->src_ether,
+ af, r->src.neg, kif))
r = r->skip[PF_SKIP_SRC_ADDR].ptr;
else if (r->src.port_op && !pf_match_port(r->src.port_op,
r->src.port[0], r->src.port[1], uh->uh_sport))
r = r->skip[PF_SKIP_SRC_PORT].ptr;
- else if (PF_MISMATCHAW(&r->dst.addr, daddr, af,
- r->dst.neg, NULL))
+ else if (PF_MISMATCHAW_L2(&r->dst.addr, daddr, &pd->dst_ether,
+ af, r->dst.neg, NULL))
r = r->skip[PF_SKIP_DST_ADDR].ptr;
else if (r->dst.port_op && !pf_match_port(r->dst.port_op,
r->dst.port[0], r->dst.port[1], uh->uh_dport))
@@ -3945,11 +4007,15 @@ cleanup:
s->proto = IPPROTO_UDP;
s->direction = direction;
s->af = af;
+ if (r->rule_flag & PFRULE_ETHERSTATE)
+ s->local_flags |= PFSTATE_ETHER;
if (direction == PF_OUT) {
PF_ACPY(&s->gwy.addr, saddr, af);
s->gwy.port = uh->uh_sport;
+ s->gwy.addr_ether = pd->src_ether;
PF_ACPY(&s->ext.addr, daddr, af);
s->ext.port = uh->uh_dport;
+ s->ext.addr_ether = pd->dst_ether;
if (nr != NULL) {
PF_ACPY(&s->lan.addr, &pd->baddr, af);
s->lan.port = bport;
@@ -3960,8 +4026,10 @@ cleanup:
} else {
PF_ACPY(&s->lan.addr, daddr, af);
s->lan.port = uh->uh_dport;
+ s->lan.addr_ether = pd->dst_ether;
PF_ACPY(&s->ext.addr, saddr, af);
s->ext.port = uh->uh_sport;
+ s->ext.addr_ether = pd->src_ether;
if (nr != NULL) {
PF_ACPY(&s->gwy.addr, &pd->baddr, af);
s->gwy.port = bport;
@@ -4136,11 +4204,11 @@ pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction,
r = r->skip[PF_SKIP_AF].ptr;
else if (r->proto && r->proto != pd->proto)
r = r->skip[PF_SKIP_PROTO].ptr;
- else if (PF_MISMATCHAW(&r->src.addr, saddr, af,
- r->src.neg, kif))
+ else if (PF_MISMATCHAW_L2(&r->src.addr, saddr, &pd->src_ether,
+ af, r->src.neg, kif))
r = r->skip[PF_SKIP_SRC_ADDR].ptr;
- else if (PF_MISMATCHAW(&r->dst.addr, daddr, af,
- r->dst.neg, NULL))
+ else if (PF_MISMATCHAW_L2(&r->dst.addr, daddr, &pd->dst_ether,
+ af, r->dst.neg, NULL))
r = r->skip[PF_SKIP_DST_ADDR].ptr;
else if (r->type && r->type != icmptype + 1)
r = TAILQ_NEXT(r, entries);
@@ -4261,11 +4329,15 @@ cleanup:
s->proto = pd->proto;
s->direction = direction;
s->af = af;
+ if (r->rule_flag & PFRULE_ETHERSTATE)
+ s->local_flags |= PFSTATE_ETHER;
if (direction == PF_OUT) {
PF_ACPY(&s->gwy.addr, saddr, af);
s->gwy.port = nport;
+ s->gwy.addr_ether = pd->src_ether;
PF_ACPY(&s->ext.addr, daddr, af);
s->ext.port = 0;
+ s->ext.addr_ether = pd->dst_ether;
if (nr != NULL) {
PF_ACPY(&s->lan.addr, &pd->baddr, af);
s->lan.port = bport;
@@ -4276,8 +4348,10 @@ cleanup:
} else {
PF_ACPY(&s->lan.addr, daddr, af);
s->lan.port = nport;
+ s->lan.addr_ether = pd->dst_ether;
PF_ACPY(&s->ext.addr, saddr, af);
s->ext.port = 0;
+ s->ext.addr_ether = pd->src_ether;
if (nr != NULL) {
PF_ACPY(&s->gwy.addr, &pd->baddr, af);
s->gwy.port = bport;
@@ -4402,11 +4476,11 @@ pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
r = r->skip[PF_SKIP_AF].ptr;
else if (r->proto && r->proto != pd->proto)
r = r->skip[PF_SKIP_PROTO].ptr;
- else if (PF_MISMATCHAW(&r->src.addr, pd->src, af,
- r->src.neg, kif))
+ else if (PF_MISMATCHAW_L2(&r->src.addr, pd->src, &pd->src_ether,
+ af, r->src.neg, kif))
r = r->skip[PF_SKIP_SRC_ADDR].ptr;
- else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af,
- r->dst.neg, NULL))
+ else if (PF_MISMATCHAW_L2(&r->dst.addr, pd->dst, &pd->dst_ether,
+ af, r->dst.neg, NULL))
r = r->skip[PF_SKIP_DST_ADDR].ptr;
else if (r->tos && !(r->tos == pd->tos))
r = TAILQ_NEXT(r, entries);
@@ -4551,16 +4625,22 @@ cleanup:
s->proto = pd->proto;
s->direction = direction;
s->af = af;
+ if (r->rule_flag & PFRULE_ETHERSTATE)
+ s->local_flags |= PFSTATE_ETHER;
if (direction == PF_OUT) {
PF_ACPY(&s->gwy.addr, saddr, af);
+ s->gwy.addr_ether = pd->src_ether;
PF_ACPY(&s->ext.addr, daddr, af);
+ s->ext.addr_ether = pd->dst_ether;
if (nr != NULL)
PF_ACPY(&s->lan.addr, &pd->baddr, af);
else
PF_ACPY(&s->lan.addr, &s->gwy.addr, af);
} else {
PF_ACPY(&s->lan.addr, daddr, af);
+ s->lan.addr_ether = pd->dst_ether;
PF_ACPY(&s->ext.addr, saddr, af);
+ s->ext.addr_ether = pd->src_ether;
if (nr != NULL)
PF_ACPY(&s->gwy.addr, &pd->baddr, af);
else
@@ -4622,11 +4702,11 @@ pf_test_fragment(struct pf_rule **rm, int direction, struct pfi_kif *kif,
r = r->skip[PF_SKIP_AF].ptr;
else if (r->proto && r->proto != pd->proto)
r = r->skip[PF_SKIP_PROTO].ptr;
- else if (PF_MISMATCHAW(&r->src.addr, pd->src, af,
- r->src.neg, kif))
+ else if (PF_MISMATCHAW_L2(&r->src.addr, pd->src, &pd->src_ether,
+ af, r->src.neg, kif))
r = r->skip[PF_SKIP_SRC_ADDR].ptr;
- else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af,
- r->dst.neg, NULL))
+ else if (PF_MISMATCHAW_L2(&r->dst.addr, pd->dst, &pd->dst_ether,
+ af, r->dst.neg, NULL))
r = r->skip[PF_SKIP_DST_ADDR].ptr;
else if (r->tos && !(r->tos == pd->tos))
r = TAILQ_NEXT(r, entries);
@@ -7021,6 +7101,12 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0,
pd.tos = h->ip_tos;
pd.tot_len = ntohs(h->ip_len);
pd.eh = eh;
+ if (eh) {
+ memcpy(pd.src_ether.octet, eh->ether_shost, ETHER_ADDR_LEN);
+ pd.src_ether.flags = PFAE_CHECK;
+ memcpy(pd.dst_ether.octet, eh->ether_dhost, ETHER_ADDR_LEN);
+ pd.dst_ether.flags = PFAE_CHECK;
+ }
/* handle fragments that didn't get reassembled by normalization */
if (h->ip_off & htons(IP_MF | IP_OFFMASK)) {
@@ -7415,6 +7501,12 @@ pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0,
pd.tos = 0;
pd.tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr);
pd.eh = eh;
+ if (eh) {
+ memcpy(pd.src_ether.octet, eh->ether_shost, ETHER_ADDR_LEN);
+ pd.src_ether.flags = PFAE_CHECK;
+ memcpy(pd.dst_ether.octet, eh->ether_dhost, ETHER_ADDR_LEN);
+ pd.dst_ether.flags = PFAE_CHECK;
+ }
off = ((caddr_t)h - m->m_data) + sizeof(struct ip6_hdr);
pd.proto = h->ip6_nxt;
diff --git a/sys/contrib/pf/net/pf_ioctl.c b/sys/contrib/pf/net/pf_ioctl.c
index 514b7c3..71bab42 100644
--- a/sys/contrib/pf/net/pf_ioctl.c
+++ b/sys/contrib/pf/net/pf_ioctl.c
@@ -110,6 +110,7 @@ __FBSDID("$FreeBSD$");
#ifdef __FreeBSD__
#include <sys/md5.h>
+#include <net/ethernet.h>
#else
#include <dev/rndvar.h>
#include <crypto/md5.h>
@@ -3635,6 +3636,7 @@ pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
* byte order.
*/
struct ip *h = NULL;
+ struct m_tag *tag_ether_hdr;
int chk;
if ((*m)->m_pkthdr.len >= (int)sizeof(struct ip)) {
@@ -3643,7 +3645,10 @@ pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
HTONS(h->ip_len);
HTONS(h->ip_off);
}
- chk = pf_test(PF_IN, ifp, m, NULL, inp);
+ tag_ether_hdr = m_tag_locate(*m, MTAG_ETHER, MTAG_ETHER_HEADER, NULL);
+ chk = pf_test(PF_IN, ifp, m,
+ tag_ether_hdr ? (struct ether_header *)(tag_ether_hdr + 1) : NULL,
+ inp);
if (chk && *m) {
m_freem(*m);
*m = NULL;
@@ -3670,6 +3675,7 @@ pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
* byte order.
*/
struct ip *h = NULL;
+ struct m_tag *tag_ether_hdr;
int chk;
/* We need a proper CSUM befor we start (s. OpenBSD ip_output) */
@@ -3683,7 +3689,10 @@ pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
HTONS(h->ip_len);
HTONS(h->ip_off);
}
- chk = pf_test(PF_OUT, ifp, m, NULL, inp);
+ tag_ether_hdr = m_tag_locate(*m, MTAG_ETHER, MTAG_ETHER_HEADER, NULL);
+ chk = pf_test(PF_OUT, ifp, m,
+ tag_ether_hdr ? (struct ether_header *)(tag_ether_hdr + 1) : NULL,
+ inp);
if (chk && *m) {
m_freem(*m);
*m = NULL;
@@ -3706,6 +3715,7 @@ pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
/*
* IPv6 is not affected by ip_len/ip_off byte order changes.
*/
+ struct m_tag *tag_ether_hdr;
int chk;
/*
@@ -3713,8 +3723,10 @@ pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
* order to support scoped addresses. In order to support stateful
* filtering we have change this to lo0 as it is the case in IPv4.
*/
+ tag_ether_hdr = m_tag_locate(*m, MTAG_ETHER, MTAG_ETHER_HEADER, NULL);
chk = pf_test6(PF_IN, (*m)->m_flags & M_LOOP ? V_loif : ifp, m,
- NULL, inp);
+ tag_ether_hdr ? (struct ether_header *)(tag_ether_hdr + 1) : NULL,
+ inp);
if (chk && *m) {
m_freem(*m);
*m = NULL;
@@ -3729,6 +3741,7 @@ pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
/*
* IPv6 does not affected ip_len/ip_off byte order changes.
*/
+ struct m_tag *tag_ether_hdr;
int chk;
/* We need a proper CSUM befor we start (s. OpenBSD ip_output) */
@@ -3736,7 +3749,10 @@ pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
in_delayed_cksum(*m);
(*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
- chk = pf_test6(PF_OUT, ifp, m, NULL, inp);
+ tag_ether_hdr = m_tag_locate(*m, MTAG_ETHER, MTAG_ETHER_HEADER, NULL);
+ chk = pf_test6(PF_OUT, ifp, m,
+ tag_ether_hdr ? (struct ether_header *)(tag_ether_hdr + 1) : NULL,
+ inp);
if (chk && *m) {
m_freem(*m);
*m = NULL;
diff --git a/sys/contrib/pf/net/pf_table.c b/sys/contrib/pf/net/pf_table.c
index 40c9f67..a0e48d6 100644
--- a/sys/contrib/pf/net/pf_table.c
+++ b/sys/contrib/pf/net/pf_table.c
@@ -919,6 +919,7 @@ pfr_create_kentry(struct pfr_addr *ad, int intr)
ke->pfrke_net = ad->pfra_net;
ke->pfrke_not = ad->pfra_not;
ke->pfrke_intrpool = intr;
+ ke->pfrke_ether = ad->pfra_ether;
return (ke);
}
@@ -1147,6 +1148,7 @@ pfr_copyout_addr(struct pfr_addr *ad, struct pfr_kentry *ke)
ad->pfra_ip4addr = ke->pfrke_sa.sin.sin_addr;
else if (ad->pfra_af == AF_INET6)
ad->pfra_ip6addr = ke->pfrke_sa.sin6.sin6_addr;
+ ad->pfra_ether = ke->pfrke_ether;
}
int
@@ -2091,6 +2093,13 @@ pfr_lookup_table(struct pfr_table *tbl)
int
pfr_match_addr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af)
{
+ return pfr_match_addr_ether(kt, a, af, NULL);
+}
+
+int
+pfr_match_addr_ether(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af,
+ struct pf_addr_ether *ae)
+{
struct pfr_kentry *ke = NULL;
int match;
@@ -2117,7 +2126,10 @@ pfr_match_addr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af)
break;
#endif /* INET6 */
}
- match = (ke && !ke->pfrke_not);
+ match = (ke != NULL);
+ if (match && ae)
+ match = pf_match_addr_ether(&ke->pfrke_ether, ae, 0);
+ match = (match && !ke->pfrke_not);
if (match)
kt->pfrkt_match++;
else
diff --git a/sys/contrib/pf/net/pfvar.h b/sys/contrib/pf/net/pfvar.h
index de175b1..d30ed0a 100644
--- a/sys/contrib/pf/net/pfvar.h
+++ b/sys/contrib/pf/net/pfvar.h
@@ -165,6 +165,14 @@ struct pf_addr {
#define PFI_AFLAG_MODEMASK 0x07
#define PFI_AFLAG_NOALIAS 0x08
+#define PFAE_CHECK 0x01
+#define PFAE_MULTICAST 0x02
+
+struct pf_addr_ether {
+ u_int8_t octet[6];
+ u_int16_t flags;
+};
+
struct pf_addr_wrap {
union {
struct {
@@ -185,6 +193,7 @@ struct pf_addr_wrap {
int dyncnt;
int tblcnt;
} p;
+ struct pf_addr_ether addr_ether;
u_int8_t type; /* PF_ADDR_* */
u_int8_t iflags; /* PFI_AFLAG_* */
};
@@ -401,7 +410,7 @@ extern void destroy_pf_mutex(void);
#endif /* PF_INET6_ONLY */
#endif /* PF_INET_INET6 */
-#define PF_MISMATCHAW(aw, x, af, neg, ifp) \
+#define PF_MISMATCHAW_L2(aw, x, xl2, af, neg, ifp) \
( \
(((aw)->type == PF_ADDR_NOROUTE && \
pf_routable((x), (af), NULL)) || \
@@ -410,16 +419,25 @@ extern void destroy_pf_mutex(void);
((aw)->type == PF_ADDR_RTLABEL && \
!pf_rtlabel_match((x), (af), (aw))) || \
((aw)->type == PF_ADDR_TABLE && \
- !pfr_match_addr((aw)->p.tbl, (x), (af))) || \
+ !pfr_match_addr_ether((aw)->p.tbl, (x), \
+ (af), (xl2))) || \
((aw)->type == PF_ADDR_DYNIFTL && \
- !pfi_match_addr((aw)->p.dyn, (x), (af))) || \
+ !(pfi_match_addr((aw)->p.dyn, (x), (af)) && \
+ pf_match_addr_ether(&(aw)->addr_ether, \
+ (xl2), 0))) || \
((aw)->type == PF_ADDR_ADDRMASK && \
- !PF_AZERO(&(aw)->v.a.mask, (af)) && \
- !PF_MATCHA(0, &(aw)->v.a.addr, \
- &(aw)->v.a.mask, (x), (af))))) != \
+ !(PF_AZERO(&(aw)->v.a.mask, (af)) && \
+ ((aw)->addr_ether.flags & PFAE_CHECK) == 0) && \
+ !(PF_MATCHA(0, &(aw)->v.a.addr, \
+ &(aw)->v.a.mask, (x), (af)) && \
+ pf_match_addr_ether(&(aw)->addr_ether, \
+ (xl2), 0))))) != \
(neg) \
)
+#define PF_MISMATCHAW(aw, x, af, neg, ifp) \
+ PF_MISMATCHAW_L2(aw, x, NULL, af, neg, ifp)
+
struct pf_rule_uid {
uid_t uid[2];
@@ -690,6 +708,7 @@ struct pf_rule {
#define PFRULE_NOSYNC 0x0010
#define PFRULE_SRCTRACK 0x0020 /* track source states */
#define PFRULE_RULESRCTRACK 0x0040 /* per rule */
+#define PFRULE_ETHERSTATE 0x0080 /* per rule */
/* scrub flags */
#define PFRULE_NODF 0x0100
@@ -753,6 +772,8 @@ struct pf_state_scrub {
struct pf_state_host {
struct pf_addr addr;
+ struct pf_addr_ether
+ addr_ether;
u_int16_t port;
u_int16_t pad;
};
@@ -797,6 +818,7 @@ struct pf_state {
#ifdef __FreeBSD__
u_int8_t local_flags;
#define PFSTATE_EXPIRING 0x01
+#define PFSTATE_ETHER 0x02
#else
u_int8_t pad;
#endif
@@ -905,6 +927,7 @@ struct pfr_addr {
u_int8_t pfra_net;
u_int8_t pfra_not;
u_int8_t pfra_fback;
+ struct pf_addr_ether pfra_ether;
};
#define pfra_ip4addr pfra_u._pfra_ip4addr
#define pfra_ip6addr pfra_u._pfra_ip6addr
@@ -948,6 +971,7 @@ SLIST_HEAD(pfr_kentryworkq, pfr_kentry);
struct pfr_kentry {
struct radix_node pfrke_node[2];
union sockaddr_union pfrke_sa;
+ struct pf_addr_ether pfrke_ether;
u_int64_t pfrke_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX];
u_int64_t pfrke_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX];
SLIST_ENTRY(pfr_kentry) pfrke_workq;
@@ -1057,6 +1081,10 @@ struct pf_pdesc {
struct pf_addr *dst;
struct ether_header
*eh;
+ struct pf_addr_ether
+ src_ether;
+ struct pf_addr_ether
+ dst_ether;
struct pf_mtag *pf_mtag;
u_int16_t *ip_sum;
u_int32_t p_len; /* total length of payload */
@@ -1658,6 +1686,8 @@ int pflog_packet(struct pfi_kif *, struct mbuf *, sa_family_t, u_int8_t,
struct pf_pdesc *);
int pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *,
struct pf_addr *, sa_family_t);
+int pf_match_addr_ether(struct pf_addr_ether *, struct pf_addr_ether *,
+ int);
int pf_match(u_int8_t, u_int32_t, u_int32_t, u_int32_t);
int pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t);
int pf_match_uid(u_int8_t, uid_t, uid_t, uid_t);
@@ -1688,6 +1718,8 @@ int pf_socket_lookup(int, struct pf_pdesc *);
#endif
void pfr_initialize(void);
int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t);
+int pfr_match_addr_ether(struct pfr_ktable *, struct pf_addr *, sa_family_t,
+ struct pf_addr_ether *);
void pfr_update_stats(struct pfr_ktable *, struct pf_addr *, sa_family_t,
u_int64_t, int, int, int);
int pfr_pool_get(struct pfr_ktable *, int *, struct pf_addr *,
diff --git a/sys/net/ethernet.h b/sys/net/ethernet.h
index ae7341e..65f18fe 100644
--- a/sys/net/ethernet.h
+++ b/sys/net/ethernet.h
@@ -362,6 +362,10 @@ struct ether_addr {
} while (0)
#ifdef _KERNEL
+#include <net/pfil.h>
+
+#define MTAG_ETHER 1080579719
+#define MTAG_ETHER_HEADER 0
struct ifnet;
struct mbuf;
@@ -383,6 +387,8 @@ void ether_vlan_mtap(struct bpf_if *, struct mbuf *,
void *, u_int);
struct mbuf *ether_vlanencap(struct mbuf *, uint16_t);
+extern struct pfil_head ether_pfil_hook; /* Packet filter hooks */
+
#else /* _KERNEL */
#include <sys/cdefs.h>
diff --git a/sys/net/if.h b/sys/net/if.h
index f94b54a..c3bd460 100644
--- a/sys/net/if.h
+++ b/sys/net/if.h
@@ -151,6 +151,8 @@ struct if_data {
#define IFF_STATICARP 0x80000 /* (n) static ARP */
#define IFF_DYING 0x200000 /* (n) interface is winding down */
#define IFF_RENAMING 0x400000 /* (n) interface is being renamed */
+#define IFF_L2FILTER 0x800000 /* (n) perform layer2 filtering */
+#define IFF_L2TAG 0x1000000 /* (n) tag packets with layer2 header */
/*
* Old names for driver flags so that user space tools can continue to use
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index d3a55fd..6f7fff2 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -127,6 +127,7 @@ __FBSDID("$FreeBSD$");
#include <machine/in_cksum.h>
#include <netinet/if_ether.h> /* for struct arpcom */
#include <net/bridgestp.h>
+#include <net/ethernet.h> /* for ether_pfil_hook */
#include <net/if_bridgevar.h>
#include <net/if_llc.h>
#include <net/if_vlan_var.h>
@@ -350,16 +351,15 @@ SYSCTL_NODE(_net_link, IFT_BRIDGE, bridge, CTLFLAG_RW, 0, "Bridge");
static int pfil_onlyip = 1; /* only pass IP[46] packets when pfil is enabled */
static int pfil_bridge = 1; /* run pfil hooks on the bridge interface */
static int pfil_member = 1; /* run pfil hooks on the member interface */
-static int pfil_ipfw = 0; /* layer2 filter with ipfw */
-static int pfil_ipfw_arp = 0; /* layer2 filter with ipfw */
+static int pfil_layer2_arp = 0; /* layer2 filter with PFIL */
static int pfil_local_phys = 0; /* run pfil hooks on the physical interface for
locally destined packets */
static int log_stp = 0; /* log STP state changes */
static int bridge_inherit_mac = 0; /* share MAC with first bridge member */
SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_onlyip, CTLFLAG_RW,
&pfil_onlyip, 0, "Only pass IP packets when pfil is enabled");
-SYSCTL_INT(_net_link_bridge, OID_AUTO, ipfw_arp, CTLFLAG_RW,
- &pfil_ipfw_arp, 0, "Filter ARP packets through IPFW layer2");
+SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_layer2_arp, CTLFLAG_RW,
+ &pfil_layer2_arp, 0, "Filter ARP packets through PFIL layer2");
SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_bridge, CTLFLAG_RW,
&pfil_bridge, 0, "Packet filter on the bridge interface");
SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_member, CTLFLAG_RW,
@@ -520,39 +520,6 @@ DECLARE_MODULE(if_bridge, bridge_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
MODULE_DEPEND(if_bridge, bridgestp, 1, 1, 1);
/*
- * handler for net.link.bridge.pfil_ipfw
- */
-static int
-sysctl_pfil_ipfw(SYSCTL_HANDLER_ARGS)
-{
- int enable = pfil_ipfw;
- int error;
-
- error = sysctl_handle_int(oidp, &enable, 0, req);
- enable = (enable) ? 1 : 0;
-
- if (enable != pfil_ipfw) {
- pfil_ipfw = enable;
-
- /*
- * Disable pfil so that ipfw doesnt run twice, if the user
- * really wants both then they can re-enable pfil_bridge and/or
- * pfil_member. Also allow non-ip packets as ipfw can filter by
- * layer2 type.
- */
- if (pfil_ipfw) {
- pfil_onlyip = 0;
- pfil_bridge = 0;
- pfil_member = 0;
- }
- }
-
- return (error);
-}
-SYSCTL_PROC(_net_link_bridge, OID_AUTO, ipfw, CTLTYPE_INT|CTLFLAG_RW,
- &pfil_ipfw, 0, &sysctl_pfil_ipfw, "I", "Layer2 filter with IPFW");
-
-/*
* bridge_clone_create:
*
* Create a new bridge instance.
@@ -1788,23 +1755,26 @@ bridge_dummynet(struct mbuf *m, struct ifnet *ifp)
{
struct bridge_softc *sc;
- sc = ifp->if_bridge;
+ /*
+ * The packet originated from bridge interface itself
+ */
+ if (ifp->if_type == IFT_BRIDGE) {
+ sc = ifp->if_softc;
+ } else {
+ sc = ifp->if_bridge;
+ }
/*
- * The packet didnt originate from a member interface. This should only
- * ever happen if a member interface is removed while packets are
- * queued for it.
+ * The packet didnt originate from a member interface either. This
+ * should only ever happen if a member interface is removed while
+ * packets are queued for it.
*/
if (sc == NULL) {
m_freem(m);
return;
}
- if (PFIL_HOOKED(&inet_pfil_hook)
-#ifdef INET6
- || PFIL_HOOKED(&inet6_pfil_hook)
-#endif
- ) {
+ if (PFIL_HOOKED(&ether_pfil_hook)) {
if (bridge_pfil(&m, sc->sc_ifp, ifp, PFIL_OUT) != 0)
return;
if (m == NULL)
@@ -2933,7 +2903,7 @@ bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir)
{
int snap, error, i, hlen;
struct ether_header *eh1, eh2;
- struct ip_fw_args args;
+ struct m_tag *mtag_ether_header;
struct ip *ip;
struct llc llc1;
u_int16_t ether_type;
@@ -2946,7 +2916,8 @@ bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir)
KASSERT(M_WRITABLE(*mp), ("%s: modifying a shared mbuf", __func__));
#endif
- if (pfil_bridge == 0 && pfil_member == 0 && pfil_ipfw == 0)
+ if (pfil_bridge == 0 && pfil_member == 0 &&
+ !(bifp != NULL && (bifp->if_flags & IFF_L2FILTER)))
return (0); /* filtering is disabled */
i = min((*mp)->m_pkthdr.len, max_protohdr);
@@ -2988,7 +2959,7 @@ bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir)
switch (ether_type) {
case ETHERTYPE_ARP:
case ETHERTYPE_REVARP:
- if (pfil_ipfw_arp == 0)
+ if (pfil_layer2_arp == 0)
return (0); /* Automatically pass */
break;
@@ -3007,6 +2978,13 @@ bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir)
goto bad;
}
+ if (PFIL_HOOKED(&ether_pfil_hook) && dir == PFIL_OUT && bifp != NULL &&
+ (bifp->if_flags & IFF_L2FILTER)) {
+ if (pfil_run_hooks(&ether_pfil_hook, mp, bifp, PFIL_OUT, NULL) != 0 ||
+ *mp == NULL)
+ return EACCES;
+ }
+
/* Strip off the Ethernet header and keep a copy. */
m_copydata(*mp, 0, ETHER_HDR_LEN, (caddr_t) &eh2);
m_adj(*mp, ETHER_HDR_LEN);
@@ -3037,56 +3015,24 @@ bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir)
goto bad;
}
- if (ip_fw_chk_ptr && pfil_ipfw != 0 && dir == PFIL_OUT && ifp != NULL) {
- struct dn_pkt_tag *dn_tag;
-
- error = -1;
- dn_tag = ip_dn_claim_tag(*mp);
- if (dn_tag != NULL) {
- if (dn_tag->rule != NULL && V_fw_one_pass)
- /* packet already partially processed */
- goto ipfwpass;
- args.rule = dn_tag->rule; /* matching rule to restart */
- args.rule_id = dn_tag->rule_id;
- args.chain_id = dn_tag->chain_id;
- } else
- args.rule = NULL;
-
- args.m = *mp;
- args.oif = ifp;
- args.next_hop = NULL;
- args.eh = &eh2;
- args.inp = NULL; /* used by ipfw uid/gid/jail rules */
- i = ip_fw_chk_ptr(&args);
- *mp = args.m;
-
- if (*mp == NULL)
- return (error);
-
- if (ip_dn_io_ptr && (i == IP_FW_DUMMYNET)) {
-
- /* put the Ethernet header back on */
- M_PREPEND(*mp, ETHER_HDR_LEN, M_DONTWAIT);
- if (*mp == NULL)
- return (error);
- bcopy(&eh2, mtod(*mp, caddr_t), ETHER_HDR_LEN);
+ error = 0;
- /*
- * Pass the pkt to dummynet, which consumes it. The
- * packet will return to us via bridge_dummynet().
- */
- args.oif = ifp;
- ip_dn_io_ptr(mp, DN_TO_IFB_FWD, &args);
- return (error);
+ /* Add tag if member or bridge interface has IFF_L2TAG set */
+ if (IFF_L2TAG &
+ ((bifp ? bifp->if_flags : 0) | (ifp ? ifp->if_flags : 0))) {
+ mtag_ether_header = m_tag_locate(*mp, MTAG_ETHER,
+ MTAG_ETHER_HEADER, NULL);
+ if (mtag_ether_header == NULL) {
+ mtag_ether_header = m_tag_alloc(MTAG_ETHER,
+ MTAG_ETHER_HEADER, ETHER_HDR_LEN, M_NOWAIT);
+ if (mtag_ether_header != NULL) {
+ memcpy(mtag_ether_header + 1, &eh2,
+ ETHER_HDR_LEN);
+ m_tag_prepend(*mp, mtag_ether_header);
+ }
}
-
- if (i != IP_FW_PASS) /* drop */
- goto bad;
}
-ipfwpass:
- error = 0;
-
/*
* Run the packet through pfil
*/
@@ -3122,9 +3068,21 @@ ipfwpass:
if (*mp == NULL || error != 0) /* filter may consume */
break;
- if (pfil_bridge && dir == PFIL_IN && bifp != NULL)
+ if (pfil_bridge && dir == PFIL_IN && bifp != NULL) {
+ /*
+ * Mark packets as received from bridge interface.
+ * Without this hack ipfw can't distinguish filtering
+ * on bridge from filtering on member interface.
+ */
+ struct ifnet *orig_rcvif;
+
+ orig_rcvif = (*mp)->m_pkthdr.rcvif;
+ (*mp)->m_pkthdr.rcvif = bifp;
error = pfil_run_hooks(&inet_pfil_hook, mp, bifp,
dir, NULL);
+ if (*mp)
+ (*mp)->m_pkthdr.rcvif = orig_rcvif;
+ }
if (*mp == NULL || error != 0) /* filter may consume */
break;
@@ -3176,9 +3134,21 @@ ipfwpass:
if (*mp == NULL || error != 0) /* filter may consume */
break;
- if (pfil_bridge && dir == PFIL_IN && bifp != NULL)
+ if (pfil_bridge && dir == PFIL_IN && bifp != NULL) {
+ /*
+ * Mark packets as received from bridge interface.
+ * Without this hack ipfw can't distinguish filtering
+ * on bridge from filtering on member interface.
+ */
+ struct ifnet *orig_rcvif;
+
+ orig_rcvif = (*mp)->m_pkthdr.rcvif;
+ (*mp)->m_pkthdr.rcvif = bifp;
error = pfil_run_hooks(&inet6_pfil_hook, mp, bifp,
dir, NULL);
+ if (*mp)
+ (*mp)->m_pkthdr.rcvif = orig_rcvif;
+ }
break;
#endif
default:
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index bac2044..18fc129 100644
--- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -63,6 +63,7 @@
#include <net/if_bridgevar.h>
#include <net/if_vlan_var.h>
#include <net/if_llatbl.h>
+#include <net/pfil.h>
#include <net/pf_mtag.h>
#include <net/vnet.h>
@@ -70,8 +71,6 @@
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/if_ether.h>
-#include <netinet/ip_fw.h>
-#include <netinet/ip_dummynet.h>
#include <netinet/ip_var.h>
#endif
#ifdef INET6
@@ -144,13 +143,8 @@ MALLOC_DEFINE(M_ARPCOM, "arpcom", "802.* interface internals");
#define senderr(e) do { error = (e); goto bad;} while (0)
-#if defined(INET) || defined(INET6)
-int
-ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, int shared);
-static VNET_DEFINE(int, ether_ipfw);
-#define V_ether_ipfw VNET(ether_ipfw)
-#endif
-
+/* XXX_GK vnet */
+struct pfil_head ether_pfil_hook; /* Packet filter hooks */
/*
* Ethernet output routine.
@@ -432,18 +426,13 @@ bad: if (m != NULL)
int
ether_output_frame(struct ifnet *ifp, struct mbuf *m)
{
-#if defined(INET) || defined(INET6)
+ int error = 0;
- if (ip_fw_chk_ptr && V_ether_ipfw != 0) {
- if (ether_ipfw_chk(&m, ifp, 0) == 0) {
- if (m) {
- m_freem(m);
- return EACCES; /* pkt dropped */
- } else
- return 0; /* consumed e.g. in a pipe */
- }
- }
-#endif
+ if (PFIL_HOOKED(&ether_pfil_hook) && (ifp->if_flags & IFF_L2FILTER))
+ error = pfil_run_hooks(&ether_pfil_hook, &m, ifp, PFIL_OUT,
+ NULL);
+ if (m == NULL)
+ return 0; /* consumed e.g. in a pipe */
/*
* Queue message on interface, update output statistics if
@@ -452,109 +441,6 @@ ether_output_frame(struct ifnet *ifp, struct mbuf *m)
return ((ifp->if_transmit)(ifp, m));
}
-#if defined(INET) || defined(INET6)
-/*
- * ipfw processing for ethernet packets (in and out).
- * The second parameter is NULL from ether_demux, and ifp from
- * ether_output_frame.
- */
-int
-ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, int shared)
-{
- struct ether_header *eh;
- struct ether_header save_eh;
- struct mbuf *m;
- int i;
- struct ip_fw_args args;
- struct dn_pkt_tag *dn_tag;
-
- dn_tag = ip_dn_claim_tag(*m0);
-
- if (dn_tag != NULL) {
- if (dn_tag->rule != NULL && V_fw_one_pass)
- /* dummynet packet, already partially processed */
- return (1);
- args.rule = dn_tag->rule; /* matching rule to restart */
- args.rule_id = dn_tag->rule_id;
- args.chain_id = dn_tag->chain_id;
- } else
- args.rule = NULL;
-
- /*
- * I need some amt of data to be contiguous, and in case others need
- * the packet (shared==1) also better be in the first mbuf.
- */
- m = *m0;
- i = min( m->m_pkthdr.len, max_protohdr);
- if ( shared || m->m_len < i) {
- m = m_pullup(m, i);
- if (m == NULL) {
- *m0 = m;
- return 0;
- }
- }
- eh = mtod(m, struct ether_header *);
- save_eh = *eh; /* save copy for restore below */
- m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */
-
- args.m = m; /* the packet we are looking at */
- args.oif = dst; /* destination, if any */
- args.next_hop = NULL; /* we do not support forward yet */
- args.eh = &save_eh; /* MAC header for bridged/MAC packets */
- args.inp = NULL; /* used by ipfw uid/gid/jail rules */
- i = ip_fw_chk_ptr(&args);
- m = args.m;
- if (m != NULL) {
- /*
- * Restore Ethernet header, as needed, in case the
- * mbuf chain was replaced by ipfw.
- */
- M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT);
- if (m == NULL) {
- *m0 = m;
- return 0;
- }
- if (eh != mtod(m, struct ether_header *))
- bcopy(&save_eh, mtod(m, struct ether_header *),
- ETHER_HDR_LEN);
- }
- *m0 = m;
-
- if (i == IP_FW_DENY) /* drop */
- return 0;
-
- KASSERT(m != NULL, ("ether_ipfw_chk: m is NULL"));
-
- if (i == IP_FW_PASS) /* a PASS rule. */
- return 1;
-
- if (ip_dn_io_ptr && (i == IP_FW_DUMMYNET)) {
- /*
- * Pass the pkt to dummynet, which consumes it.
- * If shared, make a copy and keep the original.
- */
- if (shared) {
- m = m_copypacket(m, M_DONTWAIT);
- if (m == NULL)
- return 0;
- } else {
- /*
- * Pass the original to dummynet and
- * nothing back to the caller
- */
- *m0 = NULL ;
- }
- ip_dn_io_ptr(&m, dst ? DN_TO_ETH_OUT: DN_TO_ETH_DEMUX, &args);
- return 0;
- }
- /*
- * XXX at some point add support for divert/forward actions.
- * If none of the above matches, we have to drop the pkt.
- */
- return 0;
-}
-#endif
-
/*
* Process a received Ethernet packet; the packet is in the
* mbuf chain m with the ethernet header at the front.
@@ -762,6 +648,7 @@ void
ether_demux(struct ifnet *ifp, struct mbuf *m)
{
struct ether_header *eh;
+ struct m_tag *mtag_ether_header;
int isr;
u_short ether_type;
#if defined(NETATALK)
@@ -770,19 +657,16 @@ ether_demux(struct ifnet *ifp, struct mbuf *m)
KASSERT(ifp != NULL, ("%s: NULL interface pointer", __func__));
-#if defined(INET) || defined(INET6)
/*
- * Allow dummynet and/or ipfw to claim the frame.
+ * Allow pfil to claim the frame.
* Do not do this for PROMISC frames in case we are re-entered.
*/
- if (ip_fw_chk_ptr && V_ether_ipfw != 0 && !(m->m_flags & M_PROMISC)) {
- if (ether_ipfw_chk(&m, NULL, 0) == 0) {
- if (m)
- m_freem(m); /* dropped; free mbuf chain */
- return; /* consumed */
- }
+ if (PFIL_HOOKED(&ether_pfil_hook) && (ifp->if_flags & IFF_L2FILTER) &&
+ !(m->m_flags & M_PROMISC)) {
+ if (pfil_run_hooks(&ether_pfil_hook, &m, ifp, PFIL_IN, NULL) != 0 ||
+ m == NULL)
+ return;
}
-#endif
eh = mtod(m, struct ether_header *);
ether_type = ntohs(eh->ether_type);
@@ -814,6 +698,15 @@ ether_demux(struct ifnet *ifp, struct mbuf *m)
return;
}
+ if (ifp->if_flags & IFF_L2TAG) {
+ mtag_ether_header = m_tag_alloc(MTAG_ETHER, MTAG_ETHER_HEADER,
+ ETHER_HDR_LEN, M_NOWAIT);
+ if (mtag_ether_header != NULL) {
+ memcpy(mtag_ether_header + 1, eh, ETHER_HDR_LEN);
+ m_tag_prepend(m, mtag_ether_header);
+ }
+ }
+
/*
* Reset layer specific mbuf flags to avoid confusing upper layers.
* Strip off Ethernet header.
@@ -989,10 +882,6 @@ ether_ifdetach(struct ifnet *ifp)
SYSCTL_DECL(_net_link);
SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet");
-#if defined(INET) || defined(INET6)
-SYSCTL_VNET_INT(_net_link_ether, OID_AUTO, ipfw, CTLFLAG_RW,
- &VNET_NAME(ether_ipfw), 0, "Pass ether pkts through firewall");
-#endif
#if 0
/*
@@ -1248,10 +1137,16 @@ ether_free(void *com, u_char type)
static int
ether_modevent(module_t mod, int type, void *data)
{
+ int err;
switch (type) {
case MOD_LOAD:
if_register_com_alloc(IFT_ETHER, ether_alloc, ether_free);
+ ether_pfil_hook.ph_type = PFIL_TYPE_IFT;
+ ether_pfil_hook.ph_af = IFT_ETHER;
+ if ((err = pfil_head_register(&ether_pfil_hook)) != 0)
+ printf("%s: WARNING: unable to register pfil hook, "
+ "error %d\n", __func__, err);
break;
case MOD_UNLOAD:
if_deregister_com_alloc(IFT_ETHER);
diff --git a/sys/net/pfil.h b/sys/net/pfil.h
index 6ac750a..78d74e9 100644
--- a/sys/net/pfil.h
+++ b/sys/net/pfil.h
@@ -63,6 +63,7 @@ typedef TAILQ_HEAD(pfil_list, packet_filter_hook) pfil_list_t;
#define PFIL_TYPE_AF 1 /* key is AF_* type */
#define PFIL_TYPE_IFNET 2 /* key is ifnet pointer */
+#define PFIL_TYPE_IFT 3 /* key is IFT_* type */
struct pfil_head {
pfil_list_t ph_in;
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index 1e6feb4..ecb7d8e 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -95,8 +95,8 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_IP_DSTPORT, /* (n)port list:mask 4 byte ea */
O_PROTO, /* arg1=protocol */
- O_MACADDR2, /* 2 mac addr:mask */
- O_MAC_TYPE, /* same as srcport */
+ O_UNUSED_MACADDR2, /* 2 mac addr:mask */
+ O_ETHER_TYPE, /* same as srcport */
O_LAYER2, /* none */
O_IN, /* none */
@@ -193,6 +193,18 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_SETFIB, /* arg1=FIB number */
O_FIB, /* arg1=FIB desired fib number */
+ O_ETHER_SRC, /* ethernet (mac) addr */
+ O_ETHER_DST, /* ethernet (mac) addr */
+
+ O_STATEOPTS,
+
+ /*
+ * ARP opcodes
+ */
+ O_ARP_OP, /* same as srcport */
+ O_ARP_SRC_LOOKUP, /* arg1=table number, u32=value */
+ O_ARP_DST_LOOKUP, /* arg1=table number, u32=value */
+
O_LAST_OPCODE /* not an opcode! */
};
@@ -288,13 +300,21 @@ typedef struct _ipfw_insn_sa {
} ipfw_insn_sa;
/*
- * This is used for MAC addr-mask pairs.
+ * This is used for ethernet (MAC) address.
*/
-typedef struct _ipfw_insn_mac {
+
+#define IPFW_EA_CHECK 0x01
+#define IPFW_EA_MULTICAST 0x02
+
+typedef struct _ipfw_ether_addr {
+ u_char octet[6];
+ u_int16_t flags;
+} ipfw_ether_addr;
+
+typedef struct _ipfw_insn_ether {
ipfw_insn o;
- u_char addr[12]; /* dst[6] + src[6] */
- u_char mask[12]; /* dst[6] + src[6] */
-} ipfw_insn_mac;
+ ipfw_ether_addr ether;
+} ipfw_insn_ether;
/*
* This is used for interface match rules (recv xx, xmit xx).
@@ -499,14 +519,20 @@ struct ipfw_flow_id {
u_int8_t fib;
u_int8_t proto;
u_int8_t flags; /* protocol-specific flags */
- uint8_t addr_type; /* 4 = ipv4, 6 = ipv6, 1=ether ? */
+ uint8_t flow_type; /* IPFW_FLOW_* */
struct in6_addr dst_ip6; /* could also store MAC addr! */
struct in6_addr src_ip6;
u_int32_t flow_id6;
u_int32_t frag_id6;
+ u_int16_t tag;
+ u_int16_t _reserved0;
+ ipfw_ether_addr src_ether;
+ ipfw_ether_addr dst_ether;
};
-#define IS_IP6_FLOW_ID(id) ((id)->addr_type == 6)
+#define IPFW_FLOW_ETHER 1
+#define IPFW_FLOW_IP4 4
+#define IPFW_FLOW_IP6 6
/*
* Dynamic ipfw rule.
@@ -555,10 +581,16 @@ struct _ipfw_dyn_rule {
#define ICMP6_UNREACH_RST 0x100 /* fake ICMPv6 code (send a TCP RST) */
/*
+ * Definitions for state (dynamic rule) option names.
+ */
+#define IP_FW_STATEOPT_ETHER 0x01
+
+/*
* These are used for lookup tables.
*/
typedef struct _ipfw_table_entry {
in_addr_t addr; /* network address */
+ ipfw_ether_addr ether_addr; /* ethernet address */
u_int32_t value; /* value */
u_int16_t tbl; /* table number */
u_int8_t masklen; /* mask length */
@@ -610,6 +642,8 @@ struct _ip6dn_args {
struct route_in6 ro_pmtu_or;
};
+#define IP_FW_ARGS_LAYER2 0x01
+
/*
* Arguments for calling ipfw_chk() and dummynet_io(). We put them
* all into a structure because this way it is easier and more
@@ -622,7 +656,8 @@ struct ip_fw_args {
struct ip_fw *rule; /* matching rule */
uint32_t rule_id; /* matching rule id */
uint32_t chain_id; /* ruleset id */
- struct ether_header *eh; /* for bridged packets */
+ struct ether_header *eh; /* for saved ethernet header */
+ int flags;
struct ipfw_flow_id f_id; /* grabbed from IP header */
uint32_t cookie; /* a cookie depending on rule action */
@@ -640,8 +675,12 @@ struct ip_fw_args {
struct sockopt;
struct dn_flow_set;
-int ipfw_check_in(void *, struct mbuf **, struct ifnet *, int, struct inpcb *inp);
-int ipfw_check_out(void *, struct mbuf **, struct ifnet *, int, struct inpcb *inp);
+int ipfw_check_in(void *, struct mbuf **, struct ifnet *, int, struct inpcb *);
+int ipfw_check_out(void *, struct mbuf **, struct ifnet *, int, struct inpcb *);
+int ipfw_ether_check_in(void *, struct mbuf **, struct ifnet *, int,
+ struct inpcb *);
+int ipfw_ether_check_out(void *, struct mbuf **, struct ifnet *, int,
+ struct inpcb *);
int ipfw_chk(struct ip_fw_args *);
@@ -663,11 +702,15 @@ VNET_DECLARE(int, fw6_enable);
#define V_fw6_enable VNET(fw6_enable)
#endif
+struct ipfw_table_head {
+ struct radix_node_head *in_rnh, *ether_rnh;
+};
+
struct ip_fw_chain {
struct ip_fw *rules; /* list of rules */
struct ip_fw *reap; /* list of rules to reap */
LIST_HEAD(, cfg_nat) nat; /* list of nat entries */
- struct radix_node_head *tables[IPFW_TABLES_MAX];
+ struct ipfw_table_head tables[IPFW_TABLES_MAX];
struct rwlock rwmtx;
uint32_t id; /* ruleset id */
};
diff --git a/sys/netinet/ipfw/ip_dummynet.c b/sys/netinet/ipfw/ip_dummynet.c
index e961a55..9f23c6b 100644
--- a/sys/netinet/ipfw/ip_dummynet.c
+++ b/sys/netinet/ipfw/ip_dummynet.c
@@ -944,28 +944,49 @@ dummynet_send(struct mbuf *m)
m->m_nextpkt = NULL;
pkt = dn_tag_get(m);
switch (pkt->dn_dir) {
- case DN_TO_IP_OUT:
- ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
+ case DN_TO_IP_OUT: {
+ VNET_ASSERT(pkt->ifp != NULL);
+ CURVNET_SET(pkt->ifp->if_vnet);
+ ip_output(m, NULL, NULL, IP_FORWARDING,
+ NULL, NULL);
+ CURVNET_RESTORE();
+ }
break ;
- case DN_TO_IP_IN :
- ip = mtod(m, struct ip *);
- ip->ip_len = htons(ip->ip_len);
- ip->ip_off = htons(ip->ip_off);
- netisr_dispatch(NETISR_IP, m);
+ case DN_TO_IP_IN : {
+ VNET_ASSERT(m->m_pkthdr.rcvif != NULL);
+ CURVNET_SET(m->m_pkthdr.rcvif->if_vnet);
+ ip = mtod(m, struct ip *);
+ ip->ip_len = htons(ip->ip_len);
+ ip->ip_off = htons(ip->ip_off);
+ netisr_dispatch(NETISR_IP, m);
+ CURVNET_RESTORE();
+ }
break;
#ifdef INET6
- case DN_TO_IP6_IN:
- netisr_dispatch(NETISR_IPV6, m);
+ case DN_TO_IP6_IN: {
+ VNET_ASSERT(m->m_pkthdr.rcvif != NULL);
+ CURVNET_SET(m->m_pkthdr.rcvif->if_vnet);
+ netisr_dispatch(NETISR_IPV6, m);
+ CURVNET_RESTORE();
+ }
break;
- case DN_TO_IP6_OUT:
- ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL, NULL);
+ case DN_TO_IP6_OUT: {
+ VNET_ASSERT(pkt->ifp != NULL);
+ CURVNET_SET(pkt->ifp->if_vnet);
+ ip6_output(m, NULL, NULL, IPV6_FORWARDING,
+ NULL, NULL, NULL);
+ CURVNET_RESTORE();
+ }
break;
#endif
case DN_TO_IFB_FWD:
- if (bridge_dn_p != NULL)
+ if (bridge_dn_p != NULL) {
+ VNET_ASSERT(pkt->ifp != NULL);
+ CURVNET_SET(pkt->ifp->if_vnet);
((*bridge_dn_p)(m, pkt->ifp));
- else
+ CURVNET_RESTORE();
+ } else
printf("dummynet: if_bridge not loaded\n");
break;
@@ -981,10 +1002,19 @@ dummynet_send(struct mbuf *m)
"dropping packet\n");
break;
}
- ether_demux(m->m_pkthdr.rcvif, m);
+ {
+ VNET_ASSERT(m->m_pkthdr.rcvif != NULL);
+ CURVNET_SET(m->m_pkthdr.rcvif->if_vnet);
+ ether_demux(m->m_pkthdr.rcvif, m);
+ CURVNET_RESTORE();
+ }
break;
- case DN_TO_ETH_OUT:
- ether_output_frame(pkt->ifp, m);
+ case DN_TO_ETH_OUT: {
+ VNET_ASSERT(pkt->ifp != NULL);
+ CURVNET_SET(pkt->ifp->if_vnet);
+ ether_output_frame(pkt->ifp, m);
+ CURVNET_RESTORE();
+ }
break;
case DN_TO_DROP:
@@ -1072,7 +1102,8 @@ find_queue(struct dn_flow_set *fs, struct ipfw_flow_id *id)
{
int i = 0 ; /* we need i and q for new allocations */
struct dn_flow_queue *q, *prev;
- int is_v6 = IS_IP6_FLOW_ID(id);
+ uint16_t *dst_ether, *src_ether;
+ uint16_t *mask_dst_ether, *mask_src_ether;
if ( !(fs->flags_fs & DN_HAVE_FLOW_MASK) )
q = fs->rq[0] ;
@@ -1081,8 +1112,10 @@ find_queue(struct dn_flow_set *fs, struct ipfw_flow_id *id)
id->dst_port &= fs->flow_mask.dst_port ;
id->src_port &= fs->flow_mask.src_port ;
id->proto &= fs->flow_mask.proto ;
+ id->tag &= fs->flow_mask.tag;
id->flags = 0 ; /* we don't care about this one */
- if (is_v6) {
+ switch (id->flow_type) {
+ case IPFW_FLOW_IP6:
APPLY_MASK(&id->dst_ip6, &fs->flow_mask.dst_ip6);
APPLY_MASK(&id->src_ip6, &fs->flow_mask.src_ip6);
id->flow_id6 &= fs->flow_mask.flow_id6;
@@ -1110,7 +1143,8 @@ find_queue(struct dn_flow_set *fs, struct ipfw_flow_id *id)
(id->dst_port << 1) ^ (id->src_port) ^
(id->proto ) ^
(id->flow_id6);
- } else {
+ break;
+ case IPFW_FLOW_IP4:
id->dst_ip &= fs->flow_mask.dst_ip ;
id->src_ip &= fs->flow_mask.src_ip ;
@@ -1120,28 +1154,64 @@ find_queue(struct dn_flow_set *fs, struct ipfw_flow_id *id)
( (id->src_ip >> 16 ) & 0xffff ) ^
(id->dst_port << 1) ^ (id->src_port) ^
(id->proto );
+ break;
+ case IPFW_FLOW_ETHER:
+ src_ether = (uint16_t*)id->src_ether.octet;
+ dst_ether = (uint16_t*)id->dst_ether.octet;
+ mask_src_ether = (uint16_t*)fs->flow_mask.src_ether.octet;
+ mask_dst_ether = (uint16_t*)fs->flow_mask.dst_ether.octet;
+ src_ether[0] &= mask_src_ether[0];
+ src_ether[1] &= mask_src_ether[1];
+ src_ether[2] &= mask_src_ether[2];
+ dst_ether[0] &= mask_dst_ether[0];
+ dst_ether[1] &= mask_dst_ether[1];
+ dst_ether[2] &= mask_dst_ether[2];
+ i = (((src_ether[0] >> 1) | ((src_ether[2] & 1) << 15)) ^
+ ((src_ether[1] >> 1) | ((src_ether[0] & 1) << 15)) ^
+ ((src_ether[2] >> 1) | ((src_ether[1] & 1) << 15)) ^
+ dst_ether[0] ^
+ dst_ether[1] ^
+ dst_ether[2]);
+ break;
+ default:
+ return NULL;
+ }
+ if (fs->flow_mask.tag) {
+ i ^= id->tag;
}
i = i % fs->rq_size ;
/* finally, scan the current list for a match */
searches++ ;
for (prev=NULL, q = fs->rq[i] ; q ; ) {
search_steps++;
- if (is_v6 &&
+
+ if (id->flow_type == IPFW_FLOW_IP6 &&
IN6_ARE_ADDR_EQUAL(&id->dst_ip6,&q->id.dst_ip6) &&
IN6_ARE_ADDR_EQUAL(&id->src_ip6,&q->id.src_ip6) &&
id->dst_port == q->id.dst_port &&
id->src_port == q->id.src_port &&
id->proto == q->id.proto &&
id->flags == q->id.flags &&
- id->flow_id6 == q->id.flow_id6)
+ id->flow_id6 == q->id.flow_id6 &&
+ id->tag == q->id.tag)
break ; /* found */
- if (!is_v6 && id->dst_ip == q->id.dst_ip &&
+ if (id->flow_type == IPFW_FLOW_IP4 &&
+ id->dst_ip == q->id.dst_ip &&
id->src_ip == q->id.src_ip &&
id->dst_port == q->id.dst_port &&
id->src_port == q->id.src_port &&
id->proto == q->id.proto &&
- id->flags == q->id.flags)
+ id->flags == q->id.flags &&
+ id->tag == q->id.tag)
+ break ; /* found */
+
+ if (id->flow_type == IPFW_FLOW_ETHER &&
+ memcmp(id->src_ether.octet, q->id.src_ether.octet,
+ ETHER_ADDR_LEN) == 0 &&
+ memcmp(id->dst_ether.octet, q->id.dst_ether.octet,
+ ETHER_ADDR_LEN) == 0 &&
+ id->tag == q->id.tag)
break ; /* found */
/* No match. Check if we can expire the entry */
@@ -1511,8 +1581,7 @@ dummynet_io(struct mbuf **m0, int dir, struct ip_fw_args *fwa)
}
}
done:
- if (head == m && dir != DN_TO_IFB_FWD && dir != DN_TO_ETH_DEMUX &&
- dir != DN_TO_ETH_OUT) { /* Fast io. */
+ if (head == m) { /* Fast io. */
io_pkt_fast++;
if (m->m_nextpkt != NULL)
printf("dummynet: fast io: pkt chain detected!\n");
@@ -1521,8 +1590,12 @@ done:
*m0 = NULL; /* Normal io. */
DUMMYNET_UNLOCK();
- if (head != NULL)
+ if (head != NULL) {
+ printf("dummynet fast io\n");
+ MPASS(dir != DN_TO_IFB_FWD && dir != DN_TO_ETH_DEMUX &&
+ dir != DN_TO_ETH_OUT);
dummynet_send(head);
+ }
return (0);
dropit:
diff --git a/sys/netinet/ipfw/ip_fw2.c b/sys/netinet/ipfw/ip_fw2.c
index 4c08bc5..2f13ec6 100644
--- a/sys/netinet/ipfw/ip_fw2.c
+++ b/sys/netinet/ipfw/ip_fw2.c
@@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/priv.h>
#include <sys/proc.h>
+#include <sys/refcount.h>
#include <sys/rwlock.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
@@ -65,6 +66,7 @@ __FBSDID("$FreeBSD$");
#include <sys/ucred.h>
#include <net/ethernet.h> /* for ETHERTYPE_IP */
#include <net/if.h>
+#include <net/if_arp.h>
#include <net/radix.h>
#include <net/route.h>
#include <net/pf_mtag.h>
@@ -146,9 +148,48 @@ ipfw_nat_cfg_t *ipfw_nat_del_ptr;
ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr;
ipfw_nat_cfg_t *ipfw_nat_get_log_ptr;
+static __inline int ether_addr_allow(ipfw_ether_addr *want,
+ ipfw_ether_addr *a)
+{
+ static ipfw_ether_addr mask = {
+ .octet = { 0xff, 0xff, 0xff, 0xff, 0xff,0xff },
+ .flags = 0
+ };
+ if ((want->flags & IPFW_EA_CHECK) == 0)
+ return (1);
+
+ if ((a->flags & IPFW_EA_CHECK) == 0)
+ return (0);
+
+ if (want->flags & IPFW_EA_MULTICAST) {
+ return (ETHER_IS_MULTICAST(a->octet));
+ }
+
+#define EA_CMP(a) (*((u_int64_t*)(a)) & *((u_int64_t*)&mask))
+ return (EA_CMP(want) == EA_CMP(a));
+#undef EA_CMP
+}
+
+static __inline int ether_addr_allow_dyn(ipfw_ether_addr *want,
+ ipfw_ether_addr *a)
+{
+ if ((a->flags & IPFW_EA_CHECK) == 0) {
+ return (1);
+ }
+ return (ether_addr_allow(want, a));
+}
+
+struct table_entry_addr {
+ u_char len;
+ u_char __reserved;
+ struct ether_addr ether_addr;
+ in_addr_t in_addr;
+};
+
struct table_entry {
- struct radix_node rn[2];
- struct sockaddr_in addr, mask;
+ struct radix_node in_rn[2], ether_rn[2];
+ struct table_entry_addr addr, mask;
+ int refcnt;
u_int32_t value;
};
@@ -728,18 +769,6 @@ send_reject6(struct ip_fw_args *args, int code, u_int hlen, struct ip6_hdr *ip6)
}
m_freem(m);
} else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */
-#if 0
- /*
- * Unlike above, the mbufs need to line up with the ip6 hdr,
- * as the contents are read. We need to m_adj() the
- * needed amount.
- * The mbuf will however be thrown away so we can adjust it.
- * Remember we did an m_pullup on it already so we
- * can make some assumptions about contiguousness.
- */
- if (args->L3offset)
- m_adj(m, args->L3offset);
-#endif
icmp6_error(m, ICMP6_DST_UNREACH, code, 0);
} else
m_freem(m);
@@ -765,7 +794,6 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg,
struct ip *ip)
{
- struct ether_header *eh = args->eh;
char *action;
int limit_reached = 0;
char action2[40], proto[128], fragment[32];
@@ -895,7 +923,7 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
}
if (hlen == 0) { /* non-ip */
- snprintf(SNPARGS(proto, 0), "MAC");
+ snprintf(SNPARGS(proto, 0), "ether");
} else {
int len;
@@ -905,17 +933,18 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
#endif
struct icmphdr *icmp;
- struct tcphdr *tcp;
- struct udphdr *udp;
+ struct tcphdr *tcp = NULL;
+ struct udphdr *udp = NULL;
#ifdef INET6
struct ip6_hdr *ip6 = NULL;
struct icmp6_hdr *icmp6;
+ char ip6buf[INET6_ADDRSTRLEN];
#endif
src[0] = '\0';
dst[0] = '\0';
+ switch (args->f_id.flow_type) {
#ifdef INET6
- if (IS_IP6_FLOW_ID(&(args->f_id))) {
- char ip6buf[INET6_ADDRSTRLEN];
+ case IPFW_FLOW_IP6:
snprintf(src, sizeof(src), "[%s]",
ip6_sprintf(ip6buf, &args->f_id.src_ip6));
snprintf(dst, sizeof(dst), "[%s]",
@@ -924,14 +953,27 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
ip6 = (struct ip6_hdr *)ip;
tcp = (struct tcphdr *)(((char *)ip) + hlen);
udp = (struct udphdr *)(((char *)ip) + hlen);
- } else
+ break;
#endif
- {
+ case IPFW_FLOW_IP4:
tcp = L3HDR(struct tcphdr, ip);
udp = L3HDR(struct udphdr, ip);
inet_ntoa_r(ip->ip_src, src);
inet_ntoa_r(ip->ip_dst, dst);
+ break;
+
+ case IPFW_FLOW_ETHER:
+ snprintf(src, sizeof(src), "%6D",
+ args->f_id.src_ether.octet, ":");
+ snprintf(dst, sizeof(dst), "%6D",
+ args->f_id.dst_ether.octet, ":");
+ break;
+
+ default:
+ snprintf(src, sizeof(src), "<unsupported flow type %d>",
+ args->f_id.flow_type);
+ break;
}
switch (args->f_id.proto) {
@@ -989,7 +1031,7 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
}
#ifdef INET6
- if (IS_IP6_FLOW_ID(&(args->f_id))) {
+ if (args->f_id.flow_type == IPFW_FLOW_IP6) {
if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG))
snprintf(SNPARGS(fragment, 0),
" (frag %08x:%d@%d%s)",
@@ -999,15 +1041,10 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
(offset & IP6F_MORE_FRAG) ? "+" : "");
} else
#endif
- {
+ if (args->f_id.flow_type == IPFW_FLOW_IP4) {
int ip_off, ip_len;
- if (eh != NULL) { /* layer 2 packets are as on the wire */
- ip_off = ntohs(ip->ip_off);
- ip_len = ntohs(ip->ip_len);
- } else {
- ip_off = ip->ip_off;
- ip_len = ip->ip_len;
- }
+ ip_off = ip->ip_off;
+ ip_len = ip->ip_len;
if (ip_off & (IP_MF | IP_OFFMASK))
snprintf(SNPARGS(fragment, 0),
" (frag %d:%d@%d%s)",
@@ -1045,7 +1082,7 @@ hash_packet(struct ipfw_flow_id *id)
u_int32_t i;
#ifdef INET6
- if (IS_IP6_FLOW_ID(id))
+ if (id->flow_type == IPFW_FLOW_IP6)
i = hash_packet6(id);
else
#endif /* INET6 */
@@ -1065,7 +1102,7 @@ unlink_dyn_rule_print(struct ipfw_flow_id *id)
#endif
#ifdef INET6
- if (IS_IP6_FLOW_ID(id)) {
+ if (id->flow_type == IPFW_FLOW_IP6) {
ip6_sprintf(src, &id->src_ip6);
ip6_sprintf(dst, &id->dst_ip6);
} else
@@ -1212,7 +1249,7 @@ lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int *match_direction,
}
if (pkt->proto == q->id.proto &&
q->dyn_type != O_LIMIT_PARENT) {
- if (IS_IP6_FLOW_ID(pkt)) {
+ if (pkt->flow_type == IPFW_FLOW_IP6) {
if (IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6),
&(q->id.src_ip6)) &&
IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6),
@@ -1231,7 +1268,7 @@ lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int *match_direction,
dir = MATCH_REVERSE;
break;
}
- } else {
+ } else if (pkt->flow_type == IPFW_FLOW_IP4) {
if (pkt->src_ip == q->id.src_ip &&
pkt->dst_ip == q->id.dst_ip &&
pkt->src_port == q->id.src_port &&
@@ -1255,7 +1292,21 @@ next:
if (q == NULL)
goto done; /* q = NULL, not found */
- if ( prev != NULL) { /* found and not in front */
+ /*
+ * Only check {src,dst}_ether if it was specified in rule and packet
+ * mbuf has mtag_ether_header.
+ */
+ if (dir == MATCH_NONE ||
+ !ether_addr_allow_dyn(&q->id.src_ether,
+ (dir == MATCH_FORWARD ? &pkt->src_ether : &pkt->dst_ether)) ||
+ !ether_addr_allow_dyn(&q->id.dst_ether,
+ (dir == MATCH_FORWARD ? &pkt->dst_ether : &pkt->src_ether))) {
+ q = NULL;
+ dir = MATCH_NONE;
+ goto done;
+ }
+
+ if (prev != NULL) { /* found and not in front */
prev->next = q->next;
q->next = V_ipfw_dyn_v[i];
V_ipfw_dyn_v[i] = q;
@@ -1332,6 +1383,13 @@ lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction,
{
ipfw_dyn_rule *q;
+ if (pkt->flow_type != IPFW_FLOW_IP4 &&
+ pkt->flow_type != IPFW_FLOW_IP6) {
+ printf("ipfw: %s: unsupported flow type %u\n",
+ __func__, pkt->flow_type);
+ return (NULL);
+ }
+
IPFW_DYN_LOCK();
q = lookup_dyn_rule_locked(pkt, match_direction, tcp);
if (q == NULL)
@@ -1380,7 +1438,8 @@ realloc_dynamic_table(void)
* - "parent" rules for the above (O_LIMIT_PARENT).
*/
static ipfw_dyn_rule *
-add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule)
+add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule,
+ uint32_t stateopts)
{
ipfw_dyn_rule *r;
int i;
@@ -1412,6 +1471,10 @@ add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule)
}
r->id = *id;
+ if ((stateopts & IP_FW_STATEOPT_ETHER) == 0) {
+ r->id.src_ether.flags = 0;
+ r->id.dst_ether.flags = 0;
+ }
r->expire = time_uptime + V_dyn_syn_lifetime;
r->rule = rule;
r->dyn_type = dyn_type;
@@ -1433,7 +1496,7 @@ add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule)
#endif
#ifdef INET6
- if (IS_IP6_FLOW_ID(&(r->id))) {
+ if (r->id.flow_type == IPFW_FLOW_IP6) {
ip6_sprintf(src, &r->id.src_ip6);
ip6_sprintf(dst, &r->id.dst_ip6);
} else
@@ -1456,7 +1519,8 @@ add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule)
* If the lookup fails, then install one.
*/
static ipfw_dyn_rule *
-lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule)
+lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule,
+ uint32_t stateopts)
{
ipfw_dyn_rule *q;
int i;
@@ -1464,7 +1528,7 @@ lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule)
IPFW_DYN_LOCK_ASSERT();
if (V_ipfw_dyn_v) {
- int is_v6 = IS_IP6_FLOW_ID(pkt);
+ int is_v6 = (pkt->flow_type == IPFW_FLOW_IP6);
i = hash_packet( pkt );
for (q = V_ipfw_dyn_v[i] ; q != NULL ; q=q->next)
if (q->dyn_type == O_LIMIT_PARENT &&
@@ -1488,7 +1552,7 @@ lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule)
return q;
}
}
- return add_dyn_rule(pkt, O_LIMIT_PARENT, rule);
+ return add_dyn_rule(pkt, O_LIMIT_PARENT, rule, stateopts);
}
/**
@@ -1498,7 +1562,7 @@ lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule)
* session limitations are enforced.
*/
static int
-install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
+install_state(struct ip_fw *rule, uint32_t stateopts, ipfw_insn_limit *cmd,
struct ip_fw_args *args, uint32_t tablearg)
{
static int last_log;
@@ -1510,6 +1574,13 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
#endif
+ if (args->f_id.flow_type != IPFW_FLOW_IP4 &&
+ args->f_id.flow_type != IPFW_FLOW_IP6) {
+ printf("ipfw: %s: unsupported flow type %u\n",
+ __func__, args->f_id.flow_type);
+ return (1);
+ }
+
src[0] = '\0';
dst[0] = '\0';
@@ -1517,7 +1588,7 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
DEB(
#ifdef INET6
- if (IS_IP6_FLOW_ID(&(args->f_id))) {
+ if (args->f_id.flow_type == IPFW_FLOW_IP6) {
ip6_sprintf(src, &args->f_id.src_ip6);
ip6_sprintf(dst, &args->f_id.dst_ip6);
} else
@@ -1562,7 +1633,7 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
switch (cmd->o.opcode) {
case O_KEEP_STATE: /* bidir rule */
- add_dyn_rule(&args->f_id, O_KEEP_STATE, rule);
+ add_dyn_rule(&args->f_id, O_KEEP_STATE, rule, stateopts);
break;
case O_LIMIT: { /* limit number of sessions */
@@ -1585,10 +1656,10 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
id.dst_ip = id.src_ip = id.dst_port = id.src_port = 0;
id.proto = args->f_id.proto;
- id.addr_type = args->f_id.addr_type;
+ id.flow_type = args->f_id.flow_type;
id.fib = M_GETFIB(args->m);
- if (IS_IP6_FLOW_ID (&(args->f_id))) {
+ if (args->f_id.flow_type == IPFW_FLOW_IP6) {
if (limit_mask & DYN_SRC_ADDR)
id.src_ip6 = args->f_id.src_ip6;
if (limit_mask & DYN_DST_ADDR)
@@ -1603,7 +1674,7 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
id.src_port = args->f_id.src_port;
if (limit_mask & DYN_DST_PORT)
id.dst_port = args->f_id.dst_port;
- if ((parent = lookup_dyn_parent(&id, rule)) == NULL) {
+ if ((parent = lookup_dyn_parent(&id, rule, stateopts)) == NULL) {
printf("ipfw: %s: add parent failed\n", __func__);
IPFW_DYN_UNLOCK();
return (1);
@@ -1620,7 +1691,8 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
* XXX IPv6 flows are not
* supported yet.
*/
- if (IS_IP6_FLOW_ID(&(args->f_id))) {
+ if (args->f_id.flow_type ==
+ IPFW_FLOW_IP6) {
char ip6buf[INET6_ADDRSTRLEN];
snprintf(src, sizeof(src),
"[%s]", ip6_sprintf(ip6buf,
@@ -1650,7 +1722,8 @@ install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
return (1);
}
}
- add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent);
+ add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent,
+ stateopts);
break;
}
default:
@@ -1701,12 +1774,12 @@ send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq,
(void)replyto; /* don't warn about unused arg */
#endif
- switch (id->addr_type) {
- case 4:
+ switch (id->flow_type) {
+ case IPFW_FLOW_IP4:
len = sizeof(struct ip) + sizeof(struct tcphdr);
break;
#ifdef INET6
- case 6:
+ case IPFW_FLOW_IP6:
len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
break;
#endif
@@ -1723,8 +1796,8 @@ send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq,
m->m_pkthdr.rcvif = NULL;
bzero(m->m_data, len);
- switch (id->addr_type) {
- case 4:
+ switch (id->flow_type) {
+ case IPFW_FLOW_IP4:
h = mtod(m, struct ip *);
/* prepare for checksum */
@@ -1741,7 +1814,7 @@ send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq,
th = (struct tcphdr *)(h + 1);
break;
#ifdef INET6
- case 6:
+ case IPFW_FLOW_IP6:
h6 = mtod(m, struct ip6_hdr *);
/* prepare for checksum */
@@ -1788,8 +1861,8 @@ send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq,
th->th_flags = TH_ACK;
}
- switch (id->addr_type) {
- case 4:
+ switch (id->flow_type) {
+ case IPFW_FLOW_IP4:
th->th_sum = in_cksum(m, len);
/* finish the ip header */
@@ -1802,7 +1875,7 @@ send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq,
h->ip_sum = 0;
break;
#ifdef INET6
- case 6:
+ case IPFW_FLOW_IP6:
th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(*h6),
sizeof(struct tcphdr));
@@ -1823,22 +1896,7 @@ static void
send_reject(struct ip_fw_args *args, int code, int ip_len, struct ip *ip)
{
-#if 0
- /* XXX When ip is not guaranteed to be at mtod() we will
- * need to account for this */
- * The mbuf will however be thrown away so we can adjust it.
- * Remember we did an m_pullup on it already so we
- * can make some assumptions about contiguousness.
- */
- if (args->L3offset)
- m_adj(m, args->L3offset);
-#endif
if (code != ICMP_REJECT_RST) { /* Send an ICMP unreach */
- /* We need the IP header in host order for icmp_error(). */
- if (args->eh != NULL) {
- ip->ip_len = ntohs(ip->ip_len);
- ip->ip_off = ntohs(ip->ip_off);
- }
icmp_error(args->m, ICMP_UNREACH, code, 0L, 0);
} else if (args->f_id.proto == IPPROTO_TCP) {
struct tcphdr *const tcp =
@@ -1905,85 +1963,154 @@ lookup_next_rule(struct ip_fw *me, u_int32_t tablearg)
return rule;
}
+static void
+init_table_entry_addr(struct table_entry_addr *addr,
+ struct table_entry_addr *mask, in_addr_t in_addr, uint8_t mlen,
+ ipfw_ether_addr *ether_addr)
+{
+ addr->len = mask->len = sizeof(struct table_entry_addr);
+ mask->in_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
+ addr->in_addr = in_addr & mask->in_addr;
+ if (ether_addr && (ether_addr->flags & IPFW_EA_CHECK)) {
+ if (ether_addr->flags & IPFW_EA_MULTICAST) {
+ bzero(addr->ether_addr.octet, ETHER_ADDR_LEN);
+ addr->ether_addr.octet[0] = 0x01;
+ bzero(mask->ether_addr.octet, ETHER_ADDR_LEN);
+ mask->ether_addr.octet[0] = 0x01;
+ } else {
+ memcpy(addr->ether_addr.octet, ether_addr->octet,
+ ETHER_ADDR_LEN);
+ memset(mask->ether_addr.octet, 0xff, ETHER_ADDR_LEN);
+ }
+ } else {
+ /* set any ether addr */
+ bzero(addr->ether_addr.octet, ETHER_ADDR_LEN);
+ memset(mask->ether_addr.octet, 0xff, ETHER_ADDR_LEN);
+ }
+}
+
+static __inline struct table_entry *
+rn_to_table_entry(struct radix_node *_rn, int off)
+{
+ char *rn = (char*) _rn;
+
+ if (rn == NULL)
+ return NULL;
+ return (struct table_entry*)(rn - off);
+
+}
+
+#define RN_TO_ENT(e, r) (rn_to_table_entry(e, \
+ __offsetof(struct table_entry, r)))
+
+static __inline void
+release_table_entry(struct ipfw_table_head *th, struct table_entry *ent)
+{
+ IPFW_WLOCK_ASSERT(&V_layer3_chain); /* FIXME */
+
+ if (refcount_release(&ent->refcnt)) {
+ if (ent->in_rn[0].rn_flags)
+ th->in_rnh->rnh_deladdr(&ent->addr, &ent->mask,
+ th->in_rnh);
+ free(ent, M_IPFW_TBL);
+ }
+}
+
static int
add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
- uint8_t mlen, uint32_t value)
+ uint8_t mlen, ipfw_ether_addr *ether_addr, uint32_t value)
{
- struct radix_node_head *rnh;
- struct table_entry *ent;
- struct radix_node *rn;
+ struct ipfw_table_head *th;
+ struct table_entry *ent, *in_ent;
if (tbl >= IPFW_TABLES_MAX)
return (EINVAL);
- rnh = ch->tables[tbl];
- ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO);
+ th = &ch->tables[tbl];
+ ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
if (ent == NULL)
return (ENOMEM);
+ refcount_init(&ent->refcnt, 1);
ent->value = value;
- ent->addr.sin_len = ent->mask.sin_len = 8;
- ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
- ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr;
+ init_table_entry_addr(&ent->addr, &ent->mask, addr, mlen, ether_addr);
IPFW_WLOCK(ch);
- rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent);
- if (rn == NULL) {
+ if (th->ether_rnh->rnh_addaddr(&ent->addr, &ent->mask, th->ether_rnh,
+ ent->ether_rn) == NULL) {
IPFW_WUNLOCK(ch);
free(ent, M_IPFW_TBL);
return (EEXIST);
}
+ in_ent = RN_TO_ENT(th->in_rnh->rnh_lookup(&ent->addr, &ent->mask,
+ th->in_rnh), in_rn);
+ if (in_ent == NULL) {
+ in_ent = RN_TO_ENT(th->in_rnh->rnh_addaddr(&ent->addr,
+ &ent->mask, th->in_rnh, ent->in_rn), in_rn);
+ if (in_ent == NULL) {
+ th->ether_rnh->rnh_deladdr(&ent->addr, &ent->mask,
+ th->ether_rnh);
+ IPFW_WUNLOCK(ch);
+ free(ent, M_IPFW_TBL);
+ return (EEXIST);
+ }
+ }
+ refcount_acquire(&in_ent->refcnt);
IPFW_WUNLOCK(ch);
return (0);
}
+static __inline int
+delete_table_entry_rn(struct ipfw_table_head *th, void *addr, void *mask)
+{
+ struct table_entry *ent, *in_ent;
+
+ ent = RN_TO_ENT(th->ether_rnh->rnh_deladdr(addr, mask, th->ether_rnh),
+ ether_rn);
+ if (ent == NULL)
+ return (ESRCH);
+ in_ent = RN_TO_ENT(th->in_rnh->rnh_lookup(&ent->addr, &ent->mask,
+ th->in_rnh), in_rn);
+ release_table_entry(th, in_ent);
+ release_table_entry(th, ent);
+ return (0);
+}
+
static int
del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
- uint8_t mlen)
+ uint8_t mlen, ipfw_ether_addr *ether_addr)
{
- struct radix_node_head *rnh;
- struct table_entry *ent;
- struct sockaddr_in sa, mask;
+ struct ipfw_table_head *th;
+ struct table_entry_addr sa, mask;
+ int err;
if (tbl >= IPFW_TABLES_MAX)
return (EINVAL);
- rnh = ch->tables[tbl];
- sa.sin_len = mask.sin_len = 8;
- mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
- sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
+ th = &ch->tables[tbl];
+ init_table_entry_addr(&sa, &mask, addr, mlen, ether_addr);
IPFW_WLOCK(ch);
- ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh);
- if (ent == NULL) {
- IPFW_WUNLOCK(ch);
- return (ESRCH);
- }
+ err = delete_table_entry_rn(th, &sa, &mask);
IPFW_WUNLOCK(ch);
- free(ent, M_IPFW_TBL);
- return (0);
+ return (err);
}
static int
flush_table_entry(struct radix_node *rn, void *arg)
{
- struct radix_node_head * const rnh = arg;
- struct table_entry *ent;
-
- ent = (struct table_entry *)
- rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
- if (ent != NULL)
- free(ent, M_IPFW_TBL);
+ delete_table_entry_rn((struct ipfw_table_head *)arg, rn->rn_key,
+ rn->rn_mask);
return (0);
}
static int
flush_table(struct ip_fw_chain *ch, uint16_t tbl)
{
- struct radix_node_head *rnh;
+ struct ipfw_table_head *th;
IPFW_WLOCK_ASSERT(ch);
if (tbl >= IPFW_TABLES_MAX)
return (EINVAL);
- rnh = ch->tables[tbl];
- KASSERT(rnh != NULL, ("NULL IPFW table"));
- rnh->rnh_walktree(rnh, flush_table_entry, rnh);
+ th = &ch->tables[tbl];
+ KASSERT(th->ether_rnh != NULL, ("NULL IPFW table"));
+ th->ether_rnh->rnh_walktree(th->ether_rnh, flush_table_entry, th);
return (0);
}
@@ -2005,7 +2132,12 @@ init_tables(struct ip_fw_chain *ch)
uint16_t j;
for (i = 0; i < IPFW_TABLES_MAX; i++) {
- if (!rn_inithead((void **)&ch->tables[i], 32)) {
+ struct ipfw_table_head *th = &ch->tables[i];
+
+ if (!rn_inithead((void**)&(th->in_rnh),
+ __offsetof(struct table_entry_addr, in_addr) * 8) ||
+ !rn_inithead((void**)&(th->ether_rnh),
+ __offsetof(struct table_entry_addr, ether_addr) * 8)) {
for (j = 0; j < i; j++) {
(void) flush_table(ch, j);
}
@@ -2017,18 +2149,37 @@ init_tables(struct ip_fw_chain *ch)
static int
lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
- uint32_t *val)
+ ipfw_ether_addr *ether_addr, uint32_t *val)
{
- struct radix_node_head *rnh;
- struct table_entry *ent;
- struct sockaddr_in sa;
+ const int has_ether_addr = (ether_addr &&
+ (ether_addr->flags & IPFW_EA_CHECK));
+ const int has_in_addr = (addr != INADDR_ANY);
+ struct ipfw_table_head *th;
+ struct table_entry_addr sa, mask;
+ struct table_entry *ent = NULL;
if (tbl >= IPFW_TABLES_MAX)
return (0);
- rnh = ch->tables[tbl];
- sa.sin_len = 8;
- sa.sin_addr.s_addr = addr;
- ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh));
+ th = &ch->tables[tbl];
+ init_table_entry_addr(&sa, &mask, addr, (addr == INADDR_ANY ? 0 : 32),
+ ether_addr);
+ if (has_ether_addr) {
+ ent = RN_TO_ENT(th->ether_rnh->rnh_lookup(&sa, NULL,
+ th->ether_rnh), ether_rn);
+ if (ent == NULL && has_in_addr) {
+ /*
+ * Try to lookup entry with any (zero) ether_addr. It's
+ * handled this way not to deal with non-continuous
+ * masks in radix trees.
+ */
+ bzero(sa.ether_addr.octet, ETHER_ADDR_LEN);
+ ent = RN_TO_ENT(th->ether_rnh->rnh_lookup(&sa, NULL,
+ th->ether_rnh), ether_rn);
+ }
+ } else if (has_in_addr) {
+ ent = RN_TO_ENT(th->in_rnh->rnh_lookup(&sa, NULL, th->in_rnh),
+ in_rn);
+ }
if (ent != NULL) {
*val = ent->value;
return (1);
@@ -2048,20 +2199,20 @@ count_table_entry(struct radix_node *rn, void *arg)
static int
count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt)
{
- struct radix_node_head *rnh;
+ struct ipfw_table_head *th;
if (tbl >= IPFW_TABLES_MAX)
return (EINVAL);
- rnh = ch->tables[tbl];
+ th = &ch->tables[tbl];
*cnt = 0;
- rnh->rnh_walktree(rnh, count_table_entry, cnt);
+ th->ether_rnh->rnh_walktree(th->ether_rnh, count_table_entry, cnt);
return (0);
}
static int
dump_table_entry(struct radix_node *rn, void *arg)
{
- struct table_entry * const n = (struct table_entry *)rn;
+ struct table_entry * const n = RN_TO_ENT(rn, ether_rn);
ipfw_table * const tbl = arg;
ipfw_table_entry *ent;
@@ -2069,11 +2220,24 @@ dump_table_entry(struct radix_node *rn, void *arg)
return (1);
ent = &tbl->ent[tbl->cnt];
ent->tbl = tbl->tbl;
- if (in_nullhost(n->mask.sin_addr))
+ if (n->mask.in_addr == INADDR_ANY)
ent->masklen = 0;
else
- ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr));
- ent->addr = n->addr.sin_addr.s_addr;
+ ent->masklen = 33 - ffs(ntohl(n->mask.in_addr));
+ ent->addr = n->addr.in_addr;
+ memcpy(ent->ether_addr.octet, n->addr.ether_addr.octet, ETHER_ADDR_LEN);
+ ent->ether_addr.flags = 0;
+
+#define __ETHER_IS_ZERO(a) (((a)[0] | (a)[1] | (a)[2] | (a)[3] | (a)[4] | \
+ (a)[5]) == 0)
+ if (!__ETHER_IS_ZERO(n->mask.ether_addr.octet) &&
+ !__ETHER_IS_ZERO(n->addr.ether_addr.octet)) {
+ ent->ether_addr.flags = IPFW_EA_CHECK;
+ /* Should be fixed after adding new flags */
+ if (n->mask.ether_addr.octet[0] == 0x01)
+ ent->ether_addr.flags |= IPFW_EA_MULTICAST;
+ }
+#undef __ETHER_IS_ZERO
ent->value = n->value;
tbl->cnt++;
return (0);
@@ -2082,13 +2246,13 @@ dump_table_entry(struct radix_node *rn, void *arg)
static int
dump_table(struct ip_fw_chain *ch, ipfw_table *tbl)
{
- struct radix_node_head *rnh;
+ struct ipfw_table_head *th;
if (tbl->tbl >= IPFW_TABLES_MAX)
return (EINVAL);
- rnh = ch->tables[tbl->tbl];
+ th = &ch->tables[tbl->tbl];
tbl->cnt = 0;
- rnh->rnh_walktree(rnh, dump_table_entry, tbl);
+ th->ether_rnh->rnh_walktree(th->ether_rnh, dump_table_entry, tbl);
return (0);
}
@@ -2177,10 +2341,9 @@ check_uidgid(ipfw_insn_u32 *insn, int proto, struct ifnet *oif,
* Parameters:
*
* args->m (in/out) The packet; we set to NULL when/if we nuke it.
- * Starts with the IP header.
- * args->eh (in) Mac header if present, or NULL for layer3 packet.
- * args->L3offset Number of bytes bypassed if we came from L2.
- * e.g. often sizeof(eh) ** NOTYET **
+ * Starts with the IP header or with layer2 header if
+ * IP_FW_ARGS_LAYER2 is set in args->flags.
+ * args->eh (in) ethernet header if present, or NULL for layer3 packet.
* args->oif Outgoing interface, or NULL if packet is incoming.
* The incoming interface is in the mbuf. (in)
* args->divert_rule (in/out)
@@ -2191,6 +2354,7 @@ check_uidgid(ipfw_insn_u32 *insn, int proto, struct ifnet *oif,
* args->next_hop Socket we are forwarding to (out).
* args->f_id Addresses grabbed from the packet (out)
* args->cookie a cookie depending on rule action
+ * args->flags Flags
*
* Return value:
*
@@ -2215,10 +2379,8 @@ ipfw_chk(struct ip_fw_args *args)
* the implementation of the various instructions to make sure
* that they still work.
*
- * args->eh The MAC header. It is non-null for a layer2
+ * args->eh The ethernet header. It is non-null for a layer2
* packet, it is NULL for a layer-3 packet.
- * **notyet**
- * args->L3offset Offset in the packet to the L3 (IP or equiv.) header.
*
* m | args->m Pointer to the mbuf, as received from the caller.
* It may change if ipfw_chk() does an m_pullup, or if it
@@ -2226,12 +2388,10 @@ ipfw_chk(struct ip_fw_args *args)
* XXX This has to change, so that ipfw_chk() never modifies
* or consumes the buffer.
* ip is the beginning of the ip(4 or 6) header.
- * Calculated by adding the L3offset to the start of data.
- * (Until we start using L3offset, the packet is
* supposed to start with the ip header).
*/
struct mbuf *m = args->m;
- struct ip *ip = mtod(m, struct ip *);
+ struct ip *ip = NULL;
/*
* For rules which contain uid/gid or jail constraints, cache
@@ -2327,6 +2487,9 @@ ipfw_chk(struct ip_fw_args *args)
if (m->m_flags & M_SKIP_FIREWALL || (! V_ipfw_vnet_ready))
return (IP_FW_PASS); /* accept */
+ if ((args->flags & IP_FW_ARGS_LAYER2) == 0)
+ ip = mtod(m, struct ip *);
+
dst_ip.s_addr = 0; /* make sure it is initialized */
src_ip.s_addr = 0; /* make sure it is initialized */
pktlen = m->m_pkthdr.len;
@@ -2354,15 +2517,25 @@ do { \
/*
* if we have an ether header,
*/
- if (args->eh)
+ if (args->eh != NULL) {
etype = ntohs(args->eh->ether_type);
+ memcpy(args->f_id.src_ether.octet, args->eh->ether_shost,
+ ETHER_ADDR_LEN);
+ args->f_id.src_ether.flags = IPFW_EA_CHECK;
+ memcpy(args->f_id.dst_ether.octet, args->eh->ether_dhost,
+ ETHER_ADDR_LEN);
+ args->f_id.dst_ether.flags = IPFW_EA_CHECK;
+ } else {
+ args->f_id.src_ether.flags = 0;
+ args->f_id.dst_ether.flags = 0;
+ }
/* Identify IP packets and fill up variables. */
if (pktlen >= sizeof(struct ip6_hdr) &&
- (args->eh == NULL || etype == ETHERTYPE_IPV6) && ip->ip_v == 6) {
+ (args->flags & IP_FW_ARGS_LAYER2) == 0 && ip->ip_v == 6) {
struct ip6_hdr *ip6 = (struct ip6_hdr *)ip;
is_ipv6 = 1;
- args->f_id.addr_type = 6;
+ args->f_id.flow_type = IPFW_FLOW_IP6;
hlen = sizeof(struct ip6_hdr);
proto = ip6->ip6_nxt;
@@ -2524,10 +2697,10 @@ do { \
args->f_id.dst_ip = 0;
args->f_id.flow_id6 = ntohl(ip6->ip6_flow);
} else if (pktlen >= sizeof(struct ip) &&
- (args->eh == NULL || etype == ETHERTYPE_IP) && ip->ip_v == 4) {
+ (args->flags & IP_FW_ARGS_LAYER2) == 0 && ip->ip_v == 4) {
is_ipv4 = 1;
hlen = ip->ip_hl << 2;
- args->f_id.addr_type = 4;
+ args->f_id.flow_type = IPFW_FLOW_IP4;
/*
* Collect parameters into local variables for faster matching.
@@ -2535,13 +2708,8 @@ do { \
proto = ip->ip_p;
src_ip = ip->ip_src;
dst_ip = ip->ip_dst;
- if (args->eh != NULL) { /* layer 2 packets are as on the wire */
- offset = ntohs(ip->ip_off) & IP_OFFMASK;
- ip_len = ntohs(ip->ip_len);
- } else {
- offset = ip->ip_off & IP_OFFMASK;
- ip_len = ip->ip_len;
- }
+ offset = ip->ip_off & IP_OFFMASK;
+ ip_len = ip->ip_len;
pktlen = ip_len < pktlen ? ip_len : pktlen;
if (offset == 0) {
@@ -2572,6 +2740,14 @@ do { \
ip = mtod(m, struct ip *);
args->f_id.src_ip = ntohl(src_ip.s_addr);
args->f_id.dst_ip = ntohl(dst_ip.s_addr);
+ } else if (pktlen >= ETHER_HDR_LEN && args->eh != NULL &&
+ (args->flags & IP_FW_ARGS_LAYER2)) {
+ void *hdr;
+ args->f_id.flow_type = IPFW_FLOW_ETHER;
+ switch (ntohs(args->eh->ether_type)) {
+ case ETHERTYPE_ARP:
+ PULLUP_TO(ETHER_HDR_LEN, hdr, struct arphdr);
+ }
}
#undef PULLUP_TO
if (proto) { /* we may have port numbers, store them */