Hello, the issue described here has been hit bu Stuart some time ago. feel free to stop reading if you don't care/use pf(4) synproxy.
let's assume there are rules which allow just surfing web over http: block all pass proto tcp from any to any port = 80 synproxy state pass proto udp from any to any port = 53 The 'synproxy' rule is not bound to any interface nor any direction. As such it matches every forwarded SYN packet to port 80 two times. If there would have been no 'synproxy state' in rule, then pair of states gets created: A -> B:80 @ IN # inbound direction at RX NIC A -> B:80 @ OUT # outbound direction at TX NIC The 'synproxy' action changes things a bit. The 'synproxy' action is handled here in pf_create_state(): 4163 if (pd->proto == IPPROTO_TCP && (th->th_flags & (TH_SYN|TH_ACK)) == 4164 TH_SYN && r->keep_state == PF_STATE_SYNPROXY) { 4165 int rtid = pd->rdomain; 4166 if (act->rtableid >= 0) 4167 rtid = act->rtableid; 4168 pf_set_protostate(s, PF_PEER_SRC, PF_TCPS_PROXY_SRC); 4169 s->src.seqhi = arc4random(); 4170 /* Find mss option */ 4171 mss = pf_get_mss(pd); 4172 mss = pf_calc_mss(pd->src, pd->af, rtid, mss); 4173 mss = pf_calc_mss(pd->dst, pd->af, rtid, mss); 4174 s->src.mss = mss; 4175 pf_send_tcp(r, pd->af, pd->dst, pd->src, th->th_dport, 4176 th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1, 4177 TH_SYN|TH_ACK, 0, s->src.mss, 0, 1, 0, pd->rdomain); ^^ 4178 REASON_SET(&reason, PFRES_SYNPROXY); 4179 return (PF_SYNPROXY_DROP); 4180 } Note pf_send_tcp() at line 4175 is being called with argument `tag` set to 1. `tag` == 1 orders pf_send_tcp() to send PF_TAG_GENERATED flag at packet. 2770 struct mbuf * 2771 pf_build_tcp(const struct pf_rule *r, sa_family_t af, 2772 const struct pf_addr *saddr, const struct pf_addr *daddr, 2773 u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack, 2774 u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, int tag, 2775 u_int16_t rtag, u_int sack, u_int rdom) 2776 { .... 2805 2806 /* create outgoing mbuf */ 2807 m = m_gethdr(M_DONTWAIT, MT_HEADER); 2808 if (m == NULL) 2809 return (NULL); 2810 if (tag) 2811 m->m_pkthdr.pf.flags |= PF_TAG_GENERATED; 2812 m->m_pkthdr.pf.tag = rtag; the PF_TAG_GENERATED flag brings an unexpected effect to packet sent by pf_send_tcp() at line 4175. The packet sent by pf_send_tcp() is intercepted by pf_test() as outbound. However PF_TAG_GENERATED flag turns pf_test() into No-OP: 6855 int 6856 pf_test(sa_family_t af, int fwdir, struct ifnet *ifp, struct mbuf **m0) 6857 { .... #ifdef DIAGNOSTIC 6890 if (((*m0)->m_flags & M_PKTHDR) == 0) 6891 panic("non-M_PKTHDR is passed to pf_test"); 6892 #endif /* DIAGNOSTIC */ 6893 6894 if ((*m0)->m_pkthdr.pf.flags & PF_TAG_GENERATED) 6895 return (PF_PASS); 6896 6897 if ((*m0)->m_pkthdr.pf.flags & PF_TAG_DIVERTED_PACKET) 6898 return (PF_PASS); 6899 however it is not desired in this case. We actually do want PF to create a state for such packet, so response B:80 -> A can enter the firewall. the fix is to apply synproxy action on inbound packets only. Diff below does that exactly. Furthermore it also makes pfctl(8) to emit warning, when synproxy is being used in outbound/unbound rule: lumpy$ echo 'pass proto tcp from any to any port = 80 synproxy state' |./pfctl -nf - warning (stdin:1): synproxy acts on inbound packets only synproxy action is ignored for outbound packets OK? thanks and regards sashan --------8<---------------8<---------------8<------------------8<-------- diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index f06171158cb..d052b5b9a0e 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -4042,6 +4042,13 @@ rule_consistent(struct pf_rule *r) "synproxy state or modulate state"); problems++; } + + if ((r->keep_state == PF_STATE_SYNPROXY) && (r->direction != PF_IN)) + fprintf(stderr, "warning (%s:%d): " + "synproxy acts on inbound packets only\n" + " synproxy action is ignored for outbound packets\n", + file->name, yylval.lineno); + if ((r->nat.addr.type != PF_ADDR_NONE || r->rdr.addr.type != PF_ADDR_NONE) && r->action != PF_MATCH && !r->keep_state) { diff --git a/sys/net/pf.c b/sys/net/pf.c index 823fdc22133..986ee57bff9 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -4161,7 +4161,7 @@ pf_create_state(struct pf_pdesc *pd, struct pf_rule *r, struct pf_rule *a, s->tag = tag; } if (pd->proto == IPPROTO_TCP && (th->th_flags & (TH_SYN|TH_ACK)) == - TH_SYN && r->keep_state == PF_STATE_SYNPROXY) { + TH_SYN && r->keep_state == PF_STATE_SYNPROXY && pd->dir == PF_IN) { int rtid = pd->rdomain; if (act->rtableid >= 0) rtid = act->rtableid;