1/***********************************************************************
2*
3* sync-pppd.c
4*
5* An LNS handler which starts pppd attached to a PTY in
6* synchronous mode.
7*
8* Copyright (C) 2002 by Roaring Penguin Software Inc.
9*
10* This software may be distributed under the terms of the GNU General
11* Public License, Version 2, or (at your option) any later version.
12*
13* LIC: GPL
14*
15***********************************************************************/
16
17static char const RCSID[] =
18"$Id: sync-pppd.c,v 1.1.1.1 2008/10/15 03:31:00 james26_jang Exp $";
19
20#include "l2tp.h"
21#include <signal.h>
22#include <stdio.h>
23#include <string.h>
24#include <fcntl.h>
25#include <linux/if.h>
26#include <linux/if_ether.h>
27#include <linux/if_pppol2tp.h>
28#include <linux/if_pppox.h>
29
30#define HANDLER_NAME "sync-pppd"
31
32#define DEFAULT_PPPD_PATH "/usr/sbin/pppd"
33
34#define MAX_FDS 256
35
36extern int pty_get(int *mfp, int *sfp);
37static int establish_session(l2tp_session *ses);
38static void close_session(l2tp_session *ses, char const *reason, int may_reestablish);
39static void handle_frame(l2tp_session *ses, unsigned char *buf, size_t len);
40
41/* Options for invoking pppd */
42#define MAX_OPTS 64
43static char *pppd_lns_options[MAX_OPTS+1];
44static char *pppd_lac_options[MAX_OPTS+1];
45static int num_pppd_lns_options = 0;
46static int num_pppd_lac_options = 0;
47static int use_unit_option = 0;
48static int kernel_mode = 1;
49static char *pppd_path = NULL;
50
51#define PUSH_LNS_OPT(x) pppd_lns_options[num_pppd_lns_options++] = (x)
52#define PUSH_LAC_OPT(x) pppd_lac_options[num_pppd_lac_options++] = (x)
53
54/* Our call ops */
55static l2tp_call_ops my_ops = {
56    establish_session,
57    close_session,
58    handle_frame
59};
60
61/* The slave process */
62struct slave {
63    EventSelector *es;		/* Event selector */
64    l2tp_session *ses;		/* L2TP session we're hooked to */
65    pid_t pid;			/* PID of child PPPD process */
66    int fd;			/* File descriptor for event-handler loop */
67    EventHandler *event;	/* Event handler */
68};
69
70static int handle_lac_opts(EventSelector *es, l2tp_opt_descriptor *desc, char const *value);
71static int handle_lns_opts(EventSelector *es, l2tp_opt_descriptor *desc, char const *value);
72
73/* Options */
74static l2tp_opt_descriptor my_opts[] = {
75    /*  name               type                 addr */
76    { "lac-pppd-opts",     OPT_TYPE_CALLFUNC,   (void *) handle_lac_opts},
77    { "lns-pppd-opts",     OPT_TYPE_CALLFUNC,   (void *) handle_lns_opts},
78    { "set-ppp-if-name",   OPT_TYPE_BOOL,       &use_unit_option},
79    { "kernel-mode",       OPT_TYPE_BOOL,       &kernel_mode},
80    { "pppd-path",         OPT_TYPE_STRING,     &pppd_path},
81    { NULL,                OPT_TYPE_BOOL,       NULL }
82};
83
84static int
85process_option(EventSelector *es, char const *name, char const *value)
86{
87    if (!strcmp(name, "*begin*")) return 0;
88    if (!strcmp(name, "*end*")) return 0;
89    return l2tp_option_set(es, name, value, my_opts);
90}
91
92static option_handler my_option_handler = {
93    NULL, HANDLER_NAME, process_option
94};
95
96static int
97handle_lac_opts(EventSelector *es,
98		l2tp_opt_descriptor *desc, char const *value)
99{
100    char word[512];
101    while (value && *value) {
102	value = l2tp_chomp_word(value, word);
103	if (!word[0]) break;
104	if (num_pppd_lac_options < MAX_OPTS) {
105	    char *x = strdup(word);
106	    if (x) PUSH_LAC_OPT(x);
107	    pppd_lac_options[num_pppd_lac_options] = NULL;
108	} else {
109	    break;
110	}
111    }
112    return 0;
113}
114
115static int
116handle_lns_opts(EventSelector *es,
117		l2tp_opt_descriptor *desc, char const *value)
118{
119    char word[512];
120    while (value && *value) {
121	value = l2tp_chomp_word(value, word);
122	if (!word[0]) break;
123	if (num_pppd_lns_options < MAX_OPTS) {
124	    char *x = strdup(word);
125	    if (x) PUSH_LNS_OPT(x);
126	    pppd_lns_options[num_pppd_lns_options] = NULL;
127	} else {
128	    break;
129	}
130    }
131    return 0;
132}
133
134/**********************************************************************
135* %FUNCTION: handle_frame
136* %ARGUMENTS:
137*  ses -- l2tp session
138*  buf -- received PPP frame
139*  len -- length of frame
140* %RETURNS:
141*  Nothing
142* %DESCRIPTION:
143*  Shoots the frame to PPP's pty
144***********************************************************************/
145static void
146handle_frame(l2tp_session *ses,
147	     unsigned char *buf,
148	     size_t len)
149{
150    struct slave *sl = ses->private;
151    int n;
152
153    if (!sl) return;
154
155    /* Add framing bytes */
156    *--buf = 0x03;
157    *--buf = 0xFF;
158    len += 2;
159
160    /* TODO: Add error checking */
161    if (sl->fd < 0) {
162        l2tp_set_errmsg("Attempt to write %d bytes to non existent fd.", len);
163    } else n = write(sl->fd, buf, len);
164}
165
166/**********************************************************************
167* %FUNCTION: close_session
168* %ARGUMENTS:
169*  ses -- L2TP session
170*  reason -- reason why session is closing
171* %RETURNS:
172*  Nothing
173* %DESCRIPTION:
174*  Kills pppd.
175***********************************************************************/
176static void
177close_session(l2tp_session *ses, char const *reason, int may_reestablish)
178{
179    l2tp_tunnel *tunnel = ses->tunnel;
180    struct slave *sl = ses->private;
181    if (!sl) return;
182
183    /* Detach slave */
184    ses->private = NULL;
185    sl->ses = NULL;
186
187    kill(sl->pid, SIGTERM);
188    if (sl->fd >= 0) close(sl->fd);
189    sl->fd = -1;
190    if (sl->event) Event_DelHandler(sl->es, sl->event);
191    sl->event = NULL;
192
193    /* Re-establish session if desired */
194    if (may_reestablish && tunnel->peer->persist &&
195        (tunnel->peer->maxfail == 0 || tunnel->peer->fail++ < tunnel->peer->maxfail)) {
196        struct timeval t;
197
198        t.tv_sec = tunnel->peer->holdoff;
199        t.tv_usec = 0;
200        Event_AddTimerHandler(tunnel->es, t, l2tp_tunnel_reestablish, tunnel->peer);
201    }
202}
203
204/**********************************************************************
205* %FUNCTION: slave_exited
206* %ARGUMENTS:
207*  pid -- PID of exiting slave
208*  status -- exit status of slave
209*  data -- the slave structure
210* %RETURNS:
211*  Nothing
212* %DESCRIPTION:
213*  Handles an exiting slave
214***********************************************************************/
215static void
216slave_exited(pid_t pid, int status, void *data)
217{
218    l2tp_session *ses;
219    struct slave *sl = (struct slave *) data;
220    if (!sl) return;
221
222    ses = sl->ses;
223
224    if (sl->fd >= 0) close(sl->fd);
225    if (sl->event) Event_DelHandler(sl->es, sl->event);
226    sl->fd = -1;
227    sl->event = NULL;
228
229    if (ses) {
230        l2tp_tunnel *tunnel = ses->tunnel;
231
232        /* Re-establish session if desired */
233        if (tunnel->peer->persist) {
234            struct timeval t;
235
236            t.tv_sec = tunnel->peer->holdoff;
237            t.tv_usec = 0;
238            Event_AddTimerHandler(tunnel->es, t, l2tp_tunnel_reestablish, tunnel->peer);
239        }
240
241	ses->private = NULL;
242	l2tp_session_send_CDN(ses, RESULT_GENERAL_REQUEST, 0,
243			      "pppd process exited");
244    }
245    free(sl);
246}
247
248/**********************************************************************
249* %FUNCTION: readable
250* %ARGUMENTS:
251*  es -- event selector
252*  fd -- file descriptor
253*  flags -- we ignore
254*  data -- the L2TP session
255* %RETURNS:
256*  Nothing
257* %DESCRIPTION:
258*  Handles readability on PTY; shoots PPP frame over tunnel
259***********************************************************************/
260static void
261readable(EventSelector *es, int fd, unsigned int flags, void *data)
262{
263    unsigned char buf[4096+EXTRA_HEADER_ROOM];
264    int n;
265    l2tp_session *ses = (l2tp_session *) data;
266    int iters = 5;
267
268    /* It seems to be better to read in a loop than to go
269       back to select loop.  However, don't loop forever, or
270       we could have a DoS potential */
271    while(iters--) {
272	/* EXTRA_HEADER_ROOM bytes extra space for l2tp header */
273	n = read(fd, buf+EXTRA_HEADER_ROOM, sizeof(buf)-EXTRA_HEADER_ROOM);
274
275	/* TODO: Check this.... */
276	if (n <= 2) return;
277
278	if (!ses) continue;
279
280	/* Chop off framing bytes */
281	l2tp_dgram_send_ppp_frame(ses, buf+EXTRA_HEADER_ROOM+2, n-2);
282    }
283}
284
285/**********************************************************************
286* %FUNCTION: establish_session
287* %ARGUMENTS:
288*  ses -- the L2TP session
289* %RETURNS:
290*  0 if session could be established, -1 otherwise.
291* %DESCRIPTION:
292*  Forks a pppd process and connects fd to pty
293***********************************************************************/
294static int
295establish_session(l2tp_session *ses)
296{
297    int m_pty = -1, s_pty;
298    struct sockaddr_pppol2tp sax;
299    pid_t pid;
300    EventSelector *es = ses->tunnel->es;
301    struct slave *sl = malloc(sizeof(struct slave));
302    int i, flags;
303    char unit[32], fdstr[10];
304
305    ses->private = NULL;
306    if (!sl) return -1;
307    sl->ses = ses;
308    sl->es = es;
309
310    /* Get pty */
311    if (kernel_mode) {
312        s_pty = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP);
313        if (s_pty < 0) {
314            l2tp_set_errmsg("Unable to allocate PPPoL2TP socket.");
315	    free(sl);
316            return -1;
317        }
318        flags = fcntl(s_pty, F_GETFL);
319        if (flags == -1 || fcntl(s_pty, F_SETFL, flags | O_NONBLOCK) == -1) {
320            l2tp_set_errmsg("Unable to set PPPoL2TP socket nonblock.");
321	    free(sl);
322            return -1;
323        }
324        sax.sa_family = AF_PPPOX;
325        sax.sa_protocol = PX_PROTO_OL2TP;
326        sax.pppol2tp.pid = 0;
327        sax.pppol2tp.fd = Sock;
328        sax.pppol2tp.addr.sin_addr.s_addr = ses->tunnel->peer_addr.sin_addr.s_addr;
329        sax.pppol2tp.addr.sin_port = ses->tunnel->peer_addr.sin_port;
330        sax.pppol2tp.addr.sin_family = AF_INET;
331        sax.pppol2tp.s_tunnel  = ses->tunnel->my_id;
332        sax.pppol2tp.s_session = ses->my_id;
333        sax.pppol2tp.d_tunnel  = ses->tunnel->assigned_id;
334        sax.pppol2tp.d_session = ses->assigned_id;
335        if (connect(s_pty, (struct sockaddr *)&sax, sizeof(sax)) < 0) {
336            l2tp_set_errmsg("Unable to connect PPPoL2TP socket.");
337	    free(sl);
338            return -1;
339        }
340	snprintf (fdstr, sizeof(fdstr), "%d", s_pty);
341    } else {
342    if (pty_get(&m_pty, &s_pty) < 0) {
343	free(sl);
344	return -1;
345    }
346	if (fcntl(m_pty, F_SETFD, FD_CLOEXEC) == -1) {
347	    l2tp_set_errmsg("Unable to set FD_CLOEXEC");
348	    close(m_pty);
349	    close(s_pty);
350	    free(sl);
351	    return -1;
352	}
353    }
354
355    /* Fork */
356    pid = fork();
357    if (pid == (pid_t) -1) {
358	free(sl);
359	return -1;
360    }
361
362    if (pid) {
363	/* In the parent */
364	sl->pid = pid;
365
366	/* Set up handler for when pppd exits */
367	Event_HandleChildExit(es, pid, slave_exited, sl);
368
369	/* Close the slave tty */
370	close(s_pty);
371
372	sl->fd = m_pty;
373
374	if (!kernel_mode) {
375            /* Set slave FD non-blocking */
376	    flags = fcntl(sl->fd, F_GETFL);
377	    if (flags >= 0) fcntl(sl->fd, F_SETFL, (long) flags | O_NONBLOCK);
378
379	    /* Handle readability on slave end */
380	    sl->event = Event_AddHandler(es, m_pty, EVENT_FLAG_READABLE,
381			 readable, ses);
382	} else
383	    sl->event = NULL;
384
385	ses->private = sl;
386	return 0;
387    }
388
389    /* In the child.  Exec pppd */
390    /* Close all file descriptors except s_pty */
391    for (i=0; i<MAX_FDS; i++) {
392	if (i != s_pty) close(i);
393    }
394
395    /* Dup s_pty onto stdin and stdout */
396    if (!kernel_mode) {
397    	dup2(s_pty, 0);
398    	dup2(s_pty, 1);
399        if (s_pty > 1) close(s_pty);
400    }
401
402    /* Create unit */
403    sprintf(unit, "%d", (int) getpid());
404
405    if (ses->we_are_lac) {
406        char **lac_opt;
407
408	/* Push a unit option */
409	if (use_unit_option && num_pppd_lac_options <= MAX_OPTS-2) {
410	    PUSH_LAC_OPT("unit");
411	    PUSH_LAC_OPT(unit);
412	}
413	/* Push plugin options */
414	if (kernel_mode && num_pppd_lac_options <= MAX_OPTS-4) {
415	    PUSH_LAC_OPT("plugin");
416	    PUSH_LAC_OPT("pppol2tp.so");
417	    PUSH_LAC_OPT("pppol2tp");
418	    PUSH_LAC_OPT(fdstr);
419	}
420        /* push peer specific options */
421        lac_opt = ses->tunnel->peer->lac_options;
422        while (*lac_opt) {
423            if (num_pppd_lac_options <= MAX_OPTS-1) {
424		PUSH_LAC_OPT(*lac_opt);
425		++lac_opt;
426	    } else {
427		break;
428	    }
429        }
430	if (pppd_path) {
431	    execv(pppd_path, pppd_lac_options);
432	} else {
433	    execv(DEFAULT_PPPD_PATH, pppd_lac_options);
434	}
435    } else {
436        char **lns_opt;
437
438	/* Push a unit option */
439	if (use_unit_option && num_pppd_lns_options <= MAX_OPTS-2) {
440	    PUSH_LNS_OPT("unit");
441	    PUSH_LNS_OPT(unit);
442	}
443	/* Push plugin options */
444	if (kernel_mode && num_pppd_lac_options <= MAX_OPTS-5) {
445	    PUSH_LNS_OPT("plugin");
446	    PUSH_LNS_OPT("pppol2tp.so");
447	    PUSH_LNS_OPT("pppol2tp");
448	    PUSH_LNS_OPT(fdstr);
449	    PUSH_LNS_OPT("pppol2tp_lns_mode");
450	}
451        /* push peer specific options */
452        lns_opt = ses->tunnel->peer->lns_options;
453        while (*lns_opt) {
454            if (num_pppd_lns_options <= MAX_OPTS-1) {
455		PUSH_LNS_OPT(*lns_opt);
456		++lns_opt;
457	    } else {
458		break;
459	    }
460        }
461	if (pppd_path) {
462	    execv(pppd_path, pppd_lns_options);
463	} else {
464	    execv(DEFAULT_PPPD_PATH, pppd_lns_options);
465	}
466    }
467
468    /* Doh.. execl failed */
469    _exit(1);
470}
471
472static l2tp_lns_handler my_lns_handler = {
473    NULL,
474    HANDLER_NAME,
475    &my_ops
476};
477
478static l2tp_lac_handler my_lac_handler = {
479    NULL,
480    HANDLER_NAME,
481    &my_ops
482};
483
484void
485handler_init(EventSelector *es)
486{
487    l2tp_session_register_lns_handler(&my_lns_handler);
488    l2tp_session_register_lac_handler(&my_lac_handler);
489    l2tp_option_register_section(&my_option_handler);
490
491    PUSH_LNS_OPT("pppd");
492    PUSH_LNS_OPT("sync");
493    PUSH_LNS_OPT("nodetach");
494    PUSH_LNS_OPT("noaccomp");
495    PUSH_LNS_OPT("nobsdcomp");
496    PUSH_LNS_OPT("nodeflate");
497    PUSH_LNS_OPT("nopcomp");
498    PUSH_LNS_OPT("novj");
499    PUSH_LNS_OPT("novjccomp");
500#if 0
501    PUSH_LNS_OPT("logfile");
502    PUSH_LNS_OPT("/dev/null");
503    PUSH_LNS_OPT("nolog");
504#endif
505    pppd_lns_options[num_pppd_lns_options] = NULL;
506
507    PUSH_LAC_OPT("pppd");
508    PUSH_LAC_OPT("sync");
509    PUSH_LAC_OPT("nodetach");
510    PUSH_LAC_OPT("noaccomp");
511    PUSH_LAC_OPT("nobsdcomp");
512    PUSH_LAC_OPT("nodeflate");
513    PUSH_LAC_OPT("nopcomp");
514    PUSH_LAC_OPT("novj");
515    PUSH_LAC_OPT("novjccomp");
516#if 0
517    PUSH_LAC_OPT("logfile");
518    PUSH_LAC_OPT("/dev/null");
519    PUSH_LAC_OPT("nolog");
520#endif
521    pppd_lac_options[num_pppd_lac_options] = NULL;
522}
523
524