/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipl2tp_v3_state.c,v $ $Revision: 1.8.12.2 $ * $Source: /home/interpeak/CVSRoot/ipl2tp/src/ipl2tp_v3_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_v3.h" #include "ipl2tp_v3_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_v3_compulsory_avp_void[] = { IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_sccrq[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_ROUTER_ID, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_HOST_NAME, IPL2TP_AVP_PSEUDOWIRE_CAPABILITIES_LIST, IPL2TP_AVP_ASSIGNED_CONTROL_CONNECTION_ID, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_sccrp[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_ROUTER_ID, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_HOST_NAME, IPL2TP_AVP_PSEUDOWIRE_CAPABILITIES_LIST, IPL2TP_AVP_ASSIGNED_CONTROL_CONNECTION_ID, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_scccn[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_stopccn[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_RESULT_CODE, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_hello[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_icrq[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_LOCAL_SESSION_ID, IPL2TP_AVP_REMOTE_SESSION_ID, IPL2TP_AVP_CALL_SERIAL_NUMBER, IPL2TP_AVP_PSEUDOWIRE_TYPE, IPL2TP_AVP_REMOTE_END_ID, IPL2TP_AVP_CIRCUIT_STATUS, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_icrp[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_LOCAL_SESSION_ID, IPL2TP_AVP_REMOTE_SESSION_ID, IPL2TP_AVP_CIRCUIT_STATUS, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_iccn[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_LOCAL_SESSION_ID, IPL2TP_AVP_REMOTE_SESSION_ID, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_ocrq[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_LOCAL_SESSION_ID, IPL2TP_AVP_REMOTE_SESSION_ID, IPL2TP_AVP_CALL_SERIAL_NUMBER, IPL2TP_AVP_PSEUDOWIRE_TYPE, IPL2TP_AVP_REMOTE_END_ID, IPL2TP_AVP_CIRCUIT_STATUS, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_ocrp[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_LOCAL_SESSION_ID, IPL2TP_AVP_REMOTE_SESSION_ID, IPL2TP_AVP_CIRCUIT_STATUS, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_occn[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_LOCAL_SESSION_ID, IPL2TP_AVP_REMOTE_SESSION_ID, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_cdn[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_VISIBLE | IPL2TP_AVP_RESULT_CODE, IPL2TP_AVP_LOCAL_SESSION_ID, IPL2TP_AVP_REMOTE_SESSION_ID, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_wen[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_LOCAL_SESSION_ID, IPL2TP_AVP_REMOTE_SESSION_ID, IPL2TP_AVP_CALL_ERRORS, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 ipl2tp_v3_compulsory_avp_sli[] = { IPL2TP_AVP_VISIBLE | IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_LOCAL_SESSION_ID, IPL2TP_AVP_REMOTE_SESSION_ID, IPL2TP_AVP_END }; IP_STATIC CONST Ip_u8 *ipl2tp_v3_avp_compulsory[] = { ipl2tp_v3_compulsory_avp_void, /* (0) reserved */ ipl2tp_v3_compulsory_avp_sccrq, /* (1) sccrq */ ipl2tp_v3_compulsory_avp_sccrp, /* (2) sccrp */ ipl2tp_v3_compulsory_avp_scccn, /* (3) scccn */ ipl2tp_v3_compulsory_avp_stopccn, /* (4) stopccn */ ipl2tp_v3_compulsory_avp_void, /* (5) reserved */ ipl2tp_v3_compulsory_avp_hello, /* (6) hello */ ipl2tp_v3_compulsory_avp_ocrq, /* (7) ocrq */ ipl2tp_v3_compulsory_avp_ocrp, /* (8) ocrp */ ipl2tp_v3_compulsory_avp_occn, /* (9) occn */ ipl2tp_v3_compulsory_avp_icrq, /* (10) icrq */ ipl2tp_v3_compulsory_avp_icrp, /* (11) icrp */ ipl2tp_v3_compulsory_avp_iccn, /* (12) iccn */ ipl2tp_v3_compulsory_avp_void, /* (13) reserved */ ipl2tp_v3_compulsory_avp_cdn, /* (14) cdn */ ipl2tp_v3_compulsory_avp_wen, /* (15) wen */ ipl2tp_v3_compulsory_avp_sli, /* (16) sli */ ipl2tp_v3_compulsory_avp_void, /* (17) unimplemented */ ipl2tp_v3_compulsory_avp_void, /* (18) unimplemented */ ipl2tp_v3_compulsory_avp_void, /* (19) unimplemented */ ipl2tp_v3_compulsory_avp_void, /* (20) unimplemented */ IP_NULL }; /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipl2tp_v3_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_v3_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. */ /* Host name */ IPL2TP_SAFE_SWAP(avp->host_name, tunnel->common.host_name); /* Vendor name */ IPL2TP_SAFE_SWAP(avp->vendor_name, tunnel->common.vendor_name); /* Authentication method */ if (avp->message_digest.head.ok && avp->message_type.value == IPL2TP_MSG_SCCRQ) { tunnel->attr.authentication = IPL2TP_MIB_AUTH_DIGEST; tunnel->attr.hmac = avp->message_digest.type; } /* Router Identifier */ tunnel->v.v3.router_id = avp->router_id.value; /* Assigned tunnel ID */ if (avp->assigned_control_connection_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_L2TPV3], (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); /* Pseudo wires capabilities */ ipl2tp_util_vector_dup(&tunnel->v.v3.pseudo_wires, &avp->pseudo_wires_list.vec); /* Preferred language */ IPL2TP_SAFE_SWAP(avp->preferred_language, tunnel->v.v3.preferred_language); /* 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_L2TPV3], (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_L2TPV3], (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_L2TPV3], (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_L2TPV3], (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_v3_state_tunnel_initialize *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ipl2tp_status ipl2tp_v3_state_tunnel_initialize(Ipl2tp_tunnel *tunnel, Ipl2tp_avp *avp) { int cb; /* Setup the tunnel */ 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_L2TPV3], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); return IPL2TP_STATUS_ERROR; } /* Send reply */ if (ipl2tp_message_sccrp(tunnel, avp) != IPL2TP_STATUS_OK) return IPL2TP_STATUS_ERROR; /* Final fixup */ IPCOM_LOG3(INFO, "%s(%d,%s): Received good SCCRQ, sent SCCRP", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (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_v3_state_tunnel_finalize *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ipl2tp_status ipl2tp_v3_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_L2TPV3], (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_L2TPV3], (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_v3_state_sequencing_verify *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ip_bool ipl2tp_v3_state_sequencing_verify(Ipl2tp_session *session, Ipl2tp_avp *avp) { /* Check if allowed framing */ if (avp->l2_sublayer.head.ok) { switch (avp->l2_sublayer.value) { case IPL2TP_ATTR_L2_SUBLAYER_NONE: break; case IPL2TP_ATTR_L2_SUBLAYER_DEFAULT: session->attr.l2_sublayer = (Ip_u8)IPL2TP_ATTR_L2_SUBLAYER_DEFAULT; break; default: IPL2TP_TUNNEL_RESULT_SET(session->tunnel, IPL2TP_STATUS_BADL2, IPL2TP_RES_CDN_GENERAL_ERROR, IPL2TP_ERR_OUT_OF_RANGE, "Unknown L2 specific sublayer"); IPCOM_LOG5(WARNING, "%s(%d,%d,%s): Invalid L2 sublayer type %d", ipl2tp_versions[session->tunnel->local.key.version], (int) session->tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&session->tunnel->remote.addr), avp->l2_sublayer.value); return IP_FALSE; } } /* Data sequencing? */ if (avp->data_sequencing.head.ok) { switch (avp->data_sequencing.value) { case IPL2TP_ATTR_DATA_SEQUENCING_NEVER: break; case IPL2TP_ATTR_DATA_SEQUENCING_NONIPONLY: case IPL2TP_ATTR_DATA_SEQUENCING_ALWAYS: if (session->attr.l2_sublayer == 0) { IPL2TP_TUNNEL_RESULT_SET(session->tunnel, IPL2TP_STATUS_BADSEQUENCING, IPL2TP_RES_CDN_GENERAL_ERROR, IPL2TP_ERR_OUT_OF_RANGE, "Sequencing required, but no L2 sublayer specified"); IPCOM_LOG4(WARNING, "%s(%d,%d,%s): Data sequencing required, but no L2 sublayer specified", ipl2tp_versions[session->tunnel->local.key.version], (int) session->tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&session->tunnel->remote.addr)); return IP_FALSE; } /* Store the value */ session->attr.data_sequencing = (Ip_u8) avp->data_sequencing.value; break; default: IPL2TP_TUNNEL_RESULT_SET(session->tunnel, IPL2TP_STATUS_BADSEQUENCING, IPL2TP_RES_CDN_GENERAL_ERROR, IPL2TP_ERR_OUT_OF_RANGE, "Unknown data sequencing type"); IPCOM_LOG5(WARNING, "%s(%d,%d,%s): Invalid data sequencing type %d", ipl2tp_versions[session->tunnel->local.key.version], (int) session->tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&session->tunnel->remote.addr), avp->data_sequencing.value); return IP_FALSE; } } return IP_TRUE; } /* *=========================================================================== * ipl2tp_v3_state_session_sli *=========================================================================== * Description: Update the SLI parameters for this session, and invoke * the callbacks if required. * Parameters: session - The session. * avp - The AVP * Returns: * */ IP_STATIC void ipl2tp_v3_state_session_sli(Ipl2tp_session *session, Ipl2tp_avp *avp) { Ip_u16 mval; if (IP_BIT_ISSET(avp->circuit_status.value, IPL2TP_ATTR_CIRCUIT_STATUS_ACTIVE)) { /* Remote end of circuit is up */ mval = IPL2TP_MIB_UP; } else { /* Remote end of circuit is down */ mval = IPL2TP_MIB_DOWN; } /* The same? */ if (session->v.v3.circuit_status != mval) { session->v.v3.circuit_status = mval; if (IP_BIT_ISFALSE(avp->circuit_status.value, IPL2TP_ATTR_CIRCUIT_STATUS_NEW)) { IPL2TP_AVAILABLE(); IPL2TP_SAFE_CALLBACK(session->attr.slicallback, (session->attr.user_data, &session->local.key, mval, session->attr.circuit_status)); IPL2TP_OCCUPIED(return IPL2TP_STATUS_OCCUPIED); } } } /* *=========================================================================== * ipl2tp_v3_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_v3_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 ((session->remote.key.ident = avp->local_session_id.value) == 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_IDZERO, IPL2TP_RES_CDN_GENERAL_ERROR, IPL2TP_ERR_OUT_OF_RANGE, "Local session ID is 0"); IPCOM_LOG5(WARNING, "%s(%d,%d,%s): Assigned session Id is 0 in %s", ipl2tp_versions[tunnel->local.key.version], (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 || avp->message_type.value == IPL2TP_MSG_ICRQ) { /* Check if allowed bearer */ if (ipl2tp_pw_valid_type(tunnel, avp->pseudo_wire_type.value) == IP_FALSE) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_BADPW, IPL2TP_RES_CDN_PW_TYPE, IPL2TP_ERR_OUT_OF_RANGE, "Invalid pseudo wire type in session"); IPCOM_LOG5(WARNING, "%s(%d,%d,%s): Invalid pseudo wire type %d", ipl2tp_versions[tunnel->local.key.version], (int) tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), avp->pseudo_wire_type.value); return IPL2TP_STATUS_ERROR; } session->attr.pw_type = (Ip_u8) avp->pseudo_wire_type.value; if (ipl2tp_v3_state_sequencing_verify(session, avp) == IP_FALSE) return IPL2TP_STATUS_ERROR; /* Remote circuit status */ ipl2tp_v3_state_session_sli(session,avp); /* Set common values */ session->common.call_serial_number = avp->call_serial_number.value; /* The optionals */ /* Only if they exist */ IPL2TP_SAFE_ASSIGN(avp->tx_connect_speed64, session->attr.tx_connect_speed64); IPL2TP_SAFE_ASSIGN(avp->rx_connect_speed64, session->attr.rx_connect_speed64); /* */ IPL2TP_SAFE_VEC(avp->assigned_cookie, session->remote.cookie); /* Store the remote end ID */ ipl2tp_util_vector_dup(&session->attr.remote_end_id, &avp->remote_end_id.vec); if (avp->message_type.value == IPL2TP_MSG_ICRQ) { IPL2TP_SAFE_ASSIGN(avp->physical_channel_id, session->attr.phys_chan_id); } } else if (avp->message_type.value == IPL2TP_MSG_OCRP || avp->message_type.value == IPL2TP_MSG_ICRP || avp->message_type.value == IPL2TP_MSG_ICCN || avp->message_type.value == IPL2TP_MSG_OCCN) { /* Check if allowed framing */ if (ipl2tp_v3_state_sequencing_verify(session, avp) == IP_FALSE) return IPL2TP_STATUS_ERROR; /* Session SLI */ ipl2tp_v3_state_session_sli(session,avp); /* The optionals */ IPL2TP_SAFE_ASSIGN(avp->tx_connect_speed64, session->attr.tx_connect_speed64); IPL2TP_SAFE_ASSIGN(avp->rx_connect_speed64, session->attr.rx_connect_speed64); /* */ if (avp->message_type.value == IPL2TP_MSG_OCRP || avp->message_type.value == IPL2TP_MSG_ICRP) { IPL2TP_SAFE_ASSIGN(avp->physical_channel_id, session->attr.phys_chan_id); IPL2TP_SAFE_VEC(avp->assigned_cookie, session->remote.cookie); } } return IPL2TP_STATUS_OK; } /* *=========================================================================== * ipl2tp_v3_state_session_callback *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ipl2tp_status ipl2tp_v3_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_L2TPV3], (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_L2TPV3], (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_v3_state_machine_session *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ipl2tp_status ipl2tp_v3_state_machine_session(Ipl2tp_tunnel *tunnel, Ipl2tp_session *session, Ipl2tp_avp *avp) { /* Teardown? */ if (avp->status & (1L << IPL2TP_STATUS_TEARDOWN)) goto stopcdn; /* The session specific functions */ 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; /* Setup the session based on the AVP */ if (ipl2tp_v3_state_session_setup(session, avp) != IPL2TP_STATUS_OK) goto stopcdn; /* Status callback */ session->state = IPL2TP_SSTATE_WAIT_CS_ANSWER; ipl2tp_state_establish_session_timeout_init(session, 1); if (ipl2tp_v3_state_session_callback(session, IPL2TP_INCOMING_REQUEST, "Incoming session request") != IPL2TP_STATUS_OK) goto stopcdn; /* Ready to fly */ tunnel->needzlb = 1; break; case IPL2TP_MSG_ICRP: /* Only valid if we're waiting for the reply */ if (session->state != IPL2TP_SSTATE_WAIT_REPLY) goto errstate; /* Setup the session based on the AVP */ if (ipl2tp_v3_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++; tunnel->statistics.active_sessions++; /* Status callback */ if (ipl2tp_v3_state_session_callback(session, IPL2TP_INCOMING_ESTABLISHED, "Incoming session established") != IPL2TP_STATUS_OK) goto stopcdn; break; case IPL2TP_MSG_ICCN: if (session->state != IPL2TP_SSTATE_WAIT_CONNECT) goto errstate; /* Setup the session based on the AVP */ if (ipl2tp_v3_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_v3_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; /* Setup the session based on the AVP */ if (ipl2tp_v3_state_session_setup(session, avp) != IPL2TP_STATUS_OK) goto stopcdn; /* Status callback */ if (ipl2tp_v3_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; /* Status callback */ if (ipl2tp_v3_state_session_callback(session, IPL2TP_OUTGOING_REPLY, "Outgoing session reply") != IPL2TP_STATUS_OK) goto stopcdn; /* Done */ break; case IPL2TP_MSG_OCRP: /* Only valid if we're waiting for the reply */ if (session->state != IPL2TP_SSTATE_WAIT_REPLY) goto errstate; /* Setup the session based on the AVP */ if (ipl2tp_v3_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; /* Setup the session based on the AVP */ if (ipl2tp_v3_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_v3_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_L2TPV3], (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_WEN: break; case IPL2TP_MSG_SLI: ipl2tp_v3_state_session_sli(session,avp); 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_L2TPV3], (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_v3_state_machine_tunnel *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ipl2tp_status ipl2tp_v3_state_machine_tunnel(Ipl2tp_tunnel *tunnel, Ipl2tp_avp *avp) { /* Teardown? */ if (avp->status & (1L << IPL2TP_STATUS_TEARDOWN)) { IPCOM_LOG4(DEBUG, "%s(%d,%s): Tearing down when receiving %s", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), ipl2tp_message_name(avp->message_type.value)); goto stopccn; } /* */ if (tunnel->slave != IP_NULL) { /* */ Ipl2tp_tunnel *slave = tunnel->slave; /* */ slave->slave = IP_NULL; tunnel->slave = IP_NULL; /* 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 V3 */ ipl2tp_tunnel_delete(slave); } switch (avp->message_type.value) { case IPL2TP_MSG_ZLB: case IPL2TP_MSG_ACK: /* ZLB & ACK */ break; case IPL2TP_MSG_SCCRQ: switch (tunnel->state) { case IPL2TP_TSTATE_WAIT_AUTH_CB: case IPL2TP_TSTATE_IDLE: case IPL2TP_TSTATE_WAIT_CTL_REPLY: /* Remote nonce */ /* 'tis is OK. We've gotten a SCCRQ and we're in IDLE mode */ if (ipl2tp_v3_state_tunnel_setup(tunnel, avp) != IPL2TP_STATUS_OK) goto stopccn; /* Verify that we aint got a TIE-BREAK situation */ if (ipl2tp_state_tie_break_win(tunnel, avp) == 0) { /* Remove new tunnel */ ipl2tp_state_tunnel_teardown(tunnel); break; } /* Initialize the tunnel */ if (ipl2tp_v3_state_tunnel_initialize(tunnel, avp) != IPL2TP_STATUS_OK) goto stopccn; /* We're done */ break; default: /* Not allowed to receive SCCRQ in these states */ goto errstate; } break; case IPL2TP_MSG_SCCRP: /* Only valid in CTL REPLY state */ if (tunnel->state != IPL2TP_TSTATE_WAIT_CTL_REPLY) goto errstate; /* Setup the tunnel */ if (ipl2tp_v3_state_tunnel_setup(tunnel, avp) != IPL2TP_STATUS_OK) goto stopccn; /* Send the SCCCN */ if (ipl2tp_message_scccn(tunnel, avp) != IPL2TP_STATUS_OK) goto stopccn; /* Finalize the tunnel */ if (ipl2tp_v3_state_tunnel_finalize(tunnel, avp, IPL2TP_INCOMING_ESTABLISHED) != IPL2TP_STATUS_OK) goto stopccn; break; case IPL2TP_MSG_SCCCN: /* May only be done in wait ctl conn */ if (tunnel->state != IPL2TP_TSTATE_WAIT_CTL_CONN) goto errstate; /* Finalize the tunnel */ if (ipl2tp_v3_state_tunnel_finalize(tunnel, avp, IPL2TP_OUTGOING_ESTABLISHED) != IPL2TP_STATUS_OK) goto stopccn; tunnel->needzlb = 1; break; case IPL2TP_MSG_StopCCN: /* StopCCN received; just tear the tunnel down */ IPCOM_LOG6(INFO, "%s(%d,%s): Got StopCCN with result: %d, error: %d, message:\'%s\'", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (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 stopccn; /* Do send a ZLB */ tunnel->needzlb = 1; break; default: /* Unknown message type */ 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_L2TPV3], (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)); stopccn: ipl2tp_message_stopccn(tunnel); ipl2tp_state_tunnel_teardown(tunnel); return IPL2TP_STATUS_OK; } /* *=========================================================================== * ipl2tp_v3_state_machine *=========================================================================== * 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_v3_state_machine(Ipl2tp_v3_header *header, Ip_size_t length, Ipl2tp_tunnel *tunnel, Ipl2tp_avp *avp) { Ipl2tp_key skey; Ipl2tp_session *session = IP_NULL; /* Deal with ignored 'status' */ if (avp->status & (1L << IPL2TP_STATUS_IGNORE)) { IPCOM_LOG4(DEBUG, "%s(%d,%s): Ignoring %s", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), ipl2tp_message_name(avp->message_type.value)); return IPL2TP_STATUS_IGNORE; } if (tunnel->attr.authentication == IPL2TP_MIB_AUTH_DIGEST && avp->message_digest.head.ok == IP_FALSE) { IPCOM_LOG3(DEBUG, "%s(%d,%s): Control message lacks authentication", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); return IPL2TP_STATUS_IGNORE; } /* Update in sequence parameters */ ipl2tp_trpt_sequence_check(tunnel, IP_GET_NTOHS(&header->nr), IP_GET_NTOHS(&header->ns), IPL2TP_V3_ZLB(length, avp->message_type.value), IP_TRUE); skey.version = IPL2TP_VERSION_L2TPV3; switch (avp->message_type.value) { case IPL2TP_MSG_ZLB: case IPL2TP_MSG_ACK: case IPL2TP_MSG_SCCRQ: case IPL2TP_MSG_SCCRP: case IPL2TP_MSG_SCCCN: case IPL2TP_MSG_StopCCN: case IPL2TP_MSG_HELLO: return ipl2tp_v3_state_machine_tunnel(tunnel, avp); case IPL2TP_MSG_OCRQ: case IPL2TP_MSG_OCRP: case IPL2TP_MSG_OCCN: case IPL2TP_MSG_ICRQ: case IPL2TP_MSG_ICRP: case IPL2TP_MSG_ICCN: case IPL2TP_MSG_CDN: case IPL2TP_MSG_WEN: case IPL2TP_MSG_SLI: skey.ident = avp->remote_session_id.value; /* Get session ID */ if (skey.ident == 0) { avp->status |= 1L << ipl2tp_state_session_find(&session, avp, tunnel, avp->local_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_L2TPV3], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), ipl2tp_message_name(avp->message_type.value), (int) skey.ident); avp->status |= 1L << IPL2TP_STATUS_IGNORE; } if (session == IP_NULL) { IPCOM_LOG4(ERR, "%s(%d,%s): No session exists when receiving session level call (%s)", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), ipl2tp_message_name(avp->message_type.value)); avp->status |= 1L << IPL2TP_STATUS_IGNORE; } /* Deal with ignored 'status' */ if (avp->status & (1L << IPL2TP_STATUS_IGNORE)) { IPCOM_LOG4(DEBUG, "%s(%d,%s): Ignoring %s", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), ipl2tp_message_name(avp->message_type.value)); return IPL2TP_STATUS_IGNORE; } /* We've gotten a session level control channel * message, and we aint established yet */ if (tunnel->state != IPL2TP_TSTATE_ESTABLISHED) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_EXISTS, IPL2TP_RES_CDN_GENERAL_ERROR, IPL2TP_ERR_NO_CONTROL_CONN, "No control channel exists when receiving session level call"); IPCOM_LOG4(WARNING, "%s(%d,%s): No control channel exists when receiving session level call (%s)", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), ipl2tp_message_name(avp->message_type.value)); avp->status |= 1L << IPL2TP_STATUS_TEARDOWN; } return ipl2tp_v3_state_machine_session(tunnel, session, avp); default: if (avp->message_type.head.mandatory) return ipl2tp_v3_state_machine_tunnel(tunnel, avp); else { #if 0 if (session) { IPCOM_LOG5(WARNING, "%s(%d,%d,%s): Got unknown mandatory control message: %d", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int) tunnel->local.key.ident, (int) session->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), (int)avp->message_type.value); } else { #endif IPCOM_LOG4(WARNING, "%s(%d,%s): Got unknown mandatory control message: %d", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int) tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), (int)avp->message_type.value); #if 0 } #endif } break; } return IPL2TP_STATUS_OK; } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipl2tp_v3_state_machine *=========================================================================== * Description: Handle an incoming IPL2TP message according to state. * Parameters: tunnel - Tunnel descriptor. * Returns: IPL2TP_SUCCESS - Success. * */ IP_GLOBAL Ipl2tp_status ipl2tp_v3_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); /* Must be authenticated? */ if (tunnel->attr.authentication == IPL2TP_MIB_AUTH_DIGEST && tunnel->sccrq.avp->message_digest.head.ok == IP_FALSE) { IPCOM_LOG3(DEBUG, "%s(%d,%s): Control message lacks authentication", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); tunnel->sccrq.avp->status |= 1L << IPL2TP_STATUS_TEARDOWN; } /* 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 final stuff */ return ipl2tp_v3_state_machine_tunnel(tunnel, tunnel->sccrq.avp); } /* *=========================================================================== * ipl2tp_v3_state_control_input *=========================================================================== * Description: Handle an incoming L2TP control packet. * Parameters: header - An L2TP packet. * length - Length of the packet * addr - IP address of sender. * transport - Transport * Returns: IPL2TP_SUCCESS - Success. * */ IP_GLOBAL Ipl2tp_status ipl2tp_v3_state_control_input(Ipl2tp_v3_header *header, Ip_u16 length, union Ip_sockaddr_union *addr, int transport) { Ipl2tp_tunnel *tunnel; Ipl2tp_status status; Ipl2tp_avp *avp; Ip_size_t mindex = sizeof(*header); Ip_size_t message_length = IP_GET_NTOHS(&header->length); Ipl2tp_key tkey; /**/ tkey.version = IPL2TP_VERSION_L2TPV3; tkey.ident = IP_GET_NTOHL(&header->tunnel_id); if ((avp = (Ipl2tp_avp *)ipcom_malloc(sizeof(*avp))) == IP_NULL) { IPCOM_LOG2(ERR, "%s(%s): Out of memory when decoding message", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], ipl2tp_util_ntoa(addr)); return IPL2TP_STATUS_NOMEM; } /* Clear result */ ipcom_memset(avp, 0, sizeof(*avp)); avp->version = IPL2TP_VERSION_L2TPV3; avp->compulsory = ipl2tp_v3_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 *)header, mindex, message_length, IP_FALSE, IP_NULL, addr); /* Log the packet */ ipl2tp_v3_dlog_packet(addr, header, "Receiving"); /* Get tunnel ID */ if (tkey.ident == 0) { avp->status |= 1L << ipl2tp_state_tunnel_find(&tunnel, avp, addr, avp->assigned_control_connection_id.value, transport); } 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_L2TPV3], 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; } tunnel->statistics.in_octets += message_length; tunnel->statistics.in_packets++; /* Do control channel stuff */ tunnel->needzlb = 0; avp->status |= 1L << ipl2tp_trpt_sequence_check(tunnel, IP_GET_NTOHS(&header->nr), IP_GET_NTOHS(&header->ns), IPL2TP_V3_ZLB(length, avp->message_type.value), IP_FALSE); /* Abort if message is to be ignored */ if (avp->status & (1L << IPL2TP_STATUS_IGNORE)) { IPCOM_LOG4(DEBUG, "%s(%d,%s): Ignoring %s", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(addr), ipl2tp_message_name(avp->message_type.value)); 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_LOG4(WARNING, "%s(%d,%s): Got SCCRQ for tunnel in %s state", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int)tunnel->local.key.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(header, (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_LOG3(ERR, "%s(%d,%s): Out of memory when decoding message", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int)tunnel->local.key.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; /* Store the remote nonce */ IPL2TP_SAFE_VEC(avp->authentication_nonce, tunnel->remote.nonce); /* Update in sequence parameters */ avp->status |= 1L << ipl2tp_trpt_sequence_check(tunnel, IP_GET_NTOHS(&header->nr), IP_GET_NTOHS(&header->ns), IP_FALSE, IP_TRUE); 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_LOG3(WARNING, "%s(%d,%s): Tunnel creation aborted", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int)tunnel->local.key.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; } } } else if (avp->message_type.value == IPL2TP_MSG_SCCRP) { if (tunnel->state != IPL2TP_TSTATE_WAIT_CTL_REPLY) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_STATE, IPL2TP_RES_StopCCN_STATE_MACHINE, 0, "Got SCCRP for tunnel in non wait-ctl_reply state"); IPCOM_LOG4(WARNING, "%s(%d,%s): Got SCCRP for tunnel in %s state", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(addr), ipl2tp_state_tstate_name(tunnel->state)); avp->status |= 1L << IPL2TP_STATUS_TEARDOWN; } else { /* Store the remote nonce */ IPL2TP_SAFE_VEC(avp->authentication_nonce, tunnel->remote.nonce); } } /* Parse possible hidden AVPs in message */ if (avp->hidden != 0) { avp->status |= 1L << ipl2tp_avp_parse(avp, (Ip_u8 *)header, mindex, message_length, IP_TRUE, tunnel, addr); } IPCOM_LOG4(DEBUG, "%s(%d,%s): Feeding %s message to state machine", ipl2tp_versions[IPL2TP_VERSION_L2TPV3], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(addr), ipl2tp_message_name(avp->message_type.value)); /* Feed message to the state machine */ status = ipl2tp_v3_state_machine(header, length, tunnel, avp); ipl2tp_avp_delete(avp); ipl2tp_state_maybe_zlb(tunnel); return status; } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* **************************************************************************** * END OF FILE **************************************************************************** */