/* * Add a {@link :l2tpd_listener} to the {@link ::l2tpd L2TP daemon} * @param _this {@link ::l2tpd L2TP daemon} * @param idx index of the lisnter * @param tun_name tunnel name (ex. "L2TP") * @param bindaddr bind address */ int l2tpd_add_listener(l2tpd *_this, int idx, struct l2tp_conf *conf, struct sockaddr *addr) { l2tpd_listener *plistener, *plsnr; plistener = NULL; if (idx == 0 && slist_length(&_this->listener) > 0) { slist_itr_first(&_this->listener); while (slist_itr_has_next(&_this->listener)) { slist_itr_next(&_this->listener); plsnr = slist_itr_remove(&_this->listener); L2TPD_ASSERT(plsnr != NULL); L2TPD_ASSERT(plsnr->sock == -1); free(plsnr); } } L2TPD_ASSERT(slist_length(&_this->listener) == idx); if (slist_length(&_this->listener) != idx) { l2tpd_log(_this, LOG_ERR, "Invalid argument error on %s(): idx must be %d but %d", __func__, slist_length(&_this->listener), idx); goto fail; } if ((plistener = calloc(1, sizeof(l2tpd_listener))) == NULL) { l2tpd_log(_this, LOG_ERR, "calloc() failed in %s: %m", __func__); goto fail; } L2TPD_ASSERT(sizeof(plistener->bind) >= addr->sa_len); memcpy(&plistener->bind, addr, addr->sa_len); if (plistener->bind.sin6.sin6_port == 0) plistener->bind.sin6.sin6_port = htons(L2TPD_DEFAULT_UDP_PORT); plistener->sock = -1; plistener->self = _this; plistener->index = idx; plistener->conf = conf; strlcpy(plistener->tun_name, conf->name, sizeof(plistener->tun_name)); if (slist_add(&_this->listener, plistener) == NULL) { l2tpd_log(_this, LOG_ERR, "slist_add() failed in %s: %m", __func__); goto fail; } return 0; fail: free(plistener); return 1; }
/* start L2TP daemon */ int l2tpd_start(l2tpd *_this) { int rval; l2tpd_listener *plsnr; rval = 0; L2TPD_ASSERT(_this->state == L2TPD_STATE_INIT); if (_this->state != L2TPD_STATE_INIT) { l2tpd_log(_this, LOG_ERR, "Failed to start l2tpd: illegal " "state."); return -1; } slist_itr_first(&_this->listener); while (slist_itr_has_next(&_this->listener)) { plsnr = slist_itr_next(&_this->listener); rval |= l2tpd_listener_start(plsnr); } if (rval == 0) _this->state = L2TPD_STATE_RUNNING; return rval; }
/** assign the call to the l2tpd */ int l2tpd_assign_call(l2tpd *_this, l2tp_call *call) { int shuffle_cnt; u_int session_id; shuffle_cnt = 0; do { session_id = (uintptr_t)slist_remove_first( &_this->free_session_id_list); if (session_id != L2TP_SESSION_ID_SHUFFLE_MARK) break; L2TPD_ASSERT(shuffle_cnt == 0); if (shuffle_cnt++ > 0) { l2tpd_log(_this, LOG_ERR, "unexpected errror in %s(): free_session_id_list " "full", __func__); slist_add(&_this->free_session_id_list, (void *)L2TP_SESSION_ID_SHUFFLE_MARK); return 1; } slist_shuffle(&_this->free_session_id_list); slist_add(&_this->free_session_id_list, (void *)L2TP_SESSION_ID_SHUFFLE_MARK); } while (1); call->id = session_id; return 0; }
/** * initialize L2TP daemon instance * <p> * {@link _l2tpd#bind_sin} will return with .sin_family = AF_INET, * .sin_port = 1701 and .sin_len = "appropriate value" * </p> */ int l2tpd_init(l2tpd *_this) { int i, off; u_int id; L2TPD_ASSERT(_this != NULL); memset(_this, 0, sizeof(l2tpd)); slist_init(&_this->listener); slist_init(&_this->free_session_id_list); _this->id = l2tpd_id_seq++; if ((_this->ctrl_map = hash_create(short_cmp, short_hash, L2TPD_TUNNEL_HASH_SIZ)) == NULL) { log_printf(LOG_ERR, "hash_create() failed in %s(): %m", __func__); return 1; } if (slist_add(&_this->free_session_id_list, (void *)L2TP_SESSION_ID_SHUFFLE_MARK) == NULL) { l2tpd_log(_this, LOG_ERR, "slist_add() failed on %s(): %m", __func__); return 1; } off = arc4random() & L2TP_SESSION_ID_MASK; for (i = 0; i < L2TP_NCALL; i++) { id = (i + off) & L2TP_SESSION_ID_MASK; if (id == 0) id = (off - 1) & L2TP_SESSION_ID_MASK; if (slist_add(&_this->free_session_id_list, (void *)(uintptr_t)id) == NULL) { l2tpd_log(_this, LOG_ERR, "slist_add() failed on %s(): %m", __func__); return 1; } } _this->purge_ipsec_sa = 1; _this->state = L2TPD_STATE_INIT; return 0; }
/* send control packet */ int l2tp_ctrl_send_packet(l2tp_ctrl *_this, int call_id, bytebuffer *bytebuf) { struct l2tp_header *hdr; int rval; time_t curr_time; curr_time = get_monosec(); bytebuffer_flip(bytebuf); hdr = (struct l2tp_header *)bytebuffer_pointer(bytebuf); memset(hdr, 0, sizeof(*hdr)); hdr->t = 1; hdr->ver = L2TP_HEADER_VERSION_RFC2661; hdr->l = 1; hdr->length = htons(bytebuffer_remaining(bytebuf)); hdr->tunnel_id = htons(_this->peer_tunnel_id); hdr->session_id = htons(call_id); hdr->s = 1; hdr->ns = htons(_this->snd_nxt); hdr->nr = htons(_this->rcv_nxt); if (bytebuffer_remaining(bytebuf) > sizeof(struct l2tp_header)) /* Not ZLB */ _this->snd_nxt++; L2TP_CTRL_DBG((_this, DEBUG_LEVEL_2, "SEND C ns=%u nr=%u snd_nxt=%u snd_una=%u rcv_nxt=%u ", ntohs(hdr->ns), htons(hdr->nr), _this->snd_nxt, _this->snd_una, _this->rcv_nxt)); if (L2TP_CTRL_CONF(_this)->ctrl_out_pktdump != 0) { l2tpd_log(_this->l2tpd, LOG_DEBUG, "L2TP Control output packet dump"); show_hd(debug_get_debugfp(), bytebuffer_pointer(bytebuf), bytebuffer_remaining(bytebuf)); } if ((rval = l2tp_ctrl_send(_this, bytebuffer_pointer(bytebuf), bytebuffer_remaining(bytebuf))) < 0) { L2TP_CTRL_DBG((_this, LOG_DEBUG, "sendto() failed: %m")); } _this->last_snd_ctrl = curr_time; return (rval == bytebuffer_remaining(bytebuf))? 0 : 1; }
/* stop l2tp lisnter */ static void l2tpd_listener_stop(l2tpd_listener *_this) { char hbuf[NI_MAXHOST + NI_MAXSERV + 16]; if (_this->sock >= 0) { event_del(&_this->ev_sock); close(_this->sock); l2tpd_log(_this->self, LOG_INFO, "Shutdown %s/udp (L2TP LNS)", addrport_tostring((struct sockaddr *)&_this->bind, _this->bind.sin6.sin6_len, hbuf, sizeof(hbuf))); _this->sock = -1; } }
static void l2tpd_stop_timeout(int fd, short evtype, void *ctx) { hash_link *hl; l2tp_ctrl *ctrl; l2tpd *_this; _this = ctx; l2tpd_log(_this, LOG_INFO, "Shutdown timeout"); for (hl = hash_first(_this->ctrl_map); hl != NULL; hl = hash_next(_this->ctrl_map)) { ctrl = hl->item; l2tp_ctrl_stop(ctrl, 0); } l2tpd_stop_immediatly(_this); }
/* send L2TP data message */ static int l2tp_call_send_data_packet(l2tp_call *_this, bytebuffer *buffer) { int rval; struct l2tp_header *hdr; bytebuffer_flip(buffer); hdr = (struct l2tp_header *)bytebuffer_pointer(buffer); memset(hdr, 0, sizeof(*hdr) - 4); /* Nr, NS are option */ hdr->t = 0; hdr->ver = L2TP_HEADER_VERSION_RFC2661; hdr->l = 1; hdr->length = htons(bytebuffer_remaining(buffer)); hdr->tunnel_id = htons(_this->ctrl->peer_tunnel_id); hdr->session_id = htons(_this->peer_session_id); if (_this->use_seq) { hdr->s = 1; hdr->ns = htons(_this->snd_nxt++); hdr->nr = htons(_this->rcv_nxt); } if (L2TP_CTRL_CONF(_this->ctrl)->data_out_pktdump != 0) { l2tpd_log(_this->ctrl->l2tpd, LOG_DEBUG, "ctrl=%u call=%u L2TP Data output packet dump", _this->ctrl->id, _this->id); show_hd(debug_get_debugfp(), bytebuffer_pointer(buffer), bytebuffer_remaining(buffer)); } if ((rval = l2tp_ctrl_send(_this->ctrl, bytebuffer_pointer(buffer), bytebuffer_remaining(buffer))) < 0) { L2TP_CALL_DBG((_this, LOG_DEBUG, "sendto() failed: %m")); } return (rval == bytebuffer_remaining(buffer))? 0 : 1; }
/* start l2tpd listner */ static int l2tpd_listener_start(l2tpd_listener *_this) { l2tpd *_l2tpd; int af, lvl, opt, sock, ival; char hbuf[NI_MAXHOST + NI_MAXSERV + 16]; _l2tpd = _this->self; sock = -1; af = _this->bind.sin6.sin6_family; lvl = (af == AF_INET)? IPPROTO_IP : IPPROTO_IPV6; if (_this->tun_name[0] == '\0') strlcpy(_this->tun_name, L2TPD_DEFAULT_LAYER2_LABEL, sizeof(_this->tun_name)); if ((sock = socket(_this->bind.sin6.sin6_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP)) < 0) { l2tpd_log(_l2tpd, LOG_ERR, "socket() failed in %s(): %m", __func__); goto fail; } #if defined(IP_STRICT_RCVIF) && defined(USE_STRICT_RCVIF) ival = 1; if (setsockopt(sock, IPPROTO_IP, IP_STRICT_RCVIF, &ival, sizeof(ival)) != 0) l2tpd_log(_l2tpd, LOG_WARNING, "%s(): setsockopt(IP_STRICT_RCVIF) failed: %m", __func__); #endif ival = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &ival, sizeof(ival)) != 0) { l2tpd_log(_l2tpd, LOG_ERR, "setsockopt(,,SO_REUSEPORT) failed in %s(): %m", __func__); goto fail; } if (bind(sock, (struct sockaddr *)&_this->bind, _this->bind.sin6.sin6_len) != 0) { l2tpd_log(_l2tpd, LOG_ERR, "Binding %s/udp: %m", addrport_tostring((struct sockaddr *)&_this->bind, _this->bind.sin6.sin6_len, hbuf, sizeof(hbuf))); goto fail; } #ifdef USE_LIBSOCKUTIL if (setsockoptfromto(sock) != 0) { l2tpd_log(_l2tpd, LOG_ERR, "setsockoptfromto() failed in %s(): %m", __func__); goto fail; } #else opt = (af == AF_INET)? IP_RECVDSTADDR : IPV6_RECVPKTINFO; ival = 1; if (setsockopt(sock, lvl, opt, &ival, sizeof(ival)) != 0) { l2tpd_log(_l2tpd, LOG_ERR, "setsockopt(,,IP{,V6}_RECVDSTADDR) failed in %s(): %m", __func__); goto fail; } #endif #ifdef USE_SA_COOKIE if (af == AF_INET) { ival = 1; if (setsockopt(sock, IPPROTO_IP, IP_IPSECFLOWINFO, &ival, sizeof(ival)) != 0) { l2tpd_log(_l2tpd, LOG_ERR, "setsockopt(,,IP_IPSECFLOWINFO) failed in %s(): %m", __func__); goto fail; } } #endif #ifdef IP_PIPEX opt = (af == AF_INET)? IP_PIPEX : IPV6_PIPEX; ival = 1; if (setsockopt(sock, lvl, opt, &ival, sizeof(ival)) != 0) l2tpd_log(_l2tpd, LOG_WARNING, "%s(): setsockopt(IP{,V6}_PIPEX) failed: %m", __func__); #endif if (_this->conf->require_ipsec) { #ifdef IP_IPSEC_POLICY caddr_t ipsec_policy_in, ipsec_policy_out; opt = (af == AF_INET)? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY; /* * Note: ipsec_set_policy() will assign the buffer for * yacc parser stack, however it never free. * it cause memory leak (-2000byte). */ if ((ipsec_policy_in = ipsec_set_policy(L2TPD_IPSEC_POLICY_IN, strlen(L2TPD_IPSEC_POLICY_IN))) == NULL) { l2tpd_log(_l2tpd, LOG_ERR, "ipsec_set_policy(L2TPD_IPSEC_POLICY_IN) failed " "at %s(): %s: %m", __func__, ipsec_strerror()); } else if (setsockopt(sock, lvl, opt, ipsec_policy_in, ipsec_get_policylen(ipsec_policy_in)) < 0) { l2tpd_log(_l2tpd, LOG_WARNING, "setsockopt(,,IP_IPSEC_POLICY(in)) failed " "in %s(): %m", __func__); } if ((ipsec_policy_out = ipsec_set_policy(L2TPD_IPSEC_POLICY_OUT, strlen(L2TPD_IPSEC_POLICY_OUT))) == NULL) { l2tpd_log(_l2tpd, LOG_ERR, "ipsec_set_policy(L2TPD_IPSEC_POLICY_OUT) failed " "at %s(): %s: %m", __func__, ipsec_strerror()); } if (ipsec_policy_out != NULL && setsockopt(sock, lvl, opt, ipsec_policy_out, ipsec_get_policylen(ipsec_policy_out)) < 0) { l2tpd_log(_l2tpd, LOG_WARNING, "setsockopt(,,IP_IPSEC_POLICY(out)) failed " "in %s(): %m", __func__); } free(ipsec_policy_in); free(ipsec_policy_out); #elif defined(IP_ESP_TRANS_LEVEL) opt = (af == AF_INET) ? IP_ESP_TRANS_LEVEL : IPV6_ESP_TRANS_LEVEL; ival = IPSEC_LEVEL_REQUIRE; if (setsockopt(sock, lvl, opt, &ival, sizeof(ival)) != 0) { l2tpd_log(_l2tpd, LOG_WARNING, "setsockopt(,,IP{,V6}_ESP_TRANS_LEVEL(out)) failed " "in %s(): %m", __func__); } #else #error IP_IPSEC_POLICY or IP_ESP_TRANS_LEVEL must be usable. #endif } _this->sock = sock; event_set(&_this->ev_sock, _this->sock, EV_READ | EV_PERSIST, l2tpd_io_event, _this); event_add(&_this->ev_sock, NULL); l2tpd_log(_l2tpd, LOG_INFO, "Listening %s/udp (L2TP LNS) [%s]", addrport_tostring((struct sockaddr *)&_this->bind, _this->bind.sin6.sin6_len, hbuf, sizeof(hbuf)), _this->tun_name); return 0; fail: if (sock >= 0) close(sock); return 1; }
/* Receive packet */ void l2tp_ctrl_input(l2tpd *_this, int listener_index, struct sockaddr *peer, struct sockaddr *sock, void *nat_t_ctx, u_char *pkt, int pktlen) { int i, len, offsiz, reqlen, is_ctrl; uint16_t mestype; struct l2tp_avp *avp, *avp0; l2tp_ctrl *ctrl; l2tp_call *call; char buf[L2TP_AVP_MAXSIZ], errmsg[256]; time_t curr_time; u_char *pkt0; struct l2tp_header hdr; char hbuf[NI_MAXHOST + NI_MAXSERV + 16]; ctrl = NULL; curr_time = get_monosec(); pkt0 = pkt; L2TP_CTRL_ASSERT(peer->sa_family == sock->sa_family); L2TP_CTRL_ASSERT(peer->sa_family == AF_INET || peer->sa_family == AF_INET6) /* * Parse L2TP Header */ memset(&hdr, 0, sizeof(hdr)); if (pktlen < 2) { snprintf(errmsg, sizeof(errmsg), "a short packet. " "length=%d", pktlen); goto bad_packet; } memcpy(&hdr, pkt, 2); pkt += 2; if (hdr.ver != L2TP_HEADER_VERSION_RFC2661) { /* XXX: only RFC2661 is supported */ snprintf(errmsg, sizeof(errmsg), "Unsupported version at header = %d", hdr.ver); goto bad_packet; } is_ctrl = (hdr.t != 0)? 1 : 0; /* calc required length */ reqlen = 6; /* for Flags, Tunnel-Id, Session-Id field */ if (hdr.l) reqlen += 2; /* for Length field (opt) */ if (hdr.s) reqlen += 4; /* for Ns, Nr field (opt) */ if (hdr.o) reqlen += 2; /* for Offset Size field (opt) */ if (reqlen > pktlen) { snprintf(errmsg, sizeof(errmsg), "a short packet. length=%d", pktlen); goto bad_packet; } if (hdr.l != 0) { GETSHORT(hdr.length, pkt); if (hdr.length > pktlen) { snprintf(errmsg, sizeof(errmsg), "Actual packet size is smaller than the length " "field %d < %d", pktlen, hdr.length); goto bad_packet; } pktlen = hdr.length; /* remove trailing trash */ } GETSHORT(hdr.tunnel_id, pkt); GETSHORT(hdr.session_id, pkt); if (hdr.s != 0) { GETSHORT(hdr.ns, pkt); GETSHORT(hdr.nr, pkt); } if (hdr.o != 0) { GETSHORT(offsiz, pkt); if (pktlen < offsiz) { snprintf(errmsg, sizeof(errmsg), "offset field is bigger than remaining packet " "length %d > %d", offsiz, pktlen); goto bad_packet; } pkt += offsiz; } L2TP_CTRL_ASSERT(pkt - pkt0 == reqlen); pktlen -= (pkt - pkt0); /* cut down the length of header */ ctrl = NULL; memset(buf, 0, sizeof(buf)); mestype = 0; avp = NULL; if (is_ctrl) { avp0 = (struct l2tp_avp *)buf; avp = avp_find_message_type_avp(avp0, pkt, pktlen); if (avp != NULL) mestype = avp->attr_value[0] << 8 | avp->attr_value[1]; } ctrl = l2tpd_get_ctrl(_this, hdr.tunnel_id); if (ctrl == NULL) { /* new control */ if (!is_ctrl) { snprintf(errmsg, sizeof(errmsg), "bad data message: tunnelId=%d is not " "found.", hdr.tunnel_id); goto bad_packet; } if (mestype != L2TP_AVP_MESSAGE_TYPE_SCCRQ) { snprintf(errmsg, sizeof(errmsg), "bad control message: tunnelId=%d is not " "found. mestype=%s", hdr.tunnel_id, avp_mes_type_string(mestype)); goto bad_packet; } if ((ctrl = l2tp_ctrl_create()) == NULL) { l2tp_ctrl_log(ctrl, LOG_ERR, "l2tp_ctrl_create() failed: %m"); goto fail; } if (l2tp_ctrl_init(ctrl, _this, peer, sock, nat_t_ctx) != 0) { l2tp_ctrl_log(ctrl, LOG_ERR, "l2tp_ctrl_start() failed: %m"); goto fail; } ctrl->listener_index = listener_index; l2tp_ctrl_reload(ctrl); } else { /* * treat as an error if src address and port is not * match. (because it is potentially DoS attach) */ int notmatch = 0; if (ctrl->peer.ss_family != peer->sa_family) notmatch = 1; else if (peer->sa_family == AF_INET) { if (SIN(peer)->sin_addr.s_addr != SIN(&ctrl->peer)->sin_addr.s_addr || SIN(peer)->sin_port != SIN(&ctrl->peer)->sin_port) notmatch = 1; } else if (peer->sa_family == AF_INET6) { if (!IN6_ARE_ADDR_EQUAL(&(SIN6(peer)->sin6_addr), &(SIN6(&ctrl->peer)->sin6_addr)) || SIN6(peer)->sin6_port != SIN6(&ctrl->peer)->sin6_port) notmatch = 1; } if (notmatch) { snprintf(errmsg, sizeof(errmsg), "tunnelId=%u is already assigned for %s", hdr.tunnel_id, addrport_tostring( (struct sockaddr *)&ctrl->peer, ctrl->peer.ss_len, hbuf, sizeof(hbuf))); goto bad_packet; } } ctrl->last_rcv = curr_time; call = NULL; if (hdr.session_id != 0) { /* search l2tp_call by Session ID */ /* linear search is enough for this purpose */ len = slist_length(&ctrl->call_list); for (i = 0; i < len; i++) { call = slist_get(&ctrl->call_list, i); if (call->session_id == hdr.session_id) break; call = NULL; } } if (!is_ctrl) { int delayed = 0; /* L2TP data */ if (ctrl->state != L2TP_CTRL_STATE_ESTABLISHED) { l2tp_ctrl_log(ctrl, LOG_WARNING, "Received Data packet in '%s'", l2tp_ctrl_state_string(ctrl)); goto fail; } if (call == NULL) { l2tp_ctrl_log(ctrl, LOG_WARNING, "Received a data packet but it has no call. " "session_id=%u", hdr.session_id); goto fail; } L2TP_CTRL_DBG((ctrl, DEBUG_LEVEL_2, "call=%u RECV ns=%u nr=%u snd_nxt=%u rcv_nxt=%u len=%d", call->id, hdr.ns, hdr.nr, call->snd_nxt, call->rcv_nxt, pktlen)); if (call->state != L2TP_CALL_STATE_ESTABLISHED){ l2tp_ctrl_log(ctrl, LOG_WARNING, "Received a data packet but call is not " "established"); goto fail; } if (hdr.s != 0) { if (SEQ_LT(hdr.ns, call->rcv_nxt)) { if (SEQ_LT(hdr.ns, call->rcv_nxt - L2TP_CALL_DELAY_LIMIT)) { /* sequence number seems to be delayed */ /* XXX: need to log? */ L2TP_CTRL_DBG((ctrl, LOG_DEBUG, "receive a out of sequence " "data packet: %u < %u.", hdr.ns, call->rcv_nxt)); return; } delayed = 1; } else { call->rcv_nxt = hdr.ns + 1; } } l2tp_call_ppp_input(call, pkt, pktlen, delayed); return; } if (hdr.s != 0) { L2TP_CTRL_DBG((ctrl, DEBUG_LEVEL_2, "RECV %s ns=%u nr=%u snd_nxt=%u snd_una=%u rcv_nxt=%u " "len=%d", (is_ctrl)? "C" : "", hdr.ns, hdr.nr, ctrl->snd_nxt, ctrl->snd_una, ctrl->rcv_nxt, pktlen)); if (pktlen <= 0) l2tp_ctrl_log(ctrl, LOG_INFO, "RecvZLB"); if (SEQ_GT(hdr.nr, ctrl->snd_una)) { if (hdr.nr == ctrl->snd_nxt || SEQ_LT(hdr.nr, ctrl->snd_nxt)) ctrl->snd_una = hdr.nr; else { l2tp_ctrl_log(ctrl, LOG_INFO, "Received message has bad Nr field: " "%u < %u.", hdr.ns, ctrl->snd_nxt); /* XXX Drop with ZLB? */ goto fail; } } if (l2tp_ctrl_txwin_size(ctrl) <= 0) { /* no waiting ack */ if (ctrl->hello_wait_ack != 0) { /* * Reset Hello state, as an ack for the Hello * is recived. */ ctrl->hello_wait_ack = 0; ctrl->hello_io_time = curr_time; } switch (ctrl->state) { case L2TP_CTRL_STATE_CLEANUP_WAIT: l2tp_ctrl_stop(ctrl, 0); return; } } if (hdr.ns != ctrl->rcv_nxt) { /* there are remaining packet */ if (l2tp_ctrl_resend_una_packets(ctrl) <= 0) { /* resend or sent ZLB */ l2tp_ctrl_send_ZLB(ctrl); } #ifdef L2TP_CTRL_DEBUG if (pktlen != 0) { /* not ZLB */ L2TP_CTRL_DBG((ctrl, LOG_DEBUG, "receive out of sequence %u must be %u. " "mestype=%s", hdr.ns, ctrl->rcv_nxt, avp_mes_type_string(mestype))); } #endif return; } if (pktlen <= 0) return; /* ZLB */ if (l2tp_ctrl_txwin_is_full(ctrl)) { L2TP_CTRL_DBG((ctrl, LOG_DEBUG, "Received message cannot be handled. " "Transmission window is full.")); l2tp_ctrl_send_ZLB(ctrl); return; } ctrl->rcv_nxt++; if (avp == NULL) { l2tpd_log(_this, LOG_WARNING, "bad control message: no message-type AVP."); goto fail; } } /* * state machine (RFC2661 pp. 56-57) */ switch (ctrl->state) { case L2TP_CTRL_STATE_IDLE: switch (mestype) { case L2TP_AVP_MESSAGE_TYPE_SCCRQ: if (l2tp_ctrl_recv_SCCRQ(ctrl, pkt, pktlen, _this, peer) == 0) { /* acceptable */ l2tp_ctrl_send_SCCRP(ctrl); ctrl->state = L2TP_CTRL_STATE_WAIT_CTL_CONN; return; } /* * in case un-acceptable, it was already processed * at l2tcp_ctrl_recv_SCCRQ */ return; case L2TP_AVP_MESSAGE_TYPE_SCCRP: /* * RFC specifies that sent of StopCCN in the state, * However as this implementation only support Passive * open, this packet will not received. */ /* FALLTHROUGH */ case L2TP_AVP_MESSAGE_TYPE_SCCCN: default: break; } goto fsm_fail; case L2TP_CTRL_STATE_WAIT_CTL_CONN: /* Wait-Ctl-Conn */ switch (mestype) { case L2TP_AVP_MESSAGE_TYPE_SCCCN: l2tp_ctrl_log(ctrl, LOG_INFO, "RecvSCCN"); if (l2tp_ctrl_send_ZLB(ctrl) == 0) { ctrl->state = L2TP_CTRL_STATE_ESTABLISHED; } return; case L2TP_AVP_MESSAGE_TYPE_StopCCN: goto receive_stop_ccn; case L2TP_AVP_MESSAGE_TYPE_SCCRQ: case L2TP_AVP_MESSAGE_TYPE_SCCRP: default: break; } break; /* fsm_fail */ case L2TP_CTRL_STATE_ESTABLISHED: /* Established */ switch (mestype) { case L2TP_AVP_MESSAGE_TYPE_SCCCN: case L2TP_AVP_MESSAGE_TYPE_SCCRQ: case L2TP_AVP_MESSAGE_TYPE_SCCRP: break; receive_stop_ccn: case L2TP_AVP_MESSAGE_TYPE_StopCCN: if (l2tp_ctrl_recv_StopCCN(ctrl, pkt, pktlen) == 0) { if (l2tp_ctrl_resend_una_packets(ctrl) <= 0) l2tp_ctrl_send_ZLB(ctrl); l2tp_ctrl_stop(ctrl, 0); return; } l2tp_ctrl_log(ctrl, LOG_ERR, "Received bad StopCCN"); l2tp_ctrl_send_ZLB(ctrl); l2tp_ctrl_stop(ctrl, 0); return; case L2TP_AVP_MESSAGE_TYPE_HELLO: if (l2tp_ctrl_resend_una_packets(ctrl) <= 0) l2tp_ctrl_send_ZLB(ctrl); return; case L2TP_AVP_MESSAGE_TYPE_CDN: case L2TP_AVP_MESSAGE_TYPE_ICRP: case L2TP_AVP_MESSAGE_TYPE_ICCN: if (call == NULL) { l2tp_ctrl_log(ctrl, LOG_INFO, "Unknown call message: %s", avp_mes_type_string(mestype)); goto fail; } /* FALLTHROUGH */ case L2TP_AVP_MESSAGE_TYPE_ICRQ: l2tp_call_recv_packet(ctrl, call, mestype, pkt, pktlen); return; default: break; } break; /* fsm_fail */ case L2TP_CTRL_STATE_CLEANUP_WAIT: if (mestype == L2TP_AVP_MESSAGE_TYPE_StopCCN) { /* * We left ESTABLISHED state, but the peer sent StopCCN. */ goto receive_stop_ccn; } break; /* fsm_fail */ } fsm_fail: /* state machine error */ l2tp_ctrl_log(ctrl, LOG_WARNING, "Received %s in '%s' state", avp_mes_type_string(mestype), l2tp_ctrl_state_string(ctrl)); l2tp_ctrl_stop(ctrl, L2TP_STOP_CCN_RCODE_FSM_ERROR); return; fail: if (ctrl != NULL && mestype != 0) { l2tp_ctrl_log(ctrl, LOG_WARNING, "Received %s in '%s' state", avp_mes_type_string(mestype), l2tp_ctrl_state_string(ctrl)); l2tp_ctrl_stop(ctrl, L2TP_STOP_CCN_RCODE_GENERAL_ERROR); } return; bad_packet: l2tpd_log(_this, LOG_INFO, "Received from=%s: %s", addrport_tostring(peer, peer->sa_len, hbuf, sizeof(hbuf)), errmsg); return; }
/** * initialize and startup of {@link ::_l2tp_ctrl L2TP LNS control connection} * instance */ static int l2tp_ctrl_init(l2tp_ctrl *_this, l2tpd *_l2tpd, struct sockaddr *peer, struct sockaddr *sock, void *nat_t_ctx) { int tunid, i; bytebuffer *bytebuf; time_t curr_time; memset(_this, 0, sizeof(l2tp_ctrl)); curr_time = get_monosec(); _this->l2tpd = _l2tpd; _this->state = L2TP_CTRL_STATE_IDLE; _this->last_snd_ctrl = curr_time; slist_init(&_this->call_list); /* seek a free tunnel ID */ i = 0; _this->id = ++l2tp_ctrl_id_seq; for (i = 0, tunid = _this->id; ; i++, tunid++) { tunid &= 0xffff; _this->tunnel_id = l2tp_ctrl_id_seq & 0xffff; if (tunid == 0) continue; if (l2tpd_get_ctrl(_l2tpd, tunid) == NULL) break; if (i > 80000) { /* this must be happen, just log it. */ l2tpd_log(_l2tpd, LOG_ERR, "Too many l2tp controls"); return -1; } } _this->tunnel_id = tunid; L2TP_CTRL_ASSERT(peer != NULL); L2TP_CTRL_ASSERT(sock != NULL); memcpy(&_this->peer, peer, peer->sa_len); memcpy(&_this->sock, sock, sock->sa_len); /* prepare send buffer */ _this->winsz = L2TPD_DEFAULT_SEND_WINSZ; if ((_this->snd_buffers = calloc(_this->winsz, sizeof(bytebuffer *))) == NULL) { l2tpd_log(_l2tpd, LOG_ERR, "calloc() failed in %s(): %m", __func__); goto fail; } for (i = 0; i < _this->winsz; i++) { if ((bytebuf = bytebuffer_create(L2TPD_SND_BUFSIZ)) == NULL) { l2tpd_log(_l2tpd, LOG_ERR, "bytebuffer_create() failed in %s(): %m", __func__); goto fail; } _this->snd_buffers[i] = bytebuf; } if ((_this->zlb_buffer = bytebuffer_create(sizeof(struct l2tp_header) + 128)) == NULL) { l2tpd_log(_l2tpd, LOG_ERR, "bytebuffer_create() failed in %s(): %m", __func__); goto fail; } #if defined(USE_LIBSOCKUTIL) || defined(USE_SA_COOKIE) if (nat_t_ctx != NULL) { if ((_this->sa_cookie = malloc( sizeof(struct in_ipsec_sa_cookie))) != NULL) { *(struct in_ipsec_sa_cookie *)_this->sa_cookie = *(struct in_ipsec_sa_cookie *)nat_t_ctx; } else { l2tpd_log(_l2tpd, LOG_ERR, "creating sa_cookie failed: %m"); goto fail; } } #endif _this->hello_interval = L2TP_CTRL_DEFAULT_HELLO_INTERVAL; _this->hello_timeout = L2TP_CTRL_DEFAULT_HELLO_TIMEOUT; _this->hello_io_time = curr_time; /* initialize timeout timer */ l2tp_ctrl_reset_timeout(_this); /* register l2tp context */ l2tpd_add_ctrl(_l2tpd, _this); return 0; fail: l2tp_ctrl_stop(_this, 0); return -1; }