Files
m241Neelima/components/ip_net2-6.9/ipl2tp/src/ipl2tp_v3_state.c
T
2026-07-30 11:19:44 +05:30

1759 lines
62 KiB
C

/*
******************************************************************************
* 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 <joel.hortelis@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 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 ?
"<none>" : 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 ?
"<none>" : 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
****************************************************************************
*/