Blame view
net/netfilter/nf_conntrack_proto_gre.c
9.71 KB
f09943fef [NETFILTER]: nf_c... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/* * ip_conntrack_proto_gre.c - Version 3.0 * * Connection tracking protocol helper module for GRE. * * GRE is a generic encapsulation protocol, which is generally not very * suited for NAT, as it has no protocol-specific part as port numbers. * * It has an optional key field, which may help us distinguishing two * connections between the same two hosts. * * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 * * PPTP is built on top of a modified version of GRE, and has a mandatory * field called "CallID", which serves us for the same purpose as the key * field in plain GRE. * * Documentation about PPTP can be found in RFC 2637 * * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org> * * Development of this code funded by Astaro AG (http://www.astaro.com/) * */ #include <linux/module.h> #include <linux/types.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/seq_file.h> #include <linux/in.h> |
3bb0d1c00 netfilter: netns ... |
32 |
#include <linux/netdevice.h> |
f09943fef [NETFILTER]: nf_c... |
33 |
#include <linux/skbuff.h> |
5a0e3ad6a include cleanup: ... |
34 |
#include <linux/slab.h> |
3bb0d1c00 netfilter: netns ... |
35 36 37 |
#include <net/dst.h> #include <net/net_namespace.h> #include <net/netns/generic.h> |
f09943fef [NETFILTER]: nf_c... |
38 39 40 41 42 43 44 45 |
#include <net/netfilter/nf_conntrack_l4proto.h> #include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_core.h> #include <linux/netfilter/nf_conntrack_proto_gre.h> #include <linux/netfilter/nf_conntrack_pptp.h> #define GRE_TIMEOUT (30 * HZ) #define GRE_STREAM_TIMEOUT (180 * HZ) |
f99189b18 netns: net_identi... |
46 |
static int proto_gre_net_id __read_mostly; |
3bb0d1c00 netfilter: netns ... |
47 48 49 50 |
struct netns_proto_gre { rwlock_t keymap_lock; struct list_head keymap_list; }; |
f09943fef [NETFILTER]: nf_c... |
51 |
|
3bb0d1c00 netfilter: netns ... |
52 |
void nf_ct_gre_keymap_flush(struct net *net) |
f09943fef [NETFILTER]: nf_c... |
53 |
{ |
3bb0d1c00 netfilter: netns ... |
54 |
struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); |
51807e91a netfilter: nf_con... |
55 |
struct nf_ct_gre_keymap *km, *tmp; |
f09943fef [NETFILTER]: nf_c... |
56 |
|
3bb0d1c00 netfilter: netns ... |
57 58 |
write_lock_bh(&net_gre->keymap_lock); list_for_each_entry_safe(km, tmp, &net_gre->keymap_list, list) { |
51807e91a netfilter: nf_con... |
59 60 |
list_del(&km->list); kfree(km); |
f09943fef [NETFILTER]: nf_c... |
61 |
} |
3bb0d1c00 netfilter: netns ... |
62 |
write_unlock_bh(&net_gre->keymap_lock); |
f09943fef [NETFILTER]: nf_c... |
63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
} EXPORT_SYMBOL(nf_ct_gre_keymap_flush); static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km, const struct nf_conntrack_tuple *t) { return km->tuple.src.l3num == t->src.l3num && !memcmp(&km->tuple.src.u3, &t->src.u3, sizeof(t->src.u3)) && !memcmp(&km->tuple.dst.u3, &t->dst.u3, sizeof(t->dst.u3)) && km->tuple.dst.protonum == t->dst.protonum && km->tuple.dst.u.all == t->dst.u.all; } /* look up the source key for a given tuple */ |
3bb0d1c00 netfilter: netns ... |
77 |
static __be16 gre_keymap_lookup(struct net *net, struct nf_conntrack_tuple *t) |
f09943fef [NETFILTER]: nf_c... |
78 |
{ |
3bb0d1c00 netfilter: netns ... |
79 |
struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); |
f09943fef [NETFILTER]: nf_c... |
80 81 |
struct nf_ct_gre_keymap *km; __be16 key = 0; |
3bb0d1c00 netfilter: netns ... |
82 83 |
read_lock_bh(&net_gre->keymap_lock); list_for_each_entry(km, &net_gre->keymap_list, list) { |
f09943fef [NETFILTER]: nf_c... |
84 85 86 87 88 |
if (gre_key_cmpfn(km, t)) { key = km->tuple.src.u.gre.key; break; } } |
3bb0d1c00 netfilter: netns ... |
89 |
read_unlock_bh(&net_gre->keymap_lock); |
f09943fef [NETFILTER]: nf_c... |
90 |
|
0d53778e8 [NETFILTER]: Conv... |
91 |
pr_debug("lookup src key 0x%x for ", key); |
3c9fba656 [NETFILTER]: nf_c... |
92 |
nf_ct_dump_tuple(t); |
f09943fef [NETFILTER]: nf_c... |
93 94 95 96 97 98 99 100 |
return key; } /* add a single keymap entry, associate with specified master ct */ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, struct nf_conntrack_tuple *t) { |
3bb0d1c00 netfilter: netns ... |
101 102 |
struct net *net = nf_ct_net(ct); struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); |
f09943fef [NETFILTER]: nf_c... |
103 104 |
struct nf_conn_help *help = nfct_help(ct); struct nf_ct_gre_keymap **kmp, *km; |
f09943fef [NETFILTER]: nf_c... |
105 106 107 |
kmp = &help->help.ct_pptp_info.keymap[dir]; if (*kmp) { /* check whether it's a retransmission */ |
3bb0d1c00 netfilter: netns ... |
108 109 |
read_lock_bh(&net_gre->keymap_lock); list_for_each_entry(km, &net_gre->keymap_list, list) { |
887464a41 netfilter: nf_con... |
110 |
if (gre_key_cmpfn(km, t) && km == *kmp) { |
3bb0d1c00 netfilter: netns ... |
111 |
read_unlock_bh(&net_gre->keymap_lock); |
f09943fef [NETFILTER]: nf_c... |
112 |
return 0; |
887464a41 netfilter: nf_con... |
113 |
} |
f09943fef [NETFILTER]: nf_c... |
114 |
} |
3bb0d1c00 netfilter: netns ... |
115 |
read_unlock_bh(&net_gre->keymap_lock); |
0d53778e8 [NETFILTER]: Conv... |
116 117 118 |
pr_debug("trying to override keymap_%s for ct %p ", dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct); |
f09943fef [NETFILTER]: nf_c... |
119 120 121 122 123 124 125 126 |
return -EEXIST; } km = kmalloc(sizeof(*km), GFP_ATOMIC); if (!km) return -ENOMEM; memcpy(&km->tuple, t, sizeof(*t)); *kmp = km; |
0d53778e8 [NETFILTER]: Conv... |
127 |
pr_debug("adding new entry %p: ", km); |
3c9fba656 [NETFILTER]: nf_c... |
128 |
nf_ct_dump_tuple(&km->tuple); |
f09943fef [NETFILTER]: nf_c... |
129 |
|
3bb0d1c00 netfilter: netns ... |
130 131 132 |
write_lock_bh(&net_gre->keymap_lock); list_add_tail(&km->list, &net_gre->keymap_list); write_unlock_bh(&net_gre->keymap_lock); |
f09943fef [NETFILTER]: nf_c... |
133 134 135 136 137 138 139 140 |
return 0; } EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add); /* destroy the keymap entries associated with specified master ct */ void nf_ct_gre_keymap_destroy(struct nf_conn *ct) { |
3bb0d1c00 netfilter: netns ... |
141 142 |
struct net *net = nf_ct_net(ct); struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); |
f09943fef [NETFILTER]: nf_c... |
143 144 |
struct nf_conn_help *help = nfct_help(ct); enum ip_conntrack_dir dir; |
0d53778e8 [NETFILTER]: Conv... |
145 146 |
pr_debug("entering for ct %p ", ct); |
f09943fef [NETFILTER]: nf_c... |
147 |
|
3bb0d1c00 netfilter: netns ... |
148 |
write_lock_bh(&net_gre->keymap_lock); |
f09943fef [NETFILTER]: nf_c... |
149 150 |
for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) { if (help->help.ct_pptp_info.keymap[dir]) { |
0d53778e8 [NETFILTER]: Conv... |
151 152 153 |
pr_debug("removing %p from list ", help->help.ct_pptp_info.keymap[dir]); |
f09943fef [NETFILTER]: nf_c... |
154 155 156 157 158 |
list_del(&help->help.ct_pptp_info.keymap[dir]->list); kfree(help->help.ct_pptp_info.keymap[dir]); help->help.ct_pptp_info.keymap[dir] = NULL; } } |
3bb0d1c00 netfilter: netns ... |
159 |
write_unlock_bh(&net_gre->keymap_lock); |
f09943fef [NETFILTER]: nf_c... |
160 161 162 163 164 165 |
} EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy); /* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */ /* invert gre part of tuple */ |
09f263cd3 [NETFILTER]: nf_c... |
166 167 |
static bool gre_invert_tuple(struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *orig) |
f09943fef [NETFILTER]: nf_c... |
168 169 170 |
{ tuple->dst.u.gre.key = orig->src.u.gre.key; tuple->src.u.gre.key = orig->dst.u.gre.key; |
09f263cd3 [NETFILTER]: nf_c... |
171 |
return true; |
f09943fef [NETFILTER]: nf_c... |
172 173 174 |
} /* gre hdr info to tuple */ |
09f263cd3 [NETFILTER]: nf_c... |
175 176 |
static bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, struct nf_conntrack_tuple *tuple) |
f09943fef [NETFILTER]: nf_c... |
177 |
{ |
adf30907d net: skb->dst acc... |
178 |
struct net *net = dev_net(skb->dev ? skb->dev : skb_dst(skb)->dev); |
dc35dc5a4 [NETFILTER]: nf_{... |
179 180 |
const struct gre_hdr_pptp *pgrehdr; struct gre_hdr_pptp _pgrehdr; |
f09943fef [NETFILTER]: nf_c... |
181 |
__be16 srckey; |
dc35dc5a4 [NETFILTER]: nf_{... |
182 183 |
const struct gre_hdr *grehdr; struct gre_hdr _grehdr; |
f09943fef [NETFILTER]: nf_c... |
184 185 186 187 188 189 190 |
/* first only delinearize old RFC1701 GRE header */ grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr); if (!grehdr || grehdr->version != GRE_VERSION_PPTP) { /* try to behave like "nf_conntrack_proto_generic" */ tuple->src.u.all = 0; tuple->dst.u.all = 0; |
09f263cd3 [NETFILTER]: nf_c... |
191 |
return true; |
f09943fef [NETFILTER]: nf_c... |
192 193 194 195 196 |
} /* PPTP header is variable length, only need up to the call_id field */ pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr); if (!pgrehdr) |
09f263cd3 [NETFILTER]: nf_c... |
197 |
return true; |
f09943fef [NETFILTER]: nf_c... |
198 199 |
if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) { |
0d53778e8 [NETFILTER]: Conv... |
200 201 |
pr_debug("GRE_VERSION_PPTP but unknown proto "); |
09f263cd3 [NETFILTER]: nf_c... |
202 |
return false; |
f09943fef [NETFILTER]: nf_c... |
203 204 205 |
} tuple->dst.u.gre.key = pgrehdr->call_id; |
3bb0d1c00 netfilter: netns ... |
206 |
srckey = gre_keymap_lookup(net, tuple); |
f09943fef [NETFILTER]: nf_c... |
207 |
tuple->src.u.gre.key = srckey; |
09f263cd3 [NETFILTER]: nf_c... |
208 |
return true; |
f09943fef [NETFILTER]: nf_c... |
209 210 211 212 213 214 215 216 217 218 219 220 |
} /* print gre part of tuple */ static int gre_print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple) { return seq_printf(s, "srckey=0x%x dstkey=0x%x ", ntohs(tuple->src.u.gre.key), ntohs(tuple->dst.u.gre.key)); } /* print private data for conntrack */ |
440f0d588 netfilter: nf_con... |
221 |
static int gre_print_conntrack(struct seq_file *s, struct nf_conn *ct) |
f09943fef [NETFILTER]: nf_c... |
222 223 224 225 226 227 228 229 230 231 232 |
{ return seq_printf(s, "timeout=%u, stream_timeout=%u ", (ct->proto.gre.timeout / HZ), (ct->proto.gre.stream_timeout / HZ)); } /* Returns verdict for packet, and may modify conntrack */ static int gre_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, |
76108cea0 netfilter: Use un... |
233 |
u_int8_t pf, |
f09943fef [NETFILTER]: nf_c... |
234 235 236 237 238 239 240 241 |
unsigned int hooknum) { /* If we've seen traffic both ways, this is a GRE connection. * Extend timeout. */ if (ct->status & IPS_SEEN_REPLY) { nf_ct_refresh_acct(ct, ctinfo, skb, ct->proto.gre.stream_timeout); /* Also, more likely to be important, and not a probe. */ |
98d9ae841 netfilter: nf_con... |
242 243 |
if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) nf_conntrack_event_cache(IPCT_ASSURED, ct); |
f09943fef [NETFILTER]: nf_c... |
244 245 246 247 248 249 250 251 |
} else nf_ct_refresh_acct(ct, ctinfo, skb, ct->proto.gre.timeout); return NF_ACCEPT; } /* Called when a new connection for this protocol found. */ |
09f263cd3 [NETFILTER]: nf_c... |
252 253 |
static bool gre_new(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff) |
f09943fef [NETFILTER]: nf_c... |
254 |
{ |
0d53778e8 [NETFILTER]: Conv... |
255 |
pr_debug(": "); |
3c9fba656 [NETFILTER]: nf_c... |
256 |
nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); |
f09943fef [NETFILTER]: nf_c... |
257 258 259 260 261 |
/* initialize to sane value. Ideally a conntrack helper * (e.g. in case of pptp) is increasing them */ ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT; ct->proto.gre.timeout = GRE_TIMEOUT; |
09f263cd3 [NETFILTER]: nf_c... |
262 |
return true; |
f09943fef [NETFILTER]: nf_c... |
263 264 265 266 267 268 269 |
} /* Called when a conntrack entry has already been removed from the hashes * and is about to be deleted from memory */ static void gre_destroy(struct nf_conn *ct) { struct nf_conn *master = ct->master; |
0d53778e8 [NETFILTER]: Conv... |
270 271 |
pr_debug(" entering "); |
f09943fef [NETFILTER]: nf_c... |
272 273 |
if (!master) |
0d53778e8 [NETFILTER]: Conv... |
274 275 |
pr_debug("no master !?! "); |
f09943fef [NETFILTER]: nf_c... |
276 277 278 279 280 |
else nf_ct_gre_keymap_destroy(master); } /* protocol helper struct */ |
61075af51 [NETFILTER]: nf_c... |
281 |
static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = { |
f09943fef [NETFILTER]: nf_c... |
282 283 284 285 286 287 288 289 290 291 292 |
.l3proto = AF_INET, .l4proto = IPPROTO_GRE, .name = "gre", .pkt_to_tuple = gre_pkt_to_tuple, .invert_tuple = gre_invert_tuple, .print_tuple = gre_print_tuple, .print_conntrack = gre_print_conntrack, .packet = gre_packet, .new = gre_new, .destroy = gre_destroy, .me = THIS_MODULE, |
c0cd11566 net:netfilter: us... |
293 |
#if IS_ENABLED(CONFIG_NF_CT_NETLINK) |
fdf708322 [NETFILTER]: nfne... |
294 |
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, |
a400c30ed netfilter: nf_con... |
295 |
.nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, |
fdf708322 [NETFILTER]: nfne... |
296 |
.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, |
f73e924cd [NETFILTER]: ctne... |
297 |
.nla_policy = nf_ct_port_nla_policy, |
f09943fef [NETFILTER]: nf_c... |
298 299 |
#endif }; |
3bb0d1c00 netfilter: netns ... |
300 301 |
static int proto_gre_net_init(struct net *net) { |
e8d028859 net: Simplify con... |
302 |
struct netns_proto_gre *net_gre = net_generic(net, proto_gre_net_id); |
3bb0d1c00 netfilter: netns ... |
303 |
|
3bb0d1c00 netfilter: netns ... |
304 305 |
rwlock_init(&net_gre->keymap_lock); INIT_LIST_HEAD(&net_gre->keymap_list); |
e8d028859 net: Simplify con... |
306 |
return 0; |
3bb0d1c00 netfilter: netns ... |
307 308 309 310 |
} static void proto_gre_net_exit(struct net *net) { |
3bb0d1c00 netfilter: netns ... |
311 |
nf_ct_gre_keymap_flush(net); |
3bb0d1c00 netfilter: netns ... |
312 313 314 315 316 |
} static struct pernet_operations proto_gre_net_ops = { .init = proto_gre_net_init, .exit = proto_gre_net_exit, |
e8d028859 net: Simplify con... |
317 318 |
.id = &proto_gre_net_id, .size = sizeof(struct netns_proto_gre), |
3bb0d1c00 netfilter: netns ... |
319 |
}; |
f09943fef [NETFILTER]: nf_c... |
320 321 |
static int __init nf_ct_proto_gre_init(void) { |
3bb0d1c00 netfilter: netns ... |
322 323 324 325 326 |
int rv; rv = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_gre4); if (rv < 0) return rv; |
e8d028859 net: Simplify con... |
327 |
rv = register_pernet_subsys(&proto_gre_net_ops); |
3bb0d1c00 netfilter: netns ... |
328 329 330 |
if (rv < 0) nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4); return rv; |
f09943fef [NETFILTER]: nf_c... |
331 |
} |
56bc0f960 netfilter: nf_con... |
332 |
static void __exit nf_ct_proto_gre_fini(void) |
f09943fef [NETFILTER]: nf_c... |
333 334 |
{ nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4); |
e8d028859 net: Simplify con... |
335 |
unregister_pernet_subsys(&proto_gre_net_ops); |
f09943fef [NETFILTER]: nf_c... |
336 337 338 339 340 341 |
} module_init(nf_ct_proto_gre_init); module_exit(nf_ct_proto_gre_fini); MODULE_LICENSE("GPL"); |