Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: conntrack: convert nf_conntrack_update to netfilter verdicts

This function calls helpers that can return nf-verdicts, but then
those get converted to -1/0 as thats what the caller expects.

Theoretically NF_DROP could have an errno number set in the upper 24
bits of the return value. Or any of those helpers could return
NF_STOLEN, which would result in use-after-free.

This is fine as-is, the called functions don't do this yet.

But its better to avoid possible future problems if the upcoming
patchset to add NF_DROP_REASON() support gains further users, so remove
the 0/-1 translation from the picture and pass the verdicts down to
the caller.

Signed-off-by: Florian Westphal <fw@strlen.de>

+42 -31
+33 -25
net/netfilter/nf_conntrack_core.c
··· 2169 2169 2170 2170 dataoff = get_l4proto(skb, skb_network_offset(skb), l3num, &l4num); 2171 2171 if (dataoff <= 0) 2172 - return -1; 2172 + return NF_DROP; 2173 2173 2174 2174 if (!nf_ct_get_tuple(skb, skb_network_offset(skb), dataoff, l3num, 2175 2175 l4num, net, &tuple)) 2176 - return -1; 2176 + return NF_DROP; 2177 2177 2178 2178 if (ct->status & IPS_SRC_NAT) { 2179 2179 memcpy(tuple.src.u3.all, ··· 2193 2193 2194 2194 h = nf_conntrack_find_get(net, nf_ct_zone(ct), &tuple); 2195 2195 if (!h) 2196 - return 0; 2196 + return NF_ACCEPT; 2197 2197 2198 2198 /* Store status bits of the conntrack that is clashing to re-do NAT 2199 2199 * mangling according to what it has been done already to this packet. ··· 2206 2206 2207 2207 nat_hook = rcu_dereference(nf_nat_hook); 2208 2208 if (!nat_hook) 2209 - return 0; 2209 + return NF_ACCEPT; 2210 2210 2211 - if (status & IPS_SRC_NAT && 2212 - nat_hook->manip_pkt(skb, ct, NF_NAT_MANIP_SRC, 2213 - IP_CT_DIR_ORIGINAL) == NF_DROP) 2214 - return -1; 2211 + if (status & IPS_SRC_NAT) { 2212 + unsigned int verdict = nat_hook->manip_pkt(skb, ct, 2213 + NF_NAT_MANIP_SRC, 2214 + IP_CT_DIR_ORIGINAL); 2215 + if (verdict != NF_ACCEPT) 2216 + return verdict; 2217 + } 2215 2218 2216 - if (status & IPS_DST_NAT && 2217 - nat_hook->manip_pkt(skb, ct, NF_NAT_MANIP_DST, 2218 - IP_CT_DIR_ORIGINAL) == NF_DROP) 2219 - return -1; 2219 + if (status & IPS_DST_NAT) { 2220 + unsigned int verdict = nat_hook->manip_pkt(skb, ct, 2221 + NF_NAT_MANIP_DST, 2222 + IP_CT_DIR_ORIGINAL); 2223 + if (verdict != NF_ACCEPT) 2224 + return verdict; 2225 + } 2220 2226 2221 - return 0; 2227 + return NF_ACCEPT; 2222 2228 } 2223 2229 2224 2230 /* This packet is coming from userspace via nf_queue, complete the packet ··· 2239 2233 2240 2234 help = nfct_help(ct); 2241 2235 if (!help) 2242 - return 0; 2236 + return NF_ACCEPT; 2243 2237 2244 2238 helper = rcu_dereference(help->helper); 2245 2239 if (!helper) 2246 - return 0; 2240 + return NF_ACCEPT; 2247 2241 2248 2242 if (!(helper->flags & NF_CT_HELPER_F_USERSPACE)) 2249 - return 0; 2243 + return NF_ACCEPT; 2250 2244 2251 2245 switch (nf_ct_l3num(ct)) { 2252 2246 case NFPROTO_IPV4: ··· 2261 2255 protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, 2262 2256 &frag_off); 2263 2257 if (protoff < 0 || (frag_off & htons(~0x7)) != 0) 2264 - return 0; 2258 + return NF_ACCEPT; 2265 2259 break; 2266 2260 } 2267 2261 #endif 2268 2262 default: 2269 - return 0; 2263 + return NF_ACCEPT; 2270 2264 } 2271 2265 2272 2266 if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && 2273 2267 !nf_is_loopback_packet(skb)) { 2274 2268 if (!nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) { 2275 2269 NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); 2276 - return -1; 2270 + return NF_DROP; 2277 2271 } 2278 2272 } 2279 2273 2280 2274 /* We've seen it coming out the other side: confirm it */ 2281 - return nf_conntrack_confirm(skb) == NF_DROP ? - 1 : 0; 2275 + return nf_conntrack_confirm(skb); 2282 2276 } 2283 2277 2284 2278 static int nf_conntrack_update(struct net *net, struct sk_buff *skb) 2285 2279 { 2286 2280 enum ip_conntrack_info ctinfo; 2287 2281 struct nf_conn *ct; 2288 - int err; 2289 2282 2290 2283 ct = nf_ct_get(skb, &ctinfo); 2291 2284 if (!ct) 2292 - return 0; 2285 + return NF_ACCEPT; 2293 2286 2294 2287 if (!nf_ct_is_confirmed(ct)) { 2295 - err = __nf_conntrack_update(net, skb, ct, ctinfo); 2296 - if (err < 0) 2297 - return err; 2288 + int ret = __nf_conntrack_update(net, skb, ct, ctinfo); 2289 + 2290 + if (ret != NF_ACCEPT) 2291 + return ret; 2298 2292 2299 2293 ct = nf_ct_get(skb, &ctinfo); 2294 + if (!ct) 2295 + return NF_ACCEPT; 2300 2296 } 2301 2297 2302 2298 return nf_confirm_cthelper(skb, ct, ctinfo);