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 static DEFINE_MUTEX(defrag6_mutex); 29 30 static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, 31 struct sk_buff *skb) 32 { 33 u16 zone_id = NF_CT_DEFAULT_ZONE_ID; 34 #if IS_ENABLED(CONFIG_NF_CONNTRACK) 35 if (skb_nfct(skb)) { 36 enum ip_conntrack_info ctinfo; 37 const struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 38 39 zone_id = nf_ct_zone_id(nf_ct_zone(ct), CTINFO2DIR(ctinfo)); 40 } 41 #endif 42 if (nf_bridge_in_prerouting(skb)) 43 return IP6_DEFRAG_CONNTRACK_BRIDGE_IN + zone_id; 44 45 if (hooknum == NF_INET_PRE_ROUTING) 46 return IP6_DEFRAG_CONNTRACK_IN + zone_id; 47 else 48 return IP6_DEFRAG_CONNTRACK_OUT + zone_id; 49 } 50 51 static unsigned int ipv6_defrag(void *priv, 52 struct sk_buff *skb, 53 const struct nf_hook_state *state) 54 { 55 int err; 56 57 #if IS_ENABLED(CONFIG_NF_CONNTRACK) 58 /* Previously seen (loopback)? */ 59 if (skb_nfct(skb) && !nf_ct_is_template((struct nf_conn *)skb_nfct(skb))) 60 return NF_ACCEPT; 61 62 if (skb->_nfct == IP_CT_UNTRACKED) 63 return NF_ACCEPT; 64 #endif 65 66 err = nf_ct_frag6_gather(state->net, skb, 67 nf_ct6_defrag_user(state->hook, skb)); 68 /* queued */ 69 if (err == -EINPROGRESS) 70 return NF_STOLEN; 71 72 return err == 0 ? NF_ACCEPT : NF_DROP; 73 } 74 75 static const struct nf_hook_ops ipv6_defrag_ops[] = { 76 { 77 .hook = ipv6_defrag, 78 .pf = NFPROTO_IPV6, 79 .hooknum = NF_INET_PRE_ROUTING, 80 .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, 81 }, 82 { 83 .hook = ipv6_defrag, 84 .pf = NFPROTO_IPV6, 85 .hooknum = NF_INET_LOCAL_OUT, 86 .priority = NF_IP6_PRI_CONNTRACK_DEFRAG, 87 }, 88 }; 89 90 static void __net_exit defrag6_net_exit(struct net *net) 91 { 92 if (net->nf.defrag_ipv6) { 93 nf_unregister_net_hooks(net, ipv6_defrag_ops, 94 ARRAY_SIZE(ipv6_defrag_ops)); 95 net->nf.defrag_ipv6 = false; 96 } 97 } 98 99 static struct pernet_operations defrag6_net_ops = { 100 .exit = defrag6_net_exit, 101 }; 102 103 static int __init nf_defrag_init(void) 104 { 105 int ret = 0; 106 107 ret = nf_ct_frag6_init(); 108 if (ret < 0) { 109 pr_err("nf_defrag_ipv6: can't initialize frag6.\n"); 110 return ret; 111 } 112 ret = register_pernet_subsys(&defrag6_net_ops); 113 if (ret < 0) { 114 pr_err("nf_defrag_ipv6: can't register pernet ops\n"); 115 goto cleanup_frag6; 116 } 117 return ret; 118 119 cleanup_frag6: 120 nf_ct_frag6_cleanup(); 121 return ret; 122 123 } 124 125 static void __exit nf_defrag_fini(void) 126 { 127 unregister_pernet_subsys(&defrag6_net_ops); 128 nf_ct_frag6_cleanup(); 129 } 130 131 int nf_defrag_ipv6_enable(struct net *net) 132 { 133 int err = 0; 134 135 might_sleep(); 136 137 if (net->nf.defrag_ipv6) 138 return 0; 139 140 mutex_lock(&defrag6_mutex); 141 if (net->nf.defrag_ipv6) 142 goto out_unlock; 143 144 err = nf_register_net_hooks(net, ipv6_defrag_ops, 145 ARRAY_SIZE(ipv6_defrag_ops)); 146 if (err == 0) 147 net->nf.defrag_ipv6 = true; 148 149 out_unlock: 150 mutex_unlock(&defrag6_mutex); 151 return err; 152 } 153 EXPORT_SYMBOL_GPL(nf_defrag_ipv6_enable); 154 155 module_init(nf_defrag_init); 156 module_exit(nf_defrag_fini); 157 158 MODULE_LICENSE("GPL"); 159