Changeset View
Changeset View
Standalone View
Standalone View
sys/netpfil/pf/pf.c
- This file is larger than 256 KB, so syntax highlighting is disabled by default.
Show First 20 Lines • Show All 342 Lines • ▼ Show 20 Lines | |||||
static int pf_test_rule(struct pf_krule **, struct pf_kstate **, | static int pf_test_rule(struct pf_krule **, struct pf_kstate **, | ||||
struct pf_pdesc *, struct pf_krule **, | struct pf_pdesc *, struct pf_krule **, | ||||
struct pf_kruleset **, u_short *, struct inpcb *); | struct pf_kruleset **, u_short *, struct inpcb *); | ||||
static int pf_create_state(struct pf_krule *, struct pf_krule *, | static int pf_create_state(struct pf_krule *, struct pf_krule *, | ||||
struct pf_krule *, struct pf_pdesc *, | struct pf_krule *, struct pf_pdesc *, | ||||
struct pf_state_key *, struct pf_state_key *, int *, | struct pf_state_key *, struct pf_state_key *, int *, | ||||
struct pf_kstate **, int, u_int16_t, u_int16_t, | struct pf_kstate **, int, u_int16_t, u_int16_t, | ||||
struct pf_krule_slist *, struct pf_udp_mapping *, | struct pf_krule_slist *, struct pf_udp_mapping *, | ||||
u_short *); | struct pf_kpool *, u_short *); | ||||
static int pf_state_key_addr_setup(struct pf_pdesc *, | static int pf_state_key_addr_setup(struct pf_pdesc *, | ||||
struct pf_state_key_cmp *, int); | struct pf_state_key_cmp *, int); | ||||
static int pf_tcp_track_full(struct pf_kstate *, | static int pf_tcp_track_full(struct pf_kstate *, | ||||
struct pf_pdesc *, u_short *, int *, | struct pf_pdesc *, u_short *, int *, | ||||
struct pf_state_peer *, struct pf_state_peer *, | struct pf_state_peer *, struct pf_state_peer *, | ||||
u_int8_t, u_int8_t); | u_int8_t, u_int8_t); | ||||
static int pf_tcp_track_sloppy(struct pf_kstate *, | static int pf_tcp_track_sloppy(struct pf_kstate *, | ||||
struct pf_pdesc *, u_short *, | struct pf_pdesc *, u_short *, | ||||
▲ Show 20 Lines • Show All 5,109 Lines • ▼ Show 20 Lines | |||||
#define PF_TEST_ATTRIB(t, a)\ | #define PF_TEST_ATTRIB(t, a)\ | ||||
do { \ | do { \ | ||||
if (t) { \ | if (t) { \ | ||||
r = a; \ | r = a; \ | ||||
goto nextrule; \ | goto nextrule; \ | ||||
} \ | } \ | ||||
} while (0) | } while (0) | ||||
static __inline u_short | |||||
pf_rule_apply_nat(struct pf_pdesc *pd, struct pf_state_key **skp, | |||||
struct pf_state_key **nkp, struct pf_krule *r, struct pf_krule **nr, | |||||
struct pf_udp_mapping **udp_mapping, u_int16_t virtual_type, int *rewrite, | |||||
struct pf_kpool **nat_pool) | |||||
{ | |||||
u_short transerror; | |||||
u_int8_t nat_action; | |||||
if (r->rule_flag & PFRULE_AFTO) { | |||||
/* Don't translate if there was an old style NAT rule */ | |||||
if (*nr != NULL) | |||||
return (PFRES_TRANSLATE); | |||||
/* pass af-to rules, unsupported on match rules */ | |||||
KASSERT(r->action != PF_MATCH, ("%s: af-to on match rule", __func__)); | |||||
/* XXX I can imagine scenarios where we have both NAT and RDR source tracking */ | |||||
*nat_pool = &(r->nat); | |||||
(*nr) = r; | |||||
pd->naf = r->naf; | |||||
if (pf_get_transaddr_af(*nr, pd) == -1) { | |||||
return (PFRES_TRANSLATE); | |||||
} | |||||
return (PFRES_MATCH); | |||||
} else if (r->rdr.cur || r->nat.cur) { | |||||
/* Don't translate if there was an old style NAT rule */ | |||||
if (*nr != NULL) | |||||
return (PFRES_TRANSLATE); | |||||
/* match/pass nat-to/rdr-to rules */ | |||||
(*nr) = r; | |||||
if (r->nat.cur) { | |||||
nat_action = PF_NAT; | |||||
*nat_pool = &(r->nat); | |||||
} else { | |||||
nat_action = PF_RDR; | |||||
*nat_pool = &(r->rdr); | |||||
} | |||||
transerror = pf_get_transaddr(pd, skp, nkp, *nr, udp_mapping, | |||||
nat_action, *nat_pool); | |||||
if (transerror == PFRES_MATCH) { | |||||
(*rewrite) += pf_translate_compat(pd, *skp, *nkp, *nr, | |||||
virtual_type); | |||||
return(PFRES_MATCH); | |||||
} | |||||
return (transerror); | |||||
} | |||||
return (PFRES_MAX); | |||||
} | |||||
static int | static int | ||||
pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, | pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, | ||||
struct pf_pdesc *pd, struct pf_krule **am, | struct pf_pdesc *pd, struct pf_krule **am, | ||||
struct pf_kruleset **rsm, u_short *reason, struct inpcb *inp) | struct pf_kruleset **rsm, u_short *reason, struct inpcb *inp) | ||||
{ | { | ||||
struct pf_krule *nr = NULL; | struct pf_krule *nr = NULL; | ||||
struct pf_krule *r, *a = NULL; | struct pf_krule *r, *a = NULL; | ||||
struct pf_kruleset *ruleset = NULL; | struct pf_kruleset *ruleset = NULL; | ||||
struct pf_krule_slist match_rules; | struct pf_krule_slist match_rules; | ||||
struct pf_krule_item *ri; | struct pf_krule_item *ri; | ||||
struct tcphdr *th = &pd->hdr.tcp; | struct tcphdr *th = &pd->hdr.tcp; | ||||
struct pf_state_key *sk = NULL, *nk = NULL; | struct pf_state_key *sk = NULL, *nk = NULL; | ||||
u_short transerror; | u_short transerror; | ||||
int rewrite = 0; | int rewrite = 0; | ||||
int tag = -1; | int tag = -1; | ||||
int asd = 0; | int asd = 0; | ||||
int match = 0; | int match = 0; | ||||
int state_icmp = 0, icmp_dir; | int state_icmp = 0, icmp_dir; | ||||
int action = PF_PASS; | int action = PF_PASS; | ||||
u_int16_t virtual_type, virtual_id; | u_int16_t virtual_type, virtual_id; | ||||
u_int16_t bproto_sum = 0, bip_sum = 0; | u_int16_t bproto_sum = 0, bip_sum = 0; | ||||
u_int8_t icmptype = 0, icmpcode = 0; | u_int8_t icmptype = 0, icmpcode = 0; | ||||
struct pf_kanchor_stackframe anchor_stack[PF_ANCHOR_STACKSIZE]; | struct pf_kanchor_stackframe anchor_stack[PF_ANCHOR_STACKSIZE]; | ||||
struct pf_udp_mapping *udp_mapping = NULL; | struct pf_udp_mapping *udp_mapping = NULL; | ||||
struct pf_kpool *nat_pool = NULL; | |||||
PF_RULES_RASSERT(); | PF_RULES_RASSERT(); | ||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | PF_ACPY(&pd->nsaddr, pd->src, pd->af); | ||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | ||||
SLIST_INIT(&match_rules); | SLIST_INIT(&match_rules); | ||||
if (inp != NULL) { | if (inp != NULL) { | ||||
INP_LOCK_ASSERT(inp); | INP_LOCK_ASSERT(inp); | ||||
pd->lookup.uid = inp->inp_cred->cr_uid; | pd->lookup.uid = inp->inp_cred->cr_uid; | ||||
pd->lookup.gid = inp->inp_cred->cr_groups[0]; | pd->lookup.gid = inp->inp_cred->cr_groups[0]; | ||||
pd->lookup.done = 1; | pd->lookup.done = 1; | ||||
} | } | ||||
if (pd->ip_sum) | |||||
bip_sum = *pd->ip_sum; | |||||
switch (pd->virtual_proto) { | switch (pd->virtual_proto) { | ||||
case IPPROTO_TCP: | case IPPROTO_TCP: | ||||
bproto_sum = th->th_sum; | |||||
pd->nsport = th->th_sport; | pd->nsport = th->th_sport; | ||||
pd->ndport = th->th_dport; | pd->ndport = th->th_dport; | ||||
break; | break; | ||||
case IPPROTO_UDP: | case IPPROTO_UDP: | ||||
bproto_sum = pd->hdr.udp.uh_sum; | |||||
pd->nsport = pd->hdr.udp.uh_sport; | pd->nsport = pd->hdr.udp.uh_sport; | ||||
pd->ndport = pd->hdr.udp.uh_dport; | pd->ndport = pd->hdr.udp.uh_dport; | ||||
break; | break; | ||||
case IPPROTO_SCTP: | case IPPROTO_SCTP: | ||||
pd->nsport = pd->hdr.sctp.src_port; | pd->nsport = pd->hdr.sctp.src_port; | ||||
pd->ndport = pd->hdr.sctp.dest_port; | pd->ndport = pd->hdr.sctp.dest_port; | ||||
break; | break; | ||||
#ifdef INET | #ifdef INET | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | default: | ||||
REASON_SET(reason, transerror); | REASON_SET(reason, transerror); | ||||
goto cleanup; | goto cleanup; | ||||
case PFRES_MAX: | case PFRES_MAX: | ||||
/* No match. */ | /* No match. */ | ||||
break; | break; | ||||
case PFRES_MATCH: | case PFRES_MATCH: | ||||
KASSERT(sk != NULL, ("%s: null sk", __func__)); | KASSERT(sk != NULL, ("%s: null sk", __func__)); | ||||
KASSERT(nk != NULL, ("%s: null nk", __func__)); | KASSERT(nk != NULL, ("%s: null nk", __func__)); | ||||
if (nr->log) { | if (nr->log) { | ||||
PFLOG_PACKET(nr->action, PFRES_MATCH, nr, a, | PFLOG_PACKET(nr->action, PFRES_MATCH, nr, a, | ||||
ruleset, pd, 1, NULL); | ruleset, pd, 1, NULL); | ||||
} | } | ||||
if (pd->ip_sum) | rewrite += pf_translate_compat(pd, sk, nk, nr, virtual_type); | ||||
bip_sum = *pd->ip_sum; | nat_pool = &(nr->rdr); | ||||
switch (pd->proto) { | |||||
case IPPROTO_TCP: | |||||
bproto_sum = th->th_sum; | |||||
if (PF_ANEQ(&pd->nsaddr, &nk->addr[pd->sidx], pd->af) || | |||||
nk->port[pd->sidx] != pd->nsport) { | |||||
pf_change_ap(pd, pd->src, &th->th_sport, | |||||
&nk->addr[pd->sidx], nk->port[pd->sidx]); | |||||
pd->sport = &th->th_sport; | |||||
pd->nsport = th->th_sport; | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | } | ||||
if (PF_ANEQ(&pd->ndaddr, &nk->addr[pd->didx], pd->af) || | |||||
nk->port[pd->didx] != pd->ndport) { | |||||
pf_change_ap(pd, pd->dst, &th->th_dport, | |||||
&nk->addr[pd->didx], nk->port[pd->didx]); | |||||
pd->dport = &th->th_dport; | |||||
pd->ndport = th->th_dport; | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
rewrite++; | |||||
break; | |||||
case IPPROTO_UDP: | |||||
bproto_sum = pd->hdr.udp.uh_sum; | |||||
if (PF_ANEQ(&pd->nsaddr, &nk->addr[pd->sidx], pd->af) || | |||||
nk->port[pd->sidx] != pd->nsport) { | |||||
pf_change_ap(pd, pd->src, | |||||
&pd->hdr.udp.uh_sport, | |||||
&nk->addr[pd->sidx], | |||||
nk->port[pd->sidx]); | |||||
pd->sport = &pd->hdr.udp.uh_sport; | |||||
pd->nsport = pd->hdr.udp.uh_sport; | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, &nk->addr[pd->didx], pd->af) || | |||||
nk->port[pd->didx] != pd->ndport) { | |||||
pf_change_ap(pd, pd->dst, | |||||
&pd->hdr.udp.uh_dport, | |||||
&nk->addr[pd->didx], | |||||
nk->port[pd->didx]); | |||||
pd->dport = &pd->hdr.udp.uh_dport; | |||||
pd->ndport = pd->hdr.udp.uh_dport; | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
rewrite++; | |||||
break; | |||||
case IPPROTO_SCTP: { | |||||
if (PF_ANEQ(&pd->nsaddr, &nk->addr[pd->sidx], pd->af) || | |||||
nk->port[pd->sidx] != pd->nsport) { | |||||
pf_change_ap(pd, pd->src, | |||||
&pd->hdr.sctp.src_port, | |||||
&nk->addr[pd->sidx], | |||||
nk->port[pd->sidx]); | |||||
pd->sport = &pd->hdr.sctp.src_port; | |||||
pd->nsport = pd->hdr.sctp.src_port; | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, &nk->addr[pd->didx], pd->af) || | |||||
nk->port[pd->didx] != pd->ndport) { | |||||
pf_change_ap(pd, pd->dst, | |||||
&pd->hdr.sctp.dest_port, | |||||
&nk->addr[pd->didx], | |||||
nk->port[pd->didx]); | |||||
pd->dport = &pd->hdr.sctp.dest_port; | |||||
pd->ndport = pd->hdr.sctp.dest_port; | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
break; | |||||
} | |||||
#ifdef INET | |||||
case IPPROTO_ICMP: | |||||
if (PF_ANEQ(&pd->nsaddr, &nk->addr[pd->sidx], AF_INET)) { | |||||
pf_change_a(&pd->src->v4.s_addr, pd->ip_sum, | |||||
nk->addr[pd->sidx].v4.s_addr, 0); | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, &nk->addr[pd->didx], AF_INET)) { | |||||
pf_change_a(&pd->dst->v4.s_addr, pd->ip_sum, | |||||
nk->addr[pd->didx].v4.s_addr, 0); | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
if (virtual_type == htons(ICMP_ECHO) && | |||||
nk->port[pd->sidx] != pd->hdr.icmp.icmp_id) { | |||||
pd->hdr.icmp.icmp_cksum = pf_cksum_fixup( | |||||
pd->hdr.icmp.icmp_cksum, pd->nsport, | |||||
nk->port[pd->sidx], 0); | |||||
pd->hdr.icmp.icmp_id = nk->port[pd->sidx]; | |||||
pd->sport = &pd->hdr.icmp.icmp_id; | |||||
} | |||||
m_copyback(pd->m, pd->off, ICMP_MINLEN, (caddr_t)&pd->hdr.icmp); | |||||
break; | |||||
#endif /* INET */ | |||||
#ifdef INET6 | |||||
case IPPROTO_ICMPV6: | |||||
if (PF_ANEQ(&pd->nsaddr, &nk->addr[pd->sidx], AF_INET6)) { | |||||
pf_change_a6(pd->src, &pd->hdr.icmp6.icmp6_cksum, | |||||
&nk->addr[pd->sidx], 0); | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, &nk->addr[pd->didx], AF_INET6)) { | |||||
pf_change_a6(pd->dst, &pd->hdr.icmp6.icmp6_cksum, | |||||
&nk->addr[pd->didx], 0); | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
rewrite++; | |||||
break; | |||||
#endif /* INET */ | |||||
default: | |||||
switch (pd->af) { | |||||
#ifdef INET | |||||
case AF_INET: | |||||
if (PF_ANEQ(&pd->nsaddr, | |||||
&nk->addr[pd->sidx], AF_INET)) { | |||||
pf_change_a(&pd->src->v4.s_addr, | |||||
pd->ip_sum, | |||||
nk->addr[pd->sidx].v4.s_addr, 0); | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, | |||||
&nk->addr[pd->didx], AF_INET)) { | |||||
pf_change_a(&pd->dst->v4.s_addr, | |||||
pd->ip_sum, | |||||
nk->addr[pd->didx].v4.s_addr, 0); | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
break; | |||||
#endif /* INET */ | |||||
#ifdef INET6 | |||||
case AF_INET6: | |||||
if (PF_ANEQ(&pd->nsaddr, | |||||
&nk->addr[pd->sidx], AF_INET6)) { | |||||
PF_ACPY(&pd->nsaddr, &nk->addr[pd->sidx], pd->af); | |||||
PF_ACPY(pd->src, &nk->addr[pd->sidx], pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, | |||||
&nk->addr[pd->didx], AF_INET6)) { | |||||
PF_ACPY(&pd->ndaddr, &nk->addr[pd->didx], pd->af); | |||||
PF_ACPY(pd->dst, &nk->addr[pd->didx], pd->af); | |||||
} | |||||
break; | |||||
#endif /* INET6 */ | |||||
} | |||||
break; | |||||
} | |||||
if (nr->natpass) | |||||
r = NULL; | |||||
} | |||||
while (r != NULL) { | while (r != NULL) { | ||||
if (pd->related_rule) { | if (pd->related_rule) { | ||||
*rm = pd->related_rule; | *rm = pd->related_rule; | ||||
break; | break; | ||||
} | } | ||||
pf_counter_u64_add(&r->evaluations, 1); | pf_counter_u64_add(&r->evaluations, 1); | ||||
PF_TEST_ATTRIB(pfi_kkif_match(r->kif, pd->kif) == r->ifnot, | PF_TEST_ATTRIB(pfi_kkif_match(r->kif, pd->kif) == r->ifnot, | ||||
r->skip[PF_SKIP_IFP]); | r->skip[PF_SKIP_IFP]); | ||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | PF_TEST_ATTRIB(r->os_fingerprint != PF_OSFP_ANY && | ||||
pf_osfp_fingerprint(pd, th), | pf_osfp_fingerprint(pd, th), | ||||
r->os_fingerprint)), | r->os_fingerprint)), | ||||
TAILQ_NEXT(r, entries)); | TAILQ_NEXT(r, entries)); | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
if (r->tag) | if (r->tag) | ||||
tag = r->tag; | tag = r->tag; | ||||
if (r->anchor == NULL) { | if (r->anchor == NULL) { | ||||
if (r->action == PF_MATCH) { | if (r->action == PF_MATCH) { | ||||
/* | |||||
* Apply translations before increasing counters, | |||||
* in case it fails. | |||||
*/ | |||||
transerror = pf_rule_apply_nat(pd, &sk, &nk, r, | |||||
&nr, &udp_mapping, virtual_type, &rewrite, | |||||
&nat_pool); | |||||
switch (transerror) { | |||||
case PFRES_MATCH: | |||||
/* Translation action found in rule and applied successfully */ | |||||
case PFRES_MAX: | |||||
/* No translation action found in rule */ | |||||
break; | |||||
default: | |||||
/* Translation action found in rule but failed to apply */ | |||||
REASON_SET(reason, transerror); | |||||
goto cleanup; | |||||
} | |||||
ri = malloc(sizeof(struct pf_krule_item), M_PF_RULE_ITEM, M_NOWAIT | M_ZERO); | ri = malloc(sizeof(struct pf_krule_item), M_PF_RULE_ITEM, M_NOWAIT | M_ZERO); | ||||
if (ri == NULL) { | if (ri == NULL) { | ||||
REASON_SET(reason, PFRES_MEMORY); | REASON_SET(reason, PFRES_MEMORY); | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
ri->r = r; | ri->r = r; | ||||
SLIST_INSERT_HEAD(&match_rules, ri, entry); | SLIST_INSERT_HEAD(&match_rules, ri, entry); | ||||
pf_counter_u64_critical_enter(); | pf_counter_u64_critical_enter(); | ||||
pf_counter_u64_add_protected(&r->packets[pd->dir == PF_OUT], 1); | pf_counter_u64_add_protected(&r->packets[pd->dir == PF_OUT], 1); | ||||
pf_counter_u64_add_protected(&r->bytes[pd->dir == PF_OUT], pd->tot_len); | pf_counter_u64_add_protected(&r->bytes[pd->dir == PF_OUT], pd->tot_len); | ||||
pf_counter_u64_critical_exit(); | pf_counter_u64_critical_exit(); | ||||
pf_rule_to_actions(r, &pd->act); | pf_rule_to_actions(r, &pd->act); | ||||
if (r->rule_flag & PFRULE_AFTO) | |||||
pd->naf = r->naf; | |||||
if (pd->af != pd->naf) { | |||||
if (pf_get_transaddr_af(r, pd) == -1) { | |||||
REASON_SET(reason, PFRES_TRANSLATE); | |||||
goto cleanup; | |||||
} | |||||
} | |||||
if (r->log) | if (r->log) | ||||
PFLOG_PACKET(r->action, PFRES_MATCH, r, | PFLOG_PACKET(r->action, PFRES_MATCH, r, | ||||
a, ruleset, pd, 1, NULL); | a, ruleset, pd, 1, NULL); | ||||
} else { | } else { | ||||
match = asd; | match = asd; | ||||
*rm = r; | *rm = r; | ||||
*am = a; | *am = a; | ||||
*rsm = ruleset; | *rsm = ruleset; | ||||
Show All 14 Lines | nextrule: | ||||
r = *rm; | r = *rm; | ||||
a = *am; | a = *am; | ||||
ruleset = *rsm; | ruleset = *rsm; | ||||
REASON_SET(reason, PFRES_MATCH); | REASON_SET(reason, PFRES_MATCH); | ||||
/* apply actions for last matching pass/block rule */ | /* apply actions for last matching pass/block rule */ | ||||
pf_rule_to_actions(r, &pd->act); | pf_rule_to_actions(r, &pd->act); | ||||
if (r->rule_flag & PFRULE_AFTO) | transerror = pf_rule_apply_nat(pd, &sk, &nk, r, &nr, &udp_mapping, | ||||
pd->naf = r->naf; | virtual_type, &rewrite, &nat_pool); | ||||
if (pd->af != pd->naf) { | switch (transerror) { | ||||
if (pf_get_transaddr_af(r, pd) == -1) { | case PFRES_MATCH: | ||||
REASON_SET(reason, PFRES_TRANSLATE); | /* Translation action found in rule and applied successfully */ | ||||
case PFRES_MAX: | |||||
/* No translation action found in rule */ | |||||
break; | |||||
default: | |||||
/* Translation action found in rule but failed to apply */ | |||||
REASON_SET(reason, transerror); | |||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
} | |||||
if (r->log) { | if (r->log) { | ||||
if (rewrite) | if (rewrite) | ||||
m_copyback(pd->m, pd->off, pd->hdrlen, pd->hdr.any); | m_copyback(pd->m, pd->off, pd->hdrlen, pd->hdr.any); | ||||
PFLOG_PACKET(r->action, *reason, r, a, ruleset, pd, 1, NULL); | PFLOG_PACKET(r->action, *reason, r, a, ruleset, pd, 1, NULL); | ||||
} | } | ||||
if (pd->act.log & PF_LOG_MATCHES) | if (pd->act.log & PF_LOG_MATCHES) | ||||
pf_log_matches(pd, r, a, ruleset, &match_rules); | pf_log_matches(pd, r, a, ruleset, &match_rules); | ||||
Show All 14 Lines | if (tag > 0 && pf_tag_packet(pd, tag)) { | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
if (pd->act.rtableid >= 0) | if (pd->act.rtableid >= 0) | ||||
M_SETFIB(pd->m, pd->act.rtableid); | M_SETFIB(pd->m, pd->act.rtableid); | ||||
if (r->rt) { | if (r->rt) { | ||||
struct pf_ksrc_node *sn = NULL; | struct pf_ksrc_node *sn = NULL; | ||||
struct pf_srchash *snh = NULL; | struct pf_srchash *snh = NULL; | ||||
struct pf_kpool *pool = &r->route; | |||||
/* Backwards compatibility. */ | |||||
if (TAILQ_EMPTY(&pool->list)) | |||||
pool = &r->rdr; | |||||
/* | /* | ||||
* Set act.rt here instead of in pf_rule_to_actions() because | * Set act.rt here instead of in pf_rule_to_actions() because | ||||
* it is applied only from the last pass rule. | * it is applied only from the last pass rule. | ||||
*/ | */ | ||||
pd->act.rt = r->rt; | pd->act.rt = r->rt; | ||||
/* Don't use REASON_SET, pf_map_addr increases the reason counters */ | /* Don't use REASON_SET, pf_map_addr increases the reason counters */ | ||||
*reason = pf_map_addr_sn(pd->af, r, pd->src, &pd->act.rt_addr, | *reason = pf_map_addr_sn(pd->af, r, pd->src, &pd->act.rt_addr, | ||||
&pd->act.rt_kif, NULL, &sn, &snh, pool, PF_SN_ROUTE); | &pd->act.rt_kif, NULL, &sn, &snh, &(r->route), PF_SN_ROUTE); | ||||
if (*reason != 0) | if (*reason != 0) | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
if (pd->virtual_proto != PF_VPROTO_FRAGMENT && | if (pd->virtual_proto != PF_VPROTO_FRAGMENT && | ||||
(!state_icmp && (r->keep_state || nr != NULL || | (!state_icmp && (r->keep_state || nr != NULL || | ||||
(pd->flags & PFDESC_TCP_NORM)))) { | (pd->flags & PFDESC_TCP_NORM)))) { | ||||
bool nat64; | bool nat64; | ||||
action = pf_create_state(r, nr, a, pd, nk, sk, | action = pf_create_state(r, nr, a, pd, nk, sk, | ||||
&rewrite, sm, tag, bproto_sum, bip_sum, | &rewrite, sm, tag, bproto_sum, bip_sum, | ||||
&match_rules, udp_mapping, reason); | &match_rules, udp_mapping, nat_pool, reason); | ||||
sk = nk = NULL; | sk = nk = NULL; | ||||
if (action != PF_PASS) { | if (action != PF_PASS) { | ||||
pf_udp_mapping_release(udp_mapping); | pf_udp_mapping_release(udp_mapping); | ||||
if (r->log || (nr != NULL && nr->log) || | if (r->log || (nr != NULL && nr->log) || | ||||
*reason == PFRES_MEMORY) | *reason == PFRES_MEMORY) | ||||
pd->act.log |= PF_LOG_FORCE; | pd->act.log |= PF_LOG_FORCE; | ||||
if (action == PF_DROP && | if (action == PF_DROP && | ||||
(r->rule_flag & PFRULE_RETURN)) | (r->rule_flag & PFRULE_RETURN)) | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | cleanup: | ||||
return (PF_DROP); | return (PF_DROP); | ||||
} | } | ||||
static int | static int | ||||
pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a, | pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a, | ||||
struct pf_pdesc *pd, struct pf_state_key *nk, struct pf_state_key *sk, | struct pf_pdesc *pd, struct pf_state_key *nk, struct pf_state_key *sk, | ||||
int *rewrite, struct pf_kstate **sm, int tag, u_int16_t bproto_sum, | int *rewrite, struct pf_kstate **sm, int tag, u_int16_t bproto_sum, | ||||
u_int16_t bip_sum, struct pf_krule_slist *match_rules, | u_int16_t bip_sum, struct pf_krule_slist *match_rules, | ||||
struct pf_udp_mapping *udp_mapping, u_short *reason) | struct pf_udp_mapping *udp_mapping, struct pf_kpool *nat_pool, | ||||
u_short *reason) | |||||
{ | { | ||||
struct pf_kstate *s = NULL; | struct pf_kstate *s = NULL; | ||||
struct pf_ksrc_node *sns[PF_SN_MAX] = { NULL }; | struct pf_ksrc_node *sns[PF_SN_MAX] = { NULL }; | ||||
/* | /* | ||||
* XXXKS: The hash for PF_SN_LIMIT and PF_SN_ROUTE should be the same | * XXXKS: The hash for PF_SN_LIMIT and PF_SN_ROUTE should be the same | ||||
* but for PF_SN_NAT it is different. Don't try optimizing it, | * but for PF_SN_NAT it is different. Don't try optimizing it, | ||||
* just store all 3 hashes. | * just store all 3 hashes. | ||||
*/ | */ | ||||
struct pf_srchash *snhs[PF_SN_MAX] = { NULL }; | struct pf_srchash *snhs[PF_SN_MAX] = { NULL }; | ||||
struct tcphdr *th = &pd->hdr.tcp; | struct tcphdr *th = &pd->hdr.tcp; | ||||
u_int16_t mss = V_tcp_mssdflt; | u_int16_t mss = V_tcp_mssdflt; | ||||
u_short sn_reason; | u_short sn_reason; | ||||
struct pf_krule_item *ri; | struct pf_krule_item *ri; | ||||
struct pf_kpool *pool_route = &r->route; | |||||
/* check maximums */ | /* check maximums */ | ||||
if (r->max_states && | if (r->max_states && | ||||
(counter_u64_fetch(r->states_cur) >= r->max_states)) { | (counter_u64_fetch(r->states_cur) >= r->max_states)) { | ||||
counter_u64_add(V_pf_status.lcounters[LCNT_STATES], 1); | counter_u64_add(V_pf_status.lcounters[LCNT_STATES], 1); | ||||
REASON_SET(reason, PFRES_MAXSTATES); | REASON_SET(reason, PFRES_MAXSTATES); | ||||
goto csfailed; | goto csfailed; | ||||
} | } | ||||
/* src node for limits */ | /* src node for limits */ | ||||
if ((r->rule_flag & PFRULE_SRCTRACK) && | if ((r->rule_flag & PFRULE_SRCTRACK) && | ||||
(sn_reason = pf_insert_src_node(sns, snhs, r, pd->src, pd->af, | (sn_reason = pf_insert_src_node(sns, snhs, r, pd->src, pd->af, | ||||
NULL, NULL, PF_SN_LIMIT)) != 0) { | NULL, NULL, PF_SN_LIMIT)) != 0) { | ||||
REASON_SET(reason, sn_reason); | REASON_SET(reason, sn_reason); | ||||
goto csfailed; | goto csfailed; | ||||
} | } | ||||
/* src node for route-to rule */ | /* src node for route-to rule */ | ||||
if (TAILQ_EMPTY(&pool_route->list)) /* Backwards compatibility. */ | if (r->rt) { | ||||
pool_route = &r->rdr; | if ((r->route.opts & PF_POOL_STICKYADDR) && | ||||
if ((pool_route->opts & PF_POOL_STICKYADDR) && | (sn_reason = pf_insert_src_node(sns, snhs, r, pd->src, | ||||
(sn_reason = pf_insert_src_node(sns, snhs, r, pd->src, pd->af, | pd->af, &pd->act.rt_addr, pd->act.rt_kif, | ||||
&pd->act.rt_addr, pd->act.rt_kif, PF_SN_ROUTE)) != 0) { | PF_SN_ROUTE)) != 0) { | ||||
kp: Yeah, I think that makes sense. It'd save us doing the check in multiple places.
(There.s… | |||||
REASON_SET(reason, sn_reason); | REASON_SET(reason, sn_reason); | ||||
goto csfailed; | goto csfailed; | ||||
} | } | ||||
} | |||||
/* src node for translation rule */ | /* src node for translation rule */ | ||||
if (nr != NULL && (nr->rdr.opts & PF_POOL_STICKYADDR) && | if (nr != NULL) { | ||||
(sn_reason = pf_insert_src_node(sns, snhs, nr, &sk->addr[pd->sidx], | KASSERT(nat_pool != NULL, ("%s: nat_pool is NULL", __func__)); | ||||
pd->af, &nk->addr[1], NULL, PF_SN_NAT)) != 0 ) { | if ((nat_pool->opts & PF_POOL_STICKYADDR) && | ||||
(sn_reason = pf_insert_src_node(sns, snhs, nr, | |||||
&sk->addr[pd->sidx], pd->af, &nk->addr[1], NULL, | |||||
PF_SN_NAT)) != 0 ) { | |||||
REASON_SET(reason, sn_reason); | REASON_SET(reason, sn_reason); | ||||
goto csfailed; | goto csfailed; | ||||
} | } | ||||
} | |||||
s = pf_alloc_state(M_NOWAIT); | s = pf_alloc_state(M_NOWAIT); | ||||
if (s == NULL) { | if (s == NULL) { | ||||
REASON_SET(reason, PFRES_MEMORY); | REASON_SET(reason, PFRES_MEMORY); | ||||
goto csfailed; | goto csfailed; | ||||
} | } | ||||
s->rule = r; | s->rule = r; | ||||
s->nat_rule = nr; | s->nat_rule = nr; | ||||
s->anchor = a; | s->anchor = a; | ||||
▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | if (pd->proto == IPPROTO_TCP) { | ||||
if (! (pd->sctp_flags & (PFDESC_SCTP_INIT | PFDESC_SCTP_ADD_IP))) | if (! (pd->sctp_flags & (PFDESC_SCTP_INIT | PFDESC_SCTP_ADD_IP))) | ||||
goto csfailed; | goto csfailed; | ||||
} | } | ||||
s->direction = pd->dir; | s->direction = pd->dir; | ||||
/* | /* | ||||
* sk/nk could already been setup by pf_get_translation(). | * sk/nk could already been setup by pf_get_translation(). | ||||
*/ | */ | ||||
if (nr == NULL) { | if (sk == NULL && nk == NULL) { | ||||
KASSERT((sk == NULL && nk == NULL), ("%s: nr %p sk %p, nk %p", | |||||
__func__, nr, sk, nk)); | |||||
MPASS(pd->sport == NULL || (pd->osport == *pd->sport)); | MPASS(pd->sport == NULL || (pd->osport == *pd->sport)); | ||||
MPASS(pd->dport == NULL || (pd->odport == *pd->dport)); | MPASS(pd->dport == NULL || (pd->odport == *pd->dport)); | ||||
if (pf_state_key_setup(pd, pd->nsport, pd->ndport, &sk, &nk)) { | if (pf_state_key_setup(pd, pd->nsport, pd->ndport, &sk, &nk)) { | ||||
goto csfailed; | goto csfailed; | ||||
} | } | ||||
} else | } else | ||||
KASSERT((sk != NULL && nk != NULL), ("%s: nr %p sk %p, nk %p", | KASSERT((sk != NULL && nk != NULL), ("%s: nr %p sk %p, nk %p", | ||||
__func__, nr, sk, nk)); | __func__, nr, sk, nk)); | ||||
▲ Show 20 Lines • Show All 155 Lines • ▼ Show 20 Lines | case IPPROTO_ICMPV6: | ||||
} | } | ||||
break; | break; | ||||
#endif /* INET6 */ | #endif /* INET6 */ | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
return (rewrite); | |||||
} | |||||
int | |||||
pf_translate_compat(struct pf_pdesc *pd, struct pf_state_key *sk, | |||||
struct pf_state_key *nk, struct pf_krule *nr, u_int16_t virtual_type) | |||||
{ | |||||
struct tcphdr *th = &pd->hdr.tcp; | |||||
int rewrite = 0; | |||||
KASSERT(sk != NULL, ("%s: null sk", __func__)); | |||||
KASSERT(nk != NULL, ("%s: null nk", __func__)); | |||||
switch (pd->proto) { | |||||
case IPPROTO_TCP: | |||||
if (PF_ANEQ(&pd->nsaddr, &nk->addr[pd->sidx], pd->af) || | |||||
nk->port[pd->sidx] != pd->nsport) { | |||||
pf_change_ap(pd, pd->src, &th->th_sport, | |||||
&nk->addr[pd->sidx], nk->port[pd->sidx]); | |||||
pd->sport = &th->th_sport; | |||||
Not Done Inline ActionsIf we use pd->sport/pd->dport we ought to be able to fold the UDP and TCP cases into one. It'd be fine to do that in a follow up commit too. kp: If we use pd->sport/pd->dport we ought to be able to fold the UDP and TCP cases into one. It'd… | |||||
pd->nsport = th->th_sport; | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, &nk->addr[pd->didx], pd->af) || | |||||
nk->port[pd->didx] != pd->ndport) { | |||||
pf_change_ap(pd, pd->dst, &th->th_dport, | |||||
&nk->addr[pd->didx], nk->port[pd->didx]); | |||||
pd->dport = &th->th_dport; | |||||
pd->ndport = th->th_dport; | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
rewrite++; | |||||
break; | |||||
case IPPROTO_UDP: | |||||
if (PF_ANEQ(&pd->nsaddr, &nk->addr[pd->sidx], pd->af) || | |||||
nk->port[pd->sidx] != pd->nsport) { | |||||
pf_change_ap(pd, pd->src, | |||||
&pd->hdr.udp.uh_sport, | |||||
&nk->addr[pd->sidx], | |||||
nk->port[pd->sidx]); | |||||
pd->sport = &pd->hdr.udp.uh_sport; | |||||
pd->nsport = pd->hdr.udp.uh_sport; | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, &nk->addr[pd->didx], pd->af) || | |||||
nk->port[pd->didx] != pd->ndport) { | |||||
pf_change_ap(pd, pd->dst, | |||||
&pd->hdr.udp.uh_dport, | |||||
&nk->addr[pd->didx], | |||||
nk->port[pd->didx]); | |||||
pd->dport = &pd->hdr.udp.uh_dport; | |||||
pd->ndport = pd->hdr.udp.uh_dport; | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
rewrite++; | |||||
break; | |||||
case IPPROTO_SCTP: { | |||||
if (PF_ANEQ(&pd->nsaddr, &nk->addr[pd->sidx], pd->af) || | |||||
nk->port[pd->sidx] != pd->nsport) { | |||||
pf_change_ap(pd, pd->src, | |||||
&pd->hdr.sctp.src_port, | |||||
&nk->addr[pd->sidx], | |||||
nk->port[pd->sidx]); | |||||
pd->sport = &pd->hdr.sctp.src_port; | |||||
pd->nsport = pd->hdr.sctp.src_port; | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, &nk->addr[pd->didx], pd->af) || | |||||
nk->port[pd->didx] != pd->ndport) { | |||||
pf_change_ap(pd, pd->dst, | |||||
&pd->hdr.sctp.dest_port, | |||||
&nk->addr[pd->didx], | |||||
nk->port[pd->didx]); | |||||
pd->dport = &pd->hdr.sctp.dest_port; | |||||
pd->ndport = pd->hdr.sctp.dest_port; | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
break; | |||||
Not Done Inline ActionsI think we're missing the 'rewrite++' here. kp: I think we're missing the 'rewrite++' here.
We don't expect to change SCTP port numbers, but… | |||||
} | |||||
#ifdef INET | |||||
case IPPROTO_ICMP: | |||||
if (PF_ANEQ(&pd->nsaddr, &nk->addr[pd->sidx], AF_INET)) { | |||||
pf_change_a(&pd->src->v4.s_addr, pd->ip_sum, | |||||
nk->addr[pd->sidx].v4.s_addr, 0); | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, &nk->addr[pd->didx], AF_INET)) { | |||||
pf_change_a(&pd->dst->v4.s_addr, pd->ip_sum, | |||||
nk->addr[pd->didx].v4.s_addr, 0); | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
if (virtual_type == htons(ICMP_ECHO) && | |||||
nk->port[pd->sidx] != pd->hdr.icmp.icmp_id) { | |||||
pd->hdr.icmp.icmp_cksum = pf_cksum_fixup( | |||||
pd->hdr.icmp.icmp_cksum, pd->nsport, | |||||
nk->port[pd->sidx], 0); | |||||
pd->hdr.icmp.icmp_id = nk->port[pd->sidx]; | |||||
pd->sport = &pd->hdr.icmp.icmp_id; | |||||
} | |||||
m_copyback(pd->m, pd->off, ICMP_MINLEN, (caddr_t)&pd->hdr.icmp); | |||||
break; | |||||
#endif /* INET */ | |||||
#ifdef INET6 | |||||
case IPPROTO_ICMPV6: | |||||
if (PF_ANEQ(&pd->nsaddr, &nk->addr[pd->sidx], AF_INET6)) { | |||||
pf_change_a6(pd->src, &pd->hdr.icmp6.icmp6_cksum, | |||||
&nk->addr[pd->sidx], 0); | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, &nk->addr[pd->didx], AF_INET6)) { | |||||
pf_change_a6(pd->dst, &pd->hdr.icmp6.icmp6_cksum, | |||||
&nk->addr[pd->didx], 0); | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
rewrite++; | |||||
break; | |||||
#endif /* INET */ | |||||
default: | |||||
switch (pd->af) { | |||||
#ifdef INET | |||||
case AF_INET: | |||||
if (PF_ANEQ(&pd->nsaddr, | |||||
&nk->addr[pd->sidx], AF_INET)) { | |||||
pf_change_a(&pd->src->v4.s_addr, | |||||
pd->ip_sum, | |||||
nk->addr[pd->sidx].v4.s_addr, 0); | |||||
PF_ACPY(&pd->nsaddr, pd->src, pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, | |||||
&nk->addr[pd->didx], AF_INET)) { | |||||
pf_change_a(&pd->dst->v4.s_addr, | |||||
pd->ip_sum, | |||||
nk->addr[pd->didx].v4.s_addr, 0); | |||||
PF_ACPY(&pd->ndaddr, pd->dst, pd->af); | |||||
} | |||||
break; | |||||
#endif /* INET */ | |||||
#ifdef INET6 | |||||
case AF_INET6: | |||||
if (PF_ANEQ(&pd->nsaddr, | |||||
&nk->addr[pd->sidx], AF_INET6)) { | |||||
PF_ACPY(&pd->nsaddr, &nk->addr[pd->sidx], pd->af); | |||||
PF_ACPY(pd->src, &nk->addr[pd->sidx], pd->af); | |||||
} | |||||
if (PF_ANEQ(&pd->ndaddr, | |||||
&nk->addr[pd->didx], AF_INET6)) { | |||||
PF_ACPY(&pd->ndaddr, &nk->addr[pd->didx], pd->af); | |||||
PF_ACPY(pd->dst, &nk->addr[pd->didx], pd->af); | |||||
} | |||||
break; | |||||
#endif /* INET6 */ | |||||
} | |||||
break; | |||||
} | |||||
return (rewrite); | return (rewrite); | ||||
} | } | ||||
static int | static int | ||||
pf_tcp_track_full(struct pf_kstate *state, struct pf_pdesc *pd, | pf_tcp_track_full(struct pf_kstate *state, struct pf_pdesc *pd, | ||||
u_short *reason, int *copyback, struct pf_state_peer *src, | u_short *reason, int *copyback, struct pf_state_peer *src, | ||||
struct pf_state_peer *dst, u_int8_t psrc, u_int8_t pdst) | struct pf_state_peer *dst, u_int8_t psrc, u_int8_t pdst) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 4,289 Lines • Show Last 20 Lines |
Yeah, I think that makes sense. It'd save us doing the check in multiple places.
(There.s another one like this in pf_test_rule()).