summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--accel-pptpd/CMakeLists.txt4
-rw-r--r--accel-pptpd/ipcp_opt_ipaddr.c2
-rw-r--r--accel-pptpd/main.c82
-rw-r--r--accel-pptpd/ppp.c26
-rw-r--r--accel-pptpd/ppp.h5
-rw-r--r--accel-pptpd/ppp_ccp.c4
-rw-r--r--accel-pptpd/ppp_fsm.c18
-rw-r--r--accel-pptpd/ppp_ipcp.c4
-rw-r--r--accel-pptpd/ppp_lcp.c4
-rw-r--r--accel-pptpd/pptp.c91
-rw-r--r--accel-pptpd/triton/CMakeLists.txt5
-rw-r--r--accel-pptpd/triton/conf_file.c54
-rw-r--r--accel-pptpd/triton/conf_file.h16
-rw-r--r--accel-pptpd/triton/loader.c47
-rw-r--r--accel-pptpd/triton/log.c34
-rw-r--r--accel-pptpd/triton/md.c65
-rw-r--r--accel-pptpd/triton/spinlock.h6
-rw-r--r--accel-pptpd/triton/timer.c80
-rw-r--r--accel-pptpd/triton/triton.c104
-rw-r--r--accel-pptpd/triton/triton.h61
-rw-r--r--accel-pptpd/triton/triton_p.h45
-rw-r--r--doc/rfc3079.txt1179
22 files changed, 1600 insertions, 336 deletions
diff --git a/accel-pptpd/CMakeLists.txt b/accel-pptpd/CMakeLists.txt
index 5e424036..d68cbe91 100644
--- a/accel-pptpd/CMakeLists.txt
+++ b/accel-pptpd/CMakeLists.txt
@@ -1,12 +1,12 @@
PROJECT (pptpd)
cmake_minimum_required(VERSION 2.6)
-SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fvisibility=hidden -D_GNU_SOURCE")
ADD_SUBDIRECTORY(triton)
ADD_EXECUTABLE(pptpd
- pptpd.c
+ main.c
pptp.c
log.c
ppp.c
diff --git a/accel-pptpd/ipcp_opt_ipaddr.c b/accel-pptpd/ipcp_opt_ipaddr.c
index 8eae84b6..206c2e6e 100644
--- a/accel-pptpd/ipcp_opt_ipaddr.c
+++ b/accel-pptpd/ipcp_opt_ipaddr.c
@@ -12,7 +12,7 @@ static void ipaddr_free(struct ppp_ipcp_t *ipcp, struct ipcp_option_t *opt);
static int ipaddr_send_conf_req(struct ppp_ipcp_t *ipcp, struct ipcp_option_t *opt, uint8_t *ptr);
static int ipaddr_send_conf_nak(struct ppp_ipcp_t *ipcp, struct ipcp_option_t *opt, uint8_t *ptr);
static int ipaddr_recv_conf_req(struct ppp_ipcp_t *ipcp, struct ipcp_option_t *opt, uint8_t *ptr);
-static int ipaddr_recv_conf_ack(struct ppp_ipcp_t *ipcp, struct ipcp_option_t *opt, uint8_t *ptr);
+//static int ipaddr_recv_conf_ack(struct ppp_ipcp_t *ipcp, struct ipcp_option_t *opt, uint8_t *ptr);
static void ipaddr_print(void (*print)(const char *fmt,...),struct ipcp_option_t*, uint8_t *ptr);
struct ipaddr_option_t
diff --git a/accel-pptpd/main.c b/accel-pptpd/main.c
index ff74305b..90f206df 100644
--- a/accel-pptpd/main.c
+++ b/accel-pptpd/main.c
@@ -1,17 +1,82 @@
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
#include "triton/triton.h"
-#include "log.h"
void sigterm(int num)
{
- triton_terminate();
}
int main(int argc,char **argv)
{
+ int i;
+ int daemon=0;
+ char *pid_file=NULL;
+ char *conf_file=NULL;
sigset_t set;
- log_init(stdout,4,0);
+ if (argc<2)
+ goto usage;
+
+ for(i=1; i<argc; i++)
+ {
+ if (!strcmp(argv[i],"-d"))
+ daemon=1;
+ else if (!strcmp(argv[i],"-p"))
+ {
+ if (i==argc-1)
+ goto usage;
+ pid_file=argv[++i];
+ }
+ else if (!strcmp(argv[i],"-c"))
+ {
+ if (i==argc-1)
+ goto usage;
+ conf_file=argv[++i];
+ }
+ }
+
+ if (!conf_file)
+ goto usage;
+
+ if (triton_init(conf_file))
+ return EXIT_FAILURE;
- triton_init();
+ if (daemon)
+ {
+ pid_t pid=fork();
+ if (pid>0)
+ _exit(EXIT_SUCCESS);
+ if (pid<0)
+ {
+ perror("fork");
+ return EXIT_FAILURE;
+ }
+ if (setsid()<0)
+ return EXIT_FAILURE;
+ pid=fork();
+ if (pid)
+ _exit(0);
+ umask(0);
+ chdir("/");
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+
+ if (pid_file)
+ {
+ FILE *f=fopen("pid_file","w");
+ if (f)
+ {
+ fprintf(f,"%i",getpid());
+ fclose(f);
+ }
+ }
+
triton_run();
signal(SIGTERM,sigterm);
@@ -23,7 +88,16 @@ int main(int argc,char **argv)
sigdelset(&set, SIGBUS);
sigsuspend(&set);
+
+ triton_terminate();
return EXIT_SUCCESS;
+usage:
+ printf("usage: pptpd [-d] [-p <file>] -c <file>\
+ where:\
+ -d - daemon mode\
+ -p - write pid to <file>\
+ -c - config file\n");
+ return EXIT_FAILURE;
}
diff --git a/accel-pptpd/ppp.c b/accel-pptpd/ppp.c
index 518f7572..b4388718 100644
--- a/accel-pptpd/ppp.c
+++ b/accel-pptpd/ppp.c
@@ -25,8 +25,8 @@ struct layer_node_t
struct list_head items;
};
-static void ppp_chan_read(struct triton_md_handler_t*);
-static void ppp_unit_read(struct triton_md_handler_t*);
+static int ppp_chan_read(struct triton_md_handler_t*);
+static int ppp_unit_read(struct triton_md_handler_t*);
static void init_layers(struct ppp_t *);
static void start_first_layer(struct ppp_t *);
@@ -103,10 +103,10 @@ int establish_ppp(struct ppp_t *ppp)
ppp->chan_hnd.fd=ppp->chan_fd;
ppp->chan_hnd.read=ppp_chan_read;
- ppp->chan_hnd.twait=-1;
+ //ppp->chan_hnd.twait=-1;
ppp->unit_hnd.fd=ppp->unit_fd;
ppp->unit_hnd.read=ppp_unit_read;
- ppp->unit_hnd.twait=-1;
+ //ppp->unit_hnd.twait=-1;
triton_md_register_handler(&ppp->chan_hnd);
triton_md_register_handler(&ppp->unit_hnd);
@@ -142,7 +142,7 @@ void destablish_ppp(struct ppp_t *ppp)
log_debug("ppp destablished\n");
- if (ppp->events.finished) ppp->events.finished(ppp);
+ ppp->ctrl->finished(ppp);
}
void print_buf(uint8_t *buf,int size)
@@ -179,7 +179,7 @@ int ppp_unit_send(struct ppp_t *ppp, void *data, int size)
return n;
}
-static void ppp_chan_read(struct triton_md_handler_t*h)
+static int ppp_chan_read(struct triton_md_handler_t*h)
{
struct ppp_t *ppp=container_of(h,typeof(*ppp),chan_hnd);
struct ppp_handler_t *ppp_h;
@@ -193,7 +193,7 @@ static void ppp_chan_read(struct triton_md_handler_t*h)
if (ppp->chan_buf_size<2)
{
log_error("ppp_chan_read: short read %i\n",ppp->chan_buf_size);
- return;
+ return 0;
}
proto=ntohs(*(uint16_t*)ppp->chan_buf);
@@ -202,14 +202,15 @@ static void ppp_chan_read(struct triton_md_handler_t*h)
if (ppp_h->proto==proto)
{
ppp_h->recv(ppp_h);
- return;
+ return 0;
}
}
log_warn("ppp_chan_read: discarding unknown packet %x\n",proto);
+ return 0;
}
-static void ppp_unit_read(struct triton_md_handler_t*h)
+static int ppp_unit_read(struct triton_md_handler_t*h)
{
struct ppp_t *ppp=container_of(h,typeof(*ppp),unit_hnd);
struct ppp_handler_t *ppp_h;
@@ -223,7 +224,7 @@ static void ppp_unit_read(struct triton_md_handler_t*h)
if (ppp->unit_buf_size<2)
{
log_error("ppp_chan_read: short read %i\n",ppp->unit_buf_size);
- return;
+ return 0;
}
proto=ntohs(*(uint16_t*)ppp->unit_buf);
@@ -232,11 +233,12 @@ static void ppp_unit_read(struct triton_md_handler_t*h)
if (ppp_h->proto==proto)
{
ppp_h->recv(ppp_h);
- return;
+ return 0;
}
}
log_warn("ppp_chan_read: discarding unknown packet %x\n",proto);
+ return 0;
}
void ppp_layer_started(struct ppp_t *ppp, struct ppp_layer_data_t *d)
@@ -250,7 +252,7 @@ void ppp_layer_started(struct ppp_t *ppp, struct ppp_layer_data_t *d)
if (n->entry.next==&ppp->layers)
{
- if (ppp->events.started) ppp->events.started(ppp);
+ ppp->ctrl->started(ppp);
}else
{
n=list_entry(n->entry.next,typeof(*n),entry);
diff --git a/accel-pptpd/ppp.h b/accel-pptpd/ppp.h
index 39a0d41b..6dd05bea 100644
--- a/accel-pptpd/ppp.h
+++ b/accel-pptpd/ppp.h
@@ -52,8 +52,9 @@
struct ppp_t;
-struct ppp_events_t
+struct ppp_ctrl_t
{
+ struct triton_ctx_t *ctx;
void (*started)(struct ppp_t*);
void (*finished)(struct ppp_t*);
};
@@ -71,7 +72,7 @@ struct ppp_t
char *chan_name;
- struct ppp_events_t events;
+ struct ppp_ctrl_t *ctrl;
int log:1;
diff --git a/accel-pptpd/ppp_ccp.c b/accel-pptpd/ppp_ccp.c
index 3c8c5cdb..4bceb5b2 100644
--- a/accel-pptpd/ppp_ccp.c
+++ b/accel-pptpd/ppp_ccp.c
@@ -528,14 +528,14 @@ static void ccp_recv(struct ppp_handler_t*h)
ppp_fsm_recv_conf_rej(&ccp->fsm);
break;
case TERMREQ:
- term_msg=strndup((uint8_t*)(hdr+1),ntohs(hdr->len));
+ term_msg=strndup((char*)(hdr+1),ntohs(hdr->len));
log_debug("recv [CCP TermReq id=%x \"%s\"]\n",hdr->id,term_msg);
free(term_msg);
ppp_fsm_recv_term_req(&ccp->fsm);
ppp_terminate(ccp->ppp);
break;
case TERMACK:
- term_msg=strndup((uint8_t*)(hdr+1),ntohs(hdr->len));
+ term_msg=strndup((char*)(hdr+1),ntohs(hdr->len));
log_debug("recv [CCP TermAck id=%x \"%s\"]\n",hdr->id,term_msg);
free(term_msg);
ppp_fsm_recv_term_ack(&ccp->fsm);
diff --git a/accel-pptpd/ppp_fsm.c b/accel-pptpd/ppp_fsm.c
index 41436ebe..72a42d0e 100644
--- a/accel-pptpd/ppp_fsm.c
+++ b/accel-pptpd/ppp_fsm.c
@@ -29,10 +29,7 @@ static int restart_timer_func(struct triton_timer_t*t);
void ppp_fsm_init(struct ppp_fsm_t *layer)
{
layer->fsm_state=FSM_Initial;
- layer->restart_timer.active=0;
- layer->restart_timer.pd=layer;
- layer->restart_timer.expire=restart_timer_func;
- layer->restart_timer.period=3000;
+ //layer->restart_timer.active=0;
layer->restart_counter=0;
layer->max_terminate=2;
layer->max_configure=10;
@@ -461,20 +458,20 @@ void send_term_ack(struct ppp_fsm_t *layer)
static void init_req_counter(struct ppp_fsm_t *layer,int timeout)
{
- triton_timer_del(&layer->restart_timer);
+ //triton_timer_del(&layer->restart_timer);
layer->restart_timer.expire_tv.tv_sec=0;
- triton_timer_add(&layer->restart_timer);
+ //triton_timer_add(&layer->restart_timer);
layer->restart_counter=timeout;
}
static void zero_req_counter(struct ppp_fsm_t *layer)
{
- triton_timer_del(&layer->restart_timer);
+ //triton_timer_del(&layer->restart_timer);
layer->restart_timer.expire_tv.tv_sec=0;
- triton_timer_add(&layer->restart_timer);
+ //triton_timer_add(&layer->restart_timer);
layer->restart_counter=0;
}
-static int restart_timer_func(struct triton_timer_t*t)
+/*static int restart_timer_func(struct triton_timer_t*t)
{
struct ppp_fsm_t *layer=(struct ppp_fsm_t *)t->pd;
@@ -486,4 +483,5 @@ static int restart_timer_func(struct triton_timer_t*t)
ppp_fsm_timeout1(layer);
return 0;
-}
+}*/
+
diff --git a/accel-pptpd/ppp_ipcp.c b/accel-pptpd/ppp_ipcp.c
index fc25230a..ad552bdd 100644
--- a/accel-pptpd/ppp_ipcp.c
+++ b/accel-pptpd/ppp_ipcp.c
@@ -519,14 +519,14 @@ static void ipcp_recv(struct ppp_handler_t*h)
ppp_fsm_recv_conf_rej(&ipcp->fsm);
break;
case TERMREQ:
- term_msg=strndup((uint8_t*)(hdr+1),ntohs(hdr->len));
+ term_msg=strndup((char*)(hdr+1),ntohs(hdr->len));
log_debug("recv [IPCP TermReq id=%x \"%s\"]\n",hdr->id,term_msg);
free(term_msg);
ppp_fsm_recv_term_req(&ipcp->fsm);
ppp_terminate(ipcp->ppp);
break;
case TERMACK:
- term_msg=strndup((uint8_t*)(hdr+1),ntohs(hdr->len));
+ term_msg=strndup((char*)(hdr+1),ntohs(hdr->len));
log_debug("recv [IPCP TermAck id=%x \"%s\"]\n",hdr->id,term_msg);
free(term_msg);
ppp_fsm_recv_term_ack(&ipcp->fsm);
diff --git a/accel-pptpd/ppp_lcp.c b/accel-pptpd/ppp_lcp.c
index dbc819a1..1f0431a7 100644
--- a/accel-pptpd/ppp_lcp.c
+++ b/accel-pptpd/ppp_lcp.c
@@ -532,14 +532,14 @@ static void lcp_recv(struct ppp_handler_t*h)
ppp_fsm_recv_conf_rej(&lcp->fsm);
break;
case TERMREQ:
- term_msg=strndup((uint8_t*)(hdr+1),ntohs(hdr->len));
+ term_msg=strndup((char*)(hdr+1),ntohs(hdr->len));
log_debug("recv [LCP TermReq id=%x \"%s\"]\n",hdr->id,term_msg);
free(term_msg);
ppp_fsm_recv_term_req(&lcp->fsm);
ppp_terminate(lcp->ppp);
break;
case TERMACK:
- term_msg=strndup((uint8_t*)(hdr+1),ntohs(hdr->len));
+ term_msg=strndup((char*)(hdr+1),ntohs(hdr->len));
log_debug("recv [LCP TermAck id=%x \"%s\"]\n",hdr->id,term_msg);
free(term_msg);
ppp_fsm_recv_term_ack(&lcp->fsm);
diff --git a/accel-pptpd/pptp.c b/accel-pptpd/pptp.c
index a261b195..1ec35c8f 100644
--- a/accel-pptpd/pptp.c
+++ b/accel-pptpd/pptp.c
@@ -50,20 +50,22 @@ struct pptp_conn_t
int out_size;
int out_pos;
+ struct ppp_ctrl_t ctrl;
struct ppp_t ppp;
};
-static void pptp_read(struct triton_md_handler_t *h);
-static void pptp_write(struct triton_md_handler_t *h);
+static int pptp_read(struct triton_md_handler_t *h);
+static int pptp_write(struct triton_md_handler_t *h);
static void pptp_timeout(struct triton_md_handler_t *h);
static void ppp_started(struct ppp_t *);
static void ppp_finished(struct ppp_t *);
static void disconnect(struct pptp_conn_t *conn)
{
- close(conn->hnd.fd);
triton_md_unregister_handler(&conn->hnd);
- free(conn);
+ close(conn->hnd.fd);
+ conn->hnd.fd=-1;
+ triton_unregister_ctx(&conn->ctx);
}
static int post_msg(struct pptp_conn_t *conn,void *buf,int size)
@@ -125,7 +127,7 @@ static int pptp_stop_ctrl_conn_rqst(struct pptp_conn_t *conn)
ppp_terminate(&conn->ppp);
conn->state=STATE_FIN;
- conn->hnd.twait=1000;
+ //conn->hnd.twait=1000;
return send_pptp_stop_ctrl_conn_rply(conn,PPTP_CONN_STOP_OK,0);
}
@@ -258,14 +260,16 @@ static int pptp_out_call_rqst(struct pptp_conn_t *conn)
conn->ppp.fd=pptp_sock;
conn->ppp.chan_name=strdup(inet_ntoa(dst_addr.sa_addr.pptp.sin_addr));
- conn->ppp.events.started=ppp_started;
- conn->ppp.events.finished=ppp_finished;
+ conn->ppp.ctrl=&conn->ctrl;
+ conn->ctrl.ctx=&conn->ctx;
+ conn->ctrl.started=ppp_started;
+ conn->ctrl.finished=ppp_finished;
if (establish_ppp(&conn->ppp))
{
close(pptp_sock);
send_pptp_stop_ctrl_conn_rqst(conn,0,0);
conn->state=STATE_FIN;
- conn->hnd.twait=1000;
+ //conn->hnd.twait=1000;
}else conn->state=STATE_PPP;
return 0;
@@ -286,7 +290,7 @@ static int process_packet(struct pptp_conn_t *conn)
return 0;
}
-static void pptp_read(struct triton_md_handler_t *h)
+static int pptp_read(struct triton_md_handler_t *h)
{
struct pptp_conn_t *conn=container_of(h,typeof(*conn),hnd);
struct pptp_header *hdr=(struct pptp_header *)conn->in_buf;
@@ -295,9 +299,8 @@ static void pptp_read(struct triton_md_handler_t *h)
n=read(h->fd,conn->in_buf,PPTP_CTRL_SIZE_MAX-conn->in_size);
if (n<=0)
{
- if (errno==EAGAIN) return;
- disconnect(conn);
- return;
+ if (errno==EAGAIN) return 0;
+ goto drop;
}
conn->in_size+=n;
if (conn->in_size>=sizeof(*hdr))
@@ -312,25 +315,25 @@ static void pptp_read(struct triton_md_handler_t *h)
conn->in_size=0;
}
}
- h->twait=TIMEOUT;
- return;
+ //h->twait=TIMEOUT;
+ return 0;
drop:
disconnect(conn);
- return;
+ return 1;
}
-static void pptp_write(struct triton_md_handler_t *h)
+static int pptp_write(struct triton_md_handler_t *h)
{
struct pptp_conn_t *conn=container_of(h,typeof(*conn),hnd);
int n=write(h->fd,conn->out_buf+conn->out_pos,conn->out_size-conn->out_pos);
if (n<0)
{
- if (errno==EINTR) n=0;
+ if (errno==EAGAIN) n=0;
else
{
log_debug("post_msg: failed to write socket %i\n",errno);
disconnect(conn);
- return;
+ return 1;
}
}
@@ -341,12 +344,27 @@ static void pptp_write(struct triton_md_handler_t *h)
conn->out_size=0;
triton_md_disable_handler(h,MD_MODE_WRITE);
}
- h->twait=TIMEOUT;
+ return 0;
+ //h->twait=TIMEOUT;
}
static void pptp_timeout(struct triton_md_handler_t *h)
{
}
-
+static void pptp_close(struct triton_ctx_t *ctx)
+{
+ struct pptp_conn_t *conn=container_of(ctx,typeof(*conn),ctx);
+ if (conn->state==STATE_PPP)
+ ppp_terminate(&conn->ppp);
+ else
+ disconnect(conn);
+}
+static void pptp_free(struct triton_ctx_t *ctx)
+{
+ struct pptp_conn_t *conn=container_of(ctx,typeof(*conn),ctx);
+ free(conn->in_buf);
+ free(conn->out_buf);
+ free(conn);
+}
static void ppp_started(struct ppp_t *ppp)
{
log_msg("ppp_started\n");
@@ -359,11 +377,17 @@ static void ppp_finished(struct ppp_t *ppp)
close(conn->ppp.fd);
send_pptp_stop_ctrl_conn_rqst(conn,0,0);
conn->state=STATE_FIN;
- conn->hnd.twait=1000;
+ //conn->hnd.twait=1000;
}
//==================================
+struct pptp_serv_t
+{
+ struct triton_ctx_t ctx;
+ struct triton_md_handler_t hnd;
+};
+
static int pptp_connect(struct triton_md_handler_t *h)
{
struct sockaddr_in addr;
@@ -373,7 +397,7 @@ static int pptp_connect(struct triton_md_handler_t *h)
while(1)
{
- sock=accept(f->fd,(struct sockaddr *)&addr,&size);
+ sock=accept(h->fd,(struct sockaddr *)&addr,&size);
if (sock<0)
{
if (errno==EAGAIN)
@@ -383,10 +407,11 @@ static int pptp_connect(struct triton_md_handler_t *h)
}
conn=malloc(sizeof(*conn));
memset(conn,0,sizeof(*conn));
- conn->hnd.fd=fd;
+ conn->hnd.fd=sock;
conn->hnd.read=pptp_read;
conn->hnd.write=pptp_write;
- conn->hnd.close=pptp_close;
+ conn->ctx.close=pptp_close;
+ conn->ctx.free=pptp_free;
conn->hnd.ctx=&conn->ctx;
conn->in_buf=malloc(PPTP_CTRL_SIZE_MAX);
conn->out_buf=malloc(PPTP_CTRL_SIZE_MAX);
@@ -397,29 +422,23 @@ static int pptp_connect(struct triton_md_handler_t *h)
}
return 0;
}
-static void pptp_serv_close(struct triton_md_handler_t *h)
+static void pptp_serv_close(struct triton_ctx_t *ctx)
{
- triton_md_unregister_handler(h);
- close(h->fd);
+ struct pptp_serv_t *s=container_of(ctx,typeof(*s),ctx);
+ triton_md_unregister_handler(&s->hnd);
+ close(s->hnd.fd);
}
-struct pptp_serv_t
-{
- struct triton_context_t ctx;
- struct triton_md_handler_t hnd;
-};
-
static struct pptp_serv_t serv=
{
.hnd.read=pptp_connect,
- .hnd.close=pptp_serv_close,
+ .ctx.close=pptp_serv_close,
.hnd.ctx=&serv.ctx,
};
void __init pptp_init()
{
struct sockaddr_in addr;
- socklen_t size;
serv.hnd.fd=socket (PF_INET, SOCK_STREAM, 0);
if (serv.hnd.fd<0)
@@ -442,7 +461,7 @@ void __init pptp_init()
{
log_error("pptp: failed to listen socket\n");
close(serv.hnd.fd);
- return -1;
+ return;
}
triton_register_ctx(&serv.ctx);
diff --git a/accel-pptpd/triton/CMakeLists.txt b/accel-pptpd/triton/CMakeLists.txt
index f2de0175..a531438c 100644
--- a/accel-pptpd/triton/CMakeLists.txt
+++ b/accel-pptpd/triton/CMakeLists.txt
@@ -1,10 +1,11 @@
SET(target triton)
SET(sources_c
md.c
- conf_file.c
timer.c
- options.c
+ triton.c
+ conf_file.c
loader.c
+ log.c
)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/accel-pptpd/triton/conf_file.c b/accel-pptpd/triton/conf_file.c
index 3ddb649e..026c4d3c 100644
--- a/accel-pptpd/triton/conf_file.c
+++ b/accel-pptpd/triton/conf_file.c
@@ -3,14 +3,13 @@
#include <unistd.h>
#include <stdlib.h>
-#include "conf_file.h"
#include "triton_p.h"
struct sect_t
{
struct list_head entry;
- struct conf_file_sect_t *sect;
+ struct conf_sect_t *sect;
};
static LIST_HEAD(sections);
@@ -18,22 +17,22 @@ static LIST_HEAD(sections);
static char* skip_space(char *str);
static char* skip_word(char *str);
-static struct conf_file_sect_t *find_sect(const char *name);
-static struct conf_file_sect_t *create_sect(const char *name);
-static void sect_add_item(struct conf_file_sect_t *sect,const char *name,const char *val);
-static struct option_t *find_item(struct conf_file_sect_t *,const char *name);
+static struct conf_sect_t *find_sect(const char *name);
+static struct conf_sect_t *create_sect(const char *name);
+static void sect_add_item(struct conf_sect_t *sect,const char *name,const char *val);
+static struct conf_option_t *find_item(struct conf_sect_t *,const char *name);
-void conf_file_load(const char *fname)
+int conf_load(const char *fname)
{
char *buf,*str,*str2;
char *path0,*path;
int cur_line=0;
- static struct conf_file_sect_t *cur_sect=NULL;
+ static struct conf_sect_t *cur_sect=NULL;
FILE *f=fopen(fname,"r");
if (!f)
{
- perror("triton: open conf file");
- return;
+ perror("conf_file: open");
+ return -1;
}
buf=(char*)malloc(1024);
@@ -62,7 +61,7 @@ void conf_file_load(const char *fname)
strcat(path,str+1);
str=path;
}*/
- conf_file_load(str);
+ conf_load(str);
continue;
}
if (*str=='[')
@@ -71,8 +70,8 @@ void conf_file_load(const char *fname)
if (*str2!=']')
{
//L1:
- printf("triton: sintax error in conf file %s line %i\n",fname,cur_line);
- return;
+ fprintf(stderr,"conf_file:%s:%i: sintax error\n",fname,cur_line);
+ return -1;
}
*str2=0;
cur_sect=find_sect(str);
@@ -81,8 +80,8 @@ void conf_file_load(const char *fname)
}
if (!cur_sect)
{
- printf("triton: no section opened in conf file %s line %i\n",fname,cur_line);
- return;
+ fprintf(stderr,"conf_file:%s:%i: no section opened\n",fname,cur_line);
+ return -1;
}
str2=skip_word(str);
if (*str2==' ')
@@ -98,7 +97,7 @@ void conf_file_load(const char *fname)
if (*str2 && *(str2+1) && *str2=='$' && *(str2+1)=='{')
{
char *s;
- struct option_t *opt;
+ struct conf_option_t *opt;
for (s=str2+2; *s && *s!='}'; s++);
if (*s=='}')
{
@@ -108,8 +107,8 @@ void conf_file_load(const char *fname)
opt=find_item(cur_sect,str2);
if (!opt)
{
- printf("triton: parent option not found int conf file %s line %i\n",fname,cur_line);
- return;
+ fprintf(stderr,"conf_file:%s:%i: parent option not found\n",fname,cur_line);
+ return -1;
}
str2=opt->val;
}
@@ -121,6 +120,8 @@ void conf_file_load(const char *fname)
free(path);
free(path0);
fclose(f);
+
+ return 0;
}
static char* skip_space(char *str)
@@ -134,7 +135,7 @@ static char* skip_word(char *str)
return str;
}
-static struct conf_file_sect_t *find_sect(const char *name)
+static struct conf_sect_t *find_sect(const char *name)
{
struct sect_t *s;
list_for_each_entry(s,&sections,entry)
@@ -144,11 +145,11 @@ static struct conf_file_sect_t *find_sect(const char *name)
return NULL;
}
-static struct conf_file_sect_t *create_sect(const char *name)
+static struct conf_sect_t *create_sect(const char *name)
{
struct sect_t *s=(struct sect_t *)malloc(sizeof(struct sect_t));
- s->sect=(struct conf_file_sect_t*)malloc(sizeof(struct conf_file_sect_t));
+ s->sect=(struct conf_sect_t*)malloc(sizeof(struct conf_sect_t));
s->sect->name=(char*)strdup(name);
INIT_LIST_HEAD(&s->sect->items);
@@ -157,9 +158,9 @@ static struct conf_file_sect_t *create_sect(const char *name)
return s->sect;
}
-static void sect_add_item(struct conf_file_sect_t *sect,const char *name,const char *val)
+static void sect_add_item(struct conf_sect_t *sect,const char *name,const char *val)
{
- struct option_t *opt=(struct option_t *)malloc(sizeof(struct option_t));
+ struct conf_option_t *opt=(struct conf_option_t *)malloc(sizeof(struct conf_option_t));
opt->name=(char*)strdup(name);
opt->val=val?(char*)strdup(val):NULL;
@@ -167,9 +168,9 @@ static void sect_add_item(struct conf_file_sect_t *sect,const char *name,const c
list_add_tail(&opt->entry,&sect->items);
}
-static struct option_t *find_item(struct conf_file_sect_t *sect,const char *name)
+static struct conf_option_t *find_item(struct conf_sect_t *sect,const char *name)
{
- struct option_t *opt;
+ struct conf_option_t *opt;
list_for_each_entry(opt,&sect->items,entry)
{
if (strcmp(opt->name,name)==0)
@@ -179,7 +180,8 @@ static struct option_t *find_item(struct conf_file_sect_t *sect,const char *name
return NULL;
}
-struct conf_file_sect_t *conf_file_get_section(const char *name)
+struct conf_sect_t *conf_file_get_section(const char *name)
{
return find_sect(name);
}
+
diff --git a/accel-pptpd/triton/conf_file.h b/accel-pptpd/triton/conf_file.h
deleted file mode 100644
index 47ade313..00000000
--- a/accel-pptpd/triton/conf_file.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef CONF_FILE_H
-#define CONF_FILE_H
-
-#include "list.h"
-
-struct conf_file_sect_t
-{
- const char *name;
-
- struct list_head items;
-};
-
-void conf_file_load(const char *fname);
-struct conf_file_sect_t *conf_file_get_section(const char *name);
-
-#endif
diff --git a/accel-pptpd/triton/loader.c b/accel-pptpd/triton/loader.c
index 33d48315..a390f0a9 100644
--- a/accel-pptpd/triton/loader.c
+++ b/accel-pptpd/triton/loader.c
@@ -4,52 +4,5 @@
#include <stdio.h>
#include <pthread.h>
-#include "conf_file.h"
#include "triton_p.h"
-void md_init(void);
-void event_init(void);
-void timer_init(void);
-
-struct thread_arg_t
-{
- int (*post_init)(void*);
- void *arg;
-};
-
-void *thread(struct thread_arg_t *arg)
-{
- printf("triton: starting new thread\n");
- #ifdef USE_CORO
- coroutine_init();
- #endif
- md_init();
- event_init();
- timer_init();
-
- arg->post_init(arg->arg);
-
- free(arg);
-
- //conf_file_load(cf_name);
- #ifdef USE_CORO
- schedule();
- #else
- md_run();
- #endif
-
- return NULL;
-}
-
-int triton_init(const char *conf_file)
-{
- return 0;
-}
-int triton_run(int (*post_init)(void*),void *arg)
-{
- pthread_t thr;
- struct thread_arg_t *thr_arg=malloc(sizeof(*thr_arg));
- thr_arg->post_init=post_init;
- thr_arg->arg=arg;
- return pthread_create(&thr,NULL,(void*(*)(void*))thread,thr_arg);
-}
diff --git a/accel-pptpd/triton/log.c b/accel-pptpd/triton/log.c
new file mode 100644
index 00000000..39a9cb18
--- /dev/null
+++ b/accel-pptpd/triton/log.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+
+#include "triton_p.h"
+
+static FILE *f_error;
+static FILE *f_debug;
+
+int log_init(void)
+{
+ char *log_error=conf_get_opt("core","log_error");
+ char *log_debug=conf_get_opt("core","log_debug");
+
+ if (log_error)
+ {
+ f_error=fopen(log_error,"a");
+ if (!f_error)
+ {
+ perror("log:log_error:open");
+ return -1;
+ }
+ }
+ if (log_debug)
+ {
+ f_debug=fopen(log_debug,"a");
+ if (!f_debug)
+ {
+ perror("log:log_debug:open");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/accel-pptpd/triton/md.c b/accel-pptpd/triton/md.c
index 3080c5a2..50e969c9 100644
--- a/accel-pptpd/triton/md.c
+++ b/accel-pptpd/triton/md.c
@@ -3,6 +3,7 @@
#include <sys/time.h>
#include <signal.h>
#include <string.h>
+#include <errno.h>
#include "triton_p.h"
@@ -12,9 +13,9 @@ static int epoll_fd;
static struct epoll_event *epoll_events;
static pthread_t md_thr;
-static void* md_thread(void *arg)
+static void* md_thread(void *arg);
-int md_init()
+int md_init(void)
{
epoll_fd=epoll_create(1);
if (epoll_fd<0)
@@ -34,40 +35,42 @@ int md_init()
}
void md_run()
{
- pthread_create(&md_thr,md_thread,NULL);
+ pthread_create(&md_thr,NULL,md_thread,NULL);
}
void md_terminate()
{
- pthread_join(&md_thr);
+ pthread_cancel(md_thr);
+ pthread_join(md_thr,NULL);
}
static void* md_thread(void *arg)
{
int i,n,r;
struct triton_md_handler_t *h;
-
- n=epoll_wait(epoll_fd,epoll_events,MAX_EVENTS,-1);
- if (n<0)
- {
- if (errno!=EINTR)
- perror("epoll_wait");
- continue;
- }
- if (n==0)
- return;
-
- for(i=0; i<n; i++)
+
+ while(1)
{
- h=(struct triton_md_handler_t*)epoll_events[i].data.ptr;
- spin_lock(&h->ctx->lock);
- h->trig_epoll_events=epoll_events[i].events;
- list_add_tail(&h->entry2,&h->ctx->pending_handlers);
- h->pending=1;
- r=triton_queue_ctx(h->ctx);
- spin_unlock(&h->ctx->lock);
- if (r)
- triton_thread_wakeup(ctx->thread);
+ n=epoll_wait(epoll_fd,epoll_events,max_events,-1);
+ if (n<0)
+ {
+ if (errno!=EINTR)
+ perror("epoll_wait");
+ continue;
+ }
+
+ for(i=0; i<n; i++)
+ {
+ h=(struct triton_md_handler_t*)epoll_events[i].data.ptr;
+ spin_lock(&h->ctx->lock);
+ h->trig_epoll_events=epoll_events[i].events;
+ list_add_tail(&h->entry2,&h->ctx->pending_handlers);
+ h->pending=1;
+ r=triton_queue_ctx(h->ctx);
+ spin_unlock(&h->ctx->lock);
+ if (r)
+ triton_thread_wakeup(h->ctx->thread);
+ }
}
}
@@ -76,17 +79,17 @@ void triton_md_register_handler(struct triton_md_handler_t *h)
h->epoll_event.data.ptr=h;
if (!h->ctx)
h->ctx=default_ctx;
- pthread_mutex_lock(&h->ctx->lock);
+ spin_lock(&h->ctx->lock);
list_add_tail(&h->entry,&h->ctx->handlers);
- pthread_mutex_unlock(&h->ctx->lock);
+ spin_unlock(&h->ctx->lock);
}
void triton_md_unregister_handler(struct triton_md_handler_t *h)
{
- pthread_mutex_lock(&h->ctx->lock);
+ spin_lock(&h->ctx->lock);
list_del(&h->entry);
if (h->pending)
list_del(&h->entry2);
- pthread_lock_unlock(&h->ctx->lock);
+ spin_unlock(&h->ctx->lock);
}
int triton_md_enable_handler(struct triton_md_handler_t *h, int mode)
{
@@ -109,7 +112,9 @@ int triton_md_enable_handler(struct triton_md_handler_t *h, int mode)
}
int triton_md_disable_handler(struct triton_md_handler_t *h,int mode)
{
- if (h->epoll_events.events)
+ int r;
+
+ if (!h->epoll_event.events)
return -1;
if (mode&MD_MODE_READ)
diff --git a/accel-pptpd/triton/spinlock.h b/accel-pptpd/triton/spinlock.h
index a75e29cb..7da93f8c 100644
--- a/accel-pptpd/triton/spinlock.h
+++ b/accel-pptpd/triton/spinlock.h
@@ -2,15 +2,17 @@
#define __TRITON_SPINLOCK_H
#ifdef USE_SPINLOCK
-typedef spinlock_t unsigned char;
+typedef unsigned char spinlock_t;
#define spin_lock(l) {while(__sync_lock_test_and_set(l,1);}
#define spin_unlock(l) __sync_lock_release(l)
#define SPINLOCK_INITIALIZER 0
#else
-typedef spinlock_t pthread_mutex_t;
+#include <pthread.h>
+typedef pthread_mutex_t spinlock_t;
#define spin_lock(l) pthread_mutex_lock(l)
#define spin_unlock(l) pthread_mutex_unlock(l)
#define SPINLOCK_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#define spinlock_init(l) pthread_mutex_init(l,NULL)
#endif
#endif
diff --git a/accel-pptpd/triton/timer.c b/accel-pptpd/triton/timer.c
index 57270f65..3adbde5a 100644
--- a/accel-pptpd/triton/timer.c
+++ b/accel-pptpd/triton/timer.c
@@ -4,22 +4,19 @@
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <string.h>
+#include <errno.h>
+#include <unistd.h>
#include "triton_p.h"
-static pthread_thread_t timer_thr;
-static void *timer_thread(void *arg);
-
-static spinlock_t timers_lock=SPINLOCK_INITIALIZER;
-static LIST_HEAD(timers);
-
-extern int max_events;
-static epoll_fd;
+int max_events=128;
+static int epoll_fd;
static struct epoll_event *epoll_events;
-static void tv_add(struct timeval *tv,int msec);
+static pthread_t timer_thr;
+static void *timer_thread(void *arg);
-void timer_init(void)
+int timer_init(void)
{
epoll_fd=epoll_create(1);
if (epoll_fd<0)
@@ -34,6 +31,8 @@ void timer_init(void)
fprintf(stderr,"cann't allocate memory\n");
return -1;
}
+
+ return 0;
}
void timer_run(void)
@@ -43,8 +42,8 @@ void timer_run(void)
void timer_terminate(void)
{
- pthread_cancel(&timer_thr);
- pthread_join(&timer_thr);
+ pthread_cancel(timer_thr);
+ pthread_join(timer_thr,NULL);
}
void *timer_thread(void *arg)
@@ -52,43 +51,44 @@ void *timer_thread(void *arg)
int i,n,r;
struct triton_timer_t *t;
- n=epoll_wait(epoll_fd,epoll_events,MAX_EVENTS,-1);
- if (n<0)
+ while(1)
{
- if (errno!=EINTR)
- perror("epoll_wait");
- continue;
- }
- if (n==0)
- return;
-
- for(i=0; i<n; i++)
- {
- t=(struct triton_md_handler_t*)epoll_events[i].data.ptr;
- spin_lock(&t->ctx->lock);
- list_add_tail(&t->entry2,&t->ctx->pending_timers);
- t->pending=1;
- r=triton_queue_ctx(t->ctx);
- spin_unlock(&t->ctx->lock);
- if (r)
- triton_thread_wakeup(ctx->thread);
+ n=epoll_wait(epoll_fd,epoll_events,max_events,-1);
+ if (n<0)
+ {
+ if (errno!=EINTR)
+ perror("epoll_wait");
+ continue;
+ }
+
+ for(i=0; i<n; i++)
+ {
+ t=(struct triton_timer_t*)epoll_events[i].data.ptr;
+ spin_lock(&t->ctx->lock);
+ list_add_tail(&t->entry2,&t->ctx->pending_timers);
+ t->pending=1;
+ r=triton_queue_ctx(t->ctx);
+ spin_unlock(&t->ctx->lock);
+ if (r)
+ triton_thread_wakeup(t->ctx->thread);
+ }
}
}
-int triton_timer_add(struct triton_timer_t *t)
+int triton_timer_add(struct triton_timer_t *t, int abs_time)
{
t->epoll_event.data.ptr=t;
t->epoll_event.events=EPOLLIN|EPOLLET;
if (!t->ctx)
t->ctx=default_ctx;
- t->fd=timerfd_create(CLOCK_MONOTONIC,0);
+ t->fd=timerfd_create(CLOCK_MONOTONIC,TFD_NONBLOCK);
if (t->fd<0)
{
fprintf(stderr,"timer: timerfd_create failed: %s\n",strerror(errno));
return -1;
}
- if (triton_timer_mod(t))
+ if (triton_timer_mod(t,abs_time))
{
close(t->fd);
return -1;
@@ -110,10 +110,8 @@ int triton_timer_add(struct triton_timer_t *t)
return 0;
}
-int triton_timer_mod(struct triton_timer_t *t)
+int triton_timer_mod(struct triton_timer_t *t,int abs_time)
{
- int flags;
-
struct itimerspec ts=
{
.it_value.tv_sec=t->expire_tv.tv_sec,
@@ -123,13 +121,9 @@ int triton_timer_mod(struct triton_timer_t *t)
};
if (t->expire_tv.tv_sec==0 && t->expire_tv.tv_usec==0)
- {
- ts.it_value=ts.interval;
- flags=0;
- }else
- flags=TFD_TIMER_ABSTIME;
+ ts.it_value=ts.it_interval;
- if (timerfd_settime(t->fd,flags,&ts,NULL))
+ if (timerfd_settime(t->fd,abs_time?TFD_TIMER_ABSTIME:0,&ts,NULL))
{
fprintf(stderr,"timer: timerfd_settime failed: %s\n",strerror(errno));
return -1;
diff --git a/accel-pptpd/triton/triton.c b/accel-pptpd/triton/triton.c
index 23a69c60..e4fa117c 100644
--- a/accel-pptpd/triton/triton.c
+++ b/accel-pptpd/triton/triton.c
@@ -1,4 +1,8 @@
#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
#include "triton_p.h"
@@ -6,19 +10,19 @@ int thread_count=64;
static spinlock_t threads_lock=SPINLOCK_INITIALIZER;
static LIST_HEAD(threads);
-static int threads_count;
+static LIST_HEAD(sleep_threads);
-static spinlock_t ctx_queue_lock=SPINLOCK_INITIALIZER;
static LIST_HEAD(ctx_queue);
static spinlock_t ctx_list_lock=SPINLOCK_INITIALIZER;
static LIST_HEAD(ctx_list);
struct triton_ctx_t *default_ctx;
+static int terminate;
void triton_thread_wakeup(struct triton_thread_t *thread)
{
- pthread_kill(&thread->thread,SIGUSR1);
+ pthread_kill(thread->thread,SIGUSR1);
}
static void* triton_thread(struct triton_thread_t *thread)
@@ -36,19 +40,11 @@ static void* triton_thread(struct triton_thread_t *thread)
{
sigwait(&set,&sig);
- if (thread->terminate)
- return NULL;
-
cont:
- if (thread->ctx->close)
+ if (thread->ctx->need_close)
{
- list_for_each_entry(h,&thread->ctx->handlers,entry)
- if (h->close)
- h->close(h);
- list_for_each_entry(t,&thread->ctx->timers,entry)
- if (t->close)
- t->close(t);
- thread->ctx->close=0;
+ thread->ctx->close(thread->ctx);
+ thread->ctx->need_close=0;
}
while (1)
@@ -56,15 +52,15 @@ cont:
spin_lock(&thread->ctx->lock);
if (!list_empty(&thread->ctx->pending_timers))
{
- t=list_entry(thread->ctx->pending_timers.next);
+ t=list_entry(thread->ctx->pending_timers.next,typeof(*t),entry2);
list_del(&t->entry2);
spin_unlock(&thread->ctx->lock);
if (t->expire(t))
continue;
}
- if (!list_empty(&thread->ctx->pending_events))
+ if (!list_empty(&thread->ctx->pending_handlers))
{
- h=list_entry(thread->ctx->pending_events.next);
+ h=list_entry(thread->ctx->pending_handlers.next,typeof(*h),entry2);
list_del(&h->entry2);
h->pending=0;
spin_unlock(&thread->ctx->lock);
@@ -73,7 +69,7 @@ cont:
if (h->read)
if (h->read(h))
continue;
- if (h->trig_epoll_events&(EPOLLOUT|EPOLLERR|EPOLLHUP))
+ if (h->trig_epoll_events&(EPOLLOUT))
if (h->write)
if (h->write(h))
continue;
@@ -82,6 +78,8 @@ cont:
}
thread->ctx->thread=NULL;
spin_unlock(&thread->ctx->lock);
+ if (thread->ctx->need_free)
+ thread->ctx->free(thread->ctx);
thread->ctx=NULL;
break;
}
@@ -89,18 +87,21 @@ cont:
spin_lock(&threads_lock);
if (!list_empty(&ctx_queue))
{
- thread->ctx=list_entry(ctx_queue.next);
+ thread->ctx=list_entry(ctx_queue.next,typeof(*thread->ctx),entry2);
list_del(&thread->ctx->entry2);
spin_unlock(&threads_lock);
spin_lock(&thread->ctx->lock);
- ctx->thread=thread;
- ctx->queue=0;
+ thread->ctx->thread=thread;
+ thread->ctx->queued=0;
spin_unlock(&thread->ctx->lock);
goto cont;
}else
{
- list_add(&thread->entry,&threads);
+ if (!terminate)
+ list_add(&thread->entry2,&sleep_threads);
spin_unlock(&threads_lock);
+ if (terminate)
+ return NULL;
}
}
}
@@ -110,21 +111,18 @@ struct triton_thread_t *create_thread()
struct triton_thread_t *thread=malloc(sizeof(*thread));
memset(thread,0,sizeof(*thread));
- pthread_mutex_init(&thread->lock);
- pthread_cond_init(&thread->cond);
- pthread_create(&thread->thread,NULL,md_thread,thread);
- ++threads_count;
+ pthread_create(&thread->thread,NULL,(void*(*)(void*))triton_thread,thread);
return thread;
}
-void triton_queue_ctx(struct triton_ctx_t *ctx)
+int triton_queue_ctx(struct triton_ctx_t *ctx)
{
if (ctx->thread || ctx->queued)
return 0;
spin_lock(&threads_lock);
- if (list_empty(&threads))
+ if (list_empty(&sleep_threads))
{
list_add_tail(&ctx->entry2,&ctx_queue);
spin_unlock(&threads_lock);
@@ -132,8 +130,8 @@ void triton_queue_ctx(struct triton_ctx_t *ctx)
return 0;
}
- ctx->thread=list_entry(threads.next);
- list_del(&ctx->thread->entry);
+ ctx->thread=list_entry(sleep_threads.next,typeof(*ctx->thread),entry2);
+ list_del(&ctx->thread->entry2);
spin_unlock(&threads_lock);
return 1;
@@ -141,7 +139,7 @@ void triton_queue_ctx(struct triton_ctx_t *ctx)
void triton_register_ctx(struct triton_ctx_t *ctx)
{
- pthread_mutex_init(&ctx->lock);
+ spinlock_init(&ctx->lock);
INIT_LIST_HEAD(&ctx->handlers);
INIT_LIST_HEAD(&ctx->timers);
INIT_LIST_HEAD(&ctx->pending_handlers);
@@ -154,12 +152,13 @@ void triton_register_ctx(struct triton_ctx_t *ctx)
void triton_unregister_ctx(struct triton_ctx_t *ctx)
{
+ ctx->need_free=1;
spin_lock(&ctx_list_lock);
- list_add_tail(&ctx->entry,&ctx_list);
+ list_del(&ctx->entry);
spin_unlock(&ctx_list_lock);
}
-int triton_init()
+int triton_init(const char *conf_file)
{
default_ctx=malloc(sizeof(*default_ctx));
if (!default_ctx)
@@ -169,10 +168,19 @@ int triton_init()
}
triton_register_ctx(default_ctx);
+ if (conf_load(conf_file))
+ return -1;
+
+ if (log_init())
+ return -1;
+
if (md_init())
return -1;
+
if (timer_init())
return -1;
+
+ return 0;
}
void triton_run()
@@ -180,11 +188,13 @@ void triton_run()
struct triton_thread_t *t;
int i;
- for(i=0;i<max_threads;i++)
+ for(i=0;i<thread_count;i++)
{
t=create_thread();
list_add_tail(&t->entry,&threads);
+ list_add_tail(&t->entry2,&sleep_threads);
}
+
md_run();
timer_run();
}
@@ -192,17 +202,29 @@ void triton_run()
void triton_terminate()
{
struct triton_ctx_t *ctx;
- pthread_mutex_lock(&ctx_list_lock);
+ struct triton_thread_t *t;
+
+ md_terminate();
+ timer_terminate();
+
+ spin_lock(&ctx_list_lock);
list_for_each_entry(ctx,&ctx_list,entry)
{
- pthread_mutex_lock(&ctx->lock);
- ctx->close=1;
+ spin_lock(&ctx->lock);
+ ctx->need_close=1;
triton_queue_ctx(ctx);
- pthread_mutex_unlock(&ctx->lock);
+ spin_unlock(&ctx->lock);
}
- pthread_mutex_unlock(&ctx_list_lock);
+ spin_unlock(&ctx_list_lock);
- timer_terminate();
- md_terminate();
+ spin_lock(&threads_lock);
+ terminate=1;
+ spin_unlock(&threads_lock);
+
+ list_for_each_entry(t,&threads,entry)
+ triton_thread_wakeup(t);
+
+ list_for_each_entry(t,&threads,entry)
+ pthread_join(t->thread,NULL);
}
diff --git a/accel-pptpd/triton/triton.h b/accel-pptpd/triton/triton.h
index 0e9b73bf..73640219 100644
--- a/accel-pptpd/triton/triton.h
+++ b/accel-pptpd/triton/triton.h
@@ -5,11 +5,13 @@
#include <pthread.h>
#include <sys/epoll.h>
-#include <triton.h>
+#include "list.h"
+#include "spinlock.h"
struct triton_thread_t
{
struct list_head entry;
+ struct list_head entry2;
pthread_t thread;
int terminate:1;
struct triton_ctx_t *ctx;
@@ -23,11 +25,15 @@ struct triton_ctx_t
struct list_head handlers;
struct list_head timers;
- triton_thread_t *thread;
+ struct triton_thread_t *thread;
struct list_head pending_handlers;
struct list_head pending_timers;
int queued:1;
- int close:1;
+ int need_close:1;
+ int need_free:1;
+
+ void (*close)(struct triton_ctx_t*);
+ void (*free)(struct triton_ctx_t*);
};
struct triton_md_handler_t
@@ -38,7 +44,7 @@ struct triton_md_handler_t
struct list_head entry2;
struct triton_ctx_t *ctx;
struct epoll_event epoll_event;
- uint32_t trig_epoll_event;
+ uint32_t trig_epoll_events;
int pending:1;
//=========
@@ -48,14 +54,16 @@ struct triton_md_handler_t
int (*read)(struct triton_md_handler_t *);
int (*write)(struct triton_md_handler_t *);
- void (*close)(struct triton_md_handler_t *);
//=========
};
struct triton_timer_t
{
struct list_head entry;
- int active;
+ struct list_head entry2;
+ struct epoll_event epoll_event;
+ struct triton_ctx_t *ctx;
+ int fd;
int pending:1;
struct timeval expire_tv;
@@ -63,28 +71,36 @@ struct triton_timer_t
int (*expire)(struct triton_timer_t *);
};
+struct conf_option_t
+{
+ struct list_head entry;
+
+ char *name;
+ char *val;
+};
+
+struct conf_sect_t
+{
+ const char *name;
+ struct list_head items;
+};
+
+void triton_register_ctx(struct triton_ctx_t *);
+void triton_unregister_ctx(struct triton_ctx_t *);
+
#define MD_MODE_READ 1
#define MD_MODE_WRITE 2
void triton_md_register_handler(struct triton_md_handler_t *h);
void triton_md_unregister_handler(struct triton_md_handler_t *h);
-void triton_md_enable_handler(struct triton_md_handler_t *h, int mode);
-void triton_md_disable_handler(struct triton_md_handler_t *h,int mode);
-void triton_md_set_timeout(struct triton_md_handler_t *h, int msec);
+int triton_md_enable_handler(struct triton_md_handler_t *h, int mode);
+int triton_md_disable_handler(struct triton_md_handler_t *h,int mode);
-void triton_timer_add(struct triton_timer_t*);
+int triton_timer_add(struct triton_timer_t*,int abs_time);
+int triton_timer_mod(struct triton_timer_t*,int abs_time);
void triton_timer_del(struct triton_timer_t*);
-typedef void (*triton_ss_func)(void);
-void triton_timer_single_shot1(int twait,triton_ss_func,int argc,...);
-void triton_timer_single_shot2(struct timeval *shot_tv,triton_ss_func,int argc,...);
-void triton_timer_single_shot3(int tv_sec,int tv_usec,triton_ss_func,int argc,...);
-
-int triton_get_int_option(const char *str);
-const char* triton_get_str_option(const char *str);
-double triton_get_double_option(const char *str);
-
-void triton_terminate(void);
-void triton_process_events(void);
+struct conf_sect_t *conf_get_section(const char *name);
+char *conf_get_opt(const char *sect, const char *name);
#define TRITON_OK 0
#define TRITON_ERR_NOCOMP -1
@@ -96,6 +112,7 @@ void triton_process_events(void);
#define TRITON_ERR_BUSY -5
int triton_init(const char *conf_file);
-int triton_run(int (*post_init)(void*),void *arg);
+void triton_run(void);
+void triton_terminate(void);
#endif
diff --git a/accel-pptpd/triton/triton_p.h b/accel-pptpd/triton/triton_p.h
index fae48487..e10c7a8e 100644
--- a/accel-pptpd/triton/triton_p.h
+++ b/accel-pptpd/triton/triton_p.h
@@ -4,39 +4,16 @@
#include "triton.h"
#include "list.h"
-#include <stdarg.h>
-
-#define MAX_ARGS 32
-
-struct option_t
-{
- struct list_head entry;
-
- char *name;
- char *val;
-};
-
-struct timer_t
-{
- struct list_head entry;
- int del;
- struct triton_timer_t *timer;
-};
-
-struct timer_single_shot_t
-{
- struct list_head entry;
-
- struct timeval expire_tv;
- int arg_cnt;
- void *args;
- triton_ss_func ss_func;
-};
-
-extern void md_run();
-extern void md_terminate();
-extern void timer_run();
-extern void timer_terminate();
-extern struct triton_ctx_t *default_ctx;
+int log_init(void);
+int md_init();
+void md_run();
+void md_terminate();
+int timer_init();
+void timer_run();
+void timer_terminate();
+struct triton_ctx_t *default_ctx;
+int triton_queue_ctx(struct triton_ctx_t*);
+void triton_thread_wakeup(struct triton_thread_t*);
+int conf_load(const char *fname);
#endif
diff --git a/doc/rfc3079.txt b/doc/rfc3079.txt
new file mode 100644
index 00000000..4d7ba0de
--- /dev/null
+++ b/doc/rfc3079.txt
@@ -0,0 +1,1179 @@
+
+
+
+
+
+
+Network Working Group G. Zorn
+Request for Comments: 3079 cisco Systems
+Category: Informational March 2001
+
+
+ Deriving Keys for use with Microsoft Point-to-Point Encryption (MPPE)
+
+Status of this Memo
+
+ This memo provides information for the Internet community. It does
+ not specify an Internet standard of any kind. Distribution of this
+ memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2001). All Rights Reserved.
+
+Abstract
+
+ The Point-to-Point Protocol (PPP) provides a standard method for
+ transporting multi-protocol datagrams over point-to-point links.
+
+ The PPP Compression Control Protocol provides a method to negotiate
+ and utilize compression protocols over PPP encapsulated links.
+
+ Microsoft Point to Point Encryption (MPPE) is a means of representing
+ PPP packets in an encrypted form. MPPE uses the RSA RC4 algorithm to
+ provide data confidentiality. The length of the session key to be
+ used for initializing encryption tables can be negotiated. MPPE
+ currently supports 40-bit, 56-bit and 128-bit session keys. MPPE
+ session keys are changed frequently; the exact frequency depends upon
+ the options negotiated, but may be every packet. MPPE is negotiated
+ within option 18 in the Compression Control Protocol.
+
+ This document describes the method used to derive initial MPPE
+ session keys from a variety of credential types. It is expected that
+ this memo will be updated whenever Microsoft defines a new key
+ derivation method for MPPE, since its primary purpose is to provide
+ an open, easily accessible reference for third-parties wishing to
+ interoperate with Microsoft products.
+
+ MPPE itself (including the protocol used to negotiate its use, the
+ details of the encryption method used and the algorithm used to
+ change session keys during a session) is described in RFC 3078.
+
+
+
+
+
+
+
+Zorn Informational [Page 1]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+Table of Contents
+
+ 1. Specification of Requirements ............................... 2
+ 2. Deriving Session Keys from MS-CHAP Credentials .............. 2
+ 2.1. Generating 40-bit Session Keys ............................ 3
+ 2.2. Generating 56-bit Session Keys ............................ 3
+ 2.3. Generating 128-bit Session Keys ........................... 4
+ 2.4. Key Derivation Functions .................................. 5
+ 2.5. Sample Key Derivations .................................... 6
+ 2.5.1. Sample 40-bit Key Derivation ............................ 6
+ 2.5.2. Sample 56-bit Key Derivation ............................ 6
+ 2.5.3. Sample 128-bit Key Derivation ........................... 7
+ 3. Deriving Session Keys from MS-CHAP-2 Credentials ............ 7
+ 3.1. Generating 40-bit Session Keys ............................ 8
+ 3.2. Generating 56-bit Session Keys ............................ 9
+ 3.3. Generating 128-bit Session Keys ...........................10
+ 3.4. Key Derivation Functions ..................................11
+ 3.5. Sample Key Derivations ....................................13
+ 3.5.1. Sample 40-bit Key Derivation ............................13
+ 3.5.2. Sample 56-bit Key Derivation ............................14
+ 3.5.3. Sample 128-bit Key Derivation ...........................15
+ 4. Deriving MPPE Session Keys from TLS Session Keys ............16
+ 4.1. Generating 40-bit Session Keys ............................16
+ 4.2. Generating 56-bit Session Keys ............................17
+ 4.3. Generating 128-bit Session Keys ...........................17
+ 5. Security Considerations .....................................18
+ 5.1. MS-CHAP Credentials .......................................18
+ 5.2. EAP-TLS Credentials .......................................19
+ 6. References ..................................................19
+ 7. Acknowledgements ............................................20
+ 8. Author's Address ............................................20
+ 9. Full Copyright Statement ....................................21
+
+1. Specification of Requirements
+
+ In this document, the key words "MAY", "MUST, "MUST NOT", "optional",
+ "recommended", "SHOULD", and "SHOULD NOT" are to be interpreted as
+ described in [6].
+
+2. Deriving Session Keys from MS-CHAP Credentials
+
+ The Microsoft Challenge-Handshake Authentication Protocol (MS-CHAP-1)
+ [2] is a Microsoft-proprietary PPP [1] authentication protocol,
+ providing the functionality to which LAN-based users are accustomed
+ while integrating the encryption and hashing algorithms used on
+ Windows networks.
+
+
+
+
+
+Zorn Informational [Page 2]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+ The following sections detail the methods used to derive initial
+ session keys (40-, 56- and 128-bit) from MS-CHAP-1 credentials.
+
+ Implementation Note
+
+ The initial session key in both directions is derived from the
+ credentials of the peer that initiated the call and the challenge
+ used (if any) is the challenge from the first authentication.
+ This is true for both unilateral and bilateral authentication, as
+ well as for each link in a multilink bundle. In the multi-chassis
+ multilink case, implementations are responsible for ensuring that
+ the correct keys are generated on all participating machines.
+
+2.1. Generating 40-bit Session Keys
+
+ MPPE uses a derivative of the peer's LAN Manager password as the 40-
+ bit session key used for initializing the RC4 encryption tables.
+
+ The first step is to obfuscate the peer's password using the
+ LmPasswordHash() function (described in [2]). The first 8 octets of
+ the result are used as the basis for the session key generated in the
+ following way:
+
+/*
+* PasswordHash is the basis for the session key
+* SessionKey is a copy of PasswordHash and is the generative session key
+* 8 is the length (in octets) of the key to be generated.
+*
+*/
+Get_Key(PasswordHash, SessionKey, 8)
+
+/*
+* The effective length of the key is reduced to 40 bits by
+* replacing the first three bytes as follows:
+*/
+SessionKey[0] = 0xd1 ;
+SessionKey[1] = 0x26 ;
+SessionKey[2] = 0x9e ;
+
+2.2. Generating 56-bit Session Keys
+
+ MPPE uses a derivative of the peer's LAN Manager password as the 56-
+ bit session key used for initializing the RC4 encryption tables.
+
+ The first step is to obfuscate the peer's password using the
+ LmPasswordHash() function (described in [2]). The first 8 octets of
+ the result are used as the basis for the session key generated in the
+ following way:
+
+
+
+Zorn Informational [Page 3]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+/*
+* PasswordHash is the basis for the session key
+* SessionKey is a copy of PasswordHash and is the generative session key
+* 8 is the length (in octets) of the key to be generated.
+*
+*/
+Get_Key(PasswordHash, SessionKey, 8)
+
+/*
+* The effective length of the key is reduced to 56 bits by
+* replacing the first byte as follows:
+*/
+SessionKey[0] = 0xd1 ;
+
+2.3. Generating 128-bit Session Keys
+
+ MPPE uses a derivative of the peer's Windows NT password as the 128-
+ bit session key used for initializing encryption tables.
+
+ The first step is to obfuscate the peer's password using
+ NtPasswordHash() function as described in [2]. The first 16 octets
+ of the result are then hashed again using the MD4 algorithm. The
+ first 16 octets of the second hash are used as the basis for the
+ session key generated in the following way:
+
+/*
+* Challenge (as described in [9]) is sent by the PPP authenticator
+* during authentication and is 8 octets long.
+* NtPasswordHashHash is the basis for the session key.
+* On return, InitialSessionKey contains the initial session
+* key to be used.
+*/
+Get_Start_Key(Challenge, NtPasswordHashHash, InitialSessionKey)
+
+/*
+* CurrentSessionKey is a copy of InitialSessionKey
+* and is the generative session key.
+* Length (in octets) of the key to generate is 16.
+*
+*/
+Get_Key(InitialSessionKey, CurrentSessionKey, 16)
+
+
+
+
+
+
+
+
+
+
+Zorn Informational [Page 4]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+2.4. Key Derivation Functions
+
+ The following procedures are used to derive the session key.
+
+/*
+ * Pads used in key derivation
+ */
+
+SHApad1[40] =
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+SHApad2[40] =
+ {0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2};
+
+/*
+ * SHAInit(), SHAUpdate() and SHAFinal() functions are an
+ * implementation of Secure Hash Algorithm (SHA-1) [7]. These are
+ * available in public domain or can be licensed from
+ * RSA Data Security, Inc.
+ *
+ * 1) InitialSessionKey is 8 octets long for 56- and 40-bit
+ * session keys, 16 octets long for 128 bit session keys.
+ * 2) CurrentSessionKey is same as InitialSessionKey when this
+ * routine is called for the first time for the session.
+ */
+
+Get_Key(
+IN InitialSessionKey,
+IN/OUT CurrentSessionKey
+IN LengthOfDesiredKey )
+{
+ SHAInit(Context)
+ SHAUpdate(Context, InitialSessionKey, LengthOfDesiredKey)
+ SHAUpdate(Context, SHAPad1, 40)
+ SHAUpdate(Context, CurrentSessionKey, LengthOfDesiredKey)
+ SHAUpdate(Context, SHAPad2, 40)
+ SHAFinal(Context, Digest)
+ memcpy(CurrentSessionKey, Digest, LengthOfDesiredKey)
+}
+
+Get_Start_Key(
+IN Challenge,
+
+
+
+Zorn Informational [Page 5]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+IN NtPasswordHashHash,
+OUT InitialSessionKey)
+{
+ SHAInit(Context)
+ SHAUpdate(Context, NtPasswordHashHash, 16)
+ SHAUpdate(Context, NtPasswordHashHash, 16)
+ SHAUpdate(Context, Challenge, 8)
+ SHAFinal(Context, Digest)
+ memcpy(InitialSessionKey, Digest, 16)
+}
+
+2.5. Sample Key Derivations
+
+ The following sections illustrate 40-, 56- and 128-bit key
+ derivations. All intermediate values are in hexadecimal.
+
+2.5.1. Sample 40-bit Key Derivation
+
+
+ Initial Values
+ Password = "clientPass"
+
+ Step 1: LmPasswordHash(Password, PasswordHash)
+ PasswordHash = 76 a1 52 93 60 96 d7 83 0e 23 90 22 74 04 af d2
+
+ Step 2: Copy PasswordHash to SessionKey
+ SessionKey = 76 a1 52 93 60 96 d7 83 0e 23 90 22 74 04 af d2
+
+ Step 3: GetKey(PasswordHash, SessionKey, 8)
+ SessionKey = d8 08 01 53 8c ec 4a 08
+
+ Step 4: Reduce the effective key length to 40 bits
+ SessionKey = d1 26 9e 53 8c ec 4a 08
+
+2.5.2. Sample 56-bit Key Derivation
+
+ Initial Values
+ Password = "clientPass"
+
+ Step 1: LmPasswordHash(Password, PasswordHash)
+ PasswordHash = 76 a1 52 93 60 96 d7 83 0e 23 90 22 74 04 af d2
+
+ Step 2: Copy PasswordHash to SessionKey
+ SessionKey = 76 a1 52 93 60 96 d7 83 0e 23 90 22 74 04 af d2
+
+ Step 3: GetKey(PasswordHash, SessionKey, 8)
+ SessionKey = d8 08 01 53 8c ec 4a 08
+
+
+
+
+Zorn Informational [Page 6]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+ Step 4: Reduce the effective key length to 56 bits
+ SessionKey = d1 08 01 53 8c ec 4a 08
+
+2.5.3. Sample 128-bit Key Derivation
+
+Initial Values
+ Password = "clientPass"
+ Challenge = 10 2d b5 df 08 5d 30 41
+
+Step 1: NtPasswordHash(Password, PasswordHash)
+ PasswordHash = 44 eb ba 8d 53 12 b8 d6 11 47 44 11 f5 69 89 ae
+
+Step 2: PasswordHashHash = MD4(PasswordHash)
+ PasswordHashHash = 41 c0 0c 58 4b d2 d9 1c 40 17 a2 a1 2f a5 9f 3f
+
+Step 3: GetStartKey(Challenge, PasswordHashHash, InitialSessionKey)
+ InitialSessionKey = a8 94 78 50 cf c0 ac ca d1 78 9f b6 2d dc dd b0
+
+Step 4: Copy InitialSessionKey to CurrentSessionKey
+ CurrentSessionKey = a8 94 78 50 cf c0 ac c1 d1 78 9f b6 2d dc dd b0
+
+Step 5: GetKey(InitialSessionKey, CurrentSessionKey, 16)
+ CurrentSessionKey = 59 d1 59 bc 09 f7 6f 1d a2 a8 6a 28 ff ec 0b 1e
+
+3. Deriving Session Keys from MS-CHAP-2 Credentials
+
+ Version 2 of the Microsoft Challenge-Handshake Authentication
+ Protocol (MS-CHAP-2) [8] is a Microsoft-proprietary PPP
+ authentication protocol, providing the functionality to which LAN-
+ based users are accustomed while integrating the encryption and
+ hashing algorithms used on Windows networks.
+
+ The following sections detail the methods used to derive initial
+ session keys from MS-CHAP-2 credentials. 40-, 56- and 128-bit keys
+ are all derived using the same algorithm from the authenticating
+ peer's Windows NT password. The only difference is in the length of
+ the keys and their effective strength: 40- and 56-bit keys are 8
+ octets in length, while 128-bit keys are 16 octets long. Separate
+ keys are derived for the send and receive directions of the session.
+
+ Implementation Note
+
+ The initial session keys in both directions are derived from the
+ credentials of the peer that initiated the call and the challenges
+ used are those from the first authentication. This is true as
+ well for each link in a multilink bundle. In the multi-chassis
+ multilink case, implementations are responsible for ensuring that
+ the correct keys are generated on all participating machines.
+
+
+
+Zorn Informational [Page 7]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+3.1. Generating 40-bit Session Keys
+
+ When used in conjunction with MS-CHAP-2 authentication, the initial
+ MPPE session keys are derived from the peer's Windows NT password.
+
+ The first step is to obfuscate the peer's password using
+ NtPasswordHash() function as described in [8].
+
+ NtPasswordHash(Password, PasswordHash)
+
+ The first 16 octets of the result are then hashed again using the MD4
+ algorithm.
+
+ PasswordHashHash = md4(PasswordHash)
+
+ The first 16 octets of this second hash are used together with the
+ NT- Response field from the MS-CHAP-2 Response packet [8] as the
+ basis for the master session key:
+
+ GetMasterKey(PasswordHashHash, NtResponse, MasterKey)
+
+ Once the master key has been generated, it is used to derive two 40-
+ bit session keys, one for sending and one for receiving:
+
+ GetAsymmetricStartKey(MasterKey, MasterSendKey, 8, TRUE, TRUE)
+ GetAsymmetricStartKey(MasterKey, MasterReceiveKey, 8, FALSE, TRUE)
+
+ The master session keys are never used to encrypt or decrypt data;
+ they are only used in the derivation of transient session keys. The
+ initial transient session keys are obtained by calling the function
+ GetNewKeyFromSHA() (described in [3]):
+
+GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 8, SendSessionKey)
+GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 8,
+ ReceiveSessionKey)
+
+ Next, the effective strength of both keys is reduced by setting the
+ first three octets to known constants:
+
+ SendSessionKey[0] = ReceiveSessionKey[0] = 0xd1
+ SendSessionKey[1] = ReceiveSessionKey[1] = 0x26
+ SendSessionKey[2] = ReceiveSessionKey[2] = 0x9e
+
+ Finally, the RC4 tables are initialized using the new session keys:
+
+ rc4_key(SendRC4key, 8, SendSessionKey)
+ rc4_key(ReceiveRC4key, 8, ReceiveSessionKey)
+
+
+
+
+Zorn Informational [Page 8]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+3.2. Generating 56-bit Session Keys
+
+ When used in conjunction with MS-CHAP-2 authentication, the initial
+ MPPE session keys are derived from the peer's Windows NT password.
+
+ The first step is to obfuscate the peer's password using
+ NtPasswordHash() function as described in [8].
+
+ NtPasswordHash(Password, PasswordHash)
+
+ The first 16 octets of the result are then hashed again using the MD4
+ algorithm.
+
+ PasswordHashHash = md4(PasswordHash)
+
+ The first 16 octets of this second hash are used together with the
+ NT-Response field from the MS-CHAP-2 Response packet [8] as the basis
+ for the master session key:
+
+ GetMasterKey(PasswordHashHash, NtResponse, MasterKey)
+
+ Once the master key has been generated, it is used to derive two
+ 56-bit session keys, one for sending and one for receiving:
+
+ GetAsymmetricStartKey(MasterKey, MasterSendKey, 8, TRUE, TRUE)
+ GetAsymmetricStartKey(MasterKey, MasterReceiveKey, 8, FALSE, TRUE)
+
+ The master session keys are never used to encrypt or decrypt data;
+ they are only used in the derivation of transient session keys. The
+ initial transient session keys are obtained by calling the function
+ GetNewKeyFromSHA() (described in [3]):
+
+GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 8, SendSessionKey)
+GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 8,
+ ReceiveSessionKey)
+
+ Next, the effective strength of both keys is reduced by setting the
+ first octet to a known constant:
+
+ SendSessionKey[0] = ReceiveSessionKey[0] = 0xd1
+
+ Finally, the RC4 tables are initialized using the new session keys:
+
+ rc4_key(SendRC4key, 8, SendSessionKey)
+ rc4_key(ReceiveRC4key, 8, ReceiveSessionKey)
+
+
+
+
+
+
+Zorn Informational [Page 9]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+3.3. Generating 128-bit Session Keys
+
+ When used in conjunction with MS-CHAP-2 authentication, the initial
+ MPPE session keys are derived from the peer's Windows NT password.
+
+ The first step is to obfuscate the peer's password using
+ NtPasswordHash() function as described in [8].
+
+ NtPasswordHash(Password, PasswordHash)
+
+ The first 16 octets of the result are then hashed again using the MD4
+ algorithm.
+
+ PasswordHashHash = md4(PasswordHash)
+
+ The first 16 octets of this second hash are used together with the
+ NT-Response field from the MS-CHAP-2 Response packet [8] as the basis
+ for the master session key:
+
+ GetMasterKey(PasswordHashHash, NtResponse, MasterKey)
+
+ Once the master key has been generated, it is used to derive two
+ 128-bit master session keys, one for sending and one for receiving:
+
+GetAsymmetricStartKey(MasterKey, MasterSendKey, 16, TRUE, TRUE)
+GetAsymmetricStartKey(MasterKey, MasterReceiveKey, 16, FALSE, TRUE)
+
+ The master session keys are never used to encrypt or decrypt data;
+ they are only used in the derivation of transient session keys. The
+ initial transient session keys are obtained by calling the function
+ GetNewKeyFromSHA() (described in [3]):
+
+GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 16, SendSessionKey)
+GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 16,
+ ReceiveSessionKey)
+
+ Finally, the RC4 tables are initialized using the new session keys:
+
+ rc4_key(SendRC4key, 16, SendSessionKey)
+ rc4_key(ReceiveRC4key, 16, ReceiveSessionKey)
+
+
+
+
+
+
+
+
+
+
+
+Zorn Informational [Page 10]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+3.4. Key Derivation Functions
+
+ The following procedures are used to derive the session key.
+
+/*
+ * Pads used in key derivation
+ */
+
+SHSpad1[40] =
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+SHSpad2[40] =
+ {0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2};
+
+/*
+ * "Magic" constants used in key derivations
+ */
+
+Magic1[27] =
+ {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79};
+
+Magic2[84] =
+ {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+ 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+ 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e};
+
+Magic3[84] =
+ {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+ 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+
+
+
+Zorn Informational [Page 11]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e};
+
+
+ GetMasterKey(
+ IN 16-octet PasswordHashHash,
+ IN 24-octet NTResponse,
+ OUT 16-octet MasterKey )
+ {
+ 20-octet Digest
+
+ ZeroMemory(Digest, sizeof(Digest));
+
+ /*
+ * SHSInit(), SHSUpdate() and SHSFinal()
+ * are an implementation of the Secure Hash Standard [7].
+ */
+
+ SHSInit(Context);
+ SHSUpdate(Context, PasswordHashHash, 16);
+ SHSUpdate(Context, NTResponse, 24);
+ SHSUpdate(Context, Magic1, 27);
+ SHSFinal(Context, Digest);
+
+ MoveMemory(MasterKey, Digest, 16);
+ }
+
+ VOID
+ GetAsymetricStartKey(
+ IN 16-octet MasterKey,
+ OUT 8-to-16 octet SessionKey,
+ IN INTEGER SessionKeyLength,
+ IN BOOLEAN IsSend,
+ IN BOOLEAN IsServer )
+ {
+
+ 20-octet Digest;
+
+ ZeroMemory(Digest, 20);
+
+ if (IsSend) {
+ if (IsServer) {
+ s = Magic3
+ } else {
+ s = Magic2
+ }
+ } else {
+ if (IsServer) {
+
+
+
+Zorn Informational [Page 12]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+ s = Magic2
+ } else {
+ s = Magic3
+ }
+ }
+
+ /*
+ * SHSInit(), SHSUpdate() and SHSFinal()
+ * are an implementation of the Secure Hash Standard [7].
+ */
+
+ SHSInit(Context);
+ SHSUpdate(Context, MasterKey, 16);
+ SHSUpdate(Context, SHSpad1, 40);
+ SHSUpdate(Context, s, 84);
+ SHSUpdate(Context, SHSpad2, 40);
+ SHSFinal(Context, Digest);
+
+ MoveMemory(SessionKey, Digest, SessionKeyLength);
+ }
+
+3.5. Sample Key Derivations
+
+ The following sections illustrate 40-, 56- and 128-bit key
+ derivations. All intermediate values are in hexadecimal.
+
+3.5.1. Sample 40-bit Key Derivation
+
+Initial Values
+ UserName = "User"
+ = 55 73 65 72
+
+ Password = "clientPass"
+ = 63 00 6C 00 69 00 65 00 6E 00
+ 74 00 50 00 61 00 73 00 73 00
+
+ AuthenticatorChallenge = 5B 5D 7C 7D 7B 3F 2F 3E 3C 2C
+ 60 21 32 26 26 28
+ PeerChallenge = 21 40 23 24 25 5E 26 2A 28 29 5F 2B 3A 33 7C 7E
+
+ Challenge = D0 2E 43 86 BC E9 12 26
+
+ NT-Response =
+ 82 30 9E CD 8D 70 8B 5E A0 8F AA 39 81 CD 83 54 42 33
+ 11 4A 3D 85 D6 DF
+
+Step 1: NtPasswordHash(Password, PasswordHash)
+ PasswordHash = 44 EB BA 8D 53 12 B8 D6 11 47 44 11 F5 69 89 AE
+
+
+
+Zorn Informational [Page 13]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+Step 2: PasswordHashHash = MD4(PasswordHash)
+ PasswordHashHash = 41 C0 0C 58 4B D2 D9 1C 40 17 A2 A1 2F A5 9F 3F
+
+Step 3: Derive the master key (GetMasterKey())
+ MasterKey = FD EC E3 71 7A 8C 83 8C B3 88 E5 27 AE 3C DD 31
+
+Step 4: Derive the master send session key (GetAsymmetricStartKey())
+ SendStartKey40 = 8B 7C DC 14 9B 99 3A 1B
+
+Step 5: Derive the initial send session key (GetNewKeyFromSHA())
+ SendSessionKey40 = D1 26 9E C4 9F A6 2E 3E
+
+Sample Encrypted Message
+ rc4(SendSessionKey40, "test message") = 92 91 37 91 7E 58 03 D6
+ 68 D7 58 98
+
+3.5.2. Sample 56-bit Key Derivation
+
+Initial Values
+ UserName = "User"
+ = 55 73 65 72
+
+ Password = "clientPass"
+ = 63 00 6C 00 69 00 65 00 6E 00 74 00 50
+ 00 61 00 73 00 73 00
+
+ AuthenticatorChallenge = 5B 5D 7C 7D 7B 3F 2F 3E 3C 2C
+ 60 21 32 26 26 28
+ PeerChallenge = 21 40 23 24 25 5E 26 2A 28 29 5F 2B 3A 33 7C 7E
+
+ Challenge = D0 2E 43 86 BC E9 12 26
+
+ NT-Response =
+ 82 30 9E CD 8D 70 8B 5E A0 8F AA 39 81 CD 83 54 42 33
+ 11 4A 3D 85 D6 DF
+
+Step 1: NtPasswordHash(Password, PasswordHash)
+ PasswordHash = 44 EB BA 8D 53 12 B8 D6 11 47 44 11 F5 69 89 AE
+
+Step 2: PasswordHashHash = MD4(PasswordHash)
+ PasswordHashHash = 41 C0 0C 58 4B D2 D9 1C 40 17 A2 A1 2F A5 9F 3F
+
+Step 3: Derive the master key (GetMasterKey())
+ MasterKey = FD EC E3 71 7A 8C 83 8C B3 88 E5 27 AE 3C DD 31
+
+Step 4: Derive the master send session key (GetAsymmetricStartKey())
+ SendStartKey56 = 8B 7C DC 14 9B 99 3A 1B
+
+
+
+
+Zorn Informational [Page 14]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+Step 5: Derive the initial send session key (GetNewKeyFromSHA())
+ SendSessionKey56 = D1 5C 00 C4 9F A6 2E 3E
+
+Sample Encrypted Message
+ rc4(SendSessionKey40, "test message") = 3F 10 68 33 FA 44 8D
+ A8 42 BC 57 58
+
+3.5.3. Sample 128-bit Key Derivation
+
+Initial Values
+ UserName = "User"
+ = 55 73 65 72
+
+ Password = "clientPass"
+ = 63 00 6C 00 69 00 65 00 6E 00
+ 74 00 50 00 61 00 73 00 73 00
+
+ AuthenticatorChallenge = 5B 5D 7C 7D 7B 3F 2F 3E 3C 2C
+ 60 21 32 26 26 28
+
+ PeerChallenge = 21 40 23 24 25 5E 26 2A 28 29 5F 2B 3A 33 7C 7E
+
+ Challenge = D0 2E 43 86 BC E9 12 26
+
+ NT-Response =
+ 82 30 9E CD 8D 70 8B 5E A0 8F AA 39 81 CD 83 54 42 33
+ 11 4A 3D 85 D6 DF
+
+Step 1: NtPasswordHash(Password, PasswordHash)
+ PasswordHash = 44 EB BA 8D 53 12 B8 D6 11 47 44 11 F5 69 89 AE
+
+Step 2: PasswordHashHash = MD4(PasswordHash)
+ PasswordHashHash = 41 C0 0C 58 4B D2 D9 1C 40 17 A2 A1 2F A5 9F 3F
+
+Step 2: Derive the master key (GetMasterKey())
+ MasterKey = FD EC E3 71 7A 8C 83 8C B3 88 E5 27 AE 3C DD 31
+
+Step 3: Derive the send master session key (GetAsymmetricStartKey())
+
+ SendStartKey128 = 8B 7C DC 14 9B 99 3A 1B A1 18 CB 15 3F 56 DC CB
+
+Step 4: Derive the initial send session key (GetNewKeyFromSHA())
+ SendSessionKey128 = 40 5C B2 24 7A 79 56 E6 E2 11 00 7A E2 7B 22 D4
+
+Sample Encrypted Message
+ rc4(SendSessionKey128, "test message") = 81 84 83 17 DF 68
+ 84 62 72 FB 5A BE
+
+
+
+
+Zorn Informational [Page 15]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+4. Deriving MPPE Session Keys from TLS Session Keys
+
+ The Extensible Authentication Protocol (EAP) [10] is a PPP extension
+ that provides support for additional authentication methods within
+ PPP. Transport Level Security (TLS) [11] provides for mutual
+ authentication, integrity-protected ciphersuite negotiation and key
+ exchange between two endpoints. EAP-TLS [12] is an EAP
+ authentication type which allows the use of TLS within the PPP
+ authentication framework. The following sections describe the
+ methods used to derive initial session keys from TLS session keys.
+ 56-, 40- and 128-bit keys are derived using the same algorithm. The
+ only difference is in the length of the keys and their effective
+ strength: 56- and 40-bit keys are 8 octets in length, while 128-bit
+ keys are 16 octets long. Separate keys are derived for the send and
+ receive directions of the session.
+
+4.1. Generating 40-bit Session Keys
+
+ When MPPE is used in conjunction with EAP-TLS authentication, the TLS
+ master secret is used as the master session key.
+
+ The algorithm used to derive asymmetrical master session keys from
+ the TLS master secret is described in [12]. The master session keys
+ are never used to encrypt or decrypt data; they are only used in the
+ derivation of transient session keys.
+
+ Implementation Note
+
+ If the asymmetrical master keys are less than 8 octets in length,
+ they MUST be padded on the left with zeroes before being used to
+ derive the initial transient session keys. Conversely, if the
+ asymmetrical master keys are more than 8 octets in length, they
+ must be truncated to 8 octets before being used to derive the
+ initial transient session keys.
+
+ The initial transient session keys are obtained by calling the
+ function GetNewKeyFromSHA() (described in [3]):
+
+GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 8, SendSessionKey)
+GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 8,
+ReceiveSessionKey)
+
+ Next, the effective strength of both keys is reduced by setting the
+ first three octets to known constants:
+
+ SendSessionKey[0] = ReceiveSessionKey[0] = 0xD1
+ SendSessionKey[1] = ReceiveSessionKey[1] = 0x26
+ SendSessionKey[2] = ReceiveSessionKey[2] = 0x9E
+
+
+
+Zorn Informational [Page 16]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+ Finally, the RC4 tables are initialized using the new session keys:
+
+ rc4_key(SendRC4key, 8, SendSessionKey)
+ rc4_key(ReceiveRC4key, 8, ReceiveSessionKey)
+
+4.2. Generating 56-bit Session Keys
+
+ When MPPE is used in conjunction with EAP-TLS authentication, the TLS
+ master secret is used as the master session key.
+
+ The algorithm used to derive asymmetrical master session keys from
+ the TLS master secret is described in [12]. The master session keys
+ are never used to encrypt or decrypt data; they are only used in the
+ derivation of transient session keys.
+
+ Implementation Note
+
+ If the asymmetrical master keys are less than 8 octets in length,
+ they MUST be padded on the left with zeroes before being used to
+ derive the initial transient session keys. Conversely, if the
+ asymmetrical master keys are more than 8 octets in length, they
+ must be truncated to 8 octets before being used to derive the
+ initial transient session keys.
+
+ The initial transient session keys are obtained by calling the
+ function GetNewKeyFromSHA() (described in [3]):
+
+GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 8, SendSessionKey)
+GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 8,
+ReceiveSessionKey)
+
+ Next, the effective strength of both keys is reduced by setting the
+ initial octet to a known constant:
+
+ SendSessionKey[0] = ReceiveSessionKey[0] = 0xD1
+
+ Finally, the RC4 tables are initialized using the new session keys:
+
+ rc4_key(SendRC4key, 8, SendSessionKey)
+ rc4_key(ReceiveRC4key, 8, ReceiveSessionKey)
+
+4.3. Generating 128-bit Session Keys
+
+ When MPPE is used in conjunction with EAP-TLS authentication, the TLS
+ master secret is used as the master session key.
+
+
+
+
+
+
+Zorn Informational [Page 17]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+ The algorithm used to derive asymmetrical master session keys from
+ the TLS master secret is described in [12]. Note that the send key
+ on one side is the receive key on the other.
+
+ The master session keys are never used to encrypt or decrypt data;
+ they are only used in the derivation of transient session keys.
+
+ Implementation Note
+
+ If the asymmetrical master keys are less than 16 octets in length,
+ they MUST be padded on the left with zeroes before being used to
+ derive the initial transient session keys. Conversely, if the
+ asymmetrical master keys are more than 16 octets in length, they
+ must be truncated to 16 octets before being used to derive the
+ initial transient session keys.
+
+ The initial transient session keys are obtained by calling the
+ function GetNewKeyFromSHA() (described in [3]):
+
+GetNewKeyFromSHA(MasterSendKey, MasterSendKey, 16, SendSessionKey)
+GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, 16,
+ReceiveSessionKey)
+
+ Finally, the RC4 tables are initialized using the new session keys:
+
+ rc4_key(SendRC4key, 16, SendSessionKey)
+ rc4_key(ReceiveRC4key, 16, ReceiveSessionKey)
+
+5. Security Considerations
+
+5.1. MS-CHAP Credentials
+
+ Because of the way in which 40-bit keys are derived from MS-CHAP-1
+ credentials, the initial 40-bit session key will be identical in all
+ sessions established under the same peer credentials. For this
+ reason, and because RC4 with a 40-bit key length is believed to be a
+ relatively weak cipher, peers SHOULD NOT use 40-bit keys derived from
+ the LAN Manager password hash (as described above) if it can be
+ avoided.
+
+ Since the MPPE session keys are derived from user passwords (in the
+ MS- CHAP-1 and MS-CHAP-2 cases), care should be taken to ensure the
+ selection of strong passwords and passwords should be changed
+ frequently.
+
+
+
+
+
+
+
+Zorn Informational [Page 18]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+5.2. EAP-TLS Credentials
+
+ The strength of the session keys is dependent upon the security of
+ the TLS protocol.
+
+ The EAP server may be on a separate machine from the PPP
+ authenticator; if this is the case, adequate care must be taken in
+ the transmission of the EAP-TLS master keys to the authenticator.
+
+6. References
+
+ [1] Simpson, W., "The Point-to-Point Protocol (PPP)", STD 51, RFC
+ 1661, July 1994.
+
+ [2] Zorn, G. and S. Cobb, "Microsoft PPP CHAP Extensions", RFC 2433,
+ October 1998.
+
+ [3] Pall, G. and G. Zorn, "Microsoft Point-to-Point Encryption
+ (MPPE) RFC 3078, March 2001.
+
+ [4] RC4 is a proprietary encryption algorithm available under
+ license from RSA Data Security Inc. For licensing information,
+ contact:
+ RSA Data Security, Inc.
+ 100 Marine Parkway
+ Redwood City, CA 94065-1031
+
+ [5] Pall, G., "Microsoft Point-to-Point Compression (MPPC)
+ Protocol", RFC 2118, March 1997.
+
+ [6] Bradner, S., "Key words for use in RFCs to Indicate Requirement
+ Levels", BCP 14, RFC 2119, March 1997.
+
+ [7] "Secure Hash Standard", Federal Information Processing Standards
+ Publication 180-1, National Institute of Standards and
+ Technology, April 1995.
+
+ [8] Zorn, G., "Microsoft PPP CHAP Extensions, Version 2", RFC 2759,
+ January 2000.
+
+ [9] Simpson, W., "PPP Challenge Handshake Authentication Protocol
+ (CHAP)", RFC 1994, August 1996.
+
+ [10] Blunk, L. and J. Vollbrecht, "PPP Extensible Authentication
+ Protocol (EAP)", RFC 2284, March 1998.
+
+
+
+
+
+
+Zorn Informational [Page 19]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+ [11] Dierks, T. and C. Allen, "The TLS Protocol Version 1.0", RFC
+ 2246, January 1999.
+
+ [12] Aboba, B. and D. Simon, "PPP EAP TLS Authentication Protocol",
+ RFC 2716, October 1999.
+
+7. Acknowledgements
+
+ Anthony Bell, Richard B. Ward, Terence Spies and Thomas Dimitri, all
+ of Microsoft Corporation, significantly contributed to the design and
+ development of MPPE.
+
+ Additional thanks to Robert Friend, Joe Davies, Jody Terrill, Archie
+ Cobbs, Mark Deuser, Vijay Baliga, Brad Robel-Forrest and Jeff Haag
+ for useful feedback.
+
+ The technical portions of this memo were completed while the author
+ was employed by Microsoft Corporation.
+
+8. Author's Address
+
+ Questions about this memo can also be directed to:
+
+ Glen Zorn
+ cisco Systems
+ 500 108th Avenue N.E.
+ Suite 500
+ Bellevue, Washington 98004
+ USA
+
+ Phone: +1 425 438 8218
+ FAX: +1 425 438 1848
+ EMail: gwz@cisco.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zorn Informational [Page 20]
+
+RFC 3079 MPPE Key Derivation March 2001
+
+
+9. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2001). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zorn Informational [Page 21]
+