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

1740 lines
61 KiB
C

/*
******************************************************************************
* 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 <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_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 ?
"<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_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 ?
"<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 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
****************************************************************************
*/