1/***************************************************************************** 2 * Copyright (C) 2006,2007,2008 Katalix Systems Ltd 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 * 18 *****************************************************************************/ 19 20/* pppd plugin for interfacing to openl2tpd */ 21 22#include <unistd.h> 23#include <string.h> 24#include <stdlib.h> 25#include <errno.h> 26#include "pppd.h" 27#include "pathnames.h" 28#include "fsm.h" 29#include "lcp.h" 30#include "ccp.h" 31#include "ipcp.h" 32#include <sys/stat.h> 33#include <net/if.h> 34#include <sys/ioctl.h> 35#include <sys/socket.h> 36#include <sys/un.h> 37#include <netinet/in.h> 38#include <signal.h> 39#include <linux/version.h> 40#include <linux/sockios.h> 41 42#ifndef aligned_u64 43/* should be defined in sys/types.h */ 44#define aligned_u64 unsigned long long __attribute__((aligned(8))) 45#endif 46#include <linux/types.h> 47#include <linux/if_ether.h> 48#include <linux/ppp_defs.h> 49#include <linux/if_ppp.h> 50#include <linux/if_pppox.h> 51#include <linux/if_pppol2tp.h> 52 53#include "l2tp_event.h" 54 55extern int pppol2tp_tunnel_id; 56extern int pppol2tp_session_id; 57 58extern void (*pppol2tp_send_accm_hook)(int tunnel_id, int session_id, 59 uint32_t send_accm, uint32_t recv_accm); 60extern void (*pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, int up); 61 62const char pppd_version[] = VERSION; 63 64static int openl2tp_fd = -1; 65 66static void (*old_pppol2tp_send_accm_hook)(int tunnel_id, int session_id, 67 uint32_t send_accm, 68 uint32_t recv_accm) = NULL; 69static void (*old_pppol2tp_ip_updown_hook)(int tunnel_id, int session_id, 70 int up) = NULL; 71static void (*old_multilink_join_hook)(void) = NULL; 72 73/***************************************************************************** 74 * OpenL2TP interface. 75 * We send a PPP_ACCM_IND to openl2tpd to report ACCM values and 76 * SESSION_PPP_UPDOWN_IND to indicate when the PPP link comes up or 77 * goes down. 78 *****************************************************************************/ 79 80static int openl2tp_client_create(void) 81{ 82 struct sockaddr_un addr; 83 int result; 84 85 if (openl2tp_fd < 0) { 86 openl2tp_fd = socket(PF_UNIX, SOCK_DGRAM, 0); 87 if (openl2tp_fd < 0) { 88 error("openl2tp connection create: %m"); 89 return -ENOTCONN; 90 } 91 92 addr.sun_family = AF_UNIX; 93 strcpy(&addr.sun_path[0], OPENL2TP_EVENT_SOCKET_NAME); 94 95 result = connect(openl2tp_fd, (struct sockaddr *) &addr, 96 sizeof(addr)); 97 if (result < 0) { 98 error("openl2tp connection connect: %m"); 99 return -ENOTCONN; 100 } 101 } 102 103 return 0; 104} 105 106static void openl2tp_send_accm_ind(int tunnel_id, int session_id, 107 uint32_t send_accm, uint32_t recv_accm) 108{ 109 int result; 110 uint8_t buf[OPENL2TP_MSG_MAX_LEN]; 111 struct openl2tp_event_msg *msg = (void *) &buf[0]; 112 struct openl2tp_event_tlv *tlv; 113 uint16_t tid = tunnel_id; 114 uint16_t sid = session_id; 115 struct openl2tp_tlv_ppp_accm accm; 116 117 if (openl2tp_fd < 0) { 118 result = openl2tp_client_create(); 119 if (result < 0) { 120 goto out; 121 } 122 } 123 124 accm.send_accm = send_accm; 125 accm.recv_accm = recv_accm; 126 127 msg->msg_signature = OPENL2TP_MSG_SIGNATURE; 128 msg->msg_type = OPENL2TP_MSG_TYPE_PPP_ACCM_IND; 129 msg->msg_len = 0; 130 131 tlv = (void *) &msg->msg_data[msg->msg_len]; 132 tlv->tlv_type = OPENL2TP_TLV_TYPE_TUNNEL_ID; 133 tlv->tlv_len = sizeof(tid); 134 memcpy(&tlv->tlv_value[0], &tid, tlv->tlv_len); 135 msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 136 137 tlv = (void *) &msg->msg_data[msg->msg_len]; 138 tlv->tlv_type = OPENL2TP_TLV_TYPE_SESSION_ID; 139 tlv->tlv_len = sizeof(sid); 140 memcpy(&tlv->tlv_value[0], &sid, tlv->tlv_len); 141 msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 142 143 tlv = (void *) &msg->msg_data[msg->msg_len]; 144 tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_ACCM; 145 tlv->tlv_len = sizeof(accm); 146 memcpy(&tlv->tlv_value[0], &accm, tlv->tlv_len); 147 msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 148 149 result = send(openl2tp_fd, msg, sizeof(*msg) + msg->msg_len, 150 MSG_NOSIGNAL); 151 if (result < 0) { 152 error("openl2tp send: %m"); 153 } 154 if (result != (sizeof(*msg) + msg->msg_len)) { 155 warn("openl2tp send: unexpected byte count %d, expected %d", 156 result, sizeof(msg) + msg->msg_len); 157 } 158 dbglog("openl2tp send: sent PPP_ACCM_IND, %d bytes", result); 159 160out: 161 if (old_pppol2tp_send_accm_hook != NULL) { 162 (*old_pppol2tp_send_accm_hook)(tunnel_id, session_id, 163 send_accm, recv_accm); 164 } 165 return; 166} 167 168static void openl2tp_ppp_updown_ind(int tunnel_id, int session_id, int up) 169{ 170 int result; 171 uint8_t buf[OPENL2TP_MSG_MAX_LEN]; 172 struct openl2tp_event_msg *msg = (void *) &buf[0]; 173 struct openl2tp_event_tlv *tlv; 174 uint16_t tid = tunnel_id; 175 uint16_t sid = session_id; 176 uint8_t state = up; 177 int unit = ifunit; 178 char *user_name = NULL; 179 180 if (openl2tp_fd < 0) { 181 result = openl2tp_client_create(); 182 if (result < 0) { 183 goto out; 184 } 185 } 186 187 if (peer_authname[0] != '\0') { 188 user_name = strdup(peer_authname); 189 } 190 191 msg->msg_signature = OPENL2TP_MSG_SIGNATURE; 192 msg->msg_type = OPENL2TP_MSG_TYPE_PPP_UPDOWN_IND; 193 msg->msg_len = 0; 194 195 tlv = (void *) &msg->msg_data[msg->msg_len]; 196 tlv->tlv_type = OPENL2TP_TLV_TYPE_TUNNEL_ID; 197 tlv->tlv_len = sizeof(tid); 198 memcpy(&tlv->tlv_value[0], &tid, tlv->tlv_len); 199 msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 200 201 tlv = (void *) &msg->msg_data[msg->msg_len]; 202 tlv->tlv_type = OPENL2TP_TLV_TYPE_SESSION_ID; 203 tlv->tlv_len = sizeof(sid); 204 memcpy(&tlv->tlv_value[0], &sid, tlv->tlv_len); 205 msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 206 207 tlv = (void *) &msg->msg_data[msg->msg_len]; 208 tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_STATE; 209 tlv->tlv_len = sizeof(state); 210 memcpy(&tlv->tlv_value[0], &state, tlv->tlv_len); 211 msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 212 213 tlv = (void *) &msg->msg_data[msg->msg_len]; 214 tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_UNIT; 215 tlv->tlv_len = sizeof(unit); 216 memcpy(&tlv->tlv_value[0], &unit, tlv->tlv_len); 217 msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 218 219 tlv = (void *) &msg->msg_data[msg->msg_len]; 220 tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_IFNAME; 221 tlv->tlv_len = strlen(ifname) + 1; 222 memcpy(&tlv->tlv_value[0], ifname, tlv->tlv_len); 223 msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 224 225 if (user_name != NULL) { 226 tlv = (void *) &msg->msg_data[msg->msg_len]; 227 tlv->tlv_type = OPENL2TP_TLV_TYPE_PPP_USER_NAME; 228 tlv->tlv_len = strlen(user_name) + 1; 229 memcpy(&tlv->tlv_value[0], user_name, tlv->tlv_len); 230 msg->msg_len += sizeof(*tlv) + ALIGN32(tlv->tlv_len); 231 } 232 233 result = send(openl2tp_fd, msg, sizeof(*msg) + msg->msg_len, 234 MSG_NOSIGNAL); 235 if (result < 0) { 236 error("openl2tp send: %m"); 237 } 238 if (result != (sizeof(*msg) + msg->msg_len)) { 239 warn("openl2tp send: unexpected byte count %d, expected %d", 240 result, sizeof(msg) + msg->msg_len); 241 } 242 dbglog("openl2tp send: sent PPP_UPDOWN_IND, %d bytes", result); 243 244out: 245 if (old_pppol2tp_ip_updown_hook != NULL) { 246 (*old_pppol2tp_ip_updown_hook)(tunnel_id, session_id, up); 247 } 248 249 return; 250} 251 252/***************************************************************************** 253 * When a multilink interface is created, there are 2 cases to consider. 254 * 255 * 1. The new interface is the first of a multilink bundle (master). 256 * 2. The new interface is being attached to an existing bundle. 257 * 258 * The first case is handled by existing code because the interface 259 * generates ip-up events just like standard interfaces. But in the 260 * second case, where the interface is added to an existing ppp 261 * bundle, pppd does not do IP negotiation and so as a result, no 262 * ip-up event is generated when the interface is created. Since 263 * openl2tpd needs the SESSION_PPP_UPDOWN_IND for all interfaces of a 264 * PPP bundle, we must fake the event. 265 * 266 * We use the ip_multilink_join_hook to hear when an interface joins a 267 * multilink bundle. 268 *****************************************************************************/ 269 270static void openl2tp_multilink_join_ind(void) 271{ 272 if (doing_multilink && !multilink_master) { 273 /* send event only if not master */ 274 openl2tp_ppp_updown_ind(pppol2tp_tunnel_id, 275 pppol2tp_session_id, 1); 276 } 277} 278 279/***************************************************************************** 280 * Application init 281 *****************************************************************************/ 282 283void plugin_init(void) 284{ 285 old_pppol2tp_send_accm_hook = pppol2tp_send_accm_hook; 286 pppol2tp_send_accm_hook = openl2tp_send_accm_ind; 287 288 old_pppol2tp_ip_updown_hook = pppol2tp_ip_updown_hook; 289 pppol2tp_ip_updown_hook = openl2tp_ppp_updown_ind; 290 291 old_multilink_join_hook = multilink_join_hook; 292 multilink_join_hook = openl2tp_multilink_join_ind; 293} 294 295