diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -349,7 +349,8 @@ void expand_label_nr(const char *, char *, size_t, struct pfctl_rule *); void expand_eth_rule(struct pfctl_eth_rule *, - struct node_if *, struct node_etherproto *); + struct node_if *, struct node_etherproto *, + struct node_mac *, struct node_mac *); void expand_rule(struct pfctl_rule *, struct node_if *, struct node_host *, struct node_proto *, struct node_os *, struct node_host *, struct node_port *, struct node_host *, @@ -419,15 +420,12 @@ struct node_os *src_os; } fromto; struct { - u_int8_t src[ETHER_ADDR_LEN]; - u_int8_t srcneg; - u_int8_t dst[ETHER_ADDR_LEN]; - u_int8_t dstneg; + struct node_mac *src; + struct node_mac *dst; } etherfromto; - u_int8_t mac[ETHER_ADDR_LEN]; + struct node_mac *mac; struct { - uint8_t mac[ETHER_ADDR_LEN]; - u_int8_t neg; + struct node_mac *mac; } etheraddr; struct { struct node_host *host; @@ -560,7 +558,7 @@ %type etherproto etherproto_list etherproto_item %type etherfromto %type etherfrom etherto -%type mac +%type xmac mac mac_list macspec %% ruleset : /* empty */ @@ -1191,11 +1189,6 @@ r.action = $2.b1; r.direction = $3; r.quick = $4.quick; - /* XXX TODO: ! support */ - memcpy(&r.src.addr, $7.src, sizeof(r.src.addr)); - r.src.neg = $7.srcneg; - memcpy(&r.dst.addr, $7.dst, sizeof(r.dst.addr)); - r.dst.neg = $7.dstneg; if ($8.tag != NULL) memcpy(&r.tagname, $8.tag, sizeof(r.tagname)); if ($8.queues.qname != NULL) @@ -1203,7 +1196,7 @@ r.dnpipe = $8.dnpipe; r.dnflags = $8.free_flags; - expand_eth_rule(&r, $5, $6); + expand_eth_rule(&r, $5, $6, $7.src, $7.dst); } ; @@ -3169,48 +3162,78 @@ ; etherfromto : ALL { - bzero($$.src, sizeof($$.src)); - $$.srcneg = 0; - bzero($$.dst, sizeof($$.dst)); - $$.dstneg = 0; + $$.src = NULL; + $$.dst = NULL; } | etherfrom etherto { - memcpy(&$$.src, $1.mac, sizeof($$.src)); - $$.srcneg = $1.neg; - memcpy(&$$.dst, $2.mac, sizeof($$.dst)); - $$.dstneg = $2.neg; + $$.src = $1.mac; + $$.dst = $2.mac; } ; etherfrom : /* emtpy */ { bzero(&$$, sizeof($$)); } - | FROM not mac { - memcpy(&$$.mac, $3, sizeof($$)); - $$.neg = $2; + | FROM macspec { + $$.mac = $2; } ; etherto : /* empty */ { bzero(&$$, sizeof($$)); } - | TO not mac { - memcpy(&$$.mac, $3, sizeof($$)); - $$.neg = $2; + | TO macspec { + $$.mac = $2; } ; mac : string { + $$ = calloc(1, sizeof(struct node_mac)); + if ($$ == NULL) + err(1, "mac: calloc"); + if (sscanf($1, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - &$$[0], &$$[1], &$$[2], &$$[3], &$$[4], - &$$[5]) != 6) { + &$$->mac[0], &$$->mac[1], &$$->mac[2], &$$->mac[3], &$$->mac[4], + &$$->mac[5]) != 6) { free($$); free($1); yyerror("invalid MAC address"); YYERROR; } + free($1); + $$->next = NULL; + $$->tail = $$; + } +xmac : not mac { + struct node_mac *n; + + for (n = $2; n != NULL; n = n->next) + n->neg = $1; + $$ = $2; } ; +macspec : xmac { + $$ = $1; + } + | '{' optnl mac_list '}' + { + $$ = $3; + } + ; +mac_list : xmac optnl { + $$ = $1; + } + | mac_list comma xmac { + if ($3 == NULL) + $$ = $1; + else if ($1 == NULL) + $$ = $3; + else { + $1->tail->next = $3; + $1->tail = $3->tail; + $$ = $1; + } + } fromto : ALL { $$.src.host = NULL; @@ -5616,27 +5639,36 @@ void expand_eth_rule(struct pfctl_eth_rule *r, - struct node_if *interfaces, struct node_etherproto *protos) + struct node_if *interfaces, struct node_etherproto *protos, + struct node_mac *srcs, struct node_mac *dsts) { struct pfctl_eth_rule *rule; LOOP_THROUGH(struct node_if, interface, interfaces, LOOP_THROUGH(struct node_etherproto, proto, protos, + LOOP_THROUGH(struct node_mac, src, srcs, + LOOP_THROUGH(struct node_mac, dst, dsts, r->nr = pf->eth_nr++; strlcpy(r->ifname, interface->ifname, sizeof(r->ifname)); r->ifnot = interface->not; r->proto = proto->proto; + bcopy(src->mac, r->src.addr, ETHER_ADDR_LEN); + r->src.neg = src->neg; + bcopy(dst->mac, r->dst.addr, ETHER_ADDR_LEN); + r->dst.neg = dst->neg; if ((rule = calloc(1, sizeof(*rule))) == NULL) err(1, "calloc"); bcopy(r, rule, sizeof(*rule)); TAILQ_INSERT_TAIL(&pf->eth_rules, rule, entries); - )); + )))); FREE_LIST(struct node_if, interfaces); FREE_LIST(struct node_etherproto, protos); + FREE_LIST(struct node_mac, srcs); + FREE_LIST(struct node_mac, dsts); } void diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -135,6 +135,13 @@ struct node_host *tail; }; +struct node_mac { + u_int8_t mac[ETHER_ADDR_LEN]; + bool neg; + struct node_mac *next; + struct node_mac *tail; +}; + struct node_os { char *os; pf_osfp_t fingerprint; diff --git a/tests/sys/netpfil/pf/ether.sh b/tests/sys/netpfil/pf/ether.sh --- a/tests/sys/netpfil/pf/ether.sh +++ b/tests/sys/netpfil/pf/ether.sh @@ -66,6 +66,11 @@ "ether block to 00:01:02:03:04:05" atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2 + # Should still fail for 'to', even if it's in a list + pft_set_rules alcatraz \ + "ether block to { ${epair_a_mac}, 00:01:02:0:04:05 }" + atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2 + # Now try this with an interface specified pft_set_rules alcatraz \ "ether block on ${epair}b from ${epair_a_mac}" @@ -84,6 +89,16 @@ pft_set_rules alcatraz \ "ether block out on ${epair}b to ! ${epair_a_mac}" atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2 + + # Block everything not us + pft_set_rules alcatraz \ + "ether block out on ${epair}b to { ! ${epair_a_mac} }" + atf_check -s exit:0 -o ignore ping -c 1 -t 1 192.0.2.2 + + # Block us now + pft_set_rules alcatraz \ + "ether block out on ${epair}b to { ! 00:01:02:03:04:05 }" + atf_check -s exit:2 -o ignore ping -c 1 -t 1 192.0.2.2 } mac_cleanup()