]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add proto_listen_load
authorAlan T. DeKok <aland@freeradius.org>
Tue, 29 Jun 2021 19:23:15 +0000 (15:23 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 29 Jun 2021 21:01:45 +0000 (17:01 -0400)
doesn't quite work yet for a few reasons, but that will come
next.

src/listen/load/all.mk
src/listen/load/proto_load.c [new file with mode: 0644]
src/listen/load/proto_load.mk [new file with mode: 0644]
src/listen/load/proto_load_step.c

index 77a03949eba4fe25683f5b5f0a77c24385027ff1..59cc977b5880a551f398b6e7e2913787018fdf44 100644 (file)
@@ -1,2 +1,3 @@
 SUBMAKEFILES := \
+       proto_load.mk \
        proto_load_step.mk \
diff --git a/src/listen/load/proto_load.c b/src/listen/load/proto_load.c
new file mode 100644 (file)
index 0000000..fa6033c
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ *   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 loads.
+ *
+ *   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
+ */
+
+/**
+ * $Id$
+ * @file proto_load.c
+ * @brief Load master protocol handler.
+ *
+ * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ * @copyright 2016 Alan DeKok (aland@freeradius.org)
+ */
+#include <freeradius-devel/io/application.h>
+#include <freeradius-devel/io/listen.h>
+#include <freeradius-devel/io/schedule.h>
+#include <freeradius-devel/radius/radius.h>
+#include <freeradius-devel/server/base.h>
+#include <freeradius-devel/server/virtual_servers.h>
+#include <freeradius-devel/util/debug.h>
+
+#include "proto_load.h"
+
+extern fr_app_t proto_load;
+static int type_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, CONF_PARSER const *rule);
+static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, CONF_PARSER const *rule);
+
+/** How to parse a Load listen section
+ *
+ */
+static CONF_PARSER const proto_load_config[] = {
+       { FR_CONF_OFFSET("type", FR_TYPE_VOID | FR_TYPE_NOT_EMPTY | FR_TYPE_REQUIRED, proto_load_t,
+                         type), .func = type_parse },
+       { FR_CONF_OFFSET("transport", FR_TYPE_VOID, proto_load_t, io.submodule),
+         .func = transport_parse, .dflt = "step" },
+
+       /*
+        *      Add this as a synonym so normal humans can understand it.
+        */
+       { FR_CONF_OFFSET("max_entry_size", FR_TYPE_UINT32, proto_load_t, max_packet_size) } ,
+
+       /*
+        *      For performance tweaking.  NOT for normal humans.
+        */
+       { FR_CONF_OFFSET("max_packet_size", FR_TYPE_UINT32, proto_load_t, max_packet_size) } ,
+       { FR_CONF_OFFSET("num_messages", FR_TYPE_UINT32, proto_load_t, num_messages) } ,
+
+       { FR_CONF_OFFSET("priority", FR_TYPE_UINT32, proto_load_t, priority) },
+
+       CONF_PARSER_TERMINATOR
+};
+
+static fr_dict_t const *dict_freeradius;
+
+extern fr_dict_autoload_t proto_load_dict[];
+fr_dict_autoload_t proto_load_dict[] = {
+       { .out = &dict_freeradius, .proto = "freeradius" },
+
+       { NULL }
+};
+
+static fr_dict_attr_t const *attr_packet_dst_ip_address;
+static fr_dict_attr_t const *attr_packet_dst_ipv6_address;
+static fr_dict_attr_t const *attr_packet_dst_port;
+static fr_dict_attr_t const *attr_packet_original_timestamp;
+static fr_dict_attr_t const *attr_packet_src_ip_address;
+static fr_dict_attr_t const *attr_packet_src_ipv6_address;
+static fr_dict_attr_t const *attr_packet_src_port;
+static fr_dict_attr_t const *attr_protocol;
+
+extern fr_dict_attr_autoload_t proto_load_dict_attr[];
+fr_dict_attr_autoload_t proto_load_dict_attr[] = {
+       { .out = &attr_packet_dst_ip_address, .name = "Packet-Dst-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_freeradius },
+       { .out = &attr_packet_dst_ipv6_address, .name = "Packet-Dst-IPv6-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_freeradius },
+       { .out = &attr_packet_dst_port, .name = "Packet-Dst-Port", .type = FR_TYPE_UINT16, .dict = &dict_freeradius },
+       { .out = &attr_packet_original_timestamp, .name = "Packet-Original-Timestamp", .type = FR_TYPE_DATE, .dict = &dict_freeradius },
+       { .out = &attr_packet_src_ip_address, .name = "Packet-Src-IP-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_freeradius },
+       { .out = &attr_packet_src_ipv6_address, .name = "Packet-Src-IPv6-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_freeradius },
+       { .out = &attr_packet_src_port, .name = "Packet-Src-Port", .type = FR_TYPE_UINT16, .dict = &dict_freeradius },
+       { .out = &attr_protocol, .name = "Protocol", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
+
+       { NULL }
+};
+
+/** Wrapper around dl_instance which translates the packet-type into a submodule name
+ *
+ * @param[in] ctx      to allocate data in (instance of proto_load).
+ * @param[out] out     Where to write a dl_module_inst_t containing the module handle and instance.
+ * @param[in] parent   Base structure address.
+ * @param[in] ci       #CONF_PAIR specifying the name of the type module.
+ * @param[in] rule     unused.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static int type_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
+{
+       proto_load_t            *inst = talloc_get_type_abort(parent, proto_load_t);
+       fr_dict_enum_t const    *type_enum;
+       CONF_PAIR               *cp = cf_item_to_pair(ci);
+       char const              *value = cf_pair_value(cp);
+
+       *((char const **) out) = value;
+
+       inst->dict = virtual_server_namespace_by_ci(ci);
+       if (!inst->dict) {
+               cf_log_err(ci, "Please define 'namespace' in this virtual server");
+               return -1;
+       }
+
+       inst->attr_packet_type = fr_dict_attr_by_name(NULL, fr_dict_root(inst->dict), "Packet-Type");
+       if (!inst->attr_packet_type) {
+               cf_log_err(ci, "Failed to find 'Packet-Type' attribute");
+               return -1;
+       }
+
+       if (!value) {
+               cf_log_err(ci, "No value given for 'type'");
+               return -1;
+       }
+
+       type_enum = fr_dict_enum_by_name(inst->attr_packet_type, value, -1);
+       if (!type_enum) {
+               cf_log_err(ci, "Invalid type \"%s\"", value);
+               return -1;
+       }
+
+       inst->code = type_enum->value->vb_uint32;
+       return 0;
+}
+
+/** Wrapper around dl_instance
+ *
+ * @param[in] ctx      to allocate data in (instance of proto_load).
+ * @param[out] out     Where to write a dl_module_inst_t containing the module handle and instance.
+ * @param[in] parent   Base structure address.
+ * @param[in] ci       #CONF_PAIR specifying the name of the type module.
+ * @param[in] rule     unused.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
+                          CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
+{
+       char const      *name = cf_pair_value(cf_item_to_pair(ci));
+       dl_module_inst_t        *parent_inst;
+       CONF_SECTION    *listen_cs = cf_item_to_section(cf_parent(ci));
+       CONF_SECTION    *transport_cs;
+
+       transport_cs = cf_section_find(listen_cs, name, NULL);
+
+       /*
+        *      Allocate an empty section if one doesn't exist
+        *      this is so defaults get parsed.
+        */
+       if (!transport_cs) transport_cs = cf_section_alloc(listen_cs, listen_cs, name, NULL);
+
+       parent_inst = cf_data_value(cf_data_find(listen_cs, dl_module_inst_t, "proto_load"));
+       fr_assert(parent_inst);
+
+       return dl_module_instance(ctx, out, transport_cs, parent_inst, name, DL_MODULE_TYPE_SUBMODULE);
+}
+
+/** Decode the packet, and set the request->process function
+ *
+ */
+static int mod_decode(void const *instance, request_t *request, uint8_t *const data, size_t data_len)
+{
+       proto_load_t const      *inst = talloc_get_type_abort_const(instance, proto_load_t);
+
+       request->dict = inst->dict;
+       request->packet->code = inst->code;
+
+       /*
+        *      Set default addresses
+        */
+       request->packet->socket.fd = -1;
+       request->packet->socket.inet.src_ipaddr.af = AF_INET;
+       request->packet->socket.inet.src_ipaddr.addr.v4.s_addr = htonl(INADDR_NONE);
+       request->packet->socket.inet.dst_ipaddr = request->packet->socket.inet.src_ipaddr;
+
+       request->reply->socket.inet.src_ipaddr = request->packet->socket.inet.src_ipaddr;
+       request->reply->socket.inet.dst_ipaddr = request->packet->socket.inet.src_ipaddr;
+
+       /*
+        *      The app_io is responsible for decoding all of the data.
+        */
+       return inst->io.app_io->decode(inst->io.app_io_instance, request, data, data_len);
+}
+
+/*
+ *     We don't need to encode any of the replies.  We just go "yeah, it's fine".
+ */
+static ssize_t mod_encode(UNUSED void const *instance, request_t *request, uint8_t *buffer, size_t buffer_len)
+{
+       if (buffer_len < 1) return -1;
+
+       *buffer = request->reply->code;
+       return 1;
+}
+
+/** Open listen sockets/connect to external event source
+ *
+ * @param[in] instance Ctx data for this application.
+ * @param[in] sc       to add our file descriptor to.
+ * @param[in] conf     Listen section parsed to give us isntance.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static int mod_open(void *instance, fr_schedule_t *sc, UNUSED CONF_SECTION *conf)
+{
+       proto_load_t    *inst = talloc_get_type_abort(instance, proto_load_t);
+
+       inst->io.app = &proto_load;
+       inst->io.app_instance = instance;
+
+       /*
+        *      io.app_io should already be set
+        */
+       return fr_master_io_listen(inst, &inst->io, sc,
+                                  inst->max_packet_size, inst->num_messages);
+}
+
+
+/** Instantiate the application
+ *
+ * Instantiate I/O and type submodules.
+ *
+ * @param[in] instance Ctx data for this application.
+ * @param[in] conf     Listen section parsed to give us isntance.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static int mod_instantiate(void *instance, CONF_SECTION *conf)
+{
+       proto_load_t            *inst = talloc_get_type_abort(instance, proto_load_t);
+
+       fr_assert(inst->io.submodule);
+
+       /*
+        *      These configuration items are not printed by default,
+        *      because normal people shouldn't be touching them.
+        */
+       if (!inst->max_packet_size && inst->io.app_io) inst->max_packet_size = inst->io.app_io->default_message_size;
+
+       if (!inst->num_messages) inst->num_messages = 256;
+
+       FR_INTEGER_BOUND_CHECK("num_messages", inst->num_messages, >=, 32);
+       FR_INTEGER_BOUND_CHECK("num_messages", inst->num_messages, <=, 65535);
+
+       FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, 1024);
+       FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, 65535);
+
+       /*
+        *      Instantiate the master io submodule
+        */
+       return fr_master_app_io.instantiate(&inst->io, conf);
+}
+
+/** Bootstrap the application
+ *
+ * Bootstrap I/O and type submodules.
+ *
+ * @param[in] instance Ctx data for this application.
+ * @param[in] conf     Listen section parsed to give us instance.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static int mod_bootstrap(void *instance, CONF_SECTION *conf)
+{
+       proto_load_t            *inst = talloc_get_type_abort(instance, proto_load_t);
+
+       /*
+        *      Ensure that the server CONF_SECTION is always set.
+        */
+       inst->io.server_cs = cf_item_to_section(cf_parent(conf));
+
+       /*
+        *      No IO module, it's an empty listener.
+        */
+       if (!inst->io.submodule) {
+               cf_log_err(conf, "The load generator MUST have a 'transport = ...' set");
+               return -1;
+       }
+
+       /*
+        *      Tell the master handler about the main protocol instance.
+        */
+       inst->io.app = &proto_load;
+       inst->io.app_instance = inst;
+
+       /*
+        *      The listener is inside of a virtual server.
+        */
+       inst->server_cs = cf_item_to_section(cf_parent(conf));
+       inst->cs = conf;
+       inst->self = &proto_load;
+
+       virtual_server_dict_set(inst->server_cs, inst->dict, false);
+
+       /*
+        *      We will need this for dynamic clients and connected sockets.
+        */
+       inst->io.dl_inst = dl_module_instance_by_data(inst);
+       fr_assert(inst != NULL);
+
+       /*
+        *      Bootstrap the master IO handler.
+        */
+       return fr_master_app_io.bootstrap(&inst->io, conf);
+}
+
+
+fr_app_t proto_load = {
+       .magic                  = RLM_MODULE_INIT,
+       .name                   = "load",
+       .config                 = proto_load_config,
+       .inst_size              = sizeof(proto_load_t),
+
+       .bootstrap              = mod_bootstrap,
+       .instantiate            = mod_instantiate,
+       .open                   = mod_open,
+       .decode                 = mod_decode,
+       .encode                 = mod_encode,
+};
diff --git a/src/listen/load/proto_load.mk b/src/listen/load/proto_load.mk
new file mode 100644 (file)
index 0000000..0c52e91
--- /dev/null
@@ -0,0 +1,9 @@
+TARGETNAME     := proto_load
+
+ifneq "$(TARGETNAME)" ""
+TARGET         := $(TARGETNAME).a
+endif
+
+SOURCES                := proto_load.c
+
+TGT_PREREQS    := $(LIBFREERADIUS_SERVER) libfreeradius-io.a
index 8a604f3c8da9a3b65c5aa8da0aab569a20c11ad7..63a3ab9924827f7d8146ae59743a1b82203f8292 100644 (file)
@@ -17,7 +17,7 @@
 /**
  * $Id$
  * @file proto_load_step.c
- * @brief RADIUS load generator
+ * @brief Generic protocol load generator
  *
  * @copyright 2019 The FreeRADIUS server project.
  * @copyright 2019 Network RADIUS SARL (legal@networkradius.com)
@@ -34,6 +34,8 @@
 #include <freeradius-devel/io/load.h>
 #include <freeradius-devel/util/debug.h>
 
+#include "proto_load.h"
+
 extern fr_app_io_t proto_load_step;
 
 typedef struct proto_load_step_s proto_load_step_t;
@@ -60,16 +62,14 @@ typedef struct {
 } proto_load_step_thread_t;
 
 struct proto_load_step_s {
-       CONF_SECTION                    *cs;                    //!< our configuration
+       proto_load_t                    *parent;
 
-       fr_dict_t const                 *dict;                  //!< dictionary to use
-       fr_dict_attr_t const            *attr_packet_type;      //!< packet type in the dictionary
+       CONF_SECTION                    *cs;                    //!< our configuration
 
-       char const                      *filename;              //!< where to read input packets from
-       uint8_t                         *packet;                //!< encoded packet read from the file
-       size_t                          packet_len;             //!< length of packet
+       char const                      *filename;              //!< where to read input packet from
+       fr_pair_list_t                  pair_list;              //!< for input packet
 
-       uint32_t                        max_packet_size;        //!< for message ring buffer.
+       int                             code;
        uint32_t                        max_attributes;         //!< Limit maximum decodable attributes
 
        RADCLIENT                       *client;                //!< static client
@@ -81,10 +81,9 @@ struct proto_load_step_s {
 
 
 static const CONF_PARSER load_listen_config[] = {
-       { FR_CONF_OFFSET("filename", FR_TYPE_FILE_INPUT, proto_load_step_t, filename) },
+       { FR_CONF_OFFSET("filename", FR_TYPE_FILE_INPUT | FR_TYPE_REQUIRED | FR_TYPE_NOT_EMPTY, proto_load_step_t, filename) },
        { FR_CONF_OFFSET("csv", FR_TYPE_STRING, proto_load_step_t, csv) },
 
-       { FR_CONF_OFFSET("max_packet_size", FR_TYPE_UINT32, proto_load_step_t, max_packet_size), .dflt = "4096" } ,
        { FR_CONF_OFFSET("max_attributes", FR_TYPE_UINT32, proto_load_step_t, max_attributes), .dflt = STRINGIFY(RADIUS_MAX_ATTRIBUTES) } ,
 
        { FR_CONF_OFFSET("start_pps", FR_TYPE_UINT32, proto_load_step_t, load.start_pps) },
@@ -101,12 +100,10 @@ static const CONF_PARSER load_listen_config[] = {
 
 static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t *recv_time_p, uint8_t *buffer, size_t buffer_len, size_t *leftover, UNUSED uint32_t *priority, UNUSED bool *is_dup)
 {
-       proto_load_step_t const       *inst = talloc_get_type_abort_const(li->app_io_instance, proto_load_step_t);
+       proto_load_step_t const         *inst = talloc_get_type_abort_const(li->app_io_instance, proto_load_step_t);
        proto_load_step_thread_t        *thread = talloc_get_type_abort(li->thread_instance, proto_load_step_thread_t);
        fr_io_address_t                 *address, **address_p;
 
-       size_t                          packet_len;
-
        if (thread->done) return -1;
 
        *leftover = 0;          /* always for load generation */
@@ -125,32 +122,20 @@ static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t *recv_time
 
        *recv_time_p = thread->recv_time;
 
-       if (buffer_len < inst->packet_len) {
+       if (buffer_len < 1) {
                DEBUG2("proto_load_step read buffer is too small for input packet");
                return 0;
        }
 
-       memcpy(buffer, inst->packet, inst->packet_len);
-       packet_len = inst->packet_len;
-
-       // @todo - try for some variation in the packet
-
-       /*
-        *      The packet is always OK for RADIUS.
-        */
-
-       /*
-        *      proto_radius sets the priority
-        */
+       buffer[0] = 0;
 
        /*
         *      Print out what we received.
         */
-       DEBUG2("proto_load_step - Received %s ID %d length %d %s",
-              fr_packet_codes[buffer[0]], buffer[1],
-              (int) packet_len, thread->name);
+       DEBUG2("proto_load_step - reading packet for %s",
+              thread->name);
 
-       return packet_len;
+       return 1;
 }
 
 
@@ -186,12 +171,12 @@ static ssize_t mod_write(fr_listen_t *li, UNUSED void *packet_ctx, fr_time_t req
 }
 
 
-/** Open a load listener for RADIUS
+/** Open a load listener
  *
  */
 static int mod_open(fr_listen_t *li)
 {
-       proto_load_step_t const       *inst = talloc_get_type_abort_const(li->app_io_instance, proto_load_step_t);
+       proto_load_step_t const         *inst = talloc_get_type_abort_const(li->app_io_instance, proto_load_step_t);
        proto_load_step_thread_t        *thread = talloc_get_type_abort(li->thread_instance, proto_load_step_thread_t);
 
        fr_ipaddr_t                     ipaddr;
@@ -216,7 +201,7 @@ static int mod_open(fr_listen_t *li)
 }
 
 
-/** Open a load listener for RADIUS
+/** Open a load listener
  *
  */
 static int mod_close(fr_listen_t *li)
@@ -265,6 +250,53 @@ static void write_stats(fr_event_list_t *el, fr_time_t now, void *uctx)
 }
 
 
+/** Decode the packet
+ *
+ */
+static int mod_decode(void const *instance, request_t *request, UNUSED uint8_t *const data, UNUSED size_t data_len)
+{
+       proto_load_step_t const *inst = talloc_get_type_abort_const(instance, proto_load_step_t);
+       fr_io_track_t const     *track = talloc_get_type_abort_const(request->async->packet_ctx, fr_io_track_t);
+       fr_io_address_t const   *address = track->address;
+
+       /*
+        *      Set the request dictionary so that we can do
+        *      generic->protocol attribute conversions as
+        *      the request runs through the server.
+        */
+       request->dict = inst->parent->dict;
+
+       /*
+        *      Hacks for now until we have a lower-level decode routine.
+        */
+       if (inst->code) request->packet->code = inst->code;
+       request->packet->id = fr_rand() & 0xff;
+       request->reply->id = request->packet->id;
+       memset(request->packet->vector, 0, sizeof(request->packet->vector));
+
+       request->packet->data = talloc_zero_array(request->packet, uint8_t, 1);
+       request->packet->data_len = 1;
+
+       /*
+        *      Note that we don't set a limit on max_attributes here.
+        *      That MUST be set and checked in the underlying
+        *      transport, via a call to fr_radius_ok().
+        */
+       (void) fr_pair_list_copy(request->request_ctx, &request->request_pairs, &inst->pair_list);
+
+       /*
+        *      Set the rest of the fields.
+        */
+       request->client = UNCONST(RADCLIENT *, address->radclient);
+
+       request->packet->socket = address->socket;
+       fr_socket_addr_swap(&request->reply->socket, &address->socket);
+
+       REQUEST_VERIFY(request);
+
+       return 0;
+}
+
 /** Set the event list for a new socket
  *
  * @param[in] li the listener
@@ -315,11 +347,19 @@ static char const *mod_name(fr_listen_t *li)
 static int mod_bootstrap(void *instance, CONF_SECTION *cs)
 {
        proto_load_step_t       *inst = talloc_get_type_abort(instance, proto_load_step_t);
+       dl_module_inst_t const  *dl_inst;
 
-       inst->cs = cs;
+       /*
+        *      Find the dl_module_inst_t holding our instance data
+        *      so we can find out what the parent of our instance
+        *      was.
+        */
+       dl_inst = dl_module_instance_by_data(instance);
+       fr_assert(dl_inst);
 
-       FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, 20);
-       FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, 65536);
+       inst->parent = talloc_get_type_abort(dl_inst->parent->data, proto_load_t);
+
+       inst->cs = cs;
 
        FR_INTEGER_BOUND_CHECK("start_pps", inst->load.start_pps, >=, 10);
        FR_INTEGER_BOUND_CHECK("start_pps", inst->load.start_pps, <, 400000);
@@ -355,14 +395,9 @@ static int mod_instantiate(void *instance, CONF_SECTION *cs)
 {
        proto_load_step_t       *inst = talloc_get_type_abort(instance, proto_load_step_t);
        RADCLIENT               *client;
-
-       bool                    done;
        fr_pair_t               *vp;
-       fr_pair_list_t          vps;
-//     ssize_t                 packet_len;
-       int                     code = -1;
 
-       fr_pair_list_init(&vps);
+       fr_pair_list_init(&inst->pair_list);
        inst->client = client = talloc_zero(inst, RADCLIENT);
        if (!inst->client) return 0;
 
@@ -376,6 +411,7 @@ static int mod_instantiate(void *instance, CONF_SECTION *cs)
 
        if (inst->filename) {
                FILE *fp;
+               bool done = false;
 
                fp = fopen(inst->filename, "r");
                if (!fp) {
@@ -384,7 +420,7 @@ static int mod_instantiate(void *instance, CONF_SECTION *cs)
                        return -1;
                }
 
-               if (fr_pair_list_afrom_file(inst, inst->dict, &vps, fp, &done) < 0) {
+               if (fr_pair_list_afrom_file(inst, inst->parent->dict, &inst->pair_list, fp, &done) < 0) {
                        cf_log_perr(cs, "Failed reading %s", inst->filename);
                        fclose(fp);
                        return -1;
@@ -393,28 +429,8 @@ static int mod_instantiate(void *instance, CONF_SECTION *cs)
                fclose(fp);
        }
 
-       MEM(inst->packet = talloc_zero_array(inst, uint8_t, inst->max_packet_size));
-
-       vp = fr_pair_find_by_da(&vps, inst->attr_packet_type, 0);
-       if (vp) code = vp->vp_uint32;
-
-       return -1;              /* not done yet! */
-
-#if 0
-       /*
-        *      Encode the packet.
-        */
-       packet_len = fr_radius_encode(inst->packet, inst->max_packet_size, NULL,
-                                     client->secret, talloc_array_length(client->secret),
-                                     code, 0, &vps);
-       if (packet_len <= 0) {
-               cf_log_perr(cs, "Failed encoding packet from %s",
-                           inst->filename);
-               return -1;
-       }
-
-       inst->packet_len = packet_len;
-#endif
+       vp = fr_pair_find_by_da(&inst->pair_list, inst->parent->attr_packet_type, 0);
+       if (vp) inst->code = vp->vp_uint32;
 
        return 0;
 }
@@ -438,4 +454,6 @@ fr_app_io_t proto_load_step = {
        .event_list_set         = mod_event_list_set,
        .client_find            = mod_client_find,
        .get_name               = mod_name,
+
+       .decode                 = mod_decode,
 };