3938 lines
137 KiB
C
3938 lines
137 KiB
C
/*
|
|
******************************************************************************
|
|
* 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 <lob@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 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 <ipcom_type.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_inet.h>
|
|
#include <ipcom_sysvar.h>
|
|
#include <ipcom_err.h>
|
|
|
|
#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, "<GETSPI>: 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
|
|
****************************************************************************
|
|
*/
|
|
|