Home | History | Annotate | Line # | Download | only in pppol2tp
pppol2tp.c revision 1.1
      1 /* pppol2tp.c - pppd plugin to implement PPPoL2TP protocol
      2  *   for Linux using kernel pppol2tp support.
      3  *
      4  * Requires kernel pppol2tp driver which is integrated into the kernel
      5  * from 2.6.23 onwards. For earlier kernels, a version can be obtained
      6  * from the OpenL2TP project at
      7  * http://www.sourceforge.net/projects/openl2tp/
      8  *
      9  * Original by Martijn van Oosterhout <kleptog (at) svana.org>
     10  * Modified by jchapman (at) katalix.com
     11  *
     12  * Heavily based upon pppoatm.c: original notice follows
     13  *
     14  * Copyright 2000 Mitchell Blank Jr.
     15  * Based in part on work from Jens Axboe and Paul Mackerras.
     16  * Updated to ppp-2.4.1 by Bernhard Kaindl
     17  *
     18  *  This program is free software; you can redistribute it and/or
     19  *  modify it under the terms of the GNU General Public License
     20  *  as published by the Free Software Foundation; either version
     21  *  2 of the License, or (at your option) any later version.
     22  */
     23 #include <unistd.h>
     24 #include <string.h>
     25 #include <stdlib.h>
     26 #include <errno.h>
     27 #include "pppd.h"
     28 #include "pathnames.h"
     29 #include "fsm.h"
     30 #include "lcp.h"
     31 #include "ccp.h"
     32 #include "ipcp.h"
     33 #include <sys/stat.h>
     34 #include <net/if.h>
     35 #include <sys/ioctl.h>
     36 #include <sys/socket.h>
     37 #include <netinet/in.h>
     38 #include <signal.h>
     39 #include <linux/version.h>
     40 #include <linux/sockios.h>
     41 #ifndef aligned_u64
     42 /* should be defined in sys/types.h */
     43 #define aligned_u64 unsigned long long __attribute__((aligned(8)))
     44 #endif
     45 #include <linux/types.h>
     46 #include <linux/if_ether.h>
     47 #include <linux/ppp_defs.h>
     48 #include <linux/if_ppp.h>
     49 #include <linux/if_pppox.h>
     50 #include <linux/if_pppol2tp.h>
     51 
     52 /* should be added to system's socket.h... */
     53 #ifndef SOL_PPPOL2TP
     54 #define SOL_PPPOL2TP	273
     55 #endif
     56 
     57 const char pppd_version[] = VERSION;
     58 
     59 static int setdevname_pppol2tp(char **argv);
     60 
     61 static int pppol2tp_fd = -1;
     62 static char *pppol2tp_fd_str;
     63 static bool pppol2tp_lns_mode = 0;
     64 static bool pppol2tp_recv_seq = 0;
     65 static bool pppol2tp_send_seq = 0;
     66 static int pppol2tp_debug_mask = 0;
     67 static int pppol2tp_reorder_timeout = 0;
     68 static char pppol2tp_ifname[32] = { 0, };
     69 int pppol2tp_tunnel_id = 0;
     70 int pppol2tp_session_id = 0;
     71 
     72 static int device_got_set = 0;
     73 struct channel pppol2tp_channel;
     74 
     75 static void (*old_snoop_recv_hook)(unsigned char *p, int len) = NULL;
     76 static void (*old_snoop_send_hook)(unsigned char *p, int len) = NULL;
     77 static void (*old_ip_up_hook)(void) = NULL;
     78 static void (*old_ip_down_hook)(void) = NULL;
     79 
     80 /* Hook provided to allow other plugins to handle ACCM changes */
     81 void (*pppol2tp_send_accm_hook)(int tunnel_id, int session_id,
     82 				uint32_t send_accm, uint32_t recv_accm) = NULL;
     83 
     84 /* Hook provided to allow other plugins to handle IP up/down */
     85 void (*pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, int up) = NULL;
     86 
     87 static option_t pppol2tp_options[] = {
     88 	{ "pppol2tp", o_special, &setdevname_pppol2tp,
     89 	  "FD for PPPoL2TP socket", OPT_DEVNAM | OPT_A2STRVAL,
     90           &pppol2tp_fd_str },
     91 	{ "pppol2tp_lns_mode", o_bool, &pppol2tp_lns_mode,
     92 	  "PPPoL2TP LNS behavior. Default off.",
     93 	  OPT_PRIO | OPRIO_CFGFILE },
     94 	{ "pppol2tp_send_seq", o_bool, &pppol2tp_send_seq,
     95 	  "PPPoL2TP enable sequence numbers in transmitted data packets. "
     96 	  "Default off.",
     97 	  OPT_PRIO | OPRIO_CFGFILE },
     98 	{ "pppol2tp_recv_seq", o_bool, &pppol2tp_recv_seq,
     99 	  "PPPoL2TP enforce sequence numbers in received data packets. "
    100 	  "Default off.",
    101 	  OPT_PRIO | OPRIO_CFGFILE },
    102 	{ "pppol2tp_reorderto", o_int, &pppol2tp_reorder_timeout,
    103 	  "PPPoL2TP data packet reorder timeout. Default 0 (no reordering).",
    104 	  OPT_PRIO },
    105 	{ "pppol2tp_debug_mask", o_int, &pppol2tp_debug_mask,
    106 	  "PPPoL2TP debug mask. Default: no debug.",
    107 	  OPT_PRIO },
    108 	{ "pppol2tp_ifname", o_string, &pppol2tp_ifname,
    109 	  "Set interface name of PPP interface",
    110 	  OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, 16 },
    111 	{ "pppol2tp_tunnel_id", o_int, &pppol2tp_tunnel_id,
    112 	  "PPPoL2TP tunnel_id.",
    113 	  OPT_PRIO },
    114 	{ "pppol2tp_session_id", o_int, &pppol2tp_session_id,
    115 	  "PPPoL2TP session_id.",
    116 	  OPT_PRIO },
    117 	{ NULL }
    118 };
    119 
    120 static int setdevname_pppol2tp(char **argv)
    121 {
    122 	union {
    123 		char buffer[128];
    124 		struct sockaddr pppol2tp;
    125 	} s;
    126 	int len = sizeof(s);
    127 	char **a;
    128 	int tmp;
    129 	int tmp_len = sizeof(tmp);
    130 
    131 	if (device_got_set)
    132 		return 0;
    133 
    134 	if (!int_option(*argv, &pppol2tp_fd))
    135 		return 0;
    136 
    137 	if(getsockname(pppol2tp_fd, (struct sockaddr *)&s, &len) < 0) {
    138 		fatal("Given FD for PPPoL2TP socket invalid (%s)",
    139 		      strerror(errno));
    140 	}
    141 	if(s.pppol2tp.sa_family != AF_PPPOX) {
    142 		fatal("Socket of not a PPPoX socket");
    143 	}
    144 
    145 	/* Do a test getsockopt() to ensure that the kernel has the necessary
    146 	 * feature available.
    147 	 */
    148 	if (getsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_DEBUG,
    149 		       &tmp, &tmp_len) < 0) {
    150 		fatal("PPPoL2TP kernel driver not installed");
    151 	}
    152 
    153 	/* Setup option defaults. Compression options are disabled! */
    154 
    155 	modem = 0;
    156 
    157 	lcp_allowoptions[0].neg_accompression = 1;
    158 	lcp_wantoptions[0].neg_accompression = 0;
    159 
    160 	lcp_allowoptions[0].neg_pcompression = 1;
    161 	lcp_wantoptions[0].neg_pcompression = 0;
    162 
    163 	ccp_allowoptions[0].deflate = 0;
    164 	ccp_wantoptions[0].deflate = 0;
    165 
    166 	ipcp_allowoptions[0].neg_vj = 0;
    167 	ipcp_wantoptions[0].neg_vj = 0;
    168 
    169 	ccp_allowoptions[0].bsd_compress = 0;
    170 	ccp_wantoptions[0].bsd_compress = 0;
    171 
    172 	the_channel = &pppol2tp_channel;
    173 	device_got_set = 1;
    174 
    175 	return 1;
    176 }
    177 
    178 static int connect_pppol2tp(void)
    179 {
    180 	if(pppol2tp_fd == -1) {
    181 		fatal("No PPPoL2TP FD specified");
    182 	}
    183 
    184 	return pppol2tp_fd;
    185 }
    186 
    187 static void disconnect_pppol2tp(void)
    188 {
    189 	if (pppol2tp_fd >= 0) {
    190 		close(pppol2tp_fd);
    191 		pppol2tp_fd = -1;
    192 	}
    193 }
    194 
    195 static void send_config_pppol2tp(int mtu,
    196 			      u_int32_t asyncmap,
    197 			      int pcomp,
    198 			      int accomp)
    199 {
    200 	struct ifreq ifr;
    201 	int on = 1;
    202 	int fd;
    203 	char reorderto[16];
    204 	char tid[8];
    205 	char sid[8];
    206 
    207 	if (pppol2tp_ifname[0]) {
    208 		struct ifreq ifr;
    209 		int fd;
    210 
    211 		fd = socket(AF_INET, SOCK_DGRAM, 0);
    212 		if (fd >= 0) {
    213 			memset (&ifr, '\0', sizeof (ifr));
    214 			strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
    215 			strlcpy(ifr.ifr_newname, pppol2tp_ifname,
    216 				sizeof(ifr.ifr_name));
    217 			ioctl(fd, SIOCSIFNAME, (caddr_t) &ifr);
    218 			strlcpy(ifname, pppol2tp_ifname, 32);
    219 			if (pppol2tp_debug_mask & PPPOL2TP_MSG_CONTROL) {
    220 				dbglog("ppp%d: interface name %s",
    221 				       ifunit, ifname);
    222 			}
    223 		}
    224 		close(fd);
    225 	}
    226 
    227 	if ((lcp_allowoptions[0].mru > 0) && (mtu > lcp_allowoptions[0].mru)) {
    228 		warn("Overriding mtu %d to %d", mtu, lcp_allowoptions[0].mru);
    229 		mtu = lcp_allowoptions[0].mru;
    230 	}
    231 	netif_set_mtu(ifunit, mtu);
    232 
    233 	reorderto[0] = '\0';
    234 	if (pppol2tp_reorder_timeout > 0)
    235 		sprintf(&reorderto[0], "%d ", pppol2tp_reorder_timeout);
    236 	tid[0] = '\0';
    237 	if (pppol2tp_tunnel_id > 0)
    238 		sprintf(&tid[0], "%hu ", pppol2tp_tunnel_id);
    239 	sid[0] = '\0';
    240 	if (pppol2tp_session_id > 0)
    241 		sprintf(&sid[0], "%hu ", pppol2tp_session_id);
    242 
    243 	dbglog("PPPoL2TP options: %s%s%s%s%s%s%s%s%sdebugmask %d",
    244 	       pppol2tp_recv_seq ? "recvseq " : "",
    245 	       pppol2tp_send_seq ? "sendseq " : "",
    246 	       pppol2tp_lns_mode ? "lnsmode " : "",
    247 	       pppol2tp_reorder_timeout ? "reorderto " : "", reorderto,
    248 	       pppol2tp_tunnel_id ? "tid " : "", tid,
    249 	       pppol2tp_session_id ? "sid " : "", sid,
    250 	       pppol2tp_debug_mask);
    251 
    252 	if (pppol2tp_recv_seq)
    253 		if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_RECVSEQ,
    254 			       &on, sizeof(on)) < 0)
    255 			fatal("setsockopt(PPPOL2TP_RECVSEQ): %m");
    256 	if (pppol2tp_send_seq)
    257 		if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_SENDSEQ,
    258 			       &on, sizeof(on)) < 0)
    259 			fatal("setsockopt(PPPOL2TP_SENDSEQ): %m");
    260 	if (pppol2tp_lns_mode)
    261 		if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_LNSMODE,
    262 			       &on, sizeof(on)) < 0)
    263 			fatal("setsockopt(PPPOL2TP_LNSMODE): %m");
    264 	if (pppol2tp_reorder_timeout)
    265 		if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_REORDERTO,
    266 			       &pppol2tp_reorder_timeout,
    267 			       sizeof(pppol2tp_reorder_timeout)) < 0)
    268 			fatal("setsockopt(PPPOL2TP_REORDERTO): %m");
    269 	if (pppol2tp_debug_mask)
    270 		if (setsockopt(pppol2tp_fd, SOL_PPPOL2TP, PPPOL2TP_SO_DEBUG,
    271 			       &pppol2tp_debug_mask, sizeof(pppol2tp_debug_mask)) < 0)
    272 			fatal("setsockopt(PPPOL2TP_DEBUG): %m");
    273 }
    274 
    275 static void recv_config_pppol2tp(int mru,
    276 			      u_int32_t asyncmap,
    277 			      int pcomp,
    278 			      int accomp)
    279 {
    280 	if ((lcp_allowoptions[0].mru > 0) && (mru > lcp_allowoptions[0].mru)) {
    281 		warn("Overriding mru %d to mtu value %d", mru,
    282 		     lcp_allowoptions[0].mru);
    283 		mru = lcp_allowoptions[0].mru;
    284 	}
    285 	if ((ifunit >= 0) && ioctl(pppol2tp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0)
    286 		error("Couldn't set PPP MRU: %m");
    287 }
    288 
    289 /*****************************************************************************
    290  * Snoop LCP message exchanges to capture negotiated ACCM values.
    291  * When asyncmap values have been seen from both sides, give the values to
    292  * L2TP.
    293  * This code is derived from Roaring Penguin L2TP.
    294  *****************************************************************************/
    295 
    296 static void pppol2tp_lcp_snoop(unsigned char *buf, int len, int incoming)
    297 {
    298 	static bool got_send_accm = 0;
    299 	static bool got_recv_accm = 0;
    300 	static uint32_t recv_accm = 0xffffffff;
    301 	static uint32_t send_accm = 0xffffffff;
    302 	static bool snooping = 1;
    303 
    304 	uint16_t protocol;
    305 	uint16_t lcp_pkt_len;
    306 	int opt, opt_len;
    307 	int reject;
    308 	unsigned char const *opt_data;
    309 	uint32_t accm;
    310 
    311 	/* Skip HDLC header */
    312 	buf += 2;
    313 	len -= 2;
    314 
    315 	/* Unreasonably short frame?? */
    316 	if (len <= 0) return;
    317 
    318 	/* Get protocol */
    319 	if (buf[0] & 0x01) {
    320 		/* Compressed protcol field */
    321 		protocol = buf[0];
    322 	} else {
    323 		protocol = ((unsigned int) buf[0]) * 256 + buf[1];
    324 	}
    325 
    326 	/* If it's a network protocol, stop snooping */
    327 	if (protocol <= 0x3fff) {
    328 		if (pppol2tp_debug_mask & PPPOL2TP_MSG_DEBUG) {
    329 			dbglog("Turning off snooping: "
    330 			       "Network protocol %04x found.",
    331 			       protocol);
    332 		}
    333 		snooping = 0;
    334 		return;
    335 	}
    336 
    337 	/* If it's not LCP, do not snoop */
    338 	if (protocol != 0xc021) {
    339 		return;
    340 	}
    341 
    342 	/* Skip protocol; go to packet data */
    343 	buf += 2;
    344 	len -= 2;
    345 
    346 	/* Unreasonably short frame?? */
    347 	if (len <= 0) return;
    348 
    349 	/* Look for Configure-Ack or Configure-Reject code */
    350 	if (buf[0] != CONFACK && buf[0] != CONFREJ) return;
    351 
    352 	reject = (buf[0] == CONFREJ);
    353 
    354 	lcp_pkt_len = ((unsigned int) buf[2]) * 256 + buf[3];
    355 
    356 	/* Something fishy with length field? */
    357 	if (lcp_pkt_len > len) return;
    358 
    359 	/* Skip to options */
    360 	len = lcp_pkt_len - 4;
    361 	buf += 4;
    362 
    363 	while (len > 0) {
    364 		/* Pull off an option */
    365 		opt = buf[0];
    366 		opt_len = buf[1];
    367 		opt_data = &buf[2];
    368 		if (opt_len > len || opt_len < 2) break;
    369 		len -= opt_len;
    370 		buf += opt_len;
    371 		if (pppol2tp_debug_mask & PPPOL2TP_MSG_DEBUG) {
    372 			dbglog("Found option type %02x; len %d", opt, opt_len);
    373 		}
    374 
    375 		/* We are specifically interested in ACCM */
    376 		if (opt == CI_ASYNCMAP && opt_len == 0x06) {
    377 			if (reject) {
    378 				/* ACCM negotiation REJECTED; use default */
    379 				accm = 0xffffffff;
    380 				if (pppol2tp_debug_mask & PPPOL2TP_MSG_DATA) {
    381 					dbglog("Rejected ACCM negotiation; "
    382 					       "defaulting (%s)",
    383 					       incoming ? "incoming" : "outgoing");
    384 				}
    385 				recv_accm = accm;
    386 				send_accm = accm;
    387 				got_recv_accm = 1;
    388 				got_send_accm = 1;
    389 			} else {
    390 				memcpy(&accm, opt_data, sizeof(accm));
    391 				if (pppol2tp_debug_mask & PPPOL2TP_MSG_DATA) {
    392 					dbglog("Found ACCM of %08x (%s)", accm,
    393 					       incoming ? "incoming" : "outgoing");
    394 				}
    395 				if (incoming) {
    396 					recv_accm = accm;
    397 					got_recv_accm = 1;
    398 				} else {
    399 					send_accm = accm;
    400 					got_send_accm = 1;
    401 				}
    402 			}
    403 
    404 			if (got_recv_accm && got_send_accm) {
    405 				if (pppol2tp_debug_mask & PPPOL2TP_MSG_CONTROL) {
    406 					dbglog("Telling L2TP: Send ACCM = %08x; "
    407 					       "Receive ACCM = %08x", send_accm, recv_accm);
    408 				}
    409 				if (pppol2tp_send_accm_hook != NULL) {
    410 					(*pppol2tp_send_accm_hook)(pppol2tp_tunnel_id,
    411 								   pppol2tp_session_id,
    412 								   send_accm, recv_accm);
    413 				}
    414 				got_recv_accm = 0;
    415 				got_send_accm = 0;
    416 			}
    417 		}
    418 	}
    419 }
    420 
    421 static void pppol2tp_lcp_snoop_recv(unsigned char *p, int len)
    422 {
    423 	if (old_snoop_recv_hook != NULL)
    424 		(*old_snoop_recv_hook)(p, len);
    425 	pppol2tp_lcp_snoop(p, len, 1);
    426 }
    427 
    428 static void pppol2tp_lcp_snoop_send(unsigned char *p, int len)
    429 {
    430 	if (old_snoop_send_hook != NULL)
    431 		(*old_snoop_send_hook)(p, len);
    432 	pppol2tp_lcp_snoop(p, len, 0);
    433 }
    434 
    435 /*****************************************************************************
    436  * Interface up/down events
    437  *****************************************************************************/
    438 
    439 static void pppol2tp_ip_up_hook(void)
    440 {
    441 	if (old_ip_up_hook != NULL)
    442 		(*old_ip_up_hook)();
    443 
    444 	if (pppol2tp_ip_updown_hook != NULL) {
    445 		(*pppol2tp_ip_updown_hook)(pppol2tp_tunnel_id,
    446 					   pppol2tp_session_id, 1);
    447 	}
    448 }
    449 
    450 static void pppol2tp_ip_down_hook(void)
    451 {
    452 	if (old_ip_down_hook != NULL)
    453 		(*old_ip_down_hook)();
    454 
    455 	if (pppol2tp_ip_updown_hook != NULL) {
    456 		(*pppol2tp_ip_updown_hook)(pppol2tp_tunnel_id,
    457 					   pppol2tp_session_id, 0);
    458 	}
    459 }
    460 
    461 /*****************************************************************************
    462  * Application init
    463  *****************************************************************************/
    464 
    465 static void pppol2tp_check_options(void)
    466 {
    467 	/* Enable LCP snooping for ACCM options only for LNS */
    468 	if (pppol2tp_lns_mode) {
    469 		if ((pppol2tp_tunnel_id == 0) || (pppol2tp_session_id == 0)) {
    470 			fatal("tunnel_id/session_id values not specified");
    471 		}
    472 		if (pppol2tp_debug_mask & PPPOL2TP_MSG_CONTROL) {
    473 			dbglog("Enabling LCP snooping");
    474 		}
    475 		old_snoop_recv_hook = snoop_recv_hook;
    476 		old_snoop_send_hook = snoop_send_hook;
    477 
    478 		snoop_recv_hook = pppol2tp_lcp_snoop_recv;
    479 		snoop_send_hook = pppol2tp_lcp_snoop_send;
    480 	}
    481 
    482 	/* Hook up ip up/down hooks to send indicator to openl2tpd
    483 	 * that the link is up
    484 	 */
    485 	old_ip_up_hook = ip_up_hook;
    486 	ip_up_hook = pppol2tp_ip_up_hook;
    487 	old_ip_down_hook = ip_down_hook;
    488 	ip_down_hook = pppol2tp_ip_down_hook;
    489 }
    490 
    491 /* Called just before pppd exits.
    492  */
    493 static void pppol2tp_cleanup(void)
    494 {
    495 	if (pppol2tp_debug_mask & PPPOL2TP_MSG_DEBUG) {
    496 		dbglog("pppol2tp: exiting.");
    497 	}
    498 	disconnect_pppol2tp();
    499 }
    500 
    501 void plugin_init(void)
    502 {
    503 #if defined(__linux__)
    504 	extern int new_style_driver;	/* From sys-linux.c */
    505 	if (!ppp_available() && !new_style_driver)
    506 		fatal("Kernel doesn't support ppp_generic - "
    507 		    "needed for PPPoL2TP");
    508 #else
    509 	fatal("No PPPoL2TP support on this OS");
    510 #endif
    511 	add_options(pppol2tp_options);
    512 }
    513 
    514 struct channel pppol2tp_channel = {
    515     options: pppol2tp_options,
    516     process_extra_options: NULL,
    517     check_options: &pppol2tp_check_options,
    518     connect: &connect_pppol2tp,
    519     disconnect: &disconnect_pppol2tp,
    520     establish_ppp: &generic_establish_ppp,
    521     disestablish_ppp: &generic_disestablish_ppp,
    522     send_config: &send_config_pppol2tp,
    523     recv_config: &recv_config_pppol2tp,
    524     close: NULL,
    525     cleanup: NULL
    526 };
    527