[NETFILTER]: ip_conntrack: fix NAT helper unload races
[deliverable/linux.git] / net / ipv4 / netfilter / ip_conntrack_helper_pptp.c
index 57eac6e3871a85cd46aec36770a5ff9af3ccbca0..4d19373bbf0d58c85f4e0e6c9fe7151abc51954c 100644 (file)
@@ -124,6 +124,8 @@ EXPORT_SYMBOL(pptp_msg_name);
 static void pptp_expectfn(struct ip_conntrack *ct,
                         struct ip_conntrack_expect *exp)
 {
+       typeof(ip_nat_pptp_hook_expectfn) ip_nat_pptp_expectfn;
+
        DEBUGP("increasing timeouts\n");
 
        /* increase timeout of GRE data channel conntrack entry */
@@ -133,7 +135,9 @@ static void pptp_expectfn(struct ip_conntrack *ct,
        /* 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 */
 
-       if (!ip_nat_pptp_hook_expectfn) {
+       rcu_read_lock();
+       ip_nat_pptp_expectfn = rcu_dereference(ip_nat_pptp_hook_expectfn);
+       if (!ip_nat_pptp_expectfn) {
                struct ip_conntrack_tuple inv_t;
                struct ip_conntrack_expect *exp_other;
 
@@ -142,7 +146,7 @@ static void pptp_expectfn(struct ip_conntrack *ct,
                DEBUGP("trying to unexpect other dir: ");
                DUMP_TUPLE(&inv_t);
 
-               exp_other = ip_conntrack_expect_find(&inv_t);
+               exp_other = ip_conntrack_expect_find_get(&inv_t);
                if (exp_other) {
                        /* delete other expectation.  */
                        DEBUGP("found\n");
@@ -153,8 +157,9 @@ static void pptp_expectfn(struct ip_conntrack *ct,
                }
        } else {
                /* we need more than simple inversion */
-               ip_nat_pptp_hook_expectfn(ct, exp);
+               ip_nat_pptp_expectfn(ct, exp);
        }
+       rcu_read_unlock();
 }
 
 static int destroy_sibling_or_exp(const struct ip_conntrack_tuple *t)
@@ -176,7 +181,7 @@ static int destroy_sibling_or_exp(const struct ip_conntrack_tuple *t)
                ip_conntrack_put(sibling);
                return 1;
        } else {
-               exp = ip_conntrack_expect_find(t);
+               exp = ip_conntrack_expect_find_get(t);
                if (exp) {
                        DEBUGP("unexpect_related of expect %p\n", exp);
                        ip_conntrack_unexpect_related(exp);
@@ -194,6 +199,7 @@ static void pptp_destroy_siblings(struct ip_conntrack *ct)
 {
        struct ip_conntrack_tuple t;
 
+       ip_ct_gre_keymap_destroy(ct);
        /* 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 */
@@ -225,6 +231,7 @@ exp_gre(struct ip_conntrack *ct,
 {
        struct ip_conntrack_expect *exp_orig, *exp_reply;
        int ret = 1;
+       typeof(ip_nat_pptp_hook_exp_gre) ip_nat_pptp_exp_gre;
 
        exp_orig = ip_conntrack_expect_alloc(ct);
        if (exp_orig == NULL)
@@ -241,10 +248,10 @@ exp_gre(struct ip_conntrack *ct,
        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.ip = htonl(0xffffffff);
        exp_orig->mask.src.u.all = 0;
        exp_orig->mask.dst.u.gre.key = htons(0xffff);
-       exp_orig->mask.dst.ip = 0xffffffff;
+       exp_orig->mask.dst.ip = htonl(0xffffffff);
        exp_orig->mask.dst.protonum = 0xff;
 
        exp_orig->master = ct;
@@ -261,8 +268,9 @@ exp_gre(struct ip_conntrack *ct,
        exp_reply->tuple.dst.u.gre.key = peer_callid;
        exp_reply->tuple.dst.protonum = IPPROTO_GRE;
 
-       if (ip_nat_pptp_hook_exp_gre)
-               ip_nat_pptp_hook_exp_gre(exp_orig, exp_reply);
+       ip_nat_pptp_exp_gre = rcu_dereference(ip_nat_pptp_hook_exp_gre);
+       if (ip_nat_pptp_exp_gre)
+               ip_nat_pptp_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)
@@ -293,52 +301,25 @@ out_unexpect_orig:
 
 static inline int
 pptp_inbound_pkt(struct sk_buff **pskb,
-                struct tcphdr *tcph,
-                unsigned int nexthdr_off,
-                unsigned int datalen,
+                struct PptpControlHeader *ctlh,
+                union pptp_ctrl_union *pptpReq,
+                unsigned int reqlen,
                 struct ip_conntrack *ct,
                 enum ip_conntrack_info ctinfo)
 {
-       struct PptpControlHeader _ctlh, *ctlh;
-       unsigned int reqlen;
-       union pptp_ctrl_union _pptpReq, *pptpReq;
        struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
        u_int16_t msg;
-       __be16 cid, pcid;
-
-       ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh);
-       if (!ctlh) {
-               DEBUGP("error during skb_header_pointer\n");
-               return NF_ACCEPT;
-       }
-       nexthdr_off += sizeof(_ctlh);
-       datalen -= sizeof(_ctlh);
-
-       reqlen = datalen;
-       if (reqlen > sizeof(*pptpReq))
-               reqlen = sizeof(*pptpReq);
-       pptpReq = skb_header_pointer(*pskb, nexthdr_off, reqlen, &_pptpReq);
-       if (!pptpReq) {
-               DEBUGP("error during skb_header_pointer\n");
-               return NF_ACCEPT;
-       }
+       __be16 cid = 0, pcid = 0;
+       typeof(ip_nat_pptp_hook_inbound) ip_nat_pptp_inbound;
 
        msg = ntohs(ctlh->messageType);
        DEBUGP("inbound control message %s\n", pptp_msg_name[msg]);
 
        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",
-                               pptp_msg_name[msg]);
-                       break;
-               }
+               if (info->sstate < PPTP_SESSION_REQUESTED)
+                       goto invalid;
                if (pptpReq->srep.resultCode == PPTP_START_OK)
                        info->sstate = PPTP_SESSION_CONFIRMED;
                else
@@ -346,17 +327,9 @@ pptp_inbound_pkt(struct sk_buff **pskb,
                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",
-                               pptp_msg_name[msg]);
-                       break;
-               }
+               if (info->sstate > PPTP_SESSION_STOPREQ)
+                       goto invalid;
                if (pptpReq->strep.resultCode == PPTP_STOP_OK)
                        info->sstate = PPTP_SESSION_NONE;
                else
@@ -364,88 +337,52 @@ pptp_inbound_pkt(struct sk_buff **pskb,
                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;
-               }
+               if (info->sstate != PPTP_SESSION_CONFIRMED)
+                       goto invalid;
                if (info->cstate != PPTP_CALL_OUT_REQ &&
-                   info->cstate != PPTP_CALL_OUT_CONF) {
-                       DEBUGP("%s without OUTCALL_REQ\n", pptp_msg_name[msg]);
-                       break;
-               }
-               if (pptpReq->ocack.resultCode != PPTP_OUTCALL_CONNECT) {
-                       info->cstate = PPTP_CALL_NONE;
-                       break;
-               }
+                   info->cstate != PPTP_CALL_OUT_CONF)
+                       goto invalid;
 
                cid = pptpReq->ocack.callID;
                pcid = pptpReq->ocack.peersCallID;
-
-               info->pac_call_id = cid;
-
-               if (info->pns_call_id != pcid) {
-                       DEBUGP("%s for unknown callid %u\n",
-                               pptp_msg_name[msg], ntohs(pcid));
-                       break;
-               }
-
+               if (info->pns_call_id != pcid)
+                       goto invalid;
                DEBUGP("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg],
                        ntohs(cid), ntohs(pcid));
 
-               info->cstate = PPTP_CALL_OUT_CONF;
-
-               exp_gre(ct, cid, pcid);
+               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:
-               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));
+               if (info->sstate != PPTP_SESSION_CONFIRMED)
+                       goto invalid;
+
+               cid = pptpReq->icreq.callID;
+               DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
                info->cstate = PPTP_CALL_IN_REQ;
-               info->pac_call_id = pcid;
+               info->pac_call_id = cid;
                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;
-               }
-               if (info->cstate != PPTP_CALL_IN_REP
-                   && info->cstate != PPTP_CALL_IN_CONF) {
-                       DEBUGP("%s but never sent IN_CALL_REPLY\n",
-                               pptp_msg_name[msg]);
-                       break;
-               }
+               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) {
-                       DEBUGP("%s for unknown CallID %u\n",
-                               pptp_msg_name[msg], ntohs(pcid));
-                       break;
-               }
+               if (info->pns_call_id != pcid)
+                       goto invalid;
 
                DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(pcid));
                info->cstate = PPTP_CALL_IN_CONF;
@@ -455,11 +392,6 @@ pptp_inbound_pkt(struct sk_buff **pskb,
                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));
@@ -470,54 +402,40 @@ pptp_inbound_pkt(struct sk_buff **pskb,
                break;
 
        case PPTP_WAN_ERROR_NOTIFY:
-               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)
-                       ? pptp_msg_name[msg]:pptp_msg_name[0], msg);
-               break;
+               goto invalid;
        }
 
-
-       if (ip_nat_pptp_hook_inbound)
-               return ip_nat_pptp_hook_inbound(pskb, ct, ctinfo, ctlh,
-                                               pptpReq);
-
+       ip_nat_pptp_inbound = rcu_dereference(ip_nat_pptp_hook_inbound);
+       if (ip_nat_pptp_inbound)
+               return ip_nat_pptp_inbound(pskb, ct, ctinfo, ctlh, pptpReq);
        return NF_ACCEPT;
 
+invalid:
+       DEBUGP("invalid %s: type=%d cid=%u pcid=%u "
+              "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
+              msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0],
+              msg, ntohs(cid), ntohs(pcid),  info->cstate, info->sstate,
+              ntohs(info->pns_call_id), ntohs(info->pac_call_id));
+       return NF_ACCEPT;
 }
 
 static inline int
 pptp_outbound_pkt(struct sk_buff **pskb,
-                 struct tcphdr *tcph,
-                 unsigned int nexthdr_off,
-                 unsigned int datalen,
+                 struct PptpControlHeader *ctlh,
+                 union pptp_ctrl_union *pptpReq,
+                 unsigned int reqlen,
                  struct ip_conntrack *ct,
                  enum ip_conntrack_info ctinfo)
 {
-       struct PptpControlHeader _ctlh, *ctlh;
-       unsigned int reqlen;
-       union pptp_ctrl_union _pptpReq, *pptpReq;
        struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
        u_int16_t msg;
-       __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);
-       pptpReq = skb_header_pointer(*pskb, nexthdr_off, reqlen, &_pptpReq);
-       if (!pptpReq)
-               return NF_ACCEPT;
+       __be16 cid = 0, pcid = 0;
+       typeof(ip_nat_pptp_hook_outbound) ip_nat_pptp_outbound;
 
        msg = ntohs(ctlh->messageType);
        DEBUGP("outbound control message %s\n", pptp_msg_name[msg]);
@@ -525,10 +443,8 @@ pptp_outbound_pkt(struct sk_buff **pskb,
        switch (msg) {
        case PPTP_START_SESSION_REQUEST:
                /* client requests for new control session */
-               if (info->sstate != PPTP_SESSION_NONE) {
-                       DEBUGP("%s but we already have one",
-                               pptp_msg_name[msg]);
-               }
+               if (info->sstate != PPTP_SESSION_NONE)
+                       goto invalid;
                info->sstate = PPTP_SESSION_REQUESTED;
                break;
        case PPTP_STOP_SESSION_REQUEST:
@@ -537,17 +453,9 @@ pptp_outbound_pkt(struct sk_buff **pskb,
                break;
 
        case PPTP_OUT_CALL_REQUEST:
-               if (reqlen < sizeof(_pptpReq.ocreq)) {
-                       DEBUGP("%s: short packet\n", pptp_msg_name[msg]);
-                       break;
-               }
-
                /* client initiating connection to server */
-               if (info->sstate != PPTP_SESSION_CONFIRMED) {
-                       DEBUGP("%s but no session\n",
-                               pptp_msg_name[msg]);
-                       break;
-               }
+               if (info->sstate != PPTP_SESSION_CONFIRMED)
+                       goto invalid;
                info->cstate = PPTP_CALL_OUT_REQ;
                /* track PNS call id */
                cid = pptpReq->ocreq.callID;
@@ -555,65 +463,73 @@ pptp_outbound_pkt(struct sk_buff **pskb,
                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",
-                               pptp_msg_name[msg]);
-                       break;
-               }
-               if (pptpReq->icack.resultCode != PPTP_INCALL_ACCEPT) {
-                       info->cstate = PPTP_CALL_NONE;
-                       break;
-               }
+               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) {
-                       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));
-               /* part two of the three-way handshake */
-               info->cstate = PPTP_CALL_IN_REP;
-               info->pns_call_id = pcid;
+               if (info->pac_call_id != pcid)
+                       goto invalid;
+               DEBUGP("%s, CID=%X PCID=%X\n", pptp_msg_name[msg],
+                      ntohs(cid), ntohs(pcid));
+
+               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) {
-                       DEBUGP("CLEAR_CALL but no session\n");
-                       break;
-               }
+               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:
-               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)?
-                       pptp_msg_name[msg]:pptp_msg_name[0], msg);
-               /* unknown: no need to create GRE masq table entry */
-               break;
+               goto invalid;
        }
 
-       if (ip_nat_pptp_hook_outbound)
-               return ip_nat_pptp_hook_outbound(pskb, ct, ctinfo, ctlh,
-                                                pptpReq);
+       ip_nat_pptp_outbound = rcu_dereference(ip_nat_pptp_hook_outbound);
+       if (ip_nat_pptp_outbound)
+               return ip_nat_pptp_outbound(pskb, ct, ctinfo, ctlh, pptpReq);
+       return NF_ACCEPT;
 
+invalid:
+       DEBUGP("invalid %s: type=%d cid=%u pcid=%u "
+              "cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
+              msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0],
+              msg, ntohs(cid), ntohs(pcid),  info->cstate, info->sstate,
+              ntohs(info->pns_call_id), ntohs(info->pac_call_id));
        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
@@ -621,16 +537,17 @@ conntrack_pptp_help(struct sk_buff **pskb,
                    struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
 
 {
-       struct pptp_pkt_hdr _pptph, *pptph;
-       struct tcphdr _tcph, *tcph;
-       u_int32_t tcplen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4;
-       u_int32_t datalen;
        int dir = CTINFO2DIR(ctinfo);
        struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info;
-       unsigned int nexthdr_off;
-
+       struct tcphdr _tcph, *tcph;
+       struct pptp_pkt_hdr _pptph, *pptph;
+       struct PptpControlHeader _ctlh, *ctlh;
+       union pptp_ctrl_union _pptpReq, *pptpReq;
+       unsigned int tcplen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4;
+       unsigned int datalen, reqlen, nexthdr_off;
        int oldsstate, oldcstate;
        int ret;
+       u_int16_t msg;
 
        /* don't do any tracking before tcp handshake complete */
        if (ctinfo != IP_CT_ESTABLISHED
@@ -645,15 +562,6 @@ conntrack_pptp_help(struct sk_buff **pskb,
        nexthdr_off += tcph->doff * 4;
        datalen = tcplen - tcph->doff * 4;
 
-       if (tcph->fin || tcph->rst) {
-               DEBUGP("RST/FIN received, timeouting GRE\n");
-               /* can't do this after real newnat */
-               info->cstate = PPTP_CALL_NONE;
-
-               /* untrack this call id, unexpect GRE packets */
-               pptp_destroy_siblings(ct);
-       }
-
        pptph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_pptph), &_pptph);
        if (!pptph) {
                DEBUGP("no full PPTP header, can't track\n");
@@ -669,6 +577,23 @@ conntrack_pptp_help(struct sk_buff **pskb,
                return NF_ACCEPT;
        }
 
+       ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh);
+       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);
+
+       pptpReq = skb_header_pointer(*pskb, nexthdr_off, reqlen, &_pptpReq);
+       if (!pptpReq)
+               return NF_ACCEPT;
+
        oldsstate = info->sstate;
        oldcstate = info->cstate;
 
@@ -678,11 +603,11 @@ conntrack_pptp_help(struct sk_buff **pskb,
         * established from PNS->PAC.  However, RFC makes no guarantee */
        if (dir == IP_CT_DIR_ORIGINAL)
                /* client -> server (PNS -> PAC) */
-               ret = pptp_outbound_pkt(pskb, tcph, nexthdr_off, datalen, ct,
+               ret = pptp_outbound_pkt(pskb, ctlh, pptpReq, reqlen, ct,
                                        ctinfo);
        else
                /* server -> client (PAC -> PNS) */
-               ret = pptp_inbound_pkt(pskb, tcph, nexthdr_off, datalen, ct,
+               ret = pptp_inbound_pkt(pskb, ctlh, pptpReq, reqlen, ct,
                                       ctinfo);
        DEBUGP("sstate: %d->%d, cstate: %d->%d\n",
                oldsstate, info->sstate, oldcstate, info->cstate);
@@ -715,7 +640,8 @@ static struct ip_conntrack_helper pptp = {
                           .protonum = 0xff
                         }
                },
-       .help = conntrack_pptp_help
+       .help = conntrack_pptp_help,
+       .destroy = pptp_destroy_siblings,
 };
 
 extern void ip_ct_proto_gre_fini(void);
This page took 0.039297 seconds and 5 git commands to generate.