Hello. I have developed the driver for Point-to-Point Tunneling Protocol (PPTP). This driver works in pair with plugin for pppd. It is enough to use pppd's plugin for start of the client. Also there is a server (based on pptpd) which uses this driver. By results of my testing the speed of transfer has increased on >50% and cpu usage has decreased on >50% in comparison with original pptpd and pptpclient. I have published the project on http://accel-pptp.sourceforge.net/ diff -uprN linux-2.6.16.orig/drivers/net/Kconfig linux-2.6.16.pptp/drivers/net/Kconfig --- linux-2.6.16.orig/drivers/net/Kconfig 2006-06-18 00:51:35.000000000 +0400 +++ linux-2.6.16.pptp/drivers/net/Kconfig 2006-06-18 01:10:19.000000000 +0400 @@ -2603,6 +2603,15 @@ config PPPOE which contains instruction on how to use this driver (under the heading "Kernel mode PPPoE"). +config PPTP + tristate "PPTP (Point-to-Point Tunneling Protocol) (EXPERIMENTAL)" + depends on EXPERIMENTAL && PPP + help + Support for Microsoft Point-to-Point Tunneling Protocol. + + See http://accel-pptp.sourceforge.net/ for information on + configuring PPTP clients and servers to utilize this driver. + config PPPOATM tristate "PPP over ATM" depends on ATM && PPP diff -uprN linux-2.6.16.orig/drivers/net/Makefile linux-2.6.16.pptp/drivers/net/Makefile --- linux-2.6.16.orig/drivers/net/Makefile 2006-06-18 00:51:34.000000000 +0400 +++ linux-2.6.16.pptp/drivers/net/Makefile 2006-06-18 00:57:02.000000000 +0400 @@ -118,6 +118,7 @@ obj-$(CONFIG_PPP_DEFLATE) += ppp_deflate obj-$(CONFIG_PPP_BSDCOMP) += bsd_comp.o obj-$(CONFIG_PPP_MPPE) += ppp_mppe.o obj-$(CONFIG_PPPOE) += pppox.o pppoe.o +obj-$(CONFIG_PPTP) += pppox.o pptp.o obj-$(CONFIG_SLIP) += slip.o ifeq ($(CONFIG_SLIP_COMPRESSED),y) diff -uprN linux-2.6.16.orig/drivers/net/pptp.c linux-2.6.16.pptp/drivers/net/pptp.c --- linux-2.6.16.orig/drivers/net/pptp.c 1970-01-01 03:00:00.000000000 +0300 +++ linux-2.6.16.pptp/drivers/net/pptp.c 2006-06-18 01:30:40.000000000 +0400 @@ -0,0 +1,928 @@ +/* + * Point-to-Point Tunneling Protocol for Linux + * + * Authors: Kozlov D. (xeb@xxxxxxx) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include <linux/string.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/net.h> +#include <linux/skbuff.h> +#include <linux/init.h> +#include <linux/ppp_channel.h> +#include <linux/ppp_defs.h> +#include <linux/if_ppp.h> +#include <linux/if_pppox.h> +#include <linux/notifier.h> +#include <linux/file.h> +#include <linux/proc_fs.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/netfilter.h> +#include <linux/netfilter_ipv4.h> +#include <linux/workqueue.h> +#include <linux/version.h> + +#include <net/sock.h> +#include <net/protocol.h> +#include <net/ip.h> +#include <net/icmp.h> +#include <net/route.h> + +#include <asm/uaccess.h> + +#define PPTP_DRIVER_VERSION "0.7" + +MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol for Linux"); +MODULE_AUTHOR("Kozlov D. (xeb@xxxxxxx)"); +MODULE_LICENSE("GPL"); + +static int log_level=0; +static int min_window=5; +static int max_window=100; +module_param(min_window,int,5); +MODULE_PARM_DESC(min_window,"Minimum sliding window size (default=3)"); +module_param(max_window,int,100); +MODULE_PARM_DESC(max_window,"Maximum sliding window size (default=100)"); +module_param(log_level,int,0); + +static struct proc_dir_entry* proc_dir; + +#define HASH_SIZE 16 +#define HASH(addr) ((addr^(addr>>4))&0xF) +static DEFINE_RWLOCK(chan_lock); +static struct pppox_sock *chans[HASH_SIZE]; + +static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb); +static int read_proc(char *page, char **start, off_t off,int count, + int *eof, void *data); +static int __pptp_rcv(struct pppox_sock *po,struct sk_buff *skb,int new); + +static struct ppp_channel_ops pptp_chan_ops= { + .start_xmit = pptp_xmit, +}; + +/* gre header structure: -------------------------------------------- */ + +#define PPTP_GRE_PROTO 0x880B +#define PPTP_GRE_VER 0x1 + +#define PPTP_GRE_FLAG_C 0x80 +#define PPTP_GRE_FLAG_R 0x40 +#define PPTP_GRE_FLAG_K 0x20 +#define PPTP_GRE_FLAG_S 0x10 +#define PPTP_GRE_FLAG_A 0x80 + +#define PPTP_GRE_IS_C(f) ((f)&PPTP_GRE_FLAG_C) +#define PPTP_GRE_IS_R(f) ((f)&PPTP_GRE_FLAG_R) +#define PPTP_GRE_IS_K(f) ((f)&PPTP_GRE_FLAG_K) +#define PPTP_GRE_IS_S(f) ((f)&PPTP_GRE_FLAG_S) +#define PPTP_GRE_IS_A(f) ((f)&PPTP_GRE_FLAG_A) + +struct pptp_gre_header { + u_int8_t flags; /* bitfield */ + u_int8_t ver; /* should be PPTP_GRE_VER (enhanced GRE) */ + u_int16_t protocol; /* should be PPTP_GRE_PROTO (ppp-encaps) */ + u_int16_t payload_len; /* size of ppp payload, not inc. gre header */ + u_int16_t call_id; /* peer's call_id for this session */ + u_int32_t seq; /* sequence number. Present if S==1 */ + u_int32_t ack; /* seq number of highest packet recieved by */ + /* sender in this session */ +}; + +struct gre_statistics { + /* statistics for GRE receive */ + unsigned int rx_accepted; // data packet accepted + unsigned int rx_lost; // data packet did not arrive before timeout + unsigned int rx_underwin; // data packet was under window (arrived too late + // or duplicate packet) + unsigned int rx_buffered; // data packet arrived earlier than expected, + // packet(s) before it were lost or reordered + unsigned int rx_errors; // OS error on receive + unsigned int rx_truncated; // truncated packet + unsigned int rx_invalid; // wrong protocol or invalid flags + unsigned int rx_acks; // acknowledgement only + + /* statistics for GRE transmit */ + unsigned int tx_sent; // data packet sent + unsigned int tx_failed; // + unsigned int tx_acks; // sent packet with just ACK + + __u32 pt_seq; + struct timeval pt_time; + unsigned int rtt; +}; + +static struct pppox_sock * lookup_chan(__u16 call_id) +{ + struct pppox_sock *po; + read_lock_bh(&chan_lock); + for(po=chans[HASH(call_id)]; po; po=po->next) + if (po->proto.pptp.src_addr.call_id==call_id) + break; + read_unlock_bh(&chan_lock); + return po; +} + +static void add_chan(struct pppox_sock *po) +{ + write_lock_bh(&chan_lock); + po->next=chans[HASH(po->proto.pptp.src_addr.call_id)]; + chans[HASH(po->proto.pptp.src_addr.call_id)]=po; + write_unlock_bh(&chan_lock); +} + +static void add_free_chan(struct pppox_sock *po) +{ + __u16 call_id=1; + struct pppox_sock *p; + + write_lock_bh(&chan_lock); + for(call_id=1; call_id<65535; call_id++) { + for(p=chans[HASH(call_id)]; p; p=p->next) + if (p->proto.pptp.src_addr.call_id==call_id) + break; + if (!p){ + po->proto.pptp.src_addr.call_id=call_id; + po->next=chans[HASH(call_id)]; + chans[HASH(call_id)]=po; + break; + } + } + write_unlock_bh(&chan_lock); +} + +static void del_chan(struct pppox_sock *po) +{ + struct pppox_sock *p1,*p2; + write_lock_bh(&chan_lock); + for(p2=NULL,p1=chans[HASH(po->proto.pptp.src_addr.call_id)]; p1 && p1!=po; + p2=p1,p1=p1->next); + if (p2) p2->next=p1->next; + else chans[HASH(po->proto.pptp.src_addr.call_id)]=p1->next; + write_unlock_bh(&chan_lock); +} + +static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + struct sock *sk = (struct sock *) chan->private; + struct pppox_sock *po = pppox_sk(sk); + struct pptp_opt *opt=&po->proto.pptp; + struct pptp_gre_header *hdr; + unsigned int header_len=sizeof(*hdr); + int len=skb?skb->len:0; + int err=0; + + struct rtable *rt; /* Route to the other host */ + struct net_device *tdev; /* Device to other host */ + struct iphdr *iph; /* Our new IP header */ + int max_headroom; /* The extra header space needed */ + + + if (skb && opt->seq_sent-opt->ack_recv>opt->window){ + opt->pause=1; + return 0; + } + + { + struct flowi fl = { .oif = 0, + .nl_u = { .ip4_u = + { .daddr = opt->dst_addr.sin_addr.s_addr, + .saddr = opt->src_addr.sin_addr.s_addr, + .tos = RT_TOS(0) } }, + .proto = IPPROTO_GRE }; + if ((err=ip_route_output_key(&rt, &fl))) { + goto tx_error; + } + } + tdev = rt->u.dst.dev; + + max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(*iph)+sizeof(*hdr)+2; + + if (!skb){ + skb=dev_alloc_skb(max_headroom); + skb_reserve(skb,max_headroom-skb_headroom(skb)); + }else if (skb_headroom(skb) < max_headroom || + skb_cloned(skb) || skb_shared(skb)) { + struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); + if (!new_skb) { + ip_rt_put(rt); + goto tx_error; + } + if (skb->sk) + skb_set_owner_w(new_skb, skb->sk); + dev_kfree_skb(skb); + skb = new_skb; + } + + if (skb->len){ + int islcp; + unsigned char *data=skb->data; + islcp=((data[0] << 8) + data[1])== PPP_LCP && 1 <= data[2] && data[2] <= 7; + if (islcp) { + data=skb_push(skb,2); + data[0]=0xff; + data[1]=0x03; + } + } + len=skb->len; + + if (len==0) header_len-=sizeof(hdr->seq); + if (opt->ack_sent == opt->seq_recv) header_len-=sizeof(hdr->ack); + + skb->nh.raw = skb_push(skb, sizeof(*iph)+header_len); + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); + IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | + IPSKB_REROUTED); + dst_release(skb->dst); + skb->dst = &rt->u.dst; + + /* + * Push down and install the IP header. + */ + + iph = skb->nh.iph; + iph->version = 4; + iph->ihl = sizeof(struct iphdr) >> 2; + iph->frag_off = 0;//df; + iph->protocol = IPPROTO_GRE; + iph->tos = 0; + iph->daddr = rt->rt_dst; + iph->saddr = rt->rt_src; + iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT); + + hdr=(struct pptp_gre_header *)(iph+1); + skb->h.raw = (char*)hdr; + + hdr->flags = PPTP_GRE_FLAG_K; + hdr->ver = PPTP_GRE_VER; + hdr->protocol = htons(PPTP_GRE_PROTO); + hdr->call_id = htons(opt->dst_addr.call_id); + + if (!len){ + hdr->payload_len = 0; + hdr->ver |= PPTP_GRE_FLAG_A; + /* ack is in odd place because S == 0 */ + hdr->seq = htonl(opt->seq_recv); + opt->ack_sent = opt->seq_recv; + opt->stat->tx_acks++; + }else { + if (!opt->seq_sent){ + char unit[10]; + sprintf(unit,"ppp%i",ppp_unit_number(&po->chan)); + create_proc_read_entry(unit,0,proc_dir,read_proc,po); + } + + hdr->flags |= PPTP_GRE_FLAG_S; + hdr->seq = htonl(opt->seq_sent++); + if (log_level>=2) + printk("PPTP: send packet: seq=%i",opt->seq_sent); + if (opt->ack_sent != opt->seq_recv) { + /* send ack with this message */ + hdr->ver |= PPTP_GRE_FLAG_A; + hdr->ack = htonl(opt->seq_recv); + opt->ack_sent = opt->seq_recv; + if (log_level>=2) + printk(" ack=%i",opt->seq_recv); + } + hdr->payload_len = htons(len); + if (log_level>=2) + printk("\n"); + } + + nf_reset(skb); + + skb->ip_summed = CHECKSUM_NONE; + iph->tot_len = htons(skb->len); + ip_select_ident(iph, &rt->u.dst, NULL); + ip_send_check(iph); + + err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, dst_output); + if (err == NET_XMIT_SUCCESS || err == NET_XMIT_CN) { + opt->stat->tx_sent++; + if (!opt->stat->pt_seq){ + opt->stat->pt_seq = opt->seq_sent; + do_gettimeofday(&opt->stat->pt_time); + } + }else goto tx_error; + + return 1; + +tx_error: + opt->stat->tx_failed++; + if (!len) dev_kfree_skb(skb); + return 1; +} + +static void ack_work(struct pppox_sock *po) +{ + struct pptp_opt *opt=&po->proto.pptp; + if (opt->ack_sent != opt->seq_recv) + pptp_xmit(&po->chan,0); +} + +static int get_seq(struct sk_buff *skb) +{ + struct iphdr *iph; + u8 *payload; + struct pptp_gre_header *header; + + iph = (struct iphdr*)skb->data; + payload = skb->data + (iph->ihl << 2); + header = (struct pptp_gre_header *)(payload); + + return ntohl(header->seq); +} +static void buf_work(struct pppox_sock *po) +{ + struct timeval tv1,tv2; + struct sk_buff *skb; + struct pptp_opt *opt=&po->proto.pptp; + unsigned int t; + + do_gettimeofday(&tv1); + spin_lock_bh(&opt->skb_buf_lock); + while((skb=skb_dequeue(&opt->skb_buf))){ + if (!__pptp_rcv(po,skb,0)){ + skb_get_timestamp(skb,&tv2); + t=(tv1.tv_sec-tv2.tv_sec)*1000000+(tv1.tv_usec-tv2.tv_usec); + if (t<opt->stat->rtt){ + skb_queue_head(&opt->skb_buf,skb); + schedule_delayed_work(&opt->buf_work,t/100*HZ/10000); + goto exit; + } + t=get_seq(skb)-1; + opt->stat->rx_lost+=t-opt->seq_recv; + opt->seq_recv=t; + __pptp_rcv(po,skb,0); + } + } +exit: + spin_unlock_bh(&opt->skb_buf_lock); +} + + +#define MISSING_WINDOW 20 +#define WRAPPED( curseq, lastseq) \ + ((((curseq) & 0xffffff00) == 0) && \ + (((lastseq) & 0xffffff00 ) == 0xffffff00)) +static int __pptp_rcv(struct pppox_sock *po,struct sk_buff *skb,int new) +{ + struct pptp_opt *opt=&po->proto.pptp; + int headersize,payload_len,seq; + __u8 *payload; + struct pptp_gre_header *header; + + header = (struct pptp_gre_header *)(skb->data); + + if (new){ + /* test if acknowledgement present */ + if (PPTP_GRE_IS_A(header->ver)){ + __u32 ack = (PPTP_GRE_IS_S(header->flags))? + header->ack:header->seq; /* ack in different place if S = 0 */ + ack = ntohl( ack); + if (ack > opt->ack_recv) opt->ack_recv = ack; + /* also handle sequence number wrap-around */ + if (WRAPPED(ack,opt->ack_recv)) opt->ack_recv = ack; + if (opt->stat->pt_seq && opt->ack_recv > opt->stat->pt_seq){ + struct timeval tv; + unsigned int rtt; + do_gettimeofday(&tv); + rtt = (tv.tv_sec - opt->stat->pt_time.tv_sec)*1000000+ + tv.tv_usec-opt->stat->pt_time.tv_usec; + opt->stat->rtt = (opt->stat->rtt + rtt) / 2; + if (opt->stat->rtt>opt->timeout) opt->stat->rtt=opt->timeout; + opt->stat->pt_seq=0; + } + if (opt->pause){ + opt->pause=0; + ppp_output_wakeup(&po->chan); + } + } + + /* test if payload present */ + if (!PPTP_GRE_IS_S(header->flags)){ + opt->stat->rx_acks++; + goto drop; + } + } + + headersize = sizeof(*header); + payload_len = ntohs(header->payload_len); + seq = ntohl(header->seq); + + /* no ack present? */ + if (!PPTP_GRE_IS_A(header->ver)) headersize -= sizeof(header->ack); + /* check for incomplete packet (length smaller than expected) */ + if (skb->len- headersize < payload_len){ + if (log_level>=1) + printk("PPTP: discarding truncated packet (expected %d, got %d bytes)\n", + payload_len, skb->len- headersize); + opt->stat->rx_truncated++; + goto drop; + } + + payload=skb->data+headersize; + /* check for expected sequence number */ + if ((seq == opt->seq_recv + 1) || (!opt->timeout && + (seq > opt->seq_recv + 1 || WRAPPED(seq, opt->seq_recv)))){ + if ( log_level >= 2 ) + printk("PPTP: accepting packet %d size=%i (%02x %02x %02x %02x %02x %02x)\n", seq,payload_len, + *(payload +0), + *(payload +1), + *(payload +2), + *(payload +3), + *(payload +4), + *(payload +5)); + opt->stat->rx_accepted++; + opt->stat->rx_lost+=seq-(opt->seq_recv + 1); + opt->seq_recv = seq; + schedule_work(&opt->ack_work); + + skb_pull(skb,headersize); + + if (payload[0] == PPP_ALLSTATIONS && payload[1] == PPP_UI){ + /* chop off address/control */ + if (skb->len < 3) + goto drop; + skb_pull(skb,2); + } + + if ((*skb->data) & 1){ + /* protocol is compressed */ + skb_push(skb, 1)[0] = 0; + } + + ppp_input(&po->chan,skb); + + return 1; + /* out of order, check if the number is too low and discard the packet. + * (handle sequence number wrap-around, and try to do it right) */ + }else if ( seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq) ){ + if ( log_level >= 1) + printk("PPTP: discarding duplicate or old packet %d (expecting %d)\n", + seq, opt->seq_recv + 1); + opt->stat->rx_underwin++; + /* sequence number too high, is it reasonably close? */ + }else if ( seq < opt->seq_recv + MISSING_WINDOW || + WRAPPED(seq, opt->seq_recv + MISSING_WINDOW) ){ + opt->stat->rx_buffered++; + if ( log_level >= 1 && new ) + printk("PPTP: buffering packet %d (expecting %d, lost or reordered)\n", + seq, opt->seq_recv+1); + return 0; + /* no, packet must be discarded */ + }else{ + if ( log_level >= 1 ) + printk("PPTP: discarding bogus packet %d (expecting %d)\n", + seq, opt->seq_recv + 1); + } +drop: + kfree_skb(skb); + return -1; +} + + +static int pptp_rcv(struct sk_buff *skb) +{ + struct pptp_gre_header *header; + struct pppox_sock *po; + struct pptp_opt *opt; + + if (!pskb_may_pull(skb, 12)) + goto drop; + + header = (struct pptp_gre_header *)skb->data; + + if ( /* version should be 1 */ + ((header->ver & 0x7F) != PPTP_GRE_VER) || + /* PPTP-GRE protocol for PPTP */ + (ntohs(header->protocol) != PPTP_GRE_PROTO)|| + /* flag C should be clear */ + PPTP_GRE_IS_C(header->flags) || + /* flag R should be clear */ + PPTP_GRE_IS_R(header->flags) || + /* flag K should be set */ + (!PPTP_GRE_IS_K(header->flags)) || + /* routing and recursion ctrl = 0 */ + ((header->flags&0xF) != 0)){ + /* if invalid, discard this packet */ + if (log_level>=1) + printk("PPTP: Discarding GRE: %X %X %X %X %X %X\n", + header->ver&0x7F, ntohs(header->protocol), + PPTP_GRE_IS_C(header->flags), + PPTP_GRE_IS_R(header->flags), + PPTP_GRE_IS_K(header->flags), + header->flags & 0xF); + goto drop; + } + + dst_release(skb->dst); + skb->dst = NULL; + nf_reset(skb); + + if ((po=lookup_chan(htons(header->call_id)))) { + if (!(sk_pppox(po)->sk_state&PPPOX_BOUND)) + goto drop; + if (__pptp_rcv(po,skb,1)) + buf_work(po); + else{ + struct timeval tv; + do_gettimeofday(&tv); + skb_set_timestamp(skb,&tv); + opt=&po->proto.pptp; + spin_lock(&opt->skb_buf_lock); + skb_queue_tail(&opt->skb_buf, skb); + spin_unlock(&opt->skb_buf_lock); + schedule_delayed_work(&opt->buf_work,opt->stat->rtt/100*HZ/10000); + } + goto out; + }else icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0); + +drop: + kfree_skb(skb); +out: + return 0; +} + + + +static int proc_output (struct pppox_sock *po,char *buf) +{ + struct gre_statistics *stat=po->proto.pptp.stat; + char *p=buf; + p+=sprintf(p,"rx accepted = %d\n",stat->rx_accepted); + p+=sprintf(p,"rx lost = %d\n",stat->rx_lost); + p+=sprintf(p,"rx under win = %d\n",stat->rx_underwin); + p+=sprintf(p,"rx buffered = %d\n",stat->rx_buffered); + p+=sprintf(p,"rx invalid = %d\n",stat->rx_invalid); + p+=sprintf(p,"rx acks = %d\n",stat->rx_acks); + p+=sprintf(p,"tx sent = %d\n",stat->tx_sent); + p+=sprintf(p,"tx failed = %d\n",stat->tx_failed); + p+=sprintf(p,"tx acks = %d\n",stat->tx_acks); + + return p-buf; +} +static int read_proc(char *page, char **start, off_t off,int count, int *eof, void *data) +{ + struct pppox_sock *po = data; + int len = proc_output (po,page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int pptp_bind(struct socket *sock,struct sockaddr *uservaddr,int sockaddr_len) +{ + struct sock *sk = sock->sk; + struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr; + struct pppox_sock *po = pppox_sk(sk); + struct pptp_opt *opt=&po->proto.pptp; + int error=0; + + if (log_level>=1) + printk("PPTP: bind: addr=%X call_id=%i\n",sp->sa_addr.pptp.sin_addr.s_addr, + sp->sa_addr.pptp.call_id); + lock_sock(sk); + + opt->src_addr=sp->sa_addr.pptp; + if (sp->sa_addr.pptp.call_id){ + if (lookup_chan(sp->sa_addr.pptp.call_id)){ + error=-EBUSY; + goto end; + } + add_chan(po); + }else{ + add_free_chan(po); + if (!opt->src_addr.call_id) + error=-EBUSY; + if (log_level>=1) + printk("PPTP: using call_id %i\n",opt->src_addr.call_id); + } + + end: + release_sock(sk); + return error; +} + +static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr, + int sockaddr_len, int flags) +{ + struct sock *sk = sock->sk; + struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr; + struct pppox_sock *po = pppox_sk(sk); + struct pptp_opt *opt=&po->proto.pptp; + int error=0; + + if (log_level>=1) + printk("PPTP: connect: addr=%X call_id=%i\n", + sp->sa_addr.pptp.sin_addr.s_addr,sp->sa_addr.pptp.call_id); + + lock_sock(sk); + + if (sp->sa_protocol != PX_PROTO_PPTP){ + error = -EINVAL; + goto end; + } + + /* Check for already bound sockets */ + if (sk->sk_state & PPPOX_CONNECTED){ + error = -EBUSY; + goto end; + } + + /* Check for already disconnected sockets, on attempts to disconnect */ + if (sk->sk_state & PPPOX_DEAD){ + error = -EALREADY; + goto end; + } + + if (!opt->src_addr.sin_addr.s_addr || !sp->sa_addr.pptp.sin_addr.s_addr){ + error = -EINVAL; + goto end; + } + + po->chan.private=sk; + po->chan.ops=&pptp_chan_ops; + po->chan.mtu=PPP_MTU; + po->chan.hdrlen=2+sizeof(struct pptp_gre_header); + error = ppp_register_channel(&po->chan); + if (error){ + printk(KERN_ERR "PPTP: failed to register PPP channel (%d)\n",error); + goto end; + } + + opt->dst_addr=sp->sa_addr.pptp; + sk->sk_state = PPPOX_CONNECTED; + + end: + release_sock(sk); + return error; +} + +static int pptp_getname(struct socket *sock, struct sockaddr *uaddr, + int *usockaddr_len, int peer) +{ + int len = sizeof(struct sockaddr_pppox); + struct sockaddr_pppox sp; + + sp.sa_family = AF_PPPOX; + sp.sa_protocol = PX_PROTO_PPTP; + sp.sa_addr.pptp=pppox_sk(sock->sk)->proto.pptp.src_addr; + + memcpy(uaddr, &sp, len); + + *usockaddr_len = len; + + return 0; +} + +static int pptp_setsockopt(struct socket *sock, int level, int optname, + char* optval, int optlen) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po = pppox_sk(sk); + struct pptp_opt *opt=&po->proto.pptp; + int val; + + if (optlen!=sizeof(int)) + return -EINVAL; + + if (get_user(val,(int __user*)optval)) + return -EFAULT; + + switch(optname) { + case PPTP_SO_TIMEOUT: + opt->timeout=val; + break; + case PPTP_SO_WINDOW: + opt->window=val; + break; + default: + return -ENOPROTOOPT; + } + + return 0; +} + +static int pptp_getsockopt(struct socket *sock, int level, int optname, + char* optval, int *optlen) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po = pppox_sk(sk); + struct pptp_opt *opt=&po->proto.pptp; + int len,val; + + if (get_user(len,(int __user*)optlen)) + return -EFAULT; + + if (len<sizeof(int)) + return -EINVAL; + + switch(optname) { + case PPTP_SO_TIMEOUT: + val=opt->timeout; + break; + case PPTP_SO_WINDOW: + val=opt->window; + break; + default: + return -ENOPROTOOPT; + } + + if (put_user(sizeof(int),(int __user*)optlen)) + return -EFAULT; + + if (put_user(val,(int __user*)optval)) + return -EFAULT; + + return 0; +} + +static int pptp_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po; + struct pptp_opt *opt; + int error = 0; + + if (!sk) + return 0; + + if (sock_flag(sk, SOCK_DEAD)) + return -EBADF; + + po = pppox_sk(sk); + opt=&po->proto.pptp; + if (opt->src_addr.sin_addr.s_addr) { + cancel_delayed_work(&opt->buf_work); + flush_scheduled_work(); + del_chan(po); + skb_queue_purge(&opt->skb_buf); + + if (opt->seq_sent){ + char unit[10]; + sprintf(unit,"ppp%i",ppp_unit_number(&po->chan)); + remove_proc_entry(unit,proc_dir); + } + } + + pppox_unbind_sock(sk); + + kfree(opt->stat); + + /* Signal the death of the socket. */ + sk->sk_state = PPPOX_DEAD; + + sock_orphan(sk); + sock->sk = NULL; + + skb_queue_purge(&sk->sk_receive_queue); + sock_put(sk); + + return error; +} + + +static struct proto pptp_sk_proto = { + .name = "PPTP", + .owner = THIS_MODULE, + .obj_size = sizeof(struct pppox_sock), +}; + +static const struct proto_ops pptp_ops = { + .family = AF_PPPOX, + .owner = THIS_MODULE, + .release = pptp_release, + .bind = pptp_bind, + .connect = pptp_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = pptp_getname, + .poll = sock_no_poll, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = pptp_setsockopt, + .getsockopt = pptp_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .mmap = sock_no_mmap, + .ioctl = pppox_ioctl, +}; + +static int pptp_create(struct socket *sock) +{ + int error = -ENOMEM; + struct sock *sk; + struct pppox_sock *po; + struct pptp_opt *opt; + + sk = sk_alloc(PF_PPPOX, GFP_KERNEL, &pptp_sk_proto, 1); + if (!sk) + goto out; + + sock_init_data(sock, sk); + + sock->state = SS_UNCONNECTED; + sock->ops = &pptp_ops; + + //sk->sk_backlog_rcv = pppoe_rcv_core; + sk->sk_state = PPPOX_NONE; + sk->sk_type = SOCK_STREAM; + sk->sk_family = PF_PPPOX; + sk->sk_protocol = PX_PROTO_PPTP; + + po = pppox_sk(sk); + opt=&po->proto.pptp; + + opt->window=min_window; + opt->timeout=0; + opt->seq_sent=0; opt->seq_recv=-1; + opt->ack_recv=0; opt->ack_sent=-1; + skb_queue_head_init(&opt->skb_buf); + opt->skb_buf_lock=SPIN_LOCK_UNLOCKED; + INIT_WORK(&opt->ack_work,(void(*)(void*))ack_work,sk); + INIT_WORK(&opt->buf_work,(void(*)(void*))buf_work,sk); + opt->stat=kzalloc(sizeof(*opt->stat),GFP_KERNEL); + + error = 0; +out: + return error; +} + + +static struct pppox_proto pppox_pptp_proto = { + .create = pptp_create, + //.ioctl = pptp_ioctl, + .owner = THIS_MODULE, +}; + +static struct net_protocol net_pptp_protocol = { + .handler = pptp_rcv, + //.err_handler = ipgre_err, +}; + + +static int pptp_init_module(void) +{ + int err=0; + printk(KERN_INFO "PPTP driver version " PPTP_DRIVER_VERSION "\n"); + + if (inet_add_protocol(&net_pptp_protocol, IPPROTO_GRE) < 0) { + printk(KERN_INFO "PPTP: can't add protocol\n"); + goto out; + } + + err = proto_register(&pptp_sk_proto, 0); + if (err){ + printk(KERN_INFO "PPTP: can't register sk_proto\n"); + goto out_inet_del_protocol; + } + + err = register_pppox_proto(PX_PROTO_PPTP, &pppox_pptp_proto); + if (err){ + printk(KERN_INFO "PPTP: can't register pppox_proto\n"); + goto out_unregister_sk_proto; + } + + proc_dir=proc_mkdir("pptp",NULL); + if (!proc_dir){ + printk(KERN_ERR "PPTP: failed to create proc dir\n"); + } + //console_verbose(); + +out: + return err; +out_unregister_sk_proto: + proto_unregister(&pptp_sk_proto); +out_inet_del_protocol: + inet_del_protocol(&net_pptp_protocol, IPPROTO_GRE); + goto out; +} + +static void pptp_exit_module(void) +{ + unregister_pppox_proto(PX_PROTO_PPTP); + proto_unregister(&pptp_sk_proto); + inet_del_protocol(&net_pptp_protocol, IPPROTO_GRE); + + if (proc_dir) + remove_proc_entry("pptp",NULL); +} + + +module_init(pptp_init_module); +module_exit(pptp_exit_module); diff -uprN linux-2.6.16.orig/include/linux/if_pppox.h linux-2.6.16.pptp/include/linux/if_pppox.h --- linux-2.6.16.orig/include/linux/if_pppox.h 2006-06-18 00:52:01.000000000 +0400 +++ linux-2.6.16.pptp/include/linux/if_pppox.h 2006-06-17 23:45:05.000000000 +0400 @@ -1,6 +1,6 @@ /*************************************************************************** * Linux PPP over X - Generic PPP transport layer sockets - * Linux PPP over Ethernet (PPPoE) Socket Implementation (RFC 2516) + * Linux PPP over Ethernet (PPPoE) Socket Implementation (RFC 2516) * * This file supplies definitions required by the PPP over Ethernet driver * (pppox.c). All version information wrt this file is located in pppox.c @@ -19,6 +19,7 @@ #include <asm/types.h> #include <asm/byteorder.h> +#include <linux/in.h> #ifdef __KERNEL__ #include <linux/if_ether.h> @@ -36,29 +37,35 @@ #define PF_PPPOX AF_PPPOX #endif /* !(AF_PPPOX) */ -/************************************************************************ - * PPPoE addressing definition - */ -typedef __u16 sid_t; -struct pppoe_addr{ - sid_t sid; /* Session identifier */ - unsigned char remote[ETH_ALEN]; /* Remote address */ - char dev[IFNAMSIZ]; /* Local device to use */ -}; - -/************************************************************************ - * Protocols supported by AF_PPPOX - */ +/************************************************************************ + * PPPoE addressing definition + */ +typedef __u16 sid_t; +struct pppoe_addr{ + sid_t sid; /* Session identifier */ + unsigned char remote[ETH_ALEN]; /* Remote address */ + char dev[IFNAMSIZ]; /* Local device to use */ +}; + +struct pptp_addr{ + __u16 call_id; + struct in_addr sin_addr; +}; +/************************************************************************ + * Protocols supported by AF_PPPOX + */ #define PX_PROTO_OE 0 /* Currently just PPPoE */ -#define PX_MAX_PROTO 1 - -struct sockaddr_pppox { - sa_family_t sa_family; /* address family, AF_PPPOX */ - unsigned int sa_protocol; /* protocol identifier */ - union{ - struct pppoe_addr pppoe; - }sa_addr; -}__attribute__ ((packed)); +#define PX_PROTO_PPTP 1 +#define PX_MAX_PROTO 2 + +struct sockaddr_pppox { + sa_family_t sa_family; /* address family, AF_PPPOX */ + unsigned int sa_protocol; /* protocol identifier */ + union{ + struct pppoe_addr pppoe; + struct pptp_addr pptp; + }sa_addr; +}__attribute__ ((packed)); /********************************************************************* @@ -111,6 +118,12 @@ struct pppoe_hdr { struct pppoe_tag tag[0]; } __attribute__ ((packed)); + +/* Socket options */ +#define PPTP_SO_TIMEOUT 1 +#define PPTP_SO_WINDOW 2 + + #ifdef __KERNEL__ struct pppoe_opt { struct net_device *dev; /* device associated with socket*/ @@ -118,6 +131,20 @@ struct pppoe_opt { struct sockaddr_pppox relay; /* what socket data will be relayed to (PPPoE relaying) */ }; +struct pptp_opt { + struct pptp_addr src_addr; + struct pptp_addr dst_addr; + int timeout; + int window; + __u32 ack_sent, ack_recv; + __u32 seq_sent, seq_recv; + int pause; + spinlock_t skb_buf_lock; + struct sk_buff_head skb_buf; + struct work_struct ack_work; //send ack work + struct work_struct buf_work; //check bufferd packets work + struct gre_statistics *stat; +}; #include <net/sock.h> @@ -128,6 +155,7 @@ struct pppox_sock { struct pppox_sock *next; /* for hash table */ union { struct pppoe_opt pppoe; + struct pptp_opt pptp; } proto; unsigned short num; }; diff -uprN linux-2.6.16.orig/MAINTAINERS linux-2.6.16.pptp/MAINTAINERS --- linux-2.6.16.orig/MAINTAINERS 2006-06-18 00:52:00.000000000 +0400 +++ linux-2.6.16.pptp/MAINTAINERS 2006-06-18 01:26:45.000000000 +0400 @@ -2118,6 +2118,11 @@ P: Michal Ostrowski M: mostrows@xxxxxxxxxxxxx S: Maintained +PPTP +P: Dmitry Kozlov +M: xeb@xxxxxxx +S: Maintained + PREEMPTIBLE KERNEL P: Robert Love M: rml@xxxxxxxxx - To unsubscribe from this list: send the line "unsubscribe linux-ppp" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html