From nobody Wed Mar 02 16:00:42 2022 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 67FAB19E06E8; Wed, 2 Mar 2022 16:00:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4K7zNp5Mxzz3N3g; Wed, 2 Mar 2022 16:00:42 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1646236843; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=iqaL8L+Vn3gokdQQFLxIyzTC87ROCErZ5kvlmSKMWhw=; b=wIBxIlb44I/1TYe5Oop91b/snHLFBheUs1LNzEJGlontIp0GUwJzsAozNpVh5jPnCEK3oo 51aNeCBWZS17+Fdw4YPkQBMJGgkV+6yKPf79BPPyiMLs5r4ZrUBZ1D6XSgYoFwcxboZ33F yYTjyEzFdF7PIBAQo+H7TixrUNR4YJ6V3OttKXVwRydBG2EFkUxNoPk1dPKcfTLASen+Yt VBub2kZpfzbICpmvFU9RbfQ52xdHo6DARbXrZVycbSKNlZblkt2QoUbuoVIsSvK6pQRSq9 FJwSeHEJqJmp28xLrGg4XXi0kwx5mMrpFLiOJ/2qru6UX11BuZ2G6Eb4nSZUWQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 78201264E2; Wed, 2 Mar 2022 16:00:42 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 222G0gPI090976; Wed, 2 Mar 2022 16:00:42 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 222G0gvu090975; Wed, 2 Mar 2022 16:00:42 GMT (envelope-from git) Date: Wed, 2 Mar 2022 16:00:42 GMT Message-Id: <202203021600.222G0gvu090975@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kristof Provost Subject: git: 2b29ceb86f50 - main - pfctl: Print Ethernet rules List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 2b29ceb86f509dfaf34c3b7f790776c345915dba Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1646236843; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=iqaL8L+Vn3gokdQQFLxIyzTC87ROCErZ5kvlmSKMWhw=; b=DTKpC8YPWnPPARZitGnOuSknh+lpzNX8uVAwa7/zrJQEr2IF9i+rI4KYWTnaTmEGEXOLdC Re4VAwVD4MhWv6dPnnKhFbkxHQ6LH9hJepuAvNLKviok0oxB2MUS7YEHGnKjKYheGt8UDc 86x74kOeeZM9DkcJ8vgCOWl7O5nolMDr44QKVJMaj672RPtHDg0HOVF2M8JcgoTj18IaAz KbH2pO3WGzIgGN83Z8Ie84MUMnvxjzznmtSzBhaOqo/IFBPOt4TjWi2pPyp0X87yZ3Did5 g2fZd4rqaNp9aTQByWu/JR1ujJWup2+/MzT17XeQpTEwoaQcj8sgJDLEbYNZsw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1646236843; a=rsa-sha256; cv=none; b=ZCIzk0CtUcu3E3HGAp8DZJE0Sma/uM8xlhM99JQsrkLZgK257mrRr2JZF2xnjMk5u+wmNI Rv5mOQg8fmRXp6Jsmct6H+P7U+VuNNcanx0NmfZMBeQRmFaMGD8kAMMPSSqG7CE91WCFNa W4l72NTeaGKoVhH3LMs1cnms6xme2kGz+XoyElz5Br+2ezMRRuV5vHhXyhxSEzEVAgntCh w5bgNuXa98tuK5cMfOPhWOd+4WdnAq/Ijzv+oFja/EKOfY5fXe2Z9h5iZV5Z0NFqjgukkZ qWJH+5AC4ECfS5T9LTr0W8AYHOMuibbOgEMvT4sUU1NDarKgAEKV1yNqcstBgg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=2b29ceb86f509dfaf34c3b7f790776c345915dba commit 2b29ceb86f509dfaf34c3b7f790776c345915dba Author: Kristof Provost AuthorDate: 2021-02-04 12:19:12 +0000 Commit: Kristof Provost CommitDate: 2022-03-02 16:00:03 +0000 pfctl: Print Ethernet rules Extent pfctl to be able to read configured Ethernet filtering rules from the kernel and print them. Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D31738 --- lib/libpfctl/libpfctl.c | 179 ++++++++++++++++++++++++++++++++++++++ lib/libpfctl/libpfctl.h | 42 +++++++++ sbin/pfctl/parse.y | 216 +++++++++++++++++++++++++++++++++++++++++++++- sbin/pfctl/pfctl.c | 89 ++++++++++++++++++- sbin/pfctl/pfctl_parser.c | 44 ++++++++++ sbin/pfctl/pfctl_parser.h | 5 ++ 6 files changed, 568 insertions(+), 7 deletions(-) diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c index 66f7f61cc1fa..e158faf317c1 100644 --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -546,6 +546,185 @@ pf_nvrule_to_rule(const nvlist_t *nvl, struct pfctl_rule *rule) rule->src_nodes = nvlist_get_number(nvl, "src_nodes"); } +static void +pfctl_nveth_addr_to_eth_addr(const nvlist_t *nvl, struct pfctl_eth_addr *addr) +{ + size_t len; + const void *data; + + data = nvlist_get_binary(nvl, "addr", &len); + assert(len == sizeof(addr->addr)); + memcpy(addr->addr, data, sizeof(addr->addr)); + + addr->neg = nvlist_get_bool(nvl, "neg"); +} + +static nvlist_t * +pfctl_eth_addr_to_nveth_addr(const struct pfctl_eth_addr *addr) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + nvlist_add_bool(nvl, "neg", addr->neg); + nvlist_add_binary(nvl, "addr", &addr->addr, ETHER_ADDR_LEN); + + return (nvl); +} + +static void +pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule) +{ + rule->nr = nvlist_get_number(nvl, "nr"); + rule->quick = nvlist_get_bool(nvl, "quick"); + strlcpy(rule->ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ); + rule->ifnot = nvlist_get_bool(nvl, "ifnot"); + rule->direction = nvlist_get_number(nvl, "direction"); + rule->proto = nvlist_get_number(nvl, "proto"); + + pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "src"), + &rule->src); + pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "dst"), + &rule->dst); + + rule->evaluations = nvlist_get_number(nvl, "evaluations"); + rule->packets[0] = nvlist_get_number(nvl, "packets-in"); + rule->packets[1] = nvlist_get_number(nvl, "packets-out"); + rule->bytes[0] = nvlist_get_number(nvl, "bytes-in"); + rule->bytes[1] = nvlist_get_number(nvl, "bytes-out"); + + strlcpy(rule->qname, nvlist_get_string(nvl, "qname"), PF_QNAME_SIZE); + strlcpy(rule->tagname, nvlist_get_string(nvl, "tagname"), + PF_TAG_NAME_SIZE); + + rule->action = nvlist_get_number(nvl, "action"); +} + +int +pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules) +{ + uint8_t buf[1024]; + struct pfioc_nv nv; + nvlist_t *nvl; + + bzero(rules, sizeof(*rules)); + + nv.data = buf; + nv.len = nv.size = sizeof(buf); + + if (ioctl(dev, DIOCGETETHRULES, &nv) != 0) + return (errno); + + nvl = nvlist_unpack(buf, nv.len, 0); + if (nvl == NULL) + return (EIO); + + rules->nr = nvlist_get_number(nvl, "nr"); + rules->ticket = nvlist_get_number(nvl, "ticket"); + + nvlist_destroy(nvl); + return (0); +} + +int +pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket, + struct pfctl_eth_rule *rule, bool clear) +{ + uint8_t buf[1024]; + struct pfioc_nv nv; + nvlist_t *nvl; + void *data; + size_t len; + + nvl = nvlist_create(0); + + nvlist_add_number(nvl, "ticket", ticket); + nvlist_add_number(nvl, "nr", nr); + nvlist_add_bool(nvl, "clear", clear); + + data = nvlist_pack(nvl, &len); + nv.data = buf; + memcpy(buf, data, len); + free(data); + nv.len = len; + nv.size = sizeof(buf); + if (ioctl(dev, DIOCGETETHRULE, &nv)) { + nvlist_destroy(nvl); + return (errno); + } + nvlist_destroy(nvl); + + nvl = nvlist_unpack(buf, nv.len, 0); + if (nvl == NULL) { + return (EIO); + } + + pfctl_nveth_rule_to_eth_rule(nvl, rule); + + nvlist_destroy(nvl); + return (0); +} + +int +pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, uint32_t ticket) +{ + struct pfioc_nv nv; + nvlist_t *nvl, *addr; + void *packed; + int error; + size_t size; + + nvl = nvlist_create(0); + + nvlist_add_number(nvl, "ticket", ticket); + + nvlist_add_number(nvl, "nr", r->nr); + nvlist_add_bool(nvl, "quick", r->quick); + nvlist_add_string(nvl, "ifname", r->ifname); + nvlist_add_bool(nvl, "ifnot", r->ifnot); + nvlist_add_number(nvl, "direction", r->direction); + nvlist_add_number(nvl, "proto", r->proto); + + addr = pfctl_eth_addr_to_nveth_addr(&r->src); + if (addr == NULL) { + nvlist_destroy(nvl); + return (ENOMEM); + } + nvlist_add_nvlist(nvl, "src", addr); + nvlist_destroy(addr); + + addr = pfctl_eth_addr_to_nveth_addr(&r->dst); + if (addr == NULL) { + nvlist_destroy(nvl); + return (ENOMEM); + } + nvlist_add_nvlist(nvl, "dst", addr); + nvlist_destroy(addr); + + nvlist_add_string(nvl, "qname", r->qname); + nvlist_add_string(nvl, "tagname", r->tagname); + nvlist_add_number(nvl, "action", r->action); + + packed = nvlist_pack(nvl, &size); + if (packed == NULL) { + nvlist_destroy(nvl); + return (ENOMEM); + } + + nv.len = size; + nv.size = size; + nv.data = packed; + + error = ioctl(dev, DIOCADDETHRULE, &nv); + + free(packed); + nvlist_destroy(nvl); + + return (error); +} + int pfctl_add_rule(int dev, const struct pfctl_rule *r, const char *anchor, const char *anchor_call, uint32_t ticket, uint32_t pool_ticket) diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h index 6b516b0a7649..e2b3711f9ffd 100644 --- a/lib/libpfctl/libpfctl.h +++ b/lib/libpfctl/libpfctl.h @@ -65,6 +65,43 @@ struct pfctl_status { uint64_t bcounters[2][2]; }; +struct pfctl_eth_rules_info { + uint32_t nr; + uint32_t ticket; +}; + +struct pfctl_eth_addr { + uint8_t addr[ETHER_ADDR_LEN]; + bool neg; +}; + +struct pfctl_eth_rule { + uint32_t nr; + + bool quick; + + /* Filter */ + char ifname[IFNAMSIZ]; + uint8_t ifnot; + uint8_t direction; + uint16_t proto; + struct pfctl_eth_addr src, dst; + + /* Stats */ + uint64_t evaluations; + uint64_t packets[2]; + uint64_t bytes[2]; + + /* Action */ + char qname[PF_QNAME_SIZE]; + char tagname[PF_TAG_NAME_SIZE]; + uint8_t action; + + TAILQ_ENTRY(pfctl_eth_rule) entries; +}; + +TAILQ_HEAD(pfctl_eth_rules, pfctl_eth_rule); + struct pfctl_pool { struct pf_palist list; struct pf_pooladdr *cur; @@ -291,6 +328,11 @@ struct pfctl_syncookies { struct pfctl_status* pfctl_get_status(int dev); void pfctl_free_status(struct pfctl_status *status); +int pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules); +int pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket, + struct pfctl_eth_rule *rule, bool clear); +int pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, + uint32_t ticket); int pfctl_get_rule(int dev, uint32_t nr, uint32_t ticket, const char *anchor, uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call); diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index f931d1c062b9..1bf9372cd7a6 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -122,12 +122,19 @@ int atoul(char *, u_long *); enum { PFCTL_STATE_NONE, PFCTL_STATE_OPTION, + PFCTL_STATE_ETHER, PFCTL_STATE_SCRUB, PFCTL_STATE_QUEUE, PFCTL_STATE_NAT, PFCTL_STATE_FILTER }; +struct node_etherproto { + u_int16_t proto; + struct node_etherproto *next; + struct node_etherproto *tail; +}; + struct node_proto { u_int8_t proto; struct node_proto *next; @@ -341,6 +348,8 @@ void expand_label_port(const char *, char *, size_t, void expand_label_proto(const char *, char *, size_t, u_int8_t); 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 *); 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 *, @@ -396,6 +405,7 @@ typedef struct { } range; struct node_if *interface; struct node_proto *proto; + struct node_etherproto *etherproto; struct node_icmp *icmp; struct node_host *host; struct node_os *os; @@ -408,6 +418,17 @@ typedef struct { struct peer src, dst; 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; + } etherfromto; + u_int8_t mac[ETHER_ADDR_LEN]; + struct { + uint8_t mac[ETHER_ADDR_LEN]; + u_int8_t neg; + } etheraddr; struct { struct node_host *host; u_int8_t rt; @@ -470,6 +491,7 @@ int parseport(char *, struct range *r, int); %token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY FAILPOLICY %token RANDOMID REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID %token ANTISPOOF FOR INCLUDE KEEPCOUNTERS SYNCOOKIES +%token ETHER %token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY MAPEPORTSET %token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME %token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL @@ -488,6 +510,7 @@ int parseport(char *, struct range *r, int); %type probability %type no dir af fragcache optimizer syncookie_val %type sourcetrack flush unaryop statelock +%type etherprotoval %type action nataction natpasslog scrubaction %type flags flag blockspec prio %type portplain portstar portrange @@ -515,7 +538,7 @@ int parseport(char *, struct range *r, int); %type state_opt_spec state_opt_list state_opt_item %type logquick quick log logopts logopt %type antispoof_ifspc antispoof_iflst antispoof_if -%type qname +%type qname etherqname %type qassign qassign_list qassign_item %type scheduler %type cbqflags_list cbqflags_item @@ -524,7 +547,7 @@ int parseport(char *, struct range *r, int); %type fairqopts_list fairqopts_item fairq_opts %type codelopts_list codelopts_item codel_opts %type bandwidth -%type filter_opts filter_opt filter_opts_l +%type filter_opts filter_opt filter_opts_l etherfilter_opts etherfilter_opt etherfilter_opts_l %type filter_sets filter_set filter_sets_l %type antispoof_opts antispoof_opt antispoof_opts_l %type queue_opts queue_opt queue_opts_l @@ -534,12 +557,17 @@ int parseport(char *, struct range *r, int); %type tagged %type rtable %type syncookie_opts +%type etherproto etherproto_list etherproto_item +%type etherfromto +%type etherfrom etherto +%type mac %% ruleset : /* empty */ | ruleset include '\n' | ruleset '\n' | ruleset option '\n' + | ruleset etherrule '\n' | ruleset scrubrule '\n' | ruleset natrule '\n' | ruleset binatrule '\n' @@ -1151,6 +1179,58 @@ scrubaction : no SCRUB { } ; +etherrule : ETHER action dir quick interface etherproto etherfromto etherfilter_opts + { + struct pfctl_eth_rule r; + + bzero(&r, sizeof(r)); + + if (check_rulestate(PFCTL_STATE_ETHER)) + YYERROR; + + 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) + memcpy(&r.qname, $8.queues.qname, sizeof(r.qname)); + + expand_eth_rule(&r, $5, $6); + } + ; + +etherfilter_opts : { + bzero(&filter_opts, sizeof filter_opts); + } + etherfilter_opts_l + { $$ = filter_opts; } + | /* empty */ { + bzero(&filter_opts, sizeof filter_opts); + $$ = filter_opts; + } + ; + +etherfilter_opts_l : etherfilter_opts_l etherfilter_opt + | etherfilter_opt + +etherfilter_opt : etherqname { + if (filter_opts.queues.qname) { + yyerror("queue cannot be redefined"); + YYERROR; + } + filter_opts.queues = $1; + } + | TAG string { + filter_opts.tag = $2; + } + ; + scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts { struct pfctl_rule r; @@ -2978,6 +3058,56 @@ af : /* empty */ { $$ = 0; } | INET6 { $$ = AF_INET6; } ; +etherproto : /* empty */ { $$ = NULL; } + | PROTO etherproto_item { $$ = $2; } + | PROTO '{' optnl etherproto_list '}' { $$ = $4; } + ; + +etherproto_list : etherproto_item optnl { $$ = $1; } + | etherproto_list comma etherproto_item optnl { + $1->tail->next = $3; + $1->tail = $3; + $$ = $1; + } + ; + +etherproto_item : etherprotoval { + u_int16_t pr; + + pr = (u_int16_t)$1; + if (pr == 0) { + yyerror("proto 0 cannot be used"); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_proto)); + if ($$ == NULL) + err(1, "proto_item: calloc"); + $$->proto = pr; + $$->next = NULL; + $$->tail = $$; + } + ; + +etherprotoval : NUMBER { + if ($1 < 0 || $1 > 65565) { + yyerror("protocol outside range"); + YYERROR; + } + } + | STRING + { + if (!strncmp($1, "0x", 2)) { + if (sscanf($1, "0x%4x", &$$) != 1) { + free($1); + yyerror("invalid EtherType hex"); + YYERROR; + } + } else { + yyerror("Symbolic EtherType not yet supported"); + } + } + ; + proto : /* empty */ { $$ = NULL; } | PROTO proto_item { $$ = $2; } | PROTO '{' optnl proto_list '}' { $$ = $4; } @@ -3028,6 +3158,50 @@ protoval : STRING { } ; +etherfromto : ALL { + bzero($$.src, sizeof($$.src)); + $$.srcneg = 0; + bzero($$.dst, sizeof($$.dst)); + $$.dstneg = 0; + } + | etherfrom etherto { + memcpy(&$$.src, $1.mac, sizeof($$.src)); + $$.srcneg = $1.neg; + memcpy(&$$.dst, $2.mac, sizeof($$.dst)); + $$.dstneg = $2.neg; + } + ; + +etherfrom : /* emtpy */ { + bzero(&$$, sizeof($$)); + } + | FROM not mac { + memcpy(&$$.mac, $3, sizeof($$)); + $$.neg = $2; + } + ; + +etherto : /* empty */ { + bzero(&$$, sizeof($$)); + } + | TO not mac { + memcpy(&$$.mac, $3, sizeof($$)); + $$.neg = $2; + } + ; + +mac : string { + if (sscanf($1, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &$$[0], &$$[1], &$$[2], &$$[3], &$$[4], + &$$[5]) != 6) { + free($$); + free($1); + yyerror("invalid MAC address"); + YYERROR; + } + } + ; + fromto : ALL { $$.src.host = NULL; $$.src.port = NULL; @@ -3965,6 +4139,14 @@ label : LABEL STRING { } ; +etherqname : QUEUE STRING { + $$.qname = $2; + } + | QUEUE '(' STRING ')' { + $$.qname = $3; + } + ; + qname : QUEUE STRING { $$.qname = $2; $$.pqname = NULL; @@ -5422,6 +5604,31 @@ expand_queue(struct pf_altq *a, struct node_if *interfaces, return (0); } +void +expand_eth_rule(struct pfctl_eth_rule *r, + struct node_if *interfaces, struct node_etherproto *protos) +{ + struct pfctl_eth_rule *rule; + + LOOP_THROUGH(struct node_if, interface, interfaces, + LOOP_THROUGH(struct node_etherproto, proto, protos, + r->nr = pf->eth_nr++; + strlcpy(r->ifname, interface->ifname, + sizeof(r->ifname)); + r->ifnot = interface->not; + r->proto = proto->proto; + + 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); +} + void expand_rule(struct pfctl_rule *r, struct node_if *interfaces, struct node_host *rpool_hosts, @@ -5638,8 +5845,8 @@ int check_rulestate(int desired_state) { if (require_order && (rulestate > desired_state)) { - yyerror("Rules must be in order: options, normalization, " - "queueing, translation, filtering"); + yyerror("Rules must be in order: options, ethernet, " + "normalization, queueing, translation, filtering"); return (1); } rulestate = desired_state; @@ -5682,6 +5889,7 @@ lookup(char *s) { "drop", DROP}, { "drop-ovl", FRAGDROP}, { "dup-to", DUPTO}, + { "ether", ETHER}, { "fail-policy", FAILPOLICY}, { "fairq", FAIRQ}, { "fastroute", FASTROUTE}, diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c index a0eec1b09289..83b3c1db0613 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -96,7 +96,9 @@ int pfctl_load_hostid(struct pfctl *, u_int32_t); int pfctl_load_syncookies(struct pfctl *, u_int8_t); int pfctl_get_pool(int, struct pfctl_pool *, u_int32_t, u_int32_t, int, char *); +void pfctl_print_eth_rule_counters(struct pfctl_eth_rule *, int); void pfctl_print_rule_counters(struct pfctl_rule *, int); +int pfctl_show_eth_rules(int, int); int pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int); int pfctl_show_nat(int, int, char *); int pfctl_show_src_nodes(int, int); @@ -109,6 +111,7 @@ void pfctl_debug(int, u_int32_t, int); int pfctl_test_altqsupport(int, int); int pfctl_show_anchors(int, int, char *); int pfctl_ruleset_trans(struct pfctl *, char *, struct pfctl_anchor *); +int pfctl_load_eth_ruleset(struct pfctl *); int pfctl_load_ruleset(struct pfctl *, char *, struct pfctl_ruleset *, int, int); int pfctl_load_rule(struct pfctl *, char *, struct pfctl_rule *, int); @@ -222,9 +225,9 @@ static const char * const clearopt_list[] = { }; static const char * const showopt_list[] = { - "nat", "queue", "rules", "Anchors", "Sources", "states", "info", - "Interfaces", "labels", "timeouts", "memory", "Tables", "osfp", - "Running", "all", NULL + "ether", "nat", "queue", "rules", "Anchors", "Sources", "states", + "info", "Interfaces", "labels", "timeouts", "memory", "Tables", + "osfp", "Running", "all", NULL }; static const char * const tblcmdopt_list[] = { @@ -986,6 +989,20 @@ pfctl_clear_pool(struct pfctl_pool *pool) } } +void +pfctl_print_eth_rule_counters(struct pfctl_eth_rule *rule, int opts) +{ + if (opts & PF_OPT_VERBOSE) { + printf(" [ Evaluations: %-8llu Packets: %-8llu " + "Bytes: %-10llu]\n", + (unsigned long long)rule->evaluations, + (unsigned long long)(rule->packets[0] + + rule->packets[1]), + (unsigned long long)(rule->bytes[0] + + rule->bytes[1])); + } +} + void pfctl_print_rule_counters(struct pfctl_rule *rule, int opts) { @@ -1034,6 +1051,35 @@ pfctl_print_title(char *title) printf("%s\n", title); } +int +pfctl_show_eth_rules(int dev, int opts) +{ + struct pfctl_eth_rules_info info; + struct pfctl_eth_rule rule; + int dotitle = opts & PF_OPT_SHOWALL; + + if (pfctl_get_eth_rules_info(dev, &info)) { + warn("DIOCGETETHRULES"); + return (-1); + } + for (int nr = 0; nr < info.nr; nr++) { + if (pfctl_get_eth_rule(dev, nr, info.ticket, &rule, false) + != 0) { + warn("DIOCGETETHRULE"); + return (-1); + } + if (dotitle) { + pfctl_print_title("ETH RULES:"); + dotitle = 0; + } + print_eth_rule(&rule, opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG)); + printf("\n"); + pfctl_print_eth_rule_counters(&rule, opts); + } + + return (0); +} + int pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, char *anchorname, int depth) @@ -1466,6 +1512,12 @@ pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pfctl_anchor *a) { int osize = pf->trans->pfrb_size; + if ((pf->loadopt & PFCTL_FLAG_ETH) != 0) { + if (! path[0]) { + if (pfctl_add_trans(pf->trans, PF_RULESET_ETH, path)) + return (1); + } + } if ((pf->loadopt & PFCTL_FLAG_NAT) != 0) { if (pfctl_add_trans(pf->trans, PF_RULESET_NAT, path) || pfctl_add_trans(pf->trans, PF_RULESET_BINAT, path) || @@ -1491,6 +1543,27 @@ pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pfctl_anchor *a) return (0); } +int +pfctl_load_eth_ruleset(struct pfctl *pf) +{ + struct pfctl_eth_rule *r; + int error; + + while ((r = TAILQ_FIRST(&pf->eth_rules)) != NULL) { + TAILQ_REMOVE(&pf->eth_rules, r, entries); + + if ((pf->opts & PF_OPT_NOACTION) == 0) { + error = pfctl_add_eth_rule(pf->dev, r, pf->eth_ticket); + if (error) + return (error); + } + + free(r); + } + + return (0); +} + int pfctl_load_ruleset(struct pfctl *pf, char *path, struct pfctl_ruleset *rs, int rs_num, int depth) @@ -1669,6 +1742,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, pf.opts = opts; pf.optimize = optimize; pf.loadopt = loadopt; + TAILQ_INIT(&pf.eth_rules); /* non-brace anchor, create without resolving the path */ if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL) @@ -1700,6 +1774,8 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, */ if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor)) ERRX("pfctl_rules"); + if (pf.loadopt & PFCTL_FLAG_ETH) + pf.eth_ticket = pfctl_get_ticket(t, PF_RULESET_ETH, anchorname); if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ)) pa.ticket = pfctl_get_ticket(t, PF_RULESET_ALTQ, anchorname); @@ -1720,6 +1796,8 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, if ((pf.loadopt & PFCTL_FLAG_FILTER && (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_SCRUB, 0))) || + (pf.loadopt & PFCTL_FLAG_ETH && + (pfctl_load_eth_ruleset(&pf))) || (pf.loadopt & PFCTL_FLAG_NAT && (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_NAT, 0) || pfctl_load_ruleset(&pf, path, rs, PF_RULESET_RDR, 0) || @@ -2561,10 +2639,15 @@ main(int argc, char *argv[]) case 'm': pfctl_show_limits(dev, opts); break; + case 'e': + pfctl_show_eth_rules(dev, opts); + break; case 'a': opts |= PF_OPT_SHOWALL; pfctl_load_fingerprints(dev, opts); + pfctl_show_eth_rules(dev, opts); + pfctl_show_nat(dev, opts, anchorname); pfctl_show_rules(dev, path, opts, 0, anchorname, 0); pfctl_show_altq(dev, ifaceopt, opts, 0); diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index 5a01c30a076e..8814dc38b23c 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -691,6 +691,50 @@ print_src_node(struct pf_src_node *sn, int opts) } } +static void +print_eth_addr(const struct pfctl_eth_addr *a) +{ + printf("%s%02x:%02x:%02x:%02x:%02x:%02x", a->neg ? "! " : "", + a->addr[0], a->addr[1], a->addr[2], a->addr[3], a->addr[4], + a->addr[5]); +} + +void +print_eth_rule(struct pfctl_eth_rule *r, int rule_numbers) +{ + static const char *actiontypes[] = { "pass", "block" }; + + if (rule_numbers) + printf("@%u ", r->nr); + + printf("ether %s", actiontypes[r->action]); + if (r->direction == PF_IN) + printf(" in"); + else if (r->direction == PF_OUT) + printf(" out"); + + if (r->quick) + printf(" quick"); + if (r->ifname[0]) { + if (r->ifnot) + printf(" on ! %s", r->ifname); + else + printf(" on %s", r->ifname); + } + if (r->proto) + printf(" proto 0x%04x", r->proto); + + printf(" from "); + print_eth_addr(&r->src); + printf(" to "); + print_eth_addr(&r->dst); + + if (r->qname[0]) + printf(" queue %s", r->qname); + if (r->tagname[0]) + printf(" tag %s", r->tagname); +} + void print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numeric) { diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h index 0cd19a560f8d..1d69682b1b6c 100644 --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -90,6 +90,9 @@ struct pfctl { struct pfioc_queue *pqueue; struct pfr_buffer *trans; struct pfctl_anchor *anchor, *alast; + int eth_nr; + struct pfctl_eth_rules eth_rules; + u_int32_t eth_ticket; const char *ruleset; /* 'set foo' options */ @@ -284,6 +287,7 @@ int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *); void print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, sa_family_t, int); void print_src_node(struct pf_src_node *, int); +void print_eth_rule(struct pfctl_eth_rule *, int); void print_rule(struct pfctl_rule *, const char *, int, int); void print_tabledef(const char *, int, int, struct node_tinithead *); void print_status(struct pfctl_status *, struct pfctl_syncookies *, int); @@ -336,6 +340,7 @@ struct pf_timeout { #define PFCTL_FLAG_OPTION 0x08 #define PFCTL_FLAG_ALTQ 0x10 #define PFCTL_FLAG_TABLE 0x20 +#define PFCTL_FLAG_ETH 0x40 extern const struct pf_timeout pf_timeouts[];