DF-3340 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | 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 *, |