/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 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 **************************************************************************** */