linux/net/netfilter/nf_conntrack_proto_gre.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Connection tracking protocol helper module for GRE.
   4 *
   5 * GRE is a generic encapsulation protocol, which is generally not very
   6 * suited for NAT, as it has no protocol-specific part as port numbers.
   7 *
   8 * It has an optional key field, which may help us distinguishing two
   9 * connections between the same two hosts.
  10 *
  11 * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784
  12 *
  13 * PPTP is built on top of a modified version of GRE, and has a mandatory
  14 * field called "CallID", which serves us for the same purpose as the key
  15 * field in plain GRE.
  16 *
  17 * Documentation about PPTP can be found in RFC 2637
  18 *
  19 * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
  20 *
  21 * Development of this code funded by Astaro AG (http://www.astaro.com/)
  22 *
  23 * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
  24 */
  25
  26#include <linux/module.h>
  27#include <linux/types.h>
  28#include <linux/timer.h>
  29#include <linux/list.h>
  30#include <linux/seq_file.h>
  31#include <linux/in.h>
  32#include <linux/netdevice.h>
  33#include <linux/skbuff.h>
  34#include <linux/slab.h>
  35#include <net/dst.h>
  36#include <net/net_namespace.h>
  37#include <net/netns/generic.h>
  38#include <net/netfilter/nf_conntrack_l4proto.h>
  39#include <net/netfilter/nf_conntrack_helper.h>
  40#include <net/netfilter/nf_conntrack_core.h>
  41#include <net/netfilter/nf_conntrack_timeout.h>
  42#include <linux/netfilter/nf_conntrack_proto_gre.h>
  43#include <linux/netfilter/nf_conntrack_pptp.h>
  44
  45static const unsigned int gre_timeouts[GRE_CT_MAX] = {
  46        [GRE_CT_UNREPLIED]      = 30*HZ,
  47        [GRE_CT_REPLIED]        = 180*HZ,
  48};
  49
  50/* used when expectation is added */
  51static DEFINE_SPINLOCK(keymap_lock);
  52
  53static inline struct nf_gre_net *gre_pernet(struct net *net)
  54{
  55        return &net->ct.nf_ct_proto.gre;
  56}
  57
  58static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km,
  59                                const struct nf_conntrack_tuple *t)
  60{
  61        return km->tuple.src.l3num == t->src.l3num &&
  62               !memcmp(&km->tuple.src.u3, &t->src.u3, sizeof(t->src.u3)) &&
  63               !memcmp(&km->tuple.dst.u3, &t->dst.u3, sizeof(t->dst.u3)) &&
  64               km->tuple.dst.protonum == t->dst.protonum &&
  65               km->tuple.dst.u.all == t->dst.u.all;
  66}
  67
  68/* look up the source key for a given tuple */
  69static __be16 gre_keymap_lookup(struct net *net, struct nf_conntrack_tuple *t)
  70{
  71        struct nf_gre_net *net_gre = gre_pernet(net);
  72        struct nf_ct_gre_keymap *km;
  73        __be16 key = 0;
  74
  75        list_for_each_entry_rcu(km, &net_gre->keymap_list, list) {
  76                if (gre_key_cmpfn(km, t)) {
  77                        key = km->tuple.src.u.gre.key;
  78                        break;
  79                }
  80        }
  81
  82        pr_debug("lookup src key 0x%x for ", key);
  83        nf_ct_dump_tuple(t);
  84
  85        return key;
  86}
  87
  88/* add a single keymap entry, associate with specified master ct */
  89int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
  90                         struct nf_conntrack_tuple *t)
  91{
  92        struct net *net = nf_ct_net(ct);
  93        struct nf_gre_net *net_gre = gre_pernet(net);
  94        struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
  95        struct nf_ct_gre_keymap **kmp, *km;
  96
  97        kmp = &ct_pptp_info->keymap[dir];
  98        if (*kmp) {
  99                /* check whether it's a retransmission */
 100                list_for_each_entry_rcu(km, &net_gre->keymap_list, list) {
 101                        if (gre_key_cmpfn(km, t) && km == *kmp)
 102                                return 0;
 103                }
 104                pr_debug("trying to override keymap_%s for ct %p\n",
 105                         dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct);
 106                return -EEXIST;
 107        }
 108
 109        km = kmalloc(sizeof(*km), GFP_ATOMIC);
 110        if (!km)
 111                return -ENOMEM;
 112        memcpy(&km->tuple, t, sizeof(*t));
 113        *kmp = km;
 114
 115        pr_debug("adding new entry %p: ", km);
 116        nf_ct_dump_tuple(&km->tuple);
 117
 118        spin_lock_bh(&keymap_lock);
 119        list_add_tail(&km->list, &net_gre->keymap_list);
 120        spin_unlock_bh(&keymap_lock);
 121
 122        return 0;
 123}
 124EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add);
 125
 126/* destroy the keymap entries associated with specified master ct */
 127void nf_ct_gre_keymap_destroy(struct nf_conn *ct)
 128{
 129        struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
 130        enum ip_conntrack_dir dir;
 131
 132        pr_debug("entering for ct %p\n", ct);
 133
 134        spin_lock_bh(&keymap_lock);
 135        for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) {
 136                if (ct_pptp_info->keymap[dir]) {
 137                        pr_debug("removing %p from list\n",
 138                                 ct_pptp_info->keymap[dir]);
 139                        list_del_rcu(&ct_pptp_info->keymap[dir]->list);
 140                        kfree_rcu(ct_pptp_info->keymap[dir], rcu);
 141                        ct_pptp_info->keymap[dir] = NULL;
 142                }
 143        }
 144        spin_unlock_bh(&keymap_lock);
 145}
 146EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy);
 147
 148/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
 149
 150/* gre hdr info to tuple */
 151bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
 152                      struct net *net, struct nf_conntrack_tuple *tuple)
 153{
 154        const struct pptp_gre_header *pgrehdr;
 155        struct pptp_gre_header _pgrehdr;
 156        __be16 srckey;
 157        const struct gre_base_hdr *grehdr;
 158        struct gre_base_hdr _grehdr;
 159
 160        /* first only delinearize old RFC1701 GRE header */
 161        grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr);
 162        if (!grehdr || (grehdr->flags & GRE_VERSION) != GRE_VERSION_1) {
 163                /* try to behave like "nf_conntrack_proto_generic" */
 164                tuple->src.u.all = 0;
 165                tuple->dst.u.all = 0;
 166                return true;
 167        }
 168
 169        /* PPTP header is variable length, only need up to the call_id field */
 170        pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr);
 171        if (!pgrehdr)
 172                return true;
 173
 174        if (grehdr->protocol != GRE_PROTO_PPP) {
 175                pr_debug("Unsupported GRE proto(0x%x)\n", ntohs(grehdr->protocol));
 176                return false;
 177        }
 178
 179        tuple->dst.u.gre.key = pgrehdr->call_id;
 180        srckey = gre_keymap_lookup(net, tuple);
 181        tuple->src.u.gre.key = srckey;
 182
 183        return true;
 184}
 185
 186#ifdef CONFIG_NF_CONNTRACK_PROCFS
 187/* print private data for conntrack */
 188static void gre_print_conntrack(struct seq_file *s, struct nf_conn *ct)
 189{
 190        seq_printf(s, "timeout=%u, stream_timeout=%u ",
 191                   (ct->proto.gre.timeout / HZ),
 192                   (ct->proto.gre.stream_timeout / HZ));
 193}
 194#endif
 195
 196static unsigned int *gre_get_timeouts(struct net *net)
 197{
 198        return gre_pernet(net)->timeouts;
 199}
 200
 201/* Returns verdict for packet, and may modify conntrack */
 202int nf_conntrack_gre_packet(struct nf_conn *ct,
 203                            struct sk_buff *skb,
 204                            unsigned int dataoff,
 205                            enum ip_conntrack_info ctinfo,
 206                            const struct nf_hook_state *state)
 207{
 208        if (!nf_ct_is_confirmed(ct)) {
 209                unsigned int *timeouts = nf_ct_timeout_lookup(ct);
 210
 211                if (!timeouts)
 212                        timeouts = gre_get_timeouts(nf_ct_net(ct));
 213
 214                /* initialize to sane value.  Ideally a conntrack helper
 215                 * (e.g. in case of pptp) is increasing them */
 216                ct->proto.gre.stream_timeout = timeouts[GRE_CT_REPLIED];
 217                ct->proto.gre.timeout = timeouts[GRE_CT_UNREPLIED];
 218        }
 219
 220        /* If we've seen traffic both ways, this is a GRE connection.
 221         * Extend timeout. */
 222        if (ct->status & IPS_SEEN_REPLY) {
 223                nf_ct_refresh_acct(ct, ctinfo, skb,
 224                                   ct->proto.gre.stream_timeout);
 225                /* Also, more likely to be important, and not a probe. */
 226                if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
 227                        nf_conntrack_event_cache(IPCT_ASSURED, ct);
 228        } else
 229                nf_ct_refresh_acct(ct, ctinfo, skb,
 230                                   ct->proto.gre.timeout);
 231
 232        return NF_ACCEPT;
 233}
 234
 235#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 236
 237#include <linux/netfilter/nfnetlink.h>
 238#include <linux/netfilter/nfnetlink_cttimeout.h>
 239
 240static int gre_timeout_nlattr_to_obj(struct nlattr *tb[],
 241                                     struct net *net, void *data)
 242{
 243        unsigned int *timeouts = data;
 244        struct nf_gre_net *net_gre = gre_pernet(net);
 245
 246        if (!timeouts)
 247                timeouts = gre_get_timeouts(net);
 248        /* set default timeouts for GRE. */
 249        timeouts[GRE_CT_UNREPLIED] = net_gre->timeouts[GRE_CT_UNREPLIED];
 250        timeouts[GRE_CT_REPLIED] = net_gre->timeouts[GRE_CT_REPLIED];
 251
 252        if (tb[CTA_TIMEOUT_GRE_UNREPLIED]) {
 253                timeouts[GRE_CT_UNREPLIED] =
 254                        ntohl(nla_get_be32(tb[CTA_TIMEOUT_GRE_UNREPLIED])) * HZ;
 255        }
 256        if (tb[CTA_TIMEOUT_GRE_REPLIED]) {
 257                timeouts[GRE_CT_REPLIED] =
 258                        ntohl(nla_get_be32(tb[CTA_TIMEOUT_GRE_REPLIED])) * HZ;
 259        }
 260        return 0;
 261}
 262
 263static int
 264gre_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
 265{
 266        const unsigned int *timeouts = data;
 267
 268        if (nla_put_be32(skb, CTA_TIMEOUT_GRE_UNREPLIED,
 269                         htonl(timeouts[GRE_CT_UNREPLIED] / HZ)) ||
 270            nla_put_be32(skb, CTA_TIMEOUT_GRE_REPLIED,
 271                         htonl(timeouts[GRE_CT_REPLIED] / HZ)))
 272                goto nla_put_failure;
 273        return 0;
 274
 275nla_put_failure:
 276        return -ENOSPC;
 277}
 278
 279static const struct nla_policy
 280gre_timeout_nla_policy[CTA_TIMEOUT_GRE_MAX+1] = {
 281        [CTA_TIMEOUT_GRE_UNREPLIED]     = { .type = NLA_U32 },
 282        [CTA_TIMEOUT_GRE_REPLIED]       = { .type = NLA_U32 },
 283};
 284#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 285
 286void nf_conntrack_gre_init_net(struct net *net)
 287{
 288        struct nf_gre_net *net_gre = gre_pernet(net);
 289        int i;
 290
 291        INIT_LIST_HEAD(&net_gre->keymap_list);
 292        for (i = 0; i < GRE_CT_MAX; i++)
 293                net_gre->timeouts[i] = gre_timeouts[i];
 294}
 295
 296/* protocol helper struct */
 297const struct nf_conntrack_l4proto nf_conntrack_l4proto_gre = {
 298        .l4proto         = IPPROTO_GRE,
 299#ifdef CONFIG_NF_CONNTRACK_PROCFS
 300        .print_conntrack = gre_print_conntrack,
 301#endif
 302#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
 303        .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
 304        .nlattr_tuple_size = nf_ct_port_nlattr_tuple_size,
 305        .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
 306        .nla_policy      = nf_ct_port_nla_policy,
 307#endif
 308#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 309        .ctnl_timeout    = {
 310                .nlattr_to_obj  = gre_timeout_nlattr_to_obj,
 311                .obj_to_nlattr  = gre_timeout_obj_to_nlattr,
 312                .nlattr_max     = CTA_TIMEOUT_GRE_MAX,
 313                .obj_size       = sizeof(unsigned int) * GRE_CT_MAX,
 314                .nla_policy     = gre_timeout_nla_policy,
 315        },
 316#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
 317};
 318