473 lines
17 KiB
C
473 lines
17 KiB
C
|
|
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipl2tp_v3_pw.c,v $ $Revision: 1.6.12.1 $
|
|
* $Source: /home/interpeak/CVSRoot/ipl2tp/src/ipl2tp_v3_pw.c,v $
|
|
* $Author: jren $
|
|
* $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"
|
|
#include "ipl2tp_v3_pw.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
/* L2 sequence valid flag */
|
|
#define IPL2TP_V3_DS_L2_DEFAULT_SEQ_FLAG 0x40000000
|
|
#define IPL2TP_V3_DS_L2_DEFAULT_SEQ_MASK 0x00ffffff
|
|
#define IPL2TP_V3_DS_L2_DEFAULT_LTE 0x007fffff
|
|
|
|
#define IPL2TP_V3_PW_L2_SEQUENCE_MISS_OUT_OF_WINDOW 0
|
|
#define IPL2TP_V3_PW_L2_SEQUENCE_MISS_DUPLICATE 1
|
|
#define IPL2TP_V3_PW_L2_SEQUENCE_MATCH_IN_WINDOW 2
|
|
#define IPL2TP_V3_PW_L2_SEQUENCE_MATCH_EXPECTED 3
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipl2tp_v3_pw_header
|
|
*===========================================================================
|
|
* Description: Create the pseudowire header, including data sequencing.
|
|
* Parameters: session - The session
|
|
* buf - The buffer
|
|
* is_ip - Is it IP?
|
|
* Returns: The size of the header.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_size_t
|
|
ipl2tp_v3_pw_header(Ipl2tp_session *session,
|
|
Ip_u8 *buf,
|
|
Ip_bool is_ip)
|
|
{
|
|
Ip_size_t res = 0;
|
|
|
|
if (session->tunnel->attr.transport == IPL2TP_ATTR_TRANSPORT_UDP)
|
|
{
|
|
/* No flags set */
|
|
IP_SET_HTONS(&buf[res], IPL2TP_VERSION_L2TPV3);
|
|
res += 2;
|
|
|
|
/* Reserved field should be zero */
|
|
IP_SET_HTONS(&buf[res], 0);
|
|
res += 2;
|
|
}
|
|
|
|
/* Set session identifier */
|
|
IP_SET_HTONL(&buf[res], session->remote.key.ident);
|
|
res += 4;
|
|
|
|
/* If there's a cookie, set it */
|
|
if (session->remote.cookie.size)
|
|
{
|
|
ipcom_memcpy(&buf[res],
|
|
session->remote.cookie.array,
|
|
session->remote.cookie.size);
|
|
res += session->remote.cookie.size;
|
|
}
|
|
|
|
/* Deal with sequencing */
|
|
if (session->attr.data_sequencing != IPL2TP_ATTR_DATA_SEQUENCING_NEVER)
|
|
{
|
|
/* Only know how to deal with sublayer default */
|
|
if (session->attr.l2_sublayer == IPL2TP_ATTR_L2_SUBLAYER_DEFAULT)
|
|
{
|
|
Ip_u32 seq = 0;
|
|
|
|
/* Either always, or non ip */
|
|
if (session->attr.data_sequencing == IPL2TP_ATTR_DATA_SEQUENCING_ALWAYS ||
|
|
is_ip == IP_FALSE)
|
|
{
|
|
seq = session->v.v3.dataseq.l2.dfl.next_seq++;
|
|
|
|
/* Wrap if we go above the 24 bits we have available */
|
|
if (session->v.v3.dataseq.l2.dfl.next_seq > IPL2TP_V3_DS_L2_DEFAULT_SEQ_MASK)
|
|
session->v.v3.dataseq.l2.dfl.next_seq = 0;
|
|
|
|
seq |= IPL2TP_V3_DS_L2_DEFAULT_SEQ_FLAG;
|
|
}
|
|
|
|
IP_SET_HTONL(&buf[res], seq);
|
|
res += 4;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipl2tp_v3_pw_l2_sequence
|
|
*===========================================================================
|
|
* Description: Calculate the 'flattened' difference between
|
|
* the sequence numbers.
|
|
* Parameters: first - First received packet for this sequencing
|
|
* current - The current sequence number
|
|
* last - Last received sequence number
|
|
* window - Window size.
|
|
* Returns: The difference.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipl2tp_v3_pw_l2_sequence(Ip_bool first,
|
|
Ip_u32 current,
|
|
Ip_u32 last,
|
|
Ip_u32 window)
|
|
{
|
|
Ip_u32 top;
|
|
Ip_u32 diff;
|
|
|
|
top = current;
|
|
if (top < last)
|
|
top += window;
|
|
|
|
/* Get the total difference in the 'flattened' seq nr space */
|
|
diff = top - last;
|
|
|
|
IPCOM_LOG5(ERR,
|
|
"window: %u, first = %u, current = %u, last = %u, diff = %u",
|
|
window, first, current, last, diff);
|
|
|
|
/* Wraparound? */
|
|
if (diff > window / 2)
|
|
/* Outside the window, LE */
|
|
return IPL2TP_V3_PW_L2_SEQUENCE_MISS_OUT_OF_WINDOW;
|
|
else if (diff == 0 && first == IP_FALSE)
|
|
/* Equal to previous received sequenced packet */
|
|
return IPL2TP_V3_PW_L2_SEQUENCE_MISS_DUPLICATE;
|
|
else if (diff > 1)
|
|
/* Within the window but out of order */
|
|
return IPL2TP_V3_PW_L2_SEQUENCE_MATCH_IN_WINDOW;
|
|
|
|
/* Perfect frame */
|
|
return IPL2TP_V3_PW_L2_SEQUENCE_MATCH_EXPECTED;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipl2tp_v3_pw_ethernet_input
|
|
*===========================================================================
|
|
* Description: Feed an incoming Ethernet packet to the IPL2TP engine.
|
|
* Parameters: session - The session which the packet belongs to.
|
|
* buffer - The ethernet frame
|
|
* length - Length of ethernet frame.
|
|
* offset - Number of bytes before 'buffer' that may be used.
|
|
* align - Alignment requirement for output buffer.
|
|
* is_ip - Is this an IP packet?
|
|
* Returns: IPL2TP_STATUS_OK if success.
|
|
* IPL2TP_STATUS_BADID if invalid session ID.
|
|
*/
|
|
IP_GLOBAL Ipl2tp_status
|
|
ipl2tp_v3_pw_ethernet_input(Ipl2tp_session *session,
|
|
void *buffer,
|
|
int length,
|
|
int offset,
|
|
int align,
|
|
Ip_bool is_ip)
|
|
{
|
|
Ipl2tp_tunnel *tunnel = session->tunnel;
|
|
struct Ip_iovec vec[2];
|
|
Ip_u8 hdr[4 + 4 + 4 + 8]; /* Max transport header */
|
|
Ip_size_t hsize;
|
|
Ip_u8 *frame = buffer;
|
|
int iovlen = 0;
|
|
|
|
/**/
|
|
IPL2TP_UNUSED(align);
|
|
hsize = ipl2tp_v3_pw_header(session, hdr, is_ip);
|
|
|
|
/* Can we just append the header? */
|
|
if ((Ip_size_t)offset >= hsize)
|
|
{
|
|
frame -= hsize;
|
|
length += hsize;
|
|
|
|
/* Copy in the header */
|
|
ipcom_memcpy(frame, hdr, hsize);
|
|
|
|
/* Store it in the io vector */
|
|
vec[iovlen].iov_base = frame;
|
|
vec[iovlen].iov_len = length;
|
|
iovlen++;
|
|
}
|
|
else
|
|
{
|
|
/* Vector them up */
|
|
vec[iovlen].iov_base = hdr;
|
|
vec[iovlen].iov_len = hsize;
|
|
iovlen++;
|
|
|
|
vec[iovlen].iov_base = frame;
|
|
vec[iovlen].iov_len = length;
|
|
iovlen++;
|
|
}
|
|
|
|
/* And transmit */
|
|
ipl2tp_trpt_data_output(tunnel,
|
|
vec,
|
|
iovlen);
|
|
|
|
return IPL2TP_STATUS_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipl2tp_v3_pw_session_data
|
|
*===========================================================================
|
|
* Description: Feed the incoming data to the proper callback.
|
|
* Parameters: buffer - Original buffer
|
|
* session - The session in question.
|
|
* data - The data we're looking at
|
|
* size - Size of the data
|
|
* Returns: The status.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipl2tp_status
|
|
ipl2tp_v3_pw_session_data(void *buffer,
|
|
Ipl2tp_session *session,
|
|
Ip_u8 *data,
|
|
Ip_size_t size)
|
|
{
|
|
/* Data sequencing? */
|
|
if (session->attr.data_sequencing)
|
|
{
|
|
/* Which sublayer are we using? */
|
|
if (session->attr.l2_sublayer == IPL2TP_ATTR_L2_SUBLAYER_DEFAULT)
|
|
{
|
|
Ip_u32 ds = IP_GET_NTOHL(data);
|
|
|
|
/* Remove the sublayer */
|
|
data += 4;
|
|
size -= 4;
|
|
|
|
/* Is there a valid sequence number*/
|
|
if (IP_BIT_ISSET(ds, IPL2TP_V3_DS_L2_DEFAULT_SEQ_FLAG))
|
|
{
|
|
int type;
|
|
|
|
/* Remove the flags */
|
|
ds &= IPL2TP_V3_DS_L2_DEFAULT_SEQ_MASK;
|
|
|
|
/* The number of sequence numbers we have 'above' our
|
|
* last received one */
|
|
type = ipl2tp_v3_pw_l2_sequence(session->v.v3.dataseq.received == 0,
|
|
ds,
|
|
session->v.v3.dataseq.l2.dfl.match_last,
|
|
IPL2TP_V3_DS_L2_DEFAULT_SEQ_MASK);
|
|
|
|
/* Duplicate; just abort */
|
|
if (type == IPL2TP_V3_PW_L2_SEQUENCE_MISS_DUPLICATE)
|
|
{
|
|
if (session->tunnel != IP_NULL)
|
|
IPCOM_LOG4(DEBUG,
|
|
"%s(%d,%d,%s): Duplicate session level data frame.",
|
|
ipl2tp_versions[IPL2TP_VERSION_L2TPV3],
|
|
(int)session->tunnel->local.key.ident,
|
|
(int)session->local.key.ident,
|
|
ipl2tp_util_ntoa(&session->tunnel->remote.addr));
|
|
return IPL2TP_STATUS_IGNORE;
|
|
}
|
|
|
|
/* We're out of window */
|
|
if (type == IPL2TP_V3_PW_L2_SEQUENCE_MISS_OUT_OF_WINDOW)
|
|
{
|
|
if (session->tunnel != IP_NULL)
|
|
IPCOM_LOG4(DEBUG,
|
|
"%s(%d,%d,%s): Out of window session level data frame.",
|
|
ipl2tp_versions[IPL2TP_VERSION_L2TPV3],
|
|
(int)session->tunnel->local.key.ident,
|
|
(int)session->local.key.ident,
|
|
ipl2tp_util_ntoa(&session->tunnel->remote.addr));
|
|
|
|
/* Deal with out of order */
|
|
if (session->v.v3.dataseq.l2.dfl.miss_in_sequence == 0)
|
|
{
|
|
session->v.v3.dataseq.l2.dfl.miss_last = ds;
|
|
session->v.v3.dataseq.l2.dfl.miss_in_sequence = 1;
|
|
return IPL2TP_STATUS_IGNORE;
|
|
}
|
|
else
|
|
{
|
|
int oorder = ipl2tp_v3_pw_l2_sequence(IP_FALSE,
|
|
ds,
|
|
session->v.v3.dataseq.l2.dfl.miss_last,
|
|
IPL2TP_V3_DS_L2_DEFAULT_SEQ_MASK);
|
|
|
|
/* If its not in order, reset out of order counter */
|
|
if (oorder != IPL2TP_V3_PW_L2_SEQUENCE_MATCH_EXPECTED)
|
|
{
|
|
session->v.v3.dataseq.l2.dfl.miss_last = ds;
|
|
session->v.v3.dataseq.l2.dfl.miss_in_sequence = 1;
|
|
return IPL2TP_STATUS_IGNORE;
|
|
}
|
|
|
|
/* Reset if we have this many L2 sequence number in line misses */
|
|
if (++session->v.v3.dataseq.l2.dfl.miss_in_sequence < session->attr.l2_out_of_order_reset)
|
|
{
|
|
session->v.v3.dataseq.l2.dfl.miss_last = ds;
|
|
return IPL2TP_STATUS_IGNORE;
|
|
}
|
|
|
|
if (session->tunnel != IP_NULL)
|
|
IPCOM_LOG4(DEBUG,
|
|
"%s(%d,%d,%s): Out of window session level data frame -- too many misses, resetting sequence.",
|
|
ipl2tp_versions[IPL2TP_VERSION_L2TPV3],
|
|
(int)session->tunnel->local.key.ident,
|
|
(int)session->local.key.ident,
|
|
ipl2tp_util_ntoa(&session->tunnel->remote.addr));
|
|
|
|
}
|
|
}
|
|
|
|
/* Either EXPECTED or IN_WINDOW; just accept both */
|
|
if (type == IPL2TP_V3_PW_L2_SEQUENCE_MATCH_IN_WINDOW)
|
|
{
|
|
if (session->tunnel != IP_NULL)
|
|
IPCOM_LOG4(DEBUG,
|
|
"%s(%d,%d,%s): In window session level data frame",
|
|
ipl2tp_versions[IPL2TP_VERSION_L2TPV3],
|
|
(int)session->tunnel->local.key.ident,
|
|
(int)session->local.key.ident,
|
|
ipl2tp_util_ntoa(&session->tunnel->remote.addr));
|
|
}
|
|
|
|
if (type == IPL2TP_V3_PW_L2_SEQUENCE_MATCH_EXPECTED)
|
|
{
|
|
if (session->tunnel != IP_NULL)
|
|
IPCOM_LOG4(DEBUG,
|
|
"%s(%d,%d,%s): In sequence session level data frame",
|
|
ipl2tp_versions[IPL2TP_VERSION_L2TPV3],
|
|
(int)session->tunnel->local.key.ident,
|
|
(int)session->local.key.ident,
|
|
ipl2tp_util_ntoa(&session->tunnel->remote.addr));
|
|
}
|
|
|
|
/* Feed this message; it's a match */
|
|
session->v.v3.dataseq.l2.dfl.match_last = ds;
|
|
session->v.v3.dataseq.l2.dfl.miss_in_sequence = 0;
|
|
session->v.v3.dataseq.received++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (session->tunnel != IP_NULL)
|
|
IPCOM_LOG4(DEBUG,
|
|
"%s(%d,%d,%s): Session data received",
|
|
ipl2tp_versions[IPL2TP_VERSION_L2TPV3],
|
|
(int)session->tunnel->local.key.ident,
|
|
(int)session->local.key.ident,
|
|
ipl2tp_util_ntoa(&session->tunnel->remote.addr));
|
|
|
|
if (session->attr.circuit_status != IPL2TP_MIB_UP ||
|
|
session->v.v3.circuit_status != IPL2TP_MIB_UP)
|
|
{
|
|
if (session->tunnel != IP_NULL)
|
|
IPCOM_LOG4(INFO,
|
|
"%s(%d,%d,%s): Session circuit is not currently up, frame discarded",
|
|
ipl2tp_versions[IPL2TP_VERSION_L2TPV3],
|
|
(int)session->tunnel->local.key.ident,
|
|
(int)session->local.key.ident,
|
|
ipl2tp_util_ntoa(&session->tunnel->remote.addr));
|
|
return IPL2TP_STATUS_CIRCUIT_DOWN;
|
|
}
|
|
|
|
/* Send to user interface */
|
|
IPL2TP_SAFE_CALLBACK(session->attr.dcallback,
|
|
(session->attr.user_data,
|
|
&session->local.key,
|
|
buffer,
|
|
data,
|
|
(Ip_u16)size));
|
|
|
|
/* Do process */
|
|
return IPL2TP_STATUS_OK;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|