Blame view
net/netfilter/nf_conntrack_pptp.c
18.9 KB
09c434b8a treewide: Add SPD... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
f09943fef [NETFILTER]: nf_c... |
2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* * Connection tracking support for PPTP (Point to Point Tunneling Protocol). * PPTP is a a protocol for creating virtual private networks. * It is a specification defined by Microsoft and some vendors * working with Microsoft. PPTP is built on top of a modified * version of the Internet Generic Routing Encapsulation Protocol. * GRE is defined in RFC 1701 and RFC 1702. Documentation of * 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/) * |
f229f6ce4 netfilter: add my... |
15 16 |
* (C) 2006-2012 Patrick McHardy <kaber@trash.net> * |
f09943fef [NETFILTER]: nf_c... |
17 18 19 |
* Limitations: * - We blindly assume that control connections are always * established in PNS->PAC direction. This is a violation |
38124328f netfilter: nf_ct_... |
20 |
* of RFC 2637 |
f09943fef [NETFILTER]: nf_c... |
21 22 23 24 25 26 27 28 29 30 31 32 33 |
* - We can only support one single call within each session * TODO: * - testing of incoming PPTP calls */ #include <linux/module.h> #include <linux/skbuff.h> #include <linux/in.h> #include <linux/tcp.h> #include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_core.h> #include <net/netfilter/nf_conntrack_helper.h> |
5d0aa2ccd netfilter: nf_con... |
34 |
#include <net/netfilter/nf_conntrack_zones.h> |
f09943fef [NETFILTER]: nf_c... |
35 36 37 38 39 40 41 42 43 |
#include <linux/netfilter/nf_conntrack_proto_gre.h> #include <linux/netfilter/nf_conntrack_pptp.h> #define NF_CT_PPTP_VERSION "3.1" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP"); MODULE_ALIAS("ip_conntrack_pptp"); |
4dc06f963 netfilter: nf_con... |
44 |
MODULE_ALIAS_NFCT_HELPER("pptp"); |
f09943fef [NETFILTER]: nf_c... |
45 46 47 48 |
static DEFINE_SPINLOCK(nf_pptp_lock); int |
3db05fea5 [NETFILTER]: Repl... |
49 |
(*nf_nat_pptp_hook_outbound)(struct sk_buff *skb, |
f09943fef [NETFILTER]: nf_c... |
50 |
struct nf_conn *ct, enum ip_conntrack_info ctinfo, |
051966c0c netfilter: nf_nat... |
51 |
unsigned int protoff, struct PptpControlHeader *ctlh, |
f09943fef [NETFILTER]: nf_c... |
52 53 54 55 |
union pptp_ctrl_union *pptpReq) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_outbound); int |
3db05fea5 [NETFILTER]: Repl... |
56 |
(*nf_nat_pptp_hook_inbound)(struct sk_buff *skb, |
f09943fef [NETFILTER]: nf_c... |
57 |
struct nf_conn *ct, enum ip_conntrack_info ctinfo, |
051966c0c netfilter: nf_nat... |
58 |
unsigned int protoff, struct PptpControlHeader *ctlh, |
f09943fef [NETFILTER]: nf_c... |
59 60 61 62 63 64 65 66 67 68 69 70 71 |
union pptp_ctrl_union *pptpReq) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_inbound); void (*nf_nat_pptp_hook_exp_gre)(struct nf_conntrack_expect *expect_orig, struct nf_conntrack_expect *expect_reply) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_exp_gre); void (*nf_nat_pptp_hook_expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp) __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn); |
e9d376f0f dynamic debug: co... |
72 |
#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG) |
f09943fef [NETFILTER]: nf_c... |
73 |
/* PptpControlMessageType names */ |
9f57771d0 netfilter: nf_con... |
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
static const char *const pptp_msg_name_array[PPTP_MSG_MAX + 1] = { [0] = "UNKNOWN_MESSAGE", [PPTP_START_SESSION_REQUEST] = "START_SESSION_REQUEST", [PPTP_START_SESSION_REPLY] = "START_SESSION_REPLY", [PPTP_STOP_SESSION_REQUEST] = "STOP_SESSION_REQUEST", [PPTP_STOP_SESSION_REPLY] = "STOP_SESSION_REPLY", [PPTP_ECHO_REQUEST] = "ECHO_REQUEST", [PPTP_ECHO_REPLY] = "ECHO_REPLY", [PPTP_OUT_CALL_REQUEST] = "OUT_CALL_REQUEST", [PPTP_OUT_CALL_REPLY] = "OUT_CALL_REPLY", [PPTP_IN_CALL_REQUEST] = "IN_CALL_REQUEST", [PPTP_IN_CALL_REPLY] = "IN_CALL_REPLY", [PPTP_IN_CALL_CONNECT] = "IN_CALL_CONNECT", [PPTP_CALL_CLEAR_REQUEST] = "CALL_CLEAR_REQUEST", [PPTP_CALL_DISCONNECT_NOTIFY] = "CALL_DISCONNECT_NOTIFY", [PPTP_WAN_ERROR_NOTIFY] = "WAN_ERROR_NOTIFY", [PPTP_SET_LINK_INFO] = "SET_LINK_INFO" |
f09943fef [NETFILTER]: nf_c... |
91 |
}; |
9f57771d0 netfilter: nf_con... |
92 |
|
3f57fa847 netfilter: nf_con... |
93 |
const char *pptp_msg_name(u_int16_t msg) |
9f57771d0 netfilter: nf_con... |
94 95 96 97 98 99 |
{ if (msg > PPTP_MSG_MAX) return pptp_msg_name_array[0]; return pptp_msg_name_array[msg]; } |
f09943fef [NETFILTER]: nf_c... |
100 |
EXPORT_SYMBOL(pptp_msg_name); |
f09943fef [NETFILTER]: nf_c... |
101 102 103 104 105 106 107 108 109 110 111 112 |
#endif #define SECS *HZ #define MINS * 60 SECS #define HOURS * 60 MINS #define PPTP_GRE_TIMEOUT (10 MINS) #define PPTP_GRE_STREAM_TIMEOUT (5 HOURS) static void pptp_expectfn(struct nf_conn *ct, struct nf_conntrack_expect *exp) { |
0e6e75af9 netfilter: netns ... |
113 |
struct net *net = nf_ct_net(ct); |
f09943fef [NETFILTER]: nf_c... |
114 |
typeof(nf_nat_pptp_hook_expectfn) nf_nat_pptp_expectfn; |
0d53778e8 [NETFILTER]: Conv... |
115 116 |
pr_debug("increasing timeouts "); |
f09943fef [NETFILTER]: nf_c... |
117 118 119 120 121 122 123 |
/* increase timeout of GRE data channel conntrack entry */ ct->proto.gre.timeout = PPTP_GRE_TIMEOUT; ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT; /* Can you see how rusty this code is, compared with the pre-2.6.11 * one? That's what happened to my shiny newnat of 2002 ;( -HW */ |
f09943fef [NETFILTER]: nf_c... |
124 |
nf_nat_pptp_expectfn = rcu_dereference(nf_nat_pptp_hook_expectfn); |
7399072a7 [NETFILTER]: nf_c... |
125 |
if (nf_nat_pptp_expectfn && ct->master->status & IPS_NAT_MASK) |
f09943fef [NETFILTER]: nf_c... |
126 127 128 129 130 131 |
nf_nat_pptp_expectfn(ct, exp); else { struct nf_conntrack_tuple inv_t; struct nf_conntrack_expect *exp_other; /* obviously this tuple inversion only works until you do NAT */ |
303e0c558 netfilter: conntr... |
132 |
nf_ct_invert_tuple(&inv_t, &exp->tuple); |
0d53778e8 [NETFILTER]: Conv... |
133 |
pr_debug("trying to unexpect other dir: "); |
3c9fba656 [NETFILTER]: nf_c... |
134 |
nf_ct_dump_tuple(&inv_t); |
f09943fef [NETFILTER]: nf_c... |
135 |
|
5d0aa2ccd netfilter: nf_con... |
136 |
exp_other = nf_ct_expect_find_get(net, nf_ct_zone(ct), &inv_t); |
f09943fef [NETFILTER]: nf_c... |
137 138 |
if (exp_other) { /* delete other expectation. */ |
0d53778e8 [NETFILTER]: Conv... |
139 140 |
pr_debug("found "); |
6823645d6 [NETFILTER]: nf_c... |
141 142 |
nf_ct_unexpect_related(exp_other); nf_ct_expect_put(exp_other); |
f09943fef [NETFILTER]: nf_c... |
143 |
} else { |
0d53778e8 [NETFILTER]: Conv... |
144 145 |
pr_debug("not found "); |
f09943fef [NETFILTER]: nf_c... |
146 147 |
} } |
f09943fef [NETFILTER]: nf_c... |
148 |
} |
5d0aa2ccd netfilter: nf_con... |
149 |
static int destroy_sibling_or_exp(struct net *net, struct nf_conn *ct, |
0e6e75af9 netfilter: netns ... |
150 |
const struct nf_conntrack_tuple *t) |
f09943fef [NETFILTER]: nf_c... |
151 |
{ |
9ddd0ed05 [NETFILTER]: nf_{... |
152 |
const struct nf_conntrack_tuple_hash *h; |
308ac9143 netfilter: nf_con... |
153 |
const struct nf_conntrack_zone *zone; |
f09943fef [NETFILTER]: nf_c... |
154 155 |
struct nf_conntrack_expect *exp; struct nf_conn *sibling; |
0d53778e8 [NETFILTER]: Conv... |
156 |
pr_debug("trying to timeout ct or exp for tuple "); |
3c9fba656 [NETFILTER]: nf_c... |
157 |
nf_ct_dump_tuple(t); |
f09943fef [NETFILTER]: nf_c... |
158 |
|
308ac9143 netfilter: nf_con... |
159 |
zone = nf_ct_zone(ct); |
5d0aa2ccd netfilter: nf_con... |
160 |
h = nf_conntrack_find_get(net, zone, t); |
f09943fef [NETFILTER]: nf_c... |
161 162 |
if (h) { sibling = nf_ct_tuplehash_to_ctrack(h); |
0d53778e8 [NETFILTER]: Conv... |
163 164 |
pr_debug("setting timeout of conntrack %p to 0 ", sibling); |
f09943fef [NETFILTER]: nf_c... |
165 166 |
sibling->proto.gre.timeout = 0; sibling->proto.gre.stream_timeout = 0; |
f330a7fdb netfilter: conntr... |
167 |
nf_ct_kill(sibling); |
f09943fef [NETFILTER]: nf_c... |
168 169 170 |
nf_ct_put(sibling); return 1; } else { |
5d0aa2ccd netfilter: nf_con... |
171 |
exp = nf_ct_expect_find_get(net, zone, t); |
f09943fef [NETFILTER]: nf_c... |
172 |
if (exp) { |
0d53778e8 [NETFILTER]: Conv... |
173 174 |
pr_debug("unexpect_related of expect %p ", exp); |
6823645d6 [NETFILTER]: nf_c... |
175 176 |
nf_ct_unexpect_related(exp); nf_ct_expect_put(exp); |
f09943fef [NETFILTER]: nf_c... |
177 178 179 180 181 182 183 184 185 |
return 1; } } return 0; } /* timeout GRE data connections */ static void pptp_destroy_siblings(struct nf_conn *ct) { |
0e6e75af9 netfilter: netns ... |
186 |
struct net *net = nf_ct_net(ct); |
1afc56794 netfilter: nf_ct_... |
187 |
const struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct); |
f09943fef [NETFILTER]: nf_c... |
188 189 190 191 192 193 194 |
struct nf_conntrack_tuple t; nf_ct_gre_keymap_destroy(ct); /* try original (pns->pac) tuple */ memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t)); t.dst.protonum = IPPROTO_GRE; |
1afc56794 netfilter: nf_ct_... |
195 196 |
t.src.u.gre.key = ct_pptp_info->pns_call_id; t.dst.u.gre.key = ct_pptp_info->pac_call_id; |
5d0aa2ccd netfilter: nf_con... |
197 |
if (!destroy_sibling_or_exp(net, ct, &t)) |
0d53778e8 [NETFILTER]: Conv... |
198 199 |
pr_debug("failed to timeout original pns->pac ct/exp "); |
f09943fef [NETFILTER]: nf_c... |
200 201 202 203 |
/* try reply (pac->pns) tuple */ memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t)); t.dst.protonum = IPPROTO_GRE; |
1afc56794 netfilter: nf_ct_... |
204 205 |
t.src.u.gre.key = ct_pptp_info->pac_call_id; t.dst.u.gre.key = ct_pptp_info->pns_call_id; |
5d0aa2ccd netfilter: nf_con... |
206 |
if (!destroy_sibling_or_exp(net, ct, &t)) |
0d53778e8 [NETFILTER]: Conv... |
207 208 |
pr_debug("failed to timeout reply pac->pns ct/exp "); |
f09943fef [NETFILTER]: nf_c... |
209 210 211 212 213 214 215 216 217 |
} /* expect GRE connections (PNS->PAC and PAC->PNS direction) */ static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid) { struct nf_conntrack_expect *exp_orig, *exp_reply; enum ip_conntrack_dir dir; int ret = 1; typeof(nf_nat_pptp_hook_exp_gre) nf_nat_pptp_exp_gre; |
6823645d6 [NETFILTER]: nf_c... |
218 |
exp_orig = nf_ct_expect_alloc(ct); |
f09943fef [NETFILTER]: nf_c... |
219 220 |
if (exp_orig == NULL) goto out; |
6823645d6 [NETFILTER]: nf_c... |
221 |
exp_reply = nf_ct_expect_alloc(ct); |
f09943fef [NETFILTER]: nf_c... |
222 223 224 225 226 |
if (exp_reply == NULL) goto out_put_orig; /* original direction, PNS->PAC */ dir = IP_CT_DIR_ORIGINAL; |
6002f266b [NETFILTER]: nf_c... |
227 |
nf_ct_expect_init(exp_orig, NF_CT_EXPECT_CLASS_DEFAULT, |
5e8fbe2ac [NETFILTER]: nf_c... |
228 |
nf_ct_l3num(ct), |
6823645d6 [NETFILTER]: nf_c... |
229 230 231 |
&ct->tuplehash[dir].tuple.src.u3, &ct->tuplehash[dir].tuple.dst.u3, IPPROTO_GRE, &peer_callid, &callid); |
f09943fef [NETFILTER]: nf_c... |
232 233 234 235 |
exp_orig->expectfn = pptp_expectfn; /* reply direction, PAC->PNS */ dir = IP_CT_DIR_REPLY; |
6002f266b [NETFILTER]: nf_c... |
236 |
nf_ct_expect_init(exp_reply, NF_CT_EXPECT_CLASS_DEFAULT, |
5e8fbe2ac [NETFILTER]: nf_c... |
237 |
nf_ct_l3num(ct), |
6823645d6 [NETFILTER]: nf_c... |
238 239 240 |
&ct->tuplehash[dir].tuple.src.u3, &ct->tuplehash[dir].tuple.dst.u3, IPPROTO_GRE, &callid, &peer_callid); |
f09943fef [NETFILTER]: nf_c... |
241 242 243 244 245 |
exp_reply->expectfn = pptp_expectfn; nf_nat_pptp_exp_gre = rcu_dereference(nf_nat_pptp_hook_exp_gre); if (nf_nat_pptp_exp_gre && ct->status & IPS_NAT_MASK) nf_nat_pptp_exp_gre(exp_orig, exp_reply); |
3c00fb0bf netfilter: nf_con... |
246 |
if (nf_ct_expect_related(exp_orig, 0) != 0) |
f09943fef [NETFILTER]: nf_c... |
247 |
goto out_put_both; |
3c00fb0bf netfilter: nf_con... |
248 |
if (nf_ct_expect_related(exp_reply, 0) != 0) |
f09943fef [NETFILTER]: nf_c... |
249 250 251 252 253 254 255 256 257 258 259 260 |
goto out_unexpect_orig; /* Add GRE keymap entries */ if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_ORIGINAL, &exp_orig->tuple) != 0) goto out_unexpect_both; if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_REPLY, &exp_reply->tuple) != 0) { nf_ct_gre_keymap_destroy(ct); goto out_unexpect_both; } ret = 0; out_put_both: |
6823645d6 [NETFILTER]: nf_c... |
261 |
nf_ct_expect_put(exp_reply); |
f09943fef [NETFILTER]: nf_c... |
262 |
out_put_orig: |
6823645d6 [NETFILTER]: nf_c... |
263 |
nf_ct_expect_put(exp_orig); |
f09943fef [NETFILTER]: nf_c... |
264 265 266 267 |
out: return ret; out_unexpect_both: |
6823645d6 [NETFILTER]: nf_c... |
268 |
nf_ct_unexpect_related(exp_reply); |
f09943fef [NETFILTER]: nf_c... |
269 |
out_unexpect_orig: |
6823645d6 [NETFILTER]: nf_c... |
270 |
nf_ct_unexpect_related(exp_orig); |
f09943fef [NETFILTER]: nf_c... |
271 272 |
goto out_put_both; } |
2fe7c321a netfilter: pptp: ... |
273 |
static int |
051966c0c netfilter: nf_nat... |
274 |
pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, |
f09943fef [NETFILTER]: nf_c... |
275 276 277 278 279 280 |
struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq, unsigned int reqlen, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { |
1afc56794 netfilter: nf_ct_... |
281 |
struct nf_ct_pptp_master *info = nfct_help_data(ct); |
f09943fef [NETFILTER]: nf_c... |
282 283 284 285 286 |
u_int16_t msg; __be16 cid = 0, pcid = 0; typeof(nf_nat_pptp_hook_inbound) nf_nat_pptp_inbound; msg = ntohs(ctlh->messageType); |
9f57771d0 netfilter: nf_con... |
287 288 |
pr_debug("inbound control message %s ", pptp_msg_name(msg)); |
f09943fef [NETFILTER]: nf_c... |
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
switch (msg) { case PPTP_START_SESSION_REPLY: /* server confirms new control session */ if (info->sstate < PPTP_SESSION_REQUESTED) goto invalid; if (pptpReq->srep.resultCode == PPTP_START_OK) info->sstate = PPTP_SESSION_CONFIRMED; else info->sstate = PPTP_SESSION_ERROR; break; case PPTP_STOP_SESSION_REPLY: /* server confirms end of control session */ if (info->sstate > PPTP_SESSION_STOPREQ) goto invalid; if (pptpReq->strep.resultCode == PPTP_STOP_OK) info->sstate = PPTP_SESSION_NONE; else info->sstate = PPTP_SESSION_ERROR; break; case PPTP_OUT_CALL_REPLY: /* server accepted call, we now expect GRE frames */ if (info->sstate != PPTP_SESSION_CONFIRMED) goto invalid; if (info->cstate != PPTP_CALL_OUT_REQ && info->cstate != PPTP_CALL_OUT_CONF) goto invalid; cid = pptpReq->ocack.callID; pcid = pptpReq->ocack.peersCallID; if (info->pns_call_id != pcid) goto invalid; |
9f57771d0 netfilter: nf_con... |
323 324 |
pr_debug("%s, CID=%X, PCID=%X ", pptp_msg_name(msg), |
0d53778e8 [NETFILTER]: Conv... |
325 |
ntohs(cid), ntohs(pcid)); |
f09943fef [NETFILTER]: nf_c... |
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
if (pptpReq->ocack.resultCode == PPTP_OUTCALL_CONNECT) { info->cstate = PPTP_CALL_OUT_CONF; info->pac_call_id = cid; exp_gre(ct, cid, pcid); } else info->cstate = PPTP_CALL_NONE; break; case PPTP_IN_CALL_REQUEST: /* server tells us about incoming call request */ if (info->sstate != PPTP_SESSION_CONFIRMED) goto invalid; cid = pptpReq->icreq.callID; |
9f57771d0 netfilter: nf_con... |
341 342 |
pr_debug("%s, CID=%X ", pptp_msg_name(msg), ntohs(cid)); |
f09943fef [NETFILTER]: nf_c... |
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
info->cstate = PPTP_CALL_IN_REQ; info->pac_call_id = cid; break; case PPTP_IN_CALL_CONNECT: /* server tells us about incoming call established */ if (info->sstate != PPTP_SESSION_CONFIRMED) goto invalid; if (info->cstate != PPTP_CALL_IN_REP && info->cstate != PPTP_CALL_IN_CONF) goto invalid; pcid = pptpReq->iccon.peersCallID; cid = info->pac_call_id; if (info->pns_call_id != pcid) goto invalid; |
9f57771d0 netfilter: nf_con... |
360 361 |
pr_debug("%s, PCID=%X ", pptp_msg_name(msg), ntohs(pcid)); |
f09943fef [NETFILTER]: nf_c... |
362 363 364 365 366 367 368 369 370 |
info->cstate = PPTP_CALL_IN_CONF; /* we expect a GRE connection from PAC to PNS */ exp_gre(ct, cid, pcid); break; case PPTP_CALL_DISCONNECT_NOTIFY: /* server confirms disconnect */ cid = pptpReq->disc.callID; |
9f57771d0 netfilter: nf_con... |
371 372 |
pr_debug("%s, CID=%X ", pptp_msg_name(msg), ntohs(cid)); |
f09943fef [NETFILTER]: nf_c... |
373 374 375 376 377 378 379 |
info->cstate = PPTP_CALL_NONE; /* untrack this call id, unexpect GRE packets */ pptp_destroy_siblings(ct); break; case PPTP_WAN_ERROR_NOTIFY: |
4c6e42096 netfilter: nf_ct_... |
380 |
case PPTP_SET_LINK_INFO: |
f09943fef [NETFILTER]: nf_c... |
381 382 383 384 385 386 387 388 389 390 391 |
case PPTP_ECHO_REQUEST: case PPTP_ECHO_REPLY: /* I don't have to explain these ;) */ break; default: goto invalid; } nf_nat_pptp_inbound = rcu_dereference(nf_nat_pptp_hook_inbound); if (nf_nat_pptp_inbound && ct->status & IPS_NAT_MASK) |
051966c0c netfilter: nf_nat... |
392 393 |
return nf_nat_pptp_inbound(skb, ct, ctinfo, protoff, ctlh, pptpReq); |
f09943fef [NETFILTER]: nf_c... |
394 395 396 |
return NF_ACCEPT; invalid: |
0d53778e8 [NETFILTER]: Conv... |
397 398 399 |
pr_debug("invalid %s: type=%d cid=%u pcid=%u " "cstate=%d sstate=%d pns_cid=%u pac_cid=%u ", |
9f57771d0 netfilter: nf_con... |
400 |
pptp_msg_name(msg), |
0d53778e8 [NETFILTER]: Conv... |
401 402 |
msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate, ntohs(info->pns_call_id), ntohs(info->pac_call_id)); |
f09943fef [NETFILTER]: nf_c... |
403 404 |
return NF_ACCEPT; } |
2fe7c321a netfilter: pptp: ... |
405 |
static int |
051966c0c netfilter: nf_nat... |
406 |
pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff, |
f09943fef [NETFILTER]: nf_c... |
407 408 409 410 411 412 |
struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq, unsigned int reqlen, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { |
1afc56794 netfilter: nf_ct_... |
413 |
struct nf_ct_pptp_master *info = nfct_help_data(ct); |
f09943fef [NETFILTER]: nf_c... |
414 415 416 417 418 |
u_int16_t msg; __be16 cid = 0, pcid = 0; typeof(nf_nat_pptp_hook_outbound) nf_nat_pptp_outbound; msg = ntohs(ctlh->messageType); |
9f57771d0 netfilter: nf_con... |
419 420 |
pr_debug("outbound control message %s ", pptp_msg_name(msg)); |
f09943fef [NETFILTER]: nf_c... |
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 |
switch (msg) { case PPTP_START_SESSION_REQUEST: /* client requests for new control session */ if (info->sstate != PPTP_SESSION_NONE) goto invalid; info->sstate = PPTP_SESSION_REQUESTED; break; case PPTP_STOP_SESSION_REQUEST: /* client requests end of control session */ info->sstate = PPTP_SESSION_STOPREQ; break; case PPTP_OUT_CALL_REQUEST: /* client initiating connection to server */ if (info->sstate != PPTP_SESSION_CONFIRMED) goto invalid; info->cstate = PPTP_CALL_OUT_REQ; /* track PNS call id */ cid = pptpReq->ocreq.callID; |
9f57771d0 netfilter: nf_con... |
442 443 |
pr_debug("%s, CID=%X ", pptp_msg_name(msg), ntohs(cid)); |
f09943fef [NETFILTER]: nf_c... |
444 445 446 447 448 449 450 451 452 453 454 455 456 |
info->pns_call_id = cid; break; case PPTP_IN_CALL_REPLY: /* client answers incoming call */ if (info->cstate != PPTP_CALL_IN_REQ && info->cstate != PPTP_CALL_IN_REP) goto invalid; cid = pptpReq->icack.callID; pcid = pptpReq->icack.peersCallID; if (info->pac_call_id != pcid) goto invalid; |
9f57771d0 netfilter: nf_con... |
457 458 |
pr_debug("%s, CID=%X PCID=%X ", pptp_msg_name(msg), |
0d53778e8 [NETFILTER]: Conv... |
459 |
ntohs(cid), ntohs(pcid)); |
f09943fef [NETFILTER]: nf_c... |
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 |
if (pptpReq->icack.resultCode == PPTP_INCALL_ACCEPT) { /* part two of the three-way handshake */ info->cstate = PPTP_CALL_IN_REP; info->pns_call_id = cid; } else info->cstate = PPTP_CALL_NONE; break; case PPTP_CALL_CLEAR_REQUEST: /* client requests hangup of call */ if (info->sstate != PPTP_SESSION_CONFIRMED) goto invalid; /* FUTURE: iterate over all calls and check if * call ID is valid. We don't do this without newnat, * because we only know about last call */ info->cstate = PPTP_CALL_CLEAR_REQ; break; case PPTP_SET_LINK_INFO: case PPTP_ECHO_REQUEST: case PPTP_ECHO_REPLY: /* I don't have to explain these ;) */ break; default: goto invalid; } nf_nat_pptp_outbound = rcu_dereference(nf_nat_pptp_hook_outbound); if (nf_nat_pptp_outbound && ct->status & IPS_NAT_MASK) |
051966c0c netfilter: nf_nat... |
491 492 |
return nf_nat_pptp_outbound(skb, ct, ctinfo, protoff, ctlh, pptpReq); |
f09943fef [NETFILTER]: nf_c... |
493 494 495 |
return NF_ACCEPT; invalid: |
0d53778e8 [NETFILTER]: Conv... |
496 497 498 |
pr_debug("invalid %s: type=%d cid=%u pcid=%u " "cstate=%d sstate=%d pns_cid=%u pac_cid=%u ", |
9f57771d0 netfilter: nf_con... |
499 |
pptp_msg_name(msg), |
0d53778e8 [NETFILTER]: Conv... |
500 501 |
msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate, ntohs(info->pns_call_id), ntohs(info->pac_call_id)); |
f09943fef [NETFILTER]: nf_c... |
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 |
return NF_ACCEPT; } static const unsigned int pptp_msg_size[] = { [PPTP_START_SESSION_REQUEST] = sizeof(struct PptpStartSessionRequest), [PPTP_START_SESSION_REPLY] = sizeof(struct PptpStartSessionReply), [PPTP_STOP_SESSION_REQUEST] = sizeof(struct PptpStopSessionRequest), [PPTP_STOP_SESSION_REPLY] = sizeof(struct PptpStopSessionReply), [PPTP_OUT_CALL_REQUEST] = sizeof(struct PptpOutCallRequest), [PPTP_OUT_CALL_REPLY] = sizeof(struct PptpOutCallReply), [PPTP_IN_CALL_REQUEST] = sizeof(struct PptpInCallRequest), [PPTP_IN_CALL_REPLY] = sizeof(struct PptpInCallReply), [PPTP_IN_CALL_CONNECT] = sizeof(struct PptpInCallConnected), [PPTP_CALL_CLEAR_REQUEST] = sizeof(struct PptpClearCallRequest), [PPTP_CALL_DISCONNECT_NOTIFY] = sizeof(struct PptpCallDisconnectNotify), [PPTP_WAN_ERROR_NOTIFY] = sizeof(struct PptpWanErrorNotify), [PPTP_SET_LINK_INFO] = sizeof(struct PptpSetLinkInfo), }; /* track caller id inside control connection, call expect_related */ static int |
3db05fea5 [NETFILTER]: Repl... |
523 |
conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff, |
f09943fef [NETFILTER]: nf_c... |
524 525 526 527 |
struct nf_conn *ct, enum ip_conntrack_info ctinfo) { int dir = CTINFO2DIR(ctinfo); |
1afc56794 netfilter: nf_ct_... |
528 |
const struct nf_ct_pptp_master *info = nfct_help_data(ct); |
9ddd0ed05 [NETFILTER]: nf_{... |
529 530 531 532 |
const struct tcphdr *tcph; struct tcphdr _tcph; const struct pptp_pkt_hdr *pptph; struct pptp_pkt_hdr _pptph; |
f09943fef [NETFILTER]: nf_c... |
533 534 |
struct PptpControlHeader _ctlh, *ctlh; union pptp_ctrl_union _pptpReq, *pptpReq; |
3db05fea5 [NETFILTER]: Repl... |
535 |
unsigned int tcplen = skb->len - protoff; |
f09943fef [NETFILTER]: nf_c... |
536 537 538 539 |
unsigned int datalen, reqlen, nexthdr_off; int oldsstate, oldcstate; int ret; u_int16_t msg; |
2fe7c321a netfilter: pptp: ... |
540 541 542 543 544 545 546 547 |
#if IS_ENABLED(CONFIG_NF_NAT) if (!nf_ct_is_confirmed(ct) && (ct->status & IPS_NAT_MASK)) { struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT); if (!nat && !nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC)) return NF_DROP; } #endif |
f09943fef [NETFILTER]: nf_c... |
548 |
/* don't do any tracking before tcp handshake complete */ |
fb0488337 netfilter: add mo... |
549 |
if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) |
f09943fef [NETFILTER]: nf_c... |
550 551 552 |
return NF_ACCEPT; nexthdr_off = protoff; |
3db05fea5 [NETFILTER]: Repl... |
553 |
tcph = skb_header_pointer(skb, nexthdr_off, sizeof(_tcph), &_tcph); |
f09943fef [NETFILTER]: nf_c... |
554 555 |
BUG_ON(!tcph); nexthdr_off += tcph->doff * 4; |
601e68e10 [NETFILTER]: Fix ... |
556 |
datalen = tcplen - tcph->doff * 4; |
f09943fef [NETFILTER]: nf_c... |
557 |
|
3db05fea5 [NETFILTER]: Repl... |
558 |
pptph = skb_header_pointer(skb, nexthdr_off, sizeof(_pptph), &_pptph); |
f09943fef [NETFILTER]: nf_c... |
559 |
if (!pptph) { |
0d53778e8 [NETFILTER]: Conv... |
560 561 |
pr_debug("no full PPTP header, can't track "); |
f09943fef [NETFILTER]: nf_c... |
562 563 564 565 566 567 568 569 |
return NF_ACCEPT; } nexthdr_off += sizeof(_pptph); datalen -= sizeof(_pptph); /* if it's not a control message we can't do anything with it */ if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { |
0d53778e8 [NETFILTER]: Conv... |
570 571 |
pr_debug("not a control packet "); |
f09943fef [NETFILTER]: nf_c... |
572 573 |
return NF_ACCEPT; } |
3db05fea5 [NETFILTER]: Repl... |
574 |
ctlh = skb_header_pointer(skb, nexthdr_off, sizeof(_ctlh), &_ctlh); |
f09943fef [NETFILTER]: nf_c... |
575 576 577 578 579 580 581 582 583 584 585 |
if (!ctlh) return NF_ACCEPT; nexthdr_off += sizeof(_ctlh); datalen -= sizeof(_ctlh); reqlen = datalen; msg = ntohs(ctlh->messageType); if (msg > 0 && msg <= PPTP_MSG_MAX && reqlen < pptp_msg_size[msg]) return NF_ACCEPT; if (reqlen > sizeof(*pptpReq)) reqlen = sizeof(*pptpReq); |
3db05fea5 [NETFILTER]: Repl... |
586 |
pptpReq = skb_header_pointer(skb, nexthdr_off, reqlen, &_pptpReq); |
f09943fef [NETFILTER]: nf_c... |
587 588 589 590 591 592 593 594 595 596 597 598 |
if (!pptpReq) return NF_ACCEPT; oldsstate = info->sstate; oldcstate = info->cstate; spin_lock_bh(&nf_pptp_lock); /* FIXME: We just blindly assume that the control connection is always * established from PNS->PAC. However, RFC makes no guarantee */ if (dir == IP_CT_DIR_ORIGINAL) /* client -> server (PNS -> PAC) */ |
051966c0c netfilter: nf_nat... |
599 |
ret = pptp_outbound_pkt(skb, protoff, ctlh, pptpReq, reqlen, ct, |
f09943fef [NETFILTER]: nf_c... |
600 601 602 |
ctinfo); else /* server -> client (PAC -> PNS) */ |
051966c0c netfilter: nf_nat... |
603 |
ret = pptp_inbound_pkt(skb, protoff, ctlh, pptpReq, reqlen, ct, |
f09943fef [NETFILTER]: nf_c... |
604 |
ctinfo); |
0d53778e8 [NETFILTER]: Conv... |
605 606 607 |
pr_debug("sstate: %d->%d, cstate: %d->%d ", oldsstate, info->sstate, oldcstate, info->cstate); |
f09943fef [NETFILTER]: nf_c... |
608 609 610 611 |
spin_unlock_bh(&nf_pptp_lock); return ret; } |
6002f266b [NETFILTER]: nf_c... |
612 613 614 615 |
static const struct nf_conntrack_expect_policy pptp_exp_policy = { .max_expected = 2, .timeout = 5 * 60, }; |
f09943fef [NETFILTER]: nf_c... |
616 617 618 619 |
/* control protocol helper */ static struct nf_conntrack_helper pptp __read_mostly = { .name = "pptp", .me = THIS_MODULE, |
f09943fef [NETFILTER]: nf_c... |
620 |
.tuple.src.l3num = AF_INET, |
09640e636 net: replace uses... |
621 |
.tuple.src.u.tcp.port = cpu_to_be16(PPTP_CONTROL_PORT), |
f09943fef [NETFILTER]: nf_c... |
622 |
.tuple.dst.protonum = IPPROTO_TCP, |
f09943fef [NETFILTER]: nf_c... |
623 624 |
.help = conntrack_pptp_help, .destroy = pptp_destroy_siblings, |
6002f266b [NETFILTER]: nf_c... |
625 |
.expect_policy = &pptp_exp_policy, |
f09943fef [NETFILTER]: nf_c... |
626 627 628 629 |
}; static int __init nf_conntrack_pptp_init(void) { |
dcf67740f netfilter: helper... |
630 |
NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_pptp_master)); |
8142b227e netfilter: nf_con... |
631 |
return nf_conntrack_helper_register(&pptp); |
f09943fef [NETFILTER]: nf_c... |
632 633 634 635 636 |
} static void __exit nf_conntrack_pptp_fini(void) { nf_conntrack_helper_unregister(&pptp); |
f09943fef [NETFILTER]: nf_c... |
637 638 639 640 |
} module_init(nf_conntrack_pptp_init); module_exit(nf_conntrack_pptp_fini); |