/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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; iend-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<<<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