* - We can only support one single call within each session
*
* TODO:
- * - testing of incoming PPTP calls
+ * - testing of incoming PPTP calls
*
- * Changes:
+ * Changes:
* 2002-02-05 - Version 1.3
- * - Call ip_conntrack_unexpect_related() from
+ * - Call ip_conntrack_unexpect_related() from
* pptp_destroy_siblings() to destroy expectations in case
* CALL_DISCONNECT_NOTIFY or tcp fin packet was seen
* (Philip Craig <philipc@snapgear.com>)
*
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/ip.h>
struct PptpControlHeader *ctlh,
union pptp_ctrl_union *pptpReq);
-int
+void
(*ip_nat_pptp_hook_exp_gre)(struct ip_conntrack_expect *expect_orig,
struct ip_conntrack_expect *expect_reply);
invert_tuplepr(&inv_t, &exp->tuple);
DEBUGP("trying to unexpect other dir: ");
DUMP_TUPLE(&inv_t);
-
+
exp_other = ip_conntrack_expect_find(&inv_t);
if (exp_other) {
/* delete other expectation. */
{
struct ip_conntrack_tuple t;
- /* Since ct->sibling_list has literally rusted away in 2.6.11,
+ /* Since ct->sibling_list has literally rusted away in 2.6.11,
* we now need another way to find out about our sibling
* contrack and expects... -HW */
/* try original (pns->pac) tuple */
memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t));
t.dst.protonum = IPPROTO_GRE;
- t.src.u.gre.key = htons(ct->help.ct_pptp_info.pns_call_id);
- t.dst.u.gre.key = htons(ct->help.ct_pptp_info.pac_call_id);
+ t.src.u.gre.key = ct->help.ct_pptp_info.pns_call_id;
+ t.dst.u.gre.key = ct->help.ct_pptp_info.pac_call_id;
if (!destroy_sibling_or_exp(&t))
DEBUGP("failed to timeout original pns->pac ct/exp\n");
/* try reply (pac->pns) tuple */
memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t));
t.dst.protonum = IPPROTO_GRE;
- t.src.u.gre.key = htons(ct->help.ct_pptp_info.pac_call_id);
- t.dst.u.gre.key = htons(ct->help.ct_pptp_info.pns_call_id);
+ t.src.u.gre.key = ct->help.ct_pptp_info.pac_call_id;
+ t.dst.u.gre.key = ct->help.ct_pptp_info.pns_call_id;
if (!destroy_sibling_or_exp(&t))
DEBUGP("failed to timeout reply pac->pns ct/exp\n");
/* expect GRE connections (PNS->PAC and PAC->PNS direction) */
static inline int
-exp_gre(struct ip_conntrack *master,
- u_int32_t seq,
+exp_gre(struct ip_conntrack *ct,
__be16 callid,
__be16 peer_callid)
{
- struct ip_conntrack_tuple inv_tuple;
- struct ip_conntrack_tuple exp_tuples[] = {
- /* tuple in original direction, PNS->PAC */
- { .src = { .ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip,
- .u = { .gre = { .key = peer_callid } }
- },
- .dst = { .ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip,
- .u = { .gre = { .key = callid } },
- .protonum = IPPROTO_GRE
- },
- },
- /* tuple in reply direction, PAC->PNS */
- { .src = { .ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip,
- .u = { .gre = { .key = callid } }
- },
- .dst = { .ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip,
- .u = { .gre = { .key = peer_callid } },
- .protonum = IPPROTO_GRE
- },
- }
- };
struct ip_conntrack_expect *exp_orig, *exp_reply;
int ret = 1;
- exp_orig = ip_conntrack_expect_alloc(master);
+ exp_orig = ip_conntrack_expect_alloc(ct);
if (exp_orig == NULL)
goto out;
- exp_reply = ip_conntrack_expect_alloc(master);
+ exp_reply = ip_conntrack_expect_alloc(ct);
if (exp_reply == NULL)
goto out_put_orig;
- memcpy(&exp_orig->tuple, &exp_tuples[0], sizeof(exp_orig->tuple));
+ /* original direction, PNS->PAC */
+ exp_orig->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
+ exp_orig->tuple.src.u.gre.key = peer_callid;
+ exp_orig->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
+ exp_orig->tuple.dst.u.gre.key = callid;
+ exp_orig->tuple.dst.protonum = IPPROTO_GRE;
exp_orig->mask.src.ip = 0xffffffff;
exp_orig->mask.src.u.all = 0;
- exp_orig->mask.dst.u.all = 0;
exp_orig->mask.dst.u.gre.key = htons(0xffff);
exp_orig->mask.dst.ip = 0xffffffff;
exp_orig->mask.dst.protonum = 0xff;
-
- exp_orig->master = master;
+
+ exp_orig->master = ct;
exp_orig->expectfn = pptp_expectfn;
exp_orig->flags = 0;
/* both expectations are identical apart from tuple */
memcpy(exp_reply, exp_orig, sizeof(*exp_reply));
- memcpy(&exp_reply->tuple, &exp_tuples[1], sizeof(exp_reply->tuple));
-
- if (ip_nat_pptp_hook_exp_gre)
- ret = ip_nat_pptp_hook_exp_gre(exp_orig, exp_reply);
- else {
-
- DEBUGP("calling expect_related PNS->PAC");
- DUMP_TUPLE(&exp_orig->tuple);
- if (ip_conntrack_expect_related(exp_orig) != 0) {
- DEBUGP("cannot expect_related()\n");
- goto out_put_both;
- }
-
- DEBUGP("calling expect_related PAC->PNS");
- DUMP_TUPLE(&exp_reply->tuple);
-
- if (ip_conntrack_expect_related(exp_reply) != 0) {
- DEBUGP("cannot expect_related()\n");
- goto out_unexpect_orig;
- }
+ /* reply direction, PAC->PNS */
+ exp_reply->tuple.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
+ exp_reply->tuple.src.u.gre.key = callid;
+ exp_reply->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
+ exp_reply->tuple.dst.u.gre.key = peer_callid;
+ exp_reply->tuple.dst.protonum = IPPROTO_GRE;
- /* Add GRE keymap entries */
- if (ip_ct_gre_keymap_add(master, &exp_reply->tuple, 0) != 0) {
- DEBUGP("cannot keymap_add() exp\n");
- goto out_unexpect_both;
- }
-
- invert_tuplepr(&inv_tuple, &exp_reply->tuple);
- if (ip_ct_gre_keymap_add(master, &inv_tuple, 1) != 0) {
- ip_ct_gre_keymap_destroy(master);
- DEBUGP("cannot keymap_add() exp_inv\n");
- goto out_unexpect_both;
- }
- ret = 0;
+ if (ip_nat_pptp_hook_exp_gre)
+ ip_nat_pptp_hook_exp_gre(exp_orig, exp_reply);
+ if (ip_conntrack_expect_related(exp_orig) != 0)
+ goto out_put_both;
+ if (ip_conntrack_expect_related(exp_reply) != 0)
+ goto out_unexpect_orig;
+
+ /* Add GRE keymap entries */
+ if (ip_ct_gre_keymap_add(ct, &exp_orig->tuple, 0) != 0)
+ goto out_unexpect_both;
+ if (ip_ct_gre_keymap_add(ct, &exp_reply->tuple, 1) != 0) {
+ ip_ct_gre_keymap_destroy(ct);
+ goto out_unexpect_both;
}
+ ret = 0;
out_put_both:
ip_conntrack_expect_put(exp_reply);
goto out_put_both;
}
-static inline int
+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),
+};
+
+static inline int
pptp_inbound_pkt(struct sk_buff **pskb,
struct tcphdr *tcph,
unsigned int nexthdr_off,
union pptp_ctrl_union _pptpReq, *pptpReq;
struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
u_int16_t msg;
- __be16 *cid, *pcid;
- u_int32_t seq;
+ __be16 cid, pcid;
ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh);
if (!ctlh) {
msg = ntohs(ctlh->messageType);
DEBUGP("inbound control message %s\n", pptp_msg_name[msg]);
+ if (msg > 0 && msg <= PPTP_MSG_MAX && reqlen < pptp_msg_size[msg])
+ return NF_ACCEPT;
+
switch (msg) {
case PPTP_START_SESSION_REPLY:
- if (reqlen < sizeof(_pptpReq.srep)) {
- DEBUGP("%s: short packet\n", pptp_msg_name[msg]);
- break;
- }
-
/* server confirms new control session */
if (info->sstate < PPTP_SESSION_REQUESTED) {
DEBUGP("%s without START_SESS_REQUEST\n",
}
if (pptpReq->srep.resultCode == PPTP_START_OK)
info->sstate = PPTP_SESSION_CONFIRMED;
- else
+ else
info->sstate = PPTP_SESSION_ERROR;
break;
case PPTP_STOP_SESSION_REPLY:
- if (reqlen < sizeof(_pptpReq.strep)) {
- DEBUGP("%s: short packet\n", pptp_msg_name[msg]);
- break;
- }
-
/* server confirms end of control session */
if (info->sstate > PPTP_SESSION_STOPREQ) {
DEBUGP("%s without STOP_SESS_REQUEST\n",
break;
case PPTP_OUT_CALL_REPLY:
- if (reqlen < sizeof(_pptpReq.ocack)) {
- DEBUGP("%s: short packet\n", pptp_msg_name[msg]);
- break;
- }
-
/* server accepted call, we now expect GRE frames */
if (info->sstate != PPTP_SESSION_CONFIRMED) {
DEBUGP("%s but no session\n", pptp_msg_name[msg]);
break;
}
- cid = &pptpReq->ocack.callID;
- pcid = &pptpReq->ocack.peersCallID;
+ cid = pptpReq->ocack.callID;
+ pcid = pptpReq->ocack.peersCallID;
- info->pac_call_id = ntohs(*cid);
-
- if (htons(info->pns_call_id) != *pcid) {
+ info->pac_call_id = cid;
+
+ if (info->pns_call_id != pcid) {
DEBUGP("%s for unknown callid %u\n",
- pptp_msg_name[msg], ntohs(*pcid));
+ pptp_msg_name[msg], ntohs(pcid));
break;
}
- DEBUGP("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg],
- ntohs(*cid), ntohs(*pcid));
-
+ DEBUGP("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg],
+ ntohs(cid), ntohs(pcid));
+
info->cstate = PPTP_CALL_OUT_CONF;
- seq = ntohl(tcph->seq) + sizeof(struct pptp_pkt_hdr)
- + sizeof(struct PptpControlHeader)
- + ((void *)pcid - (void *)pptpReq);
-
- if (exp_gre(ct, seq, *cid, *pcid) != 0)
- printk("ip_conntrack_pptp: error during exp_gre\n");
+ exp_gre(ct, cid, pcid);
break;
case PPTP_IN_CALL_REQUEST:
- if (reqlen < sizeof(_pptpReq.icack)) {
- DEBUGP("%s: short packet\n", pptp_msg_name[msg]);
- break;
- }
-
/* server tells us about incoming call request */
if (info->sstate != PPTP_SESSION_CONFIRMED) {
DEBUGP("%s but no session\n", pptp_msg_name[msg]);
break;
}
- pcid = &pptpReq->icack.peersCallID;
- DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(*pcid));
+ pcid = pptpReq->icack.peersCallID;
+ DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(pcid));
info->cstate = PPTP_CALL_IN_REQ;
- info->pac_call_id = ntohs(*pcid);
+ info->pac_call_id = pcid;
break;
case PPTP_IN_CALL_CONNECT:
- if (reqlen < sizeof(_pptpReq.iccon)) {
- DEBUGP("%s: short packet\n", pptp_msg_name[msg]);
- break;
- }
-
/* server tells us about incoming call established */
if (info->sstate != PPTP_SESSION_CONFIRMED) {
DEBUGP("%s but no session\n", pptp_msg_name[msg]);
break;
}
- pcid = &pptpReq->iccon.peersCallID;
- cid = &info->pac_call_id;
+ pcid = pptpReq->iccon.peersCallID;
+ cid = info->pac_call_id;
- if (info->pns_call_id != ntohs(*pcid)) {
- DEBUGP("%s for unknown CallID %u\n",
- pptp_msg_name[msg], ntohs(*pcid));
+ if (info->pns_call_id != pcid) {
+ DEBUGP("%s for unknown CallID %u\n",
+ pptp_msg_name[msg], ntohs(pcid));
break;
}
- DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(*pcid));
+ DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(pcid));
info->cstate = PPTP_CALL_IN_CONF;
/* we expect a GRE connection from PAC to PNS */
- seq = ntohl(tcph->seq) + sizeof(struct pptp_pkt_hdr)
- + sizeof(struct PptpControlHeader)
- + ((void *)pcid - (void *)pptpReq);
-
- if (exp_gre(ct, seq, *cid, *pcid) != 0)
- printk("ip_conntrack_pptp: error during exp_gre\n");
-
+ exp_gre(ct, cid, pcid);
break;
case PPTP_CALL_DISCONNECT_NOTIFY:
- if (reqlen < sizeof(_pptpReq.disc)) {
- DEBUGP("%s: short packet\n", pptp_msg_name[msg]);
- break;
- }
-
/* server confirms disconnect */
- cid = &pptpReq->disc.callID;
- DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(*cid));
+ cid = pptpReq->disc.callID;
+ DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
info->cstate = PPTP_CALL_NONE;
/* untrack this call id, unexpect GRE packets */
break;
case PPTP_WAN_ERROR_NOTIFY:
- break;
-
case PPTP_ECHO_REQUEST:
case PPTP_ECHO_REPLY:
/* I don't have to explain these ;) */
union pptp_ctrl_union _pptpReq, *pptpReq;
struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
u_int16_t msg;
- __be16 *cid, *pcid;
+ __be16 cid, pcid;
ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh);
if (!ctlh)
return NF_ACCEPT;
nexthdr_off += sizeof(_ctlh);
datalen -= sizeof(_ctlh);
-
+
reqlen = datalen;
if (reqlen > sizeof(*pptpReq))
reqlen = sizeof(*pptpReq);
msg = ntohs(ctlh->messageType);
DEBUGP("outbound control message %s\n", pptp_msg_name[msg]);
+ if (msg > 0 && msg <= PPTP_MSG_MAX && reqlen < pptp_msg_size[msg])
+ return NF_ACCEPT;
+
switch (msg) {
case PPTP_START_SESSION_REQUEST:
/* client requests for new control session */
break;
case PPTP_OUT_CALL_REQUEST:
- if (reqlen < sizeof(_pptpReq.ocreq)) {
- DEBUGP("%s: short packet\n", pptp_msg_name[msg]);
- /* FIXME: break; */
- }
-
/* client initiating connection to server */
if (info->sstate != PPTP_SESSION_CONFIRMED) {
DEBUGP("%s but no session\n",
}
info->cstate = PPTP_CALL_OUT_REQ;
/* track PNS call id */
- cid = &pptpReq->ocreq.callID;
- DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(*cid));
- info->pns_call_id = ntohs(*cid);
+ cid = pptpReq->ocreq.callID;
+ DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
+ info->pns_call_id = cid;
break;
case PPTP_IN_CALL_REPLY:
- if (reqlen < sizeof(_pptpReq.icack)) {
- DEBUGP("%s: short packet\n", pptp_msg_name[msg]);
- break;
- }
-
/* client answers incoming call */
if (info->cstate != PPTP_CALL_IN_REQ
&& info->cstate != PPTP_CALL_IN_REP) {
- DEBUGP("%s without incall_req\n",
+ DEBUGP("%s without incall_req\n",
pptp_msg_name[msg]);
break;
}
info->cstate = PPTP_CALL_NONE;
break;
}
- pcid = &pptpReq->icack.peersCallID;
- if (info->pac_call_id != ntohs(*pcid)) {
- DEBUGP("%s for unknown call %u\n",
- pptp_msg_name[msg], ntohs(*pcid));
+ pcid = pptpReq->icack.peersCallID;
+ if (info->pac_call_id != pcid) {
+ DEBUGP("%s for unknown call %u\n",
+ pptp_msg_name[msg], ntohs(pcid));
break;
}
- DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(*pcid));
+ DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(pcid));
/* part two of the three-way handshake */
info->cstate = PPTP_CALL_IN_REP;
- info->pns_call_id = ntohs(pptpReq->icack.callID);
+ info->pns_call_id = pcid;
break;
case PPTP_CALL_CLEAR_REQUEST:
info->cstate = PPTP_CALL_CLEAR_REQ;
break;
case PPTP_SET_LINK_INFO:
- break;
case PPTP_ECHO_REQUEST:
case PPTP_ECHO_REPLY:
/* I don't have to explain these ;) */
break;
default:
- DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)?
+ DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)?
pptp_msg_name[msg]:pptp_msg_name[0], msg);
/* unknown: no need to create GRE masq table entry */
break;
}
-
+
if (ip_nat_pptp_hook_outbound)
return ip_nat_pptp_hook_outbound(pskb, ct, ctinfo, ctlh,
pptpReq);
/* track caller id inside control connection, call expect_related */
-static int
+static int
conntrack_pptp_help(struct sk_buff **pskb,
struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
int ret;
/* don't do any tracking before tcp handshake complete */
- if (ctinfo != IP_CT_ESTABLISHED
+ if (ctinfo != IP_CT_ESTABLISHED
&& ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
DEBUGP("ctinfo = %u, skipping\n", ctinfo);
return NF_ACCEPT;
}
-
+
nexthdr_off = (*pskb)->nh.iph->ihl*4;
tcph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_tcph), &_tcph);
BUG_ON(!tcph);
}
/* control protocol helper */
-static struct ip_conntrack_helper pptp = {
+static struct ip_conntrack_helper pptp = {
.list = { NULL, NULL },
- .name = "pptp",
+ .name = "pptp",
.me = THIS_MODULE,
.max_expected = 2,
.timeout = 5 * 60,
- .tuple = { .src = { .ip = 0,
- .u = { .tcp = { .port =
- __constant_htons(PPTP_CONTROL_PORT) } }
- },
- .dst = { .ip = 0,
+ .tuple = { .src = { .ip = 0,
+ .u = { .tcp = { .port =
+ __constant_htons(PPTP_CONTROL_PORT) } }
+ },
+ .dst = { .ip = 0,
.u = { .all = 0 },
.protonum = IPPROTO_TCP
- }
+ }
},
- .mask = { .src = { .ip = 0,
- .u = { .tcp = { .port = __constant_htons(0xffff) } }
- },
- .dst = { .ip = 0,
+ .mask = { .src = { .ip = 0,
+ .u = { .tcp = { .port = __constant_htons(0xffff) } }
+ },
+ .dst = { .ip = 0,
.u = { .all = 0 },
- .protonum = 0xff
- }
+ .protonum = 0xff
+ }
},
.help = conntrack_pptp_help
};
static int __init ip_conntrack_helper_pptp_init(void)
{
int retcode;
-
+
retcode = ip_ct_proto_gre_init();
if (retcode < 0)
return retcode;