From 150c20025892ead49b29a00d8ac3849109b0a395 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 15 Jun 2008 07:26:19 +0000 Subject: [PATCH] Add openl2tp plugin. Patch from James Chapman. This patch adds a plugin that lets OpenL2TP receive events from pppd. It requires the pppol2tp plugin. Events are passed using a Unix socket. Signed-off-by: James Chapman --- pppd/plugins/pppol2tp/Makefile.linux | 4 +- pppd/plugins/pppol2tp/l2tp_event.h | 115 +++++++++++ pppd/plugins/pppol2tp/openl2tp.c | 294 +++++++++++++++++++++++++++ 3 files changed, 411 insertions(+), 2 deletions(-) create mode 100644 pppd/plugins/pppol2tp/l2tp_event.h create mode 100644 pppd/plugins/pppol2tp/openl2tp.c diff --git a/pppd/plugins/pppol2tp/Makefile.linux b/pppd/plugins/pppol2tp/Makefile.linux index 7a4b18f..85f7924 100644 --- a/pppd/plugins/pppol2tp/Makefile.linux +++ b/pppd/plugins/pppol2tp/Makefile.linux @@ -1,6 +1,6 @@ CC = gcc COPTS = -O2 -g -CFLAGS = $(COPTS) -I../.. -I../../../include -fPIC +CFLAGS = $(COPTS) -I. -I../.. -I../../../include -fPIC LDFLAGS = -shared INSTALL = install @@ -11,7 +11,7 @@ LIBDIR = $(DESTDIR)/lib/pppd/$(VERSION) VERSION = $(shell awk -F '"' '/VERSION/ { print $$2; }' ../../patchlevel.h) -PLUGINS := pppol2tp.so +PLUGINS := pppol2tp.so openl2tp.so all: $(PLUGINS) diff --git a/pppd/plugins/pppol2tp/l2tp_event.h b/pppd/plugins/pppol2tp/l2tp_event.h new file mode 100644 index 0000000..a066ef6 --- /dev/null +++ b/pppd/plugins/pppol2tp/l2tp_event.h @@ -0,0 +1,115 @@ +/***************************************************************************** + * Copyright (C) 2008 Katalix Systems Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *****************************************************************************/ + +/* + * OpenL2TP application event interface definition. + * + * This plugin is used by OpenL2TP to receive events from pppd. + * + * Events are used as follows:- + * PPP_UPDOWN_IND - tells OpenL2TP of PPP session state changes. + * PPP_ACCM_IND - tells OpenL2TP of PPP ACCM negotiated options + * + * Non-GPL applications are permitted to use this API, provided that + * any changes to this source file are made available under GPL terms. + */ + +#ifndef L2TP_EVENT_H +#define L2TP_EVENT_H + +#include + +/***************************************************************************** + * API definition + *****************************************************************************/ + +#define OPENL2TP_EVENT_SOCKET_NAME "/tmp/openl2tp-event.sock" + +#define OPENL2TP_MSG_TYPE_NULL 0 +#define OPENL2TP_MSG_TYPE_PPP_UPDOWN_IND 1 +#define OPENL2TP_MSG_TYPE_PPP_ACCM_IND 2 +#define OPENL2TP_MSG_TYPE_MAX 3 + +enum { + OPENL2TP_TLV_TYPE_TUNNEL_ID, + OPENL2TP_TLV_TYPE_SESSION_ID, + OPENL2TP_TLV_TYPE_PPP_ACCM, + OPENL2TP_TLV_TYPE_PPP_UNIT, + OPENL2TP_TLV_TYPE_PPP_IFNAME, + OPENL2TP_TLV_TYPE_PPP_USER_NAME, + OPENL2TP_TLV_TYPE_PPP_STATE +}; +#define OPENL2TP_TLV_TYPE_MAX (OPENL2TP_TLV_TYPE_PPP_STATE + 1) + +#define OPENL2TP_MSG_MAX_LEN 512 +#define OPENL2TP_MSG_SIGNATURE 0x6b6c7831 + +#define ALIGN32(n) (((n) + 3) & ~3) + +/* Each data field in a message is defined by a Type-Length-Value + * (TLV) tuplet. + */ +struct openl2tp_event_tlv { + uint16_t tlv_type; + uint16_t tlv_len; + uint8_t tlv_value[0]; +}; + +/* Messages contain a small header followed by a list of TLVs. Each + * TLV starts on a 4-byte boundary. + */ +struct openl2tp_event_msg { + uint32_t msg_signature; /* OPENL2TP_MSG_SIGNATURE */ + uint16_t msg_type; /* OPENL2TP_MSG_TYPE_* */ + uint16_t msg_len; /* length of data that follows */ + uint8_t msg_data[0]; /* list of TLVs, each always longword aligned */ +}; + +/* These structs define the data field layout of each TLV. + */ +struct openl2tp_tlv_tunnel_id { + uint16_t tunnel_id; +}; + +struct openl2tp_tlv_session_id { + uint16_t session_id; +}; + +struct openl2tp_tlv_ppp_accm { + uint32_t send_accm; + uint32_t recv_accm; +}; + +struct openl2tp_tlv_ppp_unit { + uint32_t unit; +}; + +struct openl2tp_tlv_ppp_state { + uint8_t up; /* 0=down, 1=up */ +}; + +struct openl2tp_tlv_ppp_ifname { + char ifname[0]; +}; + +struct openl2tp_tlv_ppp_user_name { + char user_name[0]; +}; + +#endif /* L2TP_EVENT_H */ diff --git a/pppd/plugins/pppol2tp/openl2tp.c b/pppd/plugins/pppol2tp/openl2tp.c new file mode 100644 index 0000000..9643b96 --- /dev/null +++ b/pppd/plugins/pppol2tp/openl2tp.c @@ -0,0 +1,294 @@ +/***************************************************************************** + * Copyright (C) 2006,2007,2008 Katalix Systems Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + *****************************************************************************/ + +/* pppd plugin for interfacing to openl2tpd */ + +#include +#include +#include +#include +#include "pppd.h" +#include "pathnames.h" +#include "fsm.h" +#include "lcp.h" +#include "ccp.h" +#include "ipcp.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef aligned_u64 +/* should be defined in sys/types.h */ +#define aligned_u64 unsigned long long __attribute__((aligned(8))) +#endif +#include +#include +#include +#include +#include +#include + +#include "l2tp_event.h" + +extern int pppol2tp_tunnel_id; +extern int pppol2tp_session_id; + +extern void (*pppol2tp_send_accm_hook)(int tunnel_id, int session_id, + uint32_t send_accm, uint32_t recv_accm); +extern void (*pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, int up); + +const char pppd_version[] = VERSION; + +static int openl2tp_fd = -1; + +static void (*old_pppol2tp_send_accm_hook)(int tunnel_id, int session_id, + uint32_t send_accm, + uint32_t recv_accm) = NULL; +static void (*old_pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, + int up) = NULL; +static void (*old_multilink_join_hook)(void) = NULL; + +/***************************************************************************** + * OpenL2TP interface. + * We send a PPP_ACCM_IND to openl2tpd to report ACCM values and + * SESSION_PPP_UPDOWN_IND to indicate when the PPP link comes up or + * goes down. + *****************************************************************************/ + +static int openl2tp_client_create(void) +{ + struct sockaddr_un addr; + int result; + + if (openl2tp_fd < 0) { + openl2tp_fd = socket(PF_UNIX, SOCK_DGRAM, 0); + if (openl2tp_fd < 0) { + error("openl2tp connection create: %m"); + return -ENOTCONN; + } + + addr.sun_family = AF_UNIX; + strcpy(&addr.sun_path[0], OPENL2TP_EVENT_SOCKET_NAME); + + result = connect(openl2tp_fd, (struct sockaddr *) &addr, + sizeof(addr)); + if (result < 0) { + error("openl2tp connection connect: %m"); + return -ENOTCONN; + } + } + + return 0; +} + +static void openl2tp_send_accm_ind(int tunnel_id, int session_id, + uint32_t send_accm, uint32_t recv_accm) +{ + int result; + uint8_t buf[OPENL2TP_MSG_MAX_LEN]; + struct openl2tp_event_msg *msg = (void *) &buf[0]; + struct openl2tp_event_tlv *tlv; + uint16_t tid = tunnel_id; + uint16_t sid = session_id; + struct openl2tp_tlv_ppp_accm accm; + + if (openl2tp_fd < 0) { + result = openl2tp_client_create(); + if (result < 0) { + goto out; + } + } + + accm.send_accm = send_accm; + accm.recv_accm = recv_accm; + + msg->msg_signature = OPENL2TP_MSG_SIGNATURE; + msg->msg_type = OPENL2TP_MSG_TYPE_PPP_ACCM_IND; + msg->msg_len = 0; + + tlv = (void *) &msg->msg_data[msg->msg_len]; + tlv->tlv_type = OPENL2TP_TLV_TYPE_TUNNEL_ID; + tlv->tlv_len = sizeof(tid); + memcpy(&tlv->tlv_value[0], &tid, tlv->tlv_len); + msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); + + tlv = (void *) &msg->msg_data[msg->msg_len]; + tlv->tlv_type = OPENL2TP_TLV_TYPE_SESSION_ID; + tlv->tlv_len = sizeof(sid); + memcpy(&tlv->tlv_value[0], &sid, tlv->tlv_len); + msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); + + tlv = (void *) &msg->msg_data[msg->msg_len]; + tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_ACCM; + tlv->tlv_len = sizeof(accm); + memcpy(&tlv->tlv_value[0], &accm, tlv->tlv_len); + msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); + + result = send(openl2tp_fd, msg, sizeof(*msg) + msg->msg_len, + MSG_NOSIGNAL); + if (result < 0) { + error("openl2tp send: %m"); + } + if (result != (sizeof(*msg) + msg->msg_len)) { + warn("openl2tp send: unexpected byte count %d, expected %d", + result, sizeof(msg) + msg->msg_len); + } + dbglog("openl2tp send: sent PPP_ACCM_IND, %d bytes", result); + +out: + if (old_pppol2tp_send_accm_hook != NULL) { + (*old_pppol2tp_send_accm_hook)(tunnel_id, session_id, + send_accm, recv_accm); + } + return; +} + +static void openl2tp_ppp_updown_ind(int tunnel_id, int session_id, int up) +{ + int result; + uint8_t buf[OPENL2TP_MSG_MAX_LEN]; + struct openl2tp_event_msg *msg = (void *) &buf[0]; + struct openl2tp_event_tlv *tlv; + uint16_t tid = tunnel_id; + uint16_t sid = session_id; + uint8_t state = up; + int unit = ifunit; + char *user_name = NULL; + + if (openl2tp_fd < 0) { + result = openl2tp_client_create(); + if (result < 0) { + goto out; + } + } + + if (peer_authname[0] != '\0') { + user_name = strdup(peer_authname); + } + + msg->msg_signature = OPENL2TP_MSG_SIGNATURE; + msg->msg_type = OPENL2TP_MSG_TYPE_PPP_UPDOWN_IND; + msg->msg_len = 0; + + tlv = (void *) &msg->msg_data[msg->msg_len]; + tlv->tlv_type = OPENL2TP_TLV_TYPE_TUNNEL_ID; + tlv->tlv_len = sizeof(tid); + memcpy(&tlv->tlv_value[0], &tid, tlv->tlv_len); + msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); + + tlv = (void *) &msg->msg_data[msg->msg_len]; + tlv->tlv_type = OPENL2TP_TLV_TYPE_SESSION_ID; + tlv->tlv_len = sizeof(sid); + memcpy(&tlv->tlv_value[0], &sid, tlv->tlv_len); + msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); + + tlv = (void *) &msg->msg_data[msg->msg_len]; + tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_STATE; + tlv->tlv_len = sizeof(state); + memcpy(&tlv->tlv_value[0], &state, tlv->tlv_len); + msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); + + tlv = (void *) &msg->msg_data[msg->msg_len]; + tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_UNIT; + tlv->tlv_len = sizeof(unit); + memcpy(&tlv->tlv_value[0], &unit, tlv->tlv_len); + msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); + + tlv = (void *) &msg->msg_data[msg->msg_len]; + tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_IFNAME; + tlv->tlv_len = strlen(ifname) + 1; + memcpy(&tlv->tlv_value[0], ifname, tlv->tlv_len); + msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); + + if (user_name != NULL) { + tlv = (void *) &msg->msg_data[msg->msg_len]; + tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_USER_NAME; + tlv->tlv_len = strlen(user_name) + 1; + memcpy(&tlv->tlv_value[0], user_name, tlv->tlv_len); + msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); + } + + result = send(openl2tp_fd, msg, sizeof(*msg) + msg->msg_len, + MSG_NOSIGNAL); + if (result < 0) { + error("openl2tp send: %m"); + } + if (result != (sizeof(*msg) + msg->msg_len)) { + warn("openl2tp send: unexpected byte count %d, expected %d", + result, sizeof(msg) + msg->msg_len); + } + dbglog("openl2tp send: sent PPP_UPDOWN_IND, %d bytes", result); + +out: + if (old_pppol2tp_ip_updown_hook != NULL) { + (*old_pppol2tp_ip_updown_hook)(tunnel_id, session_id, up); + } + + return; +} + +/***************************************************************************** + * When a multilink interface is created, there are 2 cases to consider. + * + * 1. The new interface is the first of a multilink bundle (master). + * 2. The new interface is being attached to an existing bundle. + * + * The first case is handled by existing code because the interface + * generates ip-up events just like standard interfaces. But in the + * second case, where the interface is added to an existing ppp + * bundle, pppd does not do IP negotiation and so as a result, no + * ip-up event is generated when the interface is created. Since + * openl2tpd needs the SESSION_PPP_UPDOWN_IND for all interfaces of a + * PPP bundle, we must fake the event. + * + * We use the ip_multilink_join_hook to hear when an interface joins a + * multilink bundle. + *****************************************************************************/ + +static void openl2tp_multilink_join_ind(void) +{ + if (doing_multilink && !multilink_master) { + /* send event only if not master */ + openl2tp_ppp_updown_ind(pppol2tp_tunnel_id, + pppol2tp_session_id, 1); + } +} + +/***************************************************************************** + * Application init + *****************************************************************************/ + +void plugin_init(void) +{ + old_pppol2tp_send_accm_hook = pppol2tp_send_accm_hook; + pppol2tp_send_accm_hook = openl2tp_send_accm_ind; + + old_pppol2tp_ip_updown_hook = pppol2tp_ip_updown_hook; + pppol2tp_ip_updown_hook = openl2tp_ppp_updown_ind; + + old_multilink_join_hook = multilink_join_hook; + multilink_join_hook = openl2tp_multilink_join_ind; +} + -- 2.39.2