diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c index c7b2cd17bc2..11984a30133 100644 --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -1252,6 +1252,7 @@ snl_add_msg_attr_pf_rule(struct snl_writer *nw, uint32_t type, const struct pfct snl_add_msg_attr_uid(nw, PF_RT_UID, &r->uid); snl_add_msg_attr_uid(nw, PF_RT_GID, (const struct pf_rule_uid *)&r->gid); + snl_add_msg_attr_string(nw, PF_RT_RCV_IFNAME, r->rcv_ifname); snl_add_msg_attr_u32(nw, PF_RT_RULE_FLAG, r->rule_flag); snl_add_msg_attr_u8(nw, PF_RT_ACTION, r->action); @@ -1656,6 +1657,7 @@ static struct snl_attr_parser ap_getrule[] = { { .type = PF_RT_STATES_TOTAL, .off = _OUT(r.states_tot), .cb = snl_attr_get_uint64 }, { .type = PF_RT_SRC_NODES, .off = _OUT(r.src_nodes), .cb = snl_attr_get_uint64 }, { .type = PF_RT_ANCHOR_CALL, .off = _OUT(anchor_call), .arg = (void*)MAXPATHLEN, .cb = snl_attr_copy_string }, + { .type = PF_RT_RCV_IFNAME, .off = _OUT(r.rcv_ifname), .arg = (void*)IFNAMSIZ, .cb = snl_attr_copy_string }, }; static struct snl_field_parser fp_getrule[] = {}; #undef _OUT diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h index 33d7d0f113a..6088e18554b 100644 --- a/lib/libpfctl/libpfctl.h +++ b/lib/libpfctl/libpfctl.h @@ -220,6 +220,7 @@ struct pfctl_rule { struct pf_rule_uid uid; struct pf_rule_gid gid; + char rcv_ifname[IFNAMSIZ]; uint32_t rule_flag; uint8_t action; diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index f54f24a14a7..0c551d2ef49 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -241,6 +241,7 @@ static struct filter_opts { #define FOM_FRAGCACHE 0x8000 /* does not exist in OpenBSD */ struct node_uid *uid; struct node_gid *gid; + struct node_if *rcv; struct { u_int8_t b1; u_int8_t b2; @@ -367,7 +368,7 @@ 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 *, struct node_port *, struct node_uid *, struct node_gid *, - struct node_icmp *, const char *); + struct node_if *, struct node_icmp *, const char *); int expand_altq(struct pf_altq *, struct node_if *, struct node_queue *, struct node_queue_bw bwspec, struct node_queue_opt *); @@ -516,7 +517,7 @@ int parseport(char *, struct range *r, int); %token STICKYADDRESS ENDPI MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW %token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS -%token DIVERTTO DIVERTREPLY BRIDGE_TO +%token DIVERTTO DIVERTREPLY BRIDGE_TO RECEIVEDON %token STRING %token NUMBER %token PORTBINARY @@ -1073,7 +1074,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto expand_rule(&r, $5, NULL, $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, - $9.uid, $9.gid, $9.icmpspec, + $9.uid, $9.gid, $9.rcv, $9.icmpspec, pf->astack[pf->asd + 1] ? pf->alast->name : $2); free($2); pf->astack[pf->asd + 1] = NULL; @@ -1096,7 +1097,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto expand_rule(&r, $3, NULL, $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, - 0, 0, 0, $2); + 0, 0, 0, 0, $2); free($2); } | RDRANCHOR string interface af proto fromto rtable { @@ -1138,7 +1139,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto expand_rule(&r, $3, NULL, $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, - 0, 0, 0, $2); + 0, 0, 0, 0, $2); free($2); } | BINATANCHOR string interface af proto fromto rtable { @@ -1461,7 +1462,7 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts expand_rule(&r, $4, NULL, $6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host, $7.dst.port, - NULL, NULL, NULL, ""); + NULL, NULL, NULL, NULL, ""); } ; @@ -1626,7 +1627,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { if (h != NULL) expand_rule(&r, j, NULL, NULL, NULL, h, NULL, NULL, NULL, NULL, NULL, - NULL, ""); + NULL, NULL, ""); if ((i->ifa_flags & IFF_LOOPBACK) == 0) { bzero(&r, sizeof(r)); @@ -1648,7 +1649,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { if (h != NULL) expand_rule(&r, NULL, NULL, NULL, NULL, h, NULL, NULL, - NULL, NULL, NULL, NULL, ""); + NULL, NULL, NULL, NULL, NULL, ""); } else free(hh); } @@ -2802,7 +2803,7 @@ pfrule : action dir logquick interface route af proto fromto expand_rule(&r, $4, $5.host, $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, - $9.uid, $9.gid, $9.icmpspec, ""); + $9.uid, $9.gid, $9.rcv, $9.icmpspec, ""); } ; @@ -2937,6 +2938,13 @@ filter_opt : USER uids { filter_opts.match_tag = $3; filter_opts.match_tag_not = $1; } + | RECEIVEDON if_item { + if (filter_opts.rcv) { + yyerror("cannot respecify received-on"); + YYERROR; + } + filter_opts.rcv = $2; + } | PROBABILITY probability { double p; @@ -4882,7 +4890,7 @@ natrule : nataction interface af proto fromto tag tagged rtable expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $4, $5.src_os, $5.src.host, $5.src.port, $5.dst.host, - $5.dst.port, 0, 0, 0, ""); + $5.dst.port, 0, 0, 0, 0, ""); free($9); } ; @@ -6034,8 +6042,8 @@ expand_rule(struct pfctl_rule *r, struct node_proto *protos, struct node_os *src_oses, struct node_host *src_hosts, struct node_port *src_ports, struct node_host *dst_hosts, struct node_port *dst_ports, - struct node_uid *uids, struct node_gid *gids, struct node_icmp *icmp_types, - const char *anchor_call) + struct node_uid *uids, struct node_gid *gids, struct node_if *rcv, + struct node_icmp *icmp_types, const char *anchor_call) { sa_family_t af = r->af; int added = 0, error = 0; @@ -6138,6 +6146,10 @@ expand_rule(struct pfctl_rule *r, r->gid.op = gid->op; r->gid.gid[0] = gid->gid[0]; r->gid.gid[1] = gid->gid[1]; + if (rcv) { + strlcpy(r->rcv_ifname, rcv->ifname, + sizeof(r->rcv_ifname)); + } r->type = icmp_type->type; r->code = icmp_type->code; @@ -6381,6 +6393,7 @@ lookup(char *s) { "rdr-anchor", RDRANCHOR}, { "realtime", REALTIME}, { "reassemble", REASSEMBLE}, + { "received-on", RECEIVEDON}, { "reply-to", REPLYTO}, { "require-order", REQUIREORDER}, { "return", RETURN}, diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index a9416534626..2e6a1d1cb1c 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -960,6 +960,8 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer } print_fromto(&r->src, r->os_fingerprint, &r->dst, r->af, r->proto, verbose, numeric); + if (r->rcv_ifname[0]) + printf(" received-on %s", r->rcv_ifname); if (r->uid.op) print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user", UID_MAX); diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index c123da37d2c..4bb09d637de 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -776,6 +776,7 @@ struct pf_krule { char label[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE]; uint32_t ridentifier; char ifname[IFNAMSIZ]; + char rcv_ifname[IFNAMSIZ]; char qname[PF_QNAME_SIZE]; char pqname[PF_QNAME_SIZE]; char tagname[PF_TAG_NAME_SIZE]; @@ -792,6 +793,7 @@ struct pf_krule { time_t *timestamp; struct pfi_kkif *kif; + struct pfi_kkif *rcv_kif; struct pf_kanchor *anchor; struct pfr_ktable *overload_tbl; diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index d675c5381e8..1182c5dd0c3 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -375,6 +375,7 @@ static void pf_patch_8(struct mbuf *, u_int16_t *, u_int8_t *, u_int8_t, static struct pf_kstate *pf_find_state(struct pfi_kkif *, const struct pf_state_key_cmp *, u_int); static int pf_src_connlimit(struct pf_kstate **); +static int pf_match_rcvif(struct mbuf *, struct pf_krule *); static void pf_overload_task(void *v, int pending); static u_short pf_insert_src_node(struct pf_ksrc_node **, struct pf_krule *, struct pf_addr *, sa_family_t); @@ -3951,6 +3952,27 @@ pf_match_tag(struct mbuf *m, struct pf_krule *r, int *tag, int mtag) (r->match_tag_not && r->match_tag != *tag)); } +static int +pf_match_rcvif(struct mbuf *m, struct pf_krule *r) +{ + struct ifnet *ifp = m->m_pkthdr.rcvif; + struct pfi_kkif *kif; + + if (ifp == NULL) + return (0); + + kif = (struct pfi_kkif *)ifp->if_pf_kif; + + if (kif == NULL) { + DPFPRINTF(PF_DEBUG_URGENT, + ("pf_test_via: kif == NULL, @%d via %s\n", r->nr, + r->rcv_ifname)); + return (0); + } + + return (pfi_kkif_match(r->rcv_kif, kif)); +} + int pf_tag_packet(struct mbuf *m, struct pf_pdesc *pd, int tag) { @@ -5155,6 +5177,8 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif, else if (r->match_tag && !pf_match_tag(m, r, &tag, pd->pf_mtag ? pd->pf_mtag->tag : 0)) r = TAILQ_NEXT(r, entries); + else if (r->rcv_kif && !pf_match_rcvif(m, r)) + r = TAILQ_NEXT(r, entries); else if (r->os_fingerprint != PF_OSFP_ANY && (pd->proto != IPPROTO_TCP || !pf_osfp_match( pf_osfp_fingerprint(pd, m, off, th), diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index 8d18da6cf0d..e205c5ca0c3 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -603,6 +603,8 @@ pf_free_rule(struct pf_krule *rule) pfr_detach_table(rule->overload_tbl); if (rule->kif) pfi_kkif_unref(rule->kif); + if (rule->rcv_kif) + pfi_kkif_unref(rule->rcv_kif); pf_kanchor_remove(rule); pf_empty_kpool(&rule->rpool.list); @@ -1306,6 +1308,7 @@ pf_hash_rule_rolling(MD5_CTX *ctx, struct pf_krule *rule) for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) PF_MD5_UPD_STR(rule, label[i]); PF_MD5_UPD_STR(rule, ifname); + PF_MD5_UPD_STR(rule, rcv_ifname); PF_MD5_UPD_STR(rule, match_tagname); PF_MD5_UPD_HTONS(rule, match_tag, x); /* dup? */ PF_MD5_UPD_HTONL(rule, os_fingerprint, y); @@ -2068,7 +2071,7 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket, struct pf_kruleset *ruleset; struct pf_krule *tail; struct pf_kpooladdr *pa; - struct pfi_kkif *kif = NULL; + struct pfi_kkif *kif = NULL, *rcv_kif = NULL; int rs_num; int error = 0; @@ -2081,6 +2084,8 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket, if (rule->ifname[0]) kif = pf_kkif_create(M_WAITOK); + if (rule->rcv_ifname[0]) + rcv_kif = pf_kkif_create(M_WAITOK); pf_counter_u64_init(&rule->evaluations, M_WAITOK); for (int i = 0; i < 2; i++) { pf_counter_u64_init(&rule->packets[i], M_WAITOK); @@ -2143,6 +2148,13 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket, } else rule->kif = NULL; + if (rule->rcv_ifname[0]) { + rule->rcv_kif = pfi_kkif_attach(rcv_kif, rule->rcv_ifname); + rcv_kif = NULL; + pfi_kkif_ref(rule->rcv_kif); + } else + rule->rcv_kif = NULL; + if (rule->rtableid > 0 && rule->rtableid >= rt_numfibs) error = EBUSY; @@ -2242,6 +2254,7 @@ errout: PF_RULES_WUNLOCK(); PF_CONFIG_UNLOCK(); errout_unlocked: + pf_kkif_free(rcv_kif); pf_kkif_free(kif); pf_krule_free(rule); return (error); diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c index 6e752159b4b..510aac2af48 100644 --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -730,6 +730,7 @@ static const struct nlattr_parser nla_p_rule[] = { { .type = PF_RT_SET_PRIO_REPLY, .off = _OUT(set_prio[1]), .cb = nlattr_get_uint8 }, { .type = PF_RT_DIVERT_ADDRESS, .off = _OUT(divert.addr), .cb = nlattr_get_in6_addr }, { .type = PF_RT_DIVERT_PORT, .off = _OUT(divert.port), .cb = nlattr_get_uint16 }, + { .type = PF_RT_RCV_IFNAME, .off = _OUT(rcv_ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara }, }; NL_DECLARE_ATTR_PARSER(rule_parser, nla_p_rule); #undef _OUT @@ -941,6 +942,8 @@ pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate *npt) nlattr_add_rule_uid(nw, PF_RT_UID, &rule->uid); nlattr_add_rule_uid(nw, PF_RT_GID, (const struct pf_rule_uid *)&rule->gid); + nlattr_add_string(nw, PF_RT_RCV_IFNAME, rule->rcv_ifname); + nlattr_add_u32(nw, PF_RT_RULE_FLAG, rule->rule_flag); nlattr_add_u8(nw, PF_RT_ACTION, rule->action); nlattr_add_u8(nw, PF_RT_DIRECTION, rule->direction); diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h index cfe81cf142a..bd173c0137c 100644 --- a/sys/netpfil/pf/pf_nl.h +++ b/sys/netpfil/pf/pf_nl.h @@ -257,6 +257,7 @@ enum pf_rule_type_t { PF_RT_STATES_TOTAL = 70, /* u64 */ PF_RT_SRC_NODES = 71, /* u64 */ PF_RT_ANCHOR_CALL = 72, /* string */ + PF_RT_RCV_IFNAME = 73, /* string */ }; enum pf_addrule_type_t {