linux/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
<<
>>
Prefs
   1/* (C) 1999-2001 Paul `Rusty' Russell
   2 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8
   9#include <linux/types.h>
  10#include <linux/ipv6.h>
  11#include <linux/in6.h>
  12#include <linux/netfilter.h>
  13#include <linux/module.h>
  14#include <linux/skbuff.h>
  15#include <linux/icmp.h>
  16#include <linux/sysctl.h>
  17#include <net/ipv6.h>
  18#include <net/inet_frag.h>
  19
  20#include <linux/netfilter_ipv6.h>
  21#include <linux/netfilter_bridge.h>
  22#if IS_ENABLED(CONFIG_NF_CONNTRACK)
  23#include <net/netfilter/nf_conntrack.h>
  24#include <net/netfilter/nf_conntrack_helper.h>
  25#include <net/netfilter/nf_conntrack_l4proto.h>
  26#include <net/netfilter/nf_conntrack_l3proto.h>
  27#include <net/netfilter/nf_conntrack_core.h>
  28#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
  29#endif
  30#include <net/netfilter/nf_conntrack_zones.h>
  31#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
  32
  33static DEFINE_MUTEX(defrag6_mutex);
  34
  35static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum,
  36                                                struct sk_buff *skb)
  37{
  38        u16 zone_id = NF_CT_DEFAULT_ZONE_ID;
  39#if IS_ENABLED(CONFIG_NF_CONNTRACK)
  40        if (skb_nfct(skb)) {
  41                enum ip_conntrack_info ctinfo;
  42                const struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
  43
  44                zone_id = nf_ct_zone_id(nf_ct_zone(ct), CTINFO2DIR(ctinfo));
  45        }
  46#endif
  47        if (nf_bridge_in_prerouting(skb))
  48                return IP6_DEFRAG_CONNTRACK_BRIDGE_IN + zone_id;
  49
  50        if (hooknum == NF_INET_PRE_ROUTING)
  51                return IP6_DEFRAG_CONNTRACK_IN + zone_id;
  52        else
  53                return IP6_DEFRAG_CONNTRACK_OUT + zone_id;
  54}
  55
  56static unsigned int ipv6_defrag(void *priv,
  57                                struct sk_buff *skb,
  58                                const struct nf_hook_state *state)
  59{
  60        int err;
  61
  62#if IS_ENABLED(CONFIG_NF_CONNTRACK)
  63        /* Previously seen (loopback)?  */
  64        if (skb_nfct(skb) && !nf_ct_is_template((struct nf_conn *)skb_nfct(skb)))
  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
  77static 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
  92static void __net_exit defrag6_net_exit(struct net *net)
  93{
  94        if (net->nf.defrag_ipv6) {
  95                nf_unregister_net_hooks(net, ipv6_defrag_ops,
  96                                        ARRAY_SIZE(ipv6_defrag_ops));
  97                net->nf.defrag_ipv6 = false;
  98        }
  99}
 100
 101static struct pernet_operations defrag6_net_ops = {
 102        .exit = defrag6_net_exit,
 103};
 104
 105static int __init nf_defrag_init(void)
 106{
 107        int ret = 0;
 108
 109        ret = nf_ct_frag6_init();
 110        if (ret < 0) {
 111                pr_err("nf_defrag_ipv6: can't initialize frag6.\n");
 112                return ret;
 113        }
 114        ret = register_pernet_subsys(&defrag6_net_ops);
 115        if (ret < 0) {
 116                pr_err("nf_defrag_ipv6: can't register pernet ops\n");
 117                goto cleanup_frag6;
 118        }
 119        return ret;
 120
 121cleanup_frag6:
 122        nf_ct_frag6_cleanup();
 123        return ret;
 124
 125}
 126
 127static void __exit nf_defrag_fini(void)
 128{
 129        unregister_pernet_subsys(&defrag6_net_ops);
 130        nf_ct_frag6_cleanup();
 131}
 132
 133int nf_defrag_ipv6_enable(struct net *net)
 134{
 135        int err = 0;
 136
 137        might_sleep();
 138
 139        if (net->nf.defrag_ipv6)
 140                return 0;
 141
 142        mutex_lock(&defrag6_mutex);
 143        if (net->nf.defrag_ipv6)
 144                goto out_unlock;
 145
 146        err = nf_register_net_hooks(net, ipv6_defrag_ops,
 147                                    ARRAY_SIZE(ipv6_defrag_ops));
 148        if (err == 0)
 149                net->nf.defrag_ipv6 = true;
 150
 151 out_unlock:
 152        mutex_unlock(&defrag6_mutex);
 153        return err;
 154}
 155EXPORT_SYMBOL_GPL(nf_defrag_ipv6_enable);
 156
 157module_init(nf_defrag_init);
 158module_exit(nf_defrag_fini);
 159
 160MODULE_LICENSE("GPL");
 161