| /* 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 "nl-default.h" |
| |
| #include <linux/xfrm.h> |
| |
| #include <netlink/netlink.h> |
| #include <netlink/cache.h> |
| #include <netlink/object.h> |
| #include <netlink/xfrm/ae.h> |
| |
| #include "nl-xfrm.h" |
| #include "nl-priv-dynamic-core/object-api.h" |
| #include "nl-priv-dynamic-core/nl-core.h" |
| #include "nl-priv-dynamic-core/cache-api.h" |
| #include "nl-aux-core/nl-core.h" |
| #include "nl-aux-xfrm/nl-xfrm.h" |
| |
| /** @cond SKIP */ |
| |
| struct xfrmnl_sa_id { |
| struct nl_addr* daddr; |
| uint32_t spi; |
| uint16_t family; |
| uint8_t proto; |
| }; |
| |
| struct xfrmnl_ae { |
| NLHDR_COMMON |
| |
| struct xfrmnl_sa_id sa_id; |
| struct nl_addr* saddr; |
| uint32_t flags; |
| uint32_t reqid; |
| struct xfrmnl_mark mark; |
| struct xfrmnl_lifetime_cur lifetime_cur; |
| uint32_t replay_maxage; |
| uint32_t replay_maxdiff; |
| struct xfrmnl_replay_state replay_state; |
| struct xfrmnl_replay_state_esn* replay_state_esn; |
| }; |
| |
| #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); |
| |
| dst->sa_id.daddr = NULL; |
| dst->saddr = NULL; |
| dst->replay_state_esn = NULL; |
| |
| 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 _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR) |
| diff |= _DIFF(XFRM_AE_ATTR_DADDR, |
| nl_addr_cmp(a->sa_id.daddr, b->sa_id.daddr)); |
| diff |= _DIFF(XFRM_AE_ATTR_SPI, a->sa_id.spi != b->sa_id.spi); |
| diff |= _DIFF(XFRM_AE_ATTR_PROTO, a->sa_id.proto != b->sa_id.proto); |
| diff |= _DIFF(XFRM_AE_ATTR_SADDR, nl_addr_cmp(a->saddr, b->saddr)); |
| diff |= _DIFF(XFRM_AE_ATTR_FLAGS, a->flags != b->flags); |
| diff |= _DIFF(XFRM_AE_ATTR_REQID, a->reqid != b->reqid); |
| diff |= _DIFF(XFRM_AE_ATTR_MARK, |
| (a->mark.v & a->mark.m) != (b->mark.v & b->mark.m)); |
| diff |= _DIFF(XFRM_AE_ATTR_REPLAY_MAXAGE, |
| a->replay_maxage != b->replay_maxage); |
| diff |= _DIFF(XFRM_AE_ATTR_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 _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; |
| struct tm tm_buf; |
| |
| 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", |
| (long long unsigned)ae->lifetime_cur.bytes, |
| (long long unsigned)ae->lifetime_cur.packets); |
| if (ae->lifetime_cur.add_time != 0) |
| { |
| add_time = ae->lifetime_cur.add_time; |
| add_time_tm = gmtime_r (&add_time, &tm_buf); |
| 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_r (&use_time, &tm_buf); |
| 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; |
| |
| memset(&ae_id, 0, sizeof(ae_id)); |
| |
| 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) |
| { |
| _nl_auto_xfrmnl_ae struct xfrmnl_ae *ae = NULL; |
| struct nlattr *tb[XFRMA_MAX + 1]; |
| struct xfrm_aevent_id* ae_id; |
| int err; |
| |
| ae = xfrmnl_ae_alloc(); |
| if (!ae) |
| return -NLE_NOMEM; |
| |
| 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) |
| return err; |
| |
| if (!(ae->sa_id.daddr = |
| _nl_addr_build(ae_id->sa_id.family, &ae_id->sa_id.daddr))) |
| return -NLE_NOMEM; |
| 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; |
| if (!(ae->saddr = _nl_addr_build(ae_id->sa_id.family, &ae_id->saddr))) |
| return -NLE_NOMEM; |
| 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) |
| return -NLE_NOMEM; |
| 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 = _nl_steal_pointer(&ae); |
| return 0; |
| } |
| |
| 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), |
| }; |
| |
| /** @} */ |
| |