Correct PPTP server firewall rules chain.
[tomato/davidwu.git] / release / src / router / rp-l2tp / handlers / sync-pppd.c
blob3229fbf6d766aecf1253862b527e5be2cfc56653
1 /***********************************************************************
3 * sync-pppd.c
5 * An LNS handler which starts pppd attached to a PTY in
6 * synchronous mode.
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.
13 * LIC: GPL
15 ***********************************************************************/
17 static char const RCSID[] =
18 "$Id: sync-pppd.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $";
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>
30 #define HANDLER_NAME "sync-pppd"
32 #define DEFAULT_PPPD_PATH "/usr/sbin/pppd"
34 #define MAX_FDS 256
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 */
42 #define MAX_OPTS 64
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)
54 /* Our call ops */
55 static l2tp_call_ops my_ops = {
56 establish_session,
57 close_session,
58 handle_frame
61 /* The slave process */
62 struct 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 */
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);
73 /* Options */
74 static 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 }
84 static int
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
96 static int
97 handle_lac_opts(EventSelector *es,
98 l2tp_opt_descriptor *desc, char const *value)
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;
112 return 0;
115 static int
116 handle_lns_opts(EventSelector *es,
117 l2tp_opt_descriptor *desc, char const *value)
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;
131 return 0;
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 ***********************************************************************/
145 static void
146 handle_frame(l2tp_session *ses,
147 unsigned char *buf,
148 size_t len)
150 struct slave *sl = ses->private;
151 int n;
153 if (!sl) return;
155 /* Add framing bytes */
156 *--buf = 0x03;
157 *--buf = 0xFF;
158 len += 2;
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);
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 ***********************************************************************/
176 static void
177 close_session(l2tp_session *ses, char const *reason, int may_reestablish)
179 l2tp_tunnel *tunnel = ses->tunnel;
180 struct slave *sl = ses->private;
181 if (!sl) return;
183 /* Detach slave */
184 ses->private = NULL;
185 sl->ses = NULL;
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;
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;
198 t.tv_sec = tunnel->peer->holdoff;
199 t.tv_usec = 0;
200 Event_AddTimerHandler(tunnel->es, t, l2tp_tunnel_reestablish, tunnel->peer);
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 ***********************************************************************/
215 static void
216 slave_exited(pid_t pid, int status, void *data)
218 l2tp_session *ses;
219 struct slave *sl = (struct slave *) data;
220 if (!sl) return;
222 ses = sl->ses;
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;
229 if (ses) {
230 l2tp_tunnel *tunnel = ses->tunnel;
232 /* Re-establish session if desired */
233 if (tunnel->peer->persist) {
234 struct timeval t;
236 t.tv_sec = tunnel->peer->holdoff;
237 t.tv_usec = 0;
238 Event_AddTimerHandler(tunnel->es, t, l2tp_tunnel_reestablish, tunnel->peer);
241 ses->private = NULL;
242 l2tp_session_send_CDN(ses, RESULT_GENERAL_REQUEST, 0,
243 "pppd process exited");
245 free(sl);
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 ***********************************************************************/
260 static void
261 readable(EventSelector *es, int fd, unsigned int flags, void *data)
263 unsigned char buf[4096+EXTRA_HEADER_ROOM];
264 int n;
265 l2tp_session *ses = (l2tp_session *) data;
266 int iters = 5;
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);
275 /* TODO: Check this.... */
276 if (n <= 2) return;
278 if (!ses) continue;
280 /* Chop off framing bytes */
281 l2tp_dgram_send_ppp_frame(ses, buf+EXTRA_HEADER_ROOM+2, n-2);
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 ***********************************************************************/
294 static int
295 establish_session(l2tp_session *ses)
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];
305 ses->private = NULL;
306 if (!sl) return -1;
307 sl->ses = ses;
308 sl->es = es;
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;
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;
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;
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;
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;
355 /* Fork */
356 pid = fork();
357 if (pid == (pid_t) -1) {
358 free(sl);
359 return -1;
362 if (pid) {
363 /* In the parent */
364 sl->pid = pid;
366 /* Set up handler for when pppd exits */
367 Event_HandleChildExit(es, pid, slave_exited, sl);
369 /* Close the slave tty */
370 close(s_pty);
372 sl->fd = m_pty;
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);
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;
385 ses->private = sl;
386 return 0;
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 */
396 if (!kernel_mode) {
397 dup2(s_pty, 0);
398 dup2(s_pty, 1);
399 if (s_pty > 1) close(s_pty);
402 /* Create unit */
403 sprintf(unit, "%d", (int) getpid());
405 if (ses->we_are_lac) {
406 char **lac_opt;
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);
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);
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;
430 if (pppd_path) {
431 execv(pppd_path, pppd_lac_options);
432 } else {
433 execv(DEFAULT_PPPD_PATH, pppd_lac_options);
435 } else {
436 char **lns_opt;
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);
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");
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;
461 if (pppd_path) {
462 execv(pppd_path, pppd_lns_options);
463 } else {
464 execv(DEFAULT_PPPD_PATH, pppd_lns_options);
468 /* Doh.. execl failed */
469 _exit(1);
472 static l2tp_lns_handler my_lns_handler = {
473 NULL,
474 HANDLER_NAME,
475 &my_ops
478 static l2tp_lac_handler my_lac_handler = {
479 NULL,
480 HANDLER_NAME,
481 &my_ops
484 void
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");
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;
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;