</snip> > >> > >> But we'll drop this "reference" in pf_src_tree_remove_state, > >> then how will sns[PF_SN_NAT] and sns[PF_SN_ROUTE] be different? > > > > I think I should take PF class again ;-) I've just realized there > > is a test in pf_remove_src_node(): > > > > 572 if (sn->states > 0 || sn->expire > time_uptime) > > 573 return; > > > > so it will do the right thing. This is the piece I was missing. > > > >> > >> sns[] array was prepared for this state so if we can't insert > >> the state, sns entries must be cleaned up. pf_remove_src_node > >> checks the number of associated states and if source node should > >> expire some time later. > > > > yes, it seems more clear to me now. > > > > Good! At least I wasn't blind this time! (: > > >> > >> Speaking of PF_SN_ROUTE, pf_set_rt_ifp should be probably called > >> before we insert the state for the very same reason, plus it > >> should check the pf_map_addr return value and do the cleanup. > > > > I don't feel entirely qualified now, to discuss the matter ;-) > > Hey, don't worry about it. Half of the people reading this have > zero clue as to what the hell are we talking about. > > > however > > pf_set_rt_ifp() should indeed test return value of pf_map_addr(), In case of > > failure the error should be thrown further up, so pf_create_state() can > > handle > > it. Probably jumping to csfailed: should be sufficient. > > > > You can't just jump to csfailed unless you do a pf_set_rt_ifp > before the pf_state_insert, but then it needs an attached key > to only get it's address family.
true, because once pf_state_insert() succeeds, it's no longer job for csfailed: branch to clean up a state. I currently have no better suggestion than taking the easiest move: add af argument to pf_set_rt_ifp() and fetch it from skw, in pf_create_state(). Actually there is an option: remove KASSERT() at 655 in pf_state_key_attach(). 659 pf_state_key_attach(struct pf_state_key *sk, struct pf_state *s, int idx) 660 { 661 struct pf_state_item *si; 662 struct pf_state_key *cur; 663 struct pf_state *olds = NULL; 664 665 KASSERT(s->key[idx] == NULL); Then we can simply do: s->key[PF_SK_WIRE] = skw; in pf_create_state(), so pf_set_rt_ifp() will get what it expects. adding af argument to pf_set_rt_if() seems more clean approach to me. below is your patch with reshuffled pf_create_state(), so pf_set_rt_ifp() gets called before, pf_state_insert(). I've introduced a new reason: PFRES_NOROUTE however I'm not sure if it is descriptive enough... regards sasha ? create_state.diff ? pf.c.diff ? pf.c.patch Index: pf.c =================================================================== RCS file: /cvs/src/sys/net/pf.c,v retrieving revision 1.916 diff -u -r1.916 pf.c --- pf.c 26 May 2015 16:17:51 -0000 1.916 +++ pf.c 28 May 2015 21:38:09 -0000 @@ -204,8 +204,8 @@ u_int16_t pf_get_mss(struct pf_pdesc *); u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, int, u_int16_t); -void pf_set_rt_ifp(struct pf_state *, - struct pf_addr *); +int pf_set_rt_ifp(struct pf_state *, + struct pf_addr *, sa_family_t); struct pf_divert *pf_get_divert(struct mbuf *); int pf_walk_option6(struct pf_pdesc *, struct ip6_hdr *, int, int, u_short *); @@ -2958,32 +2958,39 @@ return (mss); } -void -pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr) +int +pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr, sa_family_t af) { struct pf_rule *r = s->rule.ptr; struct pf_src_node *sns[PF_SN_MAX]; + int rv; s->rt_kif = NULL; if (!r->rt) - return; + return (0); + bzero(sns, sizeof(sns)); - switch (s->key[PF_SK_WIRE]->af) { + switch (af) { case AF_INET: - pf_map_addr(AF_INET, r, saddr, &s->rt_addr, NULL, sns, + rv = pf_map_addr(AF_INET, r, saddr, &s->rt_addr, NULL, sns, &r->route, PF_SN_ROUTE); - s->rt_kif = r->route.kif; - s->natrule.ptr = r; break; #ifdef INET6 case AF_INET6: - pf_map_addr(AF_INET6, r, saddr, &s->rt_addr, NULL, sns, + rv = pf_map_addr(AF_INET6, r, saddr, &s->rt_addr, NULL, sns, &r->route, PF_SN_ROUTE); - s->rt_kif = r->route.kif; - s->natrule.ptr = r; break; #endif /* INET6 */ + default: + rv = 1; } + + if (rv == 0) { + s->rt_kif = r->route.kif; + s->natrule.ptr = r; + } + + return (rv); } u_int32_t @@ -3557,16 +3564,6 @@ goto csfailed; } - if (pf_state_insert(BOUND_IFACE(r, pd->kif), skw, sks, s)) { - pf_state_key_detach(s, PF_SK_STACK); - pf_state_key_detach(s, PF_SK_WIRE); - *sks = *skw = NULL; - REASON_SET(&reason, PFRES_STATEINS); - goto csfailed; - } else - *sm = s; - - /* attach src nodes late, otherwise cleanup on error nontrivial */ for (i = 0; i < PF_SN_MAX; i++) if (sns[i] != NULL) { struct pf_sn_item *sni; @@ -3574,17 +3571,26 @@ sni = pool_get(&pf_sn_item_pl, PR_NOWAIT); if (sni == NULL) { REASON_SET(&reason, PFRES_MEMORY); - pf_src_tree_remove_state(s); - STATE_DEC_COUNTERS(s); - pool_put(&pf_state_pl, s); - return (PF_DROP); + goto csfailed; } sni->sn = sns[i]; SLIST_INSERT_HEAD(&s->src_nodes, sni, next); sni->sn->states++; } - pf_set_rt_ifp(s, pd->src); /* needs s->state_key set */ + if (pf_set_rt_ifp(s, pd->src, (*skw)->af) != 0) { + REASON_SET(&reason, PFRES_NOROUTE); + goto csfailed; + } + + if (pf_state_insert(BOUND_IFACE(r, pd->kif), skw, sks, s)) { + pf_detach_state(s); + *sks = *skw = NULL; + REASON_SET(&reason, PFRES_STATEINS); + goto csfailed; + } else + *sm = s; + if (tag > 0) { pf_tag_ref(tag); s->tag = tag; @@ -3612,12 +3618,16 @@ return (PF_PASS); csfailed: + if (s) { + pf_normalize_tcp_cleanup(s); /* safe even w/o init */ + pf_src_tree_remove_state(s); + } + for (i = 0; i < PF_SN_MAX; i++) if (sns[i] != NULL) pf_remove_src_node(sns[i]); + if (s) { - pf_normalize_tcp_cleanup(s); /* safe even w/o init */ - pf_src_tree_remove_state(s); STATE_DEC_COUNTERS(s); pool_put(&pf_state_pl, s); } Index: pfvar.h =================================================================== RCS file: /cvs/src/sys/net/pfvar.h,v retrieving revision 1.414 diff -u -r1.414 pfvar.h --- pfvar.h 11 Apr 2015 13:00:12 -0000 1.414 +++ pfvar.h 28 May 2015 21:38:09 -0000 @@ -1316,7 +1316,8 @@ #define PFRES_SRCLIMIT 13 /* Source node/conn limit */ #define PFRES_SYNPROXY 14 /* SYN proxy */ #define PFRES_TRANSLATE 15 /* No translation address available */ -#define PFRES_MAX 16 /* total+1 */ +#define PFRES_NOROUTE 16 /* No route found for PBR action */ +#define PFRES_MAX 17 /* total+1 */ #define PFRES_NAMES { \ "match", \ @@ -1335,6 +1336,7 @@ "src-limit", \ "synproxy", \ "translate", \ + "no-route", \ NULL \ }