]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
make it mostly work
authorAlan T. DeKok <aland@freeradius.org>
Tue, 5 May 2020 12:32:27 +0000 (08:32 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 5 May 2020 12:33:29 +0000 (08:33 -0400)
compile the processing sections
add proto_arp_ethernet which (for now) just reads packets
no reply is currently sent
various other cleanups and fixes

src/modules/proto_arp/all.mk
src/modules/proto_arp/proto_arp.c
src/modules/proto_arp/proto_arp.mk
src/modules/proto_arp/proto_arp_ethernet.c [new file with mode: 0644]
src/modules/proto_arp/proto_arp_ethernet.mk [new file with mode: 0644]
src/modules/proto_arp/proto_arp_process.c

index ebc403e82f95b8a4cd3bd74ea34102669ded683f..d16f1d39bd4c149a5d61061d6a9ebdcc8a5298d2 100644 (file)
@@ -1,5 +1,5 @@
 SUBMAKEFILES := \
        proto_arp.mk \
+       proto_arp_ethernet.mk \
        proto_arp_process.mk
 
-#      proto_arp_ethernet.mk \
index 1820f5d9df978d04717c72b8714c20509cc52c6d..c769859c5f11c7965c57f72d84114b6e7013b073 100644 (file)
  * @copyright 2016 Alan DeKok (aland@freeradius.org)
  */
 #include <freeradius-devel/server/base.h>
-#include <freeradius-devel/arp/arp.h>
-#include <freeradius-devel/io/listen.h>
 #include <freeradius-devel/server/module.h>
+#include <freeradius-devel/server/virtual_servers.h>
 #include <freeradius-devel/unlang/base.h>
 #include <freeradius-devel/util/debug.h>
+#include "proto_arp.h"
 
 extern fr_app_t proto_arp;
 
-typedef struct {
-       CONF_SECTION                    *server_cs;                     //!< server CS for this listener
-       CONF_SECTION                    *cs;                            //!< my configuration
-
-       dl_module_inst_t                *io_submodule;                  //!< As provided by the transport_parse
-                                                                       ///< callback.  Broken out into the
-                                                                       ///< app_io_* fields below for convenience.
-
-       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.
-
-       dl_module_inst_t                *app_process;                   //!< app_process pointer
-       void                            *process_instance;              //!< app_process instance
-
-       fr_dict_t                       *dict;                          //!< root dictionary
-
-       char const                      *interface;                     //!< interface name
-       uint32_t                        num_messages;                   //!< for message ring buffer
-       uint32_t                        priority;                       //!< for packet processing, larger == higher
-
-       fr_schedule_t                   *sc;                            //!< the scheduler, where we insert new readers
-
-       fr_listen_t                     *listen;                        //!< The listener structure which describes
-                                                                       //!< the I/O path.
-} proto_arp_t;
-
-
 /** How to parse an ARP listen section
  *
  */
 static CONF_PARSER const proto_arp_config[] = {
-       { FR_CONF_OFFSET("interface", FR_TYPE_STRING | FR_TYPE_NOT_EMPTY, proto_arp_t,
-                         interface), .dflt = "eth0" },
-
-       /*
-        *      @todo - allow for pcap filter
-        */
-
        { FR_CONF_OFFSET("num_messages", FR_TYPE_UINT32, proto_arp_t, num_messages) } ,
 
        CONF_PARSER_TERMINATOR
@@ -114,7 +79,7 @@ static int mod_decode(UNUSED void const *instance, REQUEST *request, uint8_t *co
        }
 
        arp = (fr_arp_packet_t const *) data;
-       request->packet->code = arp->op;
+       request->packet->code = (arp->op[0] << 8) | arp->op[1];
 
        request->packet->data = talloc_memdup(request->packet, data, data_len);
        request->packet->data_len = data_len;
@@ -231,8 +196,10 @@ static int mod_open(void *instance, fr_schedule_t *sc, UNUSED CONF_SECTION *conf
 
        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 (li->app_io->thread_inst_size) {
+               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);
+       }
 
        /*
         *      Open the raw socket.
@@ -241,8 +208,9 @@ static int mod_open(void *instance, fr_schedule_t *sc, UNUSED CONF_SECTION *conf
                talloc_free(li);
                return -1;
        }
+       fr_assert(li->fd >= 0);
 
-       li->name = talloc_asprintf(li, "arp on interface %s", inst->interface);
+       li->name = inst->app_io->get_name(li);
 
        /*
         *      Watch the directory for changes.
@@ -299,7 +267,21 @@ static int mod_instantiate(void *instance, CONF_SECTION *conf)
                return -1;
        }
 
-       return -1;
+       /*
+        *      Compile the processing sections.
+        */
+       if (app_process->compile_list) {
+               vp_tmpl_rules_t         parse_rules;
+
+               memset(&parse_rules, 0, sizeof(parse_rules));
+               parse_rules.dict_def = dict_arp;
+
+               if (virtual_server_compile_sections(inst->server_cs, app_process->compile_list, &parse_rules, inst->app_process->data) < 0) {
+                       return -1;
+               }
+       }
+
+       return 0;
 }
 
 
@@ -323,6 +305,7 @@ static int mod_bootstrap(void *instance, CONF_SECTION *conf)
         *      Ensure that the server CONF_SECTION is always set.
         */
        inst->server_cs = cf_item_to_section(cf_parent(conf));
+       inst->cs = conf;
 
        parent_inst = cf_data_value(cf_data_find(inst->cs, dl_module_inst_t, "proto_arp"));
        fr_assert(parent_inst);
@@ -365,6 +348,29 @@ static int mod_bootstrap(void *instance, CONF_SECTION *conf)
                return -1;
        }
 
+       /*
+        *      Register the processing sections with the
+        *      module manager.
+        *
+        *      We have to do this ourselves because we don't call
+        *      fr_app_process_bootstrap().  That function requires a
+        *      "type = ..." configuration, which isn't used for ARP.
+        */
+       if (app_process->compile_list) {
+               int j;
+               virtual_server_compile_t const *list = app_process->compile_list;
+
+               for (j = 0; list[j].name != NULL; j++) {
+                       if (list[j].name == CF_IDENT_ANY) continue;
+
+                       if (virtual_server_section_register(&list[j]) < 0) {
+                               cf_log_err(conf, "Failed registering section name for %s",
+                                          app_process->name);
+                               return -1;
+                       }
+               }
+       }
+
        return 0;
 }
 
index 3fe90a8cc9c26c69c3b08d579505bfcbbd97225d..2d2fa6b728bb53fc79fc59f1fa6bf37013c622d9 100644 (file)
@@ -8,5 +8,5 @@ TARGET          := $(TARGETNAME).a
 endif
 
 SOURCES                := proto_arp.c
-TGT_PREREQS    := libfreeradius-util.a
+TGT_PREREQS    := libfreeradius-util.a libfreeradius-arp.a
 TGT_LDLIBS     := $(PCAP_LIBS)
diff --git a/src/modules/proto_arp/proto_arp_ethernet.c b/src/modules/proto_arp/proto_arp_ethernet.c
new file mode 100644 (file)
index 0000000..a6b3537
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ *   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
+ */
+
+/**
+ * $Id$
+ * @file proto_arp_udp.c
+ * @brief RADIUS handler for UDP.
+ *
+ * @copyright 2016 The FreeRADIUS server project.
+ * @copyright 2016 Alan DeKok (aland@deployingradius.com)
+ */
+#include <netdb.h>
+#include <freeradius-devel/server/base.h>
+#include <freeradius-devel/server/protocol.h>
+#include <freeradius-devel/util/udp.h>
+#include <freeradius-devel/util/trie.h>
+#include <freeradius-devel/radius/radius.h>
+#include <freeradius-devel/io/base.h>
+#include <freeradius-devel/io/application.h>
+#include <freeradius-devel/io/listen.h>
+#include <freeradius-devel/io/schedule.h>
+#include <freeradius-devel/util/debug.h>
+
+#include "proto_arp.h"
+
+extern fr_app_io_t proto_arp_ethernet;
+
+typedef struct {
+       char const                      *name;                  //!< socket name
+       fr_pcap_t                       *pcap;                  //!< PCAP handler
+} proto_arp_ethernet_thread_t;
+
+typedef struct {
+       CONF_SECTION                    *cs;                    //!< our configuration
+       char const                      *interface;             //!< Interface to bind to.
+} proto_arp_ethernet_t;
+
+
+/** How to parse an ARP listen section
+ *
+ */
+static CONF_PARSER const arp_listen_config[] = {
+       { FR_CONF_OFFSET("interface", FR_TYPE_STRING | FR_TYPE_NOT_EMPTY, proto_arp_ethernet_t,
+                         interface), .dflt = "eth0" },
+
+       /*
+        *      @todo - allow for pcap filter
+        */
+
+       CONF_PARSER_TERMINATOR
+};
+
+static ssize_t mod_read(fr_listen_t *li, UNUSED 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_arp_ethernet_thread_t     *thread = talloc_get_type_abort(li->thread_instance, proto_arp_ethernet_thread_t);
+       int                             ret;
+       uint8_t const                   *data;
+       struct pcap_pkthdr              *header;
+       uint8_t const                   *p, *end;
+       ssize_t                         len;
+
+       *leftover = 0;          /* always for message oriented protocols */
+
+       ret = pcap_next_ex(thread->pcap->handle, &header, &data);
+       if (ret == 0) return 0;
+       if (ret < 0) {
+               DEBUG("Failed getting next PCAP packet");
+               return 0;
+       }
+
+       p = data;
+       end = data + header->caplen;
+
+       len = fr_pcap_link_layer_offset(data, header->caplen, thread->pcap->link_layer);
+       if (len < 0) {
+               DEBUG("Failed determining link layer header offset");
+               return 0;
+       }
+       p += len;
+
+       if ((end - p) < FR_ARP_PACKET_SIZE) {
+               DEBUG("Packet is too small (%d) to be ARP", (int) (end - p));
+               return 0;
+       }
+
+       fr_assert(buffer_len >= FR_ARP_PACKET_SIZE);
+
+       memcpy(buffer, p, FR_ARP_PACKET_SIZE);
+
+       // @todo - talloc packet_ctx which is the ethernet header, so we know what kind of VLAN, etc. to encode?
+
+       *recv_time_p = fr_time();
+       return FR_ARP_PACKET_SIZE;
+}
+
+
+static ssize_t mod_write(fr_listen_t *li, UNUSED void *packet_ctx, UNUSED fr_time_t request_time,
+                        uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
+{
+       proto_arp_ethernet_thread_t     *thread = talloc_get_type_abort(li->thread_instance, proto_arp_ethernet_thread_t);
+
+       DEBUG("Fake write ARP");
+
+       /*
+        *      @todo - mirror src/protocols/dhcpv4/pcap.c for ARP send / receive.
+        *      We will need that functionality for rlm_arp, too.
+        */
+
+       return FR_ARP_PACKET_SIZE;
+}
+
+/** Open a pcap file for ARP
+ *
+ */
+static int mod_open(fr_listen_t *li)
+{
+       proto_arp_ethernet_t const      *inst = talloc_get_type_abort_const(li->app_io_instance, proto_arp_ethernet_t);
+       proto_arp_ethernet_thread_t     *thread = talloc_get_type_abort(li->thread_instance, proto_arp_ethernet_thread_t);
+
+       CONF_SECTION                    *server_cs;
+       CONF_ITEM                       *ci;
+
+       thread->pcap = fr_pcap_init(thread, inst->interface, PCAP_INTERFACE_IN);
+       if (!thread->pcap) {
+               PERROR("Failed initializing pcap handle.");
+               return -1;
+       }
+
+       if (fr_pcap_open(thread->pcap) < 0) {
+               PERROR("Failed opening interface %s", inst->interface);
+               return -1;
+       }
+
+       /*
+        *      Ensure that we only get ARP.
+        *
+        *      @todo - only get ARP requests?
+        */
+       if (fr_pcap_apply_filter(thread->pcap, "arp") < 0) {
+               PERROR("Failed applying pcap filter");
+               return -1;
+       }
+
+       li->fd = thread->pcap->fd;
+
+       ci = cf_parent(inst->cs); /* listen { ... } */
+       fr_assert(ci != NULL);
+       server_cs = cf_item_to_section(ci);
+
+       thread->name = talloc_asprintf(thread, "proto arp on interface %s", inst->interface);
+
+       DEBUG("Listening on %s bound to virtual server %s",
+             thread->name, cf_section_name2(server_cs));
+
+       return 0;
+}
+
+static char const *mod_name(fr_listen_t *li)
+{
+       proto_arp_ethernet_thread_t     *thread = talloc_get_type_abort(li->thread_instance, proto_arp_ethernet_thread_t);
+
+       return thread->name;
+}
+
+
+static int mod_bootstrap(void *instance, CONF_SECTION *cs)
+{
+       proto_arp_ethernet_t    *inst = talloc_get_type_abort(instance, proto_arp_ethernet_t);
+
+       inst->cs = cs;
+
+       return 0;
+}
+
+
+fr_app_io_t proto_arp_ethernet = {
+       .magic                  = RLM_MODULE_INIT,
+       .name                   = "arp_ethernet",
+       .config                 = arp_listen_config,
+       .inst_size              = sizeof(proto_arp_ethernet_t),
+       .thread_inst_size       = sizeof(proto_arp_ethernet_thread_t),
+       .bootstrap              = mod_bootstrap,
+
+       .default_message_size   = FR_ARP_PACKET_SIZE,
+
+       .open                   = mod_open,
+       .read                   = mod_read,
+       .write                  = mod_write,
+       .get_name               = mod_name,
+};
diff --git a/src/modules/proto_arp/proto_arp_ethernet.mk b/src/modules/proto_arp/proto_arp_ethernet.mk
new file mode 100644 (file)
index 0000000..fce6319
--- /dev/null
@@ -0,0 +1,12 @@
+TARGETNAME := proto_arp_ethernet
+
+#
+#  ARP depends on pcap.
+#
+ifneq "$(PCAP_LIBS)" ""
+TARGET         := $(TARGETNAME).a
+endif
+
+SOURCES                := proto_arp_ethernet.c
+TGT_PREREQS    := libfreeradius-util.a
+TGT_LDLIBS     := $(PCAP_LIBS)
index c641d8c58cd7cddc6c139e581bc7d639173b30f6..54aa9f0c4d2b6d8252f0bbc4fb6e144c9665dafc 100644 (file)
@@ -42,7 +42,7 @@ static fr_dict_attr_t const *attr_arp_operation;
 
 extern fr_dict_attr_autoload_t proto_arp_process_dict_attr[];
 fr_dict_attr_autoload_t proto_arp_process_dict_attr[] = {
-       { .out = &attr_arp_operation, .name = "ARP-Operation", .type = FR_TYPE_UINT8, .dict = &dict_arp},
+       { .out = &attr_arp_operation, .name = "ARP-Operation", .type = FR_TYPE_UINT16, .dict = &dict_arp},
        { NULL }
 };
 
@@ -76,7 +76,7 @@ static rlm_rcode_t mod_process(UNUSED void *instance, UNUSED void *thread, REQUE
 
                request->component = "arp";
 
-               dv = fr_dict_enum_by_value(attr_arp_operation, fr_box_uint8(request->packet->code));
+               dv = fr_dict_enum_by_value(attr_arp_operation, fr_box_uint16(request->packet->code));
                if (!dv) {
                        REDEBUG("Failed to find value for &request:ARP-Operation");
                        return RLM_MODULE_FAIL;