Branch data Line data Source code
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 : 0 : 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 : 0 : if (skb_nfct(skb)) { 36 : : enum ip_conntrack_info ctinfo; 37 : : const struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 38 : : 39 : 0 : zone_id = nf_ct_zone_id(nf_ct_zone(ct), CTINFO2DIR(ctinfo)); 40 : : } 41 : : #endif 42 : 0 : if (nf_bridge_in_prerouting(skb)) 43 : 0 : return IP6_DEFRAG_CONNTRACK_BRIDGE_IN + zone_id; 44 : : 45 : 0 : if (hooknum == NF_INET_PRE_ROUTING) 46 : 0 : return IP6_DEFRAG_CONNTRACK_IN + zone_id; 47 : : else 48 : 0 : return IP6_DEFRAG_CONNTRACK_OUT + zone_id; 49 : : } 50 : : 51 : 0 : 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 : 0 : if (skb_nfct(skb) && !nf_ct_is_template((struct nf_conn *)skb_nfct(skb))) 60 : : return NF_ACCEPT; 61 : : 62 : 0 : if (skb->_nfct == IP_CT_UNTRACKED) 63 : : return NF_ACCEPT; 64 : : #endif 65 : : 66 : 0 : err = nf_ct_frag6_gather(state->net, skb, 67 : 0 : nf_ct6_defrag_user(state->hook, skb)); 68 : : /* queued */ 69 : 0 : if (err == -EINPROGRESS) 70 : : return NF_STOLEN; 71 : : 72 : 0 : 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 : 1 : static void __net_exit defrag6_net_exit(struct net *net) 91 : : { 92 : 1 : if (net->nf.defrag_ipv6) { 93 : 0 : nf_unregister_net_hooks(net, ipv6_defrag_ops, 94 : : ARRAY_SIZE(ipv6_defrag_ops)); 95 : 0 : net->nf.defrag_ipv6 = false; 96 : : } 97 : 1 : } 98 : : 99 : : static struct pernet_operations defrag6_net_ops = { 100 : : .exit = defrag6_net_exit, 101 : : }; 102 : : 103 : 3 : static int __init nf_defrag_init(void) 104 : : { 105 : : int ret = 0; 106 : : 107 : 3 : ret = nf_ct_frag6_init(); 108 : 3 : if (ret < 0) { 109 : 0 : pr_err("nf_defrag_ipv6: can't initialize frag6.\n"); 110 : 0 : return ret; 111 : : } 112 : 3 : ret = register_pernet_subsys(&defrag6_net_ops); 113 : 3 : if (ret < 0) { 114 : 0 : pr_err("nf_defrag_ipv6: can't register pernet ops\n"); 115 : : goto cleanup_frag6; 116 : : } 117 : : return ret; 118 : : 119 : : cleanup_frag6: 120 : 0 : nf_ct_frag6_cleanup(); 121 : 0 : return ret; 122 : : 123 : : } 124 : : 125 : 0 : static void __exit nf_defrag_fini(void) 126 : : { 127 : 0 : unregister_pernet_subsys(&defrag6_net_ops); 128 : 0 : nf_ct_frag6_cleanup(); 129 : 0 : } 130 : : 131 : 0 : int nf_defrag_ipv6_enable(struct net *net) 132 : : { 133 : : int err = 0; 134 : : 135 : 0 : might_sleep(); 136 : : 137 : 0 : if (net->nf.defrag_ipv6) 138 : : return 0; 139 : : 140 : 0 : mutex_lock(&defrag6_mutex); 141 : 0 : if (net->nf.defrag_ipv6) 142 : : goto out_unlock; 143 : : 144 : 0 : err = nf_register_net_hooks(net, ipv6_defrag_ops, 145 : : ARRAY_SIZE(ipv6_defrag_ops)); 146 : 0 : if (err == 0) 147 : 0 : net->nf.defrag_ipv6 = true; 148 : : 149 : : out_unlock: 150 : 0 : mutex_unlock(&defrag6_mutex); 151 : 0 : 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");