void l2tp_pkt_queue_purge(struct usl_list_head *head) { struct usl_list_head *tmp; struct usl_list_head *walk; struct l2tp_packet *pkt; usl_list_for_each(walk, tmp, head) { pkt = usl_list_entry(walk, struct l2tp_packet, list); usl_list_del(&pkt->list); l2tp_pkt_free(pkt); }
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; }