/* 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 #include #include #include #include #include /** @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), }; /** @} */