/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipl2tp_v2_state.c,v $ $Revision: 1.10.12.1 $ * $Source: /home/interpeak/CVSRoot/ipl2tp/src/ipl2tp_v2_state.c,v $ * $Author: hxu0 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Joel Hortelius ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #include "ipl2tp_h.h" #include "ipl2tp_api.h" #include "ipl2tp_avp.h" #include "ipl2tp_data.h" #include "ipl2tp_message.h" #include "ipl2tp_state.h" #include "ipl2tp_pseudowire.h" #include "ipl2tp_trpt.h" #include "ipl2tp_v2.h" #include "ipl2tp_v2_state.h" /* **************************************************************************** * 4 DEFINES **************************************************************************** */ /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ /* *=========================================================================== * ipl2tpv2_compulsory_avp_* *=========================================================================== * * Indicates which AVP types that are compulsory in a message. */ IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_void[] = { IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_sccrq[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_PROTOCOL_VERSION, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_HOST_NAME, IPL2TP_AVP_FRAMING_CAPABILITIES, IPL2TP_AVP_ASSIGNED_TUNNEL_ID, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_sccrp[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_PROTOCOL_VERSION, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_HOST_NAME, IPL2TP_AVP_FRAMING_CAPABILITIES, IPL2TP_AVP_ASSIGNED_TUNNEL_ID, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_scccn[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_stopccn[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_ASSIGNED_TUNNEL_ID, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_RESULT_CODE, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_hello[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_icrq[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_ASSIGNED_SESSION_ID, IPL2TP_AVP_CALL_SERIAL_NUMBER, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_icrp[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_ASSIGNED_SESSION_ID, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_iccn[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_TX_CONNECT_SPEED, IPL2TP_AVP_FRAMING_TYPE, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_ocrq[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_ASSIGNED_SESSION_ID, IPL2TP_AVP_CALL_SERIAL_NUMBER, IPL2TP_AVP_MINIMUM_BPS, IPL2TP_AVP_MAXIMUM_BPS, IPL2TP_AVP_BEARER_TYPE, IPL2TP_AVP_FRAMING_TYPE, IPL2TP_AVP_CALLED_NUMBER, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_ocrp[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_ASSIGNED_SESSION_ID, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_occn[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_TX_CONNECT_SPEED, IPL2TP_AVP_FRAMING_TYPE, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_cdn[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_ASSIGNED_SESSION_ID, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_RESULT_CODE, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_wen[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_CALL_ERRORS, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v2_compulsory_avp_sli[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_ACCM, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 *ipl2tp_v2_avp_compulsory[] = { ipl2tp_v2_compulsory_avp_void, /* (0) reserved */ ipl2tp_v2_compulsory_avp_sccrq, /* (1) sccrq */ ipl2tp_v2_compulsory_avp_sccrp, /* (2) sccrp */ ipl2tp_v2_compulsory_avp_scccn, /* (3) scccn */ ipl2tp_v2_compulsory_avp_stopccn, /* (4) stopccn */ ipl2tp_v2_compulsory_avp_void, /* (5) reserved */ ipl2tp_v2_compulsory_avp_hello, /* (6) hello */ ipl2tp_v2_compulsory_avp_ocrq, /* (7) ocrq */ ipl2tp_v2_compulsory_avp_ocrp, /* (8) ocrp */ ipl2tp_v2_compulsory_avp_occn, /* (9) occn */ ipl2tp_v2_compulsory_avp_icrq, /* (10) icrq */ ipl2tp_v2_compulsory_avp_icrp, /* (11) icrp */ ipl2tp_v2_compulsory_avp_iccn, /* (12) iccn */ ipl2tp_v2_compulsory_avp_void, /* (13) reserved */ ipl2tp_v2_compulsory_avp_cdn, /* (14) cdn */ ipl2tp_v2_compulsory_avp_wen, /* (15) wen */ ipl2tp_v2_compulsory_avp_sli, /* (16) sli */ ipl2tp_v2_compulsory_avp_void, /* (17) unimplemented */ ipl2tp_v2_compulsory_avp_void, /* (18) unimplemented */ ipl2tp_v2_compulsory_avp_void, /* (19) unimplemented */ ipl2tp_v2_compulsory_avp_void, /* (20) unimplemented */ IP_NULL }; /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipl2tp_v2_state_tunnel_setup *=========================================================================== * Description: Check if AVPs in SCCRQ or SCCRP message are appropriate. * Parameters: tunnel - Tunnel descriptor. * avp - Parsed AVPs from the message. * Returns: IPL2TP_STATUS_OK if ok. * */ IP_STATIC int ipl2tp_v2_state_tunnel_setup(Ipl2tp_tunnel *tunnel, Ipl2tp_avp *avp) { /* * No need to check that the compulsory AVPs exist since * they already should have been checked when parsing. */ /* Protocol version */ if (avp->protocol_version.ver > IPL2TP_PROTOCOL_VER) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_PROTVERSION, IPL2TP_RES_StopCCN_PROTOCOL_VERSION, IPL2TP_PROTOCOL_VER, "Invalid protocol version"); IPCOM_LOG5(WARNING, "%s(%d,%s): Protocol version %d.%d is not supported, only " IPL2TP_NUM2STR(IPL2TP_PROTOCOL_VER) ".x", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), (int)avp->protocol_version.ver, (int)avp->protocol_version.rev); return IPL2TP_STATUS_ERROR; } /* Host name */ IPL2TP_SAFE_SWAP(avp->host_name, tunnel->common.host_name); IPL2TP_SAFE_SWAP(avp->vendor_name, tunnel->common.vendor_name); /* Firmware revision */ tunnel->v.v2.firmware_revision = IP_GET_NTOHS(&avp->firmware_revision.value); /* Protocol version */ tunnel->v.v2.protocol_version[0] = avp->protocol_version.ver; tunnel->v.v2.protocol_version[1] = avp->protocol_version.rev; /* Assigned tunnel ID */ if (avp->assigned_tunnel_id.value == 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_IDZERO, IPL2TP_RES_StopCCN_GENERAL_ERROR, IPL2TP_ERR_OUT_OF_RANGE, "Assigned tunnel ID is 0"); IPCOM_LOG4(WARNING, "%s(%d,%s): Assigned tunnel ID is 0 in %s", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), ipl2tp_message_name(avp->message_type.value)); return IPL2TP_STATUS_ERROR; } /* Assign peer ID */ IPL2TP_TUNNEL_ASSIGN_PEER_ID(tunnel, avp); /* Framing capabilities */ tunnel->v.v2.framing_capabilities = (Ip_u8)avp->framing_capabilities.value; /* Bearer capabilities */ tunnel->v.v2.bearer_capabilities = (Ip_u8)avp->bearer_capabilities.value; /* RWS */ if (avp->receive_window_size.head.ok && (tunnel->common.receive_window_size = avp->receive_window_size.value) == 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_BADRECWIN, IPL2TP_RES_StopCCN_GENERAL_ERROR, IPL2TP_ERR_OUT_OF_RANGE, "Receive window size is 0"); IPCOM_LOG4(WARNING, "%s(%d,%s): Receive window size is 0 in %s", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), ipl2tp_message_name(avp->message_type.value)); return IPL2TP_STATUS_ERROR; } /* Handle host name authentication */ if (tunnel->attr.authentication == IPL2TP_MIB_AUTH_SIMPLE) { if (tunnel->attr.peer_host_name == IP_NULL) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_BADAUTH, IPL2TP_RES_StopCCN_NOT_AUTHORIZED, 0, "No peer host name configured"); IPCOM_LOG3(WARNING, "%s(%d,%s): No peer host name configured", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); return IPL2TP_STATUS_ERROR; } else if (ipcom_strcmp(tunnel->common.host_name, tunnel->attr.peer_host_name) != 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_BADAUTH, IPL2TP_RES_StopCCN_NOT_AUTHORIZED, 0, "Host name authentication failure"); IPCOM_LOG3(WARNING, "%s(%d,%s): Host name authentication failure", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); return IPL2TP_STATUS_ERROR; } else { IPCOM_LOG3(DEBUG, "%s(%d,%s): Host name authentication OK", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); } } /* Tie breaker */ IPL2TP_SAFE_VEC(avp->tie_breaker, tunnel->common.tie_breaker); return IPL2TP_STATUS_OK; } /* *=========================================================================== * ipl2tp_v2_state_bearer_verify *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_bool ipl2tp_v2_state_bearer_verify(Ipl2tp_session *session, Ipl2tp_avp *avp) { /* Check if allowed bearer */ if (avp->bearer_type.head.ok) { if (avp->bearer_type.value > IPL2TP_MIB_BEARER_NONE && (ipl2tp_pw_bearer2bits(avp->bearer_type.value) & ipl2tp_pw_bearer2bits(session->tunnel->v.v2.bearer_capabilities)) == 0) { IPL2TP_TUNNEL_RESULT_SET(session->tunnel, IPL2TP_STATUS_BADBEARER, IPL2TP_RES_CDN_GENERAL_ERROR, IPL2TP_ERR_OUT_OF_RANGE, "Invalid bearer type for tunnel"); IPCOM_LOG5(WARNING, "%s(%d,%d,%s): Invalid bearer type for tunnel: %d", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) session->tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&session->tunnel->remote.addr), (int) avp->bearer_type.value); return IP_FALSE; } session->attr.bearer_type = (Ip_u8)avp->bearer_type.value; } return IP_TRUE; } /* *=========================================================================== * ipl2tp_v2_state_framing_verify *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_bool ipl2tp_v2_state_framing_verify(Ipl2tp_session *session, Ipl2tp_avp *avp) { /* Check if allowed framing */ if (avp->framing_type.value > IPL2TP_MIB_FRAMING_NONE && (ipl2tp_pw_framing2bits(avp->framing_type.value) & ipl2tp_pw_framing2bits(session->tunnel->v.v2.framing_capabilities)) == 0) { IPL2TP_TUNNEL_RESULT_SET(session->tunnel, IPL2TP_STATUS_BADFRAMING, IPL2TP_RES_CDN_GENERAL_ERROR, IPL2TP_ERR_OUT_OF_RANGE, "Invalid framing type for tunnel"); IPCOM_LOG5(WARNING, "%s(%d,%d,%s): Invalid framing type for tunnel: %d", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) session->tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&session->tunnel->remote.addr), (int) avp->framing_type.value); return IP_FALSE; } session->attr.framing_type = (Ip_u8)avp->framing_type.value; return IP_TRUE; } /* *=========================================================================== * ipl2tp_v2_state_session_setup *=========================================================================== * Description: Check if AVPs in ICRP or OCRQ message are appropriate. * Parameters: session - Session descriptor. * avp - Parsed AVPs from the message. * Returns: IPL2TP_STATUS_OK if ok. * */ IP_STATIC int ipl2tp_v2_state_session_setup(Ipl2tp_session *session, Ipl2tp_avp *avp) { Ipl2tp_tunnel *tunnel = session->tunnel; /* * No need to check that the compulsory AVPs exist since * they already should have been checked when parsing. */ /* Assigned session ID */ if (avp->message_type.value == IPL2TP_MSG_OCRQ || avp->message_type.value == IPL2TP_MSG_ICRQ || avp->message_type.value == IPL2TP_MSG_OCRP || avp->message_type.value == IPL2TP_MSG_ICRP) { if ((session->remote.key.ident = avp->assigned_session_id.value) == 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_IDZERO, IPL2TP_RES_CDN_GENERAL_ERROR, IPL2TP_ERR_OUT_OF_RANGE, "Assigned session ID is 0"); IPCOM_LOG5(WARNING, "%s(%d,%d,%s): Assigned session ID is 0 in %s", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), ipl2tp_message_name(avp->message_type.value)); return IPL2TP_STATUS_ERROR; } } if (avp->message_type.value == IPL2TP_MSG_OCRQ) { /* Check if allowed bearer */ if (ipl2tp_v2_state_bearer_verify(session, avp) == IP_FALSE) return IPL2TP_STATUS_ERROR; /* Check framing */ if (ipl2tp_v2_state_framing_verify(session, avp) == IP_FALSE) return IPL2TP_STATUS_ERROR; /* Enter rest of OCRQ */ session->attr.minimum_bps = avp->minimum_bps.value; session->attr.maximum_bps = avp->maximum_bps.value; IPL2TP_SAFE_SWAP(avp->called_number, session->attr.called_number); IPL2TP_SAFE_SWAP(avp->sub_address, session->attr.sub_address); session->common.call_serial_number = avp->call_serial_number.value; } else if (avp->message_type.value == IPL2TP_MSG_ICRQ) { session->common.call_serial_number = avp->call_serial_number.value; /* Physical channel ID may be provided */ IPL2TP_SAFE_ASSIGN(avp->physical_channel_id, session->attr.phys_chan_id); /* Calling number may be provided. */ IPL2TP_SAFE_SWAP(avp->calling_number, session->attr.calling_number); IPL2TP_SAFE_SWAP(avp->called_number, session->attr.called_number); IPL2TP_SAFE_SWAP(avp->sub_address, session->attr.sub_address); } else if (avp->message_type.value == IPL2TP_MSG_OCRP) { /* Physical channel ID may be provided */ IPL2TP_SAFE_ASSIGN(avp->physical_channel_id, session->attr.phys_chan_id); } else if (avp->message_type.value == IPL2TP_MSG_ICRP) { /* Not much to do */ } else if (avp->message_type.value == IPL2TP_MSG_OCCN) { if (ipl2tp_v2_state_framing_verify(session, avp) != IP_TRUE) return IPL2TP_STATUS_ERROR; IPL2TP_SAFE_ASSIGN(avp->tx_connect_speed, session->attr.tx_connect_speed); IPL2TP_SAFE_ASSIGN(avp->rx_connect_speed, session->attr.rx_connect_speed); if (avp->sequencing_required.head.ok) session->v.v2.sequencing = 1; } else if (avp->message_type.value == IPL2TP_MSG_ICCN) { if (ipl2tp_v2_state_framing_verify(session, avp) != IP_TRUE) return IPL2TP_STATUS_ERROR; /* Do the AVP's */ IPL2TP_SAFE_ASSIGN(avp->tx_connect_speed, session->attr.tx_connect_speed); IPL2TP_SAFE_VEC(avp->initial_received_lcp_confreq, session->attr.first_received_confreq); IPL2TP_SAFE_VEC(avp->last_sent_lcp_confreq, session->attr.last_sent_confreq); IPL2TP_SAFE_VEC(avp->last_received_lcp_confreq, session->attr.last_received_confreq); IPL2TP_SAFE_ASSIGN(avp->proxy_authen_type, session->attr.auth_method); IPL2TP_SAFE_SWAP(avp->proxy_authen_name, session->attr.proxy_authen_name); IPL2TP_SAFE_VEC(avp->proxy_authen_challenge, session->attr.proxy_authen_challenge); IPL2TP_SAFE_ASSIGN(avp->proxy_authen_id, session->attr.proxy_authen_id); IPL2TP_SAFE_VEC(avp->proxy_authen_response, session->attr.proxy_authen_response); IPL2TP_SAFE_SWAP(avp->private_group_id, session->attr.private_group_id); IPL2TP_SAFE_ASSIGN(avp->rx_connect_speed, session->attr.rx_connect_speed); if (avp->sequencing_required.head.ok) session->v.v2.sequencing = 1; } return IPL2TP_STATUS_OK; } /* *=========================================================================== * ipl2tp_v2_state_response_check *=========================================================================== * Description: Check if challenge response OK. * Parameters: tunnel - Tunnel descriptor. * avp - Parsed AVPs. * Returns: * */ IP_STATIC Ipl2tp_status ipl2tp_v2_state_response_check(Ipl2tp_tunnel *tunnel, Ipl2tp_avp *avp) { /* A response may only be present when requested */ if (tunnel->attr.authentication == IPL2TP_MIB_AUTH_CHALLENGE) { if (avp->challenge_response.head.ok == 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_BADAUTH, IPL2TP_RES_StopCCN_NOT_AUTHORIZED, 0, "Missing challenge response from peer"); IPCOM_LOG3(WARNING, "%s(%d,%s) Missing challenge response from peer", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); } else if (tunnel->attr.secret.size == 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_BADAUTH, IPL2TP_RES_StopCCN_NOT_AUTHORIZED, 0, "No authentication secret configured"); IPCOM_LOG3(WARNING, "%s(%d,%s) No authentication secret configured", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); } else if (ipl2tp_avp_challenge_verify(avp, tunnel) != IP_TRUE) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_BADAUTH, IPL2TP_RES_StopCCN_NOT_AUTHORIZED, 0, "Challenge authentication failure"); IPCOM_LOG3(WARNING, "%s(%d,%s) Challenge authentication failure", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); } else { IPCOM_LOG3(DEBUG, "%s(%d,%s) Challenge authentication OK", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); return IPL2TP_STATUS_OK; } } else { if (avp->challenge_response.head.ok != 0) { IPCOM_LOG3(WARNING, "%s(%d,%s) Unsolicited challenge response from peer", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); } return IPL2TP_STATUS_OK; } return IPL2TP_STATUS_ERROR; } /* *=========================================================================== * ipl2tp_v2_state_session_callback *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ipl2tp_status ipl2tp_v2_state_session_callback(Ipl2tp_session *session, Ip_u16 code, const char *what) { int cb; Ipl2tp_tunnel *tunnel = session->tunnel; /* Notify waiting clients */ IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_OK, code, 0, what); IPL2TP_AVAILABLE(); cb = IPL2TP_SAFE_ICALLBACK(session->attr.scallback, (session->attr.user_data, &session->local.key, &tunnel->last_result)); IPL2TP_OCCUPIED(return IPL2TP_STATUS_OCCUPIED); if (cb != 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_TEARDOWN, IPL2TP_RES_CDN_GENERAL_ERROR, 0, "Session creation aborted"); IPCOM_LOG4(WARNING, "%s(%d,%d,%s): Session creation aborted", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); return IPL2TP_STATUS_ERROR; } else { IPCOM_LOG6(INFO, "%s(%d,%d,%s): %s, remote id is %d", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), what, (int)session->remote.key.ident); return IPL2TP_STATUS_OK; } } /* *=========================================================================== * ipl2tp_v2_state_tunnel_initialize *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ipl2tp_status ipl2tp_v2_state_tunnel_initialize(Ipl2tp_tunnel *tunnel, Ipl2tp_avp *avp) { int cb; IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_OK, IPL2TP_OUTGOING_REQUEST, 0, "Outgoing tunnel request"); IPL2TP_AVAILABLE(); cb = IPL2TP_SAFE_ICALLBACK(tunnel->attr.scallback, (tunnel->attr.user_data, &tunnel->local.key, &tunnel->last_result)); IPL2TP_OCCUPIED(return IPL2TP_STATUS_OCCUPIED); if (cb != 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_TEARDOWN, IPL2TP_RES_StopCCN_GENERAL_ERROR, IPL2TP_ERR_VENDOR_ERROR_LAC, "Tunnel creation aborted"); IPCOM_LOG3(WARNING, "%s(%d,%s): Tunnel creation aborted", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); return IPL2TP_STATUS_ERROR; } if (ipl2tp_message_sccrp(tunnel, avp) != IPL2TP_STATUS_OK) return IPL2TP_STATUS_ERROR; IPCOM_LOG3(INFO, "%s(%d,%s): Received good SCCRQ, sent SCCRP", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); (void)ipl2tp_state_establish_tunnel_timeout_init(tunnel, 1); tunnel->state = IPL2TP_TSTATE_WAIT_CTL_CONN; return IPL2TP_STATUS_OK; } /* *=========================================================================== * ipl2tp_v2_state_tunnel_initialize *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ipl2tp_status ipl2tp_v2_state_tunnel_finalize(Ipl2tp_tunnel *tunnel, Ipl2tp_avp *avp, Ip_u16 code) { int cb; IPL2TP_UNUSED(avp); (void)ipl2tp_state_establish_tunnel_timeout_init(tunnel, 0); tunnel->state = IPL2TP_TSTATE_ESTABLISHED; /* Notify client */ IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_OK, code, 0, "Tunnel established"); IPL2TP_AVAILABLE(); cb = IPL2TP_SAFE_ICALLBACK(tunnel->attr.scallback, (tunnel->attr.user_data, &tunnel->local.key, &tunnel->last_result)); IPL2TP_OCCUPIED(return IPL2TP_STATUS_OCCUPIED); if (cb != 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_TEARDOWN, IPL2TP_RES_StopCCN_GENERAL_ERROR, IPL2TP_ERR_VENDOR_ERROR_LAC, "Tunnel creation aborted"); IPCOM_LOG3(WARNING, "%s(%d,%s): Tunnel creation aborted", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); return IPL2TP_STATUS_ERROR; } else { Ipl2tp_session *session; /* Start HELLO processing */ ipl2tp_trpt_hello_init(tunnel); IPCOM_LOG4(INFO, "%s(%d,%s): Tunnel established, remote id is %d", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), (int)tunnel->remote.key.ident); /* Start session negotiations */ for (session = IPCOM_LIST_FIRST(&tunnel->session_list); session != IP_NULL; session = IPCOM_LIST_NEXT(&session->head)) { ipl2tp_message_rq(session); } return IPL2TP_STATUS_OK; } } /* *=========================================================================== * ipl2tp_v2_state_machine_session *=========================================================================== * Description: Handle an incoming IPL2TP message according to state. * Parameters: tunnel - Tunnel descriptor. * session - Session descriptor. * avp - Parsed AVPs from the message. * Returns: IPL2TP_SUCCESS - Success. * */ IP_STATIC Ipl2tp_status ipl2tp_v2_state_machine_session(Ipl2tp_tunnel *tunnel, Ipl2tp_session *session, Ipl2tp_avp *avp) { if (session == IP_NULL) return (IPL2TP_STATUS_ERROR); /* State machine */ switch (avp->message_type.value) { case IPL2TP_MSG_ICRQ: /* Only valid if we're waiting for the reply */ if (session->state != IPL2TP_SSTATE_IDLE) goto errstate; if (ipl2tp_v2_state_session_setup(session, avp) != IPL2TP_STATUS_OK) goto stopcdn; session->state = IPL2TP_SSTATE_WAIT_CS_ANSWER; ipl2tp_state_establish_session_timeout_init(session, 1); /* Status callback */ if (ipl2tp_v2_state_session_callback(session, IPL2TP_INCOMING_REQUEST, "Incoming session request") != IPL2TP_STATUS_OK) goto stopcdn; tunnel->needzlb = 1; break; case IPL2TP_MSG_ICRP: /* Increment session count here since a teardown will decrement */ tunnel->statistics.active_sessions++; if (session->state != IPL2TP_SSTATE_WAIT_REPLY) goto errstate; if (ipl2tp_v2_state_session_setup(session, avp) != IPL2TP_STATUS_OK) goto stopcdn; /* Ready to fly */ ipl2tp_message_iccn(session); ipl2tp_state_establish_session_timeout_init(session, 0); session->state = IPL2TP_SSTATE_ESTABLISHED; tunnel->statistics.total_sessions++; if (ipl2tp_v2_state_session_callback(session, IPL2TP_INCOMING_ESTABLISHED, "Incoming tunnel established") != IPL2TP_STATUS_OK) goto stopcdn; break; case IPL2TP_MSG_ICCN: if (session->state != IPL2TP_SSTATE_WAIT_CONNECT) goto errstate; if (ipl2tp_v2_state_session_setup(session, avp) != IPL2TP_STATUS_OK) goto stopcdn; tunnel->statistics.total_sessions++; tunnel->statistics.active_sessions++; session->state = IPL2TP_SSTATE_ESTABLISHED; ipl2tp_state_establish_session_timeout_init(session, 0); /* Status callback */ if (ipl2tp_v2_state_session_callback(session, IPL2TP_INCOMING_ESTABLISHED, "Incoming session established") != IPL2TP_STATUS_OK) goto stopcdn; tunnel->needzlb = 1; break; case IPL2TP_MSG_OCRQ: if (session->state != IPL2TP_SSTATE_IDLE) goto errstate; if (ipl2tp_v2_state_session_setup(session, avp) != IPL2TP_STATUS_OK) goto stopcdn; /* Notify user */ if (ipl2tp_v2_state_session_callback(session, IPL2TP_OUTGOING_REQUEST, "Outgoing session request") != IPL2TP_STATUS_OK) goto stopcdn; /* Ready to fly */ ipl2tp_message_ocrp(session); ipl2tp_state_establish_session_timeout_init(session, 1); session->state = IPL2TP_SSTATE_WAIT_CS_ANSWER; IPCOM_LOG4(INFO, "%s(%d,%d,%s): Received good OCRQ, sent OCRP", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); /* All done? */ if (ipl2tp_v2_state_session_callback(session, IPL2TP_OUTGOING_REPLY, "Outgoing session reply") != IPL2TP_STATUS_OK) goto stopcdn; break; case IPL2TP_MSG_OCRP: /* Only valid if we're waiting for the reply */ if (session->state != IPL2TP_SSTATE_WAIT_REPLY) goto errstate; if (ipl2tp_v2_state_session_setup(session, avp) != IPL2TP_STATUS_OK) goto stopcdn; /**/ ipl2tp_state_establish_session_timeout_init(session, 1); session->state = IPL2TP_SSTATE_WAIT_CONNECT; break; case IPL2TP_MSG_OCCN: if (session->state != IPL2TP_SSTATE_WAIT_CONNECT) goto errstate; if (ipl2tp_v2_state_session_setup(session, avp) != IPL2TP_STATUS_OK) goto stopcdn; tunnel->statistics.total_sessions++; tunnel->statistics.active_sessions++; session->state = IPL2TP_SSTATE_ESTABLISHED; ipl2tp_state_establish_session_timeout_init(session, 0); /* Status callback */ if (ipl2tp_v2_state_session_callback(session, IPL2TP_OUTGOING_ESTABLISHED, "Outgoing session established") != IPL2TP_STATUS_OK) goto stopcdn; tunnel->needzlb = 1; break; case IPL2TP_MSG_CDN: /* Log */ IPCOM_LOG7(INFO, "%s(%d,%d,%s): Got CDN with result: %d, error: %d, message:\'%s\'", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), (int)avp->result_code.result_code, (int)avp->result_code.error_code, (avp->result_code.error_message == IP_NULL ? "" : avp->result_code.error_message)); /* Set status */ IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_GOTCDN, avp->result_code.result_code, avp->result_code.error_code, "Got CDN from peer"); /* Prepare reply */ session->remote.key.ident = avp->assigned_session_id.value; tunnel->needzlb = 1; ipl2tp_state_session_teardown(session); break; case IPL2TP_MSG_SLI: IPL2TP_AVAILABLE(); IPL2TP_SAFE_CALLBACK(session->attr.slicallback, (session->attr.user_data, &session->local.key, avp->accm.send_accm, avp->accm.receive_accm)); IPL2TP_OCCUPIED(return IPL2TP_STATUS_OCCUPIED); tunnel->needzlb = 1; break; } return IPL2TP_STATUS_OK; errstate: IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_STATE, IPL2TP_RES_CDN_GENERAL_ERROR, 0, "Session state machine error"); IPCOM_LOG6(WARNING, "%s(%d,%d,%s): Got %s for session in %s state", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), ipl2tp_message_name(avp->message_type.value), ipl2tp_state_sstate_name(session->state)); stopcdn: if (session != IP_NULL) { ipl2tp_message_cdn(session); ipl2tp_state_session_teardown(session); } return IPL2TP_STATUS_OK; } /* *=========================================================================== * ipl2tp_v2_state_machine_tunnel *=========================================================================== * Description: Handle an incoming IPL2TP message according to state. * Parameters: tunnel - Tunnel descriptor. * avp - Parsed AVPs from the message. * Returns: IPL2TP_SUCCESS - Success. * */ IP_STATIC Ipl2tp_status ipl2tp_v2_state_machine_tunnel(Ipl2tp_tunnel *tunnel, Ipl2tp_avp *avp) { /* */ if (tunnel->slave != IP_NULL) { /* */ Ipl2tp_tunnel *slave = tunnel->slave; /* */ slave->slave = IP_NULL; tunnel->slave = IP_NULL; /* Need to notify regarding key change */ /* Notify client */ IPL2TP_TUNNEL_RESULT_SET(slave, IPL2TP_STATUS_NEGOTIATION, 0, 0, "Redundant tunnel removed"); IPL2TP_AVAILABLE(); IPL2TP_SAFE_ICALLBACK(slave->attr.scallback, (slave->attr.user_data, &slave->local.key, &slave->last_result)); IPL2TP_OCCUPIED(return IPL2TP_STATUS_OCCUPIED); /* Remove the slave tunnel; we're doing V2 */ ipl2tp_tunnel_delete(slave); } /* State machine */ switch (avp->message_type.value) { case IPL2TP_MSG_SCCRQ: if (ipl2tp_v2_state_tunnel_setup(tunnel, avp) != IPL2TP_STATUS_OK) goto stopcdn; if (ipl2tp_state_tie_break_win(tunnel, avp) == 0) { /* Remove new tunnel */ IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_EXISTS, IPL2TP_RES_StopCCN_CHANNEL_EXISTS, 0, "Tunnel already exists"); ipl2tp_state_tunnel_teardown(tunnel); break; } /* Initialize the tunnel */ if (ipl2tp_v2_state_tunnel_initialize(tunnel, avp) != IPL2TP_STATUS_OK) goto stopcdn; break; case IPL2TP_MSG_SCCRP: if (tunnel->state != IPL2TP_TSTATE_WAIT_CTL_REPLY) goto errstate; if (ipl2tp_v2_state_tunnel_setup(tunnel, avp) != IPL2TP_STATUS_OK) goto stopcdn; if (ipl2tp_v2_state_response_check(tunnel, avp) != IPL2TP_STATUS_OK) goto stopcdn; if (ipl2tp_message_scccn(tunnel, avp) != IPL2TP_STATUS_OK) goto stopcdn; if (ipl2tp_v2_state_tunnel_finalize(tunnel, avp, IPL2TP_INCOMING_ESTABLISHED) != IPL2TP_STATUS_OK) goto stopcdn; break; case IPL2TP_MSG_SCCCN: if (tunnel->state != IPL2TP_TSTATE_WAIT_CTL_CONN) goto errstate; if (ipl2tp_v2_state_tunnel_finalize(tunnel, avp, IPL2TP_OUTGOING_ESTABLISHED) != IPL2TP_STATUS_OK) goto stopcdn; tunnel->needzlb = 1; break; case IPL2TP_MSG_StopCCN: /* Log */ IPCOM_LOG6(INFO, "%s(%d,%s): Got StopCCN with result: %d, error: %d, message:\'%s\'", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), (int)avp->result_code.result_code, (int)avp->result_code.error_code, (avp->result_code.error_message == IP_NULL ? "" : avp->result_code.error_message)); /* Set status */ IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_GOTSTOPCCN, avp->result_code.result_code, avp->result_code.error_code, "Got StopCCN from peer"); /* Prepare reply */ IPL2TP_TUNNEL_ASSIGN_PEER_ID(tunnel, avp); tunnel->needzlb = 1; ipl2tp_state_tunnel_teardown(tunnel); break; case IPL2TP_MSG_HELLO: if (tunnel->state != IPL2TP_TSTATE_ESTABLISHED) goto errstate; /* Reply */ tunnel->needzlb = 1; break; default: goto errstate; } return IPL2TP_STATUS_OK; errstate: IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_STATE, IPL2TP_RES_StopCCN_STATE_MACHINE, 0, "Tunnel state machine error"); IPCOM_LOG6(WARNING, "%s(%d,%s): Got %s(%u) for tunnel in %s state", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), ipl2tp_message_name(avp->message_type.value), (int)avp->message_type.value, ipl2tp_state_tstate_name(tunnel->state)); stopcdn: ipl2tp_message_stopccn(tunnel); ipl2tp_state_tunnel_teardown(tunnel); return IPL2TP_STATUS_OK; } /* *=========================================================================== * ipl2tp_v2_state_machine *=========================================================================== * Description: Handle an incoming IPL2TP message according to state. * Parameters: tunnel - Tunnel descriptor. * session - Session descriptor. * avp - Parsed AVPs from the message. * Returns: IPL2TP_SUCCESS - Success. * */ IP_STATIC Ipl2tp_status ipl2tp_v2_state_machine(Ipl2tp_tunnel *tunnel, Ipl2tp_session *session, Ipl2tp_avp *avp) { /* State machine */ switch (avp->message_type.value) { case IPL2TP_MSG_SCCRQ: case IPL2TP_MSG_SCCRP: case IPL2TP_MSG_SCCCN: case IPL2TP_MSG_StopCCN: case IPL2TP_MSG_HELLO: return ipl2tp_v2_state_machine_tunnel(tunnel, avp); case IPL2TP_MSG_ICRQ: case IPL2TP_MSG_ICRP: case IPL2TP_MSG_ICCN: case IPL2TP_MSG_OCRQ: case IPL2TP_MSG_OCRP: case IPL2TP_MSG_OCCN: case IPL2TP_MSG_CDN: case IPL2TP_MSG_SLI: return ipl2tp_v2_state_machine_session(tunnel,session,avp); default: if (avp->message_type.head.mandatory) return ipl2tp_v2_state_machine_tunnel(tunnel,avp); else { if (session) { IPCOM_LOG5(WARNING, "%s(%d,%d,%s): Got unknown mandatory control message: %d", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), (int)avp->message_type.value); } else { IPCOM_LOG4(WARNING, "%s(%d,%s): Got unknown mandatory control message: %d", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), (int)avp->message_type.value); } } break; } return IPL2TP_STATUS_OK; } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipl2tp_v2_state_machine_sccrq *=========================================================================== * Description: Handle an incoming IPL2TP SCCRQ according to state. * Parameters: tunnel - Tunnel descriptor. * Returns: IPL2TP_SUCCESS - Success. * */ IP_GLOBAL Ipl2tp_status ipl2tp_v2_state_machine_sccrq(Ipl2tp_tunnel *tunnel) { /* Parse the second round */ tunnel->sccrq.avp->status |= 1L << ipl2tp_avp_parse(tunnel->sccrq.avp, tunnel->sccrq.message, tunnel->sccrq.message_index, tunnel->sccrq.message_length, IP_TRUE, tunnel, &tunnel->remote.addr); /* Last chance to get peer tunnel ID */ IPL2TP_TUNNEL_ASSIGN_PEER_ID(tunnel, tunnel->sccrq.avp); /* Handle all accumulated errors */ if (tunnel->sccrq.avp->status & (1L << IPL2TP_STATUS_IGNORE)) { /* */ return IPL2TP_STATUS_IGNORE; } else if (tunnel->sccrq.avp->status & (1L << IPL2TP_STATUS_TEARDOWN)) { /* Get saved error codes and message */ IPL2TP_TUNNEL_RESULT_RESTORE(tunnel); } /* Do the AVP */ return ipl2tp_v2_state_machine_tunnel(tunnel, tunnel->sccrq.avp); } /* *=========================================================================== * ipl2tp_v2_state_control_input *=========================================================================== * Description: Handle an incoming L2TP control packet. * Parameters: packet - An L2TP packet. * ipaddr - IP address of sender. * Returns: IPL2TP_SUCCESS - Success. * */ IP_GLOBAL Ipl2tp_status ipl2tp_v2_state_control_input(Ipl2tp_v2_header *message, Ip_u16 length, union Ip_sockaddr_union *addr) { Ipl2tp_tunnel *tunnel = IP_NULL; Ipl2tp_session *session = IP_NULL; Ipl2tp_status status; Ipl2tp_avp *avp; Ip_size_t mindex = sizeof(*message); Ip_size_t message_length = IP_GET_NTOHS(&message->length); Ip_u16 flags = IP_GET_NTOHS(&message->flv); Ipl2tp_key tkey; Ipl2tp_key skey; IPL2TP_UNUSED(length); tkey.version = IPL2TP_VERSION_L2TPV2; skey.version = IPL2TP_VERSION_L2TPV2; tkey.ident = IP_GET_NTOHS(&message->tunnel_id); skey.ident = IP_GET_NTOHS(&message->session_id); /* Do we have the offset field? */ if (IP_BIT_ISSET(flags, IPL2TP_HEADER_FLAG_OFFSET)) { Ipl2tp_v2_umessage *umessage = (Ipl2tp_v2_umessage *) message; /* Move past the offset */ mindex += 2; /* And the rest of the offset */ mindex += IP_GET_NTOHS(&umessage->len_seq_off.offset); } /* Log the reception */ ipl2tp_v2_dlog_packet(addr, message, "Receiving"); /* Allocate an AVP structure */ if ((avp = (Ipl2tp_avp *)ipcom_malloc(sizeof(*avp))) == IP_NULL) { IPCOM_LOG4(ERR, "%s(%d,%d,%s): Out of memory when decoding message", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tkey.ident, (int)skey.ident, ipl2tp_util_ntoa(addr)); return IPL2TP_STATUS_NOMEM; } /* Clear result */ ipcom_memset(avp, 0, sizeof(*avp)); avp->version = IPL2TP_VERSION_L2TPV2; avp->compulsory = ipl2tp_v2_avp_compulsory; /* Init bitmap */ BITINI(&avp->map, IPL2TP_AVP_MAX); /* Parse non-hidden AVPs in message */ avp->status |= 1L << ipl2tp_avp_parse(avp, (Ip_u8 *)message, mindex, message_length, IP_FALSE, IP_NULL, addr); /* An SCCRQ message */ if (avp->message_type.value == IPL2TP_MSG_SCCRQ && ipl2tp.enable_v3 == IPL2TP_MIB_ENABLED) { /* See if we've seen the required V3 AVP's? */ if (avp->v3_seen) { /* This is a v3 handshake attempt */ ipl2tp_avp_delete(avp); return IPL2TP_STATUS_NEGOTIATION; } } /* Get tunnel ID */ if (tkey.ident == 0) { avp->status |= 1L << ipl2tp_state_tunnel_find(&tunnel, avp, addr, avp->assigned_tunnel_id.value, IPL2TP_ATTR_TRANSPORT_UDP); } else if ((tunnel = ipl2tp_state_tunnel_get(&tkey)) == IP_NULL) { IPCOM_LOG4(WARNING, "%s(%s): Got %s with invalid tunnel ID: %d", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], ipl2tp_util_ntoa(addr), ipl2tp_message_name(avp->message_type.value), (int) tkey.ident); avp->status |= 1L << IPL2TP_STATUS_IGNORE; } /* Abort if invalid tunnel */ if (tunnel == IP_NULL) { ipl2tp_avp_delete(avp); return IPL2TP_STATUS_IGNORE; } /* Do control channel stuff */ tunnel->needzlb = 0; avp->status |= 1L << ipl2tp_trpt_sequence_check(tunnel, IP_GET_NTOHS(&message->nr), IP_GET_NTOHS(&message->ns), IPL2TP_V2_ZLB(message_length), IP_TRUE); /* Get session ID */ if (skey.ident == 0) { if (avp->message_type.value >= IPL2TP_MSG_OCRQ) { avp->status |= 1L << ipl2tp_state_session_find(&session, avp, tunnel, avp->assigned_session_id.value); } } else if ((session = ipl2tp_state_session_get(&skey, IP_NULL)) == IP_NULL && avp->message_type.value != IPL2TP_MSG_ZLB) { IPCOM_LOG5(WARNING, "%s(%d,%s): Got %s with invalid session ID: %d", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(addr), ipl2tp_message_name(avp->message_type.value), (int)skey.ident); avp->status |= 1L << IPL2TP_STATUS_IGNORE; } /* Abort if message is to be ignored */ if (avp->status & (1L << IPL2TP_STATUS_IGNORE)) { IPCOM_LOG5(DEBUG, "%s(%d,%d,%s): Ignoring %s", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, (int)skey.ident, ipl2tp_util_ntoa(addr), ipl2tp_message_name(avp->message_type.value)); if(tunnel->sccrq.avp == avp) { tunnel->sccrq.avp = IP_NULL; } ipl2tp_avp_delete(avp); ipl2tp_state_maybe_zlb(tunnel); return IPL2TP_STATUS_IGNORE; } /* * An SCCRQ must trigger a callback at this early stage * in order to get the authentication method and possibly * the shared secret for this peer. This is required to * decode hidden AVPs. Since the lookup may take some time, * processing stops here and continues when the application * calls ipl2tp_authentication_established(). * */ if (avp->message_type.value == IPL2TP_MSG_SCCRQ) { if (tunnel->state != IPL2TP_TSTATE_IDLE) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_STATE, IPL2TP_RES_StopCCN_STATE_MACHINE, 0, "Got SCCRQ for tunnel in non-idle state"); IPCOM_LOG5(WARNING, "%s(%d,%d,%s): Got SCCRQ for tunnel in %s state", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, (int)skey.ident, ipl2tp_util_ntoa(addr), ipl2tp_state_tstate_name(tunnel->state)); avp->status |= 1L << IPL2TP_STATUS_TEARDOWN; } else if ((tunnel->sccrq.message = ipl2tp_util_memdup(message, (int)message_length, (int)message_length)) == IP_NULL) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_NOMEM, IPL2TP_RES_StopCCN_GENERAL_ERROR, IPL2TP_ERR_RESOURCE_LIMIT, "Out of memory when decoding message"); IPCOM_LOG4(ERR, "%s(%d,%d,%s): Out of memory when decoding message", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, (int)skey.ident, ipl2tp_util_ntoa(addr)); avp->status |= 1L << IPL2TP_STATUS_TEARDOWN; } else { int cb; /* Save AVP */ tunnel->sccrq.avp = avp; tunnel->sccrq.message_index = mindex; tunnel->sccrq.message_length = message_length; tunnel->state = IPL2TP_TSTATE_WAIT_AUTH_CB; tunnel->needzlb = 1; ipl2tp_state_maybe_zlb(tunnel); /* Save possible error code for later teardown */ if (avp->status & (1L << IPL2TP_STATUS_TEARDOWN)) { /* Store */ IPL2TP_TUNNEL_RESULT_SAVE(tunnel); } (void)ipl2tp_state_establish_tunnel_timeout_init(tunnel, 1); /* Set the status message */ IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_OK, IPL2TP_OUTGOING_AUTHENTICATION, 0, "Outgoing tunnel authentication request"); IPL2TP_AVAILABLE(); cb = IPL2TP_SAFE_ICALLBACK(tunnel->attr.scallback, (tunnel->attr.user_data, &tunnel->local.key, &tunnel->last_result)); IPL2TP_OCCUPIED(return IPL2TP_STATUS_OCCUPIED); if (cb != 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_TEARDOWN, IPL2TP_RES_StopCCN_GENERAL_ERROR, IPL2TP_ERR_VENDOR_ERROR_LAC, "Tunnel creation aborted"); IPCOM_LOG4(WARNING, "%s(%d,%d,%s): Tunnel creation aborted", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, (int)skey.ident, ipl2tp_util_ntoa(addr)); avp->status |= 1L << IPL2TP_STATUS_TEARDOWN; } else { /* * That's all for now folks. We now exit and allow the * application to obtain the authentication for this host. */ return IPL2TP_STATUS_OK; } } } /* Parse possible hidden AVPs in message */ if (avp->hidden != 0) { avp->status |= 1L << ipl2tp_avp_parse(avp, (Ip_u8 *)message, mindex, message_length, IP_TRUE, tunnel, addr); } /* Finally handle all accumulated errors */ if (avp->status & (1L << IPL2TP_STATUS_IGNORE)) { IPCOM_LOG5(DEBUG, "%s(%d,%d,%s): Ignoring %s", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, (int)skey.ident, ipl2tp_util_ntoa(addr), ipl2tp_message_name(avp->message_type.value)); if(tunnel->sccrq.avp == avp) { tunnel->sccrq.avp = IP_NULL; } ipl2tp_avp_delete(avp); ipl2tp_state_maybe_zlb(tunnel); return IPL2TP_STATUS_IGNORE; } else if (avp->status & (1L << IPL2TP_STATUS_TEARDOWN)) { IPCOM_LOG5(DEBUG, "%s(%d,%d,%s): Teardown requested when receiving %s", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, (int)skey.ident, ipl2tp_util_ntoa(addr), ipl2tp_message_name(avp->message_type.value)); if(tunnel->sccrq.avp == avp) { tunnel->sccrq.avp = IP_NULL; } ipl2tp_avp_delete(avp); if (session != IP_NULL) { if (session->remote.key.ident != 0) { ipl2tp_message_cdn(session); } ipl2tp_state_session_teardown(session); } else if (tunnel != IP_NULL) { if (tunnel->remote.key.ident != 0) { ipl2tp_message_stopccn(tunnel); } ipl2tp_state_tunnel_teardown(tunnel); } ipl2tp_state_maybe_zlb(tunnel); return IPL2TP_STATUS_TEARDOWN; } IPCOM_LOG5(DEBUG, "%s(%d,%d,%s): Feeding %s to state machine", ipl2tp_versions[IPL2TP_VERSION_L2TPV2], (int)tunnel->local.key.ident, (int)skey.ident, ipl2tp_util_ntoa(addr), ipl2tp_message_name(avp->message_type.value)); /* Feed message to the state machine */ status = ipl2tp_v2_state_machine(tunnel, session, avp); if(tunnel->sccrq.avp == avp) { tunnel->sccrq.avp = IP_NULL; } ipl2tp_avp_delete(avp); ipl2tp_state_maybe_zlb(tunnel); return status; } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* **************************************************************************** * END OF FILE **************************************************************************** */