1 /* pptp.c ... client shell to launch call managers, data handlers, and
2 * the pppd from the command line.
3 * C. Scott Ananian <cananian@alumni.princeton.edu>
5 * $Id: pptp.c,v 1.44 2006/02/13 03:06:25 quozl Exp $
9 #include <sys/socket.h>
10 #if defined(__FreeBSD__)
12 #elif defined(__NetBSD__) || defined(__OpenBSD__)
14 #elif defined(__APPLE__)
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
25 #include <net/route.h>
26 #include <sys/ioctl.h>
37 #include <sys/param.h>
38 #if defined(__APPLE__)
45 #include <linux/termios.h>
48 #include "pptp_callmgr.h"
51 #if defined(__linux__)
52 #include <linux/prctl.h>
57 #include "pptp_quirks.h"
59 #include "pptp_options.h"
62 #define PPPD_BINARY "pppd"
65 #define sin_addr(s) (((struct sockaddr_in *)(s))->sin_addr)
69 int disable_buffer
= 0;
71 struct in_addr
get_ip_address(char *name
);
72 int open_callmgr(struct in_addr inetaddr
, char *phonenr
, int argc
,char **argv
,char **envp
, int pty_fd
, int gre_fd
);
73 void launch_callmgr(struct in_addr inetaddr
, char *phonenr
, int argc
,char **argv
,char **envp
);
74 int get_call_id(int sock
, pid_t gre
, pid_t pppd
,
75 u_int16_t
*call_id
, u_int16_t
*peer_call_id
);
76 void launch_pppd(char *ttydev
, int argc
, char **argv
);
78 static int route_add(const struct in_addr inetaddr
, struct rtentry
*rt
);
79 static int route_del(struct rtentry
*rt
);
81 /*** print usage and exit *****************************************************/
82 void usage(char *progname
)
87 " %s <hostname> [<pptp options>] [[--] <pppd options>]\n"
89 "Or using pppd's pty option: \n"
90 " pppd pty \"%s <hostname> --nolaunchpppd <pptp options>\"\n"
92 "Available pptp options:\n"
93 " --version Display version number and exit\n"
94 " --phone <number> Pass <number> to remote host as phone number\n"
95 " --nolaunchpppd Do not launch pppd, for use as a pppd pty\n"
96 " --quirks <quirk> Work around a buggy PPTP implementation\n"
97 " Currently recognised values are BEZEQ_ISRAEL only\n"
98 " --debug Run in foreground (for debugging with gdb)\n"
99 " --sync Enable Synchronous HDLC (pppd must use it too)\n"
100 " --timeout <secs> Time to wait for reordered packets (0.01 to 10 secs)\n"
101 " --nobuffer Disable packet buffering and reordering completely\n"
102 " --idle-wait Time to wait before sending echo request\n"
103 " --max-echo-wait Time to wait before giving up on lack of reply\n"
104 " --logstring <name> Use <name> instead of 'anon' in syslog messages\n"
105 " --localbind <addr> Bind to specified IP address instead of wildcard\n"
106 " --loglevel <level> Sets the debugging level (0=low, 1=default, 2=high)\n"
107 " --no-host-route Disable adding host route to server",
109 version
, progname
, progname
);
110 log("%s called with wrong arguments, program not started.", progname
);
114 struct in_addr localbind
= { INADDR_NONE
};
115 static int signaled
= 0;
117 /*** do nothing signal handler ************************************************/
118 void do_nothing(int sig
)
120 /* do nothing signal handler. Better than SIG_IGN. */
126 /*** signal handler ***********************************************************/
127 void sighandler(int sig
)
132 /*** report statistics signal handler (SIGUSR1) *******************************/
133 void sigstats(int sig
)
135 syslog(LOG_NOTICE
, "GRE statistics:\n");
136 #define LOG(name,value) syslog(LOG_NOTICE, name "\n", stats .value)
137 LOG("rx accepted = %d", rx_accepted
);
138 LOG("rx lost = %d", rx_lost
);
139 LOG("rx under win = %d", rx_underwin
);
140 LOG("rx over win = %d", rx_overwin
);
141 LOG("rx buffered = %d", rx_buffered
);
142 LOG("rx OS errors = %d", rx_errors
);
143 LOG("rx truncated = %d", rx_truncated
);
144 LOG("rx invalid = %d", rx_invalid
);
145 LOG("rx acks = %d", rx_acks
);
146 LOG("tx sent = %d", tx_sent
);
147 LOG("tx failed = %d", tx_failed
);
148 LOG("tx short = %d", tx_short
);
149 LOG("tx acks = %d", tx_acks
);
150 LOG("tx oversize = %d", tx_oversize
);
151 LOG("round trip = %d usecs", rtt
);
155 /*** main *********************************************************************/
156 /* TODO: redesign to avoid longjmp/setjmp. Several variables here
157 have a volatile qualifier to silence warnings from gcc < 3.0.
158 Remove the volatile qualifiers if longjmp/setjmp are removed.
160 int main(int argc
, char **argv
, char **envp
)
162 struct in_addr inetaddr
;
164 volatile int callmgr_sock
= -1;
165 char ttydev
[PATH_MAX
];
166 int pty_fd
, tty_fd
, gre_fd
, rc
;
167 volatile pid_t parent_pid
, child_pid
;
168 u_int16_t call_id
, peer_call_id
;
172 char phonenrbuf
[65]; /* maximum length of field plus one for the trailing
174 char * volatile phonenr
= NULL
;
175 volatile int launchpppd
= 1, debug
= 0, add_host_route
= 1;
180 /* structure with all recognised options for pptp */
181 static struct option long_options
[] = {
183 {"nolaunchpppd", 0, 0, 0},
187 {"timeout", 1, 0, 0},
188 {"logstring", 1, 0, 0},
189 {"localbind", 1, 0, 0},
190 {"loglevel", 1, 0, 0},
191 {"nobuffer", 0, 0, 0},
192 {"idle-wait", 1, 0, 0},
193 {"max-echo-wait", 1, 0, 0},
194 {"version", 0, 0, 0},
195 {"no-host-route", 0, 0, 0},
198 int option_index
= 0;
200 c
= getopt_long (argc
, argv
, "", long_options
, &option_index
);
201 if (c
== -1) break; /* no more options */
204 if (option_index
== 0) { /* --phone specified */
205 /* copy it to a buffer, as the argv's will be overwritten
207 strncpy(phonenrbuf
,optarg
,sizeof(phonenrbuf
));
208 phonenrbuf
[sizeof(phonenrbuf
) - 1] = '\0';
209 phonenr
= phonenrbuf
;
210 } else if (option_index
== 1) {/* --nolaunchpppd specified */
212 } else if (option_index
== 2) {/* --quirks specified */
213 if (set_quirk_index(find_quirk(optarg
)))
215 } else if (option_index
== 3) {/* --debug */
217 } else if (option_index
== 4) {/* --sync specified */
219 } else if (option_index
== 5) {/* --timeout */
220 float new_packet_timeout
= atof(optarg
);
221 if (new_packet_timeout
< 0.0099 ||
222 new_packet_timeout
> 10) {
223 fprintf(stderr
, "Packet timeout %s (%f) out of range: "
224 "should be between 0.01 and 10 seconds\n",
225 optarg
, new_packet_timeout
);
226 log("Packet timeout %s (%f) out of range: should be"
227 "between 0.01 and 10 seconds", optarg
,
231 packet_timeout_usecs
= new_packet_timeout
* 1000000;
233 } else if (option_index
== 6) {/* --logstring */
234 log_string
= strdup(optarg
);
235 } else if (option_index
== 7) {/* --localbind */
236 if (inet_pton(AF_INET
, optarg
, (void *) &localbind
) < 1) {
237 fprintf(stderr
, "Local bind address %s invalid\n",
239 log("Local bind address %s invalid\n", optarg
);
242 } else if (option_index
== 8) { /* --loglevel */
243 log_level
= atoi(optarg
);
244 if (log_level
< 0 || log_level
> 2)
246 } else if (option_index
== 9) { /* --nobuffer */
248 } else if (option_index
== 10) { /* --idle-wait */
249 int x
= atoi(optarg
);
251 fprintf(stderr
, "--idle-wait must not be negative\n");
252 log("--idle-wait must not be negative\n");
257 } else if (option_index
== 11) { /* --max-echo-wait */
258 int x
= atoi(optarg
);
260 fprintf(stderr
, "--max-echo-wait must not be negative\n");
261 log("--max-echo-wait must not be negative\n");
266 fprintf(stderr
, "--max-echo-wait ignored, not yet implemented\n");
267 } else if (option_index
== 12) { /* --version */
268 fprintf(stdout
, "%s\n", version
);
270 } else if (option_index
== 13) { /* --no-host-route */
274 case '?': /* unrecognised option */
279 if (c
== -1) break; /* no more options for pptp */
282 /* at least one argument is required */
286 /* Get IP address for the hostname in argv[1] */
287 inetaddr
= get_ip_address(argv
[optind
]);
290 /* Find the ppp options, extract phone number */
291 pppdargc
= argc
- optind
;
292 pppdargv
= argv
+ optind
;
293 log("The synchronous pptp option is %sactivated\n", syncppp
? "" : "NOT ");
295 if (add_host_route
) {
296 /* Add a route to inetaddr */
297 memset(&rt
, 0, sizeof(rt
));
298 route_add(inetaddr
, &rt
);
301 /* Now we have the peer address, bind the GRE socket early,
302 before starting pppd. This prevents the ICMP Unreachable bug
303 documented in <1026868263.2855.67.camel@jander> */
304 gre_fd
= pptp_gre_bind(inetaddr
);
307 fatal("Cannot bind GRE socket, aborting.");
310 /* Find an open pty/tty pair. */
312 rc
= openpty (&pty_fd
, &tty_fd
, ttydev
, NULL
, NULL
);
315 fatal("Could not find free pty.");
319 signal(SIGUSR1
, do_nothing
); /* don't die */
320 signal(SIGCHLD
, do_nothing
); /* don't ignore SIGCHLD */
321 parent_pid
= getpid();
322 switch (child_pid
= fork()) {
324 fatal("Could not fork pppd process");
325 case 0: /* I'm the child! */
327 signal(SIGUSR1
, SIG_DFL
);
328 child_pid
= getpid();
330 default: /* parent */
333 * There is still a very small race condition here. If a signal
334 * occurs after signaled is checked but before pause is called,
338 pause(); /* wait for the signal */
341 if (signaled
== SIGCHLD
)
342 fatal("Child process died");
344 launch_pppd(ttydev
, pppdargc
, pppdargv
); /* launch pppd */
346 fatal("Could not launch pppd");
348 } else { /* ! launchpppd */
349 pty_fd
= tty_fd
= STDIN_FILENO
;
350 /* close unused file descriptor, that is redirected to the pty */
351 close(STDOUT_FILENO
);
352 child_pid
= getpid();
353 parent_pid
= 0; /* don't kill pppd */
358 * Open connection to call manager (Launch call manager if necessary.)
360 callmgr_sock
= open_callmgr(inetaddr
, phonenr
, argc
, argv
, envp
,
362 /* Exchange PIDs, get call ID */
363 } while (get_call_id(callmgr_sock
, parent_pid
, child_pid
,
364 &call_id
, &peer_call_id
) < 0);
366 /* Send signal to wake up pppd task */
368 kill(parent_pid
, SIGUSR1
);
370 /* become a daemon */
371 if (!debug
&& daemon(0, 0) != 0) {
375 /* re-open stderr as /dev/null to release it */
376 file2fd("/dev/null", "wb", STDERR_FILENO
);
379 snprintf(buf
, sizeof(buf
), "pptp: GRE-to-PPP gateway on %s",
382 rc
= prctl(PR_SET_NAME
, "pptpgw", 0, 0, 0);
383 if (rc
!= 0) perror("prctl");
385 inststr(argc
, argv
, envp
, buf
);
386 if (sigsetjmp(env
, 1)!= 0) goto shutdown
;
388 signal(SIGINT
, sighandler
);
389 signal(SIGTERM
, sighandler
);
390 signal(SIGKILL
, sighandler
);
391 signal(SIGCHLD
, sighandler
);
392 signal(SIGUSR1
, sigstats
);
395 if (ioctl(pty_fd
, TIOCSETD
, &disc
) < 0) {
396 fatal("Unable to set line discipline to N_HDLC");
398 log("Changed pty line discipline to N_HDLC for synchronous mode");
402 /* Do GRE copy until close. */
403 pptp_gre_copy(call_id
, peer_call_id
, pty_fd
, gre_fd
);
406 /* on close, kill all. */
408 kill(parent_pid
, SIGTERM
);
412 if (add_host_route) {
413 route_del(&rt); // don't delete, as otherwise it would try to use pppX in demand mode (since 1.9.2.7-9)
419 /*** get the ipaddress coming from the command line ***************************/
420 struct in_addr
get_ip_address(char *name
)
422 struct in_addr retval
;
423 struct hostent
*host
= gethostbyname(name
);
425 if (h_errno
== HOST_NOT_FOUND
)
426 fatal("gethostbyname '%s': HOST NOT FOUND", name
);
427 else if (h_errno
== NO_ADDRESS
)
428 fatal("gethostbyname '%s': NO IP ADDRESS", name
);
430 fatal("gethostbyname '%s': name server error", name
);
432 if (host
->h_addrtype
!= AF_INET
)
433 fatal("Host '%s' has non-internet address", name
);
434 memcpy(&retval
.s_addr
, host
->h_addr
, sizeof(retval
.s_addr
));
438 /*** start the call manager ***************************************************/
439 int open_callmgr(struct in_addr inetaddr
, char *phonenr
, int argc
, char **argv
,
440 char **envp
, int pty_fd
, int gre_fd
)
442 /* Try to open unix domain socket to call manager. */
443 struct sockaddr_un where
;
444 const int NUM_TRIES
= 3;
449 if ((fd
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0) {
450 fatal("Could not create unix domain socket: %s", strerror(errno
));
453 callmgr_name_unixsock(&where
, inetaddr
, localbind
);
454 for (i
= 0; i
< NUM_TRIES
; i
++) {
455 if (connect(fd
, (struct sockaddr
*) &where
, sizeof(where
)) < 0) {
456 /* couldn't connect. We'll have to launch this guy. */
458 unlink (where
.sun_path
);
460 /* fork and launch call manager process */
461 switch (pid
= fork()) {
462 case -1: /* failure */
463 fatal("fork() to launch call manager failed.");
467 /* close the pty and gre in the call manager */
470 launch_callmgr(inetaddr
, phonenr
, argc
, argv
, envp
);
472 default: /* parent */
473 waitpid(pid
, &status
, 0);
475 fatal("Call manager exited with error %d", status
);
483 fatal("Could not launch call manager after %d tries.", i
);
484 return -1; /* make gcc happy */
487 /*** call the call manager main ***********************************************/
488 void launch_callmgr(struct in_addr inetaddr
, char *phonenr
, int argc
,
489 char**argv
,char**envp
)
491 char *my_argv
[3] = { argv
[0], inet_ntoa(inetaddr
), phonenr
};
493 snprintf(buf
, sizeof(buf
), "pptp: call manager for %s", my_argv
[1]);
494 inststr(argc
, argv
, envp
, buf
);
495 exit(callmgr_main(3, my_argv
, envp
));
498 /*** exchange data with the call manager *************************************/
499 /* XXX need better error checking XXX */
500 int get_call_id(int sock
, pid_t gre
, pid_t pppd
,
501 u_int16_t
*call_id
, u_int16_t
*peer_call_id
)
503 u_int16_t m_call_id
, m_peer_call_id
;
504 /* write pid's to socket */
505 /* don't bother with network byte order, because pid's are meaningless
506 * outside the local host.
509 rc
= write(sock
, &gre
, sizeof(gre
));
510 if (rc
!= sizeof(gre
))
512 rc
= write(sock
, &pppd
, sizeof(pppd
));
513 if (rc
!= sizeof(pppd
))
515 rc
= read(sock
, &m_call_id
, sizeof(m_call_id
));
516 if (rc
!= sizeof(m_call_id
))
518 rc
= read(sock
, &m_peer_call_id
, sizeof(m_peer_call_id
));
519 if (rc
!= sizeof(m_peer_call_id
))
522 * XXX FIXME ... DO ERROR CHECKING & TIME-OUTS XXX
523 * (Rhialto: I am assuming for now that timeouts are not relevant
524 * here, because the read and write calls would return -1 (fail) when
525 * the peer goes away during the process. We know it is (or was)
526 * running because the connect() call succeeded.)
527 * (James: on the other hand, if the route to the peer goes away, we
528 * wouldn't get told by read() or write() for quite some time.)
530 *call_id
= m_call_id
;
531 *peer_call_id
= m_peer_call_id
;
535 /*** execvp pppd **************************************************************/
536 void launch_pppd(char *ttydev
, int argc
, char **argv
)
538 char *new_argv
[argc
+ 4];/* XXX if not using GCC, hard code a limit here. */
540 new_argv
[i
++] = PPPD_BINARY
;
542 new_argv
[i
++] = "-direct";
543 /* ppp expects to have stdin connected to ttydev */
544 if ((j
= open(ttydev
, O_RDWR
)) == -1)
545 fatal("Cannot open %s: %s", ttydev
, strerror(errno
));
546 if (dup2(j
, 0) == -1)
547 fatal("dup2 failed: %s", strerror(errno
));
550 new_argv
[i
++] = ttydev
;
551 new_argv
[i
++] = "38400";
553 for (j
= 0; j
< argc
; j
++)
554 new_argv
[i
++] = argv
[j
];
556 execvp(new_argv
[0], new_argv
);
559 /*** route manipulation *******************************************************/
562 route_ctrl(int ctrl
, struct rtentry
*rt
)
566 /* Open a raw socket to the kernel */
567 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0 || ioctl(s
, ctrl
, rt
) < 0)
568 warn("route_ctrl: %s", strerror(errno
));
576 route_del(struct rtentry
*rt
)
579 route_ctrl(SIOCDELRT
, rt
);
580 free(rt
->rt_dev
), rt
->rt_dev
= NULL
;
587 route_add(const struct in_addr inetaddr
, struct rtentry
*rt
)
589 char buf
[256], dev
[64];
591 u_int32_t dest
, mask
;
593 FILE *f
= fopen("/proc/net/route", "r");
595 warn("/proc/net/route: %s", strerror(errno
));
599 while (fgets(buf
, sizeof(buf
), f
))
601 if (sscanf(buf
, "%63s %x %x %X %*s %*s %d %x", dev
, &dest
,
602 &sin_addr(&rt
->rt_gateway
).s_addr
, &flags
, &metric
, &mask
) != 6)
604 if ((flags
& RTF_UP
) == RTF_UP
&& (inetaddr
.s_addr
& mask
) == dest
&&
605 (dest
|| strncmp(dev
, "ppp", 3)) /* avoid default via pppX to avoid on-demand loops*/)
607 rt
->rt_metric
= metric
;
608 rt
->rt_gateway
.sa_family
= AF_INET
;
615 /* check for no route */
616 if (rt
->rt_gateway
.sa_family
!= AF_INET
)
618 /* warn("route_add: no route to host"); */
622 /* check for existing route to this host,
623 add if missing based on the existing routes */
624 if (flags
& RTF_HOST
) {
625 /* warn("route_add: not adding existing route"); */
629 sin_addr(&rt
->rt_dst
) = inetaddr
;
630 rt
->rt_dst
.sa_family
= AF_INET
;
632 sin_addr(&rt
->rt_genmask
).s_addr
= INADDR_BROADCAST
;
633 rt
->rt_genmask
.sa_family
= AF_INET
;
635 rt
->rt_flags
= RTF_UP
| RTF_HOST
;
636 if (flags
& RTF_GATEWAY
)
637 rt
->rt_flags
|= RTF_GATEWAY
;
640 rt
->rt_dev
= strdup(dev
);
644 warn("route_add: no memory");
648 if (!route_ctrl(SIOCADDRT
, rt
))
651 free(rt
->rt_dev
), rt
->rt_dev
= NULL
;