1/* 2 * ip_conntrack_proto_gre.c - Version 1.11 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-2002 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/config.h> 27#include <linux/module.h> 28#include <linux/types.h> 29#include <linux/timer.h> 30#include <linux/netfilter.h> 31#include <linux/ip.h> 32#include <linux/in.h> 33#include <linux/list.h> 34 35#include <linux/netfilter_ipv4/lockhelp.h> 36 37DECLARE_RWLOCK(ip_ct_gre_lock); 38#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_ct_gre_lock) 39#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_ct_gre_lock) 40 41#include <linux/netfilter_ipv4/listhelp.h> 42#include <linux/netfilter_ipv4/ip_conntrack_protocol.h> 43#include <linux/netfilter_ipv4/ip_conntrack_helper.h> 44#include <linux/netfilter_ipv4/ip_conntrack_core.h> 45 46#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> 47#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> 48 49MODULE_LICENSE("GPL"); 50MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); 51MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE"); 52 53/* shamelessly stolen from ip_conntrack_proto_udp.c */ 54#define GRE_TIMEOUT (30*HZ) 55#define GRE_STREAM_TIMEOUT (180*HZ) 56 57#define DEBUGP(x, args...) 58#define DUMP_TUPLE_GRE(x) 59 60/* GRE KEYMAP HANDLING FUNCTIONS */ 61static LIST_HEAD(gre_keymap_list); 62 63static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km, 64 const struct ip_conntrack_tuple *t) 65{ 66 return ((km->tuple.src.ip == t->src.ip) && 67 (km->tuple.dst.ip == t->dst.ip) && 68 (km->tuple.dst.protonum == t->dst.protonum) && 69 (km->tuple.dst.u.all == t->dst.u.all)); 70} 71 72/* look up the source key for a given tuple */ 73static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t) 74{ 75 struct ip_ct_gre_keymap *km; 76 u_int32_t key; 77 78 READ_LOCK(&ip_ct_gre_lock); 79 km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn, 80 struct ip_ct_gre_keymap *, t); 81 if (!km) { 82 READ_UNLOCK(&ip_ct_gre_lock); 83 return 0; 84 } 85 86 key = km->tuple.src.u.gre.key; 87 READ_UNLOCK(&ip_ct_gre_lock); 88 89 return key; 90} 91 92/* add a single keymap entry, associate with specified expect */ 93int ip_ct_gre_keymap_add(struct ip_conntrack_expect *exp, 94 struct ip_conntrack_tuple *t, int reply) 95{ 96 struct ip_ct_gre_keymap *km; 97 98 km = kmalloc(sizeof(*km), GFP_ATOMIC); 99 if (!km) 100 return -1; 101 102 /* initializing list head should be sufficient */ 103 memset(km, 0, sizeof(*km)); 104 105 memcpy(&km->tuple, t, sizeof(*t)); 106 km->master = exp; 107 108 if (!reply) 109 exp->proto.gre.keymap_orig = km; 110 else 111 exp->proto.gre.keymap_reply = km; 112 113 DEBUGP("adding new entry %p: ", km); 114 DUMP_TUPLE_GRE(&km->tuple); 115 116 WRITE_LOCK(&ip_ct_gre_lock); 117 list_append(&gre_keymap_list, km); 118 WRITE_UNLOCK(&ip_ct_gre_lock); 119 120 return 0; 121} 122 123/* change the tuple of a keymap entry (used by nat helper) */ 124void ip_ct_gre_keymap_change(struct ip_ct_gre_keymap *km, 125 struct ip_conntrack_tuple *t) 126{ 127 DEBUGP("changing entry %p to: ", km); 128 DUMP_TUPLE_GRE(t); 129 130 WRITE_LOCK(&ip_ct_gre_lock); 131 memcpy(&km->tuple, t, sizeof(km->tuple)); 132 WRITE_UNLOCK(&ip_ct_gre_lock); 133} 134 135 136/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */ 137 138/* invert gre part of tuple */ 139static int gre_invert_tuple(struct ip_conntrack_tuple *tuple, 140 const struct ip_conntrack_tuple *orig) 141{ 142 tuple->dst.u.gre.protocol = orig->dst.u.gre.protocol; 143 tuple->dst.u.gre.version = orig->dst.u.gre.version; 144 145 tuple->dst.u.gre.key = orig->src.u.gre.key; 146 tuple->src.u.gre.key = orig->dst.u.gre.key; 147 148 return 1; 149} 150 151/* gre hdr info to tuple */ 152static int gre_pkt_to_tuple(const void *datah, size_t datalen, 153 struct ip_conntrack_tuple *tuple) 154{ 155 struct gre_hdr *grehdr = (struct gre_hdr *) datah; 156 struct gre_hdr_pptp *pgrehdr = (struct gre_hdr_pptp *) datah; 157 u_int32_t srckey; 158 159 /* core guarantees 8 protocol bytes, no need for size check */ 160 161 tuple->dst.u.gre.version = grehdr->version; 162 tuple->dst.u.gre.protocol = grehdr->protocol; 163 164 switch (grehdr->version) { 165 case GRE_VERSION_1701: 166 if (!grehdr->key) { 167 DEBUGP("Can't track GRE without key\n"); 168 return 0; 169 } 170 tuple->dst.u.gre.key = *(gre_key(grehdr)); 171 break; 172 173 case GRE_VERSION_PPTP: 174 if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) { 175 DEBUGP("GRE_VERSION_PPTP but unknown proto\n"); 176 return 0; 177 } 178 tuple->dst.u.gre.key = htonl(ntohs(pgrehdr->call_id)); 179 break; 180 181 default: 182 printk(KERN_WARNING "unknown GRE version %hu\n", 183 tuple->dst.u.gre.version); 184 return 0; 185 } 186 187 srckey = gre_keymap_lookup(tuple); 188 189 tuple->src.u.gre.key = srckey; 190 191 return 1; 192} 193 194/* print gre part of tuple */ 195static unsigned int gre_print_tuple(char *buffer, 196 const struct ip_conntrack_tuple *tuple) 197{ 198 return sprintf(buffer, "version=%d protocol=0x%04x srckey=0x%x dstkey=0x%x ", 199 tuple->dst.u.gre.version, 200 ntohs(tuple->dst.u.gre.protocol), 201 ntohl(tuple->src.u.gre.key), 202 ntohl(tuple->dst.u.gre.key)); 203} 204 205/* print private data for conntrack */ 206static unsigned int gre_print_conntrack(char *buffer, 207 const struct ip_conntrack *ct) 208{ 209 return sprintf(buffer, "timeout=%u, stream_timeout=%u ", 210 (ct->proto.gre.timeout / HZ), 211 (ct->proto.gre.stream_timeout / HZ)); 212} 213 214/* Returns verdict for packet, and may modify conntrack */ 215static int gre_packet(struct ip_conntrack *ct, 216 struct iphdr *iph, size_t len, 217 enum ip_conntrack_info conntrackinfo) 218{ 219 /* If we've seen traffic both ways, this is a GRE connection. 220 * Extend timeout. */ 221 if (ct->status & IPS_SEEN_REPLY) { 222 ip_ct_refresh(ct, ct->proto.gre.stream_timeout); 223 /* Also, more likely to be important, and not a probe. */ 224 set_bit(IPS_ASSURED_BIT, &ct->status); 225 } else 226 ip_ct_refresh(ct, ct->proto.gre.timeout); 227 228 return NF_ACCEPT; 229} 230 231/* Called when a new connection for this protocol found. */ 232static int gre_new(struct ip_conntrack *ct, 233 struct iphdr *iph, size_t len) 234{ 235 DEBUGP(": "); 236 DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); 237 238 /* initialize to sane value. Ideally a conntrack helper 239 * (e.g. in case of pptp) is increasing them */ 240 ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT; 241 ct->proto.gre.timeout = GRE_TIMEOUT; 242 243 return 1; 244} 245 246/* Called when a conntrack entry has already been removed from the hashes 247 * and is about to be deleted from memory */ 248static void gre_destroy(struct ip_conntrack *ct) 249{ 250 struct ip_conntrack_expect *master = ct->master; 251 252 DEBUGP(" entering\n"); 253 254 if (!master) { 255 DEBUGP("no master exp for ct %p\n", ct); 256 return; 257 } 258 259 WRITE_LOCK(&ip_ct_gre_lock); 260 if (master->proto.gre.keymap_orig) { 261 DEBUGP("removing %p from list\n", master->proto.gre.keymap_orig); 262 list_del(&master->proto.gre.keymap_orig->list); 263 kfree(master->proto.gre.keymap_orig); 264 } 265 if (master->proto.gre.keymap_reply) { 266 DEBUGP("removing %p from list\n", master->proto.gre.keymap_reply); 267 list_del(&master->proto.gre.keymap_reply->list); 268 kfree(master->proto.gre.keymap_reply); 269 } 270 WRITE_UNLOCK(&ip_ct_gre_lock); 271} 272 273/* protocol helper struct */ 274static struct ip_conntrack_protocol gre = { { NULL, NULL }, IPPROTO_GRE, 275 "gre", 276 gre_pkt_to_tuple, 277 gre_invert_tuple, 278 gre_print_tuple, 279 gre_print_conntrack, 280 gre_packet, 281 gre_new, 282 gre_destroy, 283 NULL, 284 THIS_MODULE }; 285 286/* ip_conntrack_proto_gre initialization */ 287static int __init init(void) 288{ 289 int retcode; 290 291 if ((retcode = ip_conntrack_protocol_register(&gre))) { 292 printk(KERN_ERR "Unable to register conntrack protocol " 293 "helper for gre: %d\n", retcode); 294 return -EIO; 295 } 296 297 return 0; 298} 299 300static void __exit fini(void) 301{ 302 struct list_head *pos, *n; 303 304 /* delete all keymap entries */ 305 WRITE_LOCK(&ip_ct_gre_lock); 306 list_for_each_safe(pos, n, &gre_keymap_list) { 307 DEBUGP("deleting keymap %p\n", pos); 308 list_del(pos); 309 kfree(pos); 310 } 311 WRITE_UNLOCK(&ip_ct_gre_lock); 312 313 ip_conntrack_protocol_unregister(&gre); 314} 315 316EXPORT_SYMBOL(ip_ct_gre_keymap_add); 317EXPORT_SYMBOL(ip_ct_gre_keymap_change); 318 319module_init(init); 320module_exit(fini); 321