/***************************************************************************** * 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); } if (user_name != NULL) free(user_name); 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; }