1827 lines
65 KiB
C
1827 lines
65 KiB
C
/* ipmacsec.c - MACsec implementation */
|
|
|
|
/*
|
|
* Copyright (c) 2010 Wind River Systems, Inc.
|
|
*
|
|
* The right to copy, distribute, modify, or otherwise make use
|
|
* of this software may be licensed only pursuant to the terms
|
|
* of an applicable Wind River license agreement.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
modification history
|
|
--------------------
|
|
01a,08feb10,mry Created
|
|
*/
|
|
|
|
/* DESCRIPTION:
|
|
* ===========
|
|
* Implements MACsec.
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/* Implementation of the MACsec Controlled/Uncontrolled Ports and the
|
|
* Secure Frame Generation and Verification.
|
|
* Note that comments of the format x(.y)(.z)(.w) indicate compliance to the
|
|
* corresponding section of the 802.1AE spec. e.g. * 10.5.3 * means
|
|
* the corresponding line(s) of code comply to section 10.5.3 of the 802.1AE spec.
|
|
* Note that comments of the format X.y(www-z) indicate compliance to the
|
|
* corresponding PICS Proforma section and associated item of the 802.1AE spec.
|
|
* e.g. * A.8(GEN-5) * means the corresponding line(s) of code comply to item
|
|
* GEN-5 in PICS Proforma section A.8 of the 802.1AE spec.
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_type.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_sysvar.h>
|
|
#include <ipcom_errno.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_os.h>
|
|
#include <ipcom_ipd.h>
|
|
#include <ipcom_os.h>
|
|
|
|
#include <ipnet_eth.h>
|
|
#include <ipcom_mib2.h>
|
|
#include "ipnet_h.h"
|
|
|
|
#include "ipmacsec_debug.h"
|
|
#include "ipmacsec_sysvar.h"
|
|
#include "ipmacsec_crypto_if.h"
|
|
#include "ipmacsec_secy_mgmt.h"
|
|
#include "ipmacsec_h.h"
|
|
#include "ipmacsec.h"
|
|
|
|
#ifdef IPBRIDGE
|
|
#include "ipbridge.h"
|
|
#endif
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
#include "ipmacsec_config.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
IPMACSEC_PRIVATE int ipmacsec_ioctl(Ipnet_netif *netif, Ip_u32 request, void *data);
|
|
IPMACSEC_PRIVATE int ipmacsec_secy_update_sa_nextPN(Ipnet_secy *secy, Ipmacsec_secy_sa *sa, Ip_u32 nextPN);
|
|
IPMACSEC_PRIVATE int ipmacsec_secy_update_rx_sa_PNs(Ipnet_secy *secy, Ipmacsec_secy_sa *sa, Ip_u32 rxPN);
|
|
|
|
IPMACSEC_PRIVATE int ipmacsec_secy_ucp_init(Ipnet_secy *secy);
|
|
IPMACSEC_PRIVATE int ipmacsec_secy_cp_init(Ipnet_secy *secy);
|
|
|
|
#if defined(IPMACSEC_UNIT_TEST) && defined (IPMACSEC_UNIT_TEST_INLINE_HARDCODED_CONFIG)
|
|
IPMACSEC_PRIVATE int ipmacsec_secy_unittest_setup(Ipnet_secy *secy);
|
|
IPMACSEC_PRIVATE int ipmacsec_secy_unittest_destroy(Ipnet_secy *secy);
|
|
#endif
|
|
|
|
IP_GLOBAL Ipnet_socket * ipnet_sock_packet_lookup(Ip_u16 vr, int frame_type,
|
|
IP_CONST void *unused1, Ip_u16 unused2,
|
|
IP_CONST void *unused3, Ip_u16 unused4);
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
IP_PUBLIC Ip_err ipmacsec_create(void);
|
|
IP_PUBLIC Ip_err ipmacsec_start(void);
|
|
IP_PUBLIC const char *ipmacsec_version(void);
|
|
|
|
IP_GLOBAL int ipmacsec_ucp_input(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt);
|
|
|
|
IP_STATIC int ipmacsec_secy_ctrl_init(Ipnet_secy *secy);
|
|
IP_STATIC int ipmacsec_secy_ctrl_config(Ipnet_secy *secy);
|
|
IP_STATIC int ipmacsec_input(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt);
|
|
IP_STATIC int ipmacsec_cp_input(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt);
|
|
IP_STATIC int ipmacsec_secure_frame_verify(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt);
|
|
IP_STATIC int ipmacsec_output(struct Ipnet_netif_struct *netif, IP_CONST struct Ip_sockaddr_ll *to, Ipcom_pkt *pkt);
|
|
IP_STATIC int ipmacsec_ucp_output(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt);
|
|
IP_STATIC int ipmacsec_cp_output(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt);
|
|
IP_STATIC int ipmacsec_secure_frame_generate(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt);
|
|
/*
|
|
*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_create
|
|
*===========================================================================
|
|
* Description: MACsec ipd product create function.
|
|
* Parameters: None
|
|
* Returns: IPCOM_SUCCESS
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipmacsec_create(void) {
|
|
IPCOM_LOG0(DEBUG, "Successfully initialized.");
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_start
|
|
*===========================================================================
|
|
* Description: MACsec ipd product start function.
|
|
* Parameters: None
|
|
* Returns: IPCOM_SUCCESS
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipmacsec_start(void) {
|
|
IPCOM_LOG0(DEBUG, "Successfully started.");
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_version
|
|
*===========================================================================
|
|
* Description: MACsec ipd product version query function.
|
|
* Parameters: None
|
|
* Returns: String describing the current version of the MACsec software along with the copyright string
|
|
*
|
|
*/
|
|
IP_PUBLIC const char *
|
|
ipmacsec_version(void) {
|
|
return "@(#) IPMACSEC $Name: VXWORKS_ITER40_2019102207 $ - INTERPEAK_COPYRIGHT_STRING";
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_buf_alloc
|
|
*===========================================================================
|
|
* Description: See "ipmacsec.h"
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_u8 *
|
|
ipmacsec_buf_alloc(Ipmacsec_buffer *buf, Ip_u32 size) {
|
|
ip_assert(buf);
|
|
ip_assert(!buf->ptr);
|
|
if (buf->ptr)
|
|
return IP_NULL;
|
|
buf->ptr = ipcom_calloc(1, size);
|
|
buf->alloc_size = (buf->ptr ? size : 0);
|
|
buf->size = 0;
|
|
buf->truncated = IP_FALSE;
|
|
buf->donot_free = IP_FALSE;
|
|
return buf->ptr;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_buf_set
|
|
*===========================================================================
|
|
* Description: See "ipmacsec.h"
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC Ip_u8 *
|
|
ipmacsec_buf_set(Ipmacsec_buffer *buf, Ip_u8 *ptr, Ip_u32 size) {
|
|
ip_assert(buf);
|
|
ip_assert(!buf->ptr);
|
|
if (buf->ptr)
|
|
return IP_NULL;
|
|
buf->ptr = ptr;
|
|
buf->alloc_size = (buf->ptr ? size : 0);
|
|
buf->size = 0;
|
|
buf->truncated = IP_FALSE;
|
|
buf->donot_free = IP_TRUE;
|
|
return buf->ptr;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_buf_free
|
|
*===========================================================================
|
|
* Description: See "ipmacsec.h"
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC int ipmacsec_buf_free(Ipmacsec_buffer *buf) {
|
|
ip_assert(buf);
|
|
if (buf->ptr && !buf->donot_free) {
|
|
ipcom_free(buf->ptr);
|
|
}
|
|
ipcom_memset(buf, 0, sizeof(Ipmacsec_buffer));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_buf_wipe
|
|
*===========================================================================
|
|
* Description: See "ipmacsec.h"
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipmacsec_buf_wipe(Ipmacsec_buffer *buf) { \
|
|
ip_assert(buf);
|
|
if (buf->ptr)
|
|
ipcom_memset(buf->ptr, 0, buf->alloc_size);
|
|
buf->size = 0;
|
|
buf->truncated = IP_FALSE;
|
|
return buf->alloc_size;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_buf_append
|
|
*===========================================================================
|
|
* Description: See "ipmacsec.h"
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipmacsec_buf_append(Ipmacsec_buffer *buf, Ip_u8 *ptr, Ip_u32 len) {
|
|
Ip_u32 remlen,applen;
|
|
|
|
ip_assert(buf);
|
|
ip_assert(buf->ptr);
|
|
ip_assert(buf->alloc_size>=buf->size);
|
|
|
|
remlen = buf->alloc_size-buf->size;
|
|
applen = IP_MIN(len, remlen);
|
|
if (ptr)
|
|
ipcom_memcpy(buf->ptr+buf->size, (Ip_u8 *)ptr, applen);
|
|
buf->size += applen;
|
|
buf->truncated = applen < len;
|
|
return (remlen-len);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_buf_ptr
|
|
*===========================================================================
|
|
* Description: See "ipmacsec.h"
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_PUBLIC Ip_u8 *ipmacsec_buf_ptr(Ipmacsec_buffer *buf, Ip_u32 offset, Ip_u32 *remlen) {
|
|
ip_assert(buf);
|
|
|
|
if (!remlen)
|
|
return IP_NULL;
|
|
*remlen = 0;
|
|
if (!IPMACSEC_BUF_VERIFY(buf)) {
|
|
return IP_NULL;
|
|
}
|
|
if (offset >= buf->size) {
|
|
return IP_NULL;
|
|
}
|
|
*remlen = buf->size - offset;
|
|
return buf->ptr+offset;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_secy_init
|
|
*===========================================================================
|
|
* Description: Initialize the SecY instance on an Ethernet Interface
|
|
* (Called before an interface name is set).
|
|
* Parameters: netif - Interface netif struct pointer
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmacsec_secy_init(struct Ipnet_netif_struct *netif) {
|
|
Ipnet_secy *secy = IP_NULL;
|
|
|
|
ip_assert(netif);
|
|
|
|
if (netif->ipcom.type != IP_IFT_ETHER)
|
|
return -1;
|
|
|
|
if (!(secy = netif->secy)) {
|
|
secy = netif->secy = ipcom_malloc(sizeof(Ipnet_secy));
|
|
if (!secy) {
|
|
return -1;
|
|
}
|
|
}
|
|
ipcom_memset(secy, 0, sizeof(Ipnet_secy));
|
|
if (ipmacsec_secy_keydb_init(secy) != 0) {
|
|
ipcom_free(secy);
|
|
netif->secy = IP_NULL;
|
|
return -1;
|
|
}
|
|
secy->netif = netif;
|
|
secy->input = ipmacsec_input;
|
|
secy->output = ipmacsec_output;
|
|
secy->ioctl = ipmacsec_ioctl;
|
|
ipcom_memset(&netif->controlled_port, 0, sizeof(netif->controlled_port));
|
|
netif->controlled_port = &secy->controlled_port;
|
|
netif->controlled_port->update_status = ipmacsec_secy_cp_update_status_ext;
|
|
netif->controlled_port->update_stats = ipmacsec_secy_cp_update_statistics;
|
|
ipcom_memset(&netif->uncontrolled_port, 0, sizeof(netif->uncontrolled_port));
|
|
netif->uncontrolled_port = &secy->uncontrolled_port;
|
|
netif->uncontrolled_port->update_status = ipmacsec_secy_ucp_update_status_ext;
|
|
netif->uncontrolled_port->update_stats = ipmacsec_secy_ucp_update_statistics;
|
|
IP_BIT_SET(netif->ipcom.drv_capabilities, IPCOM_IF_DRV_CAP_MACSEC_SECY);
|
|
ipcom_memset(secy->config.sci, 0xFF, sizeof(secy->config.sci)); /* Initialize to Invalid value (9.9) */
|
|
secy->config.cipher_suite_index = IPMACSEC_DEFAULT_CIPHER_SUITE;
|
|
secy->config.confidentialityOffset = 0;
|
|
secy->config.maxRxSCs = IPMACSEC_MAX_RX_SC;
|
|
secy->config.maxRxKeys = IPMACSEC_MAX_SA;
|
|
secy->config.maxTxKeys = IPMACSEC_MAX_SA;
|
|
secy->config.pn_threshold = IPMACSEC_SECY_DEFAULT_PN_THRESHOLD;
|
|
#ifdef IPMACSEC_DEBUG
|
|
secy->config.info_notify = IP_TRUE; /* Off by default unless a debug image. Can be enabled/disabled via LMI */
|
|
#endif
|
|
|
|
(void)ipmacsec_secy_ctrl_init(secy);
|
|
#ifdef IPMACSEC_DEBUG_SECY_INIT
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SECY_INIT: Initialized SecY init on interface %s", netif->ipcom.name);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_secy_config
|
|
*===========================================================================
|
|
* Description: Configure the SecY instance on the specified interface.
|
|
* Called when the interface is attached
|
|
* (at this time the interface name is known).
|
|
* Parameters: netif - Interface netif struct pointer
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmacsec_secy_config(struct Ipnet_netif_struct *netif) {
|
|
Ip_bool secy_en_flag;
|
|
char ifs[256];
|
|
Ip_size_t len;
|
|
char *token;
|
|
Ip_u32 namelen;
|
|
|
|
ip_assert(netif);
|
|
if (!netif->secy) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_CFG
|
|
IPCOM_LOG1(ERR, "IPMACSEC-SECY_CFG: SecY not init on interface %s", netif->ipcom.name);
|
|
#endif
|
|
return -1;
|
|
}
|
|
(void)ipmacsec_secy_ctrl_config(netif->secy);
|
|
|
|
/* Check the Global enable flag. If off, nothing further to be done */
|
|
secy_en_flag = ipcom_sysvar_get_as_int(IPMACSEC_SYSVAR_SECY_ENABLE, 0);
|
|
if (!secy_en_flag) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_CFG
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SECY_CFG: SecY disabled for interface %s (global disable)", netif->ipcom.name);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* If the global enable flag is on, then we need to see if this interface is listed as one that
|
|
* should be automatically enabled (or if "all" is specified).
|
|
*/
|
|
len = sizeof(ifs);
|
|
if (!ipcom_sysvar_get(IPMACSEC_SYSVAR_SECY_INTERFACES, ifs, &len)) {
|
|
IPCOM_LOG1(CRIT, "IPMACSEC-SECY_CFG: Failed to get %s sysvar", IPMACSEC_SYSVAR_SECY_INTERFACES);
|
|
#ifdef IPMACSEC_DEBUG_SECY_CFG
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SECY_CFG: SecY disabled for interface %s (list not found)", netif->ipcom.name);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
if (ipcom_strcmp(ifs, "all") == 0) {
|
|
netif->secy->ctrl.enabled = IP_TRUE;
|
|
} else {
|
|
namelen = (Ip_u32)ipcom_strlen(netif->ipcom.name);
|
|
token = ipcom_strtok(ifs, ",");
|
|
while (token) {
|
|
if ((ipcom_strncmp(token, netif->ipcom.name, namelen) == 0) &&
|
|
((token[namelen] == ',') || (token[namelen] == '\0'))) {
|
|
netif->secy->ctrl.enabled = IP_TRUE;
|
|
break;
|
|
}
|
|
token = ipcom_strtok(IP_NULL, ",");
|
|
}
|
|
}
|
|
|
|
if (!netif->secy->ctrl.enabled) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_CFG
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SECY_CFG: SecY disabled for interface %s (not in list)", netif->ipcom.name);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
return ipmacsec_secy_enable(netif->secy);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_secy_delete
|
|
*===========================================================================
|
|
* Description: Delete the SecY instance on the specified interface.
|
|
* Parameters: netif - Interface netif struct pointer
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipmacsec_secy_delete(struct Ipnet_netif_struct *netif) {
|
|
ip_assert(netif);
|
|
|
|
if (netif->secy) {
|
|
(void)ipmacsec_secy_delete_tx_sc(netif->secy);
|
|
(void)ipmacsec_secy_delete_rx_sc_all(netif->secy);
|
|
(void)ipmacsec_secy_keydb_destroy(netif->secy);
|
|
|
|
#ifdef IPMACSEC_DEBUG_SECY_INIT
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SECY_INIT: Deleting SecY on interface %s", netif->ipcom.name);
|
|
#endif
|
|
ipcom_free(netif->secy);
|
|
netif->secy = IP_NULL;
|
|
netif->controlled_port = IP_NULL;
|
|
netif->uncontrolled_port = IP_NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PRIVATE FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_secy_enable
|
|
*===========================================================================
|
|
* Description: See "ipmacsec_h.h"
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IPMACSEC_PRIVATE int
|
|
ipmacsec_secy_enable(Ipnet_secy *secy) {
|
|
|
|
ip_assert(secy);
|
|
|
|
if (secy->ctrl.enabled) {
|
|
IPCOM_LOG1(WARNING, "IPMACSEC-EXEC: SecY already enabled for interface %s ", secy->netif->ipcom.name);
|
|
return -1;
|
|
}
|
|
|
|
(void)ipmacsec_secy_ucp_update_status(secy);
|
|
(void)ipmacsec_secy_cp_update_status(secy, IPMACSEC_SECY_MAC_OPER_DEFAULT);
|
|
|
|
if (ipmacsec_secy_create_tx_sc(secy) == IP_NULL) {
|
|
goto fail_enable;
|
|
}
|
|
#if defined(IPMACSEC_UNIT_TEST) && defined (IPMACSEC_UNIT_TEST_INLINE_HARDCODED_CONFIG)
|
|
if (ipmacsec_secy_unittest_setup(secy) != 0) {
|
|
IPCOM_LOG1(ERR, "IPMACSEC-EXEC: Failed SecY unit test setup for interface %s", secy->netif->ipcom.name);
|
|
goto fail_enable;
|
|
}
|
|
#endif
|
|
/* Update the MTU to take into account the octets required for MACsec header and trailer */
|
|
secy->netif->ipcom.mtu -= IPMACSEC_RESERVED_OCTETS;
|
|
#ifdef IPBRIDGE
|
|
(void)ipbridge_mtu_change_notify(secy->netif);
|
|
#endif
|
|
secy->ctrl.enabled = IP_TRUE;
|
|
IPMACSEC_SECY_INFO_NOTIFY(secy, IPMACSEC_INFO_SECY_ENABLED);
|
|
IPCOM_LOG1(INFO, "IPMACSEC-EXEC: SecY enabled for interface %s", secy->netif->ipcom.name);
|
|
return 0;
|
|
|
|
fail_enable:
|
|
IPCOM_LOG1(INFO, "IPMACSEC-EXEC: Failed to enable SecY for interface %s", secy->netif->ipcom.name);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_secy_disable
|
|
*===========================================================================
|
|
* Description: See "ipmacsec_h.h"
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IPMACSEC_PRIVATE int
|
|
ipmacsec_secy_disable(Ipnet_secy *secy) {
|
|
ip_assert(secy);
|
|
|
|
#if defined(IPMACSEC_UNIT_TEST) && defined (IPMACSEC_UNIT_TEST_INLINE_HARDCODED_CONFIG)
|
|
(void)ipmacsec_secy_unittest_destroy(secy);
|
|
#endif
|
|
(void)ipmacsec_secy_delete_tx_sc(secy);
|
|
(void)ipmacsec_secy_delete_rx_sc_all(secy);
|
|
(void)ipmacsec_secy_delete_all_keys(secy, IP_TRUE);
|
|
|
|
if (secy->ctrl.enabled) {
|
|
secy->netif->ipcom.mtu += IPMACSEC_RESERVED_OCTETS;
|
|
#ifdef IPBRIDGE
|
|
(void)ipbridge_mtu_change_notify(secy->netif);
|
|
#endif
|
|
}
|
|
secy->ctrl.enabled = IP_FALSE;
|
|
IPMACSEC_SECY_INFO_NOTIFY(secy, IPMACSEC_INFO_SECY_DISABLED);
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-EXEC: SecY disabled for interface %s", secy->netif->ipcom.name);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_dump_buffer
|
|
*===========================================================================
|
|
* Description: Display the contents of a buffer to stdout
|
|
* Parameters: desc - A description of the buffer to display
|
|
* buf - buffer pointer
|
|
* len - buffer length (in octets)
|
|
* Returns: None
|
|
*
|
|
*/
|
|
#define IPMACSEC_DUMP_BUFFER_OCTETS_PER_LINE 8
|
|
IPMACSEC_PRIVATE void
|
|
ipmacsec_dump_buffer(IP_CONST char *desc, IP_CONST Ip_u8* buf, IP_CONST Ip_u32 len) {
|
|
char str[3*IPMACSEC_DUMP_BUFFER_OCTETS_PER_LINE+1];
|
|
Ip_u32 remlen = len, count = 0;
|
|
IP_CONST char *description;
|
|
Ip_u32 i;
|
|
|
|
description = (desc ? desc : "buffer contents");
|
|
IPCOM_LOG2(DEBUG2, "\n %s: len = %u", description, len);
|
|
if (!buf || !len) {
|
|
IPCOM_LOG1(DEBUG2, "\n %s: EMPTY", description);
|
|
return;
|
|
}
|
|
while (remlen) {
|
|
ipcom_memset(str, 0, sizeof(str));
|
|
for(i=0; i<IP_MIN(remlen, IPMACSEC_DUMP_BUFFER_OCTETS_PER_LINE); i++) {
|
|
ipcom_sprintf(&str[3*i], "%02x ", (unsigned char)buf[i+count*IPMACSEC_DUMP_BUFFER_OCTETS_PER_LINE]);
|
|
}
|
|
IPCOM_LOG3(DEBUG2, "\n %s%s: %s", description, (count ? " (cntd)" : " "), str);
|
|
if (remlen < IPMACSEC_DUMP_BUFFER_OCTETS_PER_LINE)
|
|
remlen = 0;
|
|
else
|
|
remlen -= IPMACSEC_DUMP_BUFFER_OCTETS_PER_LINE;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_dump_packet
|
|
*===========================================================================
|
|
* Description: Display the contents of a MACsec frame.
|
|
* Parameters: desc - A description of the frame to display
|
|
* pkt - Pakcet pointer
|
|
* Returns: None
|
|
*
|
|
*/
|
|
IPMACSEC_PRIVATE void
|
|
ipmacsec_dump_packet(char *desc, Ipcom_pkt *pkt) {
|
|
Ipnet_pkt_macsec *macsec;
|
|
Ip_u32 len, msdu_len;
|
|
|
|
ip_assert(pkt);
|
|
|
|
len = pkt->end-pkt->start;
|
|
if (len == 0) {
|
|
IPCOM_LOG1(DEBUG2, "\n %s: EMPTY PKT.", desc);
|
|
return;
|
|
}
|
|
IPCOM_LOG3(DEBUG2,"\n>>>>PKT DUMP START - %s: len = %u (pkt->start = %u)", desc, len, pkt->start);
|
|
macsec = (Ipnet_pkt_macsec *)&pkt->data[pkt->start];
|
|
if (len < (sizeof(macsec->dst)+sizeof(macsec->src)+sizeof(macsec->d.ethType))) {
|
|
ipmacsec_dump_buffer("Incomplete Frame:", &pkt->data[pkt->start], len);
|
|
goto ipmacsec_pkt_dump_end;
|
|
}
|
|
ipmacsec_dump_buffer("MAC DST ADDR", (Ip_u8 *)macsec->dst, sizeof(macsec->dst));
|
|
ipmacsec_dump_buffer("MAC SRC ADDR", (Ip_u8 *)macsec->src, sizeof(macsec->src));
|
|
msdu_len = len - (Ip_u32)sizeof(macsec->dst) - (Ip_u32)sizeof(macsec->src);
|
|
if (macsec->d.ethType != IPNET_ETHFRAME_MACSEC) {
|
|
ipmacsec_dump_buffer("Non-MACsec MSDU", (Ip_u8 *)&macsec->d.ethType, msdu_len);
|
|
goto ipmacsec_pkt_dump_end;
|
|
}
|
|
if (len < IPMACSEC_0SL_MIN_MSDU_LEN) {
|
|
ipmacsec_dump_buffer("Incpomplete MACsec MPDU", (Ip_u8 *)&macsec->d.ethType, msdu_len);
|
|
goto ipmacsec_pkt_dump_end;
|
|
} else {
|
|
ipmacsec_dump_buffer("MACsec MPDU", (Ip_u8 *)&macsec->d.ethType, msdu_len);
|
|
goto ipmacsec_pkt_dump_end;
|
|
}
|
|
ipmacsec_pkt_dump_end:
|
|
IPCOM_LOG1(DEBUG2,"\n<<<<PKT DUMP END - %s", desc);
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 12 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_secy_ctrl_init
|
|
*===========================================================================
|
|
* Description: Initialize the SecY Control parameters to their default values.
|
|
* Parameters: secy - SecY instance pointer
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipmacsec_secy_ctrl_init(Ipnet_secy *secy) {
|
|
ip_assert(secy);
|
|
|
|
/* Set defaults */
|
|
secy->ctrl.protectFrames = IP_TRUE;
|
|
secy->ctrl.alwaysIncludeSCI = IP_FALSE;
|
|
secy->ctrl.useES = IP_FALSE;
|
|
secy->ctrl.useSCB = IP_FALSE;
|
|
secy->ctrl.validateFrames = IPMACSEC_SECY_VALIDATEFRAMES_DEFAULT;
|
|
secy->ctrl.replayProtect = IP_TRUE;
|
|
secy->ctrl.replayWindow = 0;
|
|
|
|
#ifndef IPNET_USE_KAY
|
|
/* In the absence of a KaY, we initialize ControlledPortEnabled to TRUE
|
|
* No notification is sent since at this point in time the interface name is not known */
|
|
secy->controlled_port.ctrl.cp_enabled = IP_TRUE;
|
|
(void)secy->controlled_port.update_status(secy->netif);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_secy_ctrl_config
|
|
*===========================================================================
|
|
* Description: Configure the SecY instance based on global configuration
|
|
* values (from sysvars)
|
|
* Parameters: secy - SecY instance pointer
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipmacsec_secy_ctrl_config(Ipnet_secy *secy) {
|
|
char validateFrames[256];
|
|
Ip_size_t len;
|
|
|
|
ip_assert(secy);
|
|
|
|
/* Read sysvar defaults */
|
|
secy->ctrl.protectFrames = ipcom_sysvar_get_as_int(IPMACSEC_SYSVAR_SECY_PROTECTFRAMES, 1);
|
|
secy->ctrl.alwaysIncludeSCI = ipcom_sysvar_get_as_int(IPMACSEC_SYSVAR_SECY_ALWAYSINCSCI, 0);
|
|
secy->ctrl.useES = ipcom_sysvar_get_as_int(IPMACSEC_SYSVAR_SECY_USE_ES, 0);
|
|
secy->ctrl.useSCB = ipcom_sysvar_get_as_int(IPMACSEC_SYSVAR_SECY_USE_SCB, 0);
|
|
secy->ctrl.replayProtect = ipcom_sysvar_get_as_int(IPMACSEC_SYSVAR_SECY_REPLAYPROTECT, 1);
|
|
secy->ctrl.replayWindow = ipcom_sysvar_get_as_int(IPMACSEC_SYSVAR_SECY_REPLAYWINDOW, 0);
|
|
#ifdef IPMACSEC_DEBUG_SECY_CTRL
|
|
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: protectFrames set to %s for interface %s",
|
|
IPMACSEC_BOOL_STRING(secy->ctrl.protectFrames), secy->netif->ipcom.name);
|
|
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: alwaysIncludeSCI set to %s for interface %s",
|
|
IPMACSEC_BOOL_STRING(secy->ctrl.alwaysIncludeSCI), secy->netif->ipcom.name);
|
|
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: useES set to %s for interface %s",
|
|
IPMACSEC_BOOL_STRING(secy->ctrl.useES), secy->netif->ipcom.name);
|
|
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: useSCB set to %s for interface %s",
|
|
IPMACSEC_BOOL_STRING(secy->ctrl.useSCB), secy->netif->ipcom.name);
|
|
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: replayProtect set to %s for interface %s",
|
|
IPMACSEC_BOOL_STRING(secy->ctrl.replayProtect), secy->netif->ipcom.name);
|
|
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: replayWindow set to %u for interface %s",
|
|
secy->ctrl.replayWindow, secy->netif->ipcom.name);
|
|
#endif
|
|
|
|
secy->ctrl.validateFrames = IPMACSEC_SECY_VALIDATEFRAMES_DEFAULT;
|
|
len = sizeof(validateFrames);
|
|
if (ipcom_sysvar_get(IPMACSEC_SYSVAR_SECY_VALIDATEFRAMES, validateFrames, &len)) {
|
|
if (ipcom_strncasecmp(validateFrames, IPMACSEC_SYSVARVAL_SECY_VALIDATEFRAMES_DISABLED,
|
|
sizeof(IPMACSEC_SYSVARVAL_SECY_VALIDATEFRAMES_DISABLED)) == 0) {
|
|
secy->ctrl.validateFrames = IPMACSEC_SECY_VALIDATEFRAMES_DISABLED;
|
|
#ifdef IPMACSEC_DEBUG_SECY_CTRL
|
|
IPCOM_LOG3(DEBUG2,"IPMACSEC-SECY_CRTL: validateFrames set to %s (%d) for interface %s",
|
|
IPMACSEC_SECY_VALIDATEFRAMES_DISABLED, secy->ctrl.validateFrames, secy->netif->ipcom.name);
|
|
#endif
|
|
} else if (ipcom_strncasecmp(validateFrames, IPMACSEC_SYSVARVAL_SECY_VALIDATEFRAMES_CHECK,
|
|
sizeof(IPMACSEC_SYSVARVAL_SECY_VALIDATEFRAMES_CHECK)) == 0) {
|
|
secy->ctrl.validateFrames = IPMACSEC_SECY_VALIDATEFRAMES_CHECK;
|
|
#ifdef IPMACSEC_DEBUG_SECY_CTRL
|
|
IPCOM_LOG3(DEBUG2,"IPMACSEC-SECY_CRTL: validateFrames set to %s (%d) for interface %s",
|
|
IPMACSEC_SYSVARVAL_SECY_VALIDATEFRAMES_CHECK, secy->ctrl.validateFrames, secy->netif->ipcom.name);
|
|
#endif
|
|
} else if (ipcom_strncasecmp(validateFrames, IPMACSEC_SYSVARVAL_SECY_VALIDATEFRAMES_STRICT,
|
|
sizeof(IPMACSEC_SYSVARVAL_SECY_VALIDATEFRAMES_STRICT)) == 0) {
|
|
secy->ctrl.validateFrames = IPMACSEC_SECY_VALIDATEFRAMES_STRICT;
|
|
#ifdef IPMACSEC_DEBUG_SECY_CTRL
|
|
IPCOM_LOG3(DEBUG2,"IPMACSEC-SECY_CRTL: validateFrames set to %s (%d) for interface %s",
|
|
IPMACSEC_SYSVARVAL_SECY_VALIDATEFRAMES_STRICT, secy->ctrl.validateFrames, secy->netif->ipcom.name);
|
|
#endif
|
|
} else {
|
|
#ifdef IPMACSEC_DEBUG_SECY_CTRL
|
|
IPCOM_LOG4(DEBUG2,"IPMACSEC-SECY_CRTL: %s sysvar contains invalid value. "
|
|
"validateFrames set to default value %s (%d) for interface %s",
|
|
IPMACSEC_SYSVAR_SECY_VALIDATEFRAMES,
|
|
IPMACSEC_SYSVARVAL_SECY_VALIDATEFRAMES_DEFAULT,
|
|
secy->ctrl.validateFrames, secy->netif->ipcom.name);
|
|
#endif
|
|
}
|
|
} else {
|
|
#ifdef IPMACSEC_DEBUG_SECY_CTRL
|
|
IPCOM_LOG4(DEBUG2,"IPMACSEC-SECY_CRTL: %s sysvar not found. "
|
|
"validateFrames set to default value %s (%d) for interface %s",
|
|
IPMACSEC_SYSVAR_SECY_VALIDATEFRAMES,
|
|
IPMACSEC_SYSVARVAL_SECY_VALIDATEFRAMES_DEFAULT,
|
|
secy->ctrl.validateFrames, secy->netif->ipcom.name);
|
|
#endif
|
|
}
|
|
|
|
secy->config.confidentialityOffset = (Ip_u16)ipcom_sysvar_get_as_int(IPMACSEC_SYSVAR_SECY_CONFIDENTIALITYOFF, 0);
|
|
#ifdef IPMACSEC_DEBUG_SECY_CTRL
|
|
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: confidentialityOffset set to %u for interface %s",
|
|
secy->config.confidentialityOffset, secy->netif->ipcom.name);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_parse_sectag
|
|
*===========================================================================
|
|
* Description: Parse a MACsec Security Tag
|
|
* Parameters: s- pointer to sectag to parse
|
|
* sectag - pointer to structure to hold the parsed sectag fields.
|
|
* Returns: 0 upon success, < 0 upon failure (invalid sectag fields).
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipmacsec_parse_sectag(IP_CONST Ipnet_sectag *s, Ipmacsec_sectag *sectag) {
|
|
ip_assert(s && sectag);
|
|
ip_assert(s->ethType == IPNET_ETHFRAME_MACSEC); /* A.10(FMT-3) */
|
|
|
|
ipcom_memset(sectag, 0, sizeof(Ipmacsec_sectag));
|
|
sectag->ethType = s->ethType; /* Left in network order */
|
|
sectag->tci = (Ip_u8)(((s->tci_an) & ~0x3) >> 2);
|
|
sectag->version = ((s->tci_an) & 0x80) >> 7;
|
|
sectag->es_bit = ((s->tci_an) & 0x40) >> 6;
|
|
sectag->sc_bit = ((s->tci_an) & 0x20) >> 5;
|
|
sectag->scb_bit = ((s->tci_an) & 0x10) >> 4;
|
|
sectag->e_bit = ((s->tci_an) & 0x08) >> 3;
|
|
sectag->c_bit = ((s->tci_an) & 0x04) >> 2;
|
|
sectag->an = (Ip_u8)((s->tci_an) & 0x3);
|
|
sectag->sl = s->sl;
|
|
sectag->pn = ip_ntohl(s->pn);
|
|
/* A.10(FMT-6) */
|
|
if (sectag->sc_bit) {
|
|
IPMACSEC_COPY_SCI(sectag->sci, s->sci);
|
|
sectag->length = IPMACSEC_SECTAG_LEN;
|
|
} else {
|
|
sectag->length = IPMACSEC_SECTAG_NOSCI_LEN;
|
|
}
|
|
|
|
/* Do validation of the sectag fields */
|
|
if ((sectag->version != 0) /* A.10(FMT-4) */
|
|
|| ((sectag->es_bit || sectag->scb_bit) && sectag->sc_bit) /* A.10(FMT-5, FMT-7) */
|
|
|| (s->sl & 0xc0) /* A.10(FMT-8) */
|
|
|| (s->pn == 0)) /* 10.5.2 */
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_format_sectag
|
|
*===========================================================================
|
|
* Description: Format a MACsec Security Tag.
|
|
* See Clause 9 of 802.1AE.
|
|
* Complies to A.8(GEN-7)
|
|
* Parameters: s- pointer to sectag to fromat
|
|
* sectag - pointer to structure holding the sectag fields to be used to format s.
|
|
* Returns: 0 upon success, < 0 upon failure (invalid sectag fields).
|
|
*
|
|
*/
|
|
IP_STATIC int ipmacsec_format_sectag(Ipnet_sectag *s, IP_CONST Ipmacsec_sectag *sectag) {
|
|
ip_assert(s && sectag);
|
|
ip_assert(sectag->pn); /* A.8(GEN-5); 10.5.2 */
|
|
ip_assert(!(sectag->e_bit && !sectag->c_bit)); /* 10.5.3 */
|
|
ip_assert(!((sectag->es_bit || sectag->scb_bit) && sectag->sc_bit)); /* A.10(FMT-5, FMT-7) */
|
|
ip_assert(!(sectag->sl & 0xc0)); /* A.10(FMT-8) */
|
|
|
|
/* A.10(FMT-6) */
|
|
if (sectag->sc_bit) {
|
|
if (sectag->length != IPMACSEC_SECTAG_LEN)
|
|
return -1;
|
|
IPMACSEC_COPY_SCI(s->sci, sectag->sci);
|
|
} else {
|
|
if (sectag->length != IPMACSEC_SECTAG_NOSCI_LEN)
|
|
return -1;
|
|
}
|
|
s->ethType = IPNET_ETHFRAME_MACSEC; /* A.10(FMT-3) */
|
|
s->tci_an =
|
|
(IPMACSEC_VERSION_NUMBER << 7) | /* A.10(FMT-4) */
|
|
((sectag->es_bit & 1) << 6) |
|
|
((sectag->sc_bit & 1) << 5) |
|
|
((sectag->scb_bit & 1) << 4) |
|
|
((sectag->e_bit & 1) << 3) |
|
|
((sectag->c_bit & 1) << 2) |
|
|
(sectag->an & 0x3); /* May generate warning with 64-bit compiler but code is ok */
|
|
s->sl = sectag->sl;
|
|
s->pn = ip_htonl(sectag->pn);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_input
|
|
*===========================================================================
|
|
* Description: MACsec SecY Input handler. Called only on Ethernet Interfaces.
|
|
* Parameters: netif - Interface netif struct pointer
|
|
* pkt - ipcom packet strcuture containing the MACsec ethernet frame
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_STATIC int ipmacsec_input(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt) {
|
|
|
|
ip_assert(netif);
|
|
ip_assert(pkt);
|
|
|
|
if (netif->ipcom.type != IP_IFT_ETHER) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(CRIT, "IPMACSEC-IN: SecY not supported on non-Ethernet interface %s. dropping...", netif->ipcom.name);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
if (!netif->secy) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(ERR, "IPMACSEC-IN: SecY not init on interface %s. dropping...", netif->ipcom.name);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
pkt->rx_secy_done = 1;
|
|
|
|
if ((pkt->end - pkt->start) < (IPMACSEC_MACADDRS_LEN+2)) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-IN: Packet too short (%u < %u)", pkt->end - pkt->start, IPMACSEC_MIN_MPDU_LEN+2);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* EAPOL can bypass the CP */
|
|
if (((Ipnet_pkt_eth *)&pkt->data[pkt->start])->d.type == IPNET_ETHFRAME_EAPOL)
|
|
return 0;
|
|
|
|
/* Now send copy to controlled port */
|
|
return ipmacsec_cp_input(netif, pkt);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_ucp_input
|
|
*===========================================================================
|
|
* Description: SecY Uncontrolled Port Demux step:
|
|
* sends a copy to any registered raw socket(s)
|
|
* Parameters: netif - Interface netif struct pointer
|
|
* pkt - ipcom packet strcuture containing the MACsec ethernet frame
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_GLOBAL int ipmacsec_ucp_input(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt) {
|
|
Ipnet_secy *secy = netif->secy;
|
|
|
|
ip_assert(secy);
|
|
|
|
if (IPMACSEC_SECY_PORT_STATUS_CHANGED(secy, secy->uncontrolled_port)) {
|
|
/* Common port status may have changed, so update UCP status */
|
|
(void)ipmacsec_secy_ucp_update_status(secy);
|
|
}
|
|
|
|
if (!secy->uncontrolled_port.status.mac_oper || !secy->uncontrolled_port.status.mac_enabled) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(ERR, "IPMACSEC-IN: Uncontrolled Port disabled on interface %s. dropping...", netif->ipcom.name);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
if (ipnet->num_packet_sock > 0) {
|
|
/* Give to all sockets that should match any protocol -> all packtes */
|
|
(void)ipnet_raw_input(pkt, IP_FALSE, 0, IP_NULL, 0, IP_NULL, 0,
|
|
(Ipnet_sock_lookup_f) ipnet_sock_packet_lookup);
|
|
|
|
/* Uncontrolled port stats are same as common port, so not updated */
|
|
pkt->rx_uncontrolled_port = 1; /* set this so packet duplicated by ipnet_packet_input has the indication */
|
|
|
|
/* Give to all sockets that matches the protocol */
|
|
if (ipnet_raw_input(pkt, IP_FALSE, IPNET_ETHFRAME_MACSEC, IP_NULL, 0, IP_NULL, 0,
|
|
(Ipnet_sock_lookup_f) ipnet_sock_packet_lookup) == -IP_ERRNO_ENODATA) {
|
|
|
|
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(ERR, "IPMACSEC-IN: Uncontrolled Port receiption failed on interface %s. dropping...", netif->ipcom.name);
|
|
#endif
|
|
pkt->rx_uncontrolled_port = 0; /* Reset the indication */
|
|
return -1;
|
|
}
|
|
pkt->rx_uncontrolled_port = 0; /* Reset the indication */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_cp_input
|
|
*===========================================================================
|
|
* Description: SecY Controlled Port Input hanlder. A.5(SAP, VER)
|
|
* Parameters: netif - Interface netif struct pointer
|
|
* pkt - ipcom packet strcuture containing the MACsec ethernet frame
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_STATIC int ipmacsec_cp_input(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt) {
|
|
Ipnet_secy *secy = netif->secy;
|
|
Ipnet_pkt_macsec *macsec;
|
|
int rc = 0;
|
|
|
|
ip_assert(secy);
|
|
|
|
if (!secy->controlled_port.ctrl.cp_enabled) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG0(DEBUG2, "IPMACSEC-CPI: Pkt dropped because ControlledPortEnabled is False");
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
macsec = (Ipnet_pkt_macsec *)&pkt->data[pkt->start];
|
|
/* A.9(VER-2) */
|
|
if (macsec->d.ethType != IPNET_ETHFRAME_MACSEC) {
|
|
/* untagged */
|
|
if (secy->ctrl.validateFrames == IPMACSEC_SECY_VALIDATEFRAMES_STRICT) {
|
|
IPMACSEC_SECY_STATS(InPktsNoTag++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-CPI: InPktsNoTag (%llu), dropping", IPMACSEC_SECY_STAT_VAL(InPktsNoTag));
|
|
#endif
|
|
return -1;
|
|
} else {
|
|
/* Pass unmodified */
|
|
IPMACSEC_SECY_STATS(InPktsUntagged++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-CPI: InPktsUntagged (%llu), accepting", IPMACSEC_SECY_STAT_VAL(InPktsUntagged));
|
|
#endif
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (IPMACSEC_SECY_PORT_STATUS_CHANGED(secy, secy->controlled_port)) {
|
|
/* Common port status may have changed, so update CP status */
|
|
(void)ipmacsec_secy_cp_update_status(secy, IPMACSEC_SECY_MAC_OPER_DEFAULT);
|
|
}
|
|
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG0(DEBUG2, "IPMACSEC-CPI: processing...");
|
|
#endif
|
|
rc = ipmacsec_secure_frame_verify(netif, pkt);
|
|
if (rc == 0) {
|
|
/* 10.7.6 */
|
|
IPMACSEC_CP_STATS_INC(ifInOctets, (pkt->end-pkt->start));
|
|
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_LINK_BROADCAST))
|
|
IPMACSEC_CP_STATS(ifInBroadcastPkts++);
|
|
else if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_LINK_MULTICAST))
|
|
IPMACSEC_CP_STATS(ifInMulticastPkts++);
|
|
else
|
|
IPMACSEC_CP_STATS(ifInUcastPkts++);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_secure_frame_verify
|
|
*===========================================================================
|
|
* Description: SecY Controlled Port Secure Frame Verification Handler. A.9(VER-1)
|
|
* Parameters: netif - Interface netif struct pointer
|
|
* pkt - ipcom packet strcuture containing the MACsec ethernet frame
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_STATIC int ipmacsec_secure_frame_verify(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt) {
|
|
Ipnet_secy *secy = netif->secy;
|
|
Ipnet_pkt_macsec *macsec;
|
|
Ipmacsec_sectag sectag;
|
|
Ip_u8 *secure_data, *icv;
|
|
Ip_u32 mpdu_len, min_len, secure_data_len, icv_len;
|
|
Ipmacsec_crypto_if *crypto;
|
|
Ipmacsec_crypto_ctx crypto_ctx;
|
|
Ip_u8 sci[IPMACSEC_SCI_LEN]; /* rx sci */
|
|
Ipmacsec_secy_sc *sc; /* rx SC */
|
|
Ipmacsec_secy_sa *sa; /* rx SA */
|
|
Ip_bool valid = IP_FALSE;
|
|
int rc = -1;
|
|
|
|
mpdu_len = pkt->end - pkt->start - IPMACSEC_MACADDRS_LEN;
|
|
/* A.10(FMT-2, FMT-9); 9.12 */
|
|
if (mpdu_len < IPMACSEC_MIN_MPDU_LEN) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFV: Packet too short (%u < %u)", mpdu_len, IPMACSEC_MIN_MPDU_LEN);
|
|
#endif
|
|
IPMACSEC_SECY_STATS(InPktsBadTag++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: InPktsBadTag (%llu), dropping", IPMACSEC_SECY_STAT_VAL(InPktsBadTag));
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
IPMACSEC_DUMP_PKT("IPMACSEC-SFV: b4 ", pkt);
|
|
macsec = (Ipnet_pkt_macsec *)&pkt->data[pkt->start];
|
|
rc = ipmacsec_parse_sectag(&macsec->d.sectag, §ag);
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PARSE
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFV: SecTAG length %d, version %d", sectag.length, sectag.version);
|
|
IPCOM_LOG8(DEBUG2, "IPMACSEC-SFV: SecTAG es_bit %d, sc_bit %d, scb_bit %d, e_bit %d, c_bit %d,"
|
|
"an %d, sl %d, pn %u",
|
|
sectag.es_bit, sectag.sc_bit, sectag.scb_bit, sectag.e_bit, sectag.c_bit,
|
|
sectag.an, sectag.sl, sectag.pn);
|
|
if (sectag.sc_bit) {
|
|
IPCOM_LOG8(DEBUG2, "IPMACSEC-SFV: SecTAG SCI = %.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
|
|
sectag.sci[0], sectag.sci[1], sectag.sci[2], sectag.sci[3],
|
|
sectag.sci[4], sectag.sci[5], sectag.sci[6], sectag.sci[7]);
|
|
}
|
|
#endif
|
|
if (rc != 0) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PARSE
|
|
IPCOM_LOG0(ERR, "IPMACSEC-SFV: Failed to parse SecTAG");
|
|
#endif
|
|
IPMACSEC_SECY_STATS(InPktsBadTag++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: InPktsBadTag (%llu), dropping", IPMACSEC_SECY_STAT_VAL(InPktsBadTag));
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* A.9(VER-3) */
|
|
if (sectag.e_bit && !sectag.c_bit) {
|
|
IPMACSEC_SECY_STATS(InPktsBadTag++);
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PARSE
|
|
IPCOM_LOG0(DEBUG2, "IPMACSEC-SFV: Dropping due to E-bit set and C-bit cleared");
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* A.10(FMT-9); 9.12 */
|
|
icv_len = (Ip_u32)ipmacsec_crypto_get_icvbits(secy)/8;
|
|
if ((icv_len<8) || (icv_len>16)) {
|
|
/* 9.11 */
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PARSE
|
|
IPCOM_LOG1(CRIT, "IPMACSEC-SFV: Dropping due to Invalid ICV length (%u octets)", icv_len);
|
|
#endif
|
|
return -1;
|
|
}
|
|
min_len = IPMACSEC_SECTAG_NOSCI_LEN; /*8*/
|
|
if (sectag.c_bit == 0) {
|
|
min_len += IPMACSEC_0CBIT_ICV_LEN;
|
|
} else {
|
|
min_len += icv_len;
|
|
}
|
|
if (sectag.sc_bit) {
|
|
min_len += IPMACSEC_SCI_LEN;
|
|
}
|
|
if (sectag.sl) {
|
|
min_len += sectag.sl;
|
|
} else {
|
|
min_len += IPMACSEC_0SL_MIN_MSDU_LEN;
|
|
}
|
|
|
|
if (mpdu_len < min_len) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFV: Packet too short (%u < %u)", mpdu_len, min_len);
|
|
#endif
|
|
IPMACSEC_SECY_STATS(InPktsBadTag++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: InPktsBadTag (%llu), dropping", IPMACSEC_SECY_STAT_VAL(InPktsBadTag));
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
secure_data = ((Ip_u8 *)(&macsec->d.sectag))+sectag.length;
|
|
if (sectag.sl) {
|
|
secure_data_len = sectag.sl;
|
|
} else {
|
|
secure_data_len = mpdu_len - sectag.length - icv_len;
|
|
}
|
|
icv = secure_data + secure_data_len;
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFV: secure_data_len %u, icv_len %u", secure_data_len, icv_len);
|
|
#endif
|
|
if (secure_data_len > 0x0000FFFFUL) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG2(ERR, "IPMACSEC-SFV: secure_data_len %u too large. dropping", secure_data_len, icv_len);
|
|
#endif
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (sectag.sc_bit) {
|
|
IPMACSEC_COPY_SCI(sci, sectag.sci);
|
|
} else {
|
|
IPNET_SET_ETHADDR(sci, macsec->src);
|
|
sci[6] = 0x00;
|
|
sci[7] = (Ip_u8)(sectag.scb_bit ? 0x00 : 0x01); /* 9.5 */
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG8(DEBUG2, "IPMACSEC-SFV: SecTAG sc_bit was cleared. "
|
|
"Using derived SCI = %.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
|
|
sci[0], sci[1], sci[2], sci[3], sci[4], sci[5], sci[6], sci[7]);
|
|
#endif
|
|
}
|
|
|
|
sc = ipmacsec_secy_lookup_rx_sc(secy, sci);
|
|
|
|
if (!sc || !sc->enabled || !sc->oper.receiving) {
|
|
if (!sc || !sc->enabled) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: Invalid Rx SC (%s), dropping", (!sc ? "Null SC": "Not Enabled"));
|
|
#endif
|
|
}
|
|
if (sc && !sc->oper.receiving) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG0(DEBUG2, "IPMACSEC-SFV: Invalid Rx SC (not receiving), dropping");
|
|
#endif
|
|
}
|
|
/* A.9(VER-4) */
|
|
if ((secy->ctrl.validateFrames == IPMACSEC_SECY_VALIDATEFRAMES_STRICT) || sectag.c_bit) {
|
|
IPMACSEC_SECY_STATS(InPktsNoSCI++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: InPktsNoSCI (%llu), dropping", IPMACSEC_SECY_STAT_VAL(InPktsNoSCI));
|
|
#endif
|
|
return -1;
|
|
} else {
|
|
/* Pass unmodified */
|
|
IPMACSEC_SECY_STATS(InPktsUnknownSCI++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: InPktsUnknownSCI (%llu), accepting", IPMACSEC_SECY_STAT_VAL(InPktsUnknownSCI));
|
|
#endif
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
sa = ipmacsec_secy_lookup_rx_sa(secy, sc, sectag.an);
|
|
if (sa && sa->inUse) {
|
|
/* Handle cases when RX SA interleaving may occur (7.1.3) */
|
|
if (ipmacsec_secy_check_rx_sa_interleave(secy, sa) < 0) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: Received delayed interleaved frame on SA %d, dropping it.", sa->an);
|
|
#endif
|
|
}
|
|
}
|
|
if (!sa || !sa->inUse) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: Invalid Rx SA (%s), dropping", (!sa ? "Null SA": "Not inUse"));
|
|
#endif
|
|
/* A.9(VER-5) */
|
|
if ((secy->ctrl.validateFrames == IPMACSEC_SECY_VALIDATEFRAMES_STRICT) || sectag.c_bit) {
|
|
if (!sa) {
|
|
IPMACSEC_SECY_STATS(InPktsBadTag++);
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: InPktsBadTag (%llu), dropping", IPMACSEC_SECY_STAT_VAL(InPktsBadTag));
|
|
#endif
|
|
} else {
|
|
IPMACSEC_SA_STATS(rx.InPktsNotUsingSA++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFV: SA %d InPktsNotUsingSA (%llu), dropping",
|
|
sa->an, IPMACSEC_SA_STAT_VAL(rx.InPktsNotUsingSA));
|
|
#endif
|
|
}
|
|
return -1;
|
|
} else {
|
|
/* Pass unmodified */
|
|
if (!sa) {
|
|
IPMACSEC_SECY_STATS(InPktsBadTag++);
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: InPktsBadTag (%llu), dropping", IPMACSEC_SECY_STAT_VAL(InPktsBadTag));
|
|
#endif
|
|
} else {
|
|
IPMACSEC_SA_STATS(rx.InPktsUnusedSA++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFV: SA %d InPktsUnusedSA (%llu), accepting",
|
|
sa->an, IPMACSEC_SA_STAT_VAL(rx.InPktsUnusedSA));
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
if (!secy->controlled_port.status.mac_enabled) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: dropping frame since CP mac_enabled is FALSE on interface %s", netif->ipcom.name);
|
|
#endif
|
|
return -1;
|
|
}
|
|
if (!secy->controlled_port.status.mac_oper) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: dropping frame since CP mac_oper is FALSE on interface %s", netif->ipcom.name);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
ip_assert(sa->key);
|
|
|
|
if (secy->ctrl.replayProtect && sectag.pn < sa->lowestPN) {
|
|
/* A.9(VER-6) */
|
|
IPMACSEC_SC_STATS(in.InPktsLate++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: SC InPktsLate (%llu), dropping", IPMACSEC_SC_STAT_VAL(in.InPktsLate));
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* Crypto must be initialized or no point going any further */
|
|
crypto = sc->crypto;
|
|
if (!crypto) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG0(ERR, "IPMACSEC-SFV: Crypto handler not installed");
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
#if IPMACSEC_NOT_SUPPORTED
|
|
/* Verify if pkt meets cipher suite performance capabilities. */
|
|
/* For now since no queuing happens for cipher validations, there is
|
|
* nothing to be done to validate this.
|
|
*/
|
|
if (!ipmacsec_secy_perf_check()) {
|
|
/* A.9(VER-7) */
|
|
IPMACSEC_SECY_STATS(InPktsOverrun++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: InPktsOverrun (%llu), dropping", IPMACSEC_SECY_STAT_VAL(InPktsOverrun));
|
|
#endif
|
|
return -1;
|
|
}
|
|
#endif
|
|
/* Pass on to crypto verification handler */
|
|
if (sectag.sc_bit)
|
|
IPMACSEC_COPY_SCI(crypto_ctx.sci, sectag.sci);
|
|
else
|
|
IPMACSEC_COPY_SCI(crypto_ctx.sci, sc->sci);
|
|
crypto_ctx.pn = ip_htonl(sectag.pn);
|
|
crypto_ctx.macsec_hdr = (Ip_u8 *)macsec;
|
|
crypto_ctx.macsec_hdr_len = (Ip_u8)(IPMACSEC_MACADDRS_LEN + sectag.length);
|
|
crypto_ctx.userdata = IP_NULL; /* validate function allocates memory and sets this it if needed */
|
|
crypto_ctx.userdata_len = 0;
|
|
crypto_ctx.securedata = secure_data;
|
|
crypto_ctx.securedata_len = (Ip_u16)secure_data_len;
|
|
crypto_ctx.icv = icv;
|
|
crypto_ctx.icv_len = (Ip_u8)icv_len;
|
|
crypto_ctx.oper.decrypt = sectag.e_bit;
|
|
crypto_ctx.key_index = (Ip_u16)sa->an;
|
|
rc = -1;
|
|
if (secy->ctrl.validateFrames != IPMACSEC_SECY_VALIDATEFRAMES_DISABLED) {
|
|
ip_assert(crypto->validate);
|
|
rc = crypto->validate(crypto, &crypto_ctx, &valid); /* A.9(VER-9) */
|
|
if (sectag.e_bit) {
|
|
IPMACSEC_SECY_STATS_INC(InOctetsDecrypted,secure_data_len);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: InOctetsDecrypted (%llu)", IPMACSEC_SECY_STAT_VAL(InOctetsDecrypted));
|
|
#endif
|
|
} else {
|
|
IPMACSEC_SECY_STATS_INC(InOctetsValidated,secure_data_len);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: InOctetsValidated (%llu)", IPMACSEC_SECY_STAT_VAL(InOctetsValidated));
|
|
#endif
|
|
}
|
|
}
|
|
if (rc != 0) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG1(ERR, "IPMACSEC-SFV: Crypto validation %s",
|
|
((secy->ctrl.validateFrames != IPMACSEC_SECY_VALIDATEFRAMES_DISABLED) ? "failed" : "skipped"));
|
|
#endif
|
|
valid = IP_FALSE;
|
|
} else if (sectag.e_bit && (!crypto_ctx.userdata || !crypto_ctx.userdata_len)) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG0(ERR, "IPMACSEC-SFV: Crypto decryption error.");
|
|
#endif
|
|
valid = IP_FALSE;
|
|
} else if (crypto_ctx.userdata && (crypto_ctx.userdata_len != secure_data_len)) {
|
|
/* We donnot support this for now */
|
|
#ifdef IPMACSEC_DEBUG_SECY_IN_PROCESS
|
|
IPCOM_LOG2(ERR, "IPMACSEC-SFV: Crypto user data length (%u) differs from secure data length (%u).",
|
|
crypto_ctx.userdata_len, secure_data_len);
|
|
#endif
|
|
valid = IP_FALSE;
|
|
}
|
|
|
|
if (secy->ctrl.replayProtect && sectag.pn < sa->lowestPN){
|
|
/* A.9(VER-6) */
|
|
IPMACSEC_SC_STATS(in.InPktsLate++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: SC InPktsLate (%llu), dropping", IPMACSEC_SC_STAT_VAL(in.InPktsLate));
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
if (!valid) {
|
|
if ((secy->ctrl.validateFrames == IPMACSEC_SECY_VALIDATEFRAMES_STRICT) || sectag.c_bit) {
|
|
/* A.9(VER-10) */
|
|
IPMACSEC_SA_STATS(rx.InPktsNotValid++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: SA InPktsNotValid (%llu), dropping", IPMACSEC_SA_STAT_VAL(rx.InPktsNotValid));
|
|
#endif
|
|
return -1;
|
|
} else if (secy->ctrl.validateFrames == IPMACSEC_SECY_VALIDATEFRAMES_CHECK){
|
|
IPMACSEC_SA_STATS(rx.InPktsInvalid++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: SA InPktsInvalid (%llu), accepting", IPMACSEC_SA_STAT_VAL(rx.InPktsInvalid));
|
|
#endif
|
|
goto done_validate;
|
|
}
|
|
}
|
|
|
|
if (sectag.pn < sa->lowestPN) {
|
|
IPMACSEC_SC_STATS(in.InPktsDelayed++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: SC InPktsDelayed (%llu), accepting", IPMACSEC_SC_STAT_VAL(in.InPktsDelayed));
|
|
#endif
|
|
goto done_validate;
|
|
}
|
|
|
|
if (!valid) {
|
|
/* A.9(VER-8) */
|
|
ip_assert(secy->ctrl.validateFrames == IPMACSEC_SECY_VALIDATEFRAMES_DISABLED);
|
|
IPMACSEC_SC_STATS(in.InPktsUnchecked++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: SC InPktsUnchecked (%llu), accepting", IPMACSEC_SC_STAT_VAL(in.InPktsUnchecked));
|
|
#endif
|
|
goto done_validate;
|
|
}
|
|
|
|
(void)ipmacsec_secy_update_rx_sa_PNs(secy, sa, sectag.pn); /* A.9(VER-11); 10.6.5 */
|
|
|
|
IPMACSEC_SA_STATS(rx.InPktsOK++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_IN_PROCESS)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFV: SA InPktsOK (%llu), accepting", IPMACSEC_SA_STAT_VAL(rx.InPktsOK));
|
|
#endif
|
|
|
|
done_validate:
|
|
/* Copy decrypted data over secure one */
|
|
if (crypto_ctx.userdata) {
|
|
ipcom_memcpy(secure_data, crypto_ctx.userdata, secure_data_len);
|
|
}
|
|
|
|
/* Adjust packet to remove ICV and SecTAG A.9(VER-12) */
|
|
ipcom_memcpy(&pkt->data[pkt->start+sectag.length], &pkt->data[pkt->start], IPMACSEC_MACADDRS_LEN);
|
|
pkt->start+=sectag.length;
|
|
pkt->end-=icv_len;
|
|
|
|
pkt->rx_authentic = IP_TRUE;
|
|
pkt->rx_private = (sectag.e_bit ? 1 : 0); /* May generate warning with 64-bit compiler but code is ok */
|
|
|
|
IPMACSEC_DUMP_PKT("IPMACSEC-SFV: after ", pkt);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_output
|
|
*===========================================================================
|
|
* Description: MACsec SecY Output handler. Called only on Ethernet Interfaces.
|
|
* A.5(SAP, GEN)
|
|
* Parameters: netif - Interface netif struct pointer
|
|
* to - ignored
|
|
* pkt - ipcom packet strcuture containing the MACsec MSDU
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_STATIC int ipmacsec_output(struct Ipnet_netif_struct *netif, IP_CONST struct Ip_sockaddr_ll *to, Ipcom_pkt *pkt) {
|
|
IPCOM_UNUSED_ARG(to);
|
|
|
|
ip_assert(netif);
|
|
ip_assert(pkt);
|
|
|
|
/* If "tx_use_controlled_port" is TRUE, this overrides other conditions and always sends over CP */
|
|
if (pkt->tx_use_controlled_port)
|
|
return ipmacsec_cp_output(netif, pkt);
|
|
|
|
/*
|
|
* By default, EAPOL frames are not set over the SecY CP, nor anything that
|
|
* sets "tx_use_uncontrolled_port" to TRUE.
|
|
* However, certain EAPOL frames (such as EAPOL announcements) may override this rule by setting
|
|
* "tx_use_controlled_port" to TRUE.
|
|
*/
|
|
if ((pkt->tx_use_uncontrolled_port) ||
|
|
(((Ipnet_pkt_eth *)&pkt->data[pkt->start])->d.type == IPNET_ETHFRAME_EAPOL))
|
|
return ipmacsec_ucp_output(netif, pkt);
|
|
|
|
/* Default condition is to send over SecY CP */
|
|
return ipmacsec_cp_output(netif, pkt);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_ucp_output
|
|
*===========================================================================
|
|
* Description: SecY Uncontrolled Port Output handler.
|
|
* Parameters: netif - Interface netif struct pointer
|
|
* pkt - ipcom packet strcuture containing the MACsec MSDU
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_STATIC int ipmacsec_ucp_output(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt) {
|
|
Ipnet_secy *secy = netif->secy;
|
|
IPCOM_UNUSED_ARG(pkt);
|
|
|
|
ip_assert(secy);
|
|
|
|
/* Nothing to do on the SecY uncontrolled port. Uncontrolled port stats are same as common port, so not updated */
|
|
#ifdef IPMACSEC_DEBUG_SECY_UCP_OUT
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-UCPO: passing frame unmodified over interface %s", netif->ipcom.name);
|
|
#endif
|
|
|
|
if (IPMACSEC_SECY_PORT_STATUS_CHANGED(secy, secy->uncontrolled_port)) {
|
|
/* Common port status may have changed, so update UCP status */
|
|
(void)ipmacsec_secy_ucp_update_status(secy);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_cp_output
|
|
*===========================================================================
|
|
* Description: SecY Controlled Port Output handler.
|
|
* Parameters: netif - Interface netif struct pointer
|
|
* pkt - ipcom packet strcuture containing the MACsec MSDU
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_STATIC int ipmacsec_cp_output(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt) {
|
|
int rc=0;
|
|
Ipnet_secy *secy = netif->secy;
|
|
ip_assert(secy);
|
|
|
|
|
|
if (!secy->controlled_port.ctrl.cp_enabled) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_CP_OUT
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-CPO: dropping frame since CP not enabled on interface %s", netif->ipcom.name);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
if (!secy->ctrl.protectFrames) {
|
|
IPMACSEC_SECY_STATS(OutPktsUntagged++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_CP_OUT)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-CPO: OutPktsUntagged (%llu), sending", IPMACSEC_SECY_STAT_VAL(OutPktsUntagged));
|
|
#endif
|
|
return 0; /* A.8(GEN-1) */
|
|
}
|
|
|
|
if (IPMACSEC_SECY_PORT_STATUS_CHANGED(secy, secy->uncontrolled_port)) {
|
|
/* Common port status may have changed, so update CP/UCP status */
|
|
(void)ipmacsec_secy_ucp_update_status(secy);
|
|
(void)ipmacsec_secy_cp_update_status(secy, IPMACSEC_SECY_MAC_OPER_DEFAULT);
|
|
}
|
|
|
|
#ifdef IPMACSEC_DEBUG_SECY_CP_OUT
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-CPO: protecting frame on interface %s", netif->ipcom.name);
|
|
#endif
|
|
/* A.8(GEN-2) */
|
|
rc = ipmacsec_secure_frame_generate(netif, pkt);
|
|
if (rc == 0) {
|
|
/* 10.7.6 */
|
|
IPMACSEC_CP_STATS_INC(ifOutOctets, (pkt->end-pkt->start));
|
|
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_BROADCAST))
|
|
IPMACSEC_CP_STATS(ifOutBroadcastPkts++);
|
|
else if(IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_MULTICAST))
|
|
IPMACSEC_CP_STATS(ifOutMulticastPkts++);
|
|
else
|
|
IPMACSEC_CP_STATS(ifOutUcastPkts++);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmacsec_secure_frame_generate
|
|
*===========================================================================
|
|
* Description: SecY Controlled Port Secure Frame Generation Handler. A.10(FMT-2)
|
|
* Parameters: netif - Interface netif struct pointer
|
|
* pkt - ipcom packet strcuture containing the MACsec MSDU
|
|
* Returns: 0 upon success, < 0 upon failure.
|
|
*
|
|
*/
|
|
IP_STATIC int ipmacsec_secure_frame_generate(struct Ipnet_netif_struct *netif, Ipcom_pkt *pkt) {
|
|
Ipnet_secy *secy = netif->secy;
|
|
Ipmacsec_sectag sectag;
|
|
Ipnet_pkt_macsec *macsec;
|
|
Ip_u8 *user_data, *icv;
|
|
Ip_u32 msdu_len, user_data_len, required_len, expected_len, icv_len;
|
|
Ipmacsec_crypto_if *crypto;
|
|
Ipmacsec_crypto_ctx crypto_ctx;
|
|
Ipmacsec_secy_sc *sc; /* tx SC */
|
|
Ipmacsec_secy_sa *sa; /* tx SA */
|
|
int rc = -1;
|
|
|
|
ip_assert(secy->ctrl.protectFrames);
|
|
|
|
IPMACSEC_DUMP_PKT("IPMACSEC-SFG: b4 ", pkt);
|
|
|
|
/* Start by getting and verifying the TX SC */
|
|
sc = ipmacsec_secy_get_tx_sc(secy);
|
|
if (!sc || !sc->enabled) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFG: Invalid Tx SC (%s), dropping", (!sc ? "Null SC": "Not Enabled"));
|
|
#endif
|
|
return -1;
|
|
}
|
|
if (IPMACSEC_INVALID_SCI(sc->sci)) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG0(DEBUG2, "IPMACSEC-SFG: Invalid Tx SC (Invalid SCI), dropping");
|
|
#endif
|
|
return -1;
|
|
}
|
|
if (!sc->oper.transmitting) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG0(DEBUG2, "IPMACSEC-SFG: Invalid Tx SC (not transmitting), dropping");
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* Crypto must be initialized or no point going any further */
|
|
crypto = sc->crypto;
|
|
if (!crypto) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG0(ERR, "IPMACSEC-SFG: Crypto handler not installed");
|
|
#endif
|
|
return -1;
|
|
}
|
|
if (crypto->changesDataLength) {
|
|
/* Not supported */
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG0(ERR, "IPMACSEC-SFG: Unsupported Crypto Cipher");
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
msdu_len = pkt->end - pkt->start - IPMACSEC_MACADDRS_LEN;
|
|
if (msdu_len < 1) {
|
|
/* Secure data must contain at least 1 byte */
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFG: Secure data must contain at least 1 byte (msdu_len = %d)", msdu_len);
|
|
#endif
|
|
return -1;
|
|
}
|
|
user_data_len = msdu_len;
|
|
icv_len = crypto->icvbits/8;
|
|
ip_assert((icv_len>=8) && (icv_len<=16)); /* 9.11 */
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFG: user_data_len %u, icv_len %u", user_data_len, icv_len);
|
|
#endif
|
|
if (user_data_len > 0x0000FFFFUL) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG2(ERR, "IPMACSEC-SFG: user_data_len %u too large. dropping", user_data_len, icv_len);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* A.8(GEN-3) */
|
|
sa = ipmacsec_secy_get_tx_sa(secy);
|
|
if (!sa || !sa->inUse) {
|
|
/* A.8(GEN-4) */
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFG: Invalid SA (%u), dropping", sc->encodingSA);
|
|
#endif
|
|
/* MAC_Operational should transition to FALSE for the CP (10.5.1) */
|
|
(void)ipmacsec_secy_cp_update_status(secy, IPMACSEC_SECY_MAC_OPER_OFF);
|
|
return -1;
|
|
}
|
|
if (!secy->controlled_port.status.mac_enabled) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFG: dropping frame since CP mac_enabled is FALSE on interface %s", netif->ipcom.name);
|
|
#endif
|
|
return -1;
|
|
}
|
|
if (!secy->controlled_port.status.mac_oper) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFG: dropping frame since CP mac_oper is FALSE on interface %s", netif->ipcom.name);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
ip_assert(sa->key);
|
|
|
|
/* Prepare SecTAG */
|
|
sectag.scb_bit = secy->ctrl.useSCB && !secy->ctrl.alwaysIncludeSCI; /* 10.5.3; A.8(GEN-10) */
|
|
sectag.es_bit = secy->ctrl.useES && !secy->ctrl.alwaysIncludeSCI; /* 10.5.3; A.8(GEN-8) */
|
|
sectag.sc_bit = secy->ctrl.alwaysIncludeSCI ||
|
|
((secy->oper.numActiveRxSCs > 1) && !secy->ctrl.useES && !secy->ctrl.useSCB); /* 10.5.3; A.8(GEN-9) */
|
|
sectag.e_bit = sa->confidentiality; /* 9.5; 10.5.3; A.8(GEN-11) */
|
|
sectag.c_bit = sectag.e_bit || (crypto->changesDataLength) || (icv_len != 16); /* 10.5.3; A.8(GEN-12) */
|
|
sectag.an = (Ip_u8)sa->an;
|
|
sectag.sl = (Ip_u8)((user_data_len < IPMACSEC_0SL_MIN_MSDU_LEN) ? user_data_len : 0);
|
|
sectag.pn = sa->nextPN;
|
|
(void)ipmacsec_secy_update_sa_nextPN(secy, sa, sa->nextPN+1);/* A.8(GEN-6). */
|
|
/* Note that We will still continue to gen this frame even if nextPN becomes 0.
|
|
* However as a consequence of nextPN becoming 0, no more frames will be received or transmitted
|
|
* on this SA after this one is sent.
|
|
*/
|
|
IPMACSEC_COPY_SCI(sectag.sci, sc->sci);
|
|
sectag.length = (Ip_u8)(sectag.sc_bit ? IPMACSEC_SECTAG_LEN : IPMACSEC_SECTAG_NOSCI_LEN);
|
|
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFG: SecTAG length %d, version %d", sectag.length, IPMACSEC_VERSION_NUMBER);
|
|
IPCOM_LOG8(DEBUG2, "IPMACSEC-SFG: SecTAG es_bit %d, sc_bit %d, scb_bit %d, e_bit %d, c_bit %d,"
|
|
"an %d, sl %d, pn %u",
|
|
sectag.es_bit, sectag.sc_bit, sectag.scb_bit, sectag.e_bit, sectag.c_bit,
|
|
sectag.an, sectag.sl, sectag.pn);
|
|
if (sectag.sc_bit) {
|
|
IPCOM_LOG8(DEBUG2, "IPMACSEC-SFG: SecTAG SCI = %.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
|
|
sectag.sci[0], sectag.sci[1], sectag.sci[2], sectag.sci[3],
|
|
sectag.sci[4], sectag.sci[5], sectag.sci[6], sectag.sci[7]);
|
|
}
|
|
#endif
|
|
|
|
/* Verify if there is enough space in packet */
|
|
required_len = IPMACSEC_MACADDRS_LEN+sectag.length+user_data_len+icv_len;
|
|
if ((Ip_u32)pkt->maxlen < required_len) {
|
|
/* Drop */
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFG: pkt maxlen too small (%d < %d)", pkt->maxlen, required_len);
|
|
#endif
|
|
return -1;
|
|
}
|
|
if (pkt->start < sectag.length) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFG: Not enough space for SecTAG (%d < %d)", pkt->start, sectag.length);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* A.8(GEN-14); 10.5.5 Should check if pkt length > max_len on common port and drop incrementing OutPktsTooLong */
|
|
/* Since we do not support ciphers that change the data length,
|
|
* we can do this check now before wasting time with the crypto stuff
|
|
*/
|
|
expected_len = (Ip_u32)(user_data_len + sectag.length-sizeof(sectag.ethType) + icv_len);
|
|
if (expected_len > secy->netif->ipcom.mtu+IPMACSEC_RESERVED_OCTETS) {
|
|
IPMACSEC_SECY_STATS(OutPktsTooLong++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_OUT_PREP)
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFG: expected_len %u > mtu %u", expected_len, secy->netif->ipcom.mtu);
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFG: OutPktsTooLong (%llu)", IPMACSEC_SECY_STAT_VAL(OutPktsTooLong));
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* Adjust packet */
|
|
pkt->start-=sectag.length;
|
|
ipcom_memcpy(&pkt->data[pkt->start], &pkt->data[pkt->start+sectag.length], IPMACSEC_MACADDRS_LEN);
|
|
pkt->end+=icv_len;
|
|
|
|
macsec = (Ipnet_pkt_macsec *)&pkt->data[pkt->start];
|
|
/* Add SecTAG */
|
|
if (ipmacsec_format_sectag(&macsec->d.sectag, §ag) != 0) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG0(DEBUG2, "IPMACSEC-SFG: failed to format SecTAG");
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
user_data = ((Ip_u8 *)(&macsec->d.sectag))+sectag.length;
|
|
icv = user_data + user_data_len;
|
|
/* Pass on to crypto protection handler */
|
|
ip_assert(crypto->protect);
|
|
IPMACSEC_COPY_SCI(crypto_ctx.sci, secy->config.sci);
|
|
crypto_ctx.pn = ip_htonl(sectag.pn);
|
|
crypto_ctx.macsec_hdr = (Ip_u8 *)macsec;
|
|
crypto_ctx.macsec_hdr_len = (Ip_u8)(IPMACSEC_MACADDRS_LEN + sectag.length);
|
|
crypto_ctx.userdata = user_data;
|
|
crypto_ctx.userdata_len = (Ip_u16)user_data_len;
|
|
crypto_ctx.securedata = IP_NULL; /* validate function allocates memory and sets this it if needed */
|
|
crypto_ctx.securedata_len = 0;
|
|
crypto_ctx.icv = icv; /* Gets updated by crypto->protect() */
|
|
crypto_ctx.icv_len = (Ip_u8)icv_len;
|
|
crypto_ctx.oper.encrypt = sectag.e_bit;
|
|
crypto_ctx.key_index = (Ip_u16)sa->an;
|
|
sc->encipheringSA = sa->an; /* 10.5.4 */
|
|
rc = crypto->protect(crypto, &crypto_ctx); /* A.8(GEN-13) */
|
|
if (rc != 0) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG0(ERR, "IPMACSEC-SFG: Crypto protection operation failed");
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
if (sectag.e_bit && (!crypto_ctx.securedata || !crypto_ctx.securedata_len)) {
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG0(ERR, "IPMACSEC-SFG: Crypto encryption error.");
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
if (crypto_ctx.securedata && (crypto_ctx.securedata_len != user_data_len)) {
|
|
/* We donnot support this for now */
|
|
#ifdef IPMACSEC_DEBUG_SECY_OUT_PREP
|
|
IPCOM_LOG2(ERR, "IPMACSEC-SFG: Crypto secure data length (%u) differs from user data length (%u).",
|
|
crypto_ctx.securedata_len, user_data_len);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
/* Copy encrypted data over secure one */
|
|
if (crypto_ctx.securedata) {
|
|
ipcom_memcpy(user_data, crypto_ctx.securedata, crypto_ctx.securedata_len);
|
|
}
|
|
/* A.8(GEN-14) */
|
|
if (sectag.e_bit) {
|
|
IPMACSEC_SECY_STATS_INC(OutOctetsEncrypted,user_data_len);
|
|
#if defined(IPMACSEC_DEBUG_SECY_OUT_PREP)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFG: OutOctetsEncrypted (%llu)", IPMACSEC_SECY_STAT_VAL(OutOctetsEncrypted));
|
|
#endif
|
|
} else {
|
|
IPMACSEC_SECY_STATS_INC(OutOctetsProtected,user_data_len);
|
|
#if defined(IPMACSEC_DEBUG_SECY_OUT_PREP)
|
|
IPCOM_LOG1(DEBUG2, "IPMACSEC-SFG: OutOctetsProtected (%llu)", IPMACSEC_SECY_STAT_VAL(OutOctetsProtected));
|
|
#endif
|
|
}
|
|
|
|
if (sectag.e_bit) {
|
|
IPMACSEC_SA_STATS(tx.OutPktsEncrypted++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_OUT_PREP)
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFG: SA %d OutPktsEncrypted (%llu), sending", sa->an, IPMACSEC_SA_STAT_VAL(tx.OutPktsEncrypted));
|
|
#endif
|
|
} else {
|
|
IPMACSEC_SA_STATS(tx.OutPktsProtected++);
|
|
#if defined(IPMACSEC_DEBUG_SECY_OUT_PREP)
|
|
IPCOM_LOG2(DEBUG2, "IPMACSEC-SFG: SA %d OutPktsProtected (%llu), sending", sa->an, IPMACSEC_SA_STAT_VAL(tx.OutPktsProtected));
|
|
#endif
|
|
}
|
|
|
|
IPMACSEC_DUMP_PKT("IPMACSEC-SFG: after ", pkt);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef IPMACSEC_UNIT_TEST
|
|
void ipmacsec_dummy_api(void);
|
|
#warning This function is just for tesing purposes to avoid compiler symbol optimization. Do not use this API.
|
|
void ipmacsec_dummy_api(void) {
|
|
ip_assert(0);
|
|
|
|
(void)ipmacsec_counter_format_test();
|
|
(void)ipmacsec_parse_sci_string_test();
|
|
(void)ipmacsec_netlink_test();
|
|
(void)ipmacsec_netlink_monitor_test(0);
|
|
(void)ipmacsec_netlink_multi_test(0);
|
|
(void)ipmacsec_key_test(0);
|
|
(void)ipmacsec_lmi_secy_set_info_notification(0,0,0);
|
|
(void)ipmacsec_lmi_secy_get_info_notification(0,0,0);
|
|
(void)ipmacsec_ctrl_cmd_test(0);
|
|
(void)ipmacsec_sc_sa_test(0);
|
|
}
|
|
#endif
|
|
|