You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
986 lines
27 KiB
986 lines
27 KiB
/* SPDX-License-Identifier: LGPL-2.1-only */
|
|
/*
|
|
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
|
|
*
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* Neither the name of Texas Instruments Incorporated nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @ingroup xfrmnl
|
|
* @defgroup ae Attribute Element
|
|
* @brief
|
|
*
|
|
* The AE interface allows a user to retrieve and update various
|
|
* Security Association (SA) attributes such as lifetime, replay state etc.
|
|
*
|
|
* @par AE Flags
|
|
* @code
|
|
* XFRM_AE_UNSPEC
|
|
* XFRM_AE_RTHR=1
|
|
* XFRM_AE_RVAL=2
|
|
* XFRM_AE_LVAL=4
|
|
* XFRM_AE_ETHR=8
|
|
* XFRM_AE_CR=16
|
|
* XFRM_AE_CE=32
|
|
* XFRM_AE_CU=64
|
|
* @endcode
|
|
*
|
|
* @par AE Identification
|
|
* An AE is uniquely identified by the attributes listed below, whenever
|
|
* you refer to an existing AE all of the attributes must be set. There is
|
|
* no cache support for AE since you can retrieve the AE for any given combination
|
|
* of attributes mentioned below, but not all at once since they just characterize
|
|
* an SA.
|
|
* - destination address (xfrmnl_ae_set_daddr())
|
|
* - SPI (xfrmnl_ae_set_spi)
|
|
* - protocol (xfrmnl_ae_set_proto)
|
|
* - mark (xfrmnl_ae_set_mark)
|
|
*
|
|
* @par Changeable Attributes
|
|
* \anchor ae_changeable
|
|
* - current lifetime (xfrmnl_ae_set_curlifetime())
|
|
* - replay properties (xfrmnl_ae_set_replay_maxage(), xfrmnl_ae_set_replay_maxdiff())
|
|
* - replay state (xfrmnl_ae_set_replay_state(), xfrmnl_ae_set_replay_state_esn))
|
|
*
|
|
* @par Required Caches for Dumping
|
|
* None
|
|
*
|
|
* @par TODO
|
|
* None
|
|
*
|
|
* @par 1) Retrieving AE information for a given SA tuple
|
|
* @code
|
|
* // Create a netlink socket and connect it to XFRM subsystem in
|
|
* the kernel to be able to send/receive info from userspace.
|
|
* struct nl_sock* sk = nl_socket_alloc ();
|
|
* nl_connect (sk, NETLINK_XFRM);
|
|
*
|
|
* // AEs can then be looked up by the SA tuple, destination address,
|
|
* SPI, protocol, mark:
|
|
* struct xfrmnl_ae *ae;
|
|
* xfrmnl_ae_get_kernel(sk, dst_addr, spi, proto,mark_mask, mark_value, &ae);
|
|
*
|
|
* // After successful usage, the object must be freed
|
|
* xfrmnl_ae_put(ae);
|
|
* @endcode
|
|
*
|
|
* @par 2) Updating AE
|
|
* @code
|
|
* // Allocate an empty AE handle to be filled out with the attributes
|
|
* // of the new AE.
|
|
* struct xfrmnl_ae *ae = xfrmnl_ae_alloc();
|
|
*
|
|
* // Fill out the attributes of the new AE
|
|
* xfrmnl_ae_set_daddr(ae, dst_addr);
|
|
* xfrmnl_ae_set_spi(ae, 0xDEADBEEF);
|
|
* xfrmnl_ae_set_proto(ae, 50);
|
|
* xfrmnl_ae_set_mark(ae, 0x0);
|
|
* xfrmnl_ae_set_saddr(ae, src_addr);
|
|
* xfrmnl_ae_set_curlifetime(ae, 540, 10, 0xAABB1122, 0x0);
|
|
*
|
|
* // Build the netlink message and send it to the kernel, the operation will
|
|
* // block until the operation has been completed. Alternatively, a netlink message
|
|
* // can be built using xfrmnl_ae_build_get_request () API and be sent using
|
|
* // nl_send_auto(). Further the result from the kernel can be parsed using
|
|
* // xfrmnl_ae_parse() API.
|
|
* xfrmnl_ae_set(sk, ae, NLM_F_REPLACE);
|
|
*
|
|
* // Free the memory
|
|
* xfrmnl_ae_put(ae);
|
|
* @endcode
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
#include <netlink-private/netlink.h>
|
|
#include <netlink/netlink.h>
|
|
#include <netlink/cache.h>
|
|
#include <netlink/object.h>
|
|
#include <netlink/xfrm/ae.h>
|
|
#include <linux/xfrm.h>
|
|
|
|
/** @cond SKIP */
|
|
#define XFRM_AE_ATTR_DADDR 0x01
|
|
#define XFRM_AE_ATTR_SPI 0x02
|
|
#define XFRM_AE_ATTR_PROTO 0x04
|
|
#define XFRM_AE_ATTR_SADDR 0x08
|
|
#define XFRM_AE_ATTR_FLAGS 0x10
|
|
#define XFRM_AE_ATTR_REQID 0x20
|
|
#define XFRM_AE_ATTR_MARK 0x40
|
|
#define XFRM_AE_ATTR_LIFETIME 0x80
|
|
#define XFRM_AE_ATTR_REPLAY_MAXAGE 0x100
|
|
#define XFRM_AE_ATTR_REPLAY_MAXDIFF 0x200
|
|
#define XFRM_AE_ATTR_REPLAY_STATE 0x400
|
|
#define XFRM_AE_ATTR_FAMILY 0x800
|
|
|
|
static struct nl_object_ops xfrm_ae_obj_ops;
|
|
/** @endcond */
|
|
|
|
|
|
static void xfrm_ae_free_data(struct nl_object *c)
|
|
{
|
|
struct xfrmnl_ae* ae = nl_object_priv (c);
|
|
|
|
if (ae == NULL)
|
|
return;
|
|
|
|
nl_addr_put (ae->sa_id.daddr);
|
|
nl_addr_put (ae->saddr);
|
|
|
|
if (ae->replay_state_esn)
|
|
free (ae->replay_state_esn);
|
|
}
|
|
|
|
static int xfrm_ae_clone(struct nl_object *_dst, struct nl_object *_src)
|
|
{
|
|
struct xfrmnl_ae* dst = nl_object_priv(_dst);
|
|
struct xfrmnl_ae* src = nl_object_priv(_src);
|
|
|
|
if (src->sa_id.daddr)
|
|
if ((dst->sa_id.daddr = nl_addr_clone (src->sa_id.daddr)) == NULL)
|
|
return -NLE_NOMEM;
|
|
|
|
if (src->saddr)
|
|
if ((dst->saddr = nl_addr_clone (src->saddr)) == NULL)
|
|
return -NLE_NOMEM;
|
|
|
|
if (src->replay_state_esn)
|
|
{
|
|
uint32_t len = sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * src->replay_state_esn->bmp_len);
|
|
if ((dst->replay_state_esn = malloc (len)) == NULL)
|
|
return -NLE_NOMEM;
|
|
memcpy (dst->replay_state_esn, src->replay_state_esn, len);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint64_t xfrm_ae_compare(struct nl_object *_a, struct nl_object *_b,
|
|
uint64_t attrs, int flags)
|
|
{
|
|
struct xfrmnl_ae* a = (struct xfrmnl_ae *) _a;
|
|
struct xfrmnl_ae* b = (struct xfrmnl_ae *) _b;
|
|
uint64_t diff = 0;
|
|
int found = 0;
|
|
|
|
#define XFRM_AE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, XFRM_AE_ATTR_##ATTR, a, b, EXPR)
|
|
diff |= XFRM_AE_DIFF(DADDR, nl_addr_cmp(a->sa_id.daddr, b->sa_id.daddr));
|
|
diff |= XFRM_AE_DIFF(SPI, a->sa_id.spi != b->sa_id.spi);
|
|
diff |= XFRM_AE_DIFF(PROTO, a->sa_id.proto != b->sa_id.proto);
|
|
diff |= XFRM_AE_DIFF(SADDR, nl_addr_cmp(a->saddr, b->saddr));
|
|
diff |= XFRM_AE_DIFF(FLAGS, a->flags != b->flags);
|
|
diff |= XFRM_AE_DIFF(REQID, a->reqid != b->reqid);
|
|
diff |= XFRM_AE_DIFF(MARK, (a->mark.v & a->mark.m) != (b->mark.v & b->mark.m));
|
|
diff |= XFRM_AE_DIFF(REPLAY_MAXAGE, a->replay_maxage != b->replay_maxage);
|
|
diff |= XFRM_AE_DIFF(REPLAY_MAXDIFF, a->replay_maxdiff != b->replay_maxdiff);
|
|
|
|
/* Compare replay states */
|
|
found = AVAILABLE_MISMATCH (a, b, XFRM_AE_ATTR_REPLAY_STATE);
|
|
if (found == 0) // attribute exists in both objects
|
|
{
|
|
if (((a->replay_state_esn != NULL) && (b->replay_state_esn == NULL)) ||
|
|
((a->replay_state_esn == NULL) && (b->replay_state_esn != NULL)))
|
|
found |= 1;
|
|
|
|
if (found == 0) // same replay type. compare actual values
|
|
{
|
|
if (a->replay_state_esn)
|
|
{
|
|
if (a->replay_state_esn->bmp_len != b->replay_state_esn->bmp_len)
|
|
diff |= 1;
|
|
else
|
|
{
|
|
uint32_t len = sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * a->replay_state_esn->bmp_len);
|
|
diff |= memcmp (a->replay_state_esn, b->replay_state_esn, len);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((a->replay_state.oseq != b->replay_state.oseq) ||
|
|
(a->replay_state.seq != b->replay_state.seq) ||
|
|
(a->replay_state.bitmap != b->replay_state.bitmap))
|
|
diff |= 1;
|
|
}
|
|
}
|
|
}
|
|
#undef XFRM_AE_DIFF
|
|
|
|
return diff;
|
|
}
|
|
|
|
/**
|
|
* @name XFRM AE Attribute Translations
|
|
* @{
|
|
*/
|
|
static const struct trans_tbl ae_attrs[] =
|
|
{
|
|
__ADD(XFRM_AE_ATTR_DADDR, daddr),
|
|
__ADD(XFRM_AE_ATTR_SPI, spi),
|
|
__ADD(XFRM_AE_ATTR_PROTO, protocol),
|
|
__ADD(XFRM_AE_ATTR_SADDR, saddr),
|
|
__ADD(XFRM_AE_ATTR_FLAGS, flags),
|
|
__ADD(XFRM_AE_ATTR_REQID, reqid),
|
|
__ADD(XFRM_AE_ATTR_MARK, mark),
|
|
__ADD(XFRM_AE_ATTR_LIFETIME, cur_lifetime),
|
|
__ADD(XFRM_AE_ATTR_REPLAY_MAXAGE, replay_maxage),
|
|
__ADD(XFRM_AE_ATTR_REPLAY_MAXDIFF, replay_maxdiff),
|
|
__ADD(XFRM_AE_ATTR_REPLAY_STATE, replay_state),
|
|
};
|
|
|
|
static char* xfrm_ae_attrs2str (int attrs, char *buf, size_t len)
|
|
{
|
|
return __flags2str(attrs, buf, len, ae_attrs, ARRAY_SIZE(ae_attrs));
|
|
}
|
|
/** @} */
|
|
|
|
/**
|
|
* @name XFRM AE Flags Translations
|
|
* @{
|
|
*/
|
|
|
|
static const struct trans_tbl ae_flags[] = {
|
|
__ADD(XFRM_AE_UNSPEC, unspecified),
|
|
__ADD(XFRM_AE_RTHR, replay threshold),
|
|
__ADD(XFRM_AE_RVAL, replay value),
|
|
__ADD(XFRM_AE_LVAL, lifetime value),
|
|
__ADD(XFRM_AE_ETHR, expiry time threshold),
|
|
__ADD(XFRM_AE_CR, replay update event),
|
|
__ADD(XFRM_AE_CE, timer expiry event),
|
|
__ADD(XFRM_AE_CU, policy update event),
|
|
};
|
|
|
|
char* xfrmnl_ae_flags2str(int flags, char *buf, size_t len)
|
|
{
|
|
return __flags2str (flags, buf, len, ae_flags, ARRAY_SIZE(ae_flags));
|
|
}
|
|
|
|
int xfrmnl_ae_str2flag(const char *name)
|
|
{
|
|
return __str2flags(name, ae_flags, ARRAY_SIZE(ae_flags));
|
|
}
|
|
/** @} */
|
|
|
|
static void xfrm_ae_dump_line(struct nl_object *a, struct nl_dump_params *p)
|
|
{
|
|
char dst[INET6_ADDRSTRLEN+5], src[INET6_ADDRSTRLEN+5];
|
|
struct xfrmnl_ae* ae = (struct xfrmnl_ae *) a;
|
|
char flags[128], buf[128];
|
|
time_t add_time, use_time;
|
|
struct tm *add_time_tm, *use_time_tm;
|
|
|
|
nl_dump_line(p, "src %s dst %s \n", nl_addr2str(ae->saddr, src, sizeof(src)),
|
|
nl_addr2str(ae->sa_id.daddr, dst, sizeof(dst)));
|
|
|
|
nl_dump_line(p, "\tproto %s spi 0x%x reqid %u ",
|
|
nl_ip_proto2str (ae->sa_id.proto, buf, sizeof (buf)),
|
|
ae->sa_id.spi, ae->reqid);
|
|
|
|
xfrmnl_ae_flags2str(ae->flags, flags, sizeof (flags));
|
|
nl_dump_line(p, "flags %s(0x%x) mark mask/value 0x%x/0x%x \n", flags,
|
|
ae->flags, ae->mark.m, ae->mark.v);
|
|
|
|
nl_dump_line(p, "\tlifetime current: \n");
|
|
nl_dump_line(p, "\t\tbytes %llu packets %llu \n", ae->lifetime_cur.bytes,
|
|
ae->lifetime_cur.packets);
|
|
if (ae->lifetime_cur.add_time != 0)
|
|
{
|
|
add_time = ae->lifetime_cur.add_time;
|
|
add_time_tm = gmtime (&add_time);
|
|
strftime (flags, 128, "%Y-%m-%d %H-%M-%S", add_time_tm);
|
|
}
|
|
else
|
|
{
|
|
sprintf (flags, "%s", "-");
|
|
}
|
|
|
|
if (ae->lifetime_cur.use_time != 0)
|
|
{
|
|
use_time = ae->lifetime_cur.use_time;
|
|
use_time_tm = gmtime (&use_time);
|
|
strftime (buf, 128, "%Y-%m-%d %H-%M-%S", use_time_tm);
|
|
}
|
|
else
|
|
{
|
|
sprintf (buf, "%s", "-");
|
|
}
|
|
nl_dump_line(p, "\t\tadd_time: %s, use_time: %s\n", flags, buf);
|
|
|
|
nl_dump_line(p, "\treplay info: \n");
|
|
nl_dump_line(p, "\t\tmax age %u max diff %u \n", ae->replay_maxage, ae->replay_maxdiff);
|
|
|
|
nl_dump_line(p, "\treplay state info: \n");
|
|
if (ae->replay_state_esn)
|
|
{
|
|
nl_dump_line(p, "\t\toseq %u seq %u oseq_hi %u seq_hi %u replay window: %u \n",
|
|
ae->replay_state_esn->oseq, ae->replay_state_esn->seq,
|
|
ae->replay_state_esn->oseq_hi, ae->replay_state_esn->seq_hi,
|
|
ae->replay_state_esn->replay_window);
|
|
}
|
|
else
|
|
{
|
|
nl_dump_line(p, "\t\toseq %u seq %u bitmap: %u \n", ae->replay_state.oseq,
|
|
ae->replay_state.seq, ae->replay_state.bitmap);
|
|
}
|
|
|
|
nl_dump(p, "\n");
|
|
}
|
|
|
|
static void xfrm_ae_dump_details(struct nl_object *a, struct nl_dump_params *p)
|
|
{
|
|
xfrm_ae_dump_line(a, p);
|
|
}
|
|
|
|
static void xfrm_ae_dump_stats(struct nl_object *a, struct nl_dump_params *p)
|
|
{
|
|
xfrm_ae_dump_details(a, p);
|
|
}
|
|
|
|
|
|
static int build_xfrm_ae_message(struct xfrmnl_ae *tmpl, int cmd, int flags,
|
|
struct nl_msg **result)
|
|
{
|
|
struct nl_msg* msg;
|
|
struct xfrm_aevent_id ae_id;
|
|
|
|
if (!(tmpl->ce_mask & XFRM_AE_ATTR_DADDR) ||
|
|
!(tmpl->ce_mask & XFRM_AE_ATTR_SPI) ||
|
|
!(tmpl->ce_mask & XFRM_AE_ATTR_PROTO))
|
|
return -NLE_MISSING_ATTR;
|
|
|
|
memcpy (&ae_id.sa_id.daddr, nl_addr_get_binary_addr (tmpl->sa_id.daddr), sizeof (uint8_t) * nl_addr_get_len (tmpl->sa_id.daddr));
|
|
ae_id.sa_id.spi = htonl(tmpl->sa_id.spi);
|
|
ae_id.sa_id.family = tmpl->sa_id.family;
|
|
ae_id.sa_id.proto = tmpl->sa_id.proto;
|
|
|
|
if (tmpl->ce_mask & XFRM_AE_ATTR_SADDR)
|
|
memcpy (&ae_id.saddr, nl_addr_get_binary_addr (tmpl->saddr), sizeof (uint8_t) * nl_addr_get_len (tmpl->saddr));
|
|
|
|
if (tmpl->ce_mask & XFRM_AE_ATTR_FLAGS)
|
|
ae_id.flags = tmpl->flags;
|
|
|
|
if (tmpl->ce_mask & XFRM_AE_ATTR_REQID)
|
|
ae_id.reqid = tmpl->reqid;
|
|
|
|
msg = nlmsg_alloc_simple(cmd, flags);
|
|
if (!msg)
|
|
return -NLE_NOMEM;
|
|
|
|
if (nlmsg_append(msg, &ae_id, sizeof(ae_id), NLMSG_ALIGNTO) < 0)
|
|
goto nla_put_failure;
|
|
|
|
if (tmpl->ce_mask & XFRM_AE_ATTR_MARK)
|
|
NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrmnl_mark), &tmpl->mark);
|
|
|
|
if (tmpl->ce_mask & XFRM_AE_ATTR_LIFETIME)
|
|
NLA_PUT (msg, XFRMA_LTIME_VAL, sizeof (struct xfrmnl_lifetime_cur), &tmpl->lifetime_cur);
|
|
|
|
if (tmpl->ce_mask & XFRM_AE_ATTR_REPLAY_MAXAGE)
|
|
NLA_PUT_U32 (msg, XFRMA_ETIMER_THRESH, tmpl->replay_maxage);
|
|
|
|
if (tmpl->ce_mask & XFRM_AE_ATTR_REPLAY_MAXDIFF)
|
|
NLA_PUT_U32 (msg, XFRMA_REPLAY_THRESH, tmpl->replay_maxdiff);
|
|
|
|
if (tmpl->ce_mask & XFRM_AE_ATTR_REPLAY_STATE) {
|
|
if (tmpl->replay_state_esn) {
|
|
uint32_t len = sizeof (struct xfrm_replay_state_esn) + (sizeof (uint32_t) * tmpl->replay_state_esn->bmp_len);
|
|
NLA_PUT (msg, XFRMA_REPLAY_ESN_VAL, len, tmpl->replay_state_esn);
|
|
}
|
|
else {
|
|
NLA_PUT (msg, XFRMA_REPLAY_VAL, sizeof (struct xfrmnl_replay_state), &tmpl->replay_state);
|
|
}
|
|
}
|
|
|
|
*result = msg;
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
nlmsg_free(msg);
|
|
return -NLE_MSGSIZE;
|
|
}
|
|
|
|
/**
|
|
* @name XFRM AE Update
|
|
* @{
|
|
*/
|
|
|
|
int xfrmnl_ae_set(struct nl_sock* sk, struct xfrmnl_ae* ae, int flags)
|
|
{
|
|
int err;
|
|
struct nl_msg *msg;
|
|
|
|
if ((err = build_xfrm_ae_message(ae, XFRM_MSG_NEWAE, flags|NLM_F_REPLACE, &msg)) < 0)
|
|
return err;
|
|
|
|
err = nl_send_auto_complete(sk, msg);
|
|
nlmsg_free(msg);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return nl_wait_for_ack(sk);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name XFRM AE Object Allocation/Freeage
|
|
* @{
|
|
*/
|
|
|
|
struct xfrmnl_ae* xfrmnl_ae_alloc(void)
|
|
{
|
|
return (struct xfrmnl_ae*) nl_object_alloc(&xfrm_ae_obj_ops);
|
|
}
|
|
|
|
void xfrmnl_ae_put(struct xfrmnl_ae* ae)
|
|
{
|
|
nl_object_put((struct nl_object *) ae);
|
|
}
|
|
|
|
/** @} */
|
|
|
|
static struct nla_policy xfrm_ae_policy[XFRMA_MAX+1] = {
|
|
[XFRMA_LTIME_VAL] = { .minlen = sizeof(struct xfrm_lifetime_cur) },
|
|
[XFRMA_REPLAY_VAL] = { .minlen = sizeof(struct xfrm_replay_state) },
|
|
[XFRMA_REPLAY_THRESH] = { .type = NLA_U32 },
|
|
[XFRMA_ETIMER_THRESH] = { .type = NLA_U32 },
|
|
[XFRMA_SRCADDR] = { .minlen = sizeof(xfrm_address_t) },
|
|
[XFRMA_MARK] = { .minlen = sizeof(struct xfrm_mark) },
|
|
[XFRMA_REPLAY_ESN_VAL] = { .minlen = sizeof(struct xfrm_replay_state_esn) },
|
|
};
|
|
|
|
int xfrmnl_ae_parse(struct nlmsghdr *n, struct xfrmnl_ae **result)
|
|
{
|
|
struct xfrmnl_ae* ae;
|
|
struct nlattr *tb[XFRMA_MAX + 1];
|
|
struct xfrm_aevent_id* ae_id;
|
|
int err;
|
|
|
|
ae = xfrmnl_ae_alloc();
|
|
if (!ae) {
|
|
err = -NLE_NOMEM;
|
|
goto errout;
|
|
}
|
|
|
|
ae->ce_msgtype = n->nlmsg_type;
|
|
ae_id = nlmsg_data(n);
|
|
|
|
err = nlmsg_parse(n, sizeof(struct xfrm_aevent_id), tb, XFRMA_MAX, xfrm_ae_policy);
|
|
if (err < 0)
|
|
goto errout;
|
|
|
|
ae->sa_id.daddr = nl_addr_build(ae_id->sa_id.family, &ae_id->sa_id.daddr, sizeof (ae_id->sa_id.daddr));
|
|
ae->sa_id.family= ae_id->sa_id.family;
|
|
ae->sa_id.spi = ntohl(ae_id->sa_id.spi);
|
|
ae->sa_id.proto = ae_id->sa_id.proto;
|
|
ae->saddr = nl_addr_build(ae_id->sa_id.family, &ae_id->saddr, sizeof (ae_id->saddr));
|
|
ae->reqid = ae_id->reqid;
|
|
ae->flags = ae_id->flags;
|
|
ae->ce_mask |= (XFRM_AE_ATTR_DADDR | XFRM_AE_ATTR_FAMILY | XFRM_AE_ATTR_SPI |
|
|
XFRM_AE_ATTR_PROTO | XFRM_AE_ATTR_SADDR | XFRM_AE_ATTR_REQID |
|
|
XFRM_AE_ATTR_FLAGS);
|
|
|
|
if (tb[XFRMA_MARK]) {
|
|
struct xfrm_mark* m = nla_data(tb[XFRMA_MARK]);
|
|
ae->mark.m = m->m;
|
|
ae->mark.v = m->v;
|
|
ae->ce_mask |= XFRM_AE_ATTR_MARK;
|
|
}
|
|
|
|
if (tb[XFRMA_LTIME_VAL]) {
|
|
struct xfrm_lifetime_cur* cur = nla_data(tb[XFRMA_LTIME_VAL]);
|
|
ae->lifetime_cur.bytes = cur->bytes;
|
|
ae->lifetime_cur.packets = cur->packets;
|
|
ae->lifetime_cur.add_time = cur->add_time;
|
|
ae->lifetime_cur.use_time = cur->use_time;
|
|
ae->ce_mask |= XFRM_AE_ATTR_LIFETIME;
|
|
}
|
|
|
|
if (tb[XFRM_AE_ETHR]) {
|
|
ae->replay_maxage = *(uint32_t*)nla_data(tb[XFRM_AE_ETHR]);
|
|
ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXAGE;
|
|
}
|
|
|
|
if (tb[XFRM_AE_RTHR]) {
|
|
ae->replay_maxdiff = *(uint32_t*)nla_data(tb[XFRM_AE_RTHR]);
|
|
ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXDIFF;
|
|
}
|
|
|
|
if (tb[XFRMA_REPLAY_ESN_VAL]) {
|
|
struct xfrm_replay_state_esn* esn = nla_data (tb[XFRMA_REPLAY_ESN_VAL]);
|
|
uint32_t len = sizeof (struct xfrmnl_replay_state_esn) + (sizeof (uint32_t) * esn->bmp_len);
|
|
|
|
if ((ae->replay_state_esn = calloc (1, len)) == NULL) {
|
|
err = -ENOMEM;
|
|
goto errout;
|
|
}
|
|
ae->replay_state_esn->oseq = esn->oseq;
|
|
ae->replay_state_esn->seq = esn->seq;
|
|
ae->replay_state_esn->oseq_hi = esn->oseq_hi;
|
|
ae->replay_state_esn->seq_hi = esn->seq_hi;
|
|
ae->replay_state_esn->replay_window = esn->replay_window;
|
|
ae->replay_state_esn->bmp_len = esn->bmp_len;
|
|
memcpy (ae->replay_state_esn->bmp, esn->bmp, sizeof (uint32_t) * esn->bmp_len);
|
|
ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE;
|
|
}
|
|
else
|
|
{
|
|
struct xfrm_replay_state* replay_state = nla_data (tb[XFRMA_REPLAY_VAL]);
|
|
ae->replay_state.oseq = replay_state->oseq;
|
|
ae->replay_state.seq = replay_state->seq;
|
|
ae->replay_state.bitmap = replay_state->bitmap;
|
|
ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE;
|
|
|
|
ae->replay_state_esn = NULL;
|
|
}
|
|
|
|
*result = ae;
|
|
return 0;
|
|
|
|
errout:
|
|
xfrmnl_ae_put(ae);
|
|
return err;
|
|
}
|
|
|
|
static int xfrm_ae_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
|
|
struct nlmsghdr *n, struct nl_parser_param *pp)
|
|
{
|
|
struct xfrmnl_ae* ae;
|
|
int err;
|
|
|
|
if ((err = xfrmnl_ae_parse(n, &ae)) < 0)
|
|
return err;
|
|
|
|
err = pp->pp_cb((struct nl_object *) ae, pp);
|
|
|
|
xfrmnl_ae_put(ae);
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @name XFRM AE Get
|
|
* @{
|
|
*/
|
|
|
|
int xfrmnl_ae_build_get_request(struct nl_addr* daddr, unsigned int spi, unsigned int protocol,
|
|
unsigned int mark_mask, unsigned int mark_value, struct nl_msg **result)
|
|
{
|
|
struct nl_msg *msg;
|
|
struct xfrm_aevent_id ae_id;
|
|
struct xfrmnl_mark mark;
|
|
|
|
if (!daddr || !spi)
|
|
{
|
|
fprintf(stderr, "APPLICATION BUG: %s:%d:%s: A valid destination address, spi must be specified\n",
|
|
__FILE__, __LINE__, __func__);
|
|
assert(0);
|
|
return -NLE_MISSING_ATTR;
|
|
}
|
|
|
|
memset(&ae_id, 0, sizeof(ae_id));
|
|
memcpy (&ae_id.sa_id.daddr, nl_addr_get_binary_addr (daddr), sizeof (uint8_t) * nl_addr_get_len (daddr));
|
|
ae_id.sa_id.spi = htonl(spi);
|
|
ae_id.sa_id.family = nl_addr_get_family (daddr);
|
|
ae_id.sa_id.proto = protocol;
|
|
|
|
if (!(msg = nlmsg_alloc_simple(XFRM_MSG_GETAE, 0)))
|
|
return -NLE_NOMEM;
|
|
|
|
if (nlmsg_append(msg, &ae_id, sizeof(ae_id), NLMSG_ALIGNTO) < 0)
|
|
goto nla_put_failure;
|
|
|
|
mark.m = mark_mask;
|
|
mark.v = mark_value;
|
|
NLA_PUT (msg, XFRMA_MARK, sizeof (struct xfrmnl_mark), &mark);
|
|
|
|
*result = msg;
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
nlmsg_free(msg);
|
|
return -NLE_MSGSIZE;
|
|
}
|
|
|
|
int xfrmnl_ae_get_kernel(struct nl_sock* sock, struct nl_addr* daddr, unsigned int spi, unsigned int protocol,
|
|
unsigned int mark_mask, unsigned int mark_value, struct xfrmnl_ae** result)
|
|
{
|
|
struct nl_msg *msg = NULL;
|
|
struct nl_object *obj;
|
|
int err;
|
|
|
|
if ((err = xfrmnl_ae_build_get_request(daddr, spi, protocol, mark_mask, mark_value, &msg)) < 0)
|
|
return err;
|
|
|
|
err = nl_send_auto(sock, msg);
|
|
nlmsg_free(msg);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if ((err = nl_pickup(sock, &xfrm_ae_msg_parser, &obj)) < 0)
|
|
return err;
|
|
|
|
/* We have used xfrm_ae_msg_parser(), object is definitely a xfrm ae */
|
|
*result = (struct xfrmnl_ae *) obj;
|
|
|
|
/* If an object has been returned, we also need to wait for the ACK */
|
|
if (err == 0 && obj)
|
|
nl_wait_for_ack(sock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Attributes
|
|
* @{
|
|
*/
|
|
|
|
static inline int __assign_addr(struct xfrmnl_ae* ae, struct nl_addr **pos,
|
|
struct nl_addr *new, int flag, int nocheck)
|
|
{
|
|
if (!nocheck) {
|
|
if (ae->ce_mask & XFRM_AE_ATTR_FAMILY) {
|
|
if (nl_addr_get_family (new) != ae->sa_id.family)
|
|
return -NLE_AF_MISMATCH;
|
|
} else {
|
|
ae->sa_id.family = nl_addr_get_family (new);
|
|
ae->ce_mask |= XFRM_AE_ATTR_FAMILY;
|
|
}
|
|
}
|
|
|
|
if (*pos)
|
|
nl_addr_put(*pos);
|
|
|
|
nl_addr_get(new);
|
|
*pos = new;
|
|
|
|
ae->ce_mask |= flag;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
struct nl_addr* xfrmnl_ae_get_daddr (struct xfrmnl_ae* ae)
|
|
{
|
|
if (ae->ce_mask & XFRM_AE_ATTR_DADDR)
|
|
return ae->sa_id.daddr;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
int xfrmnl_ae_set_daddr (struct xfrmnl_ae* ae, struct nl_addr* addr)
|
|
{
|
|
return __assign_addr(ae, &ae->sa_id.daddr, addr, XFRM_AE_ATTR_DADDR, 0);
|
|
}
|
|
|
|
int xfrmnl_ae_get_spi (struct xfrmnl_ae* ae)
|
|
{
|
|
if (ae->ce_mask & XFRM_AE_ATTR_SPI)
|
|
return ae->sa_id.spi;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int xfrmnl_ae_set_spi (struct xfrmnl_ae* ae, unsigned int spi)
|
|
{
|
|
ae->sa_id.spi = spi;
|
|
ae->ce_mask |= XFRM_AE_ATTR_SPI;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xfrmnl_ae_get_family (struct xfrmnl_ae* ae)
|
|
{
|
|
if (ae->ce_mask & XFRM_AE_ATTR_FAMILY)
|
|
return ae->sa_id.family;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int xfrmnl_ae_set_family (struct xfrmnl_ae* ae, unsigned int family)
|
|
{
|
|
ae->sa_id.family = family;
|
|
ae->ce_mask |= XFRM_AE_ATTR_FAMILY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xfrmnl_ae_get_proto (struct xfrmnl_ae* ae)
|
|
{
|
|
if (ae->ce_mask & XFRM_AE_ATTR_PROTO)
|
|
return ae->sa_id.proto;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int xfrmnl_ae_set_proto (struct xfrmnl_ae* ae, unsigned int protocol)
|
|
{
|
|
ae->sa_id.proto = protocol;
|
|
ae->ce_mask |= XFRM_AE_ATTR_PROTO;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct nl_addr* xfrmnl_ae_get_saddr (struct xfrmnl_ae* ae)
|
|
{
|
|
if (ae->ce_mask & XFRM_AE_ATTR_SADDR)
|
|
return ae->saddr;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
int xfrmnl_ae_set_saddr (struct xfrmnl_ae* ae, struct nl_addr* addr)
|
|
{
|
|
return __assign_addr(ae, &ae->saddr, addr, XFRM_AE_ATTR_SADDR, 1);
|
|
}
|
|
|
|
int xfrmnl_ae_get_flags (struct xfrmnl_ae* ae)
|
|
{
|
|
if (ae->ce_mask & XFRM_AE_ATTR_FLAGS)
|
|
return ae->flags;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int xfrmnl_ae_set_flags (struct xfrmnl_ae* ae, unsigned int flags)
|
|
{
|
|
ae->flags = flags;
|
|
ae->ce_mask |= XFRM_AE_ATTR_FLAGS;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xfrmnl_ae_get_reqid (struct xfrmnl_ae* ae)
|
|
{
|
|
if (ae->ce_mask & XFRM_AE_ATTR_REQID)
|
|
return ae->reqid;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int xfrmnl_ae_set_reqid (struct xfrmnl_ae* ae, unsigned int reqid)
|
|
{
|
|
ae->reqid = reqid;
|
|
ae->ce_mask |= XFRM_AE_ATTR_REQID;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xfrmnl_ae_get_mark (struct xfrmnl_ae* ae, unsigned int* mark_mask, unsigned int* mark_value)
|
|
{
|
|
if (mark_mask == NULL || mark_value == NULL)
|
|
return -1;
|
|
|
|
if (ae->ce_mask & XFRM_AE_ATTR_MARK)
|
|
{
|
|
*mark_mask = ae->mark.m;
|
|
*mark_value = ae->mark.v;
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int xfrmnl_ae_set_mark (struct xfrmnl_ae* ae, unsigned int value, unsigned int mask)
|
|
{
|
|
ae->mark.v = value;
|
|
ae->mark.m = mask;
|
|
ae->ce_mask |= XFRM_AE_ATTR_MARK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xfrmnl_ae_get_curlifetime (struct xfrmnl_ae* ae, unsigned long long int* curr_bytes,
|
|
unsigned long long int* curr_packets, unsigned long long int* curr_add_time,
|
|
unsigned long long int* curr_use_time)
|
|
{
|
|
if (curr_bytes == NULL || curr_packets == NULL || curr_add_time == NULL || curr_use_time == NULL)
|
|
return -1;
|
|
|
|
if (ae->ce_mask & XFRM_AE_ATTR_LIFETIME)
|
|
{
|
|
*curr_bytes = ae->lifetime_cur.bytes;
|
|
*curr_packets = ae->lifetime_cur.packets;
|
|
*curr_add_time = ae->lifetime_cur.add_time;
|
|
*curr_use_time = ae->lifetime_cur.use_time;
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int xfrmnl_ae_set_curlifetime (struct xfrmnl_ae* ae, unsigned long long int curr_bytes,
|
|
unsigned long long int curr_packets, unsigned long long int curr_add_time,
|
|
unsigned long long int curr_use_time)
|
|
{
|
|
ae->lifetime_cur.bytes = curr_bytes;
|
|
ae->lifetime_cur.packets = curr_packets;
|
|
ae->lifetime_cur.add_time = curr_add_time;
|
|
ae->lifetime_cur.use_time = curr_use_time;
|
|
ae->ce_mask |= XFRM_AE_ATTR_LIFETIME;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xfrmnl_ae_get_replay_maxage (struct xfrmnl_ae* ae)
|
|
{
|
|
if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_MAXAGE)
|
|
return ae->replay_maxage;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int xfrmnl_ae_set_replay_maxage (struct xfrmnl_ae* ae, unsigned int replay_maxage)
|
|
{
|
|
ae->replay_maxage = replay_maxage;
|
|
ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXAGE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xfrmnl_ae_get_replay_maxdiff (struct xfrmnl_ae* ae)
|
|
{
|
|
if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_MAXDIFF)
|
|
return ae->replay_maxdiff;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int xfrmnl_ae_set_replay_maxdiff (struct xfrmnl_ae* ae, unsigned int replay_maxdiff)
|
|
{
|
|
ae->replay_maxdiff = replay_maxdiff;
|
|
ae->ce_mask |= XFRM_AE_ATTR_REPLAY_MAXDIFF;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xfrmnl_ae_get_replay_state (struct xfrmnl_ae* ae, unsigned int* oseq, unsigned int* seq, unsigned int* bmp)
|
|
{
|
|
if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_STATE)
|
|
{
|
|
if (ae->replay_state_esn == NULL)
|
|
{
|
|
*oseq = ae->replay_state.oseq;
|
|
*seq = ae->replay_state.seq;
|
|
*bmp = ae->replay_state.bitmap;
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int xfrmnl_ae_set_replay_state (struct xfrmnl_ae* ae, unsigned int oseq, unsigned int seq, unsigned int bitmap)
|
|
{
|
|
ae->replay_state.oseq = oseq;
|
|
ae->replay_state.seq = seq;
|
|
ae->replay_state.bitmap = bitmap;
|
|
ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xfrmnl_ae_get_replay_state_esn(struct xfrmnl_ae* ae, unsigned int* oseq, unsigned int* seq, unsigned int* oseq_hi,
|
|
unsigned int* seq_hi, unsigned int* replay_window, unsigned int* bmp_len, unsigned int* bmp)
|
|
{
|
|
if (ae->ce_mask & XFRM_AE_ATTR_REPLAY_STATE)
|
|
{
|
|
if (ae->replay_state_esn)
|
|
{
|
|
*oseq = ae->replay_state_esn->oseq;
|
|
*seq = ae->replay_state_esn->seq;
|
|
*oseq_hi= ae->replay_state_esn->oseq_hi;
|
|
*seq_hi = ae->replay_state_esn->seq_hi;
|
|
*replay_window = ae->replay_state_esn->replay_window;
|
|
*bmp_len = ae->replay_state_esn->bmp_len; // In number of 32 bit words
|
|
memcpy (bmp, ae->replay_state_esn->bmp, ae->replay_state_esn->bmp_len * sizeof (uint32_t));
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
int xfrmnl_ae_set_replay_state_esn(struct xfrmnl_ae* ae, unsigned int oseq, unsigned int seq,
|
|
unsigned int oseq_hi, unsigned int seq_hi, unsigned int replay_window,
|
|
unsigned int bmp_len, unsigned int* bmp)
|
|
{
|
|
/* Free the old replay ESN state and allocate new one */
|
|
if (ae->replay_state_esn)
|
|
free (ae->replay_state_esn);
|
|
|
|
if ((ae->replay_state_esn = calloc (1, sizeof (struct xfrmnl_replay_state_esn) + sizeof (uint32_t) * bmp_len)) == NULL)
|
|
return -1;
|
|
|
|
ae->replay_state_esn->oseq = oseq;
|
|
ae->replay_state_esn->seq = seq;
|
|
ae->replay_state_esn->oseq_hi = oseq_hi;
|
|
ae->replay_state_esn->seq_hi = seq_hi;
|
|
ae->replay_state_esn->replay_window = replay_window;
|
|
ae->replay_state_esn->bmp_len = bmp_len; // In number of 32 bit words
|
|
memcpy (ae->replay_state_esn->bmp, bmp, bmp_len * sizeof (uint32_t));
|
|
ae->ce_mask |= XFRM_AE_ATTR_REPLAY_STATE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
static struct nl_object_ops xfrm_ae_obj_ops = {
|
|
.oo_name = "xfrm/ae",
|
|
.oo_size = sizeof(struct xfrmnl_ae),
|
|
.oo_free_data = xfrm_ae_free_data,
|
|
.oo_clone = xfrm_ae_clone,
|
|
.oo_dump = {
|
|
[NL_DUMP_LINE] = xfrm_ae_dump_line,
|
|
[NL_DUMP_DETAILS] = xfrm_ae_dump_details,
|
|
[NL_DUMP_STATS] = xfrm_ae_dump_stats,
|
|
},
|
|
.oo_compare = xfrm_ae_compare,
|
|
.oo_attrs2str = xfrm_ae_attrs2str,
|
|
.oo_id_attrs = (XFRM_AE_ATTR_DADDR | XFRM_AE_ATTR_SPI | XFRM_AE_ATTR_PROTO),
|
|
};
|
|
|
|
/** @} */
|
|
|