1// SPDX-License-Identifier: GPL-2.0-only 2/* (C) 1999-2001 Paul `Rusty' Russell 3 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> 4 */ 5 6#include <linux/types.h> 7#include <linux/ipv6.h> 8#include <linux/in6.h> 9#include <linux/netfilter.h> 10#include <linux/module.h> 11#include <linux/skbuff.h> 12#include <linux/icmp.h> 13#include <linux/rcupdate.h> 14#include <linux/sysctl.h> 15#include <net/ipv6_frag.h> 16 17#include <linux/netfilter_ipv6.h> 18#include <linux/netfilter_bridge.h> 19#if IS_ENABLED(CONFIG_NF_CONNTRACK) 20#include <net/netfilter/nf_conntrack.h> 21#include <net/netfilter/nf_conntrack_helper.h> 22#include <net/netfilter/nf_conntrack_l4proto.h> 23#include <net/netfilter/nf_conntrack_core.h> 24#include <net/netfilter/ipv6/nf_conntrack_ipv6.h> 25#endif 26#include <net/netfilter/nf_conntrack_zones.h> 27#include <net/netfilter/ipv6/nf_defrag_ipv6.h> 28 29static DEFINE_MUTEX(defrag6_mutex); 30 31static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, 32 struct sk_buff *skb) 33{ 34 u16 zone_id = NF_CT_DEFAULT_ZONE_ID; 35#if IS_ENABLED(CONFIG_NF_CONNTRACK) 36 if (skb_nfct(skb)) { 37 enum ip_conntrack_info ctinfo; 38 const struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 39 40 zone_id = nf_ct_zone_id(nf_ct_zone(ct), CTINFO2DIR(ctinfo)); 41 } 42#endif 43 if (nf_bridge_in_prerouting(skb)) 44 return IP6_DEFRAG_CONNTRACK_BRIDGE_IN + zone_id; 45 46 if (hooknum == NF_INET_PRE_ROUTING) 47 return IP6_DEFRAG_CONNTRACK_IN + zone_id; 48 else 49 return IP6_DEFRAG_CONNTRACK_OUT + zone_id; 50} 51 52static unsigned int ipv6_defrag(void *priv, 53 struct sk_buff *skb, 54 const struct nf_hook_state *state) 55{ 56 int err; 57 58#if IS_ENABLED(CONFIG_NF_CONNTRACK) 59 /* Previously seen (loopback)? */ 60 if (skb_nfct(skb) && !nf_ct_is_template((struct nf_conn *)skb_nfct(skb))) 61 return NF_ACCEPT; 62 63 if (skb->_nfct == IP_CT_UNTRACKED) 64 return NF_ACCEPT; 65#endif 66 67 err = nf_ct_frag6_gather(state->net, skb, 68 nf_ct6_defrag_user(state->hook, skb)); 69 /* queued */ 70 if (err == -EINPROGRESS) 71 return NF_STOLEN; 72 73 return err == 0 ? NF_ACCEPT : NF_DROP; 74} 75 76static const struct nf_hook_ops ipv6_defrag_ops[] = { 77 { 78 .hook = ipv6_defrag, 79 .pf = NFPROTO_IPV6, 80 .hooknum = NF_INET_PRE_ROUTING, 81 .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, 82 }, 83 { 84 .hook = ipv6_defrag, 85 .pf = NFPROTO_IPV6, 86 .hooknum = NF_INET_LOCAL_OUT, 87 .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, 88 }, 89}; 90 91static void __net_exit defrag6_net_exit(struct net *net) 92{ 93 if (net->nf.defrag_ipv6_users) { 94 nf_unregister_net_hooks(net, ipv6_defrag_ops, 95 ARRAY_SIZE(ipv6_defrag_ops)); 96 net->nf.defrag_ipv6_users = 0; 97 } 98} 99 100static const struct nf_defrag_hook defrag_hook = { 101 .owner = THIS_MODULE, 102 .enable = nf_defrag_ipv6_enable, 103 .disable = nf_defrag_ipv6_disable, 104}; 105 106static struct pernet_operations defrag6_net_ops = { 107 .exit = defrag6_net_exit, 108}; 109 110static int __init nf_defrag_init(void) 111{ 112 int ret = 0; 113 114 ret = nf_ct_frag6_init(); 115 if (ret < 0) { 116 pr_err("nf_defrag_ipv6: can't initialize frag6.\n"); 117 return ret; 118 } 119 ret = register_pernet_subsys(&defrag6_net_ops); 120 if (ret < 0) { 121 pr_err("nf_defrag_ipv6: can't register pernet ops\n"); 122 goto cleanup_frag6; 123 } 124 125 rcu_assign_pointer(nf_defrag_v6_hook, &defrag_hook); 126 127 return ret; 128 129cleanup_frag6: 130 nf_ct_frag6_cleanup(); 131 return ret; 132 133} 134 135static void __exit nf_defrag_fini(void) 136{ 137 rcu_assign_pointer(nf_defrag_v6_hook, NULL); 138 unregister_pernet_subsys(&defrag6_net_ops); 139 nf_ct_frag6_cleanup(); 140} 141 142int nf_defrag_ipv6_enable(struct net *net) 143{ 144 int err = 0; 145 146 mutex_lock(&defrag6_mutex); 147 if (net->nf.defrag_ipv6_users == UINT_MAX) { 148 err = -EOVERFLOW; 149 goto out_unlock; 150 } 151 152 if (net->nf.defrag_ipv6_users) { 153 net->nf.defrag_ipv6_users++; 154 goto out_unlock; 155 } 156 157 err = nf_register_net_hooks(net, ipv6_defrag_ops, 158 ARRAY_SIZE(ipv6_defrag_ops)); 159 if (err == 0) 160 net->nf.defrag_ipv6_users = 1; 161 162 out_unlock: 163 mutex_unlock(&defrag6_mutex); 164 return err; 165} 166EXPORT_SYMBOL_GPL(nf_defrag_ipv6_enable); 167 168void nf_defrag_ipv6_disable(struct net *net) 169{ 170 mutex_lock(&defrag6_mutex); 171 if (net->nf.defrag_ipv6_users) { 172 net->nf.defrag_ipv6_users--; 173 if (net->nf.defrag_ipv6_users == 0) 174 nf_unregister_net_hooks(net, ipv6_defrag_ops, 175 ARRAY_SIZE(ipv6_defrag_ops)); 176 } 177 mutex_unlock(&defrag6_mutex); 178} 179EXPORT_SYMBOL_GPL(nf_defrag_ipv6_disable); 180 181module_init(nf_defrag_init); 182module_exit(nf_defrag_fini); 183 184MODULE_LICENSE("GPL"); 185MODULE_DESCRIPTION("IPv6 defragmentation support"); 186