summaryrefslogtreecommitdiffstats
path: root/src/modules/proto_dhcp/rlm_dhcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/proto_dhcp/rlm_dhcp.c')
-rw-r--r--src/modules/proto_dhcp/rlm_dhcp.c203
1 files changed, 203 insertions, 0 deletions
diff --git a/src/modules/proto_dhcp/rlm_dhcp.c b/src/modules/proto_dhcp/rlm_dhcp.c
new file mode 100644
index 0000000..1cd73ff
--- /dev/null
+++ b/src/modules/proto_dhcp/rlm_dhcp.c
@@ -0,0 +1,203 @@
+/*
+ * This program is 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 rlm_dhcp.c
+ * @brief Will contain dhcp listener code.
+ *
+ * @copyright 2012 The FreeRADIUS server project
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/libradius.h>
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/dhcp.h>
+
+#include <ctype.h>
+
+#define PW_DHCP_PARAMETER_REQUEST_LIST 55
+
+/*
+ * Define a structure for our module configuration.
+ *
+ * These variables do not need to be in a structure, but it's
+ * a lot cleaner to do so, and a pointer to the structure can
+ * be used as the instance handle.
+ */
+typedef struct rlm_dhcp_t {
+ int nothing;
+} rlm_dhcp_t;
+
+
+/*
+ * Allow single attribute values to be retrieved from the dhcp.
+ */
+static ssize_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
+ char const *fmt, char *out, size_t freespace)
+{
+ vp_cursor_t cursor, src_cursor;
+ vp_tmpl_t src;
+ VALUE_PAIR *vp, *head = NULL;
+ int decoded = 0;
+ ssize_t slen;
+
+ while (isspace((int) *fmt)) fmt++;
+
+ slen = tmpl_from_attr_str(&src, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
+ if (slen <= 0) {
+ REMARKER(fmt, slen, fr_strerror());
+ error:
+ *out = '\0';
+ return -1;
+ }
+
+ if (src.type != TMPL_TYPE_ATTR) {
+ REDEBUG("dhcp_options cannot operate on a %s", fr_int2str(tmpl_names, src.type, "<INVALID>"));
+ goto error;
+ }
+
+ if (src.tmpl_da->type != PW_TYPE_OCTETS) {
+ REDEBUG("dhcp_options got a %s attribute needed octets",
+ fr_int2str(dict_attr_types, src.tmpl_da->type, "<INVALID>"));
+ goto error;
+ }
+
+ for (vp = tmpl_cursor_init(NULL, &src_cursor, request, &src);
+ vp;
+ vp = tmpl_cursor_next(&src_cursor, &src)) {
+ /*
+ * @fixme: we should pass in a cursor, then decoding multiple
+ * source attributes can be made atomic.
+ */
+ if ((fr_dhcp_decode_options(request->packet, &head, vp->vp_octets, vp->vp_length) < 0) || (!head)) {
+ RWDEBUG("DHCP option decoding failed: %s", fr_strerror());
+ goto error;
+ }
+
+ for (vp = fr_cursor_init(&cursor, &head);
+ vp;
+ vp = fr_cursor_next(&cursor)) {
+ rdebug_pair(L_DBG_LVL_2, request, vp, "dhcp_options: ");
+ decoded++;
+ }
+
+ fr_pair_list_move(request->packet, &(request->packet->vps), &head, T_OP_ADD);
+
+ /* Free any unmoved pairs */
+ fr_pair_list_free(&head);
+ }
+
+ snprintf(out, freespace, "%i", decoded);
+
+ return strlen(out);
+}
+
+static ssize_t dhcp_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
+{
+ vp_cursor_t cursor;
+ VALUE_PAIR *head = NULL, *vp;
+ uint8_t binbuf[1024];
+ uint8_t *p = binbuf, *end = p + sizeof(binbuf);
+ ssize_t slen;
+
+ while (isspace((int) *fmt)) fmt++;
+
+ if ((radius_copy_vp(request, &head, request, fmt) < 0) || !head) {
+ *out = '\0';
+ return 0;
+ }
+ fr_cursor_init(&cursor, &head);
+
+ while ((vp = fr_cursor_current(&cursor))) {
+ slen = fr_dhcp_encode_option(request, p, end - p, &cursor);
+ talloc_free(vp);
+ if (slen <= 0) {
+ REDEBUG("DHCP option encoding failed: %s", fr_strerror());
+
+ return -1;
+ }
+ p += (size_t)slen;
+ }
+
+ if ((size_t)(((p - binbuf) * 2) + 1) > freespace) {
+ REDEBUG("DHCP option encoding failed: Output buffer exhausted, needed %zd bytes, have %zd bytes",
+ ((p - binbuf) * 2) + 1, freespace);
+
+ return -1;
+ }
+
+ return fr_bin2hex(out, binbuf, (p - binbuf));
+}
+
+
+/*
+ * Instantiate the module.
+ */
+static int mod_bootstrap(UNUSED CONF_SECTION *conf, void *instance)
+{
+ rlm_dhcp_t *inst = instance;
+ DICT_ATTR const *da;
+
+ xlat_register("dhcp_options", dhcp_options_xlat, NULL, inst);
+ xlat_register("dhcp", dhcp_xlat, NULL, inst);
+
+ /*
+ * Fixup dictionary entry for DHCP-Paramter-Request-List adding all the options
+ */
+ da = dict_attrbyvalue(PW_DHCP_PARAMETER_REQUEST_LIST, DHCP_MAGIC_VENDOR);
+ if (da) {
+ DICT_ATTR const *value;
+ int i;
+
+ /* No padding or termination options */
+ DEBUG3("Adding values for %s", da->name);
+ for (i = 1; i < 255; i++) {
+ value = dict_attrbyvalue(i, DHCP_MAGIC_VENDOR);
+ if (!value) {
+ DEBUG3("No DHCP RFC space attribute at %i", i);
+ continue;
+ }
+
+ DEBUG3("Adding %s value %i %s", da->name, i, value->name);
+ if (dict_addvalue(value->name, da->name, i) < 0) {
+ DEBUG3("Failed adding value: %s", fr_strerror());
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * The module name should be the only globally exported symbol.
+ * That is, everything else should be 'static'.
+ *
+ * If the module needs to temporarily modify it's instantiation
+ * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
+ * The server will then take care of ensuring that the module
+ * is single-threaded.
+ */
+extern module_t rlm_dhcp;
+module_t rlm_dhcp = {
+ .magic = RLM_MODULE_INIT,
+ .name = "dhcp",
+ .inst_size = sizeof(rlm_dhcp_t),
+ .bootstrap = mod_bootstrap,
+};