--- /dev/null
+/*
+ * track.c RADIUS packet tracking
+ *
+ * Version: $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2016 Alan DeKok <aland@freeradius.org>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/util/track.h>
+
+/*
+ * RADIUS-specific tracking table.
+ *
+ * It's a fixed-size array of 256 entries, indexed by ID. Which
+ * means we don't need to store ID in the table. We also don't
+ * need to store the packet type, as we assume that we have a
+ * unique tracking table per packet type.
+ */
+
+struct fr_tracking_t {
+ int num_entries; //!< number of used entries.
+
+ fr_tracking_entry_t packet[256];
+};
+
+/** Create a tracking table for one type of RADIUS packets.
+ *
+ * @param[in] ctx the talloc ctx
+ * @return
+ * - NULL on error
+ * - fr_tracking_t * on success
+ */
+fr_tracking_t *fr_radius_tracking_create(TALLOC_CTX *ctx)
+{
+ fr_tracking_t *ft;
+
+ if (!ctx) return NULL;
+
+ ft = talloc_zero(ctx, fr_tracking_t);
+ if (!ft) return NULL;
+
+ ft->num_entries = 0;
+ return ft;
+}
+
+/** Delete an entry from the tracking table.
+ *
+ * @param[in] ft the tracking table
+ * @param[in] id the ID of the entry to delete
+ * @return
+ * - <0 on error
+ * - 0 on success
+ */
+int fr_radius_tracking_entry_delete(fr_tracking_t *ft, uint8_t id)
+{
+ fr_tracking_entry_t *entry;
+
+#ifndef NDEBUG
+ (void) talloc_get_type_abort(ft, fr_tracking_t);
+#endif
+ entry = &ft->packet[id];
+ if (entry->timestamp == 0) return -1;
+
+ entry->timestamp = 0;
+ ft->num_entries--;
+
+ return 0;
+}
+
+/** Insert a (possibly new) packet and a timestamp
+ *
+ * @param[in] ft the tracking table
+ * @param[in] packet the RADIUS packet to insert
+ * @param[in] timestamp when this packet was received
+ * @param[out] p_entry pointer to newly inserted entry.
+ * @return
+ * - FR_TRACKING_UNUSED, there was an error inserting the element
+ * - FR_TRACKING_NEW, a new entry was created
+ * - FR_TRACKING_SAME, the packet is the same as one already in the tracking table
+ * - FR_TRACKING_DIFFERENT, the old packet was deleted, and the newer packet inserted
+ */
+fr_tracking_status_t fr_radius_tracking_entry_insert(fr_tracking_t *ft, uint8_t *packet, fr_time_t timestamp,
+ fr_tracking_entry_t **p_entry)
+{
+ fr_tracking_entry_t *entry;
+
+#ifndef NDEBUG
+ (void) talloc_get_type_abort(ft, fr_tracking_t);
+#endif
+
+ entry = &ft->packet[packet[1]];
+
+ /*
+ * The entry is unused, insert it.
+ */
+ if (entry->timestamp == 0) {
+ entry->timestamp = timestamp;
+ memcpy(&entry->data[0], packet + 2, 18);
+ *p_entry = entry;
+
+ ft->num_entries++;
+ return FR_TRACKING_NEW;
+ }
+
+ /*
+ * Is it the same packet? If so, return that.
+ */
+ if (memcmp(packet + 2, &entry->data[0], 18) == 0) {
+ *p_entry = entry;
+ return FR_TRACKING_SAME;
+ }
+
+ /*
+ * It's in use, but the new packet is different. update
+ * the timestamp, so that anyone checking it knows it's
+ * no longer relevant.
+ */
+ entry->timestamp = timestamp;
+
+ /*
+ * Copy the new packet over top of the old one.
+ */
+ memcpy(&entry->data[0], packet + 2, 18);
+ *p_entry = entry;
+
+ return FR_TRACKING_DIFFERENT;
+}
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#ifndef _FR_TRACK_H
+#define _FR_TRACK_H
+/**
+ * $Id$
+ *
+ * @file util/track.h
+ * @brief RADIUS packet tracking.
+ *
+ * @copyright 2016 Alan DeKok <aland@freeradius.org>
+ */
+RCSIDH(track_h, "$Id$")
+
+#include <freeradius-devel/util/time.h>
+#include <talloc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct fr_tracking_t fr_tracking_t;
+
+/**
+ * An entry for the tracking table. It contains the minimum
+ * information required to track RADIUS packets.
+ */
+typedef struct fr_tracking_entry_t {
+ fr_time_t timestamp; //!< when received
+ uint8_t data[18]; //!< 2 byte length + authentication vector
+} fr_tracking_entry_t;
+
+/**
+ * The status of an insert.
+ */
+typedef enum fr_tracking_status_t {
+ FR_TRACKING_UNUSED = 0,
+ FR_TRACKING_NEW,
+ FR_TRACKING_SAME,
+ FR_TRACKING_DIFFERENT,
+} fr_tracking_status_t;
+
+fr_tracking_t *fr_radius_tracking_create(TALLOC_CTX *ctx);
+int fr_radius_tracking_entry_delete(fr_tracking_t *ft, uint8_t id) CC_HINT(nonnull);
+fr_tracking_status_t fr_radius_tracking_entry_insert(fr_tracking_t *ft, uint8_t *packet, fr_time_t timestamp,
+ fr_tracking_entry_t **p_entry) CC_HINT(nonnull);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FR_TRACK_H */