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

4087 lines
125 KiB
C

/* ipmacsec_secy_mgmt.c - MACsec SecY Management 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,24feb10,mry Created
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
* Implements the MACsec SecY management related functions for SC/SA/SAK
* creation, lookup and deletion. Also implements netlink based SecY command
* handling and notification generation, and related management functions.
*/
/*
****************************************************************************
* 2 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_clib.h>
#include <ipcom_time.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 "ipmacsec_debug.h"
#include "ipmacsec_sysvar.h"
#include "ipmacsec_secy_mgmt.h"
#include "ipmacsec_h.h"
#include "ipmacsec.h"
#include "ipmacsec_netlink.h"
#include "ipmacsec_crypto_if.h"
/*
****************************************************************************
* 3 CONFIGURATION
****************************************************************************
*/
#include "ipmacsec_config.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/* Time bound (in ms) in which a receiver can accept interleaved RX SAs on a particular SC */
#define IPMACSEC_RX_SA_INTERLEAVE_PERIOD 500
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/* For now, the Keydb is implemented as a simple array instead of a hash.
* Due to the limited number of supported SAs/SCs and thus small Keydb size,
* the lookup/insert is not costly.
*/
typedef struct Ipmacsec_keydb_struct {
Ipmacsec_secy_key *key[IPMACSEC_SECY_KEYDB_SIZE]; /* Array of key pointers */
int iterator; /* used for iterative retrieval. value of -1 ==> not initialized. */
} Ipmacsec_keydb;
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
IP_STATIC int ipmacsec_secy_delete_sa(Ipnet_secy *secy, Ipmacsec_secy_sa *sa, Ipmacsec_dir dir);
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_handle_nlcmd(Ipnet_secy *secy, struct Ip_nlmsghdr *nlmsg,
Ip_bool non_blocking, Ipmacsec_buffer *reply, Ip_pid_t src_pid);
/*
****************************************************************************
* 8 INLINE FUNCTIONS
****************************************************************************
*/
/* Timer Utility functions */
IP_INLINE void ipmacsec_secy_set_current_time(Ipnet_secy *secy, Ipmacsec_time *tm) {
IPCOM_UNUSED_ARG(secy);
ipcom_time(tm);
}
IP_INLINE void ipmacsec_secy_copy_time(Ipnet_secy *secy, Ipmacsec_time *toTime, Ipmacsec_time *fromTime) {
IPCOM_UNUSED_ARG(secy);
*toTime = *fromTime;
}
/*
*===========================================================================
* ipmacsec_secy_fill_sc_info
*===========================================================================
* Description: Fill in the SC Info structure for the specified SC
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_INLINE int
ipmacsec_secy_fill_sc_info(Ipnet_secy *secy, Ipmacsec_secy_sc *sc, Ipmacsec_secy_sc_info *sc_info) {
ip_assert(secy);
ip_assert(sc);
ip_assert(sc_info);
ipcom_memset(sc_info, 0, sizeof(Ipmacsec_secy_sc_info));
if (sc->dir == IPMACSEC_TX) {
sc_info->status.tx.transmitting = sc->oper.transmitting;
sc_info->status.tx.encodingSA = (Ip_u8)sc->encodingSA;
sc_info->status.tx.encipheringSA = (Ip_u8)sc->encipheringSA;
} else if (sc->dir == IPMACSEC_RX) {
sc_info->status.rx.receiving = sc->oper.receiving;
} else {
return -1;
}
IPMACSEC_COPY_SCI(sc_info->sci, sc->sci);
sc_info->dir = sc->dir;
ipmacsec_secy_copy_time(secy, &sc_info->createdTime, &sc->createdTime);
ipmacsec_secy_copy_time(secy, &sc_info->startedTime, &sc->startedTime);
ipmacsec_secy_copy_time(secy, &sc_info->stoppedTime, &sc->stoppedTime);
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_fill_sa_descriptor
*===========================================================================
* Description: Fill in the SA descriptor structure for the specified SA
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_INLINE int
ipmacsec_secy_fill_sa_descriptor(Ipnet_secy *secy, Ipmacsec_secy_sa *sa, Ipmacsec_secy_sa_descriptor *sa_desc) {
IPCOM_UNUSED_ARG(secy);
ip_assert(secy);
ip_assert(sa);
ip_assert(sa->sc);
ip_assert(sa_desc);
ipcom_memset(sa_desc, 0, sizeof(Ipmacsec_secy_sa_descriptor));
sa_desc->an = (Ip_u8)sa->an;
if (sa->key)
sa_desc->key_id = sa->key->key_id;
sa_desc->ref = sa->ref;
sa_desc->dir = sa->sc->dir;
IPMACSEC_COPY_SCI(sa_desc->sci, sa->sc->sci);
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_fill_sa_info
*===========================================================================
* Description: Fill in the SA Info structure for the specified SA
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_INLINE int
ipmacsec_secy_fill_sa_info(Ipnet_secy *secy, Ipmacsec_secy_sa *sa, Ipmacsec_secy_sa_info *sa_info) {
ip_assert(secy);
ip_assert(sa);
ip_assert(sa->sc);
ip_assert(sa_info);
ipcom_memset(sa_info, 0, sizeof(Ipmacsec_secy_sa_info));
if (sa->sc->dir == IPMACSEC_TX) {
sa_info->config.tx.confidentiality =
(sa->confidentiality ? IPMACSEC_CONFIDENTIALITY_ON : IPMACSEC_CONFIDENTIALITY_OFF);
sa_info->config.tx.nextPN = sa->nextPN;
} else if (sa->sc->dir == IPMACSEC_RX) {
sa_info->config.rx.lowestPN = sa->lowestPN;
sa_info->config.rx.nextPN = sa->nextPN;
} else {
return -1;
}
sa_info->inUse = sa->inUse;
ipmacsec_secy_copy_time(secy, &sa_info->createdTime, &sa->createdTime);
ipmacsec_secy_copy_time(secy, &sa_info->startedTime, &sa->startedTime);
ipmacsec_secy_copy_time(secy, &sa_info->stoppedTime, &sa->stoppedTime);
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_retrieve_key_info
*===========================================================================
* Description: Retrieve information for the specified key and update the
* key descriptor accordingly.
* Parameters:
* Returns: Pointer to key upon success
*
*/
IP_INLINE Ipmacsec_secy_key *
ipmacsec_secy_retrieve_key_info(Ipnet_secy *secy, Ipmacsec_secy_key *key, Ipmacsec_secy_key_descriptor *key_desc) {
IPCOM_UNUSED_ARG(secy);
ip_assert(secy);
ip_assert(key);
ip_assert(key_desc);
ip_assert(key->sak.buf && key->sak.num_octets);
ipcom_memset(key_desc, 0, sizeof(Ipmacsec_secy_key_descriptor));
key_desc->key_id.val = key->key_id.val;
key_desc->created_time = key->createdTime;
key_desc->inUse = key->inUse;
if (key->receives)
key_desc->key_type |= IPMACSEC_KEYTYPE_RECEIVE;
if (key->transmits)
key_desc->key_type |= IPMACSEC_KEYTYPE_TRANSMIT;
key_desc->key_size = key->sak.num_octets;
return key;
}
/*
*===========================================================================
* ipmacsec_secy_sc_create_notify
*===========================================================================
* Description: Send notification of SC creation
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_INLINE int
ipmacsec_secy_sc_create_notify(Ipnet_secy *secy, Ipmacsec_secy_sc *sc) {
Ipmacsec_secy_sc_info sc_info;
ip_assert(secy);
ip_assert(sc);
if (secy->config.info_notify) {
(void)ipmacsec_secy_fill_sc_info(secy, sc, &sc_info);
return ipmacsec_secy_info_notify(secy, IPMACSEC_INFO_SECY_SC_CREATED, &sc_info, sizeof(sc_info));
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_sc_delete_notify
*===========================================================================
* Description: Send notification of SC deletion
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_INLINE int
ipmacsec_secy_sc_delete_notify(Ipnet_secy *secy, Ipmacsec_secy_sc *sc) {
ip_assert(secy);
ip_assert(sc);
if (secy->config.info_notify) {
return ipmacsec_secy_info_notify(secy, IPMACSEC_INFO_SECY_SC_DELETED, &sc->sci, sizeof(sc->sci));
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_sc_change_notify
*===========================================================================
* Description: Send notification of SC status change
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_INLINE int
ipmacsec_secy_sc_change_notify(Ipnet_secy *secy, Ipmacsec_secy_sc *sc) {
Ipmacsec_secy_sc_info sc_info;
ip_assert(secy);
ip_assert(sc);
if (secy->config.info_notify) {
(void)ipmacsec_secy_fill_sc_info(secy, sc, &sc_info);
return ipmacsec_secy_info_notify(secy, IPMACSEC_INFO_SECY_SC_STATUS_CHANGED, &sc_info, sizeof(sc_info));
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_sa_create_notify
*===========================================================================
* Description: Send notification of SA creation
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_INLINE int
ipmacsec_secy_sa_create_notify(Ipnet_secy *secy, Ipmacsec_secy_sa *sa) {
struct {
Ipmacsec_secy_sa_descriptor sa_desc;
Ipmacsec_secy_sa_info sa_info;
} param;
ip_assert(secy);
ip_assert(sa);
if (secy->config.info_notify) {
(void)ipmacsec_secy_fill_sa_descriptor(secy, sa, &param.sa_desc);
(void)ipmacsec_secy_fill_sa_info(secy, sa, &param.sa_info);
return ipmacsec_secy_info_notify(secy, IPMACSEC_INFO_SECY_SA_CREATED, &param, sizeof(param));
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_sa_delete_notify
*===========================================================================
* Description: Send notification of SA deletion
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_INLINE int
ipmacsec_secy_sa_delete_notify(Ipnet_secy *secy, Ipmacsec_secy_sa *sa) {
Ipmacsec_secy_sa_descriptor sa_desc;
ip_assert(secy);
ip_assert(sa);
if (secy->config.info_notify) {
(void)ipmacsec_secy_fill_sa_descriptor(secy, sa, &sa_desc);
return ipmacsec_secy_info_notify(secy, IPMACSEC_INFO_SECY_SA_DELETED, &sa_desc, sizeof(sa_desc));
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_sa_change_notify
*===========================================================================
* Description: Send notification of SA status change
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_INLINE int
ipmacsec_secy_sa_change_notify(Ipnet_secy *secy, Ipmacsec_secy_sa *sa) {
struct {
Ipmacsec_secy_sa_descriptor sa_desc;
Ipmacsec_secy_sa_info sa_info;
Ipmacsec_secy_sa_ctrl sa_ctrl;
} param;
ip_assert(secy);
ip_assert(sa);
if (secy->config.info_notify) {
(void)ipmacsec_secy_fill_sa_descriptor(secy, sa, &param.sa_desc);
(void)ipmacsec_secy_fill_sa_info(secy, sa, &param.sa_info);
ipcom_memcpy(&param.sa_ctrl, &sa->ctrl, sizeof(Ipmacsec_secy_sa_ctrl));
return ipmacsec_secy_info_notify(secy, IPMACSEC_INFO_SECY_SA_STATUS_CHANGED, &param, sizeof(param));
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_key_install_notify
*===========================================================================
* Description: Send notification of key installation
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_INLINE int
ipmacsec_secy_key_install_notify(Ipnet_secy *secy, Ipmacsec_secy_key *key) {
Ipmacsec_secy_key_descriptor key_desc;
ip_assert(secy);
ip_assert(key);
if (secy->config.info_notify) {
(void)ipmacsec_secy_retrieve_key_info(secy, key, &key_desc);
return ipmacsec_secy_info_notify(secy, IPMACSEC_INFO_SECY_KEY_INSTALLED, &key_desc, sizeof(key_desc));
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_key_remove_notify
*===========================================================================
* Description: Send notification of key removal
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_INLINE int
ipmacsec_secy_key_remove_notify(Ipnet_secy *secy, Ipmacsec_secy_key *key) {
Ipmacsec_secy_key_descriptor key_desc;
ip_assert(secy);
ip_assert(key);
if (secy->config.info_notify) {
(void)ipmacsec_secy_retrieve_key_info(secy, key, &key_desc);
return ipmacsec_secy_info_notify(secy, IPMACSEC_INFO_SECY_KEY_REMOVED, &key_desc, sizeof(key_desc));
}
return 0;
}
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipmacsec_secy_keydb_find_key
*===========================================================================
* Description: Find a key with the specified key_id
* (Later retrieve from keydb hash table - hashkey = key_id)
* Parameters:
* Returns: Pointer to retrieved key,
* or IP_NULL upon failure, or if key is not found.
*
*/
IP_STATIC Ipmacsec_secy_key *
ipmacsec_secy_keydb_find_key(Ipnet_secy *secy, Ipmacsec_secy_key_id *key_id) {
Ip_u32 i;
Ipmacsec_secy_key *k = IP_NULL;
ip_assert(secy);
ip_assert(key_id);
if (secy->priv.keydb == IP_NULL) {
IPCOM_LOG1(CRIT, "IPMACSEC-KEYDB: Failed to retrieve key from keydb for interface %s (keydb not initialized)",
secy->netif->ipcom.name);
return IP_NULL;
}
for (i=0; i < IPMACSEC_SECY_KEYDB_SIZE; i++) {
k = secy->priv.keydb->key[i];
if (k == IP_NULL)
continue;
if (k->key_id.val == key_id->val) {
return k;
}
}
return IP_NULL;
}
/*
*===========================================================================
* ipmacsec_secy_keydb_add_key
*===========================================================================
* Description: Add the specified key.
* Adding fails if matching key already exists.
* (Later add to keydb hash table - hashkey = key_id)
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_STATIC int
ipmacsec_secy_keydb_add_key(Ipnet_secy *secy, Ipmacsec_secy_key *key) {
Ip_u32 i;
Ipmacsec_secy_key *k = IP_NULL;
ip_assert(secy);
ip_assert(key);
if (secy->priv.keydb == IP_NULL) {
IPCOM_LOG1(CRIT, "IPMACSEC-KEYDB: Failed to add key to keydb for interface %s (keydb not initialized)",
secy->netif->ipcom.name);
return -1;
}
if (ipmacsec_secy_keydb_find_key(secy, &key->key_id) != IP_NULL) {
IPCOM_LOG1(ERR, "IPMACSEC-KEYDB: Failed to add key to keydb for interface %s (already exists)",
secy->netif->ipcom.name);
return -1;
}
for (i=0; i < IPMACSEC_SECY_KEYDB_SIZE; i++) {
k = secy->priv.keydb->key[i];
if (k == IP_NULL) {
k = secy->priv.keydb->key[i] = key;
break;
}
}
if (k != key) {
IPCOM_LOG2(CRIT, "IPMACSEC-KEYDB: Failed to add key (key_id=0x%llx) to keydb for interface %s (keydb full)",
key->key_id.val, secy->netif->ipcom.name);
return -1;
}
(void)ipmacsec_secy_key_install_notify(secy, key);
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG5(DEBUG, "IPMACSEC-KEYDB: Successfully installed key in keydb or interface %s "
"(key_id=0x%llx, transmits=%s, receives=%s, size=%u)",
secy->netif->ipcom.name, key->key_id.val, IPMACSEC_BOOL_STRING(key->transmits),
IPMACSEC_BOOL_STRING(key->receives), key->sak.num_octets);
#endif
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_keydb_remove_key
*===========================================================================
* Description: Remove the specified key
* (Later remove from hash table - hashkey = key_id))
* Parameters:
* Returns: 0 upon success, <0 upon error.
*
*/
IP_STATIC int
ipmacsec_secy_keydb_remove_key(Ipnet_secy *secy, Ipmacsec_secy_key *key) {
Ip_u32 i;
Ipmacsec_secy_key *k = IP_NULL;
ip_assert(secy);
ip_assert(key);
if (secy->priv.keydb == IP_NULL) {
IPCOM_LOG1(CRIT, "IPMACSEC-KEYDB: Failed to remove key from keydb for interface %s (keydb not initialized)",
secy->netif->ipcom.name);
return -1;
}
for (i=0; i < IPMACSEC_SECY_KEYDB_SIZE; i++) {
k = secy->priv.keydb->key[i];
if (k == key) {
secy->priv.keydb->key[i] = IP_NULL;
break;
}
}
if (i == IPMACSEC_SECY_KEYDB_SIZE) {
IPCOM_LOG2(ERR, "IPMACSEC-KEYDB: Failed to remove key (key_id=0x%llx) from keydb for interface %s (does not exist)",
key->key_id.val, secy->netif->ipcom.name);
return -1;
}
(void)ipmacsec_secy_key_remove_notify(secy, key);
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG6(DEBUG, "IPMACSEC-KEYDB: Successfully removed key from keydb or interface %s "
"(key_id=0x%llx, transmits=%s, receives=%s, size=%u, inUse=%s)",
secy->netif->ipcom.name, key->key_id.val, IPMACSEC_BOOL_STRING(key->transmits),
IPMACSEC_BOOL_STRING(key->receives), key->sak.num_octets, IPMACSEC_BOOL_STRING(key->inUse));
#endif
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_keydb_iter_init
*===========================================================================
* Description: Initialize the iterator so that all keys in the db can be
* subsequently retrieved.
* (Later init hash table iterator)
* Parameters:
* Returns: 0 upon success, <0 upon error.
*
*/
IP_STATIC int
ipmacsec_secy_keydb_iter_init(Ipnet_secy *secy) {
ip_assert(secy);
if (secy->priv.keydb == IP_NULL) {
IPCOM_LOG1(CRIT, "IPMACSEC-KEYDB: Failed to initialize keydb iterator for interface %s (keydb not initialized)",
secy->netif->ipcom.name);
return -1;
}
secy->priv.keydb->iterator = 0;
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_keydb_get_next_key
*===========================================================================
* Description: retrieve next entry in keydb based on iterator value
* (and update iterator)
* MUST call ipmacsec_secy_keydb_iter_init() before calling
* this iteratively in order to get correct results.
* (Later iterate the hash table)
* Parameters: secy - pointer to SecY instance
* pkey - pointer to variable to hold the retrieved key ptr
* Returns: 0 upon success, <0 upon error.
*
*/
IP_STATIC int
ipmacsec_secy_keydb_get_next_key(Ipnet_secy *secy, Ipmacsec_secy_key **pkey) {
ip_assert(secy);
ip_assert(pkey);
*pkey = IP_NULL;
if (secy->priv.keydb == IP_NULL) {
IPCOM_LOG1(CRIT, "IPMACSEC-KEYDB: Failed to get next key from keydb for interface %s (keydb not initialized)",
secy->netif->ipcom.name);
return -1;
}
if ((secy->priv.keydb->iterator < 0) || (secy->priv.keydb->iterator > IPMACSEC_SECY_KEYDB_SIZE)) {
IPCOM_LOG2(ERR, "IPMACSEC-KEYDB: Failed to get next key from keydb for interface %s (invalid iterator %d)",
secy->netif->ipcom.name, secy->priv.keydb->iterator);
secy->priv.keydb->iterator = -1;
return -1;
}
while (!*pkey && (secy->priv.keydb->iterator < IPMACSEC_SECY_KEYDB_SIZE)) {
*pkey = secy->priv.keydb->key[secy->priv.keydb->iterator];
secy->priv.keydb->iterator++;
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_retrieve_key
*===========================================================================
* Description: Retrieve the key whose key_id is specified and
update the key descriptor accordingly.
* Parameters: secy - pointer to SecY instance
* key_id - identified for key to retrieve
* key_desc - (optional) pointer to a key descriptor structure
* to be updated with key info for retrieved key
* Returns: Pointer to retrieved key on success,
* IP_NULL upon failure or if key is not found.
*
*/
IP_STATIC Ipmacsec_secy_key *
ipmacsec_secy_retrieve_key(Ipnet_secy *secy, Ipmacsec_secy_key_id *key_id, Ipmacsec_secy_key_descriptor *key_desc) {
Ipmacsec_secy_key *key = ipmacsec_secy_keydb_find_key(secy, key_id);
if (!key) {
return IP_NULL;
}
ip_assert(key->sak.buf && key->sak.num_octets);
if (key_desc) {
return ipmacsec_secy_retrieve_key_info(secy, key, key_desc);
}
return key;
}
/*
*===========================================================================
* ipmacsec_secy_sa_exhaust_notify
*===========================================================================
* Description: Send notification of PN Exhaustion for specified SA
* Parameters:
* Returns: 0 upon success, <0 upon error.
*
*/
IP_STATIC int
ipmacsec_secy_sa_exhaust_notify(Ipnet_secy *secy, Ipmacsec_secy_sa *sa) {
Ipmacsec_secy_sa_descriptor sa_desc;
ip_assert(secy);
ip_assert(sa);
ip_assert(sa->sc);
(void)ipmacsec_secy_fill_sa_descriptor(secy, sa, &sa_desc);
return ipmacsec_secy_status_notify(secy, IPMACSEC_STATUS_SECY_PN_EXHAUSTED, &sa_desc, sizeof(sa_desc));
}
/*
*===========================================================================
* ipmacsec_secy_sa_near_exhaust_notify
*===========================================================================
* Description: Send notification of PN Near Exhaustion for specified SA
* Parameters:
* Returns: 0 upon success, <0 upon error.
*
*/
IP_STATIC int
ipmacsec_secy_sa_near_exhaust_notify(Ipnet_secy *secy, Ipmacsec_secy_sa *sa) {
int ret = 0;
struct {
Ipmacsec_secy_sa_descriptor sa_desc;
Ipmacsec_secy_sa_info sa_info;
} param;
ip_assert(secy);
ip_assert(sa);
ip_assert(sa->sc);
(void)ipmacsec_secy_fill_sa_descriptor(secy, sa, &param.sa_desc);
(void)ipmacsec_secy_fill_sa_info(secy, sa, &param.sa_info);
ret = ipmacsec_secy_status_notify(secy, IPMACSEC_STATUS_SECY_PN_NEARLY_EXHAUSTED, &param, sizeof(param));
if (ret == 0) {
sa->pn_near_exhaust_notify = IP_TRUE;
}
return ret;
}
/*
*===========================================================================
* ipmacsec_secy_update_sc_sa_stats
*===========================================================================
* Description: Called whenever an SA is deleted so that the deleted SA stats
* are preserved in the SC stats.
* Parameters:
* Returns: 0
*
*/
IP_STATIC int
ipmacsec_secy_update_sc_sa_stats(Ipnet_secy *secy, Ipmacsec_secy_sc *sc, Ipmacsec_secy_sa *sa) {
#ifndef IPMACSEC_USE_STATS
IPCOM_UNUSED_ARG(secy);
IPCOM_UNUSED_ARG(sa);
#endif
ip_assert(secy);
ip_assert(sc);
ip_assert(sa);
if (sc->dir == IPMACSEC_TX) {
IPMACSEC_SC_STATS_INC(aggr.tx.OutPktsEncrypted, IPMACSEC_SA_STATS(tx.OutPktsEncrypted));
IPMACSEC_SC_STATS_INC(aggr.tx.OutPktsProtected, IPMACSEC_SA_STATS(tx.OutPktsProtected));
}
if (sc->dir == IPMACSEC_RX) {
IPMACSEC_SC_STATS_INC(aggr.rx.InPktsOK, IPMACSEC_SA_STATS(rx.InPktsOK));
IPMACSEC_SC_STATS_INC(aggr.rx.InPktsInvalid, IPMACSEC_SA_STATS(rx.InPktsInvalid));
IPMACSEC_SC_STATS_INC(aggr.rx.InPktsNotValid, IPMACSEC_SA_STATS(rx.InPktsNotValid));
IPMACSEC_SC_STATS_INC(aggr.rx.InPktsNotUsingSA, IPMACSEC_SA_STATS(rx.InPktsNotUsingSA));
IPMACSEC_SC_STATS_INC(aggr.rx.InPktsUnusedSA, IPMACSEC_SA_STATS(rx.InPktsUnusedSA));
}
return 0;
}
#ifdef IPMACSEC_USE_STATS
/*
*===========================================================================
* ipmacsec_secy_sc_add_sa_stats
*===========================================================================
* Description: Called whenever an SC stats are being retrieved so that the
* SA stats are added to the SC stats.
* Parameters:
* Returns: 0
*
*/
IP_STATIC int
ipmacsec_secy_sc_add_sa_stats(Ipnet_secy *secy, IP_CONST Ipmacsec_secy_sc *sc, Ipmacsec_secy_sc_stats *sc_stats) {
Ip_u8 an;
ip_assert(secy);
ip_assert(sc);
ip_assert(sc_stats);
for (an=0;an<secy->config.maxTxKeys;an++) {
IP_CONST Ipmacsec_secy_sa *sa = &sc->sa[an];
if (sc->dir == IPMACSEC_TX) {
sc_stats->aggr.tx.OutPktsEncrypted += IPMACSEC_SA_STATS(tx.OutPktsEncrypted);
sc_stats->aggr.tx.OutPktsProtected += IPMACSEC_SA_STATS(tx.OutPktsProtected);
}
if (sc->dir == IPMACSEC_RX) {
sc_stats->aggr.rx.InPktsOK += IPMACSEC_SA_STATS(rx.InPktsOK);
sc_stats->aggr.rx.InPktsInvalid += IPMACSEC_SA_STATS(rx.InPktsInvalid);
sc_stats->aggr.rx.InPktsNotValid += IPMACSEC_SA_STATS(rx.InPktsNotValid);
sc_stats->aggr.rx.InPktsNotUsingSA += IPMACSEC_SA_STATS(rx.InPktsNotUsingSA);
sc_stats->aggr.rx.InPktsUnusedSA += IPMACSEC_SA_STATS(rx.InPktsUnusedSA);
}
}
return 0;
}
#endif
/*
*===========================================================================
* ipmacsec_secy_rx_sc_iter_init
*===========================================================================
* Description: Initialize the RX SC iterator.
* Parameters:
* Returns: 0
*
*/
IP_STATIC int
ipmacsec_secy_rx_sc_iter_init(Ipnet_secy *secy) {
ip_assert(secy);
/* Simple for now. Later init hash table iterator */
secy->priv.rx_sc_iter = 0;
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_get_next_rx_sc
*===========================================================================
* Description: retrieve next RX SC based on iterator value
* (and update iterator)
* MUST call ipmacsec_secy_rx_sc_iter_init() before calling
* this iteratively in order to get correct results.
* Parameters:
* Returns: 0 upon success, <0 upon error.
*
*/
IP_STATIC int
ipmacsec_secy_get_next_rx_sc(Ipnet_secy *secy, Ipmacsec_secy_sc **sc) {
ip_assert(secy);
ip_assert(sc);
*sc = IP_NULL;
/* Simple for now since we have just 1 rx SC, later need hash table traversal */
if (secy->priv.rx_sc_iter > 1) {
IPCOM_LOG2(ERR, "IPMACSEC-MGMT: Failed to get next Rx SC for interface %s (invalid iterator %d)",
secy->netif->ipcom.name, secy->priv.rx_sc_iter);
return -1;
}
if (secy->priv.rx_sc_iter == 1) {
secy->priv.rx_sc_iter = 2;
return 0;
}
secy->priv.rx_sc_iter++;
*sc = secy->priv.rx_sc;
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_create_sc
*===========================================================================
* Description: SC Constructor
* Parameters:
* Returns: Pointer to created SC upon success,
* IP_NULL upon failure.
*
*/
IP_STATIC Ipmacsec_secy_sc *
ipmacsec_secy_create_sc(Ipnet_secy *secy, IP_CONST Ip_u8 *sci, Ipmacsec_dir dir) {
Ipmacsec_secy_sc *sc;
ip_assert(secy);
ip_assert(sci);
ip_assert((dir == IPMACSEC_TX) || (dir == IPMACSEC_RX));
if (IPMACSEC_INVALID_SCI(sci)) {
IPCOM_LOG2(CRIT, "IPMACSEC-MGMT: Failed to created SecY %s SC for interface %s (invalid SCI)",
IPMACSEC_DIR_STRING(dir), secy->netif->ipcom.name);
return IP_NULL;
}
sc = ipcom_malloc(sizeof(Ipmacsec_secy_sc));
if (!sc) {
IPCOM_LOG2(CRIT, "IPMACSEC-MGMT: Failed to alloc SecY %s SC for interface %s",
IPMACSEC_DIR_STRING(dir), secy->netif->ipcom.name);
return IP_NULL;
}
ipcom_memset(sc, 0, sizeof(Ipmacsec_secy_sc));
sc->crypto = ipmacsec_crypto_create(secy);
if (sc->crypto == IP_NULL) {
IPCOM_LOG2(CRIT, "IPMACSEC-CRYPTO: Failed to create Crypto interface of SecY %s SC for interface %s",
IPMACSEC_DIR_STRING(dir), secy->netif->ipcom.name);
ipcom_free(sc);
return IP_NULL;
}
IPMACSEC_COPY_SCI(sc->sci, sci);
sc->dir = dir;
sc->newAN = sc->oldAN = IPMACSEC_MAX_SA; /* init to invalid value */
ipmacsec_secy_set_current_time(secy, &sc->createdTime);
ipmacsec_secy_copy_time(secy, &sc->startedTime, &sc->createdTime);
ipmacsec_secy_copy_time(secy, &sc->stoppedTime, &sc->createdTime);
sc->enabled = IP_TRUE;
(void)ipmacsec_secy_sc_create_notify(secy, sc);
return sc;
}
/*
*===========================================================================
* ipmacsec_secy_delete_sc
*===========================================================================
* Description: SC Destructor
* Parameters:
* Returns: 0
*
*/
IP_STATIC int
ipmacsec_secy_delete_sc(Ipnet_secy *secy, Ipmacsec_secy_sc *sc) {
Ipmacsec_dir dir;
Ip_u8 an;
Ip_u16 maxAN;
ip_assert(secy);
ip_assert(sc);
dir = sc->dir;
if (dir == IPMACSEC_RX) {
maxAN = secy->config.maxRxKeys;
} else {
maxAN = secy->config.maxTxKeys;
}
ip_assert(maxAN <= IPMACSEC_MAX_SA);
for (an = 0; an < maxAN; an++) {
Ipmacsec_secy_sa *sa = &sc->sa[an];
(void)ipmacsec_secy_delete_sa(secy, sa, dir);
}
sc->enabled = IP_FALSE;
/* Delete the crypto interface */
(void)ipmacsec_crypto_delete(secy, sc->crypto);
/* Now update the CP stats so that historic SC stats are preserved */
(void)ipmacsec_secy_cp_update_statistics(secy->netif);
/* Send Delete notification */
(void)ipmacsec_secy_sc_delete_notify(secy, sc);
ipcom_free(sc);
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(INFO, "IPMACSEC-MGMT: Deleted SecY %s SC for interface %s",
IPMACSEC_DIR_STRING(dir), secy->netif->ipcom.name);
#endif
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_create_sa
*===========================================================================
* Description: SA Constructor
* Parameters:
* Returns: Pointer to created SA upon success,
* IP_NULL upon failure.
*
*/
IP_STATIC Ipmacsec_secy_sa *
ipmacsec_secy_create_sa(Ipnet_secy *secy, Ipmacsec_secy_sc *sc,
Ip_u8 an, Ipmacsec_secy_sa_ref ref,
Ip_u32 nextPN, Ip_u32 lowestPN,
Ipmacsec_confidentiality confidentiality,
Ipmacsec_secy_key *key, Ipmacsec_dir dir) {
Ipmacsec_secy_sa *sa;
ip_assert(secy);
ip_assert(secy->config.maxTxKeys <= IPMACSEC_MAX_SA);
ip_assert(secy->config.maxRxKeys <= IPMACSEC_MAX_SA);
ip_assert(sc);
ip_assert(sc->enabled);
ip_assert(sc->crypto);
ip_assert(an < IPMACSEC_MAX_SA);
ip_assert(IPMACSEC_CONFIDENTIALITY_VALID(confidentiality));
ip_assert((dir == IPMACSEC_TX) || (dir == IPMACSEC_RX));
ip_assert(sc->dir == dir);
if ((dir == IPMACSEC_TX) && (an >= secy->config.maxTxKeys)){
IPCOM_LOG3(ERR, "IPMACSEC-MGMT: Cannot create Tx SA %u (> maxTxKeys=%u) for interface %s.",
an, secy->config.maxTxKeys, secy->netif->ipcom.name);
return IP_NULL;
}
if ((dir == IPMACSEC_RX) && (an >= secy->config.maxRxKeys)) {
IPCOM_LOG3(ERR, "IPMACSEC-MGMT: Cannot create Rx SA %u (> maxTxKeys=%u) for interface %s.",
an, secy->config.maxRxKeys, secy->netif->ipcom.name);
return IP_NULL;
}
if (!key) {
IPCOM_LOG3(ERR, "IPMACSEC-MGMT: Cannot create %s SA %u (Key not supplied) for interface %s.",
IPMACSEC_DIR_STRING(dir), an, secy->netif->ipcom.name);
return IP_NULL;
}
if (key->inUse) {
IPCOM_LOG4(ERR, "IPMACSEC-MGMT: Cannot create %s SA %u (Key %llu in use) for interface %s.",
IPMACSEC_DIR_STRING(dir), an, key->key_id.val, secy->netif->ipcom.name);
return IP_NULL;
}
if ((dir == IPMACSEC_TX) && !key->transmits) {
IPCOM_LOG2(ERR, "IPMACSEC-MGMT: Cannot create Tx SA %u (Key not properly installed) for interface %s.",
an, secy->netif->ipcom.name);
return IP_NULL;
}
if ((dir == IPMACSEC_RX) && !key->receives) {
IPCOM_LOG2(ERR, "IPMACSEC-MGMT: Cannot create Rx SA %u (Key not properly installed) for interface %s.",
an, secy->netif->ipcom.name);
return IP_NULL;
}
if (lowestPN > nextPN) {
IPCOM_LOG5(INFO, "IPMACSEC-MGMT: Cannot create %s SA %u (lowestPN %u > nextPN %u) for interface %s",
IPMACSEC_DIR_STRING(dir), an, lowestPN, nextPN,secy->netif->ipcom.name);
return IP_NULL;
}
sa = &sc->sa[an];
(void)ipmacsec_secy_delete_sa(secy, sa, dir);
/* Init the crypto context */
if (sc->crypto->init_cipher(sc->crypto, an, &key->sak) != 0) {
IPCOM_LOG3(INFO, "IPMACSEC-MGMT: Cannot initialize crypto of %s SA %u for interface %s",
IPMACSEC_DIR_STRING(dir), an, secy->netif->ipcom.name);
return IP_NULL;
}
sa->sc = sc;
sa->an = an;
if (nextPN == 0) {
/* Not supplied, so start at 1 */
sa->nextPN = 1;
} else {
sa->nextPN = nextPN;
}
sa->lowestPN = lowestPN;
if (dir == IPMACSEC_TX) {
if (confidentiality == IPMACSEC_CONFIDENTIALITY_UNSPECIFIED) {
sa->confidentiality = ipcom_sysvar_get_as_int(IPMACSEC_SYSVAR_SECY_CONFIDENTIALITY, 0);
} else {
sa->confidentiality = ((confidentiality == IPMACSEC_CONFIDENTIALITY_ON) ? 1 : 0);
}
}
sa->key = key;
key->inUse = IP_TRUE;
sa->ref = ref;
sa->pn_threshold = secy->config.pn_threshold;
ipmacsec_secy_set_current_time(secy, &sa->createdTime);
ipmacsec_secy_copy_time(secy, &sa->startedTime, &sa->createdTime);
ipmacsec_secy_copy_time(secy, &sa->stoppedTime, &sa->createdTime);
(void)ipmacsec_secy_sa_create_notify(secy, sa);
return sa;
}
/*
*===========================================================================
* ipmacsec_secy_delete_sa
*===========================================================================
* Description: SA Destructor
* Parameters:
* Returns: 0
*
*/
IP_STATIC int
ipmacsec_secy_delete_sa(Ipnet_secy *secy, Ipmacsec_secy_sa *sa, Ipmacsec_dir dir) {
Ip_u8 an;
#ifndef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_UNUSED_ARG(dir);
#endif
ip_assert(secy);
ip_assert(sa);
if (!sa->sc) {
goto delete_done; /* Already deleted, never created, or partially created/deleted. Finish it */
}
/* Before deleting, add old sa count to sc counts */
(void)ipmacsec_secy_update_sc_sa_stats(secy, sa->sc, sa); /* 10.7.9; 10.7.18 */
/* We also need to update SA/SC status (so counts are updated) */
if (sa->sc->dir == IPMACSEC_RX) {
(void)ipmacsec_secy_enable_rx_sa(secy, sa, IP_FALSE);
if (sa->sc->oldAN == sa->an) {
sa->sc->oldAN = IPMACSEC_MAX_SA; /* Invalidate oldAN */
sa->sc->interleave_time = 0;
}
} else
(void)ipmacsec_secy_enable_tx_sa(secy, sa, IP_FALSE);
/* destroy the crypto context */
if (sa->sc->crypto)
(void)sa->sc->crypto->destroy_cipher(sa->sc->crypto, (Ip_u8)sa->an);
(void)ipmacsec_secy_sa_delete_notify(secy, sa);
an = (Ip_u8)sa->an;
if (sa->key) {
sa->key->inUse = IP_FALSE;
}
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG3(INFO, "IPMACSEC-MGMT: Deleted SecY %s SA %u for interface %s",
IPMACSEC_DIR_STRING(dir), an, secy->netif->ipcom.name);
#endif
delete_done:
ipcom_memset(sa, 0, sizeof(Ipmacsec_secy_sa));
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_start_tx_sc
*===========================================================================
* Description: Start the Tx SC if not already started
* Parameters:
* Returns: 0
*
*/
IP_STATIC int
ipmacsec_secy_start_tx_sc(Ipnet_secy *secy, Ipmacsec_secy_sc *sc, Ip_u8 an) {
ip_assert(secy);
ip_assert(sc);
ip_assert(sc == secy->priv.tx_sc);
ip_assert(sc->dir == IPMACSEC_TX);
sc->encodingSA = an; /* 10.5.1 */
if (!sc->oper.transmitting) {
ipmacsec_secy_set_current_time(secy, &sc->startedTime);
sc->oper.transmitting = IP_TRUE;
(void)ipmacsec_secy_sc_change_notify(secy, sc);
(void)ipmacsec_secy_cp_update_status(secy, IPMACSEC_SECY_MAC_OPER_DEFAULT); /* Possible CP Status Change */
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_stop_tx_sc
*===========================================================================
* Description: Stop the Tx SC is the encoding SA matches supplied AN
* Parameters:
* Returns: 0
*
*/
IP_STATIC int
ipmacsec_secy_stop_tx_sc(Ipnet_secy *secy, Ipmacsec_secy_sc *sc, Ip_u8 an) {
ip_assert(secy);
ip_assert(sc);
ip_assert(sc == secy->priv.tx_sc);
ip_assert(sc->dir == IPMACSEC_TX);
if (sc->oper.transmitting && sc->encodingSA == an) {
ipmacsec_secy_set_current_time(secy, &sc->stoppedTime);
sc->oper.transmitting = IP_FALSE;
(void)ipmacsec_secy_sc_change_notify(secy, sc);
(void)ipmacsec_secy_cp_update_status(secy, IPMACSEC_SECY_MAC_OPER_DEFAULT); /* Possible CP Status Change */
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_start_rx_sc
*===========================================================================
* Description: Start the Rx SC if not already started
* Parameters:
* Returns: 0
*
*/
IP_STATIC int
ipmacsec_secy_start_rx_sc(Ipnet_secy *secy, Ipmacsec_secy_sc *sc, Ip_u8 an) {
IPCOM_UNUSED_ARG(an);
ip_assert(secy);
ip_assert(sc);
ip_assert(sc->dir == IPMACSEC_RX);
sc->numActiveRxSAs++;
ip_assert(sc->numActiveRxSAs <= secy->config.maxRxKeys);
if (!sc->oper.receiving) {
ipmacsec_secy_set_current_time(secy, &sc->startedTime);
sc->oper.receiving = IP_TRUE;
(void)ipmacsec_secy_sc_change_notify(secy, sc);
secy->oper.numActiveRxSCs++;
if (secy->oper.numActiveRxSCs == 1) {
(void)ipmacsec_secy_cp_update_status(secy, IPMACSEC_SECY_MAC_OPER_DEFAULT); /* Possible CP Status Change */
}
ip_assert(secy->oper.numActiveRxSCs <= secy->config.maxRxSCs);
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_stop_rx_sc
*===========================================================================
* Description: Stop the Rx SC is the encoding SA matches supplied AN
* Parameters:
* Returns: 0
*
*/
IP_STATIC int
ipmacsec_secy_stop_rx_sc(Ipnet_secy *secy, Ipmacsec_secy_sc *sc, Ip_u8 an) {
IPCOM_UNUSED_ARG(an);
ip_assert(secy);
ip_assert(sc);
ip_assert(sc->dir == IPMACSEC_RX);
ip_assert(sc->numActiveRxSAs);
sc->numActiveRxSAs--;
if (sc->numActiveRxSAs == 0) {
ip_assert(sc->oper.receiving);
ipmacsec_secy_set_current_time(secy, &sc->stoppedTime);
sc->oper.receiving = IP_FALSE;
(void)ipmacsec_secy_sc_change_notify(secy, sc);
ip_assert(secy->oper.numActiveRxSCs);
secy->oper.numActiveRxSCs--;
if (secy->oper.numActiveRxSCs == 0) {
/* Possible CP Status Change */
(void)ipmacsec_secy_cp_update_status(secy, IPMACSEC_SECY_MAC_OPER_DEFAULT);
}
}
return 0;
}
#ifdef IPMACSEC_USE_STATS
/*
*===========================================================================
* ipmacsec_secy_reset_stats
*===========================================================================
* Description: Reset the SecY and CP statistics
* Parameters:
* Returns: 0 upon success, <0 upon error
*
*/
IP_STATIC int
ipmacsec_secy_reset_stats(Ipnet_secy *secy) {
ip_assert(secy);
ipcom_memset(&secy->stats, 0, sizeof(secy->stats));
ipcom_memset(&secy->controlled_port.stats, 0, sizeof(secy->controlled_port.stats));
return 0;
}
#endif
/*
*===========================================================================
* ipmacsec_secy_update_rx_sa_lowestPN
*===========================================================================
* Description: Update Rx SA lowestPN based on replayWindow and new nextPN value
* Parameters:
* Returns: 0
*
*/
IP_INLINE int
ipmacsec_secy_update_rx_sa_lowestPN(Ipnet_secy *secy, Ipmacsec_secy_sa *sa) {
ip_assert(secy);
ip_assert(sa);
/* 10.6.5 */
if (sa->nextPN > secy->ctrl.replayWindow) {
sa->lowestPN = IP_MAX(sa->lowestPN, (sa->nextPN - secy->ctrl.replayWindow));
}
return 0;
}
/*
****************************************************************************
* 10 PRIVATE FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipmacsec_secy_keydb_init
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_keydb_init(Ipnet_secy *secy) {
ip_assert(secy);
if (secy->priv.keydb == IP_NULL) {
secy->priv.keydb = ipcom_malloc(sizeof(Ipmacsec_keydb));
}
if (secy->priv.keydb == IP_NULL) {
IPCOM_LOG0(CRIT, "IPMACSEC-KEYDB: Failed to allocate keydb");
return -1;
}
ipcom_memset(secy->priv.keydb, 0, sizeof(Ipmacsec_keydb));
secy->priv.keydb->iterator = -1;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG0(DEBUG2, "IPMACSEC-MGMT: Successfully created keydb");
#endif
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_keydb_destroy
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_keydb_destroy(Ipnet_secy *secy) {
ip_assert(secy);
if (secy->priv.keydb == IP_NULL) {
ipcom_free(secy->priv.keydb);
}
secy->priv.keydb = IP_NULL;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG0(DEBUG2, "IPMACSEC-MGMT: Successfully destroyed keydb");
#endif
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_create_tx_sc
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_sc *
ipmacsec_secy_create_tx_sc(Ipnet_secy *secy) {
Ipmacsec_secy_sc *sc;
ip_assert(secy);
sc = secy->priv.tx_sc;
if (sc) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(DEBUG, "IPMACSEC-MGMT: SecY Tx SC already created for interface %s", secy->netif->ipcom.name);
#endif
return sc;
}
if (IPMACSEC_INVALID_SCI(secy->config.sci)) {
if (secy->ctrl.useES) {
/* Init SCI from MAC address and default port 00-01. (A5(SCI); 8.2.1; 9.5) */
IPNET_SET_ETHADDR(secy->config.sci, secy->netif->ipcom.link_addr);
secy->config.sci[6] = 0x00;
secy->config.sci[7] = (Ip_u8)(secy->ctrl.useSCB ? 0x00 : 0x01); /* 9.5 */
IPCOM_LOG9(WARNING,
"IPMACSEC-MGMT: SecY SCI value (%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x) used for interface %s ",
secy->config.sci[0], secy->config.sci[1], secy->config.sci[2], secy->config.sci[3],
secy->config.sci[4], secy->config.sci[5], secy->config.sci[6], secy->config.sci[7],
secy->netif->ipcom.name);
} else {
IPCOM_LOG1(CRIT, "IPMACSEC-MGMT: SecY SCI not configured for interface %s ",
secy->netif->ipcom.name);
return IP_NULL;
}
}
sc = ipmacsec_secy_create_sc(secy, secy->config.sci, IPMACSEC_TX);
if (!sc) {
IPCOM_LOG1(CRIT, "IPMACSEC-MGMT: Failed to create SecY Tx SC for interface %s", secy->netif->ipcom.name);
return IP_NULL;
}
secy->priv.tx_sc = sc;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(INFO, "IPMACSEC-MGMT: Successfully created SecY Tx SC for interface %s", secy->netif->ipcom.name);
#endif
return sc;
}
/*
*===========================================================================
* ipmacsec_secy_delete_tx_sc
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE
int ipmacsec_secy_delete_tx_sc(Ipnet_secy *secy) {
ip_assert(secy);
if (!secy->priv.tx_sc) {
return -1;
}
ip_assert(secy->priv.tx_sc->dir == IPMACSEC_TX);
(void)ipmacsec_secy_delete_sc(secy, secy->priv.tx_sc);
secy->priv.tx_sc = IP_NULL;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(INFO, "IPMACSEC-MGMT: Deleted SecY Tx SC for interface %s", secy->netif->ipcom.name);
#endif
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_create_tx_sa
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_sa *
ipmacsec_secy_create_tx_sa(Ipnet_secy *secy, Ip_u8 an, Ipmacsec_secy_sa_ref ref,
Ip_u32 nextPN, Ipmacsec_confidentiality confidentiality,
Ipmacsec_secy_key *key) {
Ipmacsec_secy_sc *sc;
Ipmacsec_secy_sa *sa;
ip_assert(secy);
sc = ipmacsec_secy_get_tx_sc(secy);
if (!sc) {
IPCOM_LOG2(ERR, "IPMACSEC-MGMT: Cannot create Tx SA %u (No Tx SC) for interface %s.",
an, secy->netif->ipcom.name);
return IP_NULL;
}
ip_assert(sc->dir == IPMACSEC_TX);
sa = ipmacsec_secy_create_sa(secy, sc, an, ref, nextPN, 0 /* no lowestPN */,
confidentiality, key, IPMACSEC_TX);
if (!sa) {
IPCOM_LOG2(ERR, "IPMACSEC-MGMT: Failed to create Tx SA %u for interface %s.",
an, secy->netif->ipcom.name);
return IP_NULL;
}
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(INFO, "IPMACSEC-MGMT: Successfully created SecY Tx SA %u for interface %s",
an, secy->netif->ipcom.name);
#endif
return sa;
}
/*
*===========================================================================
* ipmacsec_secy_delete_tx_sa
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_delete_tx_sa(Ipnet_secy *secy, Ipmacsec_secy_sa *sa) {
return ipmacsec_secy_delete_sa(secy, sa, IPMACSEC_TX);
}
/*
*===========================================================================
* ipmacsec_secy_enable_tx_sa
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_enable_tx_sa(Ipnet_secy *secy, Ipmacsec_secy_sa *sa, Ip_bool enableTransmit) {
Ipmacsec_secy_sc *sc;
ip_assert(secy);
ip_assert(sa);
sc = sa->sc;
ip_assert(sc);
ip_assert(sc->dir == IPMACSEC_TX);
sa->ctrl.tx.enableTx = (enableTransmit ? IPMACSEC_ENABLEFLAG_ON : IPMACSEC_ENABLEFLAG_OFF);
if (enableTransmit) {
if (!sa->inUse) {
Ip_u8 an;
if (!sa->key || (sa->nextPN == 0)) {
IPCOM_LOG3(ERR, "IPMACSEC-MGMT: Cannot set enableTransmit for Tx SA %u (Invalid SA %s) for interface %s.",
sa->an, (!sa->key ? "key" : "nextPN"), secy->netif->ipcom.name);
return -1;
}
/* enable this SA */
sa->inUse = IP_TRUE;
(void)ipmacsec_secy_sa_change_notify(secy, sa);
ipmacsec_secy_set_current_time(secy, &sa->startedTime);
ipmacsec_secy_start_tx_sc(secy, sc, (Ip_u8)sa->an);
/* Now disable any other enabled SAs, since only 1 tx SA can be enabled at one time */
for (an=0;an<secy->config.maxTxKeys;an++) {
if ((an == sa->an) || (!sc->sa[an].inUse))
continue;
(void)ipmacsec_secy_enable_tx_sa(secy, &sc->sa[an], IP_FALSE);
}
}
} else {
if (sa->inUse) {
sa->inUse = IP_FALSE;
(void)ipmacsec_secy_sa_change_notify(secy, sa);
ipmacsec_secy_set_current_time(secy, &sa->stoppedTime);
ipmacsec_secy_stop_tx_sc(secy, sc, (Ip_u8)sa->an);
}
}
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG3(INFO, "IPMACSEC-MGMT: enableTransmit set to %s for SecY Tx SA %u for interface %s",
IPMACSEC_BOOL_STRING(enableTransmit), sa->an, secy->netif->ipcom.name);
#endif
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_create_rx_sc
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_sc *
ipmacsec_secy_create_rx_sc(Ipnet_secy *secy, IP_CONST Ip_u8 *sci) {
Ipmacsec_secy_sc *sc;
ip_assert(secy);
ip_assert(secy->oper.numCreatedRxSCs <= secy->config.maxRxSCs);
sc = ipmacsec_secy_lookup_rx_sc(secy, sci);
if (sc) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: Failed to create RX SC (already exists) for interface %s",
secy->netif->ipcom.name);
#endif
return IP_NULL;
}
if (secy->oper.numCreatedRxSCs == secy->config.maxRxSCs) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(ERR, "IPMACSEC-MGMT: Failed to create RX SC (maxRxSCs %u reached) for interface %s",
secy->config.maxRxSCs, secy->netif->ipcom.name);
#endif
return IP_NULL;
}
sc = ipmacsec_secy_create_sc(secy, sci, IPMACSEC_RX);
if (!sc) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: Failed to create SecY Rx SC (alloc failed) for interface %s", secy->netif->ipcom.name);
#endif
return IP_NULL;
}
/* For now we support only one rx SC, so this is simple. Later we need a hash lookup/insert */
secy->oper.numCreatedRxSCs++;
secy->priv.rx_sc = sc;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(INFO, "IPMACSEC-MGMT: Successfully created SecY Rx SC (#%u) for interface %s",
secy->oper.numCreatedRxSCs, secy->netif->ipcom.name);
#endif
return sc;
}
/*
*===========================================================================
* ipmacsec_secy_delete_rx_sc
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_delete_rx_sc(Ipnet_secy *secy, Ipmacsec_secy_sc *sc) {
int rc = 0;
ip_assert(secy);
ip_assert(secy->oper.numCreatedRxSCs);
secy->oper.numCreatedRxSCs--;
/* Only 1 rx_sc for now. Later need to remove from hash */
ip_assert(secy->priv.rx_sc == sc);
rc = ipmacsec_secy_delete_sc(secy, sc);
secy->priv.rx_sc = IP_NULL;
return rc;
}
/*
*===========================================================================
* ipmacsec_secy_delete_rx_sc_all
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_delete_rx_sc_all(Ipnet_secy *secy) {
int rc =0;
ip_assert(secy);
/* Only 1 rx_sc for now. Later need to delete each and every one */
if (secy->priv.rx_sc)
rc = ipmacsec_secy_delete_rx_sc(secy, secy->priv.rx_sc);
ip_assert(secy->oper.numCreatedRxSCs == 0);
return rc;
}
/*
*===========================================================================
* ipmacsec_secy_create_rx_sa
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_sa *
ipmacsec_secy_create_rx_sa(Ipnet_secy *secy, Ipmacsec_secy_sc *sc,
Ip_u8 an, Ipmacsec_secy_sa_ref ref,
Ip_u32 nextPN, Ip_u32 lowestPN,
Ipmacsec_secy_key *key) {
Ipmacsec_secy_sa *sa;
ip_assert(secy);
ip_assert(sc);
ip_assert(sc->dir == IPMACSEC_RX);
sa = ipmacsec_secy_create_sa(secy, sc, an, ref, nextPN, lowestPN,
IPMACSEC_CONFIDENTIALITY_UNSPECIFIED, key, IPMACSEC_RX);
if (!sa) {
IPCOM_LOG2(ERR, "IPMACSEC-MGMT: Failed to create Rx SA %u for interface %s.",
an, secy->netif->ipcom.name);
return IP_NULL;
}
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(INFO, "IPMACSEC-MGMT: Successfully created SecY Rx SA %u for interface %s",
an, secy->netif->ipcom.name);
#endif
return sa;
}
/*
*===========================================================================
* ipmacsec_secy_delete_rx_sa
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_delete_rx_sa(Ipnet_secy *secy, Ipmacsec_secy_sa *sa) {
return ipmacsec_secy_delete_sa(secy, sa, IPMACSEC_RX);
}
/*
*===========================================================================
* ipmacsec_secy_enable_rx_sa
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_enable_rx_sa(Ipnet_secy *secy, Ipmacsec_secy_sa *sa, Ip_bool enableReceive) {
Ipmacsec_secy_sc *sc;
ip_assert(secy);
ip_assert(sa);
sc = sa->sc;
ip_assert(sc);
ip_assert(sc->dir == IPMACSEC_RX);
sa->ctrl.rx.enableRx = (enableReceive ? IPMACSEC_ENABLEFLAG_ON : IPMACSEC_ENABLEFLAG_OFF);
if (enableReceive) {
if (!sa->inUse) {
if (!sa->key || (sa->nextPN == 0)) {
IPCOM_LOG3(ERR, "IPMACSEC-MGMT: Cannot set enableReceive for Rx SA %u (Invalid SA %s) for interface %s.",
sa->an, (!sa->key ? "key" : "nextPN"), secy->netif->ipcom.name);
return -1;
}
sa->inUse = IP_TRUE;
(void)ipmacsec_secy_sa_change_notify(secy, sa);
ipmacsec_secy_set_current_time(secy, &sa->startedTime);
ipmacsec_secy_start_rx_sc(secy, sc, (Ip_u8)sa->an);
}
} else {
if (sa->inUse) {
sa->inUse = IP_FALSE;
(void)ipmacsec_secy_sa_change_notify(secy, sa);
ipmacsec_secy_set_current_time(secy, &sa->stoppedTime);
ipmacsec_secy_stop_rx_sc(secy, sc, (Ip_u8)sa->an);
}
}
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG3(INFO, "IPMACSEC-MGMT: enableReceive set to %s for SecY Tx SA %u for interface %s",
IPMACSEC_BOOL_STRING(enableReceive), sa->an, secy->netif->ipcom.name);
#endif
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_update_sa_nextPN
*===========================================================================
* Description: Update SA nextPN based on supplied new nextPN value
* Parameters:
* Returns: 0 upon success, <0 upon error.
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_update_sa_nextPN(Ipnet_secy *secy, Ipmacsec_secy_sa *sa, Ip_u32 nextPN) {
ip_assert(secy);
ip_assert(sa);
ip_assert(sa->sc);
sa->nextPN = nextPN;
if (sa->nextPN == 0) {
if ((sa->sc->dir == IPMACSEC_TX) && secy->ctrl.protectFrames) {
/* A.8(GEN-5); 7.1.3; 10.5.2 */
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG3(DEBUG2,
"IPMACSEC-MGMT: nextPN 0 for %s SA (%u), CP MAC_Operational now set to FALSE for interface %s",
IPMACSEC_DIR_STRING(sa->sc->dir), sa->an, secy->netif->ipcom.name);
#endif
/* Notify via LMI of SA PN exhaustion */
(void)ipmacsec_secy_sa_exhaust_notify(secy, sa);
/* MAC_Operational should transition to FALSE for the CP */
(void)ipmacsec_secy_cp_update_status(secy, IPMACSEC_SECY_MAC_OPER_OFF);
return -1;
} else {
sa->nextPN = 1;
return 0;
}
}
if ((sa->sc->dir == IPMACSEC_TX) && !sa->pn_near_exhaust_notify && (sa->nextPN >= sa->pn_threshold)) {
/* (A.11-KAY-9, 8.2.7) */
(void)ipmacsec_secy_sa_near_exhaust_notify(secy, sa);
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_update_rx_sa_PNs
*===========================================================================
* Description: Update Rx SA nextPN and/or lowestPN based on Rx PN value
* Parameters: rxPN value of 0 means do not update
* Returns: 0
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_update_rx_sa_PNs(Ipnet_secy *secy, Ipmacsec_secy_sa *sa, Ip_u32 rxPN) {
ip_assert(secy);
ip_assert(sa);
ip_assert(rxPN);
ip_assert(rxPN >= sa->lowestPN);
ip_assert(sa->sc);
ip_assert(sa->sc->dir == IPMACSEC_RX);
/* 10.6.5 */
if (rxPN >= sa->nextPN) {
(void)ipmacsec_secy_update_sa_nextPN(secy, sa, rxPN+1);
}
(void)ipmacsec_secy_update_rx_sa_lowestPN(secy, sa);
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_update_rx_sa
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_update_rx_sa(Ipnet_secy *secy, Ipmacsec_secy_sa *sa,
Ip_u32 nextPN, Ip_u32 lowestPN) {
ip_assert(secy);
ip_assert(sa);
if (!nextPN && !lowestPN) {
/* What are we trying to update here?! */
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(INFO, "IPMACSEC-MGMT: Both nextPN and lowestPN are 0 for SecY Rx SA %u for interface %s",
sa->an, secy->netif->ipcom.name);
#endif
return -1;
}
if (lowestPN && (lowestPN > nextPN)) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG4(INFO, "IPMACSEC-MGMT: Invalid lowestPN (%u > nextPN %u) SecY Rx SA %u for interface %s",
lowestPN, nextPN, sa->an, secy->netif->ipcom.name);
#endif
return -1;
}
/* 10.7.15 */
if (lowestPN > sa->lowestPN) {
sa->lowestPN = sa->ctrl.rx.updtLowestPN = lowestPN;
}
if (nextPN > sa->nextPN) {
sa->nextPN = sa->ctrl.rx.updtNextPN = nextPN;
if (!lowestPN) {
(void)ipmacsec_secy_update_rx_sa_lowestPN(secy, sa);
}
}
(void)ipmacsec_secy_sa_change_notify(secy, sa);
return 0;
}
/*
*===========================================================================
* ipmacsec_sak_copy
*===========================================================================
* Description: see "ipmacsec_h.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_sak_copy(Ipmacsec_secy_sak *to_sak, IP_CONST Ipmacsec_secy_sak *from_sak) {
if (!to_sak)
return -1;
to_sak->buf = IP_NULL;
to_sak->num_octets = 0;
if (!from_sak)
return -1;
to_sak->buf = ipcom_malloc(from_sak->num_octets);
if (!to_sak->buf) {
to_sak->num_octets = 0;
return -1;
}
to_sak->num_octets = from_sak->num_octets;
ipcom_memcpy(to_sak->buf, from_sak->buf, to_sak->num_octets);
return 0;
}
/*
*===========================================================================
* ipmacsec_sak_delete
*===========================================================================
* Description: see "ipmacsec_h.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_sak_delete(Ipmacsec_secy_sak *sak) {
if (!sak) {
return -1;
}
if (sak->buf) {
/* For security reasons, erase the SAK keying material before freeing it up */
ipcom_memset(sak->buf, 0, sak->num_octets);
ipcom_free(sak->buf);
sak->buf = IP_NULL;
}
sak->num_octets = 0;
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_create_key
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_key *
ipmacsec_secy_create_key(Ipnet_secy *secy,
Ipmacsec_secy_sak *sak, Ipmacsec_secy_key_id *key_id) {
Ipmacsec_secy_key *key;
ip_assert(secy);
if (!sak) {
return IP_NULL;
}
if (!sak->buf || !sak->num_octets) {
return IP_NULL;
}
key = ipcom_malloc(sizeof(Ipmacsec_secy_key));
if (!key) {
return IP_NULL;
}
ipcom_memset(key, 0, sizeof(Ipmacsec_secy_key));
key->sak.buf = sak->buf;
key->sak.num_octets = sak->num_octets;
ipmacsec_secy_set_current_time(secy, &key->createdTime);
if (key_id) {
key->key_id.val = key_id->val;
}
return key;
}
/*
*===========================================================================
* ipmacsec_secy_delete_key
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_delete_key(Ipnet_secy *secy, Ipmacsec_secy_key *key) {
IPCOM_UNUSED_ARG(secy);
ip_assert(secy);
ip_assert(key);
ip_assert(!key->inUse);
(void)ipmacsec_sak_delete(&key->sak);
ipcom_memset(key, 0, sizeof(Ipmacsec_secy_key));
ipcom_free(key);
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_install_key
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_install_key(Ipnet_secy *secy, Ipmacsec_secy_key *key, Ipmacsec_keytype keytype) {
ip_assert(secy);
ip_assert(key);
if ((keytype & (IPMACSEC_KEYTYPE_TRANSMIT | IPMACSEC_KEYTYPE_RECEIVE)) == 0) {
return -1;
}
if (!key->sak.buf || !key->sak.num_octets) {
return -1;
}
if (keytype & IPMACSEC_KEYTYPE_TRANSMIT)
key->transmits = IP_TRUE;
if (keytype & IPMACSEC_KEYTYPE_RECEIVE)
key->receives = IP_TRUE;
return ipmacsec_secy_keydb_add_key(secy, key);
}
/*
*===========================================================================
* ipmacsec_secy_remove_key
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_key *
ipmacsec_secy_remove_key(Ipnet_secy *secy, Ipmacsec_secy_key_id *key_id) {
Ipmacsec_secy_key *key;
ip_assert(secy);
/* retrieve key from keydb (fail if it doesn't exist) */
key = ipmacsec_secy_keydb_find_key(secy, key_id);
if (!key) {
return IP_NULL;
}
if (key->inUse) {
return IP_NULL;
}
key->transmits = IP_FALSE;
key->receives = IP_FALSE;
key->inUse = IP_FALSE; /* redundant */
if (ipmacsec_secy_keydb_remove_key(secy, key) != 0) {
return IP_NULL;
}
return key;
}
/*
*===========================================================================
* ipmacsec_secy_delete_all_keys
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_delete_all_keys(Ipnet_secy *secy, Ip_bool ignore_in_use) {
Ipmacsec_secy_key *key;
ip_assert(secy);
/* Delete all keys in hash table and clear hash table */
if (ipmacsec_secy_keydb_iter_init(secy) < 0) {
return -1;
}
for (;;) {
key = IP_NULL;
if (ipmacsec_secy_keydb_get_next_key(secy, &key) < 0) {
return -1;
}
if (!key)
break; /* No more keys */
if (!key->inUse || ignore_in_use) {
(void)ipmacsec_secy_keydb_remove_key(secy, key);
(void)ipmacsec_secy_delete_key(secy, key);
}
}
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_get_tx_sc
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_sc *
ipmacsec_secy_get_tx_sc(Ipnet_secy *secy) {
Ipmacsec_secy_sc *sc;
ip_assert(secy);
sc = secy->priv.tx_sc;
return sc;
}
/*
*===========================================================================
* ipmacsec_secy_get_tx_sa
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_sa *
ipmacsec_secy_get_tx_sa(Ipnet_secy *secy) {
Ipmacsec_secy_sc *sc;
Ipmacsec_secy_sa *sa;
ip_assert(secy);
sc = ipmacsec_secy_get_tx_sc(secy);
if (!sc || !sc->enabled) {
return IP_NULL;
}
if (sc->encodingSA >= IPMACSEC_MAX_SA) {
return IP_NULL;
}
if (!secy->config.maxTxKeys || (sc->encodingSA > (Ip_u32)(secy->config.maxTxKeys-1))){
return IP_NULL;
}
sa = &sc->sa[sc->encodingSA];
if (sa->sc != sc) {
return IP_NULL; /* Invalid SA */
}
return sa;
}
/*
*===========================================================================
* ipmacsec_secy_lookup_tx_sa
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_sa *
ipmacsec_secy_lookup_tx_sa(Ipnet_secy *secy, IP_CONST Ip_u8 an) {
Ipmacsec_secy_sc *sc;
Ipmacsec_secy_sa *sa;
ip_assert(secy);
sc = ipmacsec_secy_get_tx_sc(secy);
if (!sc || !sc->enabled) {
return IP_NULL;
}
if (sc->encodingSA >= IPMACSEC_MAX_SA){
return IP_NULL;
}
if (!secy->config.maxTxKeys || (an > (secy->config.maxTxKeys-1))){
return IP_NULL;
}
sa = &sc->sa[an];
if (sa->sc != sc) {
return IP_NULL; /* Invalid SA */
}
return sa;
}
/*
*===========================================================================
* ipmacsec_secy_lookup_rx_sc
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_sc *
ipmacsec_secy_lookup_rx_sc(Ipnet_secy *secy, IP_CONST Ip_u8 *sci) {
Ipmacsec_secy_sc *sc;
ip_assert(secy);
ip_assert(sci);
/* For now we support only one rx SC, so this is simple. Later we need a hash lookup */
sc = secy->priv.rx_sc;
if (!sc) {
return IP_NULL;
}
#ifndef IPMACSEC_UNIT_TEST_INLINE_HARDCODED_CONFIG
if (!IPMACSEC_SCI_MATCH(sci, sc->sci)) {
return IP_NULL;
}
#endif
return sc;
}
/*
*===========================================================================
* ipmacsec_secy_lookup_rx_sa
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_sa *
ipmacsec_secy_lookup_rx_sa(Ipnet_secy *secy, Ipmacsec_secy_sc *sc, IP_CONST Ip_u8 an) {
Ipmacsec_secy_sa *sa;
ip_assert(secy);
ip_assert(sc);
if (an >= IPMACSEC_MAX_SA){
return IP_NULL;
}
if (!secy->config.maxRxKeys || (an > (secy->config.maxRxKeys-1))) {
return IP_NULL;
}
if (!sc->enabled) {
return IP_NULL;
}
sa = &sc->sa[an];
if (sa->sc != sc) {
return IP_NULL; /* Invalid SA */
}
return sa;
/*
* The following is not very clear in the standard. Nothing is currently done
* to enforce this:
* (7.1.3): If any of the current SAs for inbound SCs are not usable, then the
* MAC_Operational status parameter (6.4) will transition to FALSE.
*
*/
}
/*
*===========================================================================
* ipmacsec_secy_lookup_rx_sa_sai
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE Ipmacsec_secy_sa *
ipmacsec_secy_lookup_rx_sa_sai(Ipnet_secy *secy, IP_CONST Ip_u8 *sci, IP_CONST Ip_u8 an) {
Ipmacsec_secy_sc *sc;
Ipmacsec_secy_sa *sa;
ip_assert(secy);
ip_assert(sci);
sc = ipmacsec_secy_lookup_rx_sc(secy, sci);
if (!sc) {
return IP_NULL;
}
sa = ipmacsec_secy_lookup_rx_sa(secy, sc, an);
return sa;
}
/*
*===========================================================================
* ipmacsec_secy_check_rx_sa_interleave
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_check_rx_sa_interleave(Ipnet_secy *secy, Ipmacsec_secy_sa *sa) {
Ipmacsec_secy_sc *sc;
ip_assert(secy);
ip_assert(sa);
ip_assert(sa->inUse);
ip_assert(sa->key);
sc = sa->sc;
ip_assert(sc);
ip_assert(sc->dir == IPMACSEC_RX);
if (IPMACSEC_INVALID_AN(sc->newAN)) {
/* 1st ever received frame on this SC */
sc->newAN = sa->an;
return 0;
}
if ((sc->interleave_time) && ((ipnet_msec_now()- sc->interleave_time) >= IPMACSEC_RX_SA_INTERLEAVE_PERIOD)) {
/* Check if we exceed the interleave time (0.5 sec). If so, delete old SA/SAK */
Ipmacsec_secy_sa *old_sa = ipmacsec_secy_lookup_rx_sa(secy, sc, (Ip_u8)sc->oldAN);
if (old_sa) {
Ipmacsec_secy_key *key = old_sa->key;
(void)ipmacsec_secy_delete_rx_sa(secy, old_sa);
(void)ipmacsec_secy_remove_key(secy, &key->key_id);
(void)ipmacsec_secy_delete_key(secy, key);
}
sc->oldAN = IPMACSEC_MAX_SA; /* invalidate old SA */
sc->interleave_time = 0;
}
if (sa->an == sc->newAN) {
return 0;
}
/* Different than latest rcvd AN */
if (IPMACSEC_INVALID_AN(sc->oldAN)) {
/* 1st one that is different */
sc->oldAN = sc->newAN;
sc->newAN = sa->an;
sc->interleave_time = ipnet_msec_now();
return 0;
}
if (sa->an == sc->oldAN) {
return 0;
}
return -1; /* Neither old, nor new. Something is wrong */
}
/*
*===========================================================================
* ipmacsec_secy_cp_enable
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_cp_enable(Ipnet_secy *secy, Ip_bool enable) {
ip_assert(secy);
if (secy->controlled_port.ctrl.cp_enabled != enable){
secy->controlled_port.ctrl.cp_enabled = enable;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: ControlledPortEnabled set to %s for interface %s",
IPMACSEC_BOOL_STRING(enable), secy->netif->ipcom.name);
#endif
IPMACSEC_SECY_STATUS_NOTIFY(secy,
(enable ? IPMACSEC_STATUS_SECY_CP_ENABLED_ON : IPMACSEC_STATUS_SECY_CP_ENABLED_OFF));
}
(void)ipmacsec_secy_cp_update_status(secy, IPMACSEC_SECY_MAC_OPER_DEFAULT);
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_cp_set_adminP2pMac
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_cp_set_adminP2pMac(Ipnet_secy *secy, Ip_portif_admin_ptp_mac_val admin_p2p_mac) {
ip_assert(secy);
secy->controlled_port.status.admin_ptp_mac = admin_p2p_mac;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG3(DEBUG2,"IPMACSEC-SECY_CRTL: Controlled Port admin_ptp_mac set to %s (%d) for interface %s",
ipmacsec_secy_cp_adminPointToPointMAC_name(admin_p2p_mac), admin_p2p_mac, secy->netif->ipcom.name);
#endif
(void)ipmacsec_secy_cp_update_status(secy, IPMACSEC_SECY_MAC_OPER_DEFAULT);
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_ucp_update_status_ext
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_ucp_update_status_ext(Ipnet_netif *netif) {
ip_assert(netif);
if (netif->secy) {
return ipmacsec_secy_ucp_update_status(netif->secy);
}
return -1;
}
/*
*===========================================================================
* ipmacsec_secy_ucp_update_status
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_ucp_update_status(Ipnet_secy *secy) {
ip_assert(secy);
/* 10.7.2 */
/* Retrieve from Common Port */
secy->uncontrolled_port.status.mac_enabled = IP_BIT_ISSET(secy->netif->ipcom.flags, IP_IFF_UP);
secy->uncontrolled_port.status.mac_oper = IP_BIT_ISSET(secy->netif->ipcom.flags, IP_IFF_RUNNING);
secy->uncontrolled_port.status.oper_ptp_mac = IP_BIT_ISSET(secy->netif->ipcom.flags, IP_IFF_POINTOPOINT);
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_ucp_update_statistics
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_ucp_update_statistics(Ipnet_netif *netif) {
ip_assert(netif);
if (!netif->secy) {
return -1;
}
/* 10.7.3 */
#ifdef IPCOM_USE_MIB2
IPMACSEC_UCP_STATS(ifInOctets) = netif->ipcom.mib2.ifInOctets;
IPMACSEC_UCP_STATS(ifInUcastPkts) = netif->ipcom.mib2.ifInUcastPkts;
IPMACSEC_UCP_STATS(ifInMulticastPkts) = netif->ipcom.mib2.ifInMulticastPkts;
IPMACSEC_UCP_STATS(ifInBroadcastPkts) = netif->ipcom.mib2.ifInBroadcastPkts;
IPMACSEC_UCP_STATS(ifOutOctets) = netif->ipcom.mib2.ifOutOctets;
IPMACSEC_UCP_STATS(ifOutUcastPkts) = netif->ipcom.mib2.ifOutUcastPkts;
IPMACSEC_UCP_STATS(ifOutMulticastPkts) = netif->ipcom.mib2.ifOutMulticastPkts;
IPMACSEC_UCP_STATS(ifOutBroadcastPkts) = netif->ipcom.mib2.ifOutBroadcastPkts;
/* ifInDiscards, ifInErrors, ifOutErrors are always 0 for UCP */
return 0;
#else
return -1; /* Cannot retrieve Common Port stats since not kept */
#endif
}
/*
*===========================================================================
* ipmacsec_secy_cp_update_status_ext
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_cp_update_status_ext(Ipnet_netif *netif) {
ip_assert(netif);
if (netif->secy) {
return ipmacsec_secy_cp_update_status(netif->secy, IPMACSEC_SECY_MAC_OPER_DEFAULT);
}
return -1;
}
/*
*===========================================================================
* ipmacsec_secy_cp_update_status
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_cp_update_status(Ipnet_secy *secy, Ip_bool mac_oper_off) {
Ipnet_portif *cp;
Ipmacsec_secy_sc *sc;
Ip_bool transmitting = IP_FALSE, receiving = IP_FALSE, updated = IP_FALSE;
struct {
Ip_bool mac_enabled; /* MAC_Enabled */
Ip_bool mac_oper; /* MAC_Operational */
Ip_bool oper_ptp_mac; /* operPointToPointMAC */
} orig_status;
ip_assert(secy);
cp = &secy->controlled_port;
orig_status.mac_enabled = cp->status.mac_enabled;
orig_status.mac_oper = cp->status.mac_oper;
orig_status.oper_ptp_mac = cp->status.oper_ptp_mac;
/* 6.7; 10.7.4; A.7 */
sc = ipmacsec_secy_get_tx_sc(secy);
if (sc) {
transmitting = sc->oper.transmitting;
}
receiving = (secy->oper.numActiveRxSCs != 0);
cp->status.mac_enabled = IP_FALSE;
cp->status.mac_oper = IP_FALSE;
if (cp->ctrl.cp_enabled) {
Ip_bool secy_cp_mac_oper = IP_FALSE;
Ip_bool common_port_mac_enabled = IP_BIT_ISSET(secy->netif->ipcom.flags, IP_IFF_UP);
Ipmacsec_secy_sa *sa = ipmacsec_secy_get_tx_sa(secy);
if (sa) {
secy_cp_mac_oper = (sa->nextPN != 0);
}
cp->status.mac_enabled = common_port_mac_enabled && transmitting && receiving ;
if (!mac_oper_off && cp->status.mac_enabled && secy_cp_mac_oper) {
Ip_bool common_port_mac_oper = IP_BIT_ISSET(secy->netif->ipcom.flags, IP_IFF_RUNNING);
cp->status.mac_oper = common_port_mac_oper ;
}
}
if (cp->status.admin_ptp_mac == IP_PORTIF_ADMIN_PTP_MAC_FORCE_TRUE) {
cp->status.oper_ptp_mac = IP_TRUE;
goto done;
}
cp->status.oper_ptp_mac = IP_FALSE;
if (cp->status.admin_ptp_mac == IP_PORTIF_ADMIN_PTP_MAC_FORCE_FALSE) {
goto done;
}
if (cp->status.mac_oper && (cp->status.admin_ptp_mac == IP_PORTIF_ADMIN_PTP_MAC_FORCE_AUTO)) {
if (secy->ctrl.validateFrames == IPMACSEC_SECY_VALIDATEFRAMES_STRICT) {
cp->status.oper_ptp_mac = (secy->oper.numActiveRxSCs <= 1);
} else {
Ip_bool common_port_oper_ptp_mac = IP_BIT_ISSET(secy->netif->ipcom.flags, IP_IFF_POINTOPOINT);
cp->status.oper_ptp_mac = common_port_oper_ptp_mac;
}
}
done:
/* Need to notify KaY via LMI of any Controlled Port status changes */
if (orig_status.mac_enabled != cp->status.mac_enabled) {
IPMACSEC_SECY_STATUS_NOTIFY(secy,
((cp->status.mac_enabled) ? IPMACSEC_STATUS_SECY_CP_MAC_ENABLED_ON : IPMACSEC_STATUS_SECY_CP_MAC_ENABLED_OFF));
updated = IP_TRUE;
}
if (orig_status.mac_oper != cp->status.mac_oper) {
IPMACSEC_SECY_STATUS_NOTIFY(secy,
((cp->status.mac_oper) ? IPMACSEC_STATUS_SECY_CP_MAC_OPER_ON : IPMACSEC_STATUS_SECY_CP_MAC_OPER_OFF));
updated = IP_TRUE;
}
if (orig_status.oper_ptp_mac != cp->status.oper_ptp_mac) {
IPMACSEC_SECY_STATUS_NOTIFY(secy,
((cp->status.oper_ptp_mac) ? IPMACSEC_STATUS_SECY_CP_OPER_PTP_MAC_ON : IPMACSEC_STATUS_SECY_CP_OPER_PTP_MAC_OFF));
updated = IP_TRUE;
}
#ifdef IPMACSEC_DEBUG_SECY_MGMT
if (updated)
IPCOM_LOG5(DEBUG2,
"IPMACSEC-MGMT: CP status updated (cp_enabled %s, mac_enabled %s, mac_oper %s, oper_ptp_mac %s) for interface %s",
IPMACSEC_BOOL_STRING(cp->ctrl.cp_enabled), IPMACSEC_BOOL_STRING(cp->status.mac_enabled),
IPMACSEC_BOOL_STRING(cp->status.mac_oper), IPMACSEC_BOOL_STRING(cp->status.oper_ptp_mac), secy->netif->ipcom.name);
#endif
return 0;
}
/*
*===========================================================================
* ipmacsec_secy_cp_update_statistics
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_cp_update_statistics(Ipnet_netif *netif) {
int rc;
Ipnet_secy *secy;
ip_assert(netif);
secy = netif->secy;
if (!secy) {
rc = -1;
} else
#ifdef IPMACSEC_USE_STATS
/* 10.7.6 */
{
Ip_u8 an;
Ipmacsec_secy_sc *sc;
Ipmacsec_secy_counter64 inDisc=0, inErr=0;
if (ipmacsec_secy_rx_sc_iter_init(secy) < 0) {
return -1;
}
inDisc += IPMACSEC_SECY_STATS(InPktsNoTag) + IPMACSEC_SECY_STATS(InPktsOverrun);
inErr += IPMACSEC_SECY_STATS(InPktsBadTag) + IPMACSEC_SECY_STATS(InPktsNoSCI);
/* Add all RX SC stats */
for (;;) {
rc = ipmacsec_secy_get_next_rx_sc(secy, &sc);
if (rc < 0) {
return -1;
}
if (sc == IP_NULL) {
rc = 0;
break; /* No more SCs */
}
inDisc += IPMACSEC_SC_STATS(in.InPktsLate);
inErr += IPMACSEC_SC_STATS(aggr.rx.InPktsNotUsingSA) + IPMACSEC_SC_STATS(aggr.rx.InPktsNotValid);
for (an = 0; an < secy->config.maxRxKeys; an++) {
Ipmacsec_secy_sa *sa = &sc->sa[an];
inErr += IPMACSEC_SA_STATS(rx.InPktsNotUsingSA) + IPMACSEC_SA_STATS(rx.InPktsNotValid);
}
}
IPMACSEC_CP_STATS_SET(ifInDiscards, inDisc);
IPMACSEC_CP_STATS_SET(ifInErrors, inErr);
IPMACSEC_CP_STATS_SET(ifOutErrors, secy->stats.OutPktsTooLong);
rc = 0;
}
#else
{
rc = -1; /* CP stats not kept */
}
#endif
return rc;
}
/*
*===========================================================================
* ipmacsec_secy_status_notify
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_status_notify(Ipnet_secy *secy, Ipmacsec_secy_status status, void *param, Ip_u32 param_size) {
Ipmacsec_buffer note;
Ipmacsec_secy_status theStatus = status;
struct Ip_nlmsghdr *nlmsg = IP_NULL;
int rc = 0;
ip_assert(secy);
ip_assert(secy->netif);
if (!IPMACSEC_SECY_STATUS_IS_VALID(theStatus)) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(ERR, "IPMACSEC-MGMT: Invalid status %u (interface %s)", theStatus, secy->netif->ipcom.name);
#endif
return -1;
}
IPMACSEC_BUF_INIT(&note);
if (ipmacsec_nlmsg_alloc(&note) == IP_NULL) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: note nlmsg alloc failed (interface %s)", secy->netif->ipcom.name);
#endif
rc = -1;
goto err_out;
}
nlmsg = ipmacsec_nlnote_add(&note, IPMACSEC_NLTYPE_NOTIFY, IPMACSEC_LMINOTE_STATUS, secy->netif->ipcom.name);
if (nlmsg == IP_NULL) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: failed to add status note (interface %s)", secy->netif->ipcom.name);
#endif
rc = -1;
goto err_out;
}
if (ipmacsec_nlnote_add_param(&note, nlmsg, &theStatus, sizeof(theStatus)) == IP_NULL) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: failed to add status param (interface %s)", secy->netif->ipcom.name);
#endif
rc = -1;
goto err_out;
}
if (param && param_size) {
if (ipmacsec_nlnote_add_param(&note, nlmsg, param, param_size) == IP_NULL) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: failed to add optional status param (interface %s)", secy->netif->ipcom.name);
#endif
rc = -1;
goto err_out;
}
}
rc = ipmacsec_netlink_broadcast(secy, (Ip_u8 *)IP_NLMSG_DATA(nlmsg), (Ip_u32)IP_NLMSG_PAYLOAD(nlmsg, 0),
IPMACSEC_NLTYPE_NOTIFY, 0, IPMACSEC_NLGRP_NOTIFY);
err_out:
#ifdef IPMACSEC_DEBUG_SECY_MGMT
if (rc != 0) {
IPCOM_LOG3(ERR, "IPMACSEC-MGMT: failed to send status notification %s (%u) for interface %s",
ipmacsec_secy_status_name(theStatus), theStatus, secy->netif->ipcom.name);
} else {
IPCOM_LOG3(DEBUG2, "IPMACSEC-MGMT: successfully sent status notification %s (%u) for interface %s",
ipmacsec_secy_status_name(theStatus), theStatus, secy->netif->ipcom.name);
}
#endif
ipmacsec_nlmsg_free(&note);
return rc;
}
/*
*===========================================================================
* ipmacsec_secy_info_notify
*===========================================================================
* Description: see "ipmacsec_secy_mgmt.h"
* Parameters:
* Returns:
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_info_notify(Ipnet_secy *secy, Ipmacsec_secy_info info, void *param, Ip_u32 param_size) {
Ipmacsec_buffer note;
Ipmacsec_secy_info theInfo = info;
struct Ip_nlmsghdr *nlmsg = IP_NULL;
int rc = 0;
ip_assert(secy);
ip_assert(secy->netif);
if (!IPMACSEC_SECY_INFO_IS_VALID(theInfo)) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(ERR, "IPMACSEC-MGMT: Invalid info %u (interface %s)", theInfo, secy->netif->ipcom.name);
#endif
return -1;
}
if (!secy->config.info_notify) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG3(DEBUG2, "IPMACSEC-MGMT: info_notify disabled, not sending info notification %s (%u) for interface %s",
ipmacsec_secy_info_name(theInfo), theInfo, secy->netif->ipcom.name);
#endif
return 0;
}
IPMACSEC_BUF_INIT(&note);
if (ipmacsec_nlmsg_alloc(&note) == IP_NULL) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: note nlmsg alloc failed (interface %s)", secy->netif->ipcom.name);
#endif
rc = -1;
goto err_out;
}
nlmsg = ipmacsec_nlnote_add(&note, IPMACSEC_NLTYPE_NOTIFY, IPMACSEC_LMINOTE_INFO, secy->netif->ipcom.name);
if (nlmsg == IP_NULL) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: failed to add info note (interface %s)", secy->netif->ipcom.name);
#endif
rc = -1;
goto err_out;
}
if (ipmacsec_nlnote_add_param(&note, nlmsg, &theInfo, sizeof(theInfo)) == IP_NULL) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: failed to add info param (interface %s)", secy->netif->ipcom.name);
#endif
rc = -1;
goto err_out;
}
if (param && param_size) {
if (ipmacsec_nlnote_add_param(&note, nlmsg, param, param_size) == IP_NULL) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: failed to add optional info param (interface %s)", secy->netif->ipcom.name);
#endif
rc = -1;
goto err_out;
}
}
rc = ipmacsec_netlink_broadcast(secy, (Ip_u8 *)IP_NLMSG_DATA(nlmsg), (Ip_u32)IP_NLMSG_PAYLOAD(nlmsg, 0),
IPMACSEC_NLTYPE_NOTIFY, 0, IPMACSEC_NLGRP_MONITOR);
err_out:
#ifdef IPMACSEC_DEBUG_SECY_MGMT
if (rc != 0) {
IPCOM_LOG3(ERR, "IPMACSEC-MGMT: failed to send info notification %s (%u) for interface %s",
ipmacsec_secy_info_name(theInfo), theInfo, secy->netif->ipcom.name);
} else {
IPCOM_LOG3(DEBUG2, "IPMACSEC-MGMT: successfully sent info notification %s (%u) for interface %s",
ipmacsec_secy_info_name(theInfo), theInfo, secy->netif->ipcom.name);
}
#endif
ipmacsec_nlmsg_free(&note);
return rc;
}
/*
*===========================================================================
* ipmacsec_secy_handle_nlcmd
*===========================================================================
* Description: Handler for MACsec netlink commands received from apps via
* netlink sockets.
* Parameters:
* Returns: 0 upon success, < 0 on error
*
*/
IPMACSEC_PRIVATE int
ipmacsec_secy_handle_nlcmd(Ipnet_secy *secy, struct Ip_nlmsghdr *nlmsg, Ip_bool non_blocking,
Ipmacsec_buffer *reply, Ip_pid_t src_pid) {
Ipmacsec_nlcmd_hdr *nlcmd;
Ip_bool isGet = IP_FALSE;
Ip_u32 param_size = 0;
int ret = -1;
IPCOM_UNUSED_ARG(non_blocking);
ip_assert(secy);
ip_assert(nlmsg);
nlcmd = ipmacsec_nlcmd_get(nlmsg);
if (!nlcmd) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG0(ERR, "IPMACSEC-MGMT: Failed to get MACsec netlink cmd");
#endif
return -1;
}
if (nlcmd->get) {
ip_assert(reply);
isGet = IP_TRUE;
}
#ifdef IPMACSEC_DEBUG_SECY_MGMT_VERBOSE
IPCOM_LOG4(DEBUG2, "IPMACSEC-MGMT: Processing MACsec netlink cmd %d (%s), nltype %d (%s)",
nlcmd->command, ipmacsec_lmi_cmd_name(nlcmd->command),
nlmsg->nlmsg_type, ipmacsec_netlink_type_name(nlmsg->nlmsg_type));
#endif
if (nlmsg->nlmsg_type == IPMACSEC_NLTYPE_SECY_COMMAND) {
switch (nlcmd->command) {
case IPMACSEC_LMICMD_SECY_EXISTS:
if (!isGet) {
goto no_set;
} else {
/* "I receive, therefore I am" */
if (ipmacsec_nlcmd_response_add(reply, nlmsg, IP_NULL, 0, src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_ENABLED:
if (isGet) {
Ip_bool enabled = secy->ctrl.enabled;
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &enabled, sizeof(enabled), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
} else {
Ip_bool enable;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(enable);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &enable, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (enable)
ret = ipmacsec_secy_enable(secy);
else
ret = ipmacsec_secy_disable(secy);
}
break;
case IPMACSEC_LMICMD_SECY_PROTECT_FRAMES:
case IPMACSEC_LMICMD_SECY_ALWAYS_INC_SCI:
case IPMACSEC_LMICMD_SECY_USE_ES:
case IPMACSEC_LMICMD_SECY_USE_SCB:
case IPMACSEC_LMICMD_SECY_REPLAY_PROTECT:
if (isGet) {
Ip_bool val;
if (nlcmd->command == IPMACSEC_LMICMD_SECY_PROTECT_FRAMES)
val = secy->ctrl.protectFrames;
else if (nlcmd->command == IPMACSEC_LMICMD_SECY_ALWAYS_INC_SCI)
val = secy->ctrl.alwaysIncludeSCI;
else if (nlcmd->command == IPMACSEC_LMICMD_SECY_USE_ES)
val = secy->ctrl.useES;
else if (nlcmd->command == IPMACSEC_LMICMD_SECY_USE_SCB)
val = secy->ctrl.useSCB;
else if (nlcmd->command == IPMACSEC_LMICMD_SECY_REPLAY_PROTECT)
val = secy->ctrl.replayProtect;
else {
ret = -1;
goto exec_fail;
}
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &val, sizeof(val), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
} else {
Ip_bool val;
if (secy->ctrl.enabled) {
goto no_exec_enabled;
}
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(val);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &val, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (nlcmd->command == IPMACSEC_LMICMD_SECY_PROTECT_FRAMES) {
secy->ctrl.protectFrames = val;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: protectFrames set to %s for interface %s",
IPMACSEC_BOOL_STRING(secy->ctrl.protectFrames), secy->netif->ipcom.name);
#endif
} else if (nlcmd->command == IPMACSEC_LMICMD_SECY_ALWAYS_INC_SCI) {
secy->ctrl.alwaysIncludeSCI = val;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: alwaysIncludeSCI set to %s for interface %s",
IPMACSEC_BOOL_STRING(secy->ctrl.alwaysIncludeSCI), secy->netif->ipcom.name);
#endif
} else if (nlcmd->command == IPMACSEC_LMICMD_SECY_USE_ES) {
secy->ctrl.useES = val;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: useES set to %s for interface %s",
IPMACSEC_BOOL_STRING(secy->ctrl.useES), secy->netif->ipcom.name);
#endif
} else if (nlcmd->command == IPMACSEC_LMICMD_SECY_USE_SCB) {
secy->ctrl.useSCB = val;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: useSCB set to %s for interface %s",
IPMACSEC_BOOL_STRING(secy->ctrl.useSCB), secy->netif->ipcom.name);
#endif
} else if (nlcmd->command == IPMACSEC_LMICMD_SECY_REPLAY_PROTECT) {
secy->ctrl.replayProtect = val;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: replayProtect set to %s for interface %s",
IPMACSEC_BOOL_STRING(secy->ctrl.replayProtect), secy->netif->ipcom.name);
#endif
} else {
ret = -1;
goto exec_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_VALIDATE_FRAMES:
if (isGet) {
Ipmacsec_validateFrames_type val;
val = secy->ctrl.validateFrames;
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &val, sizeof(val), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
} else {
Ipmacsec_validateFrames_type val;
if (secy->ctrl.enabled) {
goto no_exec_enabled;
}
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(val);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &val, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (!IPMACSEC_VALIDATEFRAMES_VALID(val)) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(DEBUG2, "IPMACSEC-MGMT: Invalid validateFrames value %u.", (Ip_u32)val);
#endif
ret = -1;
goto exec_fail;
}
secy->ctrl.validateFrames = val;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG3(DEBUG2, "IPMACSEC-MGMT: validateFrames set to %s (%d) for interface %s",
ipmacsec_secy_validateFrames_name(secy->ctrl.validateFrames), secy->ctrl.validateFrames,
secy->netif->ipcom.name);
#endif
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_REPLAY_WINDOW:
if (isGet) {
Ip_u32 val;
val = secy->ctrl.replayWindow;
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &val, sizeof(val), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
} else {
Ip_u32 val;
if (secy->ctrl.enabled) {
goto no_exec_enabled;
}
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(val);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &val, param_size);
if (ret < 0) {
goto param_ret_fail;
}
secy->ctrl.replayWindow = val;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: replayWindow set to %u for interface %s",
secy->ctrl.replayWindow, secy->netif->ipcom.name);
#endif
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_TX_SCI:
if (isGet) {
if (ipmacsec_nlcmd_response_add(reply, nlmsg, secy->config.sci, sizeof(secy->config.sci), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
} else {
Ip_u8 sci[IPMACSEC_SCI_LEN];
Ipmacsec_secy_sc *tx_sc = IP_NULL;
if (secy->ctrl.enabled) {
goto no_exec_enabled;
}
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(sci);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, sci, param_size);
if (ret < 0) {
goto param_ret_fail;
}
tx_sc = ipmacsec_secy_get_tx_sc(secy);
if (tx_sc && (!IPMACSEC_SCI_MATCH(sci, tx_sc->sci))) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG9(ERR,
"IPMACSEC-MGMT: Cannot set SecY SCI value (%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x) for interface %s "
"(mismatch with existing TX SC SCI)",
sci[0], sci[1], sci[2], sci[3], sci[4], sci[5], sci[6], sci[7],
secy->netif->ipcom.name);
#endif
ret = -1;
goto exec_fail;
}
IPMACSEC_COPY_SCI(secy->config.sci, sci);
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG9(INFO,
"IPMACSEC-MGMT: SecY SCI value (%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x) set for interface %s ",
secy->config.sci[0], secy->config.sci[1], secy->config.sci[2], secy->config.sci[3],
secy->config.sci[4], secy->config.sci[5], secy->config.sci[6], secy->config.sci[7],
secy->netif->ipcom.name);
#endif
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_MAX_RX_SC:
case IPMACSEC_LMICMD_SECY_MAX_RX_KEYS:
case IPMACSEC_LMICMD_SECY_MAX_TX_KEYS:
if (!isGet) {
goto no_set;
} else {
Ip_u16 val;
if (nlcmd->command == IPMACSEC_LMICMD_SECY_MAX_RX_SC)
val = secy->config.maxRxSCs;
else if (nlcmd->command == IPMACSEC_LMICMD_SECY_MAX_RX_KEYS)
val = secy->config.maxRxKeys;
else if (nlcmd->command == IPMACSEC_LMICMD_SECY_MAX_TX_KEYS)
val = secy->config.maxTxKeys;
else {
ret = -1;
goto exec_fail;
}
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &val, sizeof(val), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_PN_THRESHOLD:
if (isGet) {
Ip_u32 threshold = secy->config.pn_threshold;
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &threshold, sizeof(threshold), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
} else {
Ip_u32 threshold;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(threshold);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &threshold, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (threshold == 0) {
ret = -1;
goto exec_fail;
}
secy->config.pn_threshold = threshold;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: pn_threshold set to %u for interface %s",
secy->config.pn_threshold, secy->netif->ipcom.name);
#endif
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_INFO_NOTIFY:
if (isGet) {
Ip_bool info_notify = secy->config.info_notify;
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &info_notify, sizeof(info_notify), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
} else {
Ip_bool info_notify;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(info_notify);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &info_notify, param_size);
if (ret < 0) {
goto param_ret_fail;
}
secy->config.info_notify = info_notify;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: info_notify set to %s for interface %s",
IPMACSEC_BOOL_STRING(secy->config.info_notify), secy->netif->ipcom.name);
#endif
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_CONFIDENTIALITY_OFFSET:
if (isGet) {
Ip_u16 confidentialityOffset = secy->config.confidentialityOffset;
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &confidentialityOffset,
sizeof(confidentialityOffset), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
} else {
Ip_u16 confidentialityOffset;
if (secy->ctrl.enabled) {
goto no_exec_enabled;
}
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(confidentialityOffset);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &confidentialityOffset, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (!IPMACSEC_SECY_VERIFY_CONFIDENTIALITY_OFFSET(confidentialityOffset)) {
ret = -1;
goto exec_fail;
}
secy->config.confidentialityOffset = confidentialityOffset;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: confidentialityOffset set to %u for interface %s",
secy->config.confidentialityOffset, secy->netif->ipcom.name);
#endif
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_CURRENT_CIPHER_SUITE:
if (isGet) {
Ipmacsec_ciphersuite_descriptor descriptor;
Ipmacsec_cipher_suite_index indx = secy->config.cipher_suite_index;
ret = ipmacsec_crypto_cipher_suite_get_info(indx, &descriptor);
if (ret != 0) {
ret = -1;
goto exec_fail;
}
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &descriptor, sizeof(descriptor), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
} else {
Ipmacsec_cipher_suite_index indx;
if (secy->ctrl.enabled) {
goto no_exec_enabled;
}
if (secy->oper.numCreatedRxSCs || ipmacsec_secy_get_tx_sc(secy)) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(ERR,"IPMACSEC-SECY_CRTL: Cannot set cipher_suite_index %u for interface %s"
" (one or more SCs already created)",
indx, secy->netif->ipcom.name);
#endif
ret = -1;
goto exec_fail;
}
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(indx);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &indx, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (!ipmacsec_crypto_cipher_suite_supported(indx)) {
ret = -1;
goto exec_fail;
}
secy->config.cipher_suite_index = indx;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG2(DEBUG2,"IPMACSEC-SECY_CRTL: cipher_suite_index set to %u for interface %s",
secy->config.cipher_suite_index, secy->netif->ipcom.name);
#endif
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_CIPHER_SUITE_LIST:
case IPMACSEC_LMICMD_SECY_CIPHER_SUITE_INFO:
if (!isGet) {
goto no_set;
} else {
Ipmacsec_cipher_suite_index index_start = __IPMACSEC_CIPHERSUITE_INVALID+1;
Ipmacsec_cipher_suite_index index_end = __IPMACSEC_CIPHERSUITE_MAX;
Ipmacsec_cipher_suite_index indx;
struct Ip_nlmsghdr *nlresp = IP_NULL;
if (nlcmd->command == IPMACSEC_LMICMD_SECY_CIPHER_SUITE_INFO) {
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(indx);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &indx, param_size);
if (ret < 0) {
goto param_ret_fail;
}
index_start = indx;
index_end = index_start+1;
}
nlresp = ipmacsec_nlcmd_response_add(reply, nlmsg, IP_NULL, 0, src_pid);
if (nlresp == IP_NULL) {
ret = -1;
goto reply_fail;
}
for (indx = index_start; indx < index_end; indx++) {
Ipmacsec_ciphersuite_descriptor descriptor;
ret = ipmacsec_crypto_cipher_suite_get_info(indx, &descriptor);
if (ret != 0) {
ret = -1;
goto exec_fail;
}
ret = ipmacsec_nlcmd_add_param(reply, nlresp, &descriptor, sizeof(descriptor));
if (ret < (int)sizeof(descriptor)) {
ret = -1;
goto reply_fail;
}
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_NUM_RX_SC_ACTIVE:
case IPMACSEC_LMICMD_SECY_NUM_RX_SC_CREATED:
if (!isGet) {
goto no_set;
} else {
Ip_u16 val;
if (nlcmd->command == IPMACSEC_LMICMD_SECY_NUM_RX_SC_ACTIVE)
val = secy->oper.numActiveRxSCs;
else if (nlcmd->command == IPMACSEC_LMICMD_SECY_NUM_RX_SC_CREATED)
val = secy->oper.numCreatedRxSCs;
else {
ret = -1;
goto exec_fail;
}
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &val, sizeof(val), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_KEY_INSTALL:
if (isGet) {
goto no_get;
} else {
Ipmacsec_secy_key *key;
Ipmacsec_secy_sak sak;
Ipmacsec_secy_key_descriptor key_desc;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(key_desc);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &key_desc, param_size);
if (ret < 0) {
goto param_ret_fail;
}
sak.num_octets = key_desc.key_size;
sak.buf = ipcom_malloc(sak.num_octets);
if (sak.buf == IP_NULL) {
ret = -1;
goto exec_fail;
}
param_size = sak.num_octets;
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, sak.buf, param_size);
if (ret < 0) {
goto param_ret_fail;
}
key = ipmacsec_secy_create_key(secy, &sak, &key_desc.key_id);
if (!key) {
ipcom_free(sak.buf);
ret = -1;
goto exec_fail;
}
ret = ipmacsec_secy_install_key(secy, key, key_desc.key_type);
}
break;
case IPMACSEC_LMICMD_SECY_KEY_REMOVE:
if (isGet) {
goto no_get;
} else {
Ipmacsec_secy_key *key;
Ipmacsec_secy_key_descriptor key_desc;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(key_desc);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &key_desc, param_size);
if (ret < 0) {
goto param_ret_fail;
}
key = ipmacsec_secy_remove_key(secy, &key_desc.key_id);
if (key == IP_NULL) {
ret = -1;
goto exec_fail;
}
ret = ipmacsec_secy_delete_key(secy, key);
if (ret != 0) {
ret = -1;
goto exec_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_KEY_PURGE:
if (isGet) {
goto no_get;
} else {
ret = ipmacsec_secy_delete_all_keys(secy, IP_FALSE);
if (ret != 0) {
ret = -1;
goto exec_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_KEY_INFO:
if (!isGet) {
goto no_set;
} else {
Ipmacsec_secy_key *key;
Ipmacsec_secy_key_descriptor key_desc;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(key_desc);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &key_desc, param_size);
if (ret < 0) {
goto param_ret_fail;
}
key = ipmacsec_secy_retrieve_key(secy, &key_desc.key_id, &key_desc);
if (!key) {
ret = -1;
goto exec_fail;
}
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &key_desc, sizeof(key_desc), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_KEY_LIST:
if (!isGet) {
goto no_set;
} else {
Ipmacsec_secy_key *key;
Ipmacsec_secy_key_descriptor key_desc;
struct Ip_nlmsghdr *nlresp = IP_NULL;
/* Retrieve all keys in hash table and return info */
if (ipmacsec_secy_keydb_iter_init(secy) < 0) {
ret = -1;
goto exec_fail;
}
nlresp = ipmacsec_nlcmd_response_add(reply, nlmsg, IP_NULL, 0, src_pid);
if (nlresp == IP_NULL) {
ret = -1;
goto reply_fail;
}
for (;;) {
key = IP_NULL;
ret = ipmacsec_secy_keydb_get_next_key(secy, &key);
if (ret < 0) {
goto exec_fail;
}
if (key == IP_NULL) {
ret = 0;
break; /* No more keys */
}
if (ipmacsec_secy_retrieve_key_info(secy, key, &key_desc) == IP_NULL) {
ret = -1;
goto exec_fail;
}
ret = ipmacsec_nlcmd_add_param(reply, nlresp, &key_desc, sizeof(key_desc));
if (ret < (int)sizeof(key_desc)) {
ret = -1;
goto reply_fail;
}
}
}
break;
case IPMACSEC_LMICMD_SECY_RX_SC_CREATE:
case IPMACSEC_LMICMD_SECY_RX_SC_DELETE:
if (isGet) {
goto no_get;
} else {
Ip_u8 sci[IPMACSEC_SCI_LEN];
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(sci);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, sci, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SC_CREATE) {
if (ipmacsec_secy_create_rx_sc(secy, sci) == IP_NULL) {
ret = -1;
} else {
ret = 0 ;
}
} else {
Ipmacsec_secy_sc *sc;
sc = ipmacsec_secy_lookup_rx_sc(secy, sci);
if (!sc) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG8(ERR, "IPMACSEC-MGMT: RX SC not found. Cannot delete RX SC (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
ret = -1;
goto exec_fail;
}
ret = ipmacsec_secy_delete_rx_sc(secy, sc);
}
if (ret < 0) {
goto exec_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_TX_SC_CREATE:
case IPMACSEC_LMICMD_SECY_TX_SC_DELETE:
if (isGet) {
goto no_get;
} else {
if (nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SC_CREATE) {
if (ipmacsec_secy_create_tx_sc(secy) == IP_NULL) {
ret = -1;
} else {
ret = 0 ;
}
} else {
ret = ipmacsec_secy_delete_tx_sc(secy);
}
if (ret < 0) {
goto exec_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_RX_SC_STATUS:
case IPMACSEC_LMICMD_SECY_TX_SC_STATUS:
if (!isGet) {
goto no_set;
} else {
Ip_u8 sci[IPMACSEC_SCI_LEN];
Ipmacsec_secy_sc *sc;
Ipmacsec_secy_sc_info sc_info;
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SC_STATUS) {
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(sci);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, sci, param_size);
if (ret < 0) {
goto param_ret_fail;
}
sc = ipmacsec_secy_lookup_rx_sc(secy, sci);
} else /* IPMACSEC_LMICMD_SECY_TX_SC_STATUS */ {
sc = ipmacsec_secy_get_tx_sc(secy);
}
if (!sc) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SC_STATUS) {
IPCOM_LOG8(ERR, "IPMACSEC-MGMT: RX SC not found (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
ret = -1;
goto exec_fail;
}
(void)ipmacsec_secy_fill_sc_info(secy, sc, &sc_info);
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &sc_info, sizeof(sc_info), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_RX_SC_LIST:
if (!isGet) {
goto no_set;
} else {
struct Ip_nlmsghdr *nlresp = IP_NULL;
/* Retrieve all Rx SCs and return info */
if (ipmacsec_secy_rx_sc_iter_init(secy) < 0) {
ret = -1;
goto exec_fail;
}
nlresp = ipmacsec_nlcmd_response_add(reply, nlmsg, IP_NULL, 0, src_pid);
if (nlresp == IP_NULL) {
ret = -1;
goto reply_fail;
}
for (;;) {
Ipmacsec_secy_sc_info sc_info;
Ipmacsec_secy_sc *sc= IP_NULL;
ret = ipmacsec_secy_get_next_rx_sc(secy, &sc);
if (ret < 0) {
goto exec_fail;
}
if (sc == IP_NULL) {
ret = 0;
break; /* No more SCs */
}
(void)ipmacsec_secy_fill_sc_info(secy, sc, &sc_info);
ret = ipmacsec_nlcmd_add_param(reply, nlresp, &sc_info, sizeof(sc_info));
if (ret < (int)sizeof(sc_info)) {
ret = -1;
goto reply_fail;
}
}
}
break;
case IPMACSEC_LMICMD_SECY_RX_SA_CREATE:
case IPMACSEC_LMICMD_SECY_RX_SA_DELETE:
case IPMACSEC_LMICMD_SECY_RX_SA_CONTROL:
case IPMACSEC_LMICMD_SECY_TX_SA_CREATE:
case IPMACSEC_LMICMD_SECY_TX_SA_DELETE:
case IPMACSEC_LMICMD_SECY_TX_SA_CONTROL:
if (isGet) {
goto no_get;
} else {
Ipmacsec_secy_sa_descriptor sa_desc;
Ipmacsec_secy_sa_info sa_info;
Ipmacsec_secy_sa_ctrl sa_ctrl;
Ipmacsec_secy_sc *sc = IP_NULL;
Ipmacsec_dir dir;
ipcom_memset(&sa_info, 0, sizeof(sa_info));
ipcom_memset(&sa_ctrl, 0, sizeof(sa_ctrl));
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(sa_desc);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &sa_desc, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if ((nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_CREATE) ||
(nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_DELETE) ||
(nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_CONTROL)) {
dir = IPMACSEC_RX;
sc = ipmacsec_secy_lookup_rx_sc(secy, sa_desc.sci);
if (!sc) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG8(ERR, "IPMACSEC-MGMT: RX SC not found (SCI = "
"%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x)",
sa_desc.sci[0],sa_desc.sci[1],sa_desc.sci[2],sa_desc.sci[3],
sa_desc.sci[4],sa_desc.sci[5],sa_desc.sci[6],sa_desc.sci[7]);
#endif
ret = -1;
goto exec_fail;
}
} else if ((nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SA_CREATE) ||
(nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SA_DELETE) ||
(nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SA_CONTROL)) {
dir = IPMACSEC_TX;
} else {
ret = -1;
goto exec_fail;
}
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(sa_desc);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &sa_desc, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if ((nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_CREATE) ||
(nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SA_CREATE)) {
Ipmacsec_secy_key_descriptor key_desc;
Ipmacsec_secy_key *key = ipmacsec_secy_retrieve_key(secy, &sa_desc.key_id, &key_desc);
if (!key) {
ret = -1;
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: Cannot create RX SA. Key not found (key_id = 0x%llx)",
sa_desc.key_id.val);
#endif
goto exec_fail;
}
param_size = sizeof(sa_info);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &sa_info, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_CREATE) {
Ipmacsec_secy_sa *sa = ipmacsec_secy_lookup_rx_sa(secy, sc, sa_desc.an);
if (sa) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: Cannot create RX SA. SA Exists (an = %u)",
sa_desc.an);
#endif
ret = -1;
goto exec_fail;
}
sa = ipmacsec_secy_create_rx_sa(secy, sc, sa_desc.an, sa_desc.ref, sa_info.config.rx.nextPN,
sa_info.config.rx.lowestPN, key);
if (!sa) {
ret = -1;
goto exec_fail;
}
ret = 0;
} else if (nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SA_CREATE) {
Ipmacsec_secy_sa *sa = ipmacsec_secy_lookup_tx_sa(secy, sa_desc.an);
if (sa) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: Cannot create TX SA. SA Exists (an = %u)",
sa_desc.an);
#endif
ret = -1;
goto exec_fail;
}
sa = ipmacsec_secy_create_tx_sa(secy, sa_desc.an, sa_desc.ref, sa_info.config.tx.nextPN,
sa_info.config.tx.confidentiality, key);
if (!sa) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG1(ERR, "IPMACSEC-MGMT: Cannot create TX SA. SA Exists (an = %u)",
sa_desc.an);
#endif
ret = -1;
goto exec_fail;
}
ret = 0;
} else {
ret = -1;
goto exec_fail;
}
} else if ((nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_DELETE) ||
(nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SA_DELETE)) {
Ipmacsec_secy_sa *sa = IP_NULL;
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_DELETE)
sa = ipmacsec_secy_lookup_rx_sa(secy, sc, sa_desc.an);
else if (nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SA_DELETE)
sa = ipmacsec_secy_lookup_tx_sa(secy, sa_desc.an);
if (!sa || sa->ref != sa_desc.ref) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG3(ERR, "IPMACSEC-MGMT: Cannot delete %s SA. SA not found (an: %u, ref: 0x%x)",
IPMACSEC_DIR_STRING(dir), sa_desc.an, sa_desc.ref);
#endif
ret = -1;
goto exec_fail;
}
ret = -1;
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_DELETE)
ret = ipmacsec_secy_delete_rx_sa(secy, sa);
else if (nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SA_DELETE)
ret = ipmacsec_secy_delete_tx_sa(secy, sa);
} else if ((nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_CONTROL) ||
(nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SA_CONTROL)) {
Ipmacsec_secy_sa *sa = IP_NULL;
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_CONTROL)
sa = ipmacsec_secy_lookup_rx_sa(secy, sc, sa_desc.an);
else if (nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SA_CONTROL)
sa = ipmacsec_secy_lookup_tx_sa(secy, sa_desc.an);
if (!sa || (sa->ref != sa_desc.ref)) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG3(ERR, "IPMACSEC-MGMT: Cannot control %s SA. SA not found (an: %u, ref: 0x%x)",
IPMACSEC_DIR_STRING(dir), sa_desc.an, sa_desc.ref);
#endif
ret = -1;
goto exec_fail;
}
param_size = sizeof(sa_ctrl);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &sa_ctrl, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_CONTROL) {
ret = -1;
if (sa_ctrl.rx.updtNextPN || sa_ctrl.rx.updtLowestPN) {
ret = ipmacsec_secy_update_rx_sa(secy, sa, sa_ctrl.rx.updtNextPN, sa_ctrl.rx.updtLowestPN);
if (ret < 0) {
goto exec_fail;
}
}
if (sa_ctrl.rx.enableRx != IPMACSEC_ENABLEFLAG_IGNORE) {
ret = ipmacsec_secy_enable_rx_sa(secy, sa, (sa_ctrl.rx.enableRx == IPMACSEC_ENABLEFLAG_ON));
}
} else if (nlcmd->command == IPMACSEC_LMICMD_SECY_TX_SA_CONTROL) {
ret = -1;
if (sa_ctrl.tx.enableTx != IPMACSEC_ENABLEFLAG_IGNORE) {
ret = ipmacsec_secy_enable_tx_sa(secy, sa, (sa_ctrl.tx.enableTx == IPMACSEC_ENABLEFLAG_ON));
}
}
} else {
ret = -1;
goto exec_fail;
}
}
break;
case IPMACSEC_LMICMD_SECY_RX_SA_STATUS:
case IPMACSEC_LMICMD_SECY_TX_SA_STATUS:
if (!isGet) {
goto no_set;
} else {
Ipmacsec_secy_sa_descriptor sa_desc;
struct {
Ipmacsec_secy_sa_descriptor sa_desc;
Ipmacsec_secy_sa_info sa_info;
Ipmacsec_secy_sa_ctrl sa_ctrl;
} param;
Ipmacsec_secy_sa *sa;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(sa_desc);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &sa_desc, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_STATUS) {
/* for RX SA, retrieve the specified RX SC */
Ipmacsec_secy_sc *rx_sc = ipmacsec_secy_lookup_rx_sc(secy, sa_desc.sci);
if (!rx_sc) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG8(ERR, "IPMACSEC-MGMT: RX SC not found (SCI = "
"%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x)",
sa_desc.sci[0],sa_desc.sci[1],sa_desc.sci[2],sa_desc.sci[3],
sa_desc.sci[4],sa_desc.sci[5],sa_desc.sci[6],sa_desc.sci[7]);
#endif
ret = -1;
goto exec_fail;
}
sa = ipmacsec_secy_lookup_rx_sa(secy, rx_sc, sa_desc.an);
} else /* IPMACSEC_LMICMD_SECY_TX_SA_STATUS */ {
sa = ipmacsec_secy_lookup_tx_sa(secy, sa_desc.an);
}
if (!sa || (sa->ref != sa_desc.ref)) {
ret = -1;
goto exec_fail;
}
(void)ipmacsec_secy_fill_sa_descriptor(secy, sa, &param.sa_desc); /* Update missing fields */
(void)ipmacsec_secy_fill_sa_info(secy, sa, &param.sa_info);
ipcom_memcpy(&param.sa_ctrl, &sa->ctrl, sizeof(Ipmacsec_secy_sa_ctrl));
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &param, sizeof(param), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_RX_SA_LIST:
case IPMACSEC_LMICMD_SECY_TX_SA_LIST:
if (!isGet) {
goto no_set;
} else {
struct Ip_nlmsghdr *nlresp = IP_NULL;
Ipmacsec_secy_sc *rx_sc = IP_NULL;
Ip_u8 an;
/* for RX SA, retrieve the specified RX SC */
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_LIST) {
Ip_u8 sci[IPMACSEC_SCI_LEN];
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(sci);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, sci, param_size);
if (ret < 0) {
goto param_ret_fail;
}
rx_sc = ipmacsec_secy_lookup_rx_sc(secy, sci);
if (!rx_sc) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG8(ERR, "IPMACSEC-MGMT: RX SC not found (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
ret = -1;
goto exec_fail;
}
}
/* Retrieve all SAs for specified SC and return info */
nlresp = ipmacsec_nlcmd_response_add(reply, nlmsg, IP_NULL, 0, src_pid);
if (nlresp == IP_NULL) {
ret = -1;
goto reply_fail;
}
for (an=0;an<secy->config.maxRxKeys;an++) {
Ipmacsec_secy_sa_info sa_info;
Ipmacsec_secy_sa_descriptor sa_desc;
Ipmacsec_secy_sa *sa = IP_NULL;
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_LIST) {
sa = ipmacsec_secy_lookup_rx_sa(secy, rx_sc, an);
} else /* IPMACSEC_LMICMD_SECY_TX_SA_LIST */ {
sa = ipmacsec_secy_lookup_tx_sa(secy, an);
}
if (sa == IP_NULL) {
continue;
}
(void)ipmacsec_secy_fill_sa_descriptor(secy, sa, &sa_desc);
ret = ipmacsec_nlcmd_add_param(reply, nlresp, &sa_desc, sizeof(sa_desc));
if (ret < (int)sizeof(sa_desc)) {
ret = -1;
goto reply_fail;
}
(void)ipmacsec_secy_fill_sa_info(secy, sa, &sa_info);
ret = ipmacsec_nlcmd_add_param(reply, nlresp, &sa_info, sizeof(sa_info));
if (ret < (int)sizeof(sa_info)) {
ret = -1;
goto reply_fail;
}
ret = ipmacsec_nlcmd_add_param(reply, nlresp, &sa->ctrl, sizeof(sa->ctrl));
if (ret < (int)sizeof(sa->ctrl)) {
ret = -1;
goto reply_fail;
}
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_SECY_STATS:
if (!isGet) {
goto no_set;
} else {
#ifdef IPMACSEC_USE_STATS
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &secy->stats, sizeof(secy->stats), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
break;
#else
goto no_stats;
#endif
}
case IPMACSEC_LMICMD_SECY_RX_SC_STATS:
case IPMACSEC_LMICMD_SECY_TX_SC_STATS:
if (!isGet) {
goto no_set;
} else {
#ifdef IPMACSEC_USE_STATS
Ip_u8 sci[IPMACSEC_SCI_LEN];
Ipmacsec_secy_sc *sc;
Ipmacsec_secy_sc_stats sc_stats;
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SC_STATS) {
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(sci);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, sci, param_size);
if (ret < 0) {
goto param_ret_fail;
}
sc = ipmacsec_secy_lookup_rx_sc(secy, sci);
} else /* IPMACSEC_LMICMD_SECY_TX_SC_STATS */ {
sc = ipmacsec_secy_get_tx_sc(secy);
}
if (!sc) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SC_STATS) {
IPCOM_LOG8(ERR, "IPMACSEC-MGMT: RX SC not found (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
ret = -1;
goto exec_fail;
}
ipcom_memcpy(&sc_stats, &sc->stats, sizeof(Ipmacsec_secy_sc_stats));
ret = ipmacsec_secy_sc_add_sa_stats(secy, sc, &sc_stats);
if (ret != 0) {
ret = -1;
goto exec_fail;
}
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &sc_stats, sizeof(sc_stats), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
break;
#else
goto no_stats;
#endif
}
case IPMACSEC_LMICMD_SECY_RX_SA_STATS:
case IPMACSEC_LMICMD_SECY_TX_SA_STATS:
if (!isGet) {
goto no_set;
} else {
#ifdef IPMACSEC_USE_STATS
Ipmacsec_secy_sa *sa;
Ipmacsec_secy_sa_descriptor sa_desc;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(sa_desc);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &sa_desc, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (nlcmd->command == IPMACSEC_LMICMD_SECY_RX_SA_STATS) {
/* for RX SA, retrieve the specified RX SC */
Ipmacsec_secy_sc *rx_sc = ipmacsec_secy_lookup_rx_sc(secy, sa_desc.sci);
if (!rx_sc) {
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG8(ERR, "IPMACSEC-MGMT: RX SC not found (SCI = "
"%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x)",
sa_desc.sci[0],sa_desc.sci[1],sa_desc.sci[2],sa_desc.sci[3],
sa_desc.sci[4],sa_desc.sci[5],sa_desc.sci[6],sa_desc.sci[7]);
#endif
ret = -1;
goto exec_fail;
}
sa = ipmacsec_secy_lookup_rx_sa(secy, rx_sc, sa_desc.an);
} else /* IPMACSEC_LMICMD_SECY_TX_SA_STATS */ {
sa = ipmacsec_secy_lookup_tx_sa(secy, sa_desc.an);
}
if (!sa || (sa->ref != sa_desc.ref)) {
ret = -1;
goto exec_fail;
}
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &sa->stats, sizeof(sa->stats), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
break;
#else
goto no_stats;
#endif
}
case IPMACSEC_LMICMD_CONTROLLED_PORT_ENABLED:
case IPMACSEC_LMICMD_PORT_ADMIN_PTP_MAC:
if (isGet) {
ret = secy->controlled_port.update_status(secy->netif);
if (ret != 0) {
ret = -1;
goto exec_fail;
}
if (nlcmd->command == IPMACSEC_LMICMD_CONTROLLED_PORT_ENABLED) {
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &secy->controlled_port.ctrl.cp_enabled,
sizeof(secy->controlled_port.ctrl.cp_enabled), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
} else {
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &secy->controlled_port.status.admin_ptp_mac,
sizeof(secy->controlled_port.status.admin_ptp_mac), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
}
} else {
if (nlcmd->command == IPMACSEC_LMICMD_CONTROLLED_PORT_ENABLED) {
Ip_bool cp_enabled;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(cp_enabled);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &cp_enabled, param_size);
if (ret < 0) {
goto param_ret_fail;
}
ret = ipmacsec_secy_cp_enable(secy, cp_enabled);
} else if (nlcmd->command == IPMACSEC_LMICMD_PORT_ADMIN_PTP_MAC) {
Ip_portif_admin_ptp_mac_val admin_p2p_mac;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(admin_p2p_mac);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &admin_p2p_mac, param_size);
if (ret < 0) {
goto param_ret_fail;
}
ret = ipmacsec_secy_cp_set_adminP2pMac(secy, admin_p2p_mac);
}
if (ret != 0) {
ret = -1;
goto exec_fail;
}
}
break;
case IPMACSEC_LMICMD_PORT_MAC_ENABLED:
case IPMACSEC_LMICMD_PORT_MAC_OPER:
case IPMACSEC_LMICMD_PORT_OPER_PTP_MAC:
if (!isGet) {
goto no_set;
} else {
Ip_bool cp, boolval;
Ipnet_portif *port = IP_FALSE;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(cp);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &cp, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (cp) {
port = &secy->controlled_port;
} else {
port = &secy->uncontrolled_port;
}
ret = port->update_status(secy->netif);
if (ret != 0) {
ret = -1;
goto exec_fail;
}
ip_assert(port);
if (nlcmd->command == IPMACSEC_LMICMD_PORT_MAC_ENABLED) {
boolval = port->status.mac_enabled;
} else if (nlcmd->command == IPMACSEC_LMICMD_PORT_MAC_OPER) {
boolval = port->status.mac_oper;
} else if (nlcmd->command == IPMACSEC_LMICMD_PORT_OPER_PTP_MAC) {
boolval = port->status.oper_ptp_mac;
} else {
ret = -1;
goto exec_fail;
}
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &boolval, sizeof(boolval), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
}
break;
case IPMACSEC_LMICMD_PORT_STATISTICS:
if (!isGet) {
goto no_set;
} else {
#ifdef IPMACSEC_USE_STATS
Ip_bool cp;
Ipnet_portif *port = IP_FALSE;
(void)ipmacsec_nlcmd_retrieve_init(nlmsg);
param_size = sizeof(cp);
ret = ipmacsec_nlcmd_retrieve_param(nlmsg, &cp, param_size);
if (ret < 0) {
goto param_ret_fail;
}
if (cp) {
port = &secy->controlled_port;
} else {
port = &secy->uncontrolled_port;
}
ret = port->update_stats(secy->netif);
if (ret != 0) {
ret = -1;
goto exec_fail;
}
ip_assert(port);
if (ipmacsec_nlcmd_response_add(reply, nlmsg, &port->stats, sizeof(port->stats), src_pid) == IP_NULL) {
ret = -1;
goto reply_fail;
}
ret = 0;
break;
#else
goto no_stats;
#endif
}
case IPMACSEC_LMICMD_SECY_RESET_STATISTICS:
if (isGet) {
goto no_get;
} else {
#ifdef IPMACSEC_USE_STATS
if (secy->ctrl.enabled) {
goto no_exec_enabled;
}
ret = ipmacsec_secy_reset_stats(secy);
if (ret != 0) {
ret = -1;
goto exec_fail;
}
ret = 0;
break;
#else
goto no_stats;
#endif
}
default:
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG4(ERR, "IPMACSEC-MGMT: Invalid MACsec netlink cmd %d (%s) for nltype %d (%s)",
nlcmd->command, ipmacsec_lmi_cmd_name(nlcmd->command),
nlmsg->nlmsg_type, ipmacsec_netlink_type_name(nlmsg->nlmsg_type));
#endif
return -1;
}
}
if (ret < 0)
goto exec_fail;
return ret;
param_ret_fail:
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG6(ERR, "IPMACSEC-MGMT: Failed to retrieve MACsec netlink cmd parameter for cmd %d (%s), nltype %d (%s)."
"param_size=%u, ret=%d",
nlcmd->command, ipmacsec_lmi_cmd_name(nlcmd->command),
nlmsg->nlmsg_type, ipmacsec_netlink_type_name(nlmsg->nlmsg_type), param_size, ret);
#endif
return ret;
reply_fail:
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG5(ERR, "IPMACSEC-MGMT: Failed to create MACsec netlink reply for cmd %d (%s), nltype %d (%s). ret=%d",
nlcmd->command, ipmacsec_lmi_cmd_name(nlcmd->command),
nlmsg->nlmsg_type, ipmacsec_netlink_type_name(nlmsg->nlmsg_type), ret);
#endif
return ret;
exec_fail:
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG5(ERR, "IPMACSEC-MGMT: Failed to execute MACsec netlink cmd %d (%s), nltype %d (%s). ret=%d",
nlcmd->command, ipmacsec_lmi_cmd_name(nlcmd->command),
nlmsg->nlmsg_type, ipmacsec_netlink_type_name(nlmsg->nlmsg_type), ret);
#endif
return ret;
no_exec_enabled:
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG4(ERR, "IPMACSEC-MGMT: Cannot execute cmd %d (%s) when SecY is enabled. nltype %d (%s)",
nlcmd->command, ipmacsec_lmi_cmd_name(nlcmd->command),
nlmsg->nlmsg_type, ipmacsec_netlink_type_name(nlmsg->nlmsg_type));
#endif
return -1;
no_get:
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG5(ERR, "IPMACSEC-MGMT: Get not allowed for MACsec netlink cmd %d (%s), nltype %d (%s). ret=%d",
nlcmd->command, ipmacsec_lmi_cmd_name(nlcmd->command),
nlmsg->nlmsg_type, ipmacsec_netlink_type_name(nlmsg->nlmsg_type), ret);
#endif
return -1;
no_set:
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG5(ERR, "IPMACSEC-MGMT: Set not allowed for MACsec netlink cmd %d (%s), nltype %d (%s). ret=%d",
nlcmd->command, ipmacsec_lmi_cmd_name(nlcmd->command),
nlmsg->nlmsg_type, ipmacsec_netlink_type_name(nlmsg->nlmsg_type), ret);
#endif
return -1;
#ifndef IPMACSEC_USE_STATS
no_stats:
#ifdef IPMACSEC_DEBUG_SECY_MGMT
IPCOM_LOG5(ERR, "IPMACSEC-MGMT: MACsec Statistics not supported. Failing netlink cmd %d (%s), nltype %d (%s). ret=%d",
nlcmd->command, ipmacsec_lmi_cmd_name(nlcmd->command),
nlmsg->nlmsg_type, ipmacsec_netlink_type_name(nlmsg->nlmsg_type), ret);
#endif
return -1;
#endif
}