diff --git a/sys/net/pf/pf.c b/sys/net/pf/pf.c index 239c7372..c5c2da54 100644 --- a/sys/net/pf/pf.c +++ b/sys/net/pf/pf.c @@ -1478,6 +1478,76 @@ pf_src_tree_remove_state(struct pf_state *s) s->src_node = s->nat_src_node = NULL; } +/* + * Deferred synproxy RST transmission (DF-3340). + * + * pf_unlink_state() may run on non-netisr threads (pf_purge_thread, + * DIOCCLRSTATES/DIOCKILLSTATES ioctls, module unload), but ip_output() + * asserts that it executes on a netisr thread. When a SYN proxy state + * dies in the PF_TCPS_PROXY_DST state, forward the generated RST to a + * netisr for transmission instead of calling pf_send_tcp() directly. + * All parameters are copied: the state and its rule may be freed before + * the netmsg is dispatched. + * + * pf_synproxy_rst_pending tracks netmsgs still in flight so that the + * module is not torn down while a netmsg references module text. + */ +struct pf_synproxy_rst { + struct netmsg_base nm; + sa_family_t af; + struct pf_addr saddr; + struct pf_addr daddr; + u_int16_t sport; + u_int16_t dport; + u_int32_t seq; + u_int32_t ack; + u_int16_t rtag; + int rtableid; +#ifdef ALTQ + u_int32_t qid; + u_int8_t ecn_af; +#endif +}; + +static volatile u_int pf_synproxy_rst_pending; + +static void +pf_synproxy_rst_dispatch(netmsg_t nmsg) +{ + struct pf_synproxy_rst *msg = (struct pf_synproxy_rst *)nmsg; + struct pf_rule r; + + bzero(&r, sizeof(r)); + r.rtableid = msg->rtableid; +#ifdef ALTQ + r.qid = msg->qid; + r.ecn_af = msg->ecn_af; +#endif + + lwkt_gettoken(&pf_token); + pf_send_tcp(&r, msg->af, &msg->saddr, &msg->daddr, + msg->sport, msg->dport, msg->seq, msg->ack, + TH_RST | TH_ACK, 0, 0, 0, 1, msg->rtag, NULL, NULL); + lwkt_reltoken(&pf_token); + + netisr_replymsg(&msg->nm, 0); + atomic_subtract_int(&pf_synproxy_rst_pending, 1); + wakeup(&pf_synproxy_rst_pending); +} + +/* + * Wait (bounded) for deferred synproxy RST netmsgs to drain, so the + * module is not unloaded while a netmsg still references module text. + */ +void +pf_synproxy_rst_drain(void) +{ + int i; + + for (i = 0; i < 10 && pf_synproxy_rst_pending != 0; ++i) + tsleep(&pf_synproxy_rst_pending, 0, "pfrst", hz / 10); +} + /* callers should be at crit_enter() */ void pf_unlink_state(struct pf_state *cur) @@ -1486,13 +1556,47 @@ pf_unlink_state(struct pf_state *cur) if (cur->src.state == PF_TCPS_PROXY_DST) { /* XXX wire key the right one? */ - pf_send_tcp(cur->rule.ptr, cur->key[PF_SK_WIRE]->af, - &cur->key[PF_SK_WIRE]->addr[1], - &cur->key[PF_SK_WIRE]->addr[0], - cur->key[PF_SK_WIRE]->port[1], - cur->key[PF_SK_WIRE]->port[0], - cur->src.seqhi, cur->src.seqlo + 1, - TH_RST|TH_ACK, 0, 0, 0, 1, cur->tag, NULL, NULL); + if (IN_NETISR_NCPUS(cpu)) { + /* packet path: already on a netisr thread */ + pf_send_tcp(cur->rule.ptr, + cur->key[PF_SK_WIRE]->af, + &cur->key[PF_SK_WIRE]->addr[1], + &cur->key[PF_SK_WIRE]->addr[0], + cur->key[PF_SK_WIRE]->port[1], + cur->key[PF_SK_WIRE]->port[0], + cur->src.seqhi, cur->src.seqlo + 1, + TH_RST|TH_ACK, 0, 0, 0, 1, cur->tag, + NULL, NULL); + } else { + struct pf_synproxy_rst *msg; + const struct pf_state_key *sk = cur->key[PF_SK_WIRE]; + + msg = kmalloc(sizeof(*msg), M_LWKTMSG, + M_NOWAIT | M_NULLOK); + if (msg != NULL) { + msg->af = sk->af; + msg->saddr = sk->addr[1]; + msg->daddr = sk->addr[0]; + msg->sport = sk->port[1]; + msg->dport = sk->port[0]; + msg->seq = cur->src.seqhi; + msg->ack = cur->src.seqlo + 1; + msg->rtag = cur->tag; + msg->rtableid = cur->rule.ptr != NULL ? + cur->rule.ptr->rtableid : -1; +#ifdef ALTQ + msg->qid = cur->rule.ptr != NULL ? + cur->rule.ptr->qid : 0; + msg->ecn_af = sk->af; +#endif + netmsg_init(&msg->nm, NULL, + &netisr_afree_rport, 0, + pf_synproxy_rst_dispatch); + atomic_add_int(&pf_synproxy_rst_pending, 1); + netisr_sendmsg(&msg->nm, + cpu % netisr_ncpus); + } + } } RB_REMOVE(pf_state_tree_id, &tree_id[cpu], cur); if (cur->creatorid == pf_status.hostid) diff --git a/sys/net/pf/pf_ioctl.c b/sys/net/pf/pf_ioctl.c index ce280fc8..cd6b9644 100644 --- a/sys/net/pf/pf_ioctl.c +++ b/sys/net/pf/pf_ioctl.c @@ -3391,6 +3391,8 @@ pf_unload(void) wakeup_one(pf_purge_thread); tsleep(pf_purge_thread, 0, "pftmo", hz / 10); } + /* drain deferred synproxy RST netmsgs (DF-3340) */ + pf_synproxy_rst_drain(); pfi_cleanup(); pf_osfp_flush(); dev_ops_remove_all(&pf_ops); diff --git a/sys/net/pf/pfvar.h b/sys/net/pf/pfvar.h index 30ee636d..70411d12 100644 --- a/sys/net/pf/pfvar.h +++ b/sys/net/pf/pfvar.h @@ -1798,6 +1798,7 @@ extern int pf_purge_expired_src_nodes(int); extern int pf_purge_expired_states(u_int32_t, int); extern void pf_unlink_state(struct pf_state *); extern void pf_free_state(struct pf_state *); +extern void pf_synproxy_rst_drain(void); extern int pf_state_insert(struct pfi_kif *, struct pf_state_key *, struct pf_state_key *,