diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -5025,14 +5025,50 @@ KASSERT((sk != NULL && nk != NULL), ("%s: nr %p sk %p, nk %p", __func__, nr, sk, nk)); + struct pf_keyhash *skh; + struct pf_state_key *cur; /* Swap sk/nk for PF_OUT. */ - if (pf_state_insert(BOUND_IFACE(s, kif), kif, - (pd->dir == PF_IN) ? sk : nk, - (pd->dir == PF_IN) ? nk : sk, s)) { + struct pf_state_key *wk = (pd->dir == PF_IN) ? sk : nk; + struct pf_state_key *stk = (pd->dir == PF_IN) ? nk : sk; + int fixed_srcport = 0; + +tryagain: + + /* check if we can insert stack key */ + skh = &V_pf_keyhash[pf_hashkey(stk)]; + + PF_HASHROW_LOCK(skh); + + LIST_FOREACH(cur, &skh->keys, entry) + if (bcmp(cur, stk, sizeof(struct pf_state_key_cmp)) == 0) + break; + PF_HASHROW_UNLOCK(skh); + + if (cur != NULL) { + /* A mapping for this src port already exists */ + DTRACE_PROBE2(found_keys, + struct pf_state_key *, stk, struct pf_state_key *, cur); + + /* try to find an avialable sk src port */ + stk->port[0]++; + fixed_srcport = stk->port[0]; + + goto tryagain; + } + + if (pf_state_insert(BOUND_IFACE(s, kif), kif, wk, stk, s)) { REASON_SET(&reason, PFRES_STATEINS); goto drop; - } else + } else { + if (fixed_srcport) { + pf_change_ap(m, pd->src, &th->th_sport, + pd->ip_sum, &th->th_sum, &stk->addr[pd->sidx], + stk->port[pd->sidx], 0, pd->af); + + pd->sport = &th->th_sport; + } *sm = s; + } if (tag > 0) s->tag = tag;