linux/net/netfilter/nf_conntrack_proto_gre.c
<<
>>
Prefs
   1/*
   2 * ip_conntrack_proto_gre.c - Version 3.0
   3 *
   4 * Connection tracking protocol helper module for GRE.
   5 *
   6 * GRE is a generic encapsulation protocol, which is generally not very
   7 * suited for NAT, as it has no protocol-specific part as port numbers.
   8 *
   9 * It has an optional key field, which may help us distinguishing two
  10 * connections between the same two hosts.
  11 *
  12 * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784
  13 *
  14 * PPTP is built on top of a modified version of GRE, and has a mandatory
  15 * field called "CallID", which serves us for the same purpose as the key
  16 * field in plain GRE.
  17 *
  18 * Documentation about PPTP can be found in RFC 2637
  19 *
  20 * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
  21 *
  22 * Development of this code funded by Astaro AG (http://www.astaro.com/)
  23 *
  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/skbuff.h>
  33
  34#include <net/netfilter/nf_conntrack_l4proto.h>
  35#include <net/netfilter/nf_conntrack_helper.h>
  36#include <net/netfilter/nf_conntrack_core.h>
  37#include <linux/netfilter/nf_conntrack_proto_gre.h>
  38#include <linux/netfilter/nf_conntrack_pptp.h>
  39
  40#define GRE_TIMEOUT             (30 * HZ)
  41#define GRE_STREAM_TIMEOUT      (180 * HZ)
  42
  43static DEFINE_RWLOCK(nf_ct_gre_lock);
  44static LIST_HEAD(gre_keymap_list);
  45
  46void nf_ct_gre_keymap_flush(void)
  47{
  48        struct list_head *pos, *n;
  49
  50        write_lock_bh(&nf_ct_gre_lock);
  51        list_for_each_safe(pos, n, &gre_keymap_list) {
  52                list_del(pos);
  53                kfree(pos);
  54        }
  55        write_unlock_bh(&nf_ct_gre_lock);
  56}
  57EXPORT_SYMBOL(nf_ct_gre_keymap_flush);
  58
  59static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km,
  60                                const struct nf_conntrack_tuple *t)
  61{
  62        return km->tuple.src.l3num == t->src.l3num &&
  63               !memcmp(&km->tuple.src.u3, &t->src.u3, sizeof(t->src.u3)) &&
  64               !memcmp(&km->tuple.dst.u3, &t->dst.u3, sizeof(t->dst.u3)) &&
  65               km->tuple.dst.protonum == t->dst.protonum &&
  66               km->tuple.dst.u.all == t->dst.u.all;
  67}
  68
  69/* look up the source key for a given tuple */
  70static __be16 gre_keymap_lookup(struct nf_conntrack_tuple *t)
  71{
  72        struct nf_ct_gre_keymap *km;
  73        __be16 key = 0;
  74
  75        read_lock_bh(&nf_ct_gre_lock);
  76        list_for_each_entry(km, &gre_keymap_list, list) {
  77                if (gre_key_cmpfn(km, t)) {
  78                        key = km->tuple.src.u.gre.key;
  79                        break;
  80                }
  81        }
  82        read_unlock_bh(&nf_ct_gre_lock);
  83
  84        pr_debug("lookup src key 0x%x for ", key);
  85        NF_CT_DUMP_TUPLE(t);
  86
  87        return key;
  88}
  89
  90/* add a single keymap entry, associate with specified master ct */
  91int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
  92                         struct nf_conntrack_tuple *t)
  93{
  94        struct nf_conn_help *help = nfct_help(ct);
  95        struct nf_ct_gre_keymap **kmp, *km;
  96
  97        kmp = &help->help.ct_pptp_info.keymap[dir];
  98        if (*kmp) {
  99                /* check whether it's a retransmission */
 100                list_for_each_entry(km, &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        write_lock_bh(&nf_ct_gre_lock);
 119        list_add_tail(&km->list, &gre_keymap_list);
 120        write_unlock_bh(&nf_ct_gre_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_conn_help *help = nfct_help(ct);
 130        enum ip_conntrack_dir dir;
 131
 132        pr_debug("entering for ct %p\n", ct);
 133
 134        write_lock_bh(&nf_ct_gre_lock);
 135        for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) {
 136                if (help->help.ct_pptp_info.keymap[dir]) {
 137                        pr_debug("removing %p from list\n",
 138                                 help->help.ct_pptp_info.keymap[dir]);
 139                        list_del(&help->help.ct_pptp_info.keymap[dir]->list);
 140                        kfree(help->help.ct_pptp_info.keymap[dir]);
 141                        help->help.ct_pptp_info.keymap[dir] = NULL;
 142                }
 143        }
 144        write_unlock_bh(&nf_ct_gre_lock);
 145}
 146EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy);
 147
 148/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
 149
 150/* invert gre part of tuple */
 151static int gre_invert_tuple(struct nf_conntrack_tuple *tuple,
 152                            const struct nf_conntrack_tuple *orig)
 153{
 154        tuple->dst.u.gre.key = orig->src.u.gre.key;
 155        tuple->src.u.gre.key = orig->dst.u.gre.key;
 156        return 1;
 157}
 158
 159/* gre hdr info to tuple */
 160static int gre_pkt_to_tuple(const struct sk_buff *skb,
 161                           unsigned int dataoff,
 162                           struct nf_conntrack_tuple *tuple)
 163{
 164        struct gre_hdr_pptp _pgrehdr, *pgrehdr;
 165        __be16 srckey;
 166        struct gre_hdr _grehdr, *grehdr;
 167
 168        /* first only delinearize old RFC1701 GRE header */
 169        grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr);
 170        if (!grehdr || grehdr->version != GRE_VERSION_PPTP) {
 171                /* try to behave like "nf_conntrack_proto_generic" */
 172                tuple->src.u.all = 0;
 173                tuple->dst.u.all = 0;
 174                return 1;
 175        }
 176
 177        /* PPTP header is variable length, only need up to the call_id field */
 178        pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr);
 179        if (!pgrehdr)
 180                return 1;
 181
 182        if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) {
 183                pr_debug("GRE_VERSION_PPTP but unknown proto\n");
 184                return 0;
 185        }
 186
 187        tuple->dst.u.gre.key = pgrehdr->call_id;
 188        srckey = gre_keymap_lookup(tuple);
 189        tuple->src.u.gre.key = srckey;
 190
 191        return 1;
 192}
 193
 194/* print gre part of tuple */
 195static int gre_print_tuple(struct seq_file *s,
 196                           const struct nf_conntrack_tuple *tuple)
 197{
 198        return seq_printf(s, "srckey=0x%x dstkey=0x%x ",
 199                          ntohs(tuple->src.u.gre.key),
 200                          ntohs(tuple->dst.u.gre.key));
 201}
 202
 203/* print private data for conntrack */
 204static int gre_print_conntrack(struct seq_file *s,
 205                               const struct nf_conn *ct)
 206{
 207        return seq_printf(s, "timeout=%u, stream_timeout=%u ",
 208                          (ct->proto.gre.timeout / HZ),
 209                          (ct->proto.gre.stream_timeout / HZ));
 210}
 211
 212/* Returns verdict for packet, and may modify conntrack */
 213static int gre_packet(struct nf_conn *ct,
 214                      const struct sk_buff *skb,
 215                      unsigned int dataoff,
 216                      enum ip_conntrack_info ctinfo,
 217                      int pf,
 218                      unsigned int hooknum)
 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                set_bit(IPS_ASSURED_BIT, &ct->status);
 227                nf_conntrack_event_cache(IPCT_STATUS, skb);
 228        } else
 229                nf_ct_refresh_acct(ct, ctinfo, skb,
 230                                   ct->proto.gre.timeout);
 231
 232        return NF_ACCEPT;
 233}
 234
 235/* Called when a new connection for this protocol found. */
 236static int gre_new(struct nf_conn *ct, const struct sk_buff *skb,
 237                   unsigned int dataoff)
 238{
 239        pr_debug(": ");
 240        NF_CT_DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
 241
 242        /* initialize to sane value.  Ideally a conntrack helper
 243         * (e.g. in case of pptp) is increasing them */
 244        ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT;
 245        ct->proto.gre.timeout = GRE_TIMEOUT;
 246
 247        return 1;
 248}
 249
 250/* Called when a conntrack entry has already been removed from the hashes
 251 * and is about to be deleted from memory */
 252static void gre_destroy(struct nf_conn *ct)
 253{
 254        struct nf_conn *master = ct->master;
 255        pr_debug(" entering\n");
 256
 257        if (!master)
 258                pr_debug("no master !?!\n");
 259        else
 260                nf_ct_gre_keymap_destroy(master);
 261}
 262
 263/* protocol helper struct */
 264static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = {
 265        .l3proto         = AF_INET,
 266        .l4proto         = IPPROTO_GRE,
 267        .name            = "gre",
 268        .pkt_to_tuple    = gre_pkt_to_tuple,
 269        .invert_tuple    = gre_invert_tuple,
 270        .print_tuple     = gre_print_tuple,
 271        .print_conntrack = gre_print_conntrack,
 272        .packet          = gre_packet,
 273        .new             = gre_new,
 274        .destroy         = gre_destroy,
 275        .me              = THIS_MODULE,
 276#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
 277        .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
 278        .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
 279        .nla_policy      = nf_ct_port_nla_policy,
 280#endif
 281};
 282
 283static int __init nf_ct_proto_gre_init(void)
 284{
 285        return nf_conntrack_l4proto_register(&nf_conntrack_l4proto_gre4);
 286}
 287
 288static void nf_ct_proto_gre_fini(void)
 289{
 290        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4);
 291        nf_ct_gre_keymap_flush();
 292}
 293
 294module_init(nf_ct_proto_gre_init);
 295module_exit(nf_ct_proto_gre_fini);
 296
 297MODULE_LICENSE("GPL");
 298