]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
first draft of renewed "proto_arp"
authorAlan T. DeKok <aland@freeradius.org>
Mon, 4 May 2020 18:41:02 +0000 (14:41 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 4 May 2020 18:41:02 +0000 (14:41 -0400)
It still needs proto_arp_ethernet to open the socket.
and proto_arp_process to process the packets

src/modules/proto_arp/proto_arp.c

index 097a9de14e38d1103959508c9d49b09be7c0c95e..fdea33bb851e896aee57e0f527ef13c63ae689c4 100644 (file)
@@ -1,8 +1,4 @@
 /*
- * arp.c       ARP processing.
- *
- * 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
  *   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 2013 Network RADIUS SARL (legal@networkradius.com)
  */
 
+/**
+ * $Id$
+ * @file proto_arp.c
+ * @brief RADIUS master protocol handler.
+ *
+ * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ * @copyright 2016 Alan DeKok (aland@freeradius.org)
+ */
 #include <freeradius-devel/server/base.h>
-#include <freeradius-devel/server/protocol.h>
+#include <freeradius-devel/arp/arp.h>
+#include <freeradius-devel/io/listen.h>
 #include <freeradius-devel/server/module.h>
 #include <freeradius-devel/unlang/base.h>
 #include <freeradius-devel/util/debug.h>
-#include <freeradius-devel/util/pcap.h>
-#include <net/if_arp.h>
 
-typedef struct {
-       listen_socket_t lsock;
-       uint64_t        counter;
-       RADCLIENT       client;
-} arp_socket_t;
+extern fr_app_t proto_arp;
 
-/*
- *     ARP for ethernet && IPv4.
- */
 typedef struct {
-       uint16_t        htype;                  //!< Format of hardware address.
-       uint16_t        ptype;                  //!< Format of protocol address.
-       uint8_t         hlen;                   //!< Length of hardware address.
-       uint8_t         plen;                   //!< Length of protocol address.
-       uint8_t         op;                     //!< 1 - Request, 2 - Reply.
-       uint8_t         sha[ETHER_ADDR_LEN];    //!< sender hardware address.
-       uint8_t         spa[4];                 //!< Sender protocol address.
-       uint8_t         tha[ETHER_ADDR_LEN];    //!< Target hardware address.
-       uint8_t         tpa[4];                 //!< Target protocol address.
-} arp_over_ether_t;
+       CONF_SECTION                    *server_cs;                     //!< server CS for this listener
+       CONF_SECTION                    *cs;                            //!< my configuration
 
-static fr_dict_t const *dict_arp;
+       dl_module_inst_t                *io_submodule;                  //!< As provided by the transport_parse
+                                                                       ///< callback.  Broken out into the
+                                                                       ///< app_io_* fields below for convenience.
 
-extern fr_dict_autoload_t proto_arp_dict[];
-fr_dict_autoload_t proto_arp_dict[] = {
-       { .out = &dict_arp, .proto = "arp" },
-       { NULL }
-};
+       CONF_SECTION                    *app_io_conf;                   //!< for the APP IO
+       fr_app_io_t const               *app_io;                        //!< Easy access to the app_io handle.
+       void                            *app_io_instance;               //!< Easy access to the app_io instance.
 
-static int request_receive(UNUSED TALLOC_CTX *ctx, UNUSED rad_listen_t *listener, UNUSED RADIUS_PACKET *packet,
-                   UNUSED RADCLIENT *client, UNUSED RAD_REQUEST_FUNP fun)
-{
-       return 0;
-}
+       dl_module_inst_t                *app_process;                   //!< app_process pointer
+       void                            *process_instance;              //!< app_process instance
 
-static rlm_rcode_t arp_process(REQUEST *request)
-{
-       CONF_SECTION *unlang;
+       fr_dict_t                       *dict;                          //!< root dictionary
 
-       request->server_cs = request->listener->server_cs;
-       unlang = cf_section_find(request->server_cs, "arp", NULL);
+       char const                      *interface;                     //!< interface name
+       uint32_t                        num_messages;                   //!< for message ring buffer
+       uint32_t                        priority;                       //!< for packet processing, larger == higher
 
-       request->component = "arp";
+       fr_schedule_t                   *sc;                            //!< the scheduler, where we insert new readers
 
-       return unlang_interpret_section(request, unlang, RLM_MODULE_NOOP);
-}
+       fr_listen_t                     *listen;                        //!< The listener structure which describes
+                                                                       //!< the I/O path.
+} proto_arp_t;
 
 
-/*
- *     Check if an incoming request is "ok"
+/** How to parse an ARP listen section
  *
- *     It takes packets, not requests.  It sees if the packet looks
- *     OK.  If so, it does a number of sanity checks on it.
  */
-static int arp_socket_recv(rad_listen_t *listener)
-{
-       int ret;
-       arp_socket_t *sock = listener->data;
-       pcap_t *handle = sock->lsock.pcap->handle;
+static CONF_PARSER const proto_arp_config[] = {
+       { FR_CONF_OFFSET("interface", FR_TYPE_STRING | FR_TYPE_NOT_EMPTY, proto_arp_t,
+                         interface), .dflt = "eth0" },
 
-       const uint8_t *data;
-       struct pcap_pkthdr *header;
-       ssize_t link_len;
+       /*
+        *      @todo - allow for pcap filter
+        */
 
-       arp_over_ether_t const *arp;
-       RADIUS_PACKET *packet;
+       { FR_CONF_OFFSET("num_messages", FR_TYPE_UINT32, proto_arp_t, num_messages) } ,
 
-       ret = pcap_next_ex(handle, &header, &data);
-       if (ret == 0) {
-               DEBUG("No packet retrieved from pcap.");
-               return 0; /* no packet */
-       }
+       CONF_PARSER_TERMINATOR
+};
 
-       if (ret < 0) {
-               ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle));
-               return 0;
-       }
+static fr_dict_t const *dict_arp;
 
-       link_len = fr_pcap_link_layer_offset(data, header->caplen, sock->lsock.pcap->link_layer);
-       if (link_len < 0) {
-               PERROR("Failed determining link layer header offset");
-               return 0;
-       }
+extern fr_dict_autoload_t proto_arp_dict[];
+fr_dict_autoload_t proto_arp_dict[] = {
+       { .out = &dict_arp, .proto = "arp" },
+       { NULL }
+};
 
-       /*
-        *      Silently ignore it if it's too small to be ARP.
-        *
-        *      This can happen when pcap gets overloaded and starts truncating packets.
-        */
-       if (header->caplen < (link_len + sizeof(*arp))) {
-               ERROR("Packet too small, we require at least %zu bytes, got %i bytes",
-                     link_len + sizeof(*arp), header->caplen);
-               return 0;
-       }
+#if 0
+static fr_dict_attr_t const *attr_packet_type;
 
-       data += link_len;
-       arp = (arp_over_ether_t const *) data;
+extern fr_dict_attr_autoload_t proto_arp_dict_attr[];
+fr_dict_attr_autoload_t proto_arp_dict_attr[] = {
+       { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_arp},
+       { NULL }
+};
+#endif
 
-       if (ntohs(arp->htype) != ARPHRD_ETHER) return 0;
+/** Decode the packet
+ *
+ */
+static int mod_decode(UNUSED void const *instance, REQUEST *request, uint8_t *const data, size_t data_len)
+{
+//     proto_arp_t const       *inst = talloc_get_type_abort_const(instance, proto_arp_t);
+       fr_arp_packet_t const   *arp;
 
-       if (ntohs(arp->ptype) != 0x0800) return 0;
+       /*
+        *      Set the request dictionary so that we can do
+        *      generic->protocol attribute conversions as
+        *      the request runs through the server.
+        */
+       request->dict = dict_arp;
 
-       if (arp->hlen != ETHER_ADDR_LEN) return 0; /* FIXME: malformed error */
+       if (fr_arp_decode(request->packet, data, data_len, &request->packet->vps) < 0) {
+               RPEDEBUG("Failed decoding packet");
+               return -1;
+       }
 
-       if (arp->plen != 4) return 0; /* FIXME: malformed packet error */
+       arp = (fr_arp_packet_t const *) data;
+       request->packet->code = arp->op;
 
-       packet = talloc_zero(NULL, RADIUS_PACKET);
-       if (!packet) return 0;
+       request->packet->data = talloc_memdup(request->packet, data, data_len);
+       request->packet->data_len = data_len;
 
-       packet->dst_port = 1;   /* so it's not a "fake" request */
-       packet->data_len = header->caplen - link_len;
-       packet->data = talloc_memdup(packet, arp, packet->data_len);
-       talloc_set_type(packet->data, uint8_t);
+       request->config = main_config;
+       REQUEST_VERIFY(request);
 
-       DEBUG("ARP received on interface %s", sock->lsock.interface);
+       if (RDEBUG_ENABLED) {
+               RDEBUG("Received %d via socket %s",
+                      request->packet->code,
+                      request->async->listen->name);
 
-       if (!request_receive(NULL, listener, packet, &sock->client, arp_process)) {
-               fr_radius_packet_free(&packet);
-               return 0;
+               log_request_pair_list(L_DBG_LVL_1, request, request->packet->vps, "");
        }
 
-       return 1;
-}
-
-static int arp_socket_send(UNUSED rad_listen_t *listener, UNUSED REQUEST *request)
-{
        return 0;
 }
 
-
-static int arp_socket_encode(UNUSED rad_listen_t *listener, UNUSED REQUEST *request)
+static ssize_t mod_encode(UNUSED void const *instance, REQUEST *request, uint8_t *buffer, size_t buffer_len)
 {
-       return 0;
-}
-
+       ssize_t                 slen;
+//     proto_arp_t const       *inst = talloc_get_type_abort_const(instance, proto_arp_t);
 
-typedef struct {
-       char const      *name;
-       size_t          len;
-} arp_decode_t;
-
-static const arp_decode_t header_names[] = {
-       { "ARP-Hardware-Format",                2 },
-       { "ARP-Protocol-Format",                2 },
-       { "ARP-Hardware-Address-Length",        1 },
-       { "ARP-Protocol-Address-Length",        1 },
-       { "ARP-Operation",                      2 },
-       { "ARP-Sender-Hardware-Address",        6 },
-       { "ARP-Sender-Protocol-Address",        4 },
-       { "ARP-Target-Hardware-Address",        6 },
-       { "ARP-Target-Protocol-Address",        4 },
-
-       { NULL, 0 }
-};
+       /*
+        *      The packet timed out.  Tell the network side that the packet is dead.
+        */
+       if (buffer_len == 1) {
+               *buffer = true;
+               return 1;
+       }
 
-static int arp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
-{
-       int                     i;
-       uint8_t const           *p = request->packet->data, *end = p + request->packet->data_len;
-       fr_cursor_t             cursor;
+       /*
+        *      "Do not respond"
+        */
+       if ((request->reply->code == FR_CODE_DO_NOT_RESPOND) ||
+           (request->reply->code == 0) || (request->reply->code >= FR_ARP_MAX_PACKET_CODE)) {
+               *buffer = false;
+               return 1;
+       }
 
-       fr_cursor_init(&cursor, &request->packet->vps);
+       slen = fr_arp_encode(buffer, buffer_len, request->reply->vps);
+       if (slen <= 0) {
+               RPEDEBUG("Failed encoding reply");
+               return -1;
+       }
 
-       for (i = 0; header_names[i].name != NULL; i++) {
-               ssize_t                 ret;
-               size_t                  len;
-               fr_dict_attr_t const    *da;
-               VALUE_PAIR              *vp = NULL;
 
-               len = header_names[i].len;
+       if (RDEBUG_ENABLED) {
+               RDEBUG("Sending %d via socket %s",
+                      request->reply->code,
+                      request->async->listen->name);
 
-               if (!fr_cond_assert((size_t)(end - p) < len)) return -1; /* Should have been detected in socket_recv */
+               log_request_pair_list(L_DBG_LVL_1, request, request->reply->vps, "");
+       }
 
-               da = fr_dict_attr_by_name(dict_arp, header_names[i].name);
-               if (!da) return 0;
+       return slen;
+}
 
-               MEM(vp = fr_pair_afrom_da(request->packet, da));
-               ret = fr_value_box_from_network(vp, &vp->data, da->type, da, p, len, true);
-               if (ret <= 0) {
-                       fr_pair_to_unknown(vp);
-                       fr_pair_value_memcpy(vp, p, len, true);
-               }
+static void mod_entry_point_set(void const *instance, REQUEST *request)
+{
+       proto_arp_t const       *inst = talloc_get_type_abort_const(instance, proto_arp_t);
+       fr_app_worker_t const   *app_process;
 
-               DEBUG2("&%pP", vp);
-               fr_cursor_insert(&cursor, vp);
-       }
+       /*
+        *      Only one "process" function: proto_arp_process.
+        */
+       app_process = (fr_app_worker_t const *) inst->app_process->module->common;
 
-       return 0;
+       request->server_cs = inst->server_cs;
+       request->async->process = app_process->entry_point;
+       request->async->process_inst = inst->process_instance;
 }
 
-/** Build PCAP filter string to pass to libpcap
- * Will be called by init_pcap.
- *
- * @param this listen section (not used)
- * @return PCAP filter string
+
+/*
+ *     @todo - do actual priority setting
  */
-static const char * arp_pcap_filter_builder(UNUSED rad_listen_t *this)
+static int mod_priority_set(UNUSED void const *instance, UNUSED uint8_t const *buffer, UNUSED size_t buflen)
 {
-       return "arp";
+       return PRIORITY_NORMAL;
 }
 
-static int arp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
+/** 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 instance.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static int mod_open(void *instance, fr_schedule_t *sc, UNUSED CONF_SECTION *conf)
 {
-       int rcode;
-       arp_socket_t    *sock = this->data;
-       RADCLIENT       *client;
-       CONF_PAIR       *cp = NULL;
+       fr_listen_t     *li;
+       proto_arp_t     *inst = talloc_get_type_abort(instance, proto_arp_t);
 
-       sock->lsock.pcap_filter_builder = arp_pcap_filter_builder;
-       sock->lsock.pcap_type = PCAP_INTERFACE_IN;
+       /*
+        *      Build the #fr_listen_t.  This describes the complete
+        *      path, data takes from the socket to the decoder and
+        *      back again.
+        */
+       li = talloc_zero(inst, fr_listen_t);
+       talloc_set_destructor(li, fr_io_listen_free);
 
-       this->nodup = true;     /* don't check for duplicates */
+       li->app = &proto_arp;
+       li->app_instance = instance;
+       li->server_cs = inst->server_cs;
 
-       /* Add ipaddress to conf section as it is not required by ARP config */
-       cp = cf_pair_alloc(cs, "ipv4addr", "0.0.0.0", T_OP_SET, T_BARE_WORD, T_BARE_WORD);
-       cf_pair_add(cs, cp);
+       /*
+        *      Set configurable parameters for message ring buffer.
+        */
+       li->default_message_size = FR_ARP_PACKET_SIZE;
+       li->num_messages = inst->num_messages;
 
-       rcode = common_socket_parse(cs, this);
-       if (rcode != 0) return rcode;
+       li->app_io = inst->app_io;
+       li->app_io_instance = inst->app_io_instance;
+       li->thread_instance = talloc_zero_array(NULL, uint8_t, li->app_io->thread_inst_size);
+       talloc_set_name(li->thread_instance, "proto_%s_thread_t", inst->app_io->name);
 
-       if (!sock->lsock.interface) {
-               cf_log_err(cs, "'interface' is required for arp");
+       /*
+        *      Open the raw socket.
+        */
+       if (inst->app_io->open(li) < 0) {
+               talloc_free(li);
                return -1;
        }
 
+       li->name = talloc_asprintf(li, "arp on interface %s", inst->interface);
+
        /*
-        *      The server core is still RADIUS, and needs a client.
-        *      So we fake one here.
+        *      Watch the directory for changes.
         */
-       client = &sock->client;
-       memset(client, 0, sizeof(*client));
-       client->ipaddr.af = AF_INET;
-       client->ipaddr.addr.v4.s_addr = INADDR_NONE;
-       client->ipaddr.prefix = 0;
-       client->longname = client->shortname = sock->lsock.interface;
-       client->secret = client->shortname;
-       client->nas_type = talloc_typed_strdup(sock, "none");
+       if (!fr_schedule_listen_add(sc, li)) {
+               talloc_free(li);
+               return -1;
+       }
+
+       inst->listen = li;      /* Probably won't need it, but doesn't hurt */
+       inst->sc = sc;
 
        return 0;
 }
 
-static int arp_socket_print(const rad_listen_t *this, char *buffer, size_t bufsize)
+/** 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 instance.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+static int mod_instantiate(void *instance, UNUSED CONF_SECTION *conf)
 {
-       arp_socket_t *sock = this->data;
+       proto_arp_t             *inst = talloc_get_type_abort(instance, proto_arp_t);
+       fr_app_worker_t const   *app_process;
 
-       snprintf(buffer, bufsize, "arp interface %s", sock->lsock.interface);
+       /*
+        *      Instantiate the I/O module. But DON'T instantiate the
+        *      work submodule.  We leave that until later.
+        */
+       if (inst->app_io->instantiate &&
+           (inst->app_io->instantiate(inst->app_io_instance,
+                                      inst->app_io_conf) < 0)) {
+               cf_log_err(conf, "Instantiation failed for \"%s\"", inst->app_io->name);
+               return -1;
+       }
 
-       return 1;
-}
+       if (!inst->num_messages) inst->num_messages = 256;
 
-/*
- *     If there's no "arp" section, we can't bootstrap anything.
- */
-static int arp_socket_bootstrap(CONF_SECTION *server_cs, UNUSED CONF_SECTION *listen_cs)
-{
-       CONF_SECTION *cs;
+       FR_INTEGER_BOUND_CHECK("num_messages", inst->num_messages, >=, 32);
+       FR_INTEGER_BOUND_CHECK("num_messages", inst->num_messages, <=, 65535);
 
-       cs = cf_section_find(server_cs, "arp", NULL);
-       if (!cs) {
-               cf_log_err(server_cs, "No 'arp' sub-section found");
+       /*
+        *      Instantiate the ARP processor
+        */
+       app_process = (fr_app_worker_t const *)inst->app_process->module->common;
+       if (app_process->instantiate && (app_process->instantiate(inst->app_process->data,
+                                                                 inst->app_process->conf) < 0)) {
+               cf_log_err(conf, "Instantiation failed for \"%s\"", app_process->name);
                return -1;
        }
 
-       return 0;
+       return -1;
 }
 
-/*
- *     Ensure that the "arp" section is compiled.
+
+/** 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 arp_socket_compile(CONF_SECTION *server_cs, UNUSED CONF_SECTION *listen_cs)
+static int mod_bootstrap(void *instance, CONF_SECTION *conf)
 {
-       CONF_SECTION *cs;
+       proto_arp_t             *inst = talloc_get_type_abort(instance, proto_arp_t);
+       dl_module_inst_t        *parent_inst;
+       fr_app_worker_t const   *app_process;
+
+       /*
+        *      Ensure that the server CONF_SECTION is always set.
+        */
+       inst->server_cs = cf_item_to_section(cf_parent(conf));
+
+       parent_inst = cf_data_value(cf_data_find(inst->cs, dl_module_inst_t, "proto_arp"));
+       fr_assert(parent_inst);
+
+       if (dl_module_instance(inst->cs, &inst->io_submodule, inst->cs,
+                              parent_inst, "ethernet", DL_MODULE_TYPE_SUBMODULE) < 0) {
+               cf_log_perr(inst->cs, "Failed to load proto_arp_ethernet");
+               return -1;
+       }
 
-       cs = cf_section_find(server_cs, "arp", NULL);
-       if (!cs) {
-               cf_log_err(server_cs, "No 'arp' sub-section found");
+       /*
+        *      Parent dl_module_inst_t added in virtual_servers.c (listen_parse)
+        */
+       if (dl_module_instance(inst->cs, &inst->app_process, inst->cs,
+                              parent_inst, "process", DL_MODULE_TYPE_SUBMODULE) < 0) {
+               cf_log_perr(inst->cs, "Failed to load proto_arp_process");
                return -1;
        }
 
-       cf_log_debug(cs, "Loading arp {...}");
+       /*
+        *      Bootstrap the I/O module
+        */
+       inst->app_io = (fr_app_io_t const *) inst->io_submodule->module->common;
+       inst->app_io_instance = inst->io_submodule->data;
+       inst->app_io_conf = conf;
+
+       if (inst->app_io->bootstrap && (inst->app_io->bootstrap(inst->app_io_instance,
+                                                               inst->app_io_conf) < 0)) {
+               cf_log_err(inst->app_io_conf, "Bootstrap failed for \"%s\"", inst->app_io->name);
+               return -1;
+       }
 
-       if (unlang_compile(cs, MOD_POST_AUTH, NULL, NULL) < 0) {
-               cf_log_err(cs, "Failed compiling 'arp' section");
+       /*
+        *      Bootstrap the ARP processor
+        */
+       app_process = (fr_app_worker_t const *)(inst->app_process->module->common);
+       if (app_process->bootstrap && (app_process->bootstrap(inst->app_process->data,
+                                                             inst->app_process->conf) < 0)) {
+               cf_log_err(conf, "Bootstrap failed for \"%s\"", app_process->name);
                return -1;
        }
 
        return 0;
 }
 
-extern rad_protocol_t proto_arp;
-rad_protocol_t proto_arp = {
-       .magic          = RLM_MODULE_INIT,
-       .name           = "arp",
-       .inst_size      = sizeof(arp_socket_t),
-       .transports     = 0,
-       .tls            = false,
-
-       .bootstrap      = arp_socket_bootstrap,
-       .compile        = arp_socket_compile,
-       .parse          = arp_socket_parse,
-       .open           = common_socket_open,
-       .recv           = arp_socket_recv,
-       .send           = arp_socket_send,
-       .print          = arp_socket_print,
-       .debug          = common_packet_debug,
-       .encode         = arp_socket_encode,
-       .decode         = arp_socket_decode
+static int mod_load(void)
+{
+       if (fr_arp_init() < 0) {
+               PERROR("Failed initialising protocol library");
+               return -1;
+       }
+       return 0;
+}
+
+static void mod_unload(void)
+{
+       fr_arp_free();
+}
+
+fr_app_t proto_arp = {
+       .magic                  = RLM_MODULE_INIT,
+       .name                   = "arp",
+       .config                 = proto_arp_config,
+       .inst_size              = sizeof(proto_arp_t),
+       .dict                   = &dict_arp,
+
+       .onload                 = mod_load,
+       .unload                 = mod_unload,
+       .bootstrap              = mod_bootstrap,
+       .instantiate            = mod_instantiate,
+       .open                   = mod_open,
+       .decode                 = mod_decode,
+       .encode                 = mod_encode,
+       .entry_point_set        = mod_entry_point_set,
+       .priority               = mod_priority_set
 };