/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipipsec_pfkeyv2.c,v $ $Revision: 1.98.10.13 $ * $Source: /home/interpeak/CVSRoot/ipipsec2/src/ipipsec_pfkeyv2.c,v $ * $Author: hxu0 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Lennart Bang ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * General RFC support: * RFC4301 s4.4.1p23: "For every IPsec implementation, there MUST be a management interface that allows a user or system administrator to manage the SPD." * RFC4301 s4.4.1p24: "The management interface for the SPD MUST allow creation of entries consistent with the selectors defined in Section 4.4.1.1" * RFC4301 s4.4.1p24: "... and MUST support (total) ordering of these entries" * RFC4301 s4.4.1p24: "However, this document does specify a standard set of SPD elements that all IPsec implementations MUST support." */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #ifndef IPCOM_KERNEL #define IPCOM_KERNEL #endif #include "ipipsec_config.h" #ifdef IPWRNAD /* Define to allow PF_KEY to output key material (PF_KEY sockets should be priviliged anyway) */ #define IPIPSEC_ALLOW_PFKEY_KEY_OUTPUT #endif /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include "ipipsec_h.h" #include "ipipsec_pfkeyv2.h" /* **************************************************************************** * 4 EXTERN PROTOTYPES **************************************************************************** */ IP_EXTERN Ipipsec_data ipipsec; IP_EXTERN IP_CONST Ipipsec_auth_method ipipsec_auth_method[SADB_AALG_NUM]; IP_EXTERN IP_CONST char *ipipsec_pfkeyv2_command[SADB_MAX + 2]; IP_EXTERN int ipipsec_pfkeyv2_ext_size[SADB_EXT_MAX+1]; /* **************************************************************************** * 5 DEFINES **************************************************************************** */ #define IPCOM_SYSLOG_PRIORITY IPIPSEC_SYSTEM_PRIORITY #define IPCOM_SYSLOG_FACILITY IPCOM_LOG_IPIPSEC #define IPIPSEC_EXPIRE_TYPE_HARD 0 #define IPIPSEC_EXPIRE_TYPE_SOFT 1 #define IPIPSEC_ERROUT(errval) do { retval = errval; goto errout; } while(0) /* **************************************************************************** * 6 TYPES **************************************************************************** */ /* *=========================================================================== * Ipipsec_pfkeyv2_sadb_extbits *=========================================================================== */ typedef struct Ipipsec_pfkeyv2_sadb_extbits_struct { Ip_u64 in_req; Ip_u64 in_opt; Ip_u64 out_req; Ip_u64 out_opt; } Ipipsec_pfkeyv2_sadb_extbits; /* *=========================================================================== * Ipipsec_sadb_address_full *=========================================================================== */ struct Ipipsec_sadb_address_full { struct sadb_address addr; union Ip_sockaddr_union sock; }; /* *=========================================================================== * Ipipsec_sadb_prop_full *=========================================================================== */ struct Ipipsec_sadb_prop_full { struct sadb_prop prop; struct sadb_comb comb; }; /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ IP_STATIC int ipipsec_pfkeyv2_verify_satype(Ip_u8 satype); IP_STATIC Ipipsec_sa *ipipsec_pfkeyv2_sadb_sa_lookup(Ipipsec_pfkeyv2_sadb *sadb, Ip_bool exact); IP_STATIC int ipipsec_pfkeyv2_output(Ipcom_socket *sock, void *exthdr[SADB_EXT_MAX+1], Ip_u32 sndmode, Ip_u32 saerrno); IP_STATIC int ipipsec_pfkeyv2_output_sa(Ipcom_socket *sock, Ipipsec_sa *sa, Ipipsec_pfkeyv2_sadb *sadb, Ip_u32 sndmode); IP_STATIC int ipipsec_pfkeyv2_output_flow(Ipcom_socket *sock, Ipipsec_flow *flow, Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC void ipipsec_pfkeyv2_ipaddr_to_extaddr(struct sadb_address *address, union Ip_in_addr_union *ipaddr, Ip_u16 port_n, int domain); IP_STATIC int ipipsec_pfkeyv2_sadb_to_sa(Ipipsec_pfkeyv2_sadb *sadb, Ipipsec_sa *sa, Ip_u8 msgtype); IP_STATIC int ipipsec_pfkeyv2_sadb_to_key(Ipipsec_pfkeyv2_sadb *sadb, Ipipsec_key *key); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_add(Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_delete(Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_flush(Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_sadb_to_key2(Ipipsec_pfkeyv2_sadb *sadb, Ipipsec_key *key); IP_STATIC int ipipsec_pfkeyv2_sadb_to_flowsel(Ipipsec_pfkeyv2_sadb *sadb, Ipipsec_flowsel *flowsel); IP_STATIC int ipipsec_pfkeyv2_sadb_to_flowdir(Ipipsec_pfkeyv2_sadb *sadb, int default_flowdir); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_dump(Ipcom_socket *sock, Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_update(Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_getspi(Ipipsec_pfkeyv2_sadb *sadb, struct sadb_sa *samem); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_get(Ipcom_socket *sock, Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_x_addflow(Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_x_delflow(Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_x_findflow(Ipcom_socket *sock, Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_x_dumpflow(Ipcom_socket *sock, Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_x_grpspis(Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_register(Ipcom_socket *sock, Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC void ipipsec_pfkeyv2_copy_lifetimes(struct sadb_lifetime *lt, Ipipsec_sa_lifetime *sa_lt); #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API IP_STATIC void ipipsec_pfkeyv2_create_mask(void *mask, int netbits); IP_STATIC int ipipsec_pfkeyv2_sadb_to_flowsel_linux(Ipipsec_pfkeyv2_sadb *sadb, Ipipsec_flowsel *flowsel); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_lx_spdadd(Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_lx_spddelete(Ipipsec_pfkeyv2_sadb *sadb); IP_STATIC int ipipsec_pfkeyv2_msg_sadb_lx_spdflush(Ipipsec_pfkeyv2_sadb *sadb); #endif /* **************************************************************************** * 8 DATA **************************************************************************** */ IP_STATIC Ipipsec_pfkeyv2_sadb_extbits ipipsec_pfkeyv2_sadbextbits[SADB_MAX+1] = { /* 0 SADB_RESERVED */ { 0, /* in_req */ 0, /* in_opt */ 0, /* out_req */ 0 /* out_opt */ }, /* 1 SADB_GETSPI */ { SEBIT_SPIRANGE | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST, SEBIT_SPIRANGE | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_SA | SEBIT_X_EXT_SA_OPTIONS, SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST, SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_X_EXT_SA_OPTIONS | SEBIT_SPIRANGE }, /* 2 SADB_UPDATE */ { SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST, SEBIT_SA | SEBIT_LIFETIME | SEBIT_ADDRESS | SEBIT_KEY | SEBIT_IDENTITY | SEBIT_SENSITIVITY | SEBIT_X_EXT_SA_OPTIONS | SEBIT_X_EXT_ROHC_PARAM | SEBIT_X_EXT_ROHC_INTEG_KEY, SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST, SEBIT_SA | SEBIT_LIFETIME | SEBIT_ADDRESS | SEBIT_IDENTITY | SEBIT_SENSITIVITY | SEBIT_X_EXT_SA_OPTIONS }, /* 3 SADB_ADD */ { SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST, SEBIT_SA | SEBIT_LIFETIME | SEBIT_ADDRESS | SEBIT_KEY | SEBIT_IDENTITY | SEBIT_SENSITIVITY | SEBIT_X_UDPENCAP | SEBIT_X_EXT_SA_OPTIONS | SEBIT_X_SA2 | SEBIT_LX_SA2 #ifdef IPIPSEC_USE_ROHC | SEBIT_X_EXT_ROHC_PARAM | SEBIT_X_EXT_ROHC_INTEG_KEY, #else , #endif /* IPIPSEC_USE_ROHC */ SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST, #ifdef IPIPSEC_ALLOW_PFKEY_KEY_OUTPUT SEBIT_SA | SEBIT_ADDRESS | SEBIT_LIFETIME | SEBIT_IDENTITY | SEBIT_SENSITIVITY | SEBIT_X_UDPENCAP | SEBIT_X_EXT_SA_OPTIONS | SEBIT_LX_SA2 | SEBIT_KEY #else SEBIT_SA | SEBIT_ADDRESS | SEBIT_LIFETIME | SEBIT_IDENTITY | SEBIT_SENSITIVITY | SEBIT_X_UDPENCAP | SEBIT_X_EXT_SA_OPTIONS | SEBIT_LX_SA2 #endif }, /* 4 SADB_DELETE */ { SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST, SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_KEY | SEBIT_X_EXT_SA_OPTIONS, SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST, SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_X_EXT_SA_OPTIONS }, /* 5 SADB_GET */ { SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST, SEBIT_SA | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_X_EXT_SA_OPTIONS, SEBIT_SA | SEBIT_LIFETIME_CURRENT | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST, SEBIT_SA | SEBIT_X_SA2 | SEBIT_LIFETIME | SEBIT_ADDRESS | SEBIT_KEY | SEBIT_IDENTITY | SEBIT_SENSITIVITY | SEBIT_X_EXT_SA_OPTIONS }, /* 6 SADB_ACQUIRE */ { 0, SEBIT_ADDRESS | SEBIT_IDENTITY | SEBIT_SENSITIVITY | SEBIT_PROPOSAL , 0, SEBIT_ADDRESS | SEBIT_IDENTITY | SEBIT_SENSITIVITY | SEBIT_PROPOSAL }, /* 7 SADB_REGISTER */ { 0, 0, SEBIT_SUPPORTED, SEBIT_SUPPORTED }, /* 8 SADB_EXPIRE */ { 0, 0, SEBIT_SA | SEBIT_LIFETIME_CURRENT | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST, SEBIT_SA | SEBIT_LIFETIME | SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_X_EXT_SA_OPTIONS, }, /* 9 SADB_FLUSH */ { 0, 0, 0, 0 }, /* 10 SADB_DUMP */ { 0, 0, 0, SEBIT_SA | SEBIT_LIFETIME | SEBIT_ADDRESS | SEBIT_IDENTITY | SEBIT_SENSITIVITY | SEBIT_X_UDPENCAP | SEBIT_X_EXT_SA_OPTIONS | SEBIT_KEY | SEBIT_LX_SA2, }, { 0, 0, 0, 0 }, /* 11 LX_PROMISC */ { 0, 0, 0, 0 }, /* 12 LX_PCHANGE */ /* 13 SADB_X_SPDUPDATE */ { SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY, SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY | SEBIT_LIFETIME, SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY, SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY | SEBIT_LIFETIME }, /* 14 SADB_X_SPDADD */ { SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY, SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY | SEBIT_LIFETIME, SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY, SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY | SEBIT_LIFETIME /* SEBIT_SA | SEBIT_X_MASK | (SEBIT_X_SRC_FLOW | SEBIT_X_DST_FLOW) | SEBIT_X_PROTOCOL SEBIT_X_FLOW_OPTIONS | SEBIT_X_EXT_SA_OPTIONS, SEBIT_X_MAXFLOW */ }, /* 15 LX_SPDDELETE */ { SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY, SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY | SEBIT_LIFETIME, SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY, SEBIT_ADDRESS_SRC | SEBIT_ADDRESS_DST | SEBIT_LX_POLICY | SEBIT_LIFETIME }, { 0, 0, 0, 0 }, /* 16 LX_SPDGET */ { 0, 0, 0, 0 }, /* 17 LX_SPDACQUIRE */ { 0, 0, 0, 0 }, /* 18 LX_SPDDUMP */ { 0, 0, 0, 0 }, /* 19 LX_SPDFLUSH */ { 0, 0, 0, 0 }, /* 20 LX_SPDSETIDX */ { 0, 0, 0, 0 }, /* 21 LX_SPDEXPIRE */ { 0, 0, 0, 0 }, /* 22 LX_SPDDELETE2 */ { 0, 0, 0, 0 }, /* 23 LX_NAT_T_NEW_MAPPING */ { 0, 0, 0, 0 }, /* 24 LX_MIGRATE */ { 0, 0, 0, 0 }, /* 25 */ { 0, 0, 0, 0 }, /* 26 */ { 0, 0, 0, 0 }, /* 27 */ { 0, 0, 0, 0 }, /* 28 */ { 0, 0, 0, 0 }, /* 29 */ { 0, 0, 0, 0 }, /* 30 */ { 0, 0, 0, 0 }, /* 31 */ { 0, 0, 0, 0 }, /* 32 */ /* 33 SADB_X_PROMISC */ { 0, 0, 0, 0 }, /* 34 SADB_X_ADDFLOW */ { SEBIT_X_FLOW, SEBIT_SA | SEBIT_ADDRESS_DST | SEBIT_X_MASK | SEBIT_X_FLOW | SEBIT_ADDRESS_SRC | SEBIT_X_PROTOCOL | SEBIT_X_FLOW_OPTIONS | SEBIT_X_MAXFLOW | SEBIT_X_EXT_SA_OPTIONS, SEBIT_X_FLOW, SEBIT_SA | SEBIT_ADDRESS_DST | SEBIT_X_MASK | SEBIT_X_FLOW | SEBIT_ADDRESS_SRC | SEBIT_X_PROTOCOL | SEBIT_X_FLOW_OPTIONS | SEBIT_X_MAXFLOW | SEBIT_X_EXT_SA_OPTIONS }, /* 35 SADB_X_DELFLOW */ { 0, SEBIT_SA | SEBIT_X_MASK | SEBIT_X_FLOW | SEBIT_X_PROTOCOL | SEBIT_ADDRESS_DST | SEBIT_X_FLOW_OPTIONS | SEBIT_X_MAXFLOW | SEBIT_X_EXT_SA_OPTIONS, 0, SEBIT_SA | SEBIT_X_MASK | SEBIT_X_FLOW | SEBIT_X_PROTOCOL | SEBIT_ADDRESS_DST | SEBIT_X_FLOW_OPTIONS | SEBIT_X_MAXFLOW | SEBIT_X_EXT_SA_OPTIONS }, /* 36 SADB_X_GRPSPIS */ { SEBIT_SA | SEBIT_X_SA2 | SEBIT_X_DST2 | SEBIT_ADDRESS_DST | SEBIT_X_PROTOCOL, SEBIT_SA | SEBIT_X_SA2 | SEBIT_X_DST2 | SEBIT_ADDRESS_DST | SEBIT_X_PROTOCOL | SEBIT_X_EXT_SA_OPTIONS | SEBIT_ADDRESS_SRC, SEBIT_SA | SEBIT_X_SA2 | SEBIT_X_DST2 | SEBIT_ADDRESS_DST | SEBIT_X_PROTOCOL, SEBIT_SA | SEBIT_X_SA2 | SEBIT_X_DST2 | SEBIT_ADDRESS_DST | SEBIT_X_PROTOCOL | SEBIT_X_EXT_SA_OPTIONS | SEBIT_ADDRESS_SRC }, /* 37 SADB_X_BINDSA */ { SEBIT_SA | SEBIT_X_SA2 | SEBIT_X_DST2 | SEBIT_ADDRESS_DST | SEBIT_X_PROTOCOL, SEBIT_SA | SEBIT_X_SA2 | SEBIT_X_DST2 | SEBIT_ADDRESS_DST | SEBIT_X_PROTOCOL, SEBIT_SA | SEBIT_X_SA2 | SEBIT_X_DST2 | SEBIT_ADDRESS_DST | SEBIT_X_PROTOCOL, SEBIT_SA | SEBIT_X_SA2 | SEBIT_X_DST2 | SEBIT_ADDRESS_DST | SEBIT_X_PROTOCOL }, /* 38 SADB_X_FINDFLOW */ { SEBIT_X_FLOW | SEBIT_X_PROTOCOL, SEBIT_X_FLOW | SEBIT_X_PROTOCOL | SEBIT_SA | SEBIT_ADDRESS_DST | SEBIT_X_FLOW_OPTIONS | SEBIT_X_EXT_SA_OPTIONS, SEBIT_X_FLOW | SEBIT_X_PROTOCOL | SEBIT_X_MASK, SEBIT_X_FLOW | SEBIT_X_MAXFLOW | SEBIT_X_FLOW_OPTIONS | SEBIT_X_PROTOCOL | SEBIT_X_MASK | SEBIT_SA | SEBIT_X_EXT_SA_OPTIONS }, /* 39 SADB_X_DUMPFLOW */ { 0, SEBIT_SA | SEBIT_X_FLOW_OPTIONS, SEBIT_X_FLOW | SEBIT_X_PROTOCOL | SEBIT_X_MASK, SEBIT_X_FLOW | SEBIT_X_MAXFLOW | SEBIT_X_FLOW_OPTIONS | SEBIT_X_PROTOCOL | SEBIT_X_MASK | SEBIT_SA | SEBIT_X_EXT_SA_OPTIONS | SEBIT_LX_POLICY }, /* 40 SADB_X_MIGRATE */ { SEBIT_X_EXT_KMADDRESS, SEBIT_X_EXT_KMADDRESS, SEBIT_X_EXT_KMADDRESS, SEBIT_X_EXT_KMADDRESS }, /* 41 SADB_X_EVENT */ { SEBIT_X_EXT_EVENT_INFO, SEBIT_X_EXT_EVENT_INFO, SEBIT_X_EXT_EVENT_INFO, SEBIT_X_EXT_EVENT_INFO } }; /* **************************************************************************** * 9 LOCAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipipsec_pfkeyv2_verify_satype *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_verify_satype(Ip_u8 satype) { switch(satype) { case SADB_SATYPE_ESP: if (ipipsec_conf_esp_enable != 1) return -IP_ERRNO_EOPNOTSUPP; break; #ifdef IPIPSEC_USE_AH case SADB_SATYPE_AH: if (ipipsec_conf_ah_enable != 1) return -IP_ERRNO_EOPNOTSUPP; break; #endif #ifdef IPIPSEC_USE_IPIP case SADB_X_SATYPE_IPIP: if (ipipsec_conf_ipip_allow != 1) return -IP_ERRNO_EOPNOTSUPP; break; #endif default: return -IP_ERRNO_EOPNOTSUPP; } return 0; /* ok */ } /* *=========================================================================== * ipipsec_pfkeyv2_sadb_to_key *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_sadb_to_key(Ipipsec_pfkeyv2_sadb *sadb, Ipipsec_key *key) { struct Ip_sockaddr *sockaddr; int in_addr_size, sock_in_addr_offset; Ip_u8 domain; ip_assert(sadb->msg); ipcom_memset(key, 0, sizeof(Ipipsec_key)); /* Domain, default to IPv4 */ if (sadb->address_dst) { sockaddr = (struct Ip_sockaddr *)((Ip_u8 *)sadb->address_dst + sizeof(struct sadb_address)); domain = (Ip_u8)sockaddr->sa_family; if (domain != IP_AF_INET && domain != IP_AF_INET6) return -IP_ERRNO_EINVAL; } else domain = IP_AF_INET; /* General info */ key->domain = domain; key->proto = IPIPSEC_SADB_GET_PROTO(sadb->msg->sadb_msg_satype); key->direction = IPIPSEC_DIR_ANY; /* Priority */ if (sadb->saopt) key->priority = sadb->saopt->sadb_saopt_priority; in_addr_size = 4; sock_in_addr_offset = 2; #ifdef IPCOM_USE_INET6 if (domain == IP_AF_INET6) { in_addr_size = 16; sock_in_addr_offset = 6; } #endif /* SPI */ if (sadb->sa) key->ports.espah_spi_n = sadb->sa->sadb_sa_spi; /* Destination address */ if (sadb->address_dst) { sockaddr = (struct Ip_sockaddr *)((Ip_u8 *)sadb->address_dst + sizeof(struct sadb_address)); ipcom_memcpy(&key->dst, &sockaddr->sa_data[sock_in_addr_offset], in_addr_size); } /* Source address */ if (sadb->address_src) { sockaddr = (struct Ip_sockaddr *)((Ip_u8 *)sadb->address_src + sizeof(struct sadb_address)); ipcom_memcpy(&key->src, &sockaddr->sa_data[sock_in_addr_offset], in_addr_size); } return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_sadb_to_key2 *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_sadb_to_key2(Ipipsec_pfkeyv2_sadb *sadb, Ipipsec_key *key) { struct Ip_sockaddr *sockaddr; int in_addr_size, sock_in_addr_offset; Ip_u8 domain; ip_assert(sadb->msg); ip_assert(sadb->protocol); ipcom_memset(key, 0, sizeof(Ipipsec_key)); /* Domain */ if (sadb->dst2) { sockaddr = (struct Ip_sockaddr *)((Ip_u8 *)sadb->dst2 + sizeof(struct sadb_address)); domain = (Ip_u8)sockaddr->sa_family; } else return -IP_ERRNO_EINVAL; if (domain != IP_AF_INET && domain != IP_AF_INET6) return -IP_ERRNO_EINVAL; /* General info */ key->domain = domain; key->proto = IPIPSEC_SADB_GET_PROTO(sadb->protocol->sadb_protocol_proto); key->direction = IPIPSEC_DIR_ANY; in_addr_size = 4; sock_in_addr_offset = 2; #ifdef IPCOM_USE_INET6 if (domain == IP_AF_INET6) { in_addr_size = 16; sock_in_addr_offset = 6; } #endif /* SPI */ if (sadb->sa2) key->ports.espah_spi_n = sadb->sa2->sadb_sa_spi; /* Destination address */ if (sadb->dst2) { sockaddr = (struct Ip_sockaddr *)((Ip_u8 *)sadb->dst2 + sizeof(struct sadb_address)); ipcom_memcpy(&key->dst, &sockaddr->sa_data[sock_in_addr_offset], in_addr_size); } return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_sadb_sa_lookup *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC Ipipsec_sa * ipipsec_pfkeyv2_sadb_sa_lookup(Ipipsec_pfkeyv2_sadb *sadb, Ip_bool exact) { Ipipsec_key key; ip_assert(sadb->msg); if (sadb->msg->sadb_msg_satype == SADB_X_SATYPE_BYPASS) return IP_NULL; if (sadb->sa == IP_NULL) return IP_NULL; if (ipipsec_pfkeyv2_sadb_to_key(sadb, &key) != 0) return IP_NULL; if (exact == IP_TRUE) return ipipsec_sa_lookup_exact(&key); else return ipipsec_sa_lookup(&key, 0); } /* *=========================================================================== * ipipsec_pfkeyv2_ipaddr_to_extaddr *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC void ipipsec_pfkeyv2_ipaddr_to_extaddr(struct sadb_address *address, union Ip_in_addr_union *ipaddr, Ip_u16 port, int domain) { ip_assert(address); ip_assert(domain == IP_AF_INET || domain == IP_AF_INET6); address->sadb_address_len = sizeof(struct sadb_address); #ifdef IPCOM_USE_INET if (domain == IP_AF_INET) { struct Ip_sockaddr_in *sockaddr_in; address->sadb_address_len += IP_ROUNDUP(sizeof(struct Ip_sockaddr_in), 8); sockaddr_in = (struct Ip_sockaddr_in *)((Ip_u8 *)address + sizeof(struct sadb_address)); IPCOM_SA_LEN_SET(sockaddr_in, sizeof(struct Ip_sockaddr_in)); sockaddr_in->sin_family = IP_AF_INET; sockaddr_in->sin_port = ip_htons(port); if (ipaddr != IP_NULL) sockaddr_in->sin_addr.s_addr = ipaddr->in.s_addr; } #endif #ifdef IPCOM_USE_INET6 if (domain == IP_AF_INET6) { struct Ip_sockaddr_in6 *sockaddr_in6; address->sadb_address_len += IP_ROUNDUP(sizeof(struct Ip_sockaddr_in6), 8); sockaddr_in6 = (struct Ip_sockaddr_in6 *)((Ip_u8 *)address + sizeof(struct sadb_address)); IPCOM_SA_LEN_SET(sockaddr_in6, sizeof(struct Ip_sockaddr_in6)); sockaddr_in6->sin6_family = IP_AF_INET6; if (ipaddr != IP_NULL) ipcom_memcpy(&sockaddr_in6->sin6_addr, ipaddr, 16); sockaddr_in6->sin6_port = ip_htons(port); sockaddr_in6->sin6_scope_id = 0; } #endif ip_assert((address->sadb_address_len & 0x7) == 0); address->sadb_address_len >>= 3 ; address->sadb_address_proto = 0; ip_assert(address->sadb_address_prefixlen == 0); ip_assert(address->sadb_address_reserved == 0); } /* *=========================================================================== * ipipsec_pfkeyv2_output_sa *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_output_sa(Ipcom_socket *sock, Ipipsec_sa *sa, Ipipsec_pfkeyv2_sadb *sadb, Ip_u32 sndmode) { Ip_size_t length; int retval; Ip_u8 *orgbuf, *msgbuf; int addrsize; Ip_u16 auth_len = 0; Ip_u16 encrypt_len = 0; #if defined(IPCOM_USE_INET) && defined(IPCOM_USE_INET6) addrsize = sa->key.domain == IP_AF_INET ? EXT_SADBADDR_SIZE_IN : EXT_SADBADDR_SIZE_IN6; #elif defined(IPCOM_USE_INET6) addrsize = EXT_SADBADDR_SIZE_IN6; #else addrsize = EXT_SADBADDR_SIZE_IN; #endif /* Calculate buffer length */ length = sizeof(struct sadb_sa) + (3 * sizeof(struct sadb_lifetime)) + (3 * addrsize) + sizeof(struct sadb_x_udpencap) + sizeof(struct sadb_x_saopt); if (sa->auth) { auth_len = sizeof(struct sadb_key) + IP_ROUNDUP(sa->auth->keyLen, 8); length += auth_len; } if (sa->encrypt) { encrypt_len = sizeof(struct sadb_key) + IP_ROUNDUP(sa->encrypt->keyLen, 8); length += encrypt_len; } #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API length += sizeof(struct sadb_x_sa2); #endif /* note: no support for identity & sensitivity */ /* Create PFKEYv2 output buffer */ orgbuf = ipcom_malloc(length + sizeof(Ip_u64)); if (orgbuf == IP_NULL) return -IP_ERRNO_ENOMEM; msgbuf = IP_PTR_ALIGN(orgbuf, 8); /* 64-bit align msgbuf */ ip_assert(((Ip_ptrdiff_t)msgbuf & 0x7L) == 0); ipcom_memset(msgbuf, 0, length); /* Use 'msg' from the incoming buffer. Use 'msgbuf' for the rest */ ipcom_memset(&sadb->sa, 0, SADB_EXT_MAX * sizeof(void*)); /*lint !e419 */ /* SADB_MSG */ ip_assert(sadb->msg->sadb_msg_version == PF_KEY_V2); sadb->msg->sadb_msg_satype = sa->sadb_msg_satype; ip_assert(sadb->msg->sadb_msg_reserved == 0); /* SADB_EXT_SA */ sadb->sa = (struct sadb_sa *)msgbuf; msgbuf += sizeof(struct sadb_sa); sadb->sa->sadb_sa_len = sizeof(struct sadb_sa) / sizeof(Ip_u64); sadb->sa->sadb_sa_exttype = SADB_EXT_SA; sadb->sa->sadb_sa_spi = sa->key.ports.espah_spi_n; /* [2.3.1] already in network endian */ sadb->sa->sadb_sa_replay = sa->sadb_sa_replay; sadb->sa->sadb_sa_state = sa->sadb_sa_state; sadb->sa->sadb_sa_auth = sa->sadb_sa_auth; sadb->sa->sadb_sa_encrypt = sa->sadb_sa_encrypt; sadb->sa->sadb_sa_flags = sa->sadb_sa_flags; /* SADB_EXT_ADDRESS_SRC */ sadb->address_src = (struct sadb_address *)msgbuf; msgbuf += addrsize; ipipsec_pfkeyv2_ipaddr_to_extaddr(sadb->address_src, &sa->key.src, 0, sa->key.domain); sadb->address_src->sadb_address_exttype = SADB_EXT_ADDRESS_SRC; /* SADB_EXT_ADDRESS_DST */ sadb->address_dst = (struct sadb_address *)msgbuf; msgbuf += addrsize; ipipsec_pfkeyv2_ipaddr_to_extaddr(sadb->address_dst, &sa->key.dst, 0, sa->key.domain); sadb->address_dst->sadb_address_exttype = SADB_EXT_ADDRESS_DST; /* SADB_EXT_ADDRESS_PROXY */ if (ipcom_memcmp(ipipsec.zeroes, &sa->proxy, sa->in_addr_size)) { sadb->address_proxy = (struct sadb_address *)msgbuf; msgbuf += addrsize; ipipsec_pfkeyv2_ipaddr_to_extaddr(sadb->address_proxy, &sa->proxy, 0, sa->key.domain); sadb->address_proxy->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY; } /* SADB_EXT_KEY_AUTH */ if (sa->auth) { sadb->key_auth = (struct sadb_key *)msgbuf; msgbuf += auth_len; sadb->key_auth->sadb_key_len = auth_len / sizeof(Ip_u64); sadb->key_auth->sadb_key_exttype = SADB_EXT_KEY_AUTH; sadb->key_auth->sadb_key_bits = (uint16_t)(sa->auth->keyLen * 8); } /* SADB_EXT_KEY_ENCRYPT */ if (sa->encrypt) { sadb->key_encrypt = (struct sadb_key *)msgbuf; msgbuf += encrypt_len; sadb->key_encrypt->sadb_key_len = encrypt_len / sizeof(Ip_u64); sadb->key_encrypt->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT; sadb->key_encrypt->sadb_key_bits = (uint16_t)(sa->encrypt->keyLen * 8); } /* SADB_EXT_LIFETIME_CURRENT */ sadb->lifetime_current = (struct sadb_lifetime *)msgbuf; msgbuf += sizeof(struct sadb_lifetime); sadb->lifetime_current->sadb_lifetime_len = sizeof(struct sadb_lifetime) / sizeof(Ip_u64); sadb->lifetime_current->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; ipipsec_pfkeyv2_copy_lifetimes(sadb->lifetime_current, &sa->ltcurrent); /* SADB_EXT_LIFETIME_HARD */ if (IP_U64_GETLO(sa->lthard.bytes) || sa->lthard.addtime || sa->lthard.usetime) { sadb->lifetime_hard = (struct sadb_lifetime *)msgbuf; msgbuf += sizeof(struct sadb_lifetime); sadb->lifetime_hard->sadb_lifetime_len = sizeof(struct sadb_lifetime) / sizeof(Ip_u64); sadb->lifetime_hard->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD; ipipsec_pfkeyv2_copy_lifetimes(sadb->lifetime_hard, &sa->lthard); } /* SADB_EXT_LIFETIME_SOFT */ if (IP_U64_GETLO(sa->ltsoft.bytes) || sa->ltsoft.addtime || sa->ltsoft.usetime) { sadb->lifetime_soft = (struct sadb_lifetime *)msgbuf; msgbuf += sizeof(struct sadb_lifetime); sadb->lifetime_soft->sadb_lifetime_len = sizeof(struct sadb_lifetime) / sizeof(Ip_u64); sadb->lifetime_soft->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT; ipipsec_pfkeyv2_copy_lifetimes(sadb->lifetime_soft, &sa->ltsoft); } /* SADB_X_EXT_UDPENCAP */ if (IP_BIT_ISSET(sa->sadb_sa_flags, SADB_X_SAFLAGS_UDPENCAP)) { sadb->udpencap = (struct sadb_x_udpencap *)msgbuf; msgbuf += sizeof(struct sadb_x_udpencap); sadb->udpencap->sadb_x_udpencap_len = sizeof(struct sadb_x_udpencap) / sizeof(Ip_u64); sadb->udpencap->sadb_x_udpencap_exttype = SADB_X_EXT_UDPENCAP; sadb->udpencap->sadb_x_udpencap_src_port= sa->encap_srcport_n; sadb->udpencap->sadb_x_udpencap_dst_port= sa->encap_dstport_n; } /* SADB_X_EXT_SA_OPTIONS */ if (sa->key.priority != 0) { sadb->saopt = (struct sadb_x_saopt *)msgbuf; msgbuf += sizeof(struct sadb_x_saopt); sadb->saopt->sadb_saopt_len = sizeof(struct sadb_x_saopt) / sizeof(Ip_u64); sadb->saopt->sadb_saopt_exttype = SADB_X_EXT_SA_OPTIONS; sadb->saopt->sadb_saopt_priority = sa->key.priority; } /* SADB_LX_EXT_SA2 */ #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API sadb->lx_sa2 = (struct sadb_x_sa2 *)msgbuf; sadb->lx_sa2->sadb_x_sa2_len = sizeof(struct sadb_x_sa2) >> 3; sadb->lx_sa2->sadb_x_sa2_exttype = SADB_LX_EXT_SA2; if (IP_BIT_ISSET(sa->sadb_sa_flags, SADB_X_SAFLAGS_TUNNEL)) sadb->lx_sa2->sadb_x_sa2_mode = IPSEC_MODE_TUNNEL; else sadb->lx_sa2->sadb_x_sa2_mode = IPSEC_MODE_TRANSPORT; sadb->lx_sa2->sadb_x_sa2_sequence = 0; sadb->lx_sa2->sadb_x_sa2_reqid = sa->reqid; #endif /* IPIPSEC_USE_LINUX_PFKEYV2_API */ /* Send the message */ retval = ipipsec_pfkeyv2_output(sock, (void *)sadb, sndmode, 0); ipcom_free(orgbuf); return retval; } /* *=========================================================================== * ipipsec_pfkeyv2_sadb_to_flowdir *=========================================================================== * Description: Get flow direction from key message * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_sadb_to_flowdir(Ipipsec_pfkeyv2_sadb *sadb, int default_flowdir) { int flowdir; flowdir = default_flowdir; #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API /* Get flow direction linux style */ if (sadb->lx_policy != IP_NULL) { switch (sadb->lx_policy->sadb_x_policy_dir) { case IPSEC_DIR_INBOUND: return IPIPSEC_DIR_IN; case IPSEC_DIR_OUTBOUND: return IPIPSEC_DIR_OUT; case IPSEC_DIR_ANY: case IPSEC_DIR_FWD: /*!!what to do */ default: return -IP_ERRNO_EINVAL; } } #endif /* IPIPSEC_USE_LINUX_PFKEYV2_API */ if (sadb->sa != IP_NULL) { /* Check sadb_sa_flags first to not change behaviour for older applications */ flowdir = IP_BIT_ISSET(sadb->sa->sadb_sa_flags, SADB_X_SAFLAGS_INGRESS_FLOW) ? IPIPSEC_DIR_IN : IPIPSEC_DIR_OUT; } else if (sadb->flowopt && sadb->flowopt->sadb_flowopt_direction > 0) { /* Interpeak proprietaty style */ flowdir = sadb->flowopt->sadb_flowopt_direction - 1; } if (flowdir != IPIPSEC_DIR_IN && flowdir != IPIPSEC_DIR_OUT && flowdir != IPIPSEC_DIR_ANY) return -IP_ERRNO_EINVAL; return flowdir; } /* *=========================================================================== * ipipsec_pfkeyv2_output_flow *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_output_flow(Ipcom_socket *sock, Ipipsec_flow *flow, Ipipsec_pfkeyv2_sadb *sadb) { Ip_size_t length; int retval; Ip_u8 *orgbuf, *msgbuf; int addrsize; Ip_u16 src_port, dst_port; #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API struct policy_struct { struct sadb_x_policy policy; struct sadb_x_ipsecrequest xisr; union Ip_sockaddr_union sockbuf[2]; } *pol; #endif ip_assert(flow != IP_NULL); #if defined(IPCOM_USE_INET) && defined(IPCOM_USE_INET6) addrsize = flow->flowsel.key.domain == IP_AF_INET ? EXT_SADBADDR_SIZE_IN : EXT_SADBADDR_SIZE_IN6; #elif defined(IPCOM_USE_INET6) addrsize = EXT_SADBADDR_SIZE_IN6; #else addrsize = EXT_SADBADDR_SIZE_IN; #endif switch (flow->flowsel.key.proto) { case IP_IPPROTO_TCP: case IP_IPPROTO_UDP: src_port = ip_ntohs(flow->flowsel.key.ports.udptcp_srcdst_n[0]); dst_port = ip_ntohs(flow->flowsel.key.ports.udptcp_srcdst_n[1]); break; default: src_port = dst_port = 0; break; } length = 6 * addrsize + sizeof(struct sadb_protocol) + sizeof(struct sadb_x_flowopt); #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API length += sizeof(*pol); #endif orgbuf = ipcom_malloc(length + sizeof(Ip_u64)); if (orgbuf == IP_NULL) return -IP_ERRNO_ENOMEM; msgbuf = IP_PTR_ALIGN(orgbuf, 8); /* 64-bit align msgbuf */ ip_assert(((Ip_ptrdiff_t)msgbuf & 0x7L) == 0); ipcom_memset(msgbuf, 0, length); /* Use 'msg' from the incoming buffer. Use 'msgbuf' for the rest */ ipcom_memset(&sadb->sa, 0, SADB_EXT_MAX * sizeof(void*)); /*lint !e419 */ /* SADB_MSG */ ip_assert(sadb->msg->sadb_msg_version == PF_KEY_V2); if (flow->flowtype == SADB_X_FLOW_TYPE_BYPASS) sadb->msg->sadb_msg_satype = SADB_X_SATYPE_BYPASS; else if (flow->sa != IP_NULL) sadb->msg->sadb_msg_satype = flow->sa->sadb_msg_satype; else sadb->msg->sadb_msg_satype = SADB_SATYPE_UNSPEC; ip_assert(sadb->msg->sadb_msg_reserved == 0); /* SADB_X_EXT_SRC_MASK */ sadb->src_mask = (struct sadb_address *)msgbuf; msgbuf += addrsize; ipipsec_pfkeyv2_ipaddr_to_extaddr(sadb->src_mask, &flow->flowsel.mask.src, 0, flow->flowsel.key.domain); sadb->src_mask->sadb_address_exttype = SADB_X_EXT_SRC_MASK; /* SADB_X_EXT_DST_MASK */ sadb->dst_mask = (struct sadb_address *)msgbuf; msgbuf += addrsize; ipipsec_pfkeyv2_ipaddr_to_extaddr(sadb->dst_mask, &flow->flowsel.mask.dst, 0, flow->flowsel.key.domain); sadb->dst_mask->sadb_address_exttype = SADB_X_EXT_DST_MASK; /* SADB_X_EXT_PROTOCOL */ sadb->protocol = (struct sadb_protocol *)msgbuf; msgbuf += sizeof(struct sadb_protocol); sadb->protocol->sadb_protocol_len = sizeof(struct sadb_protocol) >> 3; sadb->protocol->sadb_protocol_exttype = SADB_X_EXT_PROTOCOL; sadb->protocol->sadb_protocol_proto = flow->flowsel.key.proto; /* TCP, UDP, etc */ sadb->protocol->sadb_protocol_ports0 = flow->flowsel.key.ports.icmp_typecode[0]; sadb->protocol->sadb_protocol_ports1 = flow->flowsel.key.ports.icmp_typecode[1]; /* SADB_X_EXT_FLOW_OPTIONS */ sadb->flowopt = (struct sadb_x_flowopt *)msgbuf; msgbuf += sizeof(struct sadb_x_flowopt); sadb->flowopt->sadb_flowopt_len = sizeof(struct sadb_x_flowopt) >> 3; sadb->flowopt->sadb_flowopt_exttype = SADB_X_EXT_FLOW_OPTIONS; sadb->flowopt->sadb_flowopt_type = flow->flowtype; sadb->flowopt->sadb_flowopt_direction = flow->flowsel.key.direction + 1; /* 0=any, 1=in, 2=out */ ip_assert(sadb->flowopt->sadb_flowopt_direction <= 2); sadb->flowopt->sadb_flowopt_priority = flow->flowsel.key.priority; sadb->flowopt->sadb_flowopt_dscp = flow->flowsel.key.dscp; sadb->flowopt->sadb_flowopt_id = flow->id; /* SADB_X_EXT_SRC_FLOW */ sadb->src_flow = (struct sadb_address *)msgbuf; msgbuf += addrsize; ipipsec_pfkeyv2_ipaddr_to_extaddr(sadb->src_flow, &flow->flowsel.key.src, src_port, flow->flowsel.key.domain); sadb->src_flow->sadb_address_exttype = SADB_X_EXT_SRC_FLOW; /* SADB_X_EXT_DST_FLOW */ sadb->dst_flow = (struct sadb_address *)msgbuf; msgbuf += addrsize; ipipsec_pfkeyv2_ipaddr_to_extaddr(sadb->dst_flow, &flow->flowsel.key.dst, dst_port, flow->flowsel.key.domain); sadb->dst_flow->sadb_address_exttype = SADB_X_EXT_DST_FLOW; /* Optional SADB_X_EXT_SRCMAX_FLOW */ if (ip_ntohs(flow->flowsel.key.ports.udptcp_srcdst_n[0]) != flow->flowsel.src_max_port || ipcom_memcmp(&flow->flowsel.key.src, &flow->flowsel.src_max, flow->flowsel.key.domain == IP_AF_INET ? 4 : 16)) { sadb->srcmax_flow = (struct sadb_address *)msgbuf; msgbuf += addrsize; ipipsec_pfkeyv2_ipaddr_to_extaddr(sadb->srcmax_flow, &flow->flowsel.src_max, flow->flowsel.src_max_port, flow->flowsel.key.domain); sadb->srcmax_flow->sadb_address_exttype = SADB_X_EXT_SRCMAX_FLOW; } /* Optional SADB_X_EXT_DSTMAX_FLOW */ if (ip_ntohs(flow->flowsel.key.ports.udptcp_srcdst_n[1]) != flow->flowsel.dst_max_port || ipcom_memcmp(&flow->flowsel.key.dst, &flow->flowsel.dst_max, flow->flowsel.key.domain == IP_AF_INET ? 4 : 16)) { sadb->dstmax_flow = (struct sadb_address *)msgbuf; msgbuf += addrsize; ipipsec_pfkeyv2_ipaddr_to_extaddr(sadb->dstmax_flow, &flow->flowsel.dst_max, flow->flowsel.dst_max_port, flow->flowsel.key.domain); sadb->dstmax_flow->sadb_address_exttype = SADB_X_EXT_DSTMAX_FLOW; } #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API if (flow->sasel) { sadb->lx_policy = (struct sadb_x_policy *)msgbuf; pol = (struct policy_struct *)sadb->lx_policy; /* POLICY */ pol->policy.sadb_x_policy_len = sizeof(struct sadb_x_policy); pol->policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY; switch (flow->flowtype) { case SADB_X_FLOW_TYPE_BYPASS: pol->policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS; break; case SADB_X_FLOW_TYPE_DENY: pol->policy.sadb_x_policy_type = IPSEC_POLICY_DISCARD; break; default: pol->policy.sadb_x_policy_type = IPSEC_POLICY_IPSEC; break; } if (flow->sasel->direction == IPIPSEC_DIR_OUT) pol->policy.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND; else pol->policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND; pol->policy.sadb_x_policy_id = flow->id; pol->policy.sadb_x_policy_priority = (uint32_t)flow->flowsel.key.priority; /* IPSECREQUEST (Used as a SA-selector) */ pol->policy.sadb_x_policy_len += sizeof(struct sadb_x_ipsecrequest); pol->xisr.sadb_x_ipsecrequest_len = sizeof(struct sadb_x_ipsecrequest); pol->xisr.sadb_x_ipsecrequest_proto = flow->sasel->proto; pol->xisr.sadb_x_ipsecrequest_mode = flow->sasel->mode; pol->xisr.sadb_x_ipsecrequest_reqid = flow->sasel->reqid; pol->xisr.sadb_x_ipsecrequest_level = flow->sasel->level; /* Optional addresses */ if (pol->xisr.sadb_x_ipsecrequest_mode == IPSEC_MODE_TUNNEL) { #ifdef IPCOM_USE_INET if (flow->sasel->domain == IP_AF_INET) { struct Ip_sockaddr_in *sockaddr_in; sockaddr_in = (struct Ip_sockaddr_in *)pol->sockbuf; ipcom_memset(sockaddr_in, 0, 2 * sizeof(struct Ip_sockaddr_in)); pol->policy.sadb_x_policy_len += (2 * sizeof(struct Ip_sockaddr_in)); pol->xisr.sadb_x_ipsecrequest_len += (2 * sizeof(struct Ip_sockaddr_in)); IPCOM_SA_LEN_SET(sockaddr_in, sizeof(struct Ip_sockaddr_in)); sockaddr_in->sin_family = IP_AF_INET; sockaddr_in->sin_addr.s_addr = flow->sasel->src_tunnel.in.s_addr; sockaddr_in++; IPCOM_SA_LEN_SET(sockaddr_in, sizeof(struct Ip_sockaddr_in)); sockaddr_in->sin_family = IP_AF_INET; sockaddr_in->sin_addr.s_addr = flow->sasel->dst_tunnel.in.s_addr; } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (flow->sasel->domain == IP_AF_INET6) { struct Ip_sockaddr_in6 *sockaddr_in6; sockaddr_in6 = (struct Ip_sockaddr_in6 *)pol->sockbuf; ipcom_memset(sockaddr_in6, 0, 2 * sizeof(struct Ip_sockaddr_in6)); pol->policy.sadb_x_policy_len += (2 * sizeof(struct Ip_sockaddr_in6)); pol->xisr.sadb_x_ipsecrequest_len += (2 * sizeof(struct Ip_sockaddr_in6)); IPCOM_SA_LEN_SET(sockaddr_in6, sizeof(struct Ip_sockaddr_in6)); sockaddr_in6->sin6_family = IP_AF_INET6; ipcom_memcpy(&sockaddr_in6->sin6_addr, &flow->sasel->src_tunnel.in6, 16); sockaddr_in6++; IPCOM_SA_LEN_SET(sockaddr_in6, sizeof(struct Ip_sockaddr_in6)); sockaddr_in6->sin6_family = IP_AF_INET6; ipcom_memcpy(&sockaddr_in6->sin6_addr, &flow->sasel->dst_tunnel.in6, 16); } #endif /* IPCOM_USE_INET6 */ } /* Adjust total policy length and step msg buffer */ msgbuf += pol->policy.sadb_x_policy_len; pol->policy.sadb_x_policy_len /= sizeof(Ip_u64); } #endif /* IPIPSEC_USE_LINUX_PFKEYV2_API */ /* Send the message */ retval = ipipsec_pfkeyv2_output(sock, (void *)sadb, IPIPSEC_SNDMODE_UNICAST, 0); ipcom_free(orgbuf); return retval; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_dump *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_dump(Ipcom_socket *sock, Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_sa *sa, *last; int retval; int domain_index; Ip_u8 satype; ip_assert(sadb->msg); ip_assert(sadb->msg->sadb_msg_type == SADB_DUMP); satype = sadb->msg->sadb_msg_satype; last = IP_NULL; for (domain_index = 0; domain_index < 2; domain_index++) { for(sa = IPCOM_LIST_FIRST(&ipipsec.shm->sa_head[domain_index]); sa != IP_NULL; sa = IPCOM_LIST_NEXT(&sa->sa_list)) if (satype == sa->sadb_msg_satype || satype == SADB_SATYPE_UNSPEC) { if (last) { retval = ipipsec_pfkeyv2_output_sa(sock, last, sadb, IPIPSEC_SNDMODE_UNICAST); if (retval != 0) return retval; } last = sa; } } /* Write the last one with seq field of zero */ if (last) { sadb->msg->sadb_msg_seq = 0; retval = ipipsec_pfkeyv2_output_sa(sock, last, sadb, IPIPSEC_SNDMODE_UNICAST); if (retval != 0) return retval; } else return -IP_ERRNO_ESRCH; return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_output *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_output(Ipcom_socket *sock, void *exthdr[SADB_EXT_MAX+1], Ip_u32 sndmode, Ip_u32 saerrno) { int i; Ip_size_t length; Ipipsec_pfkeyv2_sadb *sadb; Ipcom_pkt *pkt; Ip_u64 extmap; Ipipsec_eio_output eio_output; sadb = (Ipipsec_pfkeyv2_sadb *)exthdr; ip_assert(sadb); ip_assert(sadb->msg); ip_assert(saerrno <= 255); if (saerrno != 0) { /* Pass errno in message, clear all extensions */ ipcom_memset(&exthdr[1], 0, SADB_EXT_MAX * sizeof(void*)); } else { /* Only send extensions that are ok */ for (i = 1, extmap = 0; i <= SADB_EXT_MAX; i++) if (exthdr[i]) { if (IP_BIT_ISSET(ipipsec_pfkeyv2_sadbextbits[sadb->msg->sadb_msg_type].out_opt, (1ULL << i))) extmap |= (1ULL << i); else { exthdr[i] = IP_NULL; } } /* Check if the required OUT extensions are there */ if ((extmap & ipipsec_pfkeyv2_sadbextbits[sadb->msg->sadb_msg_type].out_req) != ipipsec_pfkeyv2_sadbextbits[sadb->msg->sadb_msg_type].out_req) { IPCOM_LOG3(ERR, "ipipsec_pfkeyv2_output() :: missing mandatory extension (msgtype=%d sndmap=0x%lx reqmap=0x%lx", sadb->msg->sadb_msg_type, extmap, ipipsec_pfkeyv2_sadbextbits[sadb->msg->sadb_msg_type].out_req); /* Pass internal errno in message, clear all extensions */ saerrno = IP_ERRNO_EINVAL; ipcom_memset(&exthdr[1], 0, SADB_EXT_MAX * sizeof(void*)); } } for (length = sizeof(struct sadb_msg), i = 1; i <= SADB_EXT_MAX; i++) if (exthdr[i]) length += (((struct sadb_ext *)exthdr[i])->sadb_ext_len * sizeof(Ip_u64)); /* Allocate packet */ pkt = ipcom_pkt_malloc(length + 7, 0); if (pkt == IP_NULL) return -IP_ERRNO_ENOMEM; pkt->start = (int)((Ip_ptrdiff_t)pkt->data & 0x7); pkt->ipstart = pkt->start; pkt->end = pkt->start; /* SADB_MSG */ ip_assert(sadb->msg->sadb_msg_version == PF_KEY_V2); sadb->msg->sadb_msg_len = (Ip_u16)length / sizeof(Ip_u64); sadb->msg->sadb_msg_errno = (Ip_u8)saerrno; ip_assert(sadb->msg->sadb_msg_errno < 100); /* detect unsigned wrap */ /* Copy over all headers */ ipcom_memcpy(&pkt->data[pkt->end], sadb->msg, sizeof(struct sadb_msg)); pkt->end += sizeof(struct sadb_msg); for (i = 1; i <= SADB_EXT_MAX; i++) if (exthdr[i] != IP_NULL) { ipcom_memcpy(&pkt->data[pkt->end], exthdr[i], ((struct sadb_ext *)exthdr[i])->sadb_ext_len * sizeof(Ip_u64)); pkt->end += ((struct sadb_ext *)exthdr[i])->sadb_ext_len * sizeof(Ip_u64); } /* Output packet to key socket */ eio_output.sndmode = sndmode; eio_output.sock = sock; eio_output.pkt = pkt; eio_output.sadb_msg_type = (Ip_u16)sadb->msg->sadb_msg_type; eio_output.sadb_msg_satype = (Ip_u16)sadb->msg->sadb_msg_satype; (void)ipcom_ipsec_ioevent(IPIPSEC_EIOXOUTPUT, &eio_output); return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_flush *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_flush(Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_sa *sa, *next; int dir, domain_index; Ip_u8 satype; ip_assert(sadb->msg); ip_assert(sadb->msg->sadb_msg_type == SADB_FLUSH); satype = sadb->msg->sadb_msg_satype; switch (satype) { case SADB_X_SATYPE_BYPASS: /* Remove all bypass flows */ ipipsec_flow_free_many(IP_AF_INET, SADB_X_FLOW_TYPE_BYPASS, IPIPSEC_DIR_OUT); ipipsec_flow_free_many(IP_AF_INET6, SADB_X_FLOW_TYPE_BYPASS, IPIPSEC_DIR_IN); return 0; default: for (domain_index = 0; domain_index < 2; domain_index++) { /* Remove all SA's */ for(sa = IPCOM_LIST_FIRST(&ipipsec.shm->sa_head[domain_index]); sa != IP_NULL; sa = next) { next = IPCOM_LIST_NEXT(&sa->sa_list); if (satype == sa->sadb_msg_satype || satype == SADB_SATYPE_UNSPEC) { ipipsec_sa_free(sa, IP_FALSE, IP_TRUE); } } } /*!!TODO: not for linux. Remove all flows */ for (dir = 0; dir < 2; dir++) { ipipsec_flow_free_many(IP_AF_INET, 0, dir); ipipsec_flow_free_many(IP_AF_INET6, 0, dir); } break; } return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_sadb_to_sa *=========================================================================== * Description: Retrieve information from KEY structure to SA * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_sadb_to_sa(Ipipsec_pfkeyv2_sadb *sadb, Ipipsec_sa *sa, Ip_u8 msgtype) { int retval; struct Ip_sockaddr *sockaddr; ip_assert(sa->key.domain == IP_AF_INET || sa->key.domain == IP_AF_INET6); /***** ADD or UPDATE LARVAL *****/ if (msgtype == SADB_ADD || msgtype == SADB_UPDATE) { Ip_u32 bytes; /* SA options */ if (sadb->saopt != IP_NULL) sa->key.priority = sadb->saopt->sadb_saopt_priority; /* SADB_EXT_SA */ ip_assert(sadb->sa != IP_NULL); if (IP_BIT_ISFALSE(sadb->sa->sadb_sa_flags, SADB_X_SAFLAGS_NOREPLAY)) sa->sadb_sa_replay = IP_ROUNDUP(sadb->sa->sadb_sa_replay, 8); sa->sadb_sa_state = SADB_SASTATE_MATURE; sa->sadb_sa_flags = sadb->sa->sadb_sa_flags; if ((sadb->msg->sadb_msg_satype == SADB_SATYPE_ESP || sadb->msg->sadb_msg_satype == SADB_SATYPE_AH) && ipcom_sysvar_get_as_int("ipipsec.pfkey.esn_is_default", 1) != 0) IP_BIT_SET(sa->sadb_sa_flags, SADB_X_SAFLAGS_ESN); #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API /* SADB_LX_EXT_SA2 */ if (sadb->lx_sa2) { switch (sadb->lx_sa2->sadb_x_sa2_mode) { case IPSEC_MODE_TRANSPORT: IP_BIT_CLR(sa->sadb_sa_flags, SADB_X_SAFLAGS_TUNNEL); break; case IPSEC_MODE_TUNNEL: IP_BIT_SET(sa->sadb_sa_flags, SADB_X_SAFLAGS_TUNNEL); break; default: break; } sa->reqid = sadb->lx_sa2->sadb_x_sa2_reqid; /*!TODO: uint32_t sadb_x_sa2_sequence; */ } #endif /* #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API */ /* Bitmap allocation only on ADD of new SA */ if (msgtype == SADB_ADD) { #ifdef IPIPSEC_USE_BITARRAY /* Create replay bitmap if required */ if (sa->sadb_sa_replay > 32) { if (IP_BIT_ISFALSE(sa->sadb_sa_flags, SADB_X_SAFLAGS_ESN)) return -IP_ERRNO_EINVAL; ip_assert(sa->replay_bitarray == IP_NULL); sa->replay_bitarray = ipcom_bitarray_new(sa->sadb_sa_replay / 8); if (sa->replay_bitarray == IP_NULL) return -IP_ERRNO_ENOMEM; } #else if (sa->sadb_sa_replay > 32) return -IP_ERRNO_EINVAL; #endif /* IPIPSEC_USE_BITARRAY */ } /*** ESP/AH/IPIP specifics ***/ switch(sadb->msg->sadb_msg_satype) { /* ESP - Encryption/Authentication */ case SADB_SATYPE_ESP: /* Do not accept both NULL auth and NULL encrypt. * RFC4301 s4.2p17: "A compliant implementation MUST NOT allow instantiation of an ESP SA that employs both NULL encryption and no integrity algorithm." */ ip_assert(sadb->sa != IP_NULL); #ifndef IPIPSEC_USE_NULL_NULL if ((sadb->sa->sadb_sa_auth == SADB_AALG_NONE || sadb->sa->sadb_sa_auth == SADB_X_AALG_NULL) && (sadb->sa->sadb_sa_encrypt == SADB_EALG_NONE || sadb->sa->sadb_sa_encrypt == SADB_EALG_NULL)) return -IP_ERRNO_EINVAL; #endif /* IPIPSEC_USE_NULL_NULL */ sa->sadb_sa_auth = sadb->sa->sadb_sa_auth; sa->sadb_sa_encrypt = sadb->sa->sadb_sa_encrypt; sa->header_size = 4 /* spi field */; if (IP_BIT_ISFALSE(sa->sadb_sa_flags, SADB_X_SAFLAGS_NOREPLAY)) sa->header_size += 4; /* seq mandatory for 'NEW' ESP */ /* ESP header/trailer lengths */ sa->header_space = sa->header_size; sa->trailer_space += 2; /* length & protocol field */ if (sadb->sa->sadb_sa_encrypt == SADB_EALG_NONE || sadb->sa->sadb_sa_encrypt == SADB_EALG_NULL) sa->trailer_space += 4; /* ESP null cipher can get padded upto 4 bytes */ break; /* AH - Authentication */ #ifdef IPIPSEC_USE_AH case SADB_SATYPE_AH: /* Do not accept a NULL AH algorithm */ ip_assert(sadb->sa != IP_NULL); if (sadb->sa->sadb_sa_auth == SADB_AALG_NONE || sadb->sa->sadb_sa_auth == SADB_X_AALG_NULL) return -IP_ERRNO_EINVAL; sa->sadb_sa_auth = sadb->sa->sadb_sa_auth; ip_assert(sa->sadb_sa_encrypt == SADB_EALG_NONE || sa->sadb_sa_encrypt == SADB_EALG_NULL); /* AH header length */ sa->header_size = 8; /* [NH,payload len,reserved] + SPI */ if (IP_BIT_ISFALSE(sa->sadb_sa_flags, SADB_X_SAFLAGS_NOREPLAY)) sa->header_size += 4; /* seq mandatory for 'NEW' AH */ /* Space for the digest/checksum in AH header is added in ipipsec_auth_setkey() */ sa->header_space = sa->header_size; break; #endif /* IPIPSEC_USE_AH */ /* IP in IP encapsulation */ #ifdef IPIPSEC_USE_IPIP case SADB_X_SATYPE_IPIP: /* Do not accept auth or encrypt */ if ((sadb->sa->sadb_sa_auth != SADB_AALG_NONE && sadb->sa->sadb_sa_auth != SADB_X_AALG_NULL) || (sadb->sa->sadb_sa_encrypt != SADB_EALG_NONE && sadb->sa->sadb_sa_encrypt != SADB_EALG_NULL)) return -IP_ERRNO_EINVAL; ip_assert(sa->sadb_sa_auth == SADB_AALG_NONE || sa->sadb_sa_auth == SADB_X_AALG_NULL); ip_assert(sa->sadb_sa_encrypt == SADB_EALG_NONE || sa->sadb_sa_encrypt == SADB_EALG_NULL); /* Force tunnel mode */ IP_BIT_SET(sa->sadb_sa_flags, SADB_X_SAFLAGS_TUNNEL); break; #endif default: IPCOM_LOG1(ERR, "ipipsec_pfkeyv2_sadb_to_sa() :: unknown satype (%d)", sadb->msg->sadb_msg_satype); IP_PANIC(); break; } /* SADB_EXT_KEY_AUTH */ ip_assert(sadb->sa != IP_NULL); if (sadb->sa->sadb_sa_auth != SADB_AALG_NONE && sadb->sa->sadb_sa_auth != SADB_X_AALG_NULL) { IP_CONST Ipipsec_auth_method *aalg; if (sadb->key_auth == IP_NULL) return -IP_ERRNO_EINVAL; bytes = sadb->key_auth->sadb_key_bits >> 3; if ((bytes + sizeof(struct sadb_key)) > (sadb->key_auth->sadb_key_len * sizeof(Ip_u64))) return -IP_ERRNO_EINVAL; /* Set the key */ retval = ipipsec_auth_setkey(&sa->auth, sadb->sa->sadb_sa_auth, (Ip_u8 *)sadb->key_auth + sizeof(struct sadb_key), bytes); if (retval != 0) return retval; #if defined(IPHWCRYPTO) || defined(IPCOM_USE_FORWARDER_IPSEC) /* Hardware crypto. Note, we must store raw key */ ipcom_memcpy(sa->esp_authkey, (Ip_u8 *)sadb->key_auth + sizeof(struct sadb_key), IP_MIN(sadb->key_auth->sadb_key_bits >> 3, sizeof(sa->esp_authkey))); #endif ip_assert(sa->auth); aalg = &ipipsec_auth_method[sa->auth->aindex]; ip_assert(aalg && aalg->update); #ifdef IPIPSEC_USE_AH /* Add header space for the digest/checksum */ if (sadb->msg->sadb_msg_satype == SADB_SATYPE_AH) sa->header_space += aalg->auth_bytes; #endif /* Add trailer space for the digest/checksum */ if (sadb->msg->sadb_msg_satype == SADB_SATYPE_ESP) sa->trailer_space += aalg->auth_bytes; } #ifdef IPIPSEC_USE_ROHC /* SADB_X_EXT_ROHC_INTEG_KEY */ if ( sadb->rohc != IP_NULL ) { IPCOM_LOG1(DEBUG2, "sadb->rohc->sadb_x_rohc_icv_len = %u", sadb->rohc->sadb_x_rohc_icv_len); IPCOM_LOG1(DEBUG2, "sadb->rohc->sadb_x_rohc_integ_alg = %u", sadb->rohc->sadb_x_rohc_integ_alg); IPCOM_LOG1(DEBUG2, "sadb->rohc->sadb_x_rohc_ifname = %s", sadb->rohc->sadb_x_rohc_ifname); sa->rohc.integ_alg = sadb->rohc->sadb_x_rohc_integ_alg; sa->rohc.icv_len = sadb->rohc->sadb_x_rohc_icv_len; /* Check if a integrity algorithm is set and if we should add * an ICV to each packet */ if ( sa->rohc.integ_alg != SADB_AALG_NONE && sa->rohc.icv_len > 0 ) { if (sadb->rohc_integ_key == IP_NULL) { IPCOM_LOG0(ERR, "ipipsec_pfkeyv2_sadb_to_sa() ::"\ " no ROHC integrity key provided"); return -IP_ERRNO_EINVAL; } bytes = sadb-> rohc_integ_key->sadb_key_bits >> 3; if ((bytes + sizeof(struct sadb_key)) > (sadb->rohc_integ_key->sadb_key_len * sizeof(Ip_u64))) { IPCOM_LOG0(ERR, "ipipsec_pfkeyv2_sadb_to_sa() ::"\ " inconsistent ROHC integrity key length"); return -IP_ERRNO_EINVAL; } /* Set the key */ retval = ipipsec_auth_setkey(&sa->rohc.integ, sa->rohc.integ_alg, (Ip_u8 *)sadb->rohc_integ_key + sizeof(struct sadb_key), bytes); if (retval != 0) { IPCOM_LOG1(ERR, "ipipsec_pfkeyv2_sadb_to_sa() ::"\ " invalid ROHC integrity algorithm (%u)", sa->rohc.integ_alg); return retval; } /* The ICV length cannot be greater than the hash digest length */ if ( sa->rohc.icv_len > ipipsec_auth_digest_len(sa->rohc.integ_alg) ) { IPCOM_LOG1(ERR, "ipipsec_pfkeyv2_sadb_to_sa() ::"\ " invalid ROHC ICV length (%u)", sa->rohc.icv_len); return -IP_ERRNO_EINVAL; } } /* Create a "fake" interface that is passed into the ROHC module */ ipcom_strlcpy(sa->rohc.netif.name, sadb->rohc->sadb_x_rohc_ifname, sizeof(sa->rohc.netif.name)); sa->rohc.netif.type = IP_IFT_ETHER; /* Enable ROHC on SA */ IP_BIT_SET(sa->sadb_sa_flags, SADB_X_SAFLAGS_ROHC); } #endif /* IPIPSEC_USE_ROHC */ /* SADB_EXT_KEY_ENCRYPT */ ip_assert(sadb->sa != IP_NULL); if (sadb->sa->sadb_sa_encrypt != SADB_EALG_NONE && sadb->sa->sadb_sa_encrypt != SADB_EALG_NULL) { if (sadb->key_encrypt == IP_NULL) return -IP_ERRNO_EINVAL; if (sadb->msg->sadb_msg_satype != SADB_SATYPE_ESP) return -IP_ERRNO_EINVAL; /* Check key length */ bytes = sadb->key_encrypt->sadb_key_bits >> 3; if ((bytes + sizeof(struct sadb_key)) > (sadb->key_encrypt->sadb_key_len * sizeof(Ip_u64))) return -IP_ERRNO_EINVAL; /* Set the key */ retval = ipipsec_encrypt_setkey(sa, sadb->sa->sadb_sa_encrypt, (Ip_u8 *)sadb->key_encrypt + sizeof(struct sadb_key), bytes); if (retval != 0) return retval; #if defined(IPHWCRYPTO) || defined(IPCOM_USE_FORWARDER_IPSEC) /* Hardware crypto. Note, we must store raw key */ ipcom_memcpy(sa->esp_enckey, (Ip_u8 *)sadb->key_encrypt + sizeof(struct sadb_key), IP_MIN(sadb->key_encrypt->sadb_key_bits >> 3, sizeof(sa->esp_enckey))); #endif } } /* Lifetime. Not for GETSPI */ if (msgtype != SADB_GETSPI) { /* SADB_EXT_LIFETIME_HARD */ if (sadb->lifetime_hard) { sa->lthard.allocations = sadb->lifetime_hard->sadb_lifetime_allocations; ipcom_memcpy(&sa->lthard.bytes, &sadb->lifetime_hard->sadb_lifetime_bytes, sizeof(Ip_u64)); sa->lthard.addtime = IP_U64_GETLO(sadb->lifetime_hard->sadb_lifetime_addtime); sa->lthard.usetime = IP_U64_GETLO(sadb->lifetime_hard->sadb_lifetime_usetime); } /* SADB_EXT_LIFETIME_SOFT */ if (sadb->lifetime_soft) { sa->ltsoft.allocations = sadb->lifetime_soft->sadb_lifetime_allocations; ipcom_memcpy(&sa->ltsoft.bytes, &sadb->lifetime_soft->sadb_lifetime_bytes, sizeof(Ip_u64)); sa->ltsoft.addtime = IP_U64_GETLO(sadb->lifetime_soft->sadb_lifetime_addtime); sa->ltsoft.usetime = IP_U64_GETLO(sadb->lifetime_soft->sadb_lifetime_usetime); } /* Reset SA timeout if lifetime changed */ if (sadb->lifetime_hard || sadb->lifetime_soft) ipipsec_sa_reset_timeout(sa); } /* Addresses. Only for GETSPI, ADD and UPDATE LARVAL */ if (msgtype != SADB_MAX) { /* SADB_MSG */ sa->sadb_msg_satype = sadb->msg->sadb_msg_satype; sa->key.proto = IPIPSEC_SADB_GET_PROTO(sadb->msg->sadb_msg_satype); /* SADB_EXT_ADDRESS_DST */ if (sadb->address_dst) { sockaddr = (struct Ip_sockaddr *)((Ip_u8 *)sadb->address_dst + sizeof(struct sadb_address)); if (sockaddr->sa_family != sa->key.domain) return -IP_ERRNO_EINVAL; ipcom_memcpy(&sa->key.dst, &sockaddr->sa_data[sa->sock_in_addr_offset], sa->in_addr_size); } /* SADB_EXT_ADDRESS_SRC */ if (sadb->address_src) { sockaddr = (struct Ip_sockaddr *)((Ip_u8 *)sadb->address_src + sizeof(struct sadb_address)); if (sockaddr->sa_family != sa->key.domain) return -IP_ERRNO_EINVAL; ipcom_memcpy(&sa->key.src, &sockaddr->sa_data[sa->sock_in_addr_offset], sa->in_addr_size); } /* SADB_EXT_ADDRESS_PROXY */ if (sadb->address_proxy) { sockaddr = (struct Ip_sockaddr *)((Ip_u8 *)sadb->address_proxy + sizeof(struct sadb_address)); if (sockaddr->sa_family != sa->key.domain) return -IP_ERRNO_EINVAL; ipcom_memcpy(&sa->proxy, &sockaddr->sa_data[sa->sock_in_addr_offset], sa->in_addr_size); } /* SADB_X_EXT_UDPENCAP */ if (IP_BIT_ISSET(sa->sadb_sa_flags, SADB_X_SAFLAGS_UDPENCAP)) { Ip_u16 nat_src_port; Ip_u16 nat_dst_port; if (sadb->udpencap) { nat_src_port = sadb->udpencap->sadb_x_udpencap_src_port; nat_dst_port = sadb->udpencap->sadb_x_udpencap_dst_port; } else { nat_src_port = ip_htons(4500); nat_dst_port = nat_src_port; } sa->encap_srcport_n = nat_src_port; sa->encap_dstport_n = nat_dst_port; } } /* Insert in ordered global list */ ipipsec_sa_insert(sa); /* Success */ return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_add *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_add(Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_sa *sa; struct Ip_sockaddr *sockaddr; int retval; ip_assert(sadb->msg); ip_assert(sadb->sa); ip_assert(sadb->address_src); ip_assert(sadb->address_dst); /* [3.1.3] Only accept mature SA's */ if (sadb->sa->sadb_sa_state != SADB_SASTATE_MATURE) return -IP_ERRNO_EINVAL; /* Do not allow SPI 0 since it is reserved and used by bypass SA */ if (sadb->sa->sadb_sa_spi == IPIPSEC_SPI_LOCAL_USE) return -IP_ERRNO_EINVAL; /* Can only add ESP, AH or IPIP SA's */ if (ipipsec_pfkeyv2_verify_satype(sadb->msg->sadb_msg_satype) != 0) return -IP_ERRNO_EOPNOTSUPP; /* Check if there is an identical SA */ sa = ipipsec_pfkeyv2_sadb_sa_lookup(sadb, IP_TRUE); if (sa != IP_NULL) { IPCOM_LOG1(ERR, "ipipsec_pfkeyv2_msg_sadb_add() :: SA exist already (spi=%d)", sadb->sa->sadb_sa_spi); return -IP_ERRNO_EEXIST; } /* Create and initialize a new SA */ sockaddr = (struct Ip_sockaddr *)((Ip_u8*)sadb->address_dst + sizeof(struct sadb_address)); sa = ipipsec_sa_malloc(sadb->sa->sadb_sa_spi, sockaddr->sa_family); if (sa == IP_NULL) return -IP_ERRNO_ENOMEM; /* Add the info from the PFKEY to the SA */ retval = ipipsec_pfkeyv2_sadb_to_sa(sadb, sa, SADB_ADD); if (retval != 0) { ipipsec_sa_free(sa, IP_FALSE, IP_FALSE); } else { IPCOM_LOG2(INFO, "Adding SA: %s, replay=%d", ipipsec_sa_name(sa), (int)sa->sadb_sa_replay); } return retval; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_update *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_update(Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_sa *sa; int retval; ip_assert(sadb->msg); ip_assert(sadb->sa); ip_assert(sadb->address_dst); /* [3.1.2] Can only update if state of new SA is mature */ if (sadb->sa->sadb_sa_state != SADB_SASTATE_MATURE) return -IP_ERRNO_EINVAL; /* Do not allow SPI 0 since it is reserved and used by bypass SA */ if (sadb->sa->sadb_sa_spi == IPIPSEC_SPI_LOCAL_USE) return -IP_ERRNO_EINVAL; /* Check if there is a SA to update */ sa = ipipsec_pfkeyv2_sadb_sa_lookup(sadb, IP_TRUE); if (sa == IP_NULL) return -IP_ERRNO_ESRCH; /* Check what state the SA is in */ switch(sa->sadb_sa_state) { case SADB_SASTATE_DEAD: /* Can't update DEAD SA, must return EINVAL */ return -IP_ERRNO_EINVAL; /* LARVAL SA, can update everything */ case SADB_SASTATE_LARVAL: /* Can only update ESP, AH or IPIP SA's */ if (ipipsec_pfkeyv2_verify_satype(sadb->msg->sadb_msg_satype) != 0) return -IP_ERRNO_EOPNOTSUPP; /* Add the info from the SA to the SA */ retval = ipipsec_pfkeyv2_sadb_to_sa(sadb, sa, SADB_UPDATE); if (retval != 0) { /* remain in larval state so we can update the SA again */ sa->sadb_sa_state = SADB_SASTATE_LARVAL; return retval; } IPCOM_LOG1(INFO, "Updating larval SA: %s", ipipsec_sa_name(sa)); /* Cancel any pending getspi timeout */ sa->larval_timeout = 0; ipipsec_sa_reset_timeout(sa); break; /* SADB_SASTATE_MATURE or SADB_SASTATE_DYING */ default: ip_assert(sa->sadb_sa_state == SADB_SASTATE_MATURE || sa->sadb_sa_state == SADB_SASTATE_DYING); /* Not allowed to change keys, identify, sensitivity or proxy */ if (sadb->ident_src || sadb->ident_dst || sadb->sens || sadb->address_proxy || sadb->key_auth || sadb->key_encrypt) { return -IP_ERRNO_EINVAL; } IPCOM_LOG1(INFO, "Updating SA: %s", ipipsec_sa_name(sa)); /* Only ok to update state and lifetime */ retval = ipipsec_pfkeyv2_sadb_to_sa(sadb, sa, SADB_MAX /* clutch, use max */); if (retval == 0) sa->sadb_sa_state = sadb->sa->sadb_sa_state; break; } return retval; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_getspi *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_getspi(Ipipsec_pfkeyv2_sadb *sadb, struct sadb_sa *samem) { Ipipsec_sa *sa; Ipipsec_key key; Ip_u32 spi_min; int retval; int seconds; ip_assert(sadb->msg); ip_assert(sadb->spirange); ip_assert(sadb->address_dst); if (ipipsec_pfkeyv2_verify_satype(sadb->msg->sadb_msg_satype) != 0) return -IP_ERRNO_EOPNOTSUPP; retval = ipipsec_pfkeyv2_sadb_to_key(sadb, &key); if (retval != 0) return retval; /* Get SPI */ for (spi_min = IP_MAX(sadb->spirange->sadb_spirange_min, IPIPSEC_SPI_RESERVED_MAX+1); spi_min <= sadb->spirange->sadb_spirange_max; spi_min++) { key.ports.espah_spi_n = ip_htonl(spi_min); sa = ipipsec_sa_lookup(&key, 0); if (sa == IP_NULL) break; } if (spi_min > sadb->spirange->sadb_spirange_max) return -IP_ERRNO_EEXIST; /* Create and init a new SA */ sa = ipipsec_sa_malloc(ip_htonl(spi_min), key.domain); if (sa == IP_NULL) return -IP_ERRNO_ENOMEM; /* Set source and destination address + satype */ retval = ipipsec_pfkeyv2_sadb_to_sa(sadb, sa, SADB_GETSPI); if (retval != 0) { ipipsec_sa_free(sa, IP_FALSE, IP_FALSE); } else { IPCOM_LOG1(DEBUG, ": SA: %s", ipipsec_sa_name(sa)); /* Set larval timeout to auto-clean unused SAs */ seconds = ipcom_sysvar_get_as_int("ipipsec.larval_sa_tmo_seconds", 600); seconds = IP_MAX(1, seconds); sa->larval_timeout = ipipsec_seconds_now() + (Ip_time_t)seconds; ipipsec_sa_reset_timeout(sa); /* Add a SADB_EXT_SA header to the reply */ if (sadb->sa == IP_NULL) sadb->sa = samem; sadb->sa->sadb_sa_len = sizeof(struct sadb_sa) / sizeof(Ip_u64); sadb->sa->sadb_sa_exttype = SADB_EXT_SA; sadb->sa->sadb_sa_spi = sa->key.ports.espah_spi_n; /* [2.3.1] in network endian */ sadb->sa->sadb_sa_replay = sa->sadb_sa_replay; sadb->sa->sadb_sa_state = sa->sadb_sa_state; sadb->sa->sadb_sa_auth = sa->sadb_sa_auth; sadb->sa->sadb_sa_encrypt = sa->sadb_sa_encrypt; sadb->sa->sadb_sa_flags = sa->sadb_sa_flags; } return retval; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_delete *=========================================================================== * Description: Delete a SA (and optionally entire group for linked SAs) * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_delete(Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_sa *sa; ip_assert(sadb->msg); ip_assert(sadb->sa); ip_assert(sadb->address_dst); /* Allow SPI 0 because that is one way to flush the bypass flows */ /* Lookup the SA (exact match) */ sa = ipipsec_pfkeyv2_sadb_sa_lookup(sadb, IP_TRUE); if (sa == IP_NULL) return -IP_ERRNO_ESRCH; /* Delete SA */ ipipsec_sa_free(sa, IP_BIT_ISSET(sadb->sa->sadb_sa_flags, SADB_X_SAFLAGS_CHAINDEL) ? IP_TRUE : IP_FALSE, IP_TRUE); return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_get *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_get(Ipcom_socket *sock, Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_sa *sa; ip_assert(sadb->msg); ip_assert(sadb->msg->sadb_msg_type == SADB_GET); ip_assert(sadb->sa); /* Lookup the SA (note: exact lookup) */ sa = ipipsec_pfkeyv2_sadb_sa_lookup(sadb, IP_TRUE); if (sa == IP_NULL) return -IP_ERRNO_ESRCH; /* [3.1.5] Return SADB_GET message to the socket that sent the message */ return ipipsec_pfkeyv2_output_sa(sock, sa, sadb, IPIPSEC_SNDMODE_UNICAST); } /* *=========================================================================== * ipipsec_pfkeyv2_sadb_to_flowsel *=========================================================================== * Description: Used for flow commands to extract info from sadb to flowsel * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_sadb_to_flowsel(Ipipsec_pfkeyv2_sadb *sadb, Ipipsec_flowsel *flowsel) { union Ip_sockaddr_union *sockaddr_src; union Ip_sockaddr_union *sockaddr_dst; union Ip_sockaddr_union *sockaddr; ip_assert(sadb->src_flow); ip_assert(sadb->dst_flow); sockaddr_src = (union Ip_sockaddr_union *)((Ip_u8 *)sadb->src_flow + sizeof(struct sadb_address)); sockaddr_dst = (union Ip_sockaddr_union *)((Ip_u8 *)sadb->dst_flow + sizeof(struct sadb_address)); if (sockaddr_src->sa.sa_family != sockaddr_dst->sa.sa_family) return -IP_ERRNO_EINVAL; if (sockaddr_dst->sa.sa_family != IP_AF_INET && sockaddr_dst->sa.sa_family != IP_AF_INET6) return -IP_ERRNO_EINVAL; ipcom_memset(flowsel, 0, sizeof(Ipipsec_flowsel)); /* Domain */ flowsel->key.domain = (Ip_u8)sockaddr_dst->sa.sa_family; /* Flow options in SADB_X_EXT_FLOW_OPTIONS: */ if (sadb->flowopt) { /* Flow priority (-128 to 127, -128=highest) */ flowsel->key.priority = sadb->flowopt->sadb_flowopt_priority; /* DSCP value (in high order 6-bits */ flowsel->key.dscp = sadb->flowopt->sadb_flowopt_dscp; } /* Optional transport protocol in SADB_X_EXT_PROTOCOL, e.g. UDP or TCP */ if (sadb->protocol) { flowsel->key.proto = sadb->protocol->sadb_protocol_proto; switch (flowsel->key.proto) { case IP_IPPROTO_TCP: case IP_IPPROTO_UDP: /* Source TCP/UDP ports (at same offset for both IPv4 and IPv6) */ flowsel->key.ports.udptcp_srcdst_n[0] = sockaddr_src->sin.sin_port; flowsel->src_max_port = ip_ntohs(sockaddr_src->sin.sin_port); if (sadb->srcmax_flow != IP_NULL) { sockaddr = (union Ip_sockaddr_union *)((Ip_u8*)sadb->srcmax_flow + sizeof(struct sadb_address)); flowsel->src_max_port = ip_ntohs(sockaddr->sin.sin_port); if (flowsel->src_max_port < ip_ntohs(flowsel->key.ports.udptcp_srcdst_n[0])) return -IP_ERRNO_EINVAL; } /* Destination TCP/UDP ports (at same offset for both IPv4 and IPv6) */ flowsel->key.ports.udptcp_srcdst_n[1] = sockaddr_dst->sin.sin_port; flowsel->dst_max_port = ip_ntohs(sockaddr_dst->sin.sin_port); if (sadb->dstmax_flow != IP_NULL) { sockaddr = (union Ip_sockaddr_union *)((Ip_u8*)sadb->dstmax_flow + sizeof(struct sadb_address)); flowsel->dst_max_port = ip_ntohs(sockaddr->sin.sin_port); if (flowsel->dst_max_port < ip_ntohs(flowsel->key.ports.udptcp_srcdst_n[1])) return -IP_ERRNO_EINVAL; } break; case IP_IPPROTO_ICMP: case IP_IPPROTO_ICMPV6: flowsel->key.ports.icmp_typecode[0] = sadb->protocol->sadb_protocol_ports0; flowsel->key.ports.icmp_typecode[1] = sadb->protocol->sadb_protocol_ports1; break; case IP_IPPROTO_MH: flowsel->key.ports.mh_type[0] = sadb->protocol->sadb_protocol_ports0; break; default: break; } } /* Source and destination addresses */ #ifdef IPCOM_USE_INET if (flowsel->key.domain == IP_AF_INET) { /* Source address and mask */ flowsel->key.src.in.s_addr = sockaddr_src->sin.sin_addr.s_addr; if (sadb->src_mask != IP_NULL) { flowsel->mask.src.in.s_addr = ((struct Ip_sockaddr_in *)((Ip_u8*)sadb->src_mask + sizeof(struct sadb_address)))->sin_addr.s_addr; flowsel->key.src.in.s_addr &= flowsel->mask.src.in.s_addr; } else flowsel->mask.src.in.s_addr = 0xffffffff; if (sadb->srcmax_flow != IP_NULL) { sockaddr = (union Ip_sockaddr_union *)((Ip_u8*)sadb->srcmax_flow + sizeof(struct sadb_address)); flowsel->src_max.in.s_addr = sockaddr->sin.sin_addr.s_addr; if (ip_ntohl(flowsel->src_max.in.s_addr) < ip_ntohl(sockaddr_src->sin.sin_addr.s_addr)) { /* Flow range end address is less than start address */ return -IP_ERRNO_EINVAL; } else if (ip_ntohl(flowsel->src_max.in.s_addr) > ip_ntohl(sockaddr_src->sin.sin_addr.s_addr)) { /* Mask not allowed with range */ if (flowsel->mask.src.in.s_addr != 0xffffffff) return -IP_ERRNO_EINVAL; } else { flowsel->src_max.in.s_addr &= flowsel->mask.src.in.s_addr; } } else flowsel->src_max.in.s_addr = flowsel->key.src.in.s_addr; /* Destination address and mask */ flowsel->key.dst.in.s_addr = sockaddr_dst->sin.sin_addr.s_addr; if (sadb->dst_mask != IP_NULL) { flowsel->mask.dst.in.s_addr = ((struct Ip_sockaddr_in *)((Ip_u8*)sadb->dst_mask + sizeof(struct sadb_address)))->sin_addr.s_addr; flowsel->key.dst.in.s_addr &= flowsel->mask.dst.in.s_addr; } else flowsel->mask.dst.in.s_addr = 0xffffffff; if (sadb->dstmax_flow != IP_NULL) { sockaddr = (union Ip_sockaddr_union *)((Ip_u8*)sadb->dstmax_flow + sizeof(struct sadb_address)); flowsel->dst_max.in.s_addr = sockaddr->sin.sin_addr.s_addr; if (ip_ntohl(flowsel->dst_max.in.s_addr) < ip_ntohl(sockaddr_dst->sin.sin_addr.s_addr)) { /* Flow range end address is less than start address */ return -IP_ERRNO_EINVAL; } else if (ip_ntohl(flowsel->dst_max.in.s_addr) > ip_ntohl(sockaddr_dst->sin.sin_addr.s_addr)) { /* Mask not allowed with range */ if (flowsel->mask.dst.in.s_addr != 0xffffffff) return -IP_ERRNO_EINVAL; } else { flowsel->dst_max.in.s_addr &= flowsel->mask.dst.in.s_addr; } } else flowsel->dst_max.in.s_addr = flowsel->key.dst.in.s_addr; } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (flowsel->key.domain == IP_AF_INET6) { struct Ip_sockaddr_in6 *sockaddr6; int i; /* Source address and mask */ ipcom_memcpy(&flowsel->key.src, &sockaddr_src->sin6.sin6_addr, sizeof(struct Ip_in6_addr)); flowsel->key.srcscope_n = ip_htonl(sockaddr_src->sin6.sin6_scope_id); if (flowsel->key.srcscope_n != 0) flowsel->mask.srcscope_n = 0xffffffff; if (sadb->src_mask != IP_NULL) { sockaddr6 = (struct Ip_sockaddr_in6 *)((Ip_u8*)sadb->src_mask + sizeof(struct sadb_address)); ipcom_memcpy(&flowsel->mask.src, &sockaddr6->sin6_addr, sizeof(struct Ip_in6_addr)); for (i = 0; i < 4; i++) flowsel->key.src.in6.in6.addr32[i] &= flowsel->mask.src.in6.in6.addr32[i]; } else ipcom_memset(&flowsel->mask.src, 0xff, sizeof(struct Ip_in6_addr)); if (sadb->srcmax_flow != IP_NULL) { sockaddr6 = (struct Ip_sockaddr_in6 *)((Ip_u8*)sadb->srcmax_flow + sizeof(struct sadb_address)); ipcom_memcpy(&flowsel->src_max, &sockaddr6->sin6_addr, sizeof(struct Ip_in6_addr)); if (ipipsec_addrcmp(IP_AF_INET6, &flowsel->src_max, (union Ip_in_addr_union *)&sockaddr_src->sin6.sin6_addr) == -1) { /* Flow range end address is less than start address */ return -IP_ERRNO_EINVAL; } else if (ipipsec_addrcmp(IP_AF_INET6, &flowsel->src_max, (union Ip_in_addr_union *)&sockaddr_src->sin6.sin6_addr) == 1) { /* Mask not allowed with range */ if (ipcom_mask_to_prefixlen(&flowsel->mask.src, 128) != 128) return -IP_ERRNO_EINVAL; } else { for (i = 0; i < 4; i++) flowsel->src_max.in6.in6.addr32[i] &= flowsel->mask.src.in6.in6.addr32[i]; } } else ipcom_memcpy(&flowsel->src_max, &flowsel->key.src, sizeof(struct Ip_in6_addr)); /* Destination address and mask */ ipcom_memcpy(&flowsel->key.dst, &sockaddr_dst->sin6.sin6_addr, sizeof(struct Ip_in6_addr)); flowsel->key.dstscope_n = ip_htonl(sockaddr_dst->sin6.sin6_scope_id); if (flowsel->key.dstscope_n != 0) flowsel->mask.dstscope_n = 0xffffffff; if (sadb->dst_mask != IP_NULL) { sockaddr6 = (struct Ip_sockaddr_in6 *)((Ip_u8*)sadb->dst_mask + sizeof(struct sadb_address)); ipcom_memcpy(&flowsel->mask.dst, &sockaddr6->sin6_addr, sizeof(struct Ip_in6_addr)); for (i = 0; i < 4; i++) flowsel->key.dst.in6.in6.addr32[i] &= flowsel->mask.dst.in6.in6.addr32[i]; } else ipcom_memset(&flowsel->mask.dst, 0xff, sizeof(struct Ip_in6_addr)); if (sadb->dstmax_flow != IP_NULL) { sockaddr6 = (struct Ip_sockaddr_in6 *)((Ip_u8*)sadb->dstmax_flow + sizeof(struct sadb_address)); ipcom_memcpy(&flowsel->dst_max, &sockaddr6->sin6_addr, sizeof(struct Ip_in6_addr)); if (ipipsec_addrcmp(IP_AF_INET6, &flowsel->dst_max, (union Ip_in_addr_union *)&sockaddr_dst->sin6.sin6_addr) == -1) { /* Flow range end address is less than start address */ return -IP_ERRNO_EINVAL; } else if (ipipsec_addrcmp(IP_AF_INET6, &flowsel->dst_max, (union Ip_in_addr_union *)&sockaddr_dst->sin6.sin6_addr) == 1) { /* Mask not allowed with range */ if (ipcom_mask_to_prefixlen(&flowsel->mask.dst, 128) != 128) return -IP_ERRNO_EINVAL; } else { for (i = 0; i < 4; i++) flowsel->dst_max.in6.in6.addr32[i] &= flowsel->mask.dst.in6.in6.addr32[i]; } } else ipcom_memcpy(&flowsel->dst_max, &flowsel->key.dst, sizeof(struct Ip_in6_addr)); } #endif /* IPCOM_USE_INET6 */ return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_x_addflow *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_x_addflow(Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_flowsel flowsel; Ipipsec_sa *sa; Ipipsec_flow *flow; int flowdir, retval; Ip_u8 flowtype; ip_assert(sadb->msg); ip_assert(sadb->src_flow); ip_assert(sadb->dst_flow); /* Get flow direction */ flowdir = ipipsec_pfkeyv2_sadb_to_flowdir(sadb, IPIPSEC_DIR_OUT); if (flowdir < 0) return flowdir; /* Get flowtype */ flowtype = SADB_X_FLOW_TYPE_UNSPEC; if (sadb->msg->sadb_msg_satype == SADB_X_SATYPE_BYPASS) { /* bypass flow */ flowtype = SADB_X_FLOW_TYPE_BYPASS; } else if (sadb->flowopt) { /* flowtype specified in extension */ flowtype = sadb->flowopt->sadb_flowopt_type; } else if (sadb->sa != IP_NULL && sadb->sa->sadb_sa_spi == 0 && sadb->msg->sadb_msg_satype == SADB_SATYPE_UNSPEC) { /* spi == 0 and satype == 0 -> flow without SA (used for require) */ flowtype = SADB_X_FLOW_TYPE_REQUIRE; } /* Default to 'use' flowtype for security reasons */ if (flowtype == SADB_X_FLOW_TYPE_UNSPEC) flowtype = SADB_X_FLOW_TYPE_USE; /* Act on flowtype */ switch (flowtype) { case SADB_X_FLOW_TYPE_BYPASS: case SADB_X_FLOW_TYPE_ACQUIRE: case SADB_X_FLOW_TYPE_REQUIRE: case SADB_X_FLOW_TYPE_DENY: sa = IP_NULL; break; case SADB_X_FLOW_TYPE_USE: /* Lookup SA (exact if SA options are specified) */ sa = ipipsec_pfkeyv2_sadb_sa_lookup(sadb, sadb->saopt != IP_NULL); if (sa == IP_NULL) return -IP_ERRNO_ESRCH; break; default: IPCOM_LOG1(DEBUG, "ipipsec_pfkeyv2_msg_sadb_x_addflow() :: invalid flowtype '%d'", (int)flowtype); return -IP_ERRNO_EINVAL; } /* Fill in flowsel */ retval = ipipsec_pfkeyv2_sadb_to_flowsel(sadb, &flowsel); if (retval != 0) return retval; flowsel.key.direction = (Ip_u8)flowdir; /* Check for duplicate flow (only submit SA for input flows) */ flow = ipipsec_flow_lookup_exact(&flowsel, 0); if (flow != IP_NULL) { /* Do not accept duplicate flows unless we are replacing them */ if (sadb->sa && IP_BIT_ISSET(sadb->sa->sadb_sa_flags, SADB_X_SAFLAGS_REPLACEFLOW)) ipipsec_flow_free(flow); else return -IP_ERRNO_EEXIST; } /* Create and init new flow */ flow = ipipsec_flow_malloc(&flowsel, sa, flowtype); if (flow == IP_NULL) return -IP_ERRNO_ENOMEM; if (sadb->flowopt != IP_NULL) { sadb->flowopt->sadb_flowopt_type = flow->flowtype; sadb->flowopt->sadb_flowopt_direction = flow->flowsel.key.direction + 1; ip_assert(sadb->flowopt->sadb_flowopt_direction <= 2); sadb->flowopt->sadb_flowopt_priority = flow->flowsel.key.priority; sadb->flowopt->sadb_flowopt_dscp = flow->flowsel.key.dscp; sadb->flowopt->sadb_flowopt_id = flow->id; } return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_x_delflow *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_x_delflow(Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_flowsel flowsel; Ipipsec_flow *flow; int flowdir, flowid, retval; /* Get flow direction */ flowdir = ipipsec_pfkeyv2_sadb_to_flowdir(sadb, IPIPSEC_DIR_OUT); if (flowdir < 0) return flowdir; flowid = sadb->flowopt ? sadb->flowopt->sadb_flowopt_id : 0; if (flowid != 0) { /* Lookup the exact flow using index only */ flow = ipipsec_flow_lookup_exact(IP_NULL, flowid); } else { /* Lookup using source and destination address */ if ( sadb->src_flow == IP_NULL || sadb->dst_flow == IP_NULL || ((sadb->src_mask == IP_NULL || sadb->dst_mask == IP_NULL) && (sadb->srcmax_flow == IP_NULL || sadb->dstmax_flow == IP_NULL))) return -IP_ERRNO_EINVAL; /* Fill in flowsel */ retval = ipipsec_pfkeyv2_sadb_to_flowsel(sadb, &flowsel); if (retval != 0) return retval; flowsel.key.direction = (Ip_u8)flowdir; /* Lookup the exact flow */ flow = ipipsec_flow_lookup_exact(&flowsel, 0); } if (flow == IP_NULL) return -IP_ERRNO_ESRCH; if (sadb->flowopt != IP_NULL) { /* Update flow option for output message */ sadb->flowopt->sadb_flowopt_type = flow->flowtype; sadb->flowopt->sadb_flowopt_direction = flow->flowsel.key.direction + 1; ip_assert(sadb->flowopt->sadb_flowopt_direction <= 2); sadb->flowopt->sadb_flowopt_priority = flow->flowsel.key.priority; sadb->flowopt->sadb_flowopt_dscp = flow->flowsel.key.dscp; sadb->flowopt->sadb_flowopt_id = flow->id; } /* Delete the flow */ ipipsec_flow_free(flow); return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_x_findflow *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_x_findflow(Ipcom_socket *sock, Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_flowsel flowsel; Ipipsec_flow *flow; int retval, flowdir; /* Get flow direction */ flowdir = ipipsec_pfkeyv2_sadb_to_flowdir(sadb, IPIPSEC_DIR_OUT); if (flowdir < 0) return flowdir; /* Fill in flowsel */ retval = ipipsec_pfkeyv2_sadb_to_flowsel(sadb, &flowsel); if (retval != 0) return retval; flowsel.key.direction = (Ip_u8)flowdir; /* Lookup flow just like a output/input operation was made */ flow = ipipsec_flow_lookup(&flowsel.key, 0); if (flow == IP_NULL) return -IP_ERRNO_ESRCH; /* [3.1.5] Return SADB_X_FINDFLOW message to the socket that sent the message */ return ipipsec_pfkeyv2_output_flow(sock, flow, sadb); } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_x_dumpflow *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_x_dumpflow(Ipcom_socket *sock, Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_flow *flow, *last; int retval; int flowtype; int flowdir, flow_index, domain_index; ip_assert(sadb->msg); ip_assert(sadb->msg->sadb_msg_type == SADB_X_DUMPFLOW); /* Get flow direction */ flowdir = ipipsec_pfkeyv2_sadb_to_flowdir(sadb, IPIPSEC_DIR_ANY); if (flowdir < 0) return flowdir; if (sadb->flowopt) flowtype = sadb->flowopt->sadb_flowopt_type; else flowtype = SADB_X_FLOW_TYPE_UNSPEC; last = IP_NULL; for (domain_index = 0; domain_index < 2; domain_index++) for (flow_index = 0; flow_index < 2; flow_index++) { if (flowdir != IPIPSEC_DIR_ANY && flowdir != flow_index) continue; for (flow = IPCOM_LIST_FIRST(&ipipsec.shm->flow_head[domain_index][flow_index]); flow != IP_NULL; flow = IPCOM_LIST_NEXT(&flow->flow_list)) if (flowtype == SADB_X_FLOW_TYPE_UNSPEC || flowtype == flow->flowtype) { if (last) { retval = ipipsec_pfkeyv2_output_flow(sock, last, sadb); if (retval != 0) return retval; } last = flow; } } /* Write the last one with seq field of zero */ if (last) { sadb->msg->sadb_msg_seq = 0; retval = ipipsec_pfkeyv2_output_flow(sock, last, sadb); if (retval != 0) return retval; } else return -IP_ERRNO_ESRCH; return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_x_grpspis *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_x_grpspis(Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_sa *sa, *sa2; Ipipsec_key key2; int retval; ip_assert(sadb->sa); ip_assert(sadb->address_dst); ip_assert(sadb->sa2); ip_assert(sadb->dst2); /* Get first SA (lookup exact first, then best match) */ sa = ipipsec_pfkeyv2_sadb_sa_lookup(sadb, IP_TRUE); if (sa == IP_NULL) sa = ipipsec_pfkeyv2_sadb_sa_lookup(sadb, IP_FALSE); if (sa == IP_NULL) return -IP_ERRNO_ESRCH; /* Get second SA (which always must be an output SA) */ retval = ipipsec_pfkeyv2_sadb_to_key2(sadb, &key2); if (retval != 0) return retval; sa2 = ipipsec_sa_lookup_exact(&key2); if (sa2 == IP_NULL) sa2 = ipipsec_sa_lookup(&key2, 0); if (sa2 == IP_NULL) return -IP_ERRNO_ESRCH; /* Group them together */ (void)ipipsec_sa_group(sa, sa2); return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_register *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_register(Ipcom_socket *sock, Ipipsec_pfkeyv2_sadb *sadb) { Ip_size_t length; int retval; Ip_u8 *orgbuf, *msgbuf; ip_assert(sadb->msg); /* Can only register for satype <= 31 since stored in 32bit bit-field */ if (sadb->msg->sadb_msg_satype > 31) return -IP_ERRNO_EINVAL; /* Reply with a sadb_supported message */ length = sizeof(struct sadb_supported) + (SADB_AALG_NUM + SADB_EALG_NUM) * sizeof(struct sadb_alg); orgbuf = (Ip_u8 *)ipcom_malloc(length + sizeof(Ip_u64)); if (orgbuf == IP_NULL) return -IP_ERRNO_ENOMEM; msgbuf = IP_PTR_ALIGN(orgbuf, 8); /* 64-bit align msgbuf */ ip_assert(((Ip_ptrdiff_t)msgbuf & 0x7L) == 0); ipcom_memset(msgbuf, 0, length); /* Only count unregistered sockets (they may be registered for multiple types) */ if (sock->ipsec_registered == 0) ipipsec.registered_num++; IP_BIT_SET(sock->ipsec_registered, 1 << sadb->msg->sadb_msg_satype); /***** Send REGISTER reply to all registered sockets *****/ /* Use 'msg' from the incoming buffer. Use 'msgbuf' for sadb_supported */ ipcom_memset(&sadb->sa, 0, SADB_EXT_MAX * sizeof(void *)); /*lint !e419 */ sadb->supported = (struct sadb_supported *)msgbuf; /* SADB_MSG */ ip_assert(sadb->msg->sadb_msg_version == PF_KEY_V2); ip_assert(sadb->msg->sadb_msg_reserved == 0); /* SADB_EXT_SUPPORTED */ ipipsec_crypt_get_algs(sadb->supported); /* Send the message to all registered sockets (sock != IP_NULL) */ retval = ipipsec_pfkeyv2_output(sock, (void *)sadb, IPIPSEC_SNDMODE_REGISTERED, 0); ipcom_free(orgbuf); return retval; } /* *=========================================================================== * ipipsec_pfkeyv2_copy_lifetimes *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC void ipipsec_pfkeyv2_copy_lifetimes(struct sadb_lifetime *lt, Ipipsec_sa_lifetime *sa_lt) { lt->sadb_lifetime_allocations = sa_lt->allocations; ipcom_memcpy(<->sadb_lifetime_bytes, &sa_lt->bytes, sizeof(lt->sadb_lifetime_bytes)); IP_U64_SETHI(lt->sadb_lifetime_addtime, 0); IP_U64_SETLO(lt->sadb_lifetime_addtime, sa_lt->addtime); IP_U64_SETHI(lt->sadb_lifetime_usetime, 0); IP_U64_SETLO(lt->sadb_lifetime_usetime, sa_lt->usetime); } /* **************************************************************************** * 11 LINUX PFKEYV2 API **************************************************************************** */ #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API /* *=========================================================================== * ipipsec_pfkeyv2_create_mask *=========================================================================== * Description: Creates a network mask with the 'netbits' leftmost bits set. * Parameters: [in] mask - Pointer to a buffer where the mask should be stored. * [in] netbits - The number of set bits. * Returns: * */ IP_STATIC void ipipsec_pfkeyv2_create_mask(void *mask, int netbits) { int i; for (i = 0; i < netbits; i++) { IP_BIT_SET(((Ip_u16*) mask)[i / 16], ip_htons(0x8000 >> (i % 16))); } } /* *=========================================================================== * ipipsec_pfkeyv2_sadb_to_flowsel_linux *=========================================================================== * Description: Used for flow commands to extract info from sadb to flowsel * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_sadb_to_flowsel_linux(Ipipsec_pfkeyv2_sadb *sadb, Ipipsec_flowsel *flowsel) { union Ip_sockaddr_union *sockaddr_src; union Ip_sockaddr_union *sockaddr_dst; ip_assert(sadb->address_src); ip_assert(sadb->address_dst); ip_assert(sadb->lx_policy); sockaddr_src = (union Ip_sockaddr_union *)((Ip_u8 *)sadb->address_src + sizeof(struct sadb_address)); sockaddr_dst = (union Ip_sockaddr_union *)((Ip_u8 *)sadb->address_dst + sizeof(struct sadb_address)); if (sockaddr_src->sa.sa_family != sockaddr_dst->sa.sa_family) return -IP_ERRNO_EINVAL; if (sockaddr_dst->sa.sa_family != IP_AF_INET && sockaddr_dst->sa.sa_family != IP_AF_INET6) return -IP_ERRNO_EINVAL; ipcom_memset(flowsel, 0, sizeof(Ipipsec_flowsel)); /* Domain */ flowsel->key.domain = (Ip_u8)sockaddr_dst->sa.sa_family; /*!!LINUX: Invert flow priority (-128 to 127, -128=highest) */ flowsel->key.priority = -sadb->lx_policy->sadb_x_policy_priority; /* Optional transport protocol in SADB_X_EXT_PROTOCOL, e.g. UDP or TCP */ flowsel->key.proto = sadb->address_src->sadb_address_proto; switch (flowsel->key.proto) { case IP_IPPROTO_TCP: case IP_IPPROTO_UDP: /* Source TCP/UDP ports (at same offset for both IPv4 and IPv6) */ flowsel->key.ports.udptcp_srcdst_n[0] = sockaddr_src->sin.sin_port; /* Destination TCP/UDP ports (at same offset for both IPv4 and IPv6) */ flowsel->key.ports.udptcp_srcdst_n[1] = sockaddr_dst->sin.sin_port; break; #ifdef TODO case IP_IPPROTO_ICMP: case IP_IPPROTO_ICMPV6: flowsel->key.ports.icmp_typecode[0] = sadb->protocol->sadb_protocol_ports0; flowsel->key.ports.icmp_typecode[1] = sadb->protocol->sadb_protocol_ports1; break; case IP_IPPROTO_MH: flowsel->key.ports.mh_type[0] = sadb->protocol->sadb_protocol_ports0; break; #endif case IP_IPPROTO_RAW: break; default: break; } /* Source and destination addresses */ #ifdef IPCOM_USE_INET if (flowsel->key.domain == IP_AF_INET) { /* Source address and mask */ flowsel->key.src.in.s_addr = sockaddr_src->sin.sin_addr.s_addr; flowsel->src_max.in.s_addr = flowsel->key.src.in.s_addr; ipipsec_pfkeyv2_create_mask(&flowsel->mask.src.in.s_addr, sadb->address_src->sadb_address_prefixlen); /* Destination address and mask */ flowsel->key.dst.in.s_addr = sockaddr_dst->sin.sin_addr.s_addr; flowsel->dst_max.in.s_addr = flowsel->key.dst.in.s_addr; ipipsec_pfkeyv2_create_mask(&flowsel->mask.dst.in.s_addr, sadb->address_dst->sadb_address_prefixlen); } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (flowsel->key.domain == IP_AF_INET6) { /* Source address and mask */ ipcom_memcpy(&flowsel->key.src, &sockaddr_src->sin6.sin6_addr, sizeof(struct Ip_in6_addr)); flowsel->key.srcscope_n = ip_htonl(sockaddr_src->sin6.sin6_scope_id); ipcom_memcpy(&flowsel->src_max, &flowsel->key.src, sizeof(struct Ip_in6_addr)); ipipsec_pfkeyv2_create_mask(&flowsel->mask.src, sadb->address_src->sadb_address_prefixlen); if (flowsel->key.srcscope_n != 0) flowsel->mask.srcscope_n = 0xffffffff; /* Destination address and mask */ ipcom_memcpy(&flowsel->key.dst, &sockaddr_dst->sin6.sin6_addr, sizeof(struct Ip_in6_addr)); flowsel->key.dstscope_n = ip_htonl(sockaddr_dst->sin6.sin6_scope_id); ipcom_memcpy(&flowsel->dst_max, &flowsel->key.dst, sizeof(struct Ip_in6_addr)); ipipsec_pfkeyv2_create_mask(&flowsel->mask.dst, sadb->address_dst->sadb_address_prefixlen); if (flowsel->key.dstscope_n != 0) flowsel->mask.dstscope_n = 0xffffffff; } #endif /* IPCOM_USE_INET6 */ return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_lx_spdadd *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_lx_spdadd(Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_flowsel flowsel; int flowdir, retval; Ip_u8 flowtype; Ip_u16 ext_len; Ipipsec_flow *flow; Ipipsec_sasel *sasel_last = IP_NULL; struct sadb_x_ipsecrequest *xisr; ip_assert(sadb->msg); ip_assert(sadb->address_src); ip_assert(sadb->address_dst); ip_assert(sadb->lx_policy); ext_len = sadb->lx_policy->sadb_x_policy_len * sizeof(Ip_u64); ext_len -= sizeof(struct sadb_x_policy); /* Get flow direction */ flowdir = ipipsec_pfkeyv2_sadb_to_flowdir(sadb, IPIPSEC_DIR_OUT); if (flowdir < 0) return flowdir; /* Get flowtype */ switch (sadb->lx_policy->sadb_x_policy_type) { case IPSEC_POLICY_BYPASS: flowtype = SADB_X_FLOW_TYPE_BYPASS; break; case IPSEC_POLICY_DISCARD: flowtype = SADB_X_FLOW_TYPE_DENY; break; case IPSEC_POLICY_IPSEC: flowtype = SADB_X_FLOW_TYPE_USE; break; case IPSEC_POLICY_ENTRUST: /*!!*/ case IPSEC_POLICY_NONE: /*!!*/ default: IP_PANIC2(); return -IP_ERRNO_EINVAL; } /* Fill in flow selector 'flowsel' */ retval = ipipsec_pfkeyv2_sadb_to_flowsel_linux(sadb, &flowsel); if (retval != 0) return retval; flowsel.key.direction = (Ip_u8)flowdir; /* Check for duplicate flow */ flow = ipipsec_flow_lookup_exact(&flowsel, 0); if (flow != IP_NULL) { /* Do not accept duplicate flows unless we are replacing them */ if (sadb->msg->sadb_msg_type == SADB_X_SPDUPDATE) ipipsec_flow_free(flow); else return -IP_ERRNO_EEXIST; } /* Create and init new flow */ flow = ipipsec_flow_malloc(&flowsel, IP_NULL, flowtype); if (flow == IP_NULL) return -IP_ERRNO_ENOMEM; /*!!TODO: add bundle support */ xisr = (struct sadb_x_ipsecrequest *)(sadb->lx_policy + 1); while (ext_len >= sizeof(struct sadb_x_ipsecrequest)) { /* Create SA selector */ Ipipsec_sasel *sasel; /* Verify ipsecrequest */ if (xisr->sadb_x_ipsecrequest_len < sizeof(struct sadb_x_ipsecrequest)) IPIPSEC_ERROUT(-IP_ERRNO_EINVAL); if (xisr->sadb_x_ipsecrequest_proto != IP_IPPROTO_AH && xisr->sadb_x_ipsecrequest_proto != IP_IPPROTO_ESP) IPIPSEC_ERROUT(-IP_ERRNO_EINVAL); if (xisr->sadb_x_ipsecrequest_mode > IPSEC_MODE_TUNNEL) IPIPSEC_ERROUT(-IP_ERRNO_EINVAL); if (xisr->sadb_x_ipsecrequest_level > IPSEC_LEVEL_UNIQUE) IPIPSEC_ERROUT(-IP_ERRNO_EINVAL); /* Create SA selector */ sasel = ipipsec_shm_calloc(1, sizeof(*sasel)); if (sasel == IP_NULL) return -IP_ERRNO_ENOMEM; /* Add SA selector last in list */ ip_assert(sasel->next == IP_NULL); if (sasel_last == IP_NULL) flow->sasel = sasel; else sasel_last->next = sasel; sasel_last = sasel; /* Init SA selector */ sasel->proto = xisr->sadb_x_ipsecrequest_proto; /* AH or ESP */ sasel->domain = flow->flowsel.key.domain; /* INET or INET6 */ sasel->mode = xisr->sadb_x_ipsecrequest_mode; /* any, tunnel or transport */ sasel->level = xisr->sadb_x_ipsecrequest_level; /* default/use, require, unique */ if (sasel->level == IPSEC_LEVEL_REQUIRE) flow->flowtype = SADB_X_FLOW_TYPE_REQUIRE; sasel->direction = flow->flowsel.key.direction; sasel->reqid = xisr->sadb_x_ipsecrequest_reqid; /* reqid for unique */ if (xisr->sadb_x_ipsecrequest_len > sizeof(struct Ip_sockaddr)) { /* Extract source/tunnel endpoints */ union Ip_sockaddr_union *sockaddr; sockaddr = (union Ip_sockaddr_union *)((Ip_caddr_t)xisr + sizeof(*xisr)); #ifdef IPCOM_USE_INET if (sockaddr->sa.sa_family == IP_AF_INET) { if (xisr->sadb_x_ipsecrequest_len - sizeof(struct sadb_x_ipsecrequest) < 2 * sizeof(struct Ip_sockaddr_in)) IPIPSEC_ERROUT(-IP_ERRNO_EINVAL); sasel->src_tunnel.in.s_addr = sockaddr->sin.sin_addr.s_addr; sockaddr = (union Ip_sockaddr_union *)((Ip_caddr_t)sockaddr + sizeof(struct Ip_sockaddr_in)); sasel->dst_tunnel.in.s_addr = sockaddr->sin.sin_addr.s_addr; } #endif /* IPCOM_USE_INET */ #ifdef IPCOM_USE_INET6 if (sockaddr->sa.sa_family == IP_AF_INET6) { if (xisr->sadb_x_ipsecrequest_len - sizeof(struct sadb_x_ipsecrequest) < 2 * sizeof(struct Ip_sockaddr_in6)) IPIPSEC_ERROUT(-IP_ERRNO_EINVAL); ipcom_memcpy(&sasel->src_tunnel.in6, &sockaddr->sin6.sin6_addr, 16); sockaddr = (union Ip_sockaddr_union *)((Ip_caddr_t)sockaddr + sizeof(struct Ip_sockaddr_in6)); ipcom_memcpy(&sasel->dst_tunnel.in6, &sockaddr->sin6.sin6_addr, 16); } #endif /* IPCOM_USE_INET6 */ } /* Step to next xisr */ ext_len -= xisr->sadb_x_ipsecrequest_len; if (ext_len > 0) xisr = (struct sadb_x_ipsecrequest *)((Ip_caddr_t)xisr + xisr->sadb_x_ipsecrequest_len); } /* while (ext_len > sizeof(struct sadb_x_ipsecrequest)) */ /*!! Update for return value (Linux API accepts this?) */ sadb->lx_policy->sadb_x_policy_id = flow->id; return 0; /* Failure */ errout: ipipsec_flow_free(flow); return retval; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_lx_spddelete *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_lx_spddelete(Ipipsec_pfkeyv2_sadb *sadb) { Ipipsec_flowsel flowsel; Ipipsec_flow *flow; int flowdir, retval; ip_assert(sadb->msg); ip_assert(sadb->address_src); ip_assert(sadb->address_dst); ip_assert(sadb->lx_policy); /* Get flow direction */ flowdir = ipipsec_pfkeyv2_sadb_to_flowdir(sadb, IPIPSEC_DIR_OUT); if (flowdir < 0) return flowdir; /* flowtype ignored */ /* Fill in flowsel */ retval = ipipsec_pfkeyv2_sadb_to_flowsel_linux(sadb, &flowsel); if (retval != 0) return retval; flowsel.key.direction = (Ip_u8)flowdir; /* Lookup the exact flow */ flow = ipipsec_flow_lookup_exact(&flowsel, 0); if (flow == IP_NULL) return -IP_ERRNO_ESRCH; /*!! Update for return value (Linux API accepts this?) */ sadb->lx_policy->sadb_x_policy_id = flow->id; /* Delete the flow */ ipipsec_flow_free(flow); return 0; } /* *=========================================================================== * ipipsec_pfkeyv2_msg_sadb_lx_spdflush *=========================================================================== * Description: * Parameters: * Returns: * */ IP_STATIC int ipipsec_pfkeyv2_msg_sadb_lx_spdflush(Ipipsec_pfkeyv2_sadb *sadb) { int dir; ip_assert(sadb->msg); ip_assert(sadb->msg->sadb_msg_type == SADB_X_SPDFLUSH); /* Remove all flows */ for (dir = 0; dir < 2; dir++) { ipipsec_flow_free_many(IP_AF_INET, 0, dir); ipipsec_flow_free_many(IP_AF_INET6, 0, dir); } return 0; } #endif /* #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API */ /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipipsec_pfkeyv2_acquire *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL void ipipsec_pfkeyv2_acquire(Ipipsec_key *key) { struct acquire_msg { struct sadb_msg msg; struct Ipipsec_sadb_prop_full prop; struct Ipipsec_sadb_address_full src; struct Ipipsec_sadb_address_full dst; } acquire; void *exthdr[SADB_EXT_MAX+1]; if (ipipsec.registered_num == 0) return; ip_assert(key); ip_assert(key->domain == IP_AF_INET || key->domain == IP_AF_INET6); /* Clear structures */ ipcom_memset(exthdr, 0, sizeof(exthdr)); ipcom_memset(&acquire, 0, sizeof(struct acquire_msg)); /* Message header */ acquire.msg.sadb_msg_version = PF_KEY_V2; acquire.msg.sadb_msg_type = SADB_ACQUIRE; acquire.msg.sadb_msg_seq = ipipsec.pfkeyv2_seq++; exthdr[0] = &acquire.msg; /* SADB_EXT_ADDRESS_SRC */ exthdr[SADB_EXT_ADDRESS_SRC] = &acquire.src; ipipsec_pfkeyv2_ipaddr_to_extaddr(&acquire.src.addr, &key->src, ip_ntohs(key->ports.udptcp_srcdst_n[0]), key->domain); acquire.src.addr.sadb_address_proto = key->proto; acquire.src.addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; #ifdef IPCOM_USE_INET6 acquire.src.sock.sin6.sin6_scope_id = key->srcscope_n; #endif /* SADB_EXT_ADDRESS_DST */ exthdr[SADB_EXT_ADDRESS_DST] = &acquire.dst; ipipsec_pfkeyv2_ipaddr_to_extaddr(&acquire.dst.addr, &key->dst, ip_ntohs(key->ports.udptcp_srcdst_n[1]), key->domain); acquire.dst.addr.sadb_address_proto = key->proto; acquire.dst.addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST; #ifdef IPCOM_USE_INET6 acquire.dst.sock.sin6.sin6_scope_id = key->dstscope_n; #endif /* Proposal */ exthdr[SADB_EXT_PROPOSAL] = &acquire.prop; acquire.prop.prop.sadb_prop_len = sizeof(acquire.prop) >> 3; acquire.prop.prop.sadb_prop_exttype = SADB_EXT_PROPOSAL; IPCOM_LOG1(DEBUG, "Sending acquire message for dst=%s", ipcom_inet_ntop(acquire.dst.sock.sa.sa_family, &key->dst, ipipsec.sysbuf, sizeof(ipipsec.sysbuf))); /* Send to all registered listeners that match the specified satype */ (void)ipipsec_pfkeyv2_output(IP_NULL, exthdr, IPIPSEC_SNDMODE_REGISTERED, 0); } /* *=========================================================================== * ipipsec_pfkeyv2_expire *=========================================================================== * Description: * Parameters: * Returns: * */ IP_GLOBAL void ipipsec_pfkeyv2_expire(Ipipsec_sa *sa, Ip_u32 type) { struct expire_msg { struct sadb_msg msg; struct sadb_sa sa; struct sadb_lifetime current; struct sadb_lifetime soft_or_hard; struct Ipipsec_sadb_address_full src; struct Ipipsec_sadb_address_full dst; struct sadb_x_saopt saopt; } expire; void *exthdr[SADB_EXT_MAX+1]; ipcom_memset(exthdr, 0, sizeof(exthdr)); ipcom_memset(&expire, 0, sizeof(struct expire_msg)); ip_assert(type == IPIPSEC_EXPIRE_TYPE_HARD || type == IPIPSEC_EXPIRE_TYPE_SOFT); expire.msg.sadb_msg_version = PF_KEY_V2; expire.msg.sadb_msg_type = SADB_EXPIRE; expire.msg.sadb_msg_satype = sa->sadb_msg_satype; expire.msg.sadb_msg_seq = ipipsec.pfkeyv2_seq++; exthdr[0] = &expire.msg; /* Security association */ expire.sa.sadb_sa_len = sizeof(expire.sa) / 8; expire.sa.sadb_sa_exttype = SADB_EXT_SA; expire.sa.sadb_sa_spi = sa->key.ports.espah_spi_n; expire.sa.sadb_sa_replay = sa->sadb_sa_replay; expire.sa.sadb_sa_state = sa->sadb_sa_state; expire.sa.sadb_sa_auth = sa->sadb_sa_auth; expire.sa.sadb_sa_encrypt = sa->sadb_sa_encrypt; expire.sa.sadb_sa_flags = sa->sadb_sa_flags; exthdr[SADB_EXT_SA] = &expire.sa; /* Lifetime */ expire.current.sadb_lifetime_len = sizeof(expire.current) / 8; expire.current.sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT; ipipsec_pfkeyv2_copy_lifetimes(&expire.current, &sa->ltcurrent); exthdr[SADB_EXT_LIFETIME_CURRENT] = &expire.current; expire.soft_or_hard.sadb_lifetime_len = sizeof(expire.soft_or_hard) / 8; if (type == IPIPSEC_EXPIRE_TYPE_HARD) { expire.soft_or_hard.sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD; ipipsec_pfkeyv2_copy_lifetimes(&expire.soft_or_hard, &sa->lthard); exthdr[SADB_EXT_LIFETIME_HARD] = &expire.soft_or_hard; } else { expire.soft_or_hard.sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT; ipipsec_pfkeyv2_copy_lifetimes(&expire.soft_or_hard, &sa->ltsoft); exthdr[SADB_EXT_LIFETIME_SOFT] = &expire.soft_or_hard; } /* SADB_EXT_ADDRESS_SRC */ exthdr[SADB_EXT_ADDRESS_SRC] = &expire.src; ipipsec_pfkeyv2_ipaddr_to_extaddr(&expire.src.addr, &sa->key.src, 0, sa->key.domain); expire.src.addr.sadb_address_proto = 0; expire.src.addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; /* SADB_EXT_ADDRESS_DST */ exthdr[SADB_EXT_ADDRESS_DST] = &expire.dst; ipipsec_pfkeyv2_ipaddr_to_extaddr(&expire.dst.addr, &sa->key.dst, 0, sa->key.domain); expire.dst.addr.sadb_address_proto = 0; expire.dst.addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST; IPCOM_LOG1(DEBUG, "Sending expire message for dst=%s", ipcom_inet_ntop(expire.dst.sock.sa.sa_family, &sa->key.dst, ipipsec.sysbuf, sizeof(ipipsec.sysbuf))); /* SADB_X_EXT_SA_OPTIONS */ if (sa->key.priority != 0) { exthdr[SADB_X_EXT_SA_OPTIONS] = &expire.saopt; expire.saopt.sadb_saopt_len = sizeof(expire.saopt) / 8; expire.saopt.sadb_saopt_exttype = SADB_X_EXT_SA_OPTIONS; expire.saopt.sadb_saopt_priority = sa->key.priority; } /* Send to all Key sockets */ (void)ipipsec_pfkeyv2_output(IP_NULL, exthdr, IPIPSEC_SNDMODE_BROADCAST, 0); if (type == IPIPSEC_EXPIRE_TYPE_SOFT) { /* Disable soft checking */ IP_U64_CLEAR(sa->ltsoft.bytes); sa->ltsoft.addtime = 0; sa->ltsoft.usetime = 0; ipipsec_sa_reset_timeout(sa); } } /* *=========================================================================== * ipipsec_pfkeyv2_input *=========================================================================== * Description: Handles writes to IP_PF_KEY sockets. * Parameters: sock - the socket used when writing * to - unused by IP_PF_KEY sockets * pkt - the data written * Returns: 0 = success, <0 = error code * */ IP_GLOBAL int ipipsec_pfkeyv2_input(Ipcom_socket *sock, IP_CONST struct Ip_msghdr *msghdr, Ipcom_pkt *pkt) { void *exthdr[SADB_EXT_MAX+1]; struct sadb_msg *msg; char *buf; struct sadb_ext *ext; int retval = -IP_ERRNO_EINVAL; Ip_u64 extmap, extdiff; Ip_u32 sndmode = IPIPSEC_SNDMODE_BROADCAST; struct sadb_sa samem; int nbyte = pkt->end - pkt->start; #ifdef IPIPSEC_USE_DAEMONS Ipnet_sig *sig; sig = ipipsec_suspend_daemons(); if (sig == IP_NULL) { retval = -IP_ERRNO_ENOMEM; goto leave; } ipipsec_poll_control_pipe(); #else IPIPSEC_DATA_LOCK(); #endif #ifdef IPIPSEC_USE_JOB ipipsec_job_process(); #endif (void)msghdr; IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_IPSEC_PF_KEY_IN); /* Prepare extension header array */ ipcom_memset(exthdr, 0, sizeof(exthdr)); msg = (struct sadb_msg *)&pkt->data[pkt->start]; buf = (char *)msg; exthdr[0] = msg; /*!!TODO: send copy of message to all PFKEYV2_SOCKETFLAGS_PROMISC sockets */ /* verify sadb_msg message header */ if (nbyte < (int)sizeof(struct sadb_msg)) { IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: too short (%d) 'sadb_msg' header", nbyte); goto leave; } if (msg->sadb_msg_version != PF_KEY_V2 || msg->sadb_msg_type == SADB_RESERVED || msg->sadb_msg_type > SADB_MAX /* [2.1] - sadb_msg_errno should be set to 0, i.e. not must */ || (msg->sadb_msg_len * sizeof(Ip_u64) != (Ip_u32) nbyte) || msg->sadb_msg_reserved != 0 || msg->sadb_msg_pid == 0) /* trivial pid check */ { IPCOM_LOG5(NOTICE, "ipipsec_pfkeyv2_input() :: bad 'sadb_msg' header (ver=%d, type=%d, len=%d, res=%d), nbyte=%d", msg->sadb_msg_version, msg->sadb_msg_type, msg->sadb_msg_len, msg->sadb_msg_reserved, nbyte); goto einval; } IPCOM_LOG1(DEBUG, "ipipsec_pfkeyv2_input() :: receiving '%s' command", ipipsec_pfkeyv2_command[msg->sadb_msg_type]); /* Step over the sadb_msg header */ buf += sizeof(struct sadb_msg); nbyte -= sizeof(struct sadb_msg); /* Step through the extension headers */ for (extmap = 0, ext = (struct sadb_ext *)buf; nbyte >= (int)sizeof(struct sadb_ext); ext = (struct sadb_ext *)buf) { int ext_len; /* extension headers must start at 32-bit alignment (acctually 64-bit) */ if (((Ip_ptrdiff_t)ext & 0x3L) != 0) { IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: bad extension alignment(0x%lx)", (Ip_ptrdiff_t)ext); goto einval; } ext_len = (int)(ext->sadb_ext_len * sizeof(Ip_u64)); if (ext_len == 0 || nbyte < ext_len || ext->sadb_ext_type == SADB_EXT_RESERVED) { IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: bad extension length(%ld) or type(0)", ext_len); goto einval; } /* [2.3] - Ignore unknown (that includes > MAX) extensions */ if (ext->sadb_ext_type <= SADB_EXT_MAX) { extmap |= (1ULL << ext->sadb_ext_type); /* [2.3] Only one instance of an extension header */ if (exthdr[ext->sadb_ext_type]) { IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: duplicate extension header [ext=%d]", ext->sadb_ext_type); goto einval; } else exthdr[ext->sadb_ext_type] = ext; /* _Exact_ extension header size check */ if (ipipsec_pfkeyv2_ext_size[ext->sadb_ext_type] > 0 && ipipsec_pfkeyv2_ext_size[ext->sadb_ext_type] != ext_len) { IPCOM_LOG2(NOTICE, "ipipsec_pfkeyv2_input() :: illegal extension header length(%d) [ext=%d]", ext_len, ext->sadb_ext_type); goto einval; } /* _Minimum_ extension header size check */ if (ipipsec_pfkeyv2_ext_size[ext->sadb_ext_type] < 0 && ext_len < -ipipsec_pfkeyv2_ext_size[ext->sadb_ext_type]) { IPCOM_LOG2(NOTICE, "ipipsec_pfkeyv2_input() :: too small extension header length(%d) [ext=%d]", ext_len, ext->sadb_ext_type); goto einval; } /* Extension specific checks */ switch(ext->sadb_ext_type) { case SADB_EXT_SA : case SADB_X_EXT_SA2 : { struct sadb_sa *sa; sa = (struct sadb_sa *)ext; if (sa->sadb_sa_state == SADB_SASTATE_DEAD || sa->sadb_sa_state > SADB_SASTATE_MAX || sa->sadb_sa_auth > SADB_AALG_MAX || sa->sadb_sa_encrypt > SADB_EALG_MAX) { IPCOM_LOG0(NOTICE, "ipipsec_pfkeyv2_input() :: invalid SA (sadb_sa) values"); goto einval; } } break; case SADB_EXT_ADDRESS_SRC : /*!! [2.3.3] source address must be unicast or 0 */ case SADB_X_EXT_DST2 : case SADB_EXT_ADDRESS_DST : case SADB_EXT_ADDRESS_PROXY : /* Ports must be 0 since not used with SRC,DST and PROXY */ if ((((struct Ip_sockaddr_in *)((Ip_u8 *)ext + sizeof(struct sadb_address)))->sin_port != 0) && msg->sadb_msg_type != SADB_X_SPDADD && msg->sadb_msg_type != SADB_X_SPDUPDATE && msg->sadb_msg_type != SADB_X_SPDDELETE) { IPCOM_LOG0(NOTICE, "ipipsec_pfkeyv2_input() :: non-zero ports"); goto einval; } /* Bypass destination SA is domain-less */ if (ext->sadb_ext_type == SADB_EXT_ADDRESS_DST && msg->sadb_msg_satype == SADB_X_SATYPE_BYPASS) break; goto check_address; case SADB_X_EXT_SRC_MASK : case SADB_X_EXT_DST_MASK : case SADB_X_EXT_SRC_FLOW : case SADB_X_EXT_DST_FLOW : case SADB_X_EXT_SRCMAX_FLOW : case SADB_X_EXT_DSTMAX_FLOW : { struct sadb_address *address; struct Ip_sockaddr *sockaddr; address = (struct sadb_address *)ext; if (address->sadb_address_reserved || address->sadb_address_prefixlen != 0) { IPCOM_LOG0(NOTICE, "ipipsec_pfkeyv2_input() :: non-zero sadb_address reserved or prefixlen"); goto einval; } check_address: sockaddr = (struct Ip_sockaddr *)((Ip_u8 *)ext + sizeof(struct sadb_address)); #ifdef IPCOM_FORWARDER_NAE if (sockaddr->sa_family != IP_AF_INET && sockaddr->sa_family != IP_AF_INET6) { /* Try to get the domain from the sa_len field. This my happen if the management system * does not use sa_len. (Linux) */ sockaddr->sa_family = sockaddr->sa_len; } #endif #if defined(IPCOM_USE_INET) && defined(IPCOM_USE_INET6) if (sockaddr->sa_family != IP_AF_INET && sockaddr->sa_family != IP_AF_INET6) { IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: bad socket address family (%d)", (int)sockaddr->sa_family); goto einval; } #elif defined(IPCOM_USE_INET6) if (sockaddr->sa_family != IP_AF_INET6) goto einval; #else if (sockaddr->sa_family != IP_AF_INET) goto einval; #endif /* [2.3.3] ignore sa_len check because it is not MUST */ } break; #ifdef IPIPSEC_USE_ROHC case SADB_X_EXT_ROHC_INTEG_KEY: #endif case SADB_EXT_KEY_AUTH : case SADB_EXT_KEY_ENCRYPT : { struct sadb_key *key; key = (struct sadb_key *)ext; if (key->sadb_key_bits == 0 || key->sadb_key_reserved != 0 || ((key->sadb_key_bits + 63) / 64) * sizeof(Ip_u64) != (ext_len - sizeof(struct sadb_key))) { IPCOM_LOG0(NOTICE, "ipipsec_pfkeyv2_input() :: invalid KEY (sadb_key) values"); goto einval; } } break; case SADB_EXT_SPIRANGE : { struct sadb_spirange *spirange; spirange = (struct sadb_spirange *)ext; /* SPI range check */ if (spirange->sadb_spirange_min > spirange->sadb_spirange_max || spirange->sadb_spirange_max <= IPIPSEC_SPI_RESERVED_MAX) { IPCOM_LOG0(NOTICE, "ipipsec_pfkeyv2_input() :: invalid SPI range"); goto einval; } } break; case SADB_X_EXT_POLICY : { struct sadb_x_policy *xpol; struct sadb_x_ipsecrequest *xisr; xpol = (struct sadb_x_policy *)ext; if ((xpol->sadb_x_policy_len << 3) >= (int)(sizeof(struct sadb_x_policy) + sizeof(struct sadb_x_ipsecrequest))) { xisr = (struct sadb_x_ipsecrequest *)(xpol + 1); if (xisr->sadb_x_ipsecrequest_len >= (sizeof(struct sadb_x_ipsecrequest) + sizeof(struct Ip_sockaddr))) { /* Extract source/tunnel endpoints */ struct Ip_sockaddr *sockaddr; sockaddr = (struct Ip_sockaddr *)(xisr + 1); #ifdef IPCOM_FORWARDER_NAE if (sockaddr->sa_family != IP_AF_INET && sockaddr->sa_family != IP_AF_INET6) { /* Try to get the domain from the sa_len field. This my happen if the management system * does not use sa_len. (Linux) */ sockaddr->sa_family = sockaddr->sa_len; } #endif #ifdef IPCOM_USE_INET if (sockaddr->sa_family == IP_AF_INET) { if (xisr->sadb_x_ipsecrequest_len >= (sizeof(struct sadb_x_ipsecrequest) + sizeof(struct Ip_sockaddr_in) + sizeof(struct Ip_sockaddr_in))) { sockaddr = (struct Ip_sockaddr *)((struct Ip_sockaddr_in *)sockaddr + 1); } else { IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: bad ipsec request length (%d)", xisr->sadb_x_ipsecrequest_len); goto einval; } } else #endif #ifdef IPCOM_USE_INET6 if (sockaddr->sa_family == IP_AF_INET6) { if (xisr->sadb_x_ipsecrequest_len >= (sizeof(struct sadb_x_ipsecrequest) + sizeof(struct Ip_sockaddr_in6) + sizeof(struct Ip_sockaddr_in6))) { sockaddr = (struct Ip_sockaddr *)((struct Ip_sockaddr_in6 *)sockaddr + 1); } else { IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: bad ipsec request length (%d)", xisr->sadb_x_ipsecrequest_len); goto einval; } } else #endif { IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: bad socket address family (%d)", (int)sockaddr->sa_family); goto einval; } sockaddr->sa_family = sockaddr->sa_len; } } } break; #ifdef IPIPSEC_USE_ROHC case SADB_X_EXT_ROHC_PARAM: { struct sadb_x_rohc * rohc; int rohc_len; rohc = (struct sadb_x_rohc * ) ext; rohc_len = (int) sizeof(struct sadb_x_rohc); rohc_len = (int) IP_ROUNDUP(rohc_len, 8); if ( ext_len != rohc_len ) { IPCOM_LOG2(NOTICE, "ipipsec_pfkeyv2_input() :: "\ "inconsistent ROHC length, got (%d) expected (%d)", ext_len, rohc_len); goto einval; } } break; #endif /* IPIPSEC_USE_ROHC */ /* no checks case SADB_X_EXT_PROTOCOL : case SADB_X_EXT_FLOW_OPTIONS : case SADB_X_EXT_UDPENCAP : */ /* only size checks: case SADB_X_EXT_PROTOCOL : case SADB_EXT_LIFETIME_CURRENT : case SADB_EXT_LIFETIME_HARD : case SADB_EXT_LIFETIME_SOFT : */ /* currently unsupported: case SADB_EXT_IDENTITY_SRC : case SADB_EXT_IDENTITY_DST : case SADB_EXT_SENSITIVITY : case SADB_EXT_PROPOSAL : case SADB_EXT_SUPPORTED : */ default: break; } } /* next extension header */ nbyte -= ext_len; buf += ext_len; } /* Do not accept trailing extra bytes */ if (nbyte != 0) { IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: bad length, additional trailing bytes (%d)", nbyte); goto einval; } /* Check if the required IN extensions are there */ extdiff = extmap & ipipsec_pfkeyv2_sadbextbits[msg->sadb_msg_type].in_req; if (extdiff != ipipsec_pfkeyv2_sadbextbits[msg->sadb_msg_type].in_req) { IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: missing mandatory extension(s), diff=0x%lx", (extdiff ^ ipipsec_pfkeyv2_sadbextbits[msg->sadb_msg_type].in_req)); goto einval; } /* Check that the optional IN extensions are ok */ extdiff = extmap | ipipsec_pfkeyv2_sadbextbits[msg->sadb_msg_type].in_opt; if ((extdiff ^ ipipsec_pfkeyv2_sadbextbits[msg->sadb_msg_type].in_opt) != 0) { IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: illegal optional extension(s), diff=0x%lx", extdiff ^ ipipsec_pfkeyv2_sadbextbits[msg->sadb_msg_type].in_opt); goto einval; } /* Act on the message type */ /* Act on the message type */ switch(msg->sadb_msg_type) { case SADB_ADD : /* reply to all */ retval = ipipsec_pfkeyv2_msg_sadb_add((Ipipsec_pfkeyv2_sadb *)exthdr); break; case SADB_DELETE : /* reply to all */ retval = ipipsec_pfkeyv2_msg_sadb_delete((Ipipsec_pfkeyv2_sadb *)exthdr); break; case SADB_FLUSH : /* reply to all */ retval = ipipsec_pfkeyv2_msg_sadb_flush((Ipipsec_pfkeyv2_sadb *)exthdr); break; case SADB_GETSPI : /* reply to all */ retval = ipipsec_pfkeyv2_msg_sadb_getspi((Ipipsec_pfkeyv2_sadb *)exthdr, &samem); break; case SADB_UPDATE : /* reply to all */ retval = ipipsec_pfkeyv2_msg_sadb_update((Ipipsec_pfkeyv2_sadb *)exthdr); break; case SADB_GET : /* reply to sender */ retval = ipipsec_pfkeyv2_msg_sadb_get(sock, (Ipipsec_pfkeyv2_sadb *)exthdr); /* If success, a sadb has already been sent */ if (retval == 0) goto leave; break; case SADB_DUMP : /* reply to sender */ retval = ipipsec_pfkeyv2_msg_sadb_dump(sock, (Ipipsec_pfkeyv2_sadb *)exthdr); /* If success, one or multiple messages have already been sent */ if (retval == 0) goto leave; break; case SADB_EXPIRE : case SADB_ACQUIRE : retval = 0; /* do nothing */ break; case SADB_X_ADDFLOW : retval = ipipsec_pfkeyv2_msg_sadb_x_addflow((Ipipsec_pfkeyv2_sadb *)exthdr); break; case SADB_X_DELFLOW : retval = ipipsec_pfkeyv2_msg_sadb_x_delflow((Ipipsec_pfkeyv2_sadb *)exthdr); break; case SADB_X_FINDFLOW : retval = ipipsec_pfkeyv2_msg_sadb_x_findflow(sock, (Ipipsec_pfkeyv2_sadb *)exthdr); /* If success, a flow has already been sent */ if (retval == 0) goto leave; break; case SADB_X_DUMPFLOW : /* reply to sender */ retval = ipipsec_pfkeyv2_msg_sadb_x_dumpflow(sock, (Ipipsec_pfkeyv2_sadb *)exthdr); /* If success, one or multiple messages have already been sent */ if (retval == 0) goto leave; break; case SADB_X_GRPSPIS : retval = ipipsec_pfkeyv2_msg_sadb_x_grpspis((Ipipsec_pfkeyv2_sadb *)exthdr); break; /* UNIMPLEMENTED */ case SADB_REGISTER : /* reply to registered */ retval = ipipsec_pfkeyv2_msg_sadb_register(sock, (Ipipsec_pfkeyv2_sadb *)exthdr); /* If success, a register message was sent to all registered sockets */ if (retval == 0) goto leave; break; case SADB_X_PROMISC : case SADB_X_BINDSA : retval = -IP_ERRNO_ENOSYS; break; case SADB_X_MIGRATE: /* ignore this message in the ipsec.. reply to all */ retval = 0; break; #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API case SADB_X_SPDADD : case SADB_X_SPDUPDATE : retval = ipipsec_pfkeyv2_msg_sadb_lx_spdadd((Ipipsec_pfkeyv2_sadb *)exthdr); break; case SADB_X_SPDDELETE : retval = ipipsec_pfkeyv2_msg_sadb_lx_spddelete((Ipipsec_pfkeyv2_sadb *)exthdr); break; case SADB_X_SPDFLUSH : /* reply to all */ retval = ipipsec_pfkeyv2_msg_sadb_lx_spdflush((Ipipsec_pfkeyv2_sadb *)exthdr); break; #endif /* #ifdef IPIPSEC_USE_LINUX_PFKEYV2_API */ default: IPCOM_LOG1(NOTICE, "ipipsec_pfkeyv2_input() :: unknown msg_type (%d)", msg->sadb_msg_type); IP_PANIC2(); retval = -IP_ERRNO_EINVAL; break; } ip_assert(retval <= 0); ip_assert(exthdr[0] == msg); /** Error **/ if (retval) { if (retval == -IP_ERRNO_EINVAL || retval == -IP_ERRNO_ENOMEM || retval == -IP_ERRNO_ENOBUFS) { /* EINVAL, ENOMEM and ENOBUFS may be returned immediately, lets not */ goto einval; } } /* [1.4] - All properly-formed messages sent to the kernel are * returned to all open PF_KEY sockets, including the sender. */ einval: (void)ipipsec_pfkeyv2_output(sock, exthdr, sndmode, -retval); retval = 0; leave: ipcom_pkt_free(pkt); #ifdef IPIPSEC_USE_SYSLOG if (retval) IPCOM_LOG1(DEBUG, "ipipsec_pfkeyv2_input() :: failed, errno = %d", -retval); else IPCOM_LOG0(DEBUG, "ipipsec_pfkeyv2_input() :: succeeded"); #endif #ifdef IPIPSEC_USE_JOB ipipsec_job_process(); #endif #ifdef IPIPSEC_USE_DAEMONS if (sig != IP_NULL) ipipsec_resume_daemons(sig); #else IPIPSEC_DATA_UNLOCK(); #endif return retval; } #ifdef IPIPSEC_USE_EVENT_LOG /* *=========================================================================== * ipipsec_pfkeyv2_event *=========================================================================== * Description: Send an event message to the IKE daemon. Creates * Parameters: type - Event type * reason - Event reason * param - IPsec parameters * sa - IPsec SA * Returns: * */ IP_GLOBAL void ipipsec_pfkeyv2_event( int type, int reason, Ipipsec_key * key, Ipipsec_sa * sa) { void * exthdr[SADB_EXT_MAX+1]; struct Ipipsec_sadb_address_full address; struct event_msg { struct sadb_msg msg; struct sadb_x_event_info info; Ipike_event_msg emsg; /* Make sure we can round up to 8 byte size */ Ip_u8 unused[8]; } event; Ipike_event_msg *emsg = &event.emsg ; if (ipipsec.registered_num == 0) return; /* Clear structures */ ipcom_memset(exthdr, 0, sizeof(exthdr)); ipcom_memset(&event, 0, sizeof(event)); /* Message header */ event.msg.sadb_msg_version = PF_KEY_V2; event.msg.sadb_msg_type = SADB_X_EVENT; event.msg.sadb_msg_seq = ipipsec.pfkeyv2_seq++; exthdr[0] = &event.msg; /* SADB_X_EXT_EVENT_INFO */ exthdr[SADB_X_EXT_EVENT_INFO] = &event.info; event.info.sadb_x_event_info_len = IP_ROUNDUP(sizeof(event.info) + sizeof(event.emsg), 8) / 8; event.info.sadb_x_event_info_exttype = SADB_X_EXT_EVENT_INFO; emsg->hdr.len = sizeof(event.emsg); emsg->event.type = ip_htons((Ip_u16)type); emsg->event.reason = ip_htons((Ip_u16)reason); emsg->event.src_port = key->ports.udptcp_srcdst_n[0]; emsg->event.dst_port = key->ports.udptcp_srcdst_n[1]; if ( sa ) emsg->event.protocol = sa->sadb_msg_satype; ipcom_memset(&address, 0, sizeof(address)); ipipsec_pfkeyv2_ipaddr_to_extaddr(&address.addr, &key->src, ip_ntohs(key->ports.udptcp_srcdst_n[0]), key->domain); ipcom_memcpy(&emsg->event.src_ip, &address.sock, sizeof(address.sock)); ipcom_memset(&address, 0, sizeof(address)); ipipsec_pfkeyv2_ipaddr_to_extaddr(&address.addr, &key->dst, ip_ntohs(key->ports.udptcp_srcdst_n[1]), key->domain); ipcom_memcpy(&emsg->event.dst_ip, &address.sock, sizeof(address.sock)); /* Send to all Key sockets */ (void)ipipsec_pfkeyv2_output(IP_NULL, exthdr, IPIPSEC_SNDMODE_BROADCAST, 0); } #endif /* IPIPSEC_USE_EVENT_LOG */ /* **************************************************************************** * END OF FILE **************************************************************************** */