+
+static struct mbuf *
+key_setdumpsastats (u_int32_t dir,
+ struct sastat *stats,
+ u_int32_t max_stats,
+ u_int64_t session_ids[],
+ u_int32_t seq,
+ u_int32_t pid)
+{
+ struct mbuf *result = NULL, *m = NULL;
+
+ m = key_setsadbmsg(SADB_GETSASTAT, 0, 0, seq, pid, 0);
+ if (!m) {
+ goto fail;
+ }
+ result = m;
+
+ m = key_setsadbsession_id(session_ids);
+ if (!m) {
+ goto fail;
+ }
+ m_cat(result, m);
+
+ m = key_setsadbsastat(dir,
+ stats,
+ max_stats);
+ if (!m) {
+ goto fail;
+ }
+ m_cat(result, m);
+
+ if ((result->m_flags & M_PKTHDR) == 0) {
+ goto fail;
+ }
+
+ if (result->m_len < sizeof(struct sadb_msg)) {
+ result = m_pullup(result, sizeof(struct sadb_msg));
+ if (result == NULL) {
+ goto fail;
+ }
+ }
+
+ result->m_pkthdr.len = 0;
+ for (m = result; m; m = m->m_next) {
+ result->m_pkthdr.len += m->m_len;
+ }
+
+ mtod(result, struct sadb_msg *)->sadb_msg_len =
+ PFKEY_UNIT64(result->m_pkthdr.len);
+
+ return result;
+
+ fail:
+ if (result) {
+ m_freem(result);
+ }
+ return NULL;
+}
+
+/*
+ * SADB_GETSASTAT processing
+ * dump all stats for matching entries in SAD.
+ *
+ * m will always be freed.
+ */
+
+static int
+key_getsastat (struct socket *so,
+ struct mbuf *m,
+ const struct sadb_msghdr *mhp)
+{
+ struct sadb_session_id *session_id;
+ u_int32_t bufsize, arg_count, res_count;
+ struct sadb_sastat *sa_stats_arg;
+ struct sastat *sa_stats_sav = NULL;
+ struct mbuf *n;
+ int error = 0;
+
+ /* sanity check */
+ if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+ panic("%s: NULL pointer is passed.\n", __FUNCTION__);
+
+ if (mhp->ext[SADB_EXT_SESSION_ID] == NULL) {
+ printf("%s: invalid message is passed. missing session-id.\n", __FUNCTION__);
+ return key_senderror(so, m, EINVAL);
+ }
+ if (mhp->extlen[SADB_EXT_SESSION_ID] < sizeof(struct sadb_session_id)) {
+ printf("%s: invalid message is passed. short session-id.\n", __FUNCTION__);
+ return key_senderror(so, m, EINVAL);
+ }
+ if (mhp->ext[SADB_EXT_SASTAT] == NULL) {
+ printf("%s: invalid message is passed. missing stat args.\n", __FUNCTION__);
+ return key_senderror(so, m, EINVAL);
+ }
+ if (mhp->extlen[SADB_EXT_SASTAT] < sizeof(*sa_stats_arg)) {
+ printf("%s: invalid message is passed. short stat args.\n", __FUNCTION__);
+ return key_senderror(so, m, EINVAL);
+ }
+
+ lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_NOTOWNED);
+
+ // exit early if there are no active SAs
+ if (ipsec_sav_count <= 0) {
+ printf("%s: No active SAs.\n", __FUNCTION__);
+ error = ENOENT;
+ goto end;
+ }
+ bufsize = (ipsec_sav_count + 1) * sizeof(*sa_stats_sav);
+
+ KMALLOC_WAIT(sa_stats_sav, __typeof__(sa_stats_sav), bufsize);
+ if (sa_stats_sav == NULL) {
+ printf("%s: No more memory.\n", __FUNCTION__);
+ error = ENOMEM;
+ goto end;
+ }
+ bzero(sa_stats_sav, bufsize);
+
+ sa_stats_arg = (__typeof__(sa_stats_arg))
+ (void *)mhp->ext[SADB_EXT_SASTAT];
+ arg_count = sa_stats_arg->sadb_sastat_list_len;
+ // exit early if there are no requested SAs
+ if (arg_count == 0) {
+ printf("%s: No SAs requested.\n", __FUNCTION__);
+ error = ENOENT;
+ goto end;
+ }
+ res_count = 0;
+
+ if (key_getsastatbyspi((struct sastat *)(sa_stats_arg + 1),
+ arg_count,
+ sa_stats_sav,
+ &res_count)) {
+ printf("%s: Error finding SAs.\n", __FUNCTION__);
+ error = ENOENT;
+ goto end;
+ }
+ if (!res_count) {
+ printf("%s: No SAs found.\n", __FUNCTION__);
+ error = ENOENT;
+ goto end;
+ }
+
+ session_id = (__typeof__(session_id))
+ (void *)mhp->ext[SADB_EXT_SESSION_ID];
+
+ /* send this to the userland. */
+ n = key_setdumpsastats(sa_stats_arg->sadb_sastat_dir,
+ sa_stats_sav,
+ res_count,
+ session_id->sadb_session_id_v,
+ mhp->msg->sadb_msg_seq,
+ mhp->msg->sadb_msg_pid);
+ if (!n) {
+ printf("%s: No bufs to dump stats.\n", __FUNCTION__);
+ error = ENOBUFS;
+ goto end;
+ }
+
+ key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
+end:
+ if (sa_stats_sav) {
+ KFREE(sa_stats_sav);
+ }
+
+ if (error)
+ return key_senderror(so, m, error);
+
+ m_freem(m);
+ return 0;
+}
+
+static void
+key_update_natt_keepalive_timestamp (struct secasvar *sav_sent,
+ struct secasvar *sav_update)
+{
+ struct secasindex saidx_swap_sent_addr;
+
+ // exit early if two SAs are identical, or if sav_update is current
+ if (sav_sent == sav_update ||
+ sav_update->natt_last_activity == natt_now) {
+ return;
+ }
+
+ // assuming that (sav_update->remote_ike_port != 0 && (esp_udp_encap_port & 0xFFFF) != 0)
+
+ bzero(&saidx_swap_sent_addr, sizeof(saidx_swap_sent_addr));
+ memcpy(&saidx_swap_sent_addr.src, &sav_sent->sah->saidx.dst, sizeof(saidx_swap_sent_addr.src));
+ memcpy(&saidx_swap_sent_addr.dst, &sav_sent->sah->saidx.src, sizeof(saidx_swap_sent_addr.dst));
+ saidx_swap_sent_addr.proto = sav_sent->sah->saidx.proto;
+ saidx_swap_sent_addr.mode = sav_sent->sah->saidx.mode;
+ // we ignore reqid for split-tunnel setups
+
+ if (key_cmpsaidx(&sav_sent->sah->saidx, &sav_update->sah->saidx, CMP_MODE | CMP_PORT) ||
+ key_cmpsaidx(&saidx_swap_sent_addr, &sav_update->sah->saidx, CMP_MODE | CMP_PORT)) {
+ sav_update->natt_last_activity = natt_now;
+ }
+}