/* * 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 #include #include #include #include #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((uint8_t) *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, "")); 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, "")); 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((uint8_t) *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, };