1 /***********************************************************************
5 * An LNS handler which starts pppd attached to a PTY in
8 * Copyright (C) 2002 by Roaring Penguin Software Inc.
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.
15 ***********************************************************************/
17 static char const RCSID
[] =
18 "$Id: sync-pppd.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $";
26 #include <linux/if_ether.h>
27 #include <linux/if_pppol2tp.h>
28 #include <linux/if_pppox.h>
30 #define HANDLER_NAME "sync-pppd"
32 #define DEFAULT_PPPD_PATH "/usr/sbin/pppd"
36 extern int pty_get(int *mfp
, int *sfp
);
37 static int establish_session(l2tp_session
*ses
);
38 static void close_session(l2tp_session
*ses
, char const *reason
, int may_reestablish
);
39 static void handle_frame(l2tp_session
*ses
, unsigned char *buf
, size_t len
);
41 /* Options for invoking pppd */
43 static char *pppd_lns_options
[MAX_OPTS
+1];
44 static char *pppd_lac_options
[MAX_OPTS
+1];
45 static int num_pppd_lns_options
= 0;
46 static int num_pppd_lac_options
= 0;
47 static int use_unit_option
= 0;
48 static int kernel_mode
= 1;
49 static char *pppd_path
= NULL
;
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)
55 static l2tp_call_ops my_ops
= {
61 /* The slave process */
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 */
70 static int handle_lac_opts(EventSelector
*es
, l2tp_opt_descriptor
*desc
, char const *value
);
71 static int handle_lns_opts(EventSelector
*es
, l2tp_opt_descriptor
*desc
, char const *value
);
74 static l2tp_opt_descriptor my_opts
[] = {
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
}
85 process_option(EventSelector
*es
, char const *name
, char const *value
)
87 if (!strcmp(name
, "*begin*")) return 0;
88 if (!strcmp(name
, "*end*")) return 0;
89 return l2tp_option_set(es
, name
, value
, my_opts
);
92 static option_handler my_option_handler
= {
93 NULL
, HANDLER_NAME
, process_option
97 handle_lac_opts(EventSelector
*es
,
98 l2tp_opt_descriptor
*desc
, char const *value
)
101 while (value
&& *value
) {
102 value
= l2tp_chomp_word(value
, word
);
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
;
116 handle_lns_opts(EventSelector
*es
,
117 l2tp_opt_descriptor
*desc
, char const *value
)
120 while (value
&& *value
) {
121 value
= l2tp_chomp_word(value
, word
);
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
;
134 /**********************************************************************
135 * %FUNCTION: handle_frame
137 * ses -- l2tp session
138 * buf -- received PPP frame
139 * len -- length of frame
143 * Shoots the frame to PPP's pty
144 ***********************************************************************/
146 handle_frame(l2tp_session
*ses
,
150 struct slave
*sl
= ses
->private;
155 /* Add framing bytes */
160 /* TODO: Add error checking */
162 l2tp_set_errmsg("Attempt to write %d bytes to non existent fd.", len
);
163 } else n
= write(sl
->fd
, buf
, len
);
166 /**********************************************************************
167 * %FUNCTION: close_session
169 * ses -- L2TP session
170 * reason -- reason why session is closing
175 ***********************************************************************/
177 close_session(l2tp_session
*ses
, char const *reason
, int may_reestablish
)
179 l2tp_tunnel
*tunnel
= ses
->tunnel
;
180 struct slave
*sl
= ses
->private;
187 kill(sl
->pid
, SIGTERM
);
188 if (sl
->fd
>= 0) close(sl
->fd
);
190 if (sl
->event
) Event_DelHandler(sl
->es
, sl
->event
);
193 /* Re-establish session if desired */
194 if (may_reestablish
&& tunnel
->peer
->persist
&&
195 (tunnel
->peer
->maxfail
== 0 || tunnel
->peer
->fail
++ < tunnel
->peer
->maxfail
)) {
198 t
.tv_sec
= tunnel
->peer
->holdoff
;
200 Event_AddTimerHandler(tunnel
->es
, t
, l2tp_tunnel_reestablish
, tunnel
->peer
);
204 /**********************************************************************
205 * %FUNCTION: slave_exited
207 * pid -- PID of exiting slave
208 * status -- exit status of slave
209 * data -- the slave structure
213 * Handles an exiting slave
214 ***********************************************************************/
216 slave_exited(pid_t pid
, int status
, void *data
)
219 struct slave
*sl
= (struct slave
*) data
;
224 if (sl
->fd
>= 0) close(sl
->fd
);
225 if (sl
->event
) Event_DelHandler(sl
->es
, sl
->event
);
230 l2tp_tunnel
*tunnel
= ses
->tunnel
;
232 /* Re-establish session if desired */
233 if (tunnel
->peer
->persist
) {
236 t
.tv_sec
= tunnel
->peer
->holdoff
;
238 Event_AddTimerHandler(tunnel
->es
, t
, l2tp_tunnel_reestablish
, tunnel
->peer
);
242 l2tp_session_send_CDN(ses
, RESULT_GENERAL_REQUEST
, 0,
243 "pppd process exited");
248 /**********************************************************************
249 * %FUNCTION: readable
251 * es -- event selector
252 * fd -- file descriptor
254 * data -- the L2TP session
258 * Handles readability on PTY; shoots PPP frame over tunnel
259 ***********************************************************************/
261 readable(EventSelector
*es
, int fd
, unsigned int flags
, void *data
)
263 unsigned char buf
[4096+EXTRA_HEADER_ROOM
];
265 l2tp_session
*ses
= (l2tp_session
*) data
;
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 */
272 /* EXTRA_HEADER_ROOM bytes extra space for l2tp header */
273 n
= read(fd
, buf
+EXTRA_HEADER_ROOM
, sizeof(buf
)-EXTRA_HEADER_ROOM
);
275 /* TODO: Check this.... */
280 /* Chop off framing bytes */
281 l2tp_dgram_send_ppp_frame(ses
, buf
+EXTRA_HEADER_ROOM
+2, n
-2);
285 /**********************************************************************
286 * %FUNCTION: establish_session
288 * ses -- the L2TP session
290 * 0 if session could be established, -1 otherwise.
292 * Forks a pppd process and connects fd to pty
293 ***********************************************************************/
295 establish_session(l2tp_session
*ses
)
297 int m_pty
= -1, s_pty
;
298 struct sockaddr_pppol2tp sax
;
300 EventSelector
*es
= ses
->tunnel
->es
;
301 struct slave
*sl
= malloc(sizeof(struct slave
));
303 char unit
[32], fdstr
[10];
312 s_pty
= socket(AF_PPPOX
, SOCK_DGRAM
, PX_PROTO_OL2TP
);
314 l2tp_set_errmsg("Unable to allocate PPPoL2TP socket.");
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.");
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.");
340 snprintf (fdstr
, sizeof(fdstr
), "%d", s_pty
);
342 if (pty_get(&m_pty
, &s_pty
) < 0) {
346 if (fcntl(m_pty
, F_SETFD
, FD_CLOEXEC
) == -1) {
347 l2tp_set_errmsg("Unable to set FD_CLOEXEC");
357 if (pid
== (pid_t
) -1) {
366 /* Set up handler for when pppd exits */
367 Event_HandleChildExit(es
, pid
, slave_exited
, sl
);
369 /* Close the slave tty */
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
);
379 /* Handle readability on slave end */
380 sl
->event
= Event_AddHandler(es
, m_pty
, EVENT_FLAG_READABLE
,
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
);
395 /* Dup s_pty onto stdin and stdout */
399 if (s_pty
> 1) close(s_pty
);
403 sprintf(unit
, "%d", (int) getpid());
405 if (ses
->we_are_lac
) {
408 /* Push a unit option */
409 if (use_unit_option
&& num_pppd_lac_options
<= MAX_OPTS
-2) {
410 PUSH_LAC_OPT("unit");
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");
420 /* push peer specific options */
421 lac_opt
= ses
->tunnel
->peer
->lac_options
;
423 if (num_pppd_lac_options
<= MAX_OPTS
-1) {
424 PUSH_LAC_OPT(*lac_opt
);
431 execv(pppd_path
, pppd_lac_options
);
433 execv(DEFAULT_PPPD_PATH
, pppd_lac_options
);
438 /* Push a unit option */
439 if (use_unit_option
&& num_pppd_lns_options
<= MAX_OPTS
-2) {
440 PUSH_LNS_OPT("unit");
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");
449 PUSH_LNS_OPT("pppol2tp_lns_mode");
451 /* push peer specific options */
452 lns_opt
= ses
->tunnel
->peer
->lns_options
;
454 if (num_pppd_lns_options
<= MAX_OPTS
-1) {
455 PUSH_LNS_OPT(*lns_opt
);
462 execv(pppd_path
, pppd_lns_options
);
464 execv(DEFAULT_PPPD_PATH
, pppd_lns_options
);
468 /* Doh.. execl failed */
472 static l2tp_lns_handler my_lns_handler
= {
478 static l2tp_lac_handler my_lac_handler
= {
485 handler_init(EventSelector
*es
)
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
);
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");
501 PUSH_LNS_OPT("logfile");
502 PUSH_LNS_OPT("/dev/null");
503 PUSH_LNS_OPT("nolog");
505 pppd_lns_options
[num_pppd_lns_options
] = NULL
;
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");
517 PUSH_LAC_OPT("logfile");
518 PUSH_LAC_OPT("/dev/null");
519 PUSH_LAC_OPT("nolog");
521 pppd_lac_options
[num_pppd_lac_options
] = NULL
;