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 29 static DEFINE_MUTEX(defrag6_mutex); 30 nf_ct6_defrag_user(unsigned int hooknum,struct sk_buff * skb)31 static 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 ipv6_defrag(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)52 static 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 76 static 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 defrag6_net_exit(struct net * net)91 static 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 100 static 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 106 static struct pernet_operations defrag6_net_ops = { 107 .exit = defrag6_net_exit, 108 }; 109 nf_defrag_init(void)110 static 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 129 cleanup_frag6: 130 nf_ct_frag6_cleanup(); 131 return ret; 132 133 } 134 nf_defrag_fini(void)135 static 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 nf_defrag_ipv6_enable(struct net * net)142 int 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 } 166 EXPORT_SYMBOL_GPL(nf_defrag_ipv6_enable); 167 nf_defrag_ipv6_disable(struct net * net)168 void 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 } 179 EXPORT_SYMBOL_GPL(nf_defrag_ipv6_disable); 180 181 module_init(nf_defrag_init); 182 module_exit(nf_defrag_fini); 183 184 MODULE_LICENSE("GPL"); 185 MODULE_DESCRIPTION("IPv6 defragmentation support"); 186