Populate DHCP-Paramter-Request-List values dynamically
[freeradius.git] / src / modules / proto_dhcp / rlm_dhcp.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15
16 /**
17  * $Id$
18  * @file rlm_dhcp.c
19  * @brief Will contain dhcp listener code.
20  *
21  * @copyright 2012  The FreeRADIUS server project
22  */
23 RCSID("$Id$")
24
25 #include <freeradius-devel/libradius.h>
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <freeradius-devel/dhcp.h>
30
31 #include <ctype.h>
32
33 #define PW_DHCP_PARAMETER_REQUEST_LIST 55
34
35 /*
36  *      Define a structure for our module configuration.
37  *
38  *      These variables do not need to be in a structure, but it's
39  *      a lot cleaner to do so, and a pointer to the structure can
40  *      be used as the instance handle.
41  */
42 typedef struct rlm_dhcp_t {
43         int nothing;
44 } rlm_dhcp_t;
45
46
47 /*
48  *      Allow single attribute values to be retrieved from the dhcp.
49  */
50 static ssize_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
51                                  char const *fmt, char *out, size_t freespace)
52 {
53         vp_cursor_t cursor;
54         VALUE_PAIR *vp, *head = NULL;
55         int decoded = 0;
56
57         while (isspace((int) *fmt)) fmt++;
58
59         if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
60                  *out = '\0';
61                  return 0;
62         }
63
64         if ((fr_dhcp_decode_options(&head, request->packet, vp->vp_octets, vp->length) < 0) || (!head)) {
65                 RWDEBUG("DHCP option decoding failed: %s", fr_strerror());
66                 *out = '\0';
67                 return -1;
68         }
69
70
71         for (vp = fr_cursor_init(&cursor, &head);
72              vp;
73              vp = fr_cursor_next(&cursor)) {
74                 decoded++;
75         }
76
77         pairmove(request->packet, &(request->packet->vps), &head);
78
79         /* Free any unmoved pairs */
80         pairfree(&head);
81
82         snprintf(out, freespace, "%i", decoded);
83
84         return strlen(out);
85 }
86
87 static ssize_t dhcp_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
88 {
89         vp_cursor_t cursor;
90         VALUE_PAIR *vp;
91         uint8_t binbuf[255];
92         ssize_t len;
93
94         while (isspace((int) *fmt)) fmt++;
95
96         if ((radius_copy_vp(&vp, request, fmt) < 0) || !vp) {
97                  *out = '\0';
98                  return 0;
99         }
100         fr_cursor_init(&cursor, &vp);
101
102         len = fr_dhcp_encode_option(binbuf, sizeof(binbuf), request, &cursor);
103         talloc_free(vp);
104         if (len <= 0) {
105                 REDEBUG("DHCP option encoding failed: %s", fr_strerror());
106
107                 return -1;
108         }
109
110         if ((size_t)((len * 2) + 1) > freespace) {
111                 REDEBUG("DHCP option encoding failed: Output buffer exhausted, needed %zd bytes, have %zd bytes",
112                         (len * 2) + 1, freespace);
113
114                 return -1;
115         }
116
117         return fr_bin2hex(out, binbuf, len);
118 }
119
120 /*
121  *      Only free memory we allocated.  The strings allocated via
122  *      cf_section_parse() do not need to be freed.
123  */
124 static int mod_detach(void *instance)
125 {
126         xlat_unregister("dhcp_options", dhcp_options_xlat, instance);
127         xlat_unregister("dhcp", dhcp_xlat, instance);
128         return 0;
129 }
130
131
132 /*
133  *      Instantiate the module.
134  */
135 static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
136 {
137         rlm_dhcp_t *inst = instance;
138         DICT_ATTR const *da;
139
140         xlat_register("dhcp_options", dhcp_options_xlat, NULL, inst);
141         xlat_register("dhcp", dhcp_xlat, NULL, inst);
142
143         /*
144          *      Fixup dictionary entry for DHCP-Paramter-Request-List adding all the options
145          */
146         da = dict_attrbyvalue(PW_DHCP_PARAMETER_REQUEST_LIST, DHCP_MAGIC_VENDOR);
147         if (da) {
148                 DICT_ATTR const *value;
149                 int i;
150
151                 /* No padding or termination options */
152                 DEBUG3("Adding values for %s", da->name);
153                 for (i = 1; i < 255; i++) {
154                         value = dict_attrbyvalue(i, DHCP_MAGIC_VENDOR);
155                         if (!value) {
156                                 DEBUG3("No DHCP RFC space attribute at %i", i);
157                                 continue;
158                         }
159
160                         DEBUG3("Adding %s value %i %s", da->name, i, value->name);
161                         if (dict_addvalue(value->name, da->name, i) < 0) {
162                                 DEBUG3("Failed adding value: %s", fr_strerror());
163                         }
164                 }
165         }
166
167         return 0;
168 }
169
170
171 /*
172  *      The module name should be the only globally exported symbol.
173  *      That is, everything else should be 'static'.
174  *
175  *      If the module needs to temporarily modify it's instantiation
176  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
177  *      The server will then take care of ensuring that the module
178  *      is single-threaded.
179  */
180 module_t rlm_dhcp = {
181         RLM_MODULE_INIT,
182         "dhcp",
183         0,                              /* type */
184         sizeof(rlm_dhcp_t),
185         NULL,                           /* CONF_PARSER */
186         mod_instantiate,                /* instantiation */
187         mod_detach,                     /* detach */
188         {
189                 NULL,                   /* authentication */
190                 NULL,                   /* authorization */
191                 NULL,                   /* preaccounting */
192                 NULL,                   /* accounting */
193                 NULL,                   /* checksimul */
194                 NULL,                   /* pre-proxy */
195                 NULL,                   /* post-proxy */
196                 NULL,                   /* post-auth */
197         },
198 };