The libnetfilter_conntrack userland library always sets IPS_CONFIRMED when building a CTA_STATUS attribute. If this toggles the bit from 0->1, the parser will return an error. On Linux 4.4+ this will cause any NFQA_EXP attribute in the packet to be ignored. This breaks conntrackd's userland helpers because they operate on unconfirmed connections. Instead of returning -EBUSY if the user program asks to modify an unchangeable bit, simply ignore the change. Also, fix the logic so that user programs are allowed to clear the bits that they are allowed to change.
Unified · Split
| @@ -82,6 +82,10 @@ enum ip_conntrack_status { · 81 unmodified lines | |||
| 82 | IPS_DYING_BIT = 9, | 82 | IPS_DYING_BIT = 9, |
| 83 | IPS_DYING = (1 << IPS_DYING_BIT), | 83 | IPS_DYING = (1 << IPS_DYING_BIT), |
| 84 | 84 | ||
| 85 | /* Bits that cannot be altered from userland. */ | ||
| 86 | IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK | | ||
| 87 | IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING), | ||
| 88 | |||
| 85 | /* Connection has fixed timeout. */ | 89 | /* Connection has fixed timeout. */ |
| 86 | IPS_FIXED_TIMEOUT_BIT = 10, | 90 | IPS_FIXED_TIMEOUT_BIT = 10, |
| 87 | IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), | 91 | IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), |
| @@ -2269,6 +2269,30 @@ nla_put_failure: · 2268 unmodified lines | |||
| 2269 | return -ENOSPC; | 2269 | return -ENOSPC; |
| 2270 | } | 2270 | } |
| 2271 | 2271 | ||
| 2272 | static int | ||
| 2273 | ctnetlink_update_status(struct nf_conn *ct, const struct nlattr * const cda[]) | ||
| 2274 | { | ||
| 2275 | unsigned int status = ntohl(nla_get_be32(cda[CTA_STATUS])); | ||
| 2276 | unsigned long d = ct->status ^ status; | ||
| 2277 | |||
| 2278 | if (d & IPS_SEEN_REPLY && !(status & IPS_SEEN_REPLY)) | ||
| 2279 | /* SEEN_REPLY bit can only be set */ | ||
| 2280 | return -EBUSY; | ||
| 2281 | |||
| 2282 | if (d & IPS_ASSURED && !(status & IPS_ASSURED)) | ||
| 2283 | /* ASSURED bit can only be set */ | ||
| 2284 | return -EBUSY; | ||
| 2285 | |||
| 2286 | /* This check is less strict than ctnetlink_change_status() | ||
| 2287 | * because callers often flip IPS_EXPECTED bits when sending | ||
| 2288 | * an NFQA_CT attribute to the kernel. So ignore the | ||
| 2289 | * unchangeable bits but do not error out. | ||
| 2290 | */ | ||
| 2291 | ct->status = (status & ~IPS_UNCHANGEABLE_MASK) | | ||
| 2292 | (ct->status & IPS_UNCHANGEABLE_MASK); | ||
| 2293 | return 0; | ||
| 2294 | } | ||
| 2295 | |||
| 2272 | static int | 2296 | static int |
| 2273 | ctnetlink_glue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct) | 2297 | ctnetlink_glue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct) |
| 2274 | { | 2298 | { |
| @@ -2280,7 +2304,7 @@ ctnetlink_glue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct) · 5 unmodified lines | |||
| 2280 | return err; | 2304 | return err; |
| 2281 | } | 2305 | } |
| 2282 | if (cda[CTA_STATUS]) { | 2306 | if (cda[CTA_STATUS]) { |
| 2283 | err = ctnetlink_change_status(ct, cda); | 2307 | err = ctnetlink_update_status(ct, cda); |
| 2284 | if (err < 0) | 2308 | if (err < 0) |
| 2285 | return err; | 2309 | return err; |
| 2286 | } | 2310 | } |