On 13:34:22 Nov 22, Stuart Henderson wrote:
> it must look at the control message on TCP/1723 and translate CallID;

Modulate, not translate. :) My terminology.

I am using arc4random() to generate unique callIDs that do not clash.
The callID is always set to zero by PPTP , hence this requirement.

( No more comments about M$ stuff :)

> then it must look at the session packets (GRE/proto 47) and translate
> CallID the same way.

Yes and maintain a mapping.

This is far more difficult than it first appears. You can see the diff
for what all needs to be done.

> 
> the parts handling control messages probably belong in userland and
> they can add translation rules to an anchor like ftp-proxy does, but
> that would need a change to PF so that you can tell it to translate
> CallID for GRE packets (like you can tell it to translate port for
> TCP/UDP).
> 
> http://blogs.isaserver.org/pouseele/2007/06/17/multiple-pptp-vpn-clients-behind-a-nat-device/

I think though it takes a lot of clever programming and even 
smarter design, I have a problem with maintaining the table in kernel. I
got it working perfectly a long time ago ( roughly a year ago) and I can
send the working diff right away if you want.

I am sure Henning is not going to like it. :)

Whether it is small or not is a matter of taste but if I were to do it
correctly I will do it the proxy rdr way.

The problem however with that approach is that there is a huge overhead
in passing packets between kernel to userland and back.

Here is the diff attached. If you like it commit it. :)

And bear in mind that I developed it against old code, so you might have
to do some tweaks.

If not I am more than willing to do it the right way.

Let me know your choice.

regards,
Girish
Index: pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.242
diff -c -r1.242 pfvar.h
*** pfvar.h     13 Dec 2006 05:10:15 -0000      1.242
--- pfvar.h     12 Mar 2007 09:18:49 -0000
***************
*** 2,7 ****
--- 2,8 ----
  
  /*
   * Copyright (c) 2001 Daniel Hartmeier
+  * Copyright (c) 2007 Girish Venkatachalam
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
***************
*** 936,941 ****
--- 937,943 ----
                struct tcphdr           *tcp;
                struct udphdr           *udp;
                struct icmp             *icmp;
+               struct gre_h             *gre;
  #ifdef INET6
                struct icmp6_hdr        *icmp6;
  #endif /* INET6 */
***************
*** 958,963 ****
--- 960,970 ----
        sa_family_t      af;
        u_int8_t         proto;
        u_int8_t         tos;
+       u_int16_t        mycallid;      /* PPTP lan call id */ 
+       u_int16_t        peercallid;    /* PPTP remote call id */ 
+       struct pfpptp_head *pptph;
+                                        
+ 
  };
  
  /* flags for RDR options */
***************
*** 1351,1356 ****
--- 1358,1372 ----
        int      pfiio_size;
        int      pfiio_nzero;
        int      pfiio_flags;
+ };
+ 
+ 
+ enum { PF_PPTP_MYID, PF_PPTP_PEERID };
+ 
+ struct pfpptp_call {
+       SLIST_ENTRY(pfpptp_call) next_call;
+       u_int16_t myid;
+       u_int16_t peerid;
  };
  
  
Index: pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.523
diff -c -r1.523 pf.c
*** pf.c        22 Dec 2006 13:24:52 -0000      1.523
--- pf.c        12 Mar 2007 09:18:01 -0000
***************
*** 3,8 ****
--- 3,9 ----
  /*
   * Copyright (c) 2001 Daniel Hartmeier
   * Copyright (c) 2002,2003 Henning Brauer
+  * Copyright (c) 2007, Girish Venkatachalam
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
***************
*** 72,77 ****
--- 73,79 ----
  #include <netinet/icmp_var.h>
  #include <netinet/if_ether.h>
  
+ #include <net/if_gre.h>
  #include <dev/rndvar.h>
  #include <net/pfvar.h>
  #include <net/if_pflog.h>
***************
*** 105,110 ****
--- 107,114 ----
  int                    altqs_inactive_open;
  u_int32_t              ticket_pabuf;
  
+ SLIST_HEAD(pfpptp_head,pfpptp_call) pf_pptph;
+ 
  struct pf_anchor_stackframe {
        struct pf_ruleset                       *rs;
        struct pf_rule                          *r;
***************
*** 163,168 ****
--- 167,176 ----
                            int, struct pfi_kif *, struct mbuf *, int,
                            void *, struct pf_pdesc *, struct pf_rule **,
                            struct pf_ruleset **, struct ifqueue *);
+ int                      pf_test_gre_pptp(struct pf_rule **, struct pf_state 
**,
+                           int , struct pfi_kif *, struct mbuf *, int,
+                           void *, struct pf_pdesc *, struct pf_rule **,
+                           struct pf_ruleset **, struct ifqueue *);
  int                    pf_test_other(struct pf_rule **, struct pf_state **,
                            int, struct pfi_kif *, struct mbuf *, int, void *,
                            struct pf_pdesc *, struct pf_rule **,
***************
*** 180,185 ****
--- 188,196 ----
  int                    pf_test_state_icmp(struct pf_state **, int,
                            struct pfi_kif *, struct mbuf *, int,
                            void *, struct pf_pdesc *, u_short *);
+ int                    pf_test_state_gre_pptp(struct pf_state **,
+                           struct mbuf *,int, int,
+                           struct pfi_kif *, struct pf_pdesc *pd);
  int                    pf_test_state_other(struct pf_state **, int,
                            struct pfi_kif *, struct pf_pdesc *);
  int                    pf_match_tag(struct mbuf *, struct pf_rule *,
***************
*** 219,224 ****
--- 230,241 ----
                            struct pf_state_cmp *, u_int8_t);
  int                    pf_src_connlimit(struct pf_state **);
  int                    pf_check_congestion(struct ifqueue *);
+ int                      pf_test(int dir, struct ifnet *ifp, 
+                           struct mbuf **m0, struct ether_header *eh);
+ int                   pf_delete_pptp_node(struct pf_pdesc *,u_int16_t ,int) ;
+ u_int16_t             pf_find_pptp_id(struct pf_pdesc *,u_int16_t,int );
+ int                   pf_append_pptp_node(struct pf_pdesc 
*,u_int16_t,u_int16_t); 
+ void pf_clear_pptp_state(struct pf_pdesc *) ;
  
  extern struct pool pfr_ktable_pl;
  extern struct pool pfr_kentry_pl;
***************
*** 496,502 ****
        return (0);
  }
  
- #ifdef INET6
  void
  pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af)
  {
--- 513,518 ----
***************
*** 506,511 ****
--- 522,528 ----
                dst->addr32[0] = src->addr32[0];
                break;
  #endif /* INET */
+ #ifdef INET6
        case AF_INET6:
                dst->addr32[0] = src->addr32[0];
                dst->addr32[1] = src->addr32[1];
***************
*** 946,951 ****
--- 963,969 ----
                rw_exit_write(&pf_consistency_lock);
  }
  
+ 
  void
  pf_src_tree_remove_state(struct pf_state *s)
  {
***************
*** 1183,1188 ****
--- 1201,1209 ----
        case IPPROTO_ICMP:
                printf("ICMP ");
                break;
+       case IPPROTO_GRE:
+               printf("GRE ");
+               break;
        case IPPROTO_ICMPV6:
                printf("ICMPV6 ");
                break;
***************
*** 2273,2280 ****
  int
  pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r,
      struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport,
!     struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high,
!     struct pf_src_node **sn)
  {
        struct pf_state_cmp     key;
        struct pf_addr          init_addr;
--- 2294,2301 ----
  int
  pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r,
      struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport,
!     struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, 
!     u_int16_t high, struct pf_src_node **sn)
  {
        struct pf_state_cmp     key;
        struct pf_addr          init_addr;
***************
*** 2301,2307 ****
                 * similar 2 portloop in in_pcbbind
                 */
                if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP ||
!                   proto == IPPROTO_ICMP)) {
                        key.gwy.port = dport;
                        if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL)
                                return (0);
--- 2322,2328 ----
                 * similar 2 portloop in in_pcbbind
                 */
                if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP ||
!                   proto == IPPROTO_ICMP  )) {
                        key.gwy.port = dport;
                        if (pf_find_state_all(&key, PF_EXT_GWY, NULL) == NULL)
                                return (0);
***************
*** 2839,2850 ****
--- 2860,2873 ----
        struct pf_ruleset       *ruleset = NULL;
        struct pf_src_node      *nsn = NULL;
        u_short                  reason;
+ 
        int                      rewrite = 0;
        int                      tag = -1, rtableid = -1;
        u_int16_t                mss = tcp_mssdflt;
        int                      asd = 0;
        int                      match = 0;
  
+ 
        if (pf_check_congestion(ifq)) {
                REASON_SET(&reason, PFRES_CONGEST);
                return (PF_DROP);
***************
*** 3079,3084 ****
--- 3102,3108 ----
                                PF_ACPY(&s->lan.addr, &s->gwy.addr, af);
                                s->lan.port = s->gwy.port;
                        }
+       
                } else {
                        PF_ACPY(&s->lan.addr, daddr, af);
                        s->lan.port = th->th_dport;
***************
*** 3091,3096 ****
--- 3115,3122 ----
                                PF_ACPY(&s->gwy.addr, &s->lan.addr, af);
                                s->gwy.port = s->lan.port;
                        }
+               
+ 
                }
  
                s->src.seqlo = ntohl(th->th_seq);
***************
*** 3800,3805 ****
--- 3826,4089 ----
  }
  
  int
+ pf_test_gre_pptp(struct pf_rule **rm, struct pf_state **sm, int direction,
+     struct pfi_kif *kif, struct mbuf *m, int off, void *h,
+     struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm,
+     struct ifqueue *ifq)
+ {
+       struct pf_rule          *nr = NULL;
+       struct pf_addr          *saddr = pd->src, *daddr = pd->dst;
+       u_int16_t                dport,sport, nport = 0;
+       sa_family_t              af = pd->af;
+       struct pf_rule          *r, *a = NULL;
+       struct pf_ruleset       *ruleset = NULL;
+       struct pf_src_node      *nsn = NULL;
+       u_short                  reason;
+       int                      rewrite = 0;
+       int                      tag = -1, rtableid = -1;
+       int                      asd = 0;
+       int                      match = 0;
+ 
+       if (pf_check_congestion(ifq)) {
+               REASON_SET(&reason, PFRES_CONGEST);
+               return (PF_DROP);
+       }
+ 
+       r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr);
+ 
+       if (direction == PF_OUT) {
+               dport = pd->hdr.gre->callid;
+               nport = pd->mycallid;
+               /* check outgoing packet for BINAT/NAT */
+               if ((nr = pf_get_translation(pd, m, off, PF_OUT, kif, &nsn,
+                   saddr, pd->mycallid, daddr, dport,
+                   &pd->naddr, &nport)) != NULL) {
+                       PF_ACPY(&pd->baddr, saddr, af);
+ 
+                       pf_change_a(&saddr->v4.s_addr, pd->ip_sum,
+                            pd->naddr.v4.s_addr,0);
+                       rewrite++;
+                       if (nr->natpass)
+                               r = NULL;
+                       pd->nat_rule = nr;
+               }
+       } else {
+               sport = pd->peercallid;
+               nport = pd->hdr.gre->callid;
+               /* check incoming packet for BINAT/RDR */
+               if ((nr = pf_get_translation(pd, m, off, PF_IN, kif, &nsn,
+                   saddr, sport, daddr, pd->hdr.gre->callid, &pd->naddr,
+                   &nport)) != NULL) {
+                       PF_ACPY(&pd->baddr, daddr, af);
+                       pf_change_a(&daddr->v4.s_addr, pd->ip_sum,
+                            pd->naddr.v4.s_addr, 0);
+                       rewrite++;
+                       if (nr->natpass)
+                               r = NULL;
+                       pd->nat_rule = nr;
+               }
+       }
+ 
+       while (r != NULL) {
+               r->evaluations++;
+               if (pfi_kif_match(r->kif, kif) == r->ifnot)
+                       r = r->skip[PF_SKIP_IFP].ptr;
+               else if (r->direction && r->direction != direction)
+                       r = r->skip[PF_SKIP_DIR].ptr;
+               else if (r->af && r->af != af)
+                       r = r->skip[PF_SKIP_AF].ptr;
+               else if (r->proto && r->proto != pd->proto)
+                       r = r->skip[PF_SKIP_PROTO].ptr;
+               else if (PF_MISMATCHAW(&r->src.addr, pd->src, af,
+                   r->src.neg, kif))
+                       r = r->skip[PF_SKIP_SRC_ADDR].ptr;
+               else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af,
+                   r->dst.neg, NULL))
+                       r = r->skip[PF_SKIP_DST_ADDR].ptr;
+               else if (r->tos && !(r->tos == pd->tos))
+                       r = TAILQ_NEXT(r, entries);
+               else if (r->rule_flag & PFRULE_FRAGMENT)
+                       r = TAILQ_NEXT(r, entries);
+               else if (r->prob && r->prob <= arc4random())
+                       r = TAILQ_NEXT(r, entries);
+               else if (r->match_tag && !pf_match_tag(m, r, pd->pf_mtag, &tag))
+                       r = TAILQ_NEXT(r, entries);
+               else if (r->os_fingerprint != PF_OSFP_ANY)
+                       r = TAILQ_NEXT(r, entries);
+               else {
+                       if (r->tag)
+                               tag = r->tag;
+                       if (r->rtableid >= 0)
+                               rtableid = r->rtableid;
+                       if (r->anchor == NULL) {
+                               match = 1;
+                               *rm = r;
+                               *am = a;
+                               *rsm = ruleset;
+                               if ((*rm)->quick)
+                                       break;
+                               r = TAILQ_NEXT(r, entries);
+                       } else
+                               pf_step_into_anchor(&asd, &ruleset,
+                                   PF_RULESET_FILTER, &r, &a, &match);
+               }
+               if (r == NULL && pf_step_out_of_anchor(&asd, &ruleset,
+                   PF_RULESET_FILTER, &r, &a, &match))
+                       break;
+       }
+ 
+       r = *rm;
+       a = *am;
+       ruleset = *rsm;
+ 
+       REASON_SET(&reason, PFRES_MATCH);
+ 
+ 
+       if ((r->action == PF_DROP) &&
+           ((r->rule_flag & PFRULE_RETURNICMP) ||
+           (r->rule_flag & PFRULE_RETURN))) {
+               /* undo NAT changes, if they have taken place */
+               if (nr != NULL) {
+                       if (direction == PF_OUT) {
+                               pf_change_a(&saddr->v4.s_addr, pd->ip_sum,
+                                    pd->baddr.v4.s_addr, 0);
+                               rewrite++;
+                       } else {
+                               pf_change_a(&daddr->v4.s_addr,pd->ip_sum,
+                                   pd->baddr.v4.s_addr,0);
+                               rewrite++;
+                       }
+               }
+               if ((af == AF_INET) && r->return_icmp)
+                       pf_send_icmp(m, r->return_icmp >> 8,
+                           r->return_icmp & 255, af, r);
+               else if ((af == AF_INET6) && r->return_icmp6)
+                       pf_send_icmp(m, r->return_icmp6 >> 8,
+                           r->return_icmp6 & 255, af, r);
+       }
+ 
+       if (r->action == PF_DROP)
+               return (PF_DROP);
+ 
+       if (pf_tag_packet(m, pd->pf_mtag, tag, rtableid)) {
+               REASON_SET(&reason, PFRES_MEMORY);
+               return (PF_DROP);
+       }
+ 
+       if (r->keep_state || nr != NULL) {
+               /* create new state */
+               struct pf_state *s = NULL;
+               struct pf_src_node *sn = NULL;
+ 
+               /* check maximums */
+               if (r->max_states && (r->states >= r->max_states)) {
+                       pf_status.lcounters[LCNT_STATES]++;
+                       REASON_SET(&reason, PFRES_MAXSTATES);
+                       goto cleanup;
+               }
+               /* src node for filter rule */
+               if ((r->rule_flag & PFRULE_SRCTRACK ||
+                   r->rpool.opts & PF_POOL_STICKYADDR) &&
+                   pf_insert_src_node(&sn, r, saddr, af) != 0) {
+                       REASON_SET(&reason, PFRES_SRCLIMIT);
+                       goto cleanup;
+               }
+               /* src node for translation rule */
+               if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) &&
+                   ((direction == PF_OUT &&
+                   pf_insert_src_node(&nsn, nr, &pd->baddr, af) != 0) ||
+                   (pf_insert_src_node(&nsn, nr, saddr, af) != 0))) {
+                       REASON_SET(&reason, PFRES_SRCLIMIT);
+                       goto cleanup;
+               }
+               s = pool_get(&pf_state_pl, PR_NOWAIT);
+               if (s == NULL) {
+                       REASON_SET(&reason, PFRES_MEMORY);
+ cleanup:
+                       if (sn != NULL && sn->states == 0 && sn->expire == 0) {
+                               RB_REMOVE(pf_src_tree, &tree_src_tracking, sn);
+                               pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
+                               pf_status.src_nodes--;
+                               pool_put(&pf_src_tree_pl, sn);
+                       }
+                       if (nsn != sn && nsn != NULL && nsn->states == 0 &&
+                           nsn->expire == 0) {
+                               RB_REMOVE(pf_src_tree, &tree_src_tracking, nsn);
+                               pf_status.scounters[SCNT_SRC_NODE_REMOVALS]++;
+                               pf_status.src_nodes--;
+                               pool_put(&pf_src_tree_pl, nsn);
+                       }
+                       return (PF_DROP);
+               }
+               bzero(s, sizeof(*s));
+               s->rule.ptr = r;
+               s->nat_rule.ptr = nr;
+               s->anchor.ptr = a;
+               STATE_INC_COUNTERS(s);
+               s->allow_opts = r->allow_opts;
+               s->log = r->log & PF_LOG_ALL;
+               if (nr != NULL)
+                       s->log |= nr->log & PF_LOG_ALL;
+               s->proto = IPPROTO_GRE;
+               s->direction = direction;
+               s->af = af;
+               if (direction == PF_OUT) {
+                       PF_ACPY(&s->gwy.addr, saddr, af);
+                       s->gwy.port = pd->mycallid;
+                       PF_ACPY(&s->ext.addr, daddr, af);
+                       s->ext.port = pd->hdr.gre->callid;
+                       if (nr != NULL) {
+                               PF_ACPY(&s->lan.addr, &pd->baddr, af);
+                       } else {
+                               PF_ACPY(&s->lan.addr, &s->gwy.addr, af);
+                       }
+                       s->lan.port = s->gwy.port;
+               } else {
+                       PF_ACPY(&s->lan.addr, daddr, af);
+                       s->lan.port = pd->hdr.gre->callid;
+                       PF_ACPY(&s->ext.addr, saddr, af);
+                       s->ext.port = pd->peercallid;
+                       if (nr != NULL) {
+                               PF_ACPY(&s->gwy.addr, &pd->baddr, af);
+                       } else {
+                               PF_ACPY(&s->gwy.addr, &s->lan.addr, af);
+                       }
+                       s->gwy.port = s->lan.port;
+               }
+               s->src.state = PFOTHERS_SINGLE;
+               s->dst.state = PFOTHERS_NO_TRAFFIC;
+               s->creation = time_second;
+               s->expire = time_second;
+               s->timeout = PFTM_OTHER_FIRST_PACKET;
+               pf_set_rt_ifp(s, saddr);
+               if (sn != NULL) {
+                       s->src_node = sn;
+                       s->src_node->states++;
+               }
+               if (nsn != NULL) {
+                       PF_ACPY(&nsn->raddr, &pd->naddr, af);
+                       s->nat_src_node = nsn;
+                       s->nat_src_node->states++;
+               }
+               if (pf_insert_state(BOUND_IFACE(r, kif), s)) {
+                       REASON_SET(&reason, PFRES_STATEINS);
+                       pf_src_tree_remove_state(s);
+                       STATE_DEC_COUNTERS(s);
+                       pool_put(&pf_state_pl, s);
+                       return (PF_DROP);
+               } else
+                       *sm = s;
+               if (tag > 0) {
+                       pf_tag_ref(tag);
+                       s->tag = tag;
+               }
+       }
+       return (PF_PASS);
+ }
+ 
+ 
+ 
+ int
  pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction,
      struct pfi_kif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc 
*pd,
      struct pf_rule **am, struct pf_ruleset **rsm, struct ifqueue *ifq)
***************
*** 4161,4167 ****
--- 4445,4456 ----
        u_int8_t                 sws, dws;
        int                      ackskew;
        int                      copyback = 0;
+       int                      pptp_offset;
        struct pf_state_peer    *src, *dst;
+       struct gre_pptp_h       *pptp;
+       struct ip               *ip;
+       struct pf_addr           gwydst;
+       u_int16_t                ocallid;
  
        key.af = pd->af;
        key.proto = IPPROTO_TCP;
***************
*** 4170,4180 ****
--- 4459,4576 ----
                PF_ACPY(&key.gwy.addr, pd->dst, key.af);
                key.ext.port = th->th_sport;
                key.gwy.port = th->th_dport;
+               /* code added by Girish for pptp callid
+                *  modulation 
+                */
+               if(ntohs(th->th_sport) == 1723) {
+ 
+                       ip = h;
+                       pptp_offset = (th->th_off + ip->ip_hl) * 4;
+                       pptp = (struct gre_pptp_h *)(m->m_data
+                                       +pptp_offset);
+ 
+                       if   (ntohs(pptp->msgtype) == 1)  {
+ 
+                         switch (ntohs(pptp->ctrltype)) {
+ 
+                           case 8:
+                               /* It is Outgoing call
+                                * Reply message
+                                */
+                                ocallid = pptp->peer_callid;
+                                pf_append_pptp_node(pd,
+                                                pptp->peer_callid,
+                                                pptp->callid);
+                                /*
+                                pd->mycallid = pptp->peer_callid;
+                                pd->peercallid = pptp->callid;
+                                */
+                                pptp->peer_callid = 0;
+                                th->th_sum = pf_cksum_fixup(
+                                       th->th_sum, ocallid,
+                                       pptp->peer_callid,0); 
+                                m_copyback(m, off, sizeof(*th),th);
+                               /* Create state for translation 
+                                * in the IN direction
+                                */
+                                 PF_ACPY(&gwydst,pd->dst,pd->af);
+                                 break;
+                           case 12:
+                                /* Call Clear request 
+                                 * Remove state 
+                                 */
+                                 pf_delete_pptp_node(pd,
+                                   pptp->callid,PF_PPTP_PEERID);
+                               break;
+                           case 13:
+                                /* Call Disconnect Notify recd.
+                                 * Remove state 
+                                 */
+                                 pf_delete_pptp_node(pd,
+                                    pptp->callid,PF_PPTP_PEERID);
+ 
+                               break;
+                           default:
+                               break;
+ 
+ 
+                         }
+                       }
+ 
+ 
+               }
+ 
        } else {
                PF_ACPY(&key.lan.addr, pd->src, key.af);
                PF_ACPY(&key.ext.addr, pd->dst, key.af);
                key.lan.port = th->th_sport;
                key.ext.port = th->th_dport;
+               /* code added by Girish for pptp callid
+                *  modulation 
+                */
+               if(ntohs(th->th_dport) == 1723) {
+ 
+                       pptp_offset = th->th_off * 4 + 20;
+                       pptp = (struct gre_pptp_h *)(m->m_data
+                                       +pptp_offset);
+ 
+                       if( (ntohs(pptp->msgtype) == 1) )  {
+                       switch(ntohs(pptp->ctrltype) ) { 
+ 
+                               case 7:
+                       /* It is Outgoing Call
+                        *  Request msg 
+                        */
+                               ocallid = pptp->callid;
+                               pptp->callid = htons(arc4random());
+                               th->th_sum = pf_cksum_fixup(th->th_sum,
+                                       ocallid,pptp->callid,0);
+                               m_copyback(m, off, sizeof(*th), th);
+                               break;
+                          case 12:
+                                /* Call Clear request 
+                                 * Remove state 
+                                 */
+                                 pf_delete_pptp_node(pd,
+                                   pptp->callid,PF_PPTP_MYID);
+                               break;
+                           case 13:
+                                /* Call Disconnect Notify recd.
+                                 * Remove state 
+                                 */
+                                 pf_delete_pptp_node(pd,
+                                    pptp->callid,PF_PPTP_MYID);
+ 
+                               break;
+ 
+                         default:
+                               break;
+                       }
+                       }
+ 
+ 
+               }
+       
        }
  
        STATE_LOOKUP();
***************
*** 4604,4609 ****
--- 5000,5006 ----
                m_copyback(m, off, sizeof(*th), th);
        }
  
+ 
        return (PF_PASS);
  }
  
***************
*** 5246,5251 ****
--- 5643,5756 ----
        }
  }
  
+ 
+ int
+ pf_test_state_gre_pptp(struct pf_state **state,struct mbuf *m,int off,int 
direction, struct pfi_kif *kif, struct pf_pdesc *pd)
+ {
+       struct pf_state_peer    *src, *dst;
+       struct pf_state_cmp      key;
+       static int num_trans;
+       /*struct tcphdr *th = NULL; */
+       u_int16_t callid;
+ 
+       key.af = pd->af;
+       key.proto = pd->proto;
+ 
+       callid = pd->hdr.gre->callid;
+       if (direction == PF_IN) {
+               PF_ACPY(&key.ext.addr, pd->src, key.af);
+               PF_ACPY(&key.gwy.addr, pd->dst, key.af);
+               key.ext.port = pd->peercallid;
+               key.gwy.port = callid;
+       } else {
+               PF_ACPY(&key.lan.addr, pd->src, key.af);
+               PF_ACPY(&key.ext.addr, pd->dst, key.af);
+               key.lan.port = pd->mycallid;
+               key.ext.port = callid;
+       }
+ 
+       STATE_LOOKUP();
+ 
+       if (direction == (*state)->direction) {
+               src = &(*state)->src;
+               dst = &(*state)->dst;
+       } else {
+               src = &(*state)->dst;
+               dst = &(*state)->src;
+       }
+ 
+       /* update states */
+       if (src->state < PFOTHERS_SINGLE)
+               src->state = PFOTHERS_SINGLE;
+       if (dst->state == PFOTHERS_SINGLE)
+               dst->state = PFOTHERS_MULTIPLE;
+ 
+       /* update expire time */
+       (*state)->expire = time_second;
+       if (src->state == PFOTHERS_MULTIPLE && dst->state == PFOTHERS_MULTIPLE)
+               (*state)->timeout = PFTM_OTHER_MULTIPLE;
+       else
+               (*state)->timeout = PFTM_OTHER_SINGLE;
+ 
+       /* translate source/destination address, if necessary */
+       if (STATE_TRANSLATE(*state)) {
+               if (direction == PF_OUT)
+                       switch (pd->af) {
+ #ifdef INET
+                       case AF_INET:
+                               pf_change_a(&pd->src->v4.s_addr,
+                                   pd->ip_sum, (*state)->gwy.addr.v4.s_addr,
+                                   0);
+                               break;
+ #endif /* INET */
+ #ifdef INET6
+                       case AF_INET6:
+                               PF_ACPY(pd->src, &(*state)->gwy.addr, pd->af);
+                               break;
+ #endif /* INET6 */
+                       }
+               else
+                       switch (pd->af) {
+ #ifdef INET
+                       case AF_INET:
+                               pf_change_a(&pd->dst->v4.s_addr,
+                                   pd->ip_sum, (*state)->lan.addr.v4.s_addr,
+                                   0);
+                               /*  Change the callid back to 0
+                                *  since we rewrote it while 
+                                *  the PPTP control protocol
+                                *  was going on...
+                               pd->hdr.gre->callid = (*state)->lan.port;
+                               th = pd->hdr.tcp;
+                               th->th_sum = pf_cksum_fixup(
+                                       th->th_sum, callid,
+                                       pd->hdr.gre->callid,0); 
+                                m_copyback(m, off, sizeof(*th),th);
+                                */
+                               break;
+ #endif /* INET */
+ #ifdef INET6
+                       case AF_INET6:
+                               PF_ACPY(pd->dst, &(*state)->lan.addr, pd->af);
+                               break;
+ #endif /* INET6 */
+                       }
+       }
+ 
+       return (PF_PASS);
+ }
+ 
  int
  pf_test_state_other(struct pf_state **state, int direction, struct pfi_kif 
*kif,
      struct pf_pdesc *pd)
***************
*** 5974,5979 ****
--- 6479,6487 ----
                        log = action != PF_PASS;
                        goto done;
                }
+ 
+               pd.pptph = &pf_pptph;
+ 
                if (dir == PF_IN && pf_check_proto_cksum(m, off,
                    ntohs(h->ip_len) - off, IPPROTO_TCP, AF_INET)) {
                        REASON_SET(&reason, PFRES_PROTCKSUM);
***************
*** 6067,6072 ****
--- 6575,6616 ----
                break;
        }
  
+       case IPPROTO_GRE: {
+               struct gre_h gh;
+ 
+               pd.hdr.gre = &gh;
+               if (!pf_pull_hdr(m, off, &gh, sizeof(gh),
+                   &action, &reason, AF_INET)) {
+                       log = action != PF_PASS;
+                       goto done;
+               }
+ 
+               pd.pptph = &pf_pptph;
+ 
+               if(dir == PF_IN) {
+                       pd.mycallid = pd.hdr.gre->callid;
+                       pd.peercallid = pf_find_pptp_id(&pd,
+                               pd.mycallid,PF_PPTP_PEERID);
+               } else {
+                       pd.peercallid = pd.hdr.gre->callid;
+                       pd.mycallid = pf_find_pptp_id(&pd,
+                               pd.peercallid,PF_PPTP_MYID);
+               }
+                               
+               
+               action = pf_test_state_gre_pptp(&s,m, off, dir, kif, &pd);
+               if (action == PF_PASS) {
+ #if NPFSYNC
+                       pfsync_update_state(s);
+ #endif /* NPFSYNC */
+                       r = s->rule.ptr;
+                       a = s->anchor.ptr;
+                       log = s->log;
+               } else if (s == NULL)
+                       action = pf_test_gre_pptp(&r, &s, dir, kif,
+                           m, off, h, &pd, &a, &ruleset, &ipintrq);
+               break;
+       }
        default:
                action = pf_test_state_other(&s, dir, kif, &pd);
                if (action == PF_PASS) {
***************
*** 6556,6559 ****
--- 7100,7168 ----
                return (1);
        else
                return (0);
+ }
+ 
+ int pf_delete_pptp_node(struct pf_pdesc *pd,u_int16_t id,int type) {
+       struct pfpptp_call *tmp;
+ 
+       SLIST_FOREACH(tmp,pd->pptph,next_call) {
+               if(type == PF_PPTP_MYID) {
+                       if(tmp->myid == id) {
+                               if(tmp == SLIST_FIRST(pd->pptph))
+                                       SLIST_REMOVE_HEAD(pd->pptph,next_call);
+                               else
+                                       
SLIST_REMOVE(pd->pptph,tmp,pfpptp_call,next_call);
+                       }
+ 
+ 
+               } else {
+                       if(tmp->peerid == id) {
+                               if(tmp == SLIST_FIRST(pd->pptph))
+                                       SLIST_REMOVE_HEAD(pd->pptph,next_call);
+                               else
+                                       
SLIST_REMOVE(pd->pptph,tmp,pfpptp_call,next_call);
+ 
+                       }
+               }
+       }
+       return 0;
+ }
+ 
+ u_int16_t pf_find_pptp_id(struct pf_pdesc *pd,u_int16_t id,int type ) {
+       struct pfpptp_call *tmp;
+ 
+       SLIST_FOREACH(tmp,pd->pptph,next_call) {
+               if(type == PF_PPTP_MYID) {
+                       if(tmp->peerid == id)
+                               return tmp->myid;
+               } else {
+                       if(tmp->myid == id)
+                               return tmp->peerid;
+               }
+ 
+       }
+       return 0;
+ }
+ 
+ int pf_append_pptp_node(struct pf_pdesc *pd,u_int16_t myid,u_int16_t peerid) {
+       struct pfpptp_call *n;
+       n = (struct pfpptp_call *)malloc(sizeof(struct 
pfpptp_call),M_TEMP,M_WAITOK);
+       n->myid = myid;
+       n->peerid = peerid;
+ 
+       if(SLIST_EMPTY(pd->pptph)) {
+               SLIST_INIT(pd->pptph);
+               SLIST_INSERT_HEAD(pd->pptph,n,next_call);
+       }
+       else {
+               SLIST_INSERT_AFTER(SLIST_FIRST(pd->pptph),n,next_call );
+       }
+ 
+               return 0;
+ 
+ }
+ 
+ void pf_clear_pptp_state(struct pf_pdesc *pd) {
+       while(!SLIST_EMPTY(pd->pptph))
+               SLIST_REMOVE_HEAD(pd->pptph,next_call);
  }
Index: if_gre.h
===================================================================
RCS file: /cvs/src/sys/net/if_gre.h,v
retrieving revision 1.10
diff -c -r1.10 if_gre.h
*** if_gre.h    14 May 2005 19:24:23 -0000      1.10
--- if_gre.h    12 Mar 2007 09:18:31 -0000
***************
*** 3,8 ****
--- 3,9 ----
  
  /*
   * Copyright (c) 1998 The NetBSD Foundation, Inc.
+  * Copyright (c) 2007 Girish Venkatachalam
   * All rights reserved
   *
   * This code is derived from software contributed to The NetBSD Foundation
***************
*** 52,62 ****
--- 53,76 ----
        u_char g_proto;         /* protocol of encapsulator */
  };    
  
+ struct gre_pptp_h {
+       u_int16_t len;
+       u_int16_t msgtype;
+       u_int32_t magic;
+       u_int16_t ctrltype;
+       u_int16_t res;
+       u_int16_t callid;
+       u_int16_t peer_callid;
+ };
+ 
  
  struct gre_h {
        u_int16_t flags;        /* GRE flags */
        u_int16_t ptype;        /* protocol type of payload typically 
                                 Ether protocol type*/
+       /* These two are the key field for PPTP */
+       u_int16_t len;
+       u_int16_t callid;
  /* 
   *  from here on: fields are optional, presence indicated by flags 
   *

Reply via email to