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

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, &sectag);
#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, &sectag) != 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