static int l2tp_statusfile_file_delete(const char *root, const char *parent, const char *name) { int result; char filename[256]; if (root == NULL) { if (name != NULL) { sprintf(filename, "%s/%s", parent, name); } else if (parent != NULL) { strcpy(filename, parent); } else { strcpy(filename, L2TP_STATUSFILE_DIR); } } else { if (name != NULL) { sprintf(filename, "%s/%s/%s", root, parent, name); } else if (parent != NULL) { sprintf(filename, "%s/%s", root, parent); } else { strcpy(filename, root); } } L2TP_DEBUG(L2TP_FUNC, "FUNC: unlink(%s)", filename); #if 0 result = 0; #else result = unlink(filename); #endif if (result < 0) { result = -errno; } return result; }
void l2tp_pkt_free(struct l2tp_packet *pkt) { int buf; L2TP_DEBUG(L2TP_DATA, "%s: pkt=%p, msg=%d tid=%hu sid=%hu ns=%hu nr=%hu", __func__, pkt, pkt->msg_type, pkt->tunnel_id, pkt->session_id, pkt->nr, pkt->ns); if (pkt->num_bufs > 0) { for (buf = 0; buf < pkt->num_bufs; buf++) { if (pkt->iov[buf].iov_base != NULL) { #ifdef DEBUG if (pkt->iov[buf].iov_len == 0) { l2tp_log(LOG_ERR, "Freeing zero length buffer %p, index %d?", pkt->iov[buf].iov_base, buf); } USL_POISON_MEMORY(pkt->iov[buf].iov_base, 0xe8, pkt->iov[buf].iov_len); #endif free(pkt->iov[buf].iov_base); } else { break; } } } USL_POISON_MEMORY(pkt, 0xe7, sizeof(*pkt)); free(pkt); }
static int l2tp_statusfile_session_deleted_ind(struct l2tp_session const *session, uint16_t tunnel_id, uint16_t session_id) { int result; char name[32]; char dir[32]; if (l2tp_old_session_deleted_hook != NULL) { result = (*l2tp_old_session_deleted_hook)(session, tunnel_id, session_id); if (result < 0) { goto out; } } sprintf(&dir[0], "%hu/sessions", tunnel_id); sprintf(&name[0], "%hu/sessions/%hu", tunnel_id, session_id); result = l2tp_statusfile_dir_delete(L2TP_STATUSFILE_DIR, "tunnels", name, 1); (void) l2tp_statusfile_dir_delete(L2TP_STATUSFILE_DIR, "tunnels", dir, 0); if (result < 0) { L2TP_DEBUG(L2TP_FUNC, "%s: result=%d", __func__, result); } out: return result; }
static int l2tp_statusfile_peer_deleted_ind(struct in_addr src, struct in_addr dest) { int result; char name[32]; char srcip[16]; char destip[16]; char *ip; if (l2tp_old_peer_deleted_hook != NULL) { result = (*l2tp_old_peer_deleted_hook)(src, dest); if (result < 0) { goto out; } } ip = inet_ntoa(src); strcpy(&srcip[0], ip); ip = inet_ntoa(dest); strcpy(&destip[0], ip); sprintf(&name[0], "%s-%s", srcip, destip); result = l2tp_statusfile_file_delete(L2TP_STATUSFILE_DIR, "peers", name); (void) l2tp_statusfile_dir_delete(L2TP_STATUSFILE_DIR, "peers", NULL, 0); if (result < 0) { L2TP_DEBUG(L2TP_FUNC, "%s: result=%d", __func__, result); } out: return result; }
static void l2tp_cleanup(void) { pid_t pid; pid = getpid(); if (pid != l2tp_my_pid) { L2TP_DEBUG(L2TP_FUNC, "%s: not main pid so returning now", __FUNCTION__); return; } l2tp_log(LOG_INFO, "Cleaning up before exiting"); usl_signal_notifier_remove(l2tp_signal_handler, NULL); /* Cleanup all resources */ l2tp_api_cleanup(); l2tp_net_cleanup(); l2tp_avp_cleanup(); l2tp_ppp_cleanup(); l2tp_session_cleanup(); l2tp_xprt_cleanup(); l2tp_tunnel_cleanup(); l2tp_peer_cleanup(); usl_timer_cleanup(); usl_fd_cleanup(); usl_signal_cleanup(); usl_pid_cleanup(); if (l2tp_rand_fd != 0) { close(l2tp_rand_fd); } L2TP_DEBUG(L2TP_FUNC, "%s: done", __FUNCTION__); #ifdef L2TP_DMALLOC dmalloc_log_changed(l2tp_dmalloc_mark, 1, 0, 1); // dmalloc_log_unfreed(); // dmalloc_log_stats(); // dmalloc_log_heap_map(); #endif /* Remove pid file */ unlink(L2TP_PID_FILENAME); }
static int l2tp_parse_args(int argc, char **argv) { int opt; int result = 0; int plugin_loaded = 0; while((opt = getopt(argc, argv, "p:d:u:L:fRDh")) != -1) { switch(opt) { case 'h': usage(argv, 0); break; case 'f': l2tp_opt_nodaemon = 1; break; #ifdef DEBUG case 'D': l2tp_opt_debug = 1; break; #endif case 'd': l2tp_opt_trace_flags = l2tp_parse_debug_mask(optarg); break; case 'p': if (l2tp_plugin_load(optarg) < 0) { exit(1); } plugin_loaded = 1; break; case 'R': l2tp_opt_remote_rpc = 1; break; case 'L': l2tp_opt_log_facility = l2tp_parse_log_facility(optarg); if (l2tp_opt_log_facility < 0) { result = -EINVAL; } break; case 'u': sscanf(optarg, "%d", &l2tp_opt_udp_port); L2TP_DEBUG(L2TP_API, "Using port %d", l2tp_opt_udp_port); break; default: usage(argv, 1); } } /* Load ppp_unix plugin if no other plugin is requested */ if (!plugin_loaded) { if (l2tp_plugin_load("ppp_unix.so") < 0) { exit(1); } } return result; }
static FILE *l2tp_statusfile_file_create(const char *parent, const char *name) { char filename[256]; FILE *file; if (name != NULL) { sprintf(filename, L2TP_STATUSFILE_DIR "/%s/%s", parent, name); } else { sprintf(filename, L2TP_STATUSFILE_DIR "/%s", parent); } L2TP_DEBUG(L2TP_FUNC, "FUNC: creat(%s)", filename); file = fopen(filename, "w+"); return file; }
static void l2tp_init(void) { l2tp_log(LOG_INFO, "started"); #ifdef L2TP_DMALLOC /* dmalloc debug options are set in the environment. However, * certain options cause problems to this application. We * therefore ensure that the troublesome options are disabled, * regardless of the user's settings. The disabled options * are: alloc-blank, free-blank, force-linear. If these * options are enabled, it causes strange problems in the * generated RPC code. */ dmalloc_debug(dmalloc_debug_current() & 0xff5dffff); l2tp_dmalloc_mark = dmalloc_mark(); if (getenv("DMALLOC_OPTIONS") != NULL) { l2tp_log(LOG_WARNING, "DMALLOC debugging enabled"); } #endif l2tp_my_pid = getpid(); atexit(l2tp_cleanup); L2TP_DEBUG(L2TP_FUNC, "%s (%s %s): trace flags = %08lx", __FUNCTION__, __DATE__, __TIME__, l2tp_opt_trace_flags); usl_set_debug(l2tp_opt_debug, l2tp_system_log); usl_signal_terminate_hook = l2tp_die; usl_signal_init(); usl_fd_init(); usl_timer_init(); usl_pid_init(); l2tp_net_init(); l2tp_rand_fd = open("/dev/random", O_RDONLY); if (l2tp_rand_fd < 0) { fprintf(stderr, "No /dev/random device found. Exiting.\n"); exit(1); } usl_signal_notifier_add(l2tp_signal_handler, NULL); l2tp_avp_init(); l2tp_peer_init(); l2tp_api_init(); l2tp_xprt_init(); l2tp_tunnel_init(); l2tp_session_init(); l2tp_ppp_init(); }
static inline void l2tp_net_parse_header(struct l2tp_packet *pkt, uint16_t *ver, int *is_data, int *has_seq, void **payload) { struct l2tp_control_hdr* hdr = pkt->buf[0].data; uint16_t offset = 0; uint16_t dlen = 0; uint16_t *optval = (uint16_t *) &hdr->data; *ver = (uint16_t) hdr->ver; if (hdr->t_bit) { *is_data = 0; } else { *is_data = 1; } if (hdr->l_bit) { dlen = ntohs(*optval); optval++; } pkt->tunnel_id = ntohs(*optval); optval++; pkt->session_id = ntohs(*optval); optval++; if (hdr->s_bit) { pkt->ns = ntohs(*optval); optval++; pkt->nr = ntohs(*optval); optval++; *has_seq = 1; } else { *has_seq = 0; } if (hdr->o_bit) { offset = ntohs(*optval); optval++; } *payload = ((void *) optval) + offset; if (hdr->l_bit) { pkt->avp_len = dlen - (((void *) optval) - ((void *) hdr)); } else { pkt->avp_len = 0; } pkt->avp_offset = ((void *) *payload) - ((void *) hdr); L2TP_DEBUG(L2TP_XPRT, "%s: ver=%hu data=%d tid=%hu sid=%hu nr=%hu ns=%hu seq=%d offs=%hu len=%hu", __FUNCTION__, *ver, *is_data, pkt->tunnel_id, pkt->session_id, pkt->nr, pkt->ns, *has_seq, offset, pkt->avp_len); }
static int l2tp_statusfile_session_created_ind(struct l2tp_session const *session, uint16_t tunnel_id, uint16_t session_id) { int result = 0; char name[32]; char dir1[32]; char dir2[32]; FILE *file; struct l2tp_api_session_msg_data sess; optstring tunnel_name = { 0, }; optstring session_name = { 0, }; if (l2tp_old_session_created_hook != NULL) { result = (*l2tp_old_session_created_hook)(session, tunnel_id, session_id); if (result < 0) { goto out; } } sprintf(&dir1[0], "tunnels/%hu", tunnel_id); sprintf(&dir2[0], "tunnels/%hu/sessions", tunnel_id); sprintf(&name[0], "tunnels/%hu/sessions/%hu", tunnel_id, session_id); (void) l2tp_statusfile_dir_create("tunnels", NULL); (void) l2tp_statusfile_dir_create(dir1, NULL); (void) l2tp_statusfile_dir_create(dir2, NULL); (void) l2tp_statusfile_dir_create(name, NULL); file = l2tp_statusfile_file_create(name, "status"); if (file == NULL) { result = -errno; L2TP_DEBUG(L2TP_FUNC, "%s: result=%d", __func__, result); } memset(&sess, 0, sizeof(sess)); if (l2tp_session_get_1_svc(tunnel_id, tunnel_name, session_id, session_name, &sess, NULL)) { l2tp_show_session(file, &sess); } fclose(file); out: return result; }
static int l2tp_statusfile_peer_created_ind(struct in_addr src, struct in_addr dest) { int result = 0; char name[32]; char srcip[16]; char destip[16]; FILE *file; char *ip; struct l2tp_api_peer_msg_data peer; struct l2tp_api_ip_addr peer_addr = { src.s_addr, }; struct l2tp_api_ip_addr local_addr = { dest.s_addr, }; if (l2tp_old_peer_created_hook != NULL) { result = (*l2tp_old_peer_created_hook)(src, dest); if (result < 0) { goto out; } } ip = inet_ntoa(src); strcpy(&srcip[0], ip); ip = inet_ntoa(dest); strcpy(&destip[0], ip); sprintf(&name[0], "%s-%s", srcip, destip); (void) l2tp_statusfile_dir_create("peers", NULL); file = l2tp_statusfile_file_create("peers", name); if (file == NULL) { result = -errno; L2TP_DEBUG(L2TP_FUNC, "%s: result=%d", __func__, result); } memset(&peer, 0, sizeof(peer)); if (l2tp_peer_get_1_svc(local_addr, peer_addr, &peer, NULL)) { l2tp_show_peer(file, &peer); } fclose(file); out: return result; }
static int l2tp_statusfile_tunnel_down_ind(uint16_t tunnel_id) { int result; if (l2tp_old_tunnel_down_hook != NULL) { result = (*l2tp_old_tunnel_down_hook)(tunnel_id); if (result < 0) { goto out; } } (void) l2tp_statusfile_tunnel_deleted_ind(tunnel_id); result = l2tp_statusfile_tunnel_created_ind(tunnel_id); if (result < 0) { L2TP_DEBUG(L2TP_FUNC, "%s: result=%d", __func__, result); } out: return result; }
static int l2tp_statusfile_session_down_ind(struct l2tp_session const *session, uint16_t tunnel_id, uint16_t session_id) { int result; if (l2tp_old_session_down_hook != NULL) { result = (*l2tp_old_session_down_hook)(session, tunnel_id, session_id); if (result < 0) { goto out; } } (void) l2tp_statusfile_session_deleted_ind(session, tunnel_id, session_id); result = l2tp_statusfile_session_created_ind(session, tunnel_id, session_id); if (result < 0) { L2TP_DEBUG(L2TP_FUNC, "%s: result=%d", __func__, result); } out: return result; }
static int l2tp_statusfile_tunnel_deleted_ind(uint16_t tunnel_id) { int result; char name[16]; if (l2tp_old_tunnel_deleted_hook != NULL) { result = (*l2tp_old_tunnel_deleted_hook)(tunnel_id); if (result < 0) { goto out; } } sprintf(&name[0], "%hu", tunnel_id); result = l2tp_statusfile_dir_delete(L2TP_STATUSFILE_DIR, "tunnels", name, 1); (void) l2tp_statusfile_dir_delete(L2TP_STATUSFILE_DIR, "tunnels", NULL, 0); if (result < 0) { L2TP_DEBUG(L2TP_FUNC, "%s: result=%d", __func__, result); } out: return result; }
static int l2tp_statusfile_dir_create(const char *parent, const char *name) { int result; char dirname[256]; if (name != NULL) { sprintf(dirname, L2TP_STATUSFILE_DIR "/%s/%s", parent, name); } else if (parent != NULL) { sprintf(dirname, L2TP_STATUSFILE_DIR "/%s", parent); } else { strcpy(dirname, L2TP_STATUSFILE_DIR); } L2TP_DEBUG(L2TP_FUNC, "FUNC: mkdir(%s)", dirname); result = mkdir(dirname, 0755); if (result < 0) { result = -errno; } if (result == -EEXIST) { result = 0; } return result; }
static int l2tp_statusfile_tunnel_created_ind(uint16_t tunnel_id) { int result = 0; char name[16]; char dir[32]; FILE *file; struct l2tp_api_tunnel_msg_data tunnel; optstring tunnel_name = { 0, }; if (l2tp_old_tunnel_created_hook != NULL) { result = (*l2tp_old_tunnel_created_hook)(tunnel_id); if (result < 0) { goto out; } } sprintf(&name[0], "%hu", tunnel_id); sprintf(&dir[0], "tunnels/%hu", tunnel_id); (void) l2tp_statusfile_dir_create("tunnels", NULL); (void) l2tp_statusfile_dir_create("tunnels", name); file = l2tp_statusfile_file_create(dir, "status"); if (file == NULL) { result = -errno; L2TP_DEBUG(L2TP_FUNC, "%s: result=%d", __func__, result); goto out; } memset(&tunnel, 0, sizeof(tunnel)); if (l2tp_tunnel_get_1_svc(tunnel_id, tunnel_name, &tunnel, NULL)) { l2tp_show_tunnel(file, &tunnel, 1, 0); } fclose(file); out: return result; }
static void l2tp_net_recv_core(int fd, struct sockaddr_in const *from, struct msghdr *msg, int recv_len, struct in_pktinfo *ipi) { int result = -EBADMSG; struct l2tp_peer *peer = NULL; struct l2tp_tunnel *tunnel = NULL; char *peer_host_name = NULL; struct l2tp_packet *pkt = NULL; int frag; uint16_t ver; int is_data; int has_seq; void *payload; uint16_t msg_type; if (recv_len < L2TP_MIN_FRAME_LEN) { l2tp_stats.short_frames++; goto out; } /* Allocate a pkt to hold info about this packet. * Once we've done this, there's no need to use the struct msghdr. */ pkt = l2tp_pkt_alloc(msg->msg_iovlen); if (pkt == NULL) { result = -ENOMEM; l2tp_stats.no_control_frame_resources++; goto out; } for (frag = 0; frag < msg->msg_iovlen; frag++) { pkt->buf[frag].data = msg->msg_iov[frag].iov_base; pkt->buf[frag].data_len = MIN(recv_len, msg->msg_iov[frag].iov_len); } pkt->total_len = recv_len; /* Parse the L2TP packet header */ l2tp_net_parse_header(pkt, &ver, &is_data, &has_seq, &payload); if (ver != 2) { l2tp_stats.wrong_version_frames++; goto out; } /* If this is a data frame, return now. This should be handled by * the kernel's L2TP code. */ if (is_data) { l2tp_stats.unexpected_data_frames++; goto out; } #ifdef L2TP_TEST if (l2tp_test_is_fake_rx_drop(pkt->tunnel_id, pkt->session_id)) { L2TP_DEBUG(L2TP_DATA, "%s: fake rx drop: tid=%hu/%hu, len=%d", __FUNCTION__, pkt->tunnel_id, pkt->session_id, recv_len); goto out; } #endif /* L2TP_TEST */ L2TP_DEBUG(L2TP_PROTOCOL, "%s: received len %d tunl %hu ses %hu, from fd %d", __FUNCTION__, recv_len, pkt->tunnel_id, pkt->session_id, fd); if (pkt->avp_len > 0) { /* Don't count ZLBs as received frames */ l2tp_stats.total_rcvd_control_frames++; } if ((pkt->avp_offset + pkt->avp_len) > recv_len) { l2tp_stats.bad_rcvd_frames++; goto out; } /* If tunnel_id non-zero, find tunnel context by id and if not found, discard the frame. * If tunnel_id is zero, pre-parse the L2TP packet looking for a HOSTNAME attribute * which is mandatory for all messages when tunnel_id is zero. Use the name there * to locate the peer profile, then create the tunnel context. */ if (pkt->tunnel_id != 0) { /* Simple case - tunnel_id non-zero. If we can't find a tunnel context, bail */ tunnel = l2tp_tunnel_find_by_id(pkt->tunnel_id); if (tunnel == NULL) { l2tp_stats.no_matching_tunnel_id_discards++; goto out; } peer = l2tp_tunnel_get_peer(tunnel); } else { /* Complicated case - tunnel_id zero. Since we support incoming L2TP tunnel * setup requests, we must create internal contexts in order to handle the * request. However, we should only do this for SCCRQ messages... */ struct l2tp_peer_profile *peer_profile; result = l2tp_avp_preparse(payload, pkt->avp_len, &peer_host_name, &msg_type); if (result < 0) { if (result != -ENOMEM) { if (from != NULL) { L2TP_DEBUG(L2TP_PROTOCOL, "%s: dropping non-SCCRQ from %x/%hu on fd %d", __FUNCTION__, ntohl(from->sin_addr.s_addr), ntohs(from->sin_port), fd); } else { L2TP_DEBUG(L2TP_PROTOCOL, "%s: dropping non-SCCRQ on fd %d", __FUNCTION__, fd); } l2tp_stats.no_matching_tunnel_id_discards++; } goto out; } /* Find a peer profile. Since this tunnel is being created by remote request, an explicit * peer profile name cannot be specified by the remote peer. So we use the HOST_NAME AVP * to select it. If a peer profile with that name does not exist, try to find a profile * that matches the source IP address. Otherwise, we use the default profile. */ peer_profile = l2tp_peer_profile_find(peer_host_name); if (peer_profile == NULL) { peer_profile = l2tp_peer_profile_find_by_addr(from->sin_addr); if (peer_profile == NULL) { peer_profile = l2tp_peer_profile_find(L2TP_API_PEER_PROFILE_DEFAULT_PROFILE_NAME); } } L2TP_DEBUG(L2TP_PROTOCOL, "%s: using peer profile %s for %s (%x/%hu) on fd %d", __FUNCTION__, peer_profile->profile_name, peer_host_name, ntohl(from->sin_addr.s_addr), ntohs(from->sin_port), fd); /* Register a new peer context and record his addr */ if (ipi != NULL) { peer = l2tp_peer_find(&ipi->ipi_addr, &from->sin_addr); if (peer == NULL) { peer = l2tp_peer_alloc(ipi->ipi_addr, from->sin_addr); if (peer == NULL) { result = -ENOMEM; l2tp_stats.no_control_frame_resources++; goto out; } } l2tp_peer_inc_use_count(peer); } if (l2tp_opt_trace_flags & L2TP_PROTOCOL) { l2tp_log(LOG_DEBUG, "PROTO: Creating new tunnel context with profile '%s' for %s (%x/%hu)", peer_profile->default_tunnel_profile_name, peer_host_name, ntohl(from->sin_addr.s_addr), ntohs(from->sin_port)); } tunnel = l2tp_tunnel_alloc(0, peer_profile->default_tunnel_profile_name, peer_profile->profile_name, 0, &result); if (tunnel == NULL) { if (result == -ENOMEM) { l2tp_stats.no_control_frame_resources++; } goto out_unlink_peer; } l2tp_tunnel_link(tunnel); result = l2tp_tunnel_xprt_create(peer, tunnel, from); if (result < 0) { if (result == -ENOMEM) { l2tp_stats.no_control_frame_resources++; } goto out_unlink_tunnel; } /* Give plugins visibility of tunnel created */ if (l2tp_tunnel_created_hook != NULL) { result = (*l2tp_tunnel_created_hook)(l2tp_tunnel_peer_id(tunnel)); if (result < 0) { goto out_unlink_tunnel; } } } BUG_TRAP(tunnel == NULL); if (!l2tp_tunnel_is_fd_connected(tunnel)) { if (from == NULL) { from = l2tp_tunnel_get_peer_addr(tunnel); } result = l2tp_net_connect_socket(l2tp_tunnel_get_fd(tunnel), from, peer, tunnel); if (result < 0) { l2tp_stats.socket_errors++; goto out_unlink_tunnel; } } l2tp_tunnel_inc_use_count(tunnel); result = l2tp_xprt_recv(l2tp_tunnel_get_xprt(tunnel), pkt); l2tp_tunnel_dec_use_count(tunnel); out: /* l2tp_xprt_recv() consumes msg only if it returns 0 */ if (result < 0) { if (pkt == NULL) { for (frag = 0; frag < msg->msg_iovlen; frag++) { if (msg->msg_iov[frag].iov_base != NULL) { free(msg->msg_iov[frag].iov_base); } } } else { l2tp_pkt_free(pkt); } } /* This might have been allocated by l2tp_avp_preparse() */ if (peer_host_name != NULL) { free(peer_host_name); } return; out_unlink_tunnel: l2tp_tunnel_dec_use_count(tunnel); out_unlink_peer: l2tp_peer_dec_use_count(peer); l2tp_stats.tunnel_setup_failures++; goto out; }
static int l2tp_statusfile_dir_delete(const char *root, const char *parent, const char *name, int recursive) { int result; char dirname[256]; char filename[256]; DIR *dir; struct dirent *entry; struct stat statbuf; if (root == NULL) { if (name != NULL) { sprintf(dirname, "%s/%s", parent, name); } else if (parent != NULL) { strcpy(dirname, parent); } else { strcpy(dirname, L2TP_STATUSFILE_DIR); } } else { if (name != NULL) { sprintf(dirname, "%s/%s/%s", root, parent, name); } else if (parent != NULL) { sprintf(dirname, "%s/%s", root, parent); } else { strcpy(dirname, root); } } if (recursive) { dir = opendir(dirname); if (dir == NULL) { result = -errno; goto out; } while ((entry = readdir(dir)) != NULL) { if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) { continue; } sprintf(filename, "%s/%s", dirname, entry->d_name); result = stat(filename, &statbuf); if (result < 0) { continue; } if (S_ISDIR(statbuf.st_mode)) { (void) l2tp_statusfile_dir_delete(NULL, dirname, entry->d_name, recursive); } else { (void) l2tp_statusfile_file_delete(NULL, dirname, entry->d_name); } } L2TP_DEBUG(L2TP_FUNC, "FUNC: rmdir(%s)", dirname); #if 0 result = 0; #else result = rmdir(dirname); #endif closedir(dir); } else { L2TP_DEBUG(L2TP_FUNC, "FUNC: rmdir(%s)", dirname); #if 0 result = 0; #else result = rmdir(dirname); #endif } out: return result; }