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

netfilter: disable defrag once its no longer needed

When I changed defrag hooks to no longer get registered by default I
intentionally made it so that registration can only be un-done by unloading
the nf_defrag_ipv4/6 module.

In hindsight this was too conservative; there is no reason to keep defrag
on while there is no feature dependency anymore.

Moreover, this won't work if user isn't allowed to remove nf_defrag module.

This adds the disable() functions for both ipv4 and ipv6 and calls them
from conntrack, TPROXY and the xtables socket module.

ipvs isn't converted here, it will behave as before this patch and
will need module removal.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
de8c1211 e0bb96db

+108 -16
+2 -1
include/net/netfilter/ipv4/nf_defrag_ipv4.h
··· 3 3 #define _NF_DEFRAG_IPV4_H 4 4 5 5 struct net; 6 - int nf_defrag_ipv4_enable(struct net *); 6 + int nf_defrag_ipv4_enable(struct net *net); 7 + void nf_defrag_ipv4_disable(struct net *net); 7 8 8 9 #endif /* _NF_DEFRAG_IPV4_H */
+2 -1
include/net/netfilter/ipv6/nf_defrag_ipv6.h
··· 5 5 #include <linux/skbuff.h> 6 6 #include <linux/types.h> 7 7 8 - int nf_defrag_ipv6_enable(struct net *); 8 + int nf_defrag_ipv6_enable(struct net *net); 9 + void nf_defrag_ipv6_disable(struct net *net); 9 10 10 11 int nf_ct_frag6_init(void); 11 12 void nf_ct_frag6_cleanup(void);
+24 -6
net/ipv4/netfilter/nf_defrag_ipv4.c
··· 141 141 struct defrag4_pernet *nf_defrag = net_generic(net, defrag4_pernet_id); 142 142 int err = 0; 143 143 144 - might_sleep(); 145 - 146 - if (nf_defrag->users) 147 - return 0; 148 - 149 144 mutex_lock(&defrag4_mutex); 150 - if (nf_defrag->users) 145 + if (nf_defrag->users == UINT_MAX) { 146 + err = -EOVERFLOW; 151 147 goto out_unlock; 148 + } 149 + 150 + if (nf_defrag->users) { 151 + nf_defrag->users++; 152 + goto out_unlock; 153 + } 152 154 153 155 err = nf_register_net_hooks(net, ipv4_defrag_ops, 154 156 ARRAY_SIZE(ipv4_defrag_ops)); ··· 162 160 return err; 163 161 } 164 162 EXPORT_SYMBOL_GPL(nf_defrag_ipv4_enable); 163 + 164 + void nf_defrag_ipv4_disable(struct net *net) 165 + { 166 + struct defrag4_pernet *nf_defrag = net_generic(net, defrag4_pernet_id); 167 + 168 + mutex_lock(&defrag4_mutex); 169 + if (nf_defrag->users) { 170 + nf_defrag->users--; 171 + if (nf_defrag->users == 0) 172 + nf_unregister_net_hooks(net, ipv4_defrag_ops, 173 + ARRAY_SIZE(ipv4_defrag_ops)); 174 + } 175 + 176 + mutex_unlock(&defrag4_mutex); 177 + } 178 + EXPORT_SYMBOL_GPL(nf_defrag_ipv4_disable); 165 179 166 180 module_init(nf_defrag_init); 167 181 module_exit(nf_defrag_fini);
+23 -6
net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
··· 137 137 struct nft_ct_frag6_pernet *nf_frag = net_generic(net, nf_frag_pernet_id); 138 138 int err = 0; 139 139 140 - might_sleep(); 141 - 142 - if (nf_frag->users) 143 - return 0; 144 - 145 140 mutex_lock(&defrag6_mutex); 146 - if (nf_frag->users) 141 + if (nf_frag->users == UINT_MAX) { 142 + err = -EOVERFLOW; 147 143 goto out_unlock; 144 + } 145 + 146 + if (nf_frag->users) { 147 + nf_frag->users++; 148 + goto out_unlock; 149 + } 148 150 149 151 err = nf_register_net_hooks(net, ipv6_defrag_ops, 150 152 ARRAY_SIZE(ipv6_defrag_ops)); ··· 158 156 return err; 159 157 } 160 158 EXPORT_SYMBOL_GPL(nf_defrag_ipv6_enable); 159 + 160 + void nf_defrag_ipv6_disable(struct net *net) 161 + { 162 + struct nft_ct_frag6_pernet *nf_frag = net_generic(net, nf_frag_pernet_id); 163 + 164 + mutex_lock(&defrag6_mutex); 165 + if (nf_frag->users) { 166 + nf_frag->users--; 167 + if (nf_frag->users == 0) 168 + nf_unregister_net_hooks(net, ipv6_defrag_ops, 169 + ARRAY_SIZE(ipv6_defrag_ops)); 170 + } 171 + mutex_unlock(&defrag6_mutex); 172 + } 173 + EXPORT_SYMBOL_GPL(nf_defrag_ipv6_disable); 161 174 162 175 module_init(nf_defrag_init); 163 176 module_exit(nf_defrag_fini);
+6 -2
net/netfilter/nf_conntrack_proto.c
··· 536 536 mutex_lock(&nf_ct_proto_mutex); 537 537 switch (nfproto) { 538 538 case NFPROTO_IPV4: 539 - if (cnet->users4 && (--cnet->users4 == 0)) 539 + if (cnet->users4 && (--cnet->users4 == 0)) { 540 540 nf_unregister_net_hooks(net, ipv4_conntrack_ops, 541 541 ARRAY_SIZE(ipv4_conntrack_ops)); 542 + nf_defrag_ipv4_disable(net); 543 + } 542 544 break; 543 545 #if IS_ENABLED(CONFIG_IPV6) 544 546 case NFPROTO_IPV6: 545 - if (cnet->users6 && (--cnet->users6 == 0)) 547 + if (cnet->users6 && (--cnet->users6 == 0)) { 546 548 nf_unregister_net_hooks(net, ipv6_conntrack_ops, 547 549 ARRAY_SIZE(ipv6_conntrack_ops)); 550 + nf_defrag_ipv6_disable(net); 551 + } 548 552 break; 549 553 #endif 550 554 case NFPROTO_BRIDGE:
+24
net/netfilter/nft_tproxy.c
··· 263 263 return 0; 264 264 } 265 265 266 + static void nft_tproxy_destroy(const struct nft_ctx *ctx, 267 + const struct nft_expr *expr) 268 + { 269 + const struct nft_tproxy *priv = nft_expr_priv(expr); 270 + 271 + switch (priv->family) { 272 + case NFPROTO_IPV4: 273 + nf_defrag_ipv4_disable(ctx->net); 274 + break; 275 + #if IS_ENABLED(CONFIG_NF_TABLES_IPV6) 276 + case NFPROTO_IPV6: 277 + nf_defrag_ipv6_disable(ctx->net); 278 + break; 279 + #endif 280 + case NFPROTO_UNSPEC: 281 + nf_defrag_ipv4_disable(ctx->net); 282 + #if IS_ENABLED(CONFIG_NF_TABLES_IPV6) 283 + nf_defrag_ipv6_disable(ctx->net); 284 + #endif 285 + break; 286 + } 287 + } 288 + 266 289 static int nft_tproxy_dump(struct sk_buff *skb, 267 290 const struct nft_expr *expr) 268 291 { ··· 311 288 .size = NFT_EXPR_SIZE(sizeof(struct nft_tproxy)), 312 289 .eval = nft_tproxy_eval, 313 290 .init = nft_tproxy_init, 291 + .destroy = nft_tproxy_destroy, 314 292 .dump = nft_tproxy_dump, 315 293 }; 316 294
+13
net/netfilter/xt_TPROXY.c
··· 200 200 pr_info_ratelimited("Can be used only with -p tcp or -p udp\n"); 201 201 return -EINVAL; 202 202 } 203 + 204 + static void tproxy_tg6_destroy(const struct xt_tgdtor_param *par) 205 + { 206 + nf_defrag_ipv6_disable(par->net); 207 + } 203 208 #endif 204 209 205 210 static int tproxy_tg4_check(const struct xt_tgchk_param *par) ··· 224 219 return -EINVAL; 225 220 } 226 221 222 + static void tproxy_tg4_destroy(const struct xt_tgdtor_param *par) 223 + { 224 + nf_defrag_ipv4_disable(par->net); 225 + } 226 + 227 227 static struct xt_target tproxy_tg_reg[] __read_mostly = { 228 228 { 229 229 .name = "TPROXY", ··· 238 228 .revision = 0, 239 229 .targetsize = sizeof(struct xt_tproxy_target_info), 240 230 .checkentry = tproxy_tg4_check, 231 + .destroy = tproxy_tg4_destroy, 241 232 .hooks = 1 << NF_INET_PRE_ROUTING, 242 233 .me = THIS_MODULE, 243 234 }, ··· 250 239 .revision = 1, 251 240 .targetsize = sizeof(struct xt_tproxy_target_info_v1), 252 241 .checkentry = tproxy_tg4_check, 242 + .destroy = tproxy_tg4_destroy, 253 243 .hooks = 1 << NF_INET_PRE_ROUTING, 254 244 .me = THIS_MODULE, 255 245 }, ··· 263 251 .revision = 1, 264 252 .targetsize = sizeof(struct xt_tproxy_target_info_v1), 265 253 .checkentry = tproxy_tg6_check, 254 + .destroy = tproxy_tg6_destroy, 266 255 .hooks = 1 << NF_INET_PRE_ROUTING, 267 256 .me = THIS_MODULE, 268 257 },
+14
net/netfilter/xt_socket.c
··· 216 216 return 0; 217 217 } 218 218 219 + static void socket_mt_destroy(const struct xt_mtdtor_param *par) 220 + { 221 + if (par->family == NFPROTO_IPV4) 222 + nf_defrag_ipv4_disable(par->net); 223 + else if (par->family == NFPROTO_IPV6) 224 + nf_defrag_ipv4_disable(par->net); 225 + } 226 + 219 227 static struct xt_match socket_mt_reg[] __read_mostly = { 220 228 { 221 229 .name = "socket", ··· 239 231 .revision = 1, 240 232 .family = NFPROTO_IPV4, 241 233 .match = socket_mt4_v1_v2_v3, 234 + .destroy = socket_mt_destroy, 242 235 .checkentry = socket_mt_v1_check, 243 236 .matchsize = sizeof(struct xt_socket_mtinfo1), 244 237 .hooks = (1 << NF_INET_PRE_ROUTING) | ··· 254 245 .match = socket_mt6_v1_v2_v3, 255 246 .checkentry = socket_mt_v1_check, 256 247 .matchsize = sizeof(struct xt_socket_mtinfo1), 248 + .destroy = socket_mt_destroy, 257 249 .hooks = (1 << NF_INET_PRE_ROUTING) | 258 250 (1 << NF_INET_LOCAL_IN), 259 251 .me = THIS_MODULE, ··· 266 256 .family = NFPROTO_IPV4, 267 257 .match = socket_mt4_v1_v2_v3, 268 258 .checkentry = socket_mt_v2_check, 259 + .destroy = socket_mt_destroy, 269 260 .matchsize = sizeof(struct xt_socket_mtinfo1), 270 261 .hooks = (1 << NF_INET_PRE_ROUTING) | 271 262 (1 << NF_INET_LOCAL_IN), ··· 279 268 .family = NFPROTO_IPV6, 280 269 .match = socket_mt6_v1_v2_v3, 281 270 .checkentry = socket_mt_v2_check, 271 + .destroy = socket_mt_destroy, 282 272 .matchsize = sizeof(struct xt_socket_mtinfo1), 283 273 .hooks = (1 << NF_INET_PRE_ROUTING) | 284 274 (1 << NF_INET_LOCAL_IN), ··· 292 280 .family = NFPROTO_IPV4, 293 281 .match = socket_mt4_v1_v2_v3, 294 282 .checkentry = socket_mt_v3_check, 283 + .destroy = socket_mt_destroy, 295 284 .matchsize = sizeof(struct xt_socket_mtinfo1), 296 285 .hooks = (1 << NF_INET_PRE_ROUTING) | 297 286 (1 << NF_INET_LOCAL_IN), ··· 305 292 .family = NFPROTO_IPV6, 306 293 .match = socket_mt6_v1_v2_v3, 307 294 .checkentry = socket_mt_v3_check, 295 + .destroy = socket_mt_destroy, 308 296 .matchsize = sizeof(struct xt_socket_mtinfo1), 309 297 .hooks = (1 << NF_INET_PRE_ROUTING) | 310 298 (1 << NF_INET_LOCAL_IN),