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