The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
ext.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** 'compositing' using talloced structures
18 *
19 * @file src/lib/util/ext.c
20 *
21 * @copyright 2020 The FreeRADIUS server project
22 * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23 */
24RCSID("$Id: bfa07ef9869f78205a6787ffcccfde5008e593ce $")
25
26#include <freeradius-devel/util/debug.h>
27#include <freeradius-devel/util/ext.h>
28#include <freeradius-devel/util/misc.h>
29#include <freeradius-devel/util/syserror.h>
30
31/** Add a variable length extension to a talloc chunk
32 *
33 * This is used to build a structure from a primary struct type and one or more
34 * extension structures. The memory for the composed structure is contiguous which
35 * has performance benefits, and means we don't have the overhead of talloc headers
36 * for each of the extensions.
37 *
38 * @note When a new extension is allocated its memory will be zeroed.
39 *
40 * @note It is highly recommended to allocate composed structures within a talloc_pool
41 * to avoid the overhead of malloc+memcpy.
42 *
43 * @param[in] def Extension definitions.
44 * @param[in,out] chunk_p The chunk to add an extension for.
45 * Under certain circumstances the value of *chunk_p will
46 * be changed to point to a new memory block.
47 * All cached copies of the previous pointer should be
48 * updated.
49 * @param[in] ext to alloc.
50 * @param[in] ext_len The length of the extension.
51 * @return
52 * - NULL if we failed allocating an extension.
53 * - A pointer to the extension we allocated.
54 */
55void *fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext_len)
56{
57 size_t aligned_len = ROUND_UP_POW2(ext_len, FR_EXT_ALIGNMENT);
58 size_t chunk_len;
59 size_t hdr_len = 0;
60
61 size_t offset;
62
63 fr_ext_info_t const *info = &def->info[ext];
64 void *n_chunk, *chunk = *chunk_p;
65 uint8_t *ext_offsets;
66 uint8_t *ext_ptr;
67 char const *type;
68
69 ext_offsets = fr_ext_offsets(def, *chunk_p);
70 if (ext_offsets[ext]) return fr_ext_ptr(*chunk_p, ext_offsets[ext], info->has_hdr);
71
72 if (info->has_hdr) hdr_len = sizeof(fr_ext_hdr_t); /* Add space for a length prefix */
73
74 /*
75 * Packing the offsets into a uint8_t array
76 * means the offset address of the final
77 * extension must be less than or equal to
78 * UINT8_MAX * FR_EXT_ALIGNMENT.
79 */
80 chunk_len = talloc_get_size(chunk);
81 offset = ROUND_UP_DIV(chunk_len, FR_EXT_ALIGNMENT);
82 if (unlikely(offset > UINT8_MAX)) {
83 fr_strerror_const("Insufficient space remaining for extensions");
84 return NULL;
85 }
86
87 /*
88 * talloc_realloc_size unhelpfully forgets
89 * the name of the chunk, so we need to
90 * record it and set it back again.
91 */
92 type = talloc_get_name(chunk);
93 n_chunk = talloc_realloc_size(NULL, chunk, (offset * FR_EXT_ALIGNMENT) + hdr_len + aligned_len);
94 if (!n_chunk) {
95 fr_strerror_printf("Failed reallocing %s (%s). Tried to realloc %zu bytes -> %zu bytes",
96 type, fr_syserror(errno), chunk_len, chunk_len + aligned_len);
97 return NULL;
98 }
99 talloc_set_name_const(n_chunk, type);
100
101 ext_offsets = fr_ext_offsets(def, n_chunk);
102 ext_offsets[ext] = (uint8_t)offset;
103
104 ext_ptr = ((uint8_t *)n_chunk) + chunk_len;
105 memset(ext_ptr, 0, hdr_len + aligned_len);
106
107 *chunk_p = n_chunk;
108
109 if (info->has_hdr) {
110 fr_ext_hdr_t *ext_hdr = (fr_ext_hdr_t *)ext_ptr;
111
112 ext_hdr->len = ext_len; /* Record the real size */
113 return &ext_hdr->data; /* Pointer to the data portion */
114 }
115
116 return ext_ptr;
117}
118
119/** Return the length of an extension
120 *
121 * @param[in] def Extension definitions.
122 * @param[in] chunk to return extension length for.
123 * @param[in] ext to return length for.
124 * @return
125 * - 0 if no extension exists or is of zero length.
126 * - >0 the length of the extension.
127 */
128size_t fr_ext_len(fr_ext_t const *def, TALLOC_CTX const *chunk, int ext)
129{
130 uint8_t offset;
131 fr_ext_info_t const *info;
132 fr_ext_hdr_t *ext_hdr;
133 uint8_t *ext_offsets;
134
135 ext_offsets = fr_ext_offsets(def, chunk);
136 offset = ext_offsets[ext];
137 if (!offset) return 0;
138
139 info = &def->info[ext];
140 if (!info->has_hdr) return info->min; /* Fixed size */
141
142 ext_hdr = fr_ext_ptr(chunk, offset, false); /* false as we're getting the header */
143 return ext_hdr->len;
144}
145
146/** Copy extension data from one attribute to another
147 *
148 * @param[in] def Extension definitions.
149 * @param[in,out] chunk_dst to copy extension to.
150 * Under certain circumstances the value of *chunk_dst will
151 * be changed to point to a new memory block.
152 * All cached copies of the previous pointer should be
153 * updated.
154 * @param[in] chunk_src to copy extension from.
155 * @param[in] ext to copy.
156 * @return
157 * - NULL if we failed to allocate an extension structure.
158 * - A pointer to the offset of the extension in da_out.
159 */
160void *fr_ext_copy(fr_ext_t const *def, TALLOC_CTX **chunk_dst, TALLOC_CTX const *chunk_src, int ext)
161{
162 int i;
163 uint8_t *ext_src_offsets = fr_ext_offsets(def, chunk_src);
164 uint8_t *ext_dst_offsets = fr_ext_offsets(def, *chunk_dst);
165 void *ext_src_ptr, *ext_dst_ptr;
166 fr_ext_info_t const *info = &def->info[ext];
167
168 if (!info->can_copy) {
169 fr_strerror_const("Extension cannot be copied");
170 return NULL;
171 }
172
173 if (!ext_src_offsets[ext]) return NULL;
174
175 ext_src_ptr = fr_ext_ptr(chunk_src, ext_src_offsets[ext], info->has_hdr);
176
177 /*
178 * Only alloc if the extension doesn't
179 * already exist.
180 */
181 if (!ext_dst_offsets[ext]) {
182 if (info->alloc) {
183 ext_dst_ptr = info->alloc(def, chunk_dst, ext,
184 ext_src_ptr,
185 fr_ext_len(def, chunk_src, ext));
186 /*
187 * If there's no special alloc function
188 * we just allocate a chunk of the same
189 * size.
190 */
191 } else {
192 ext_dst_ptr = fr_ext_alloc_size(def, chunk_dst, ext,
193 fr_ext_len(def, chunk_src, ext));
194 }
195 } else {
196 ext_dst_ptr = fr_ext_ptr(*chunk_dst, ext_dst_offsets[ext], info->has_hdr);
197 }
198
199 if (info->copy) {
200 info->copy(ext,
201 *chunk_dst,
202 ext_dst_ptr, fr_ext_len(def, *chunk_dst, ext),
203 chunk_src,
204 ext_src_ptr, fr_ext_len(def, chunk_src, ext));
205 /*
206 * If there's no special copy function
207 * we just copy the data from the old
208 * extension to the new one.
209 */
210 } else {
211 memcpy(ext_dst_ptr, ext_src_ptr, fr_ext_len(def, *chunk_dst, ext));
212 }
213
214 /*
215 * Call any fixup functions
216 */
217 ext_dst_offsets = fr_ext_offsets(def, *chunk_dst);
218 for (i = 0; i < def->max; i++) {
219 if (i == ext) continue;
220
221 if (!ext_dst_offsets[i]) continue;
222
223 if (info->fixup &&
224 info->fixup(i, *chunk_dst,
225 fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
226 fr_ext_len(def, *chunk_dst, i)) < 0) return NULL;
227 }
228
229 return ext_dst_ptr;
230}
231
232/** Copy all the extensions from one attribute to another
233 *
234 * @param[in] def Extension definitions.
235 * @param[in,out] chunk_dst to copy extensions to.
236 * Under certain circumstances the value of *chunk_dst will
237 * be changed to point to a new memory block.
238 * All cached copies of the previous pointer should be
239 * updated.
240 * @param[in] chunk_src to copy extensions from.
241 * @return
242 * - 0 on success.
243 * - -1 if a copy operation failed.
244 */
245int fr_ext_copy_all(fr_ext_t const *def, TALLOC_CTX **chunk_dst, TALLOC_CTX const *chunk_src)
246{
247 int i;
248 uint8_t *ext_src_offsets = fr_ext_offsets(def, chunk_src); /* old chunk array */
249 uint8_t *ext_dst_offsets = fr_ext_offsets(def, *chunk_dst); /* new chunk array */
250 bool ext_new_alloc[def->max];
251
252 /*
253 * Do the operation in two phases.
254 *
255 * Phase 1 allocates space for all the extensions.
256 */
257 for (i = 0; i < def->max; i++) {
258 fr_ext_info_t const *info = &def->info[i];
259
260 if (!ext_src_offsets[i] || !info->can_copy) {
261 no_copy:
262 ext_new_alloc[i] = false;
263 continue;
264 }
265
266 if (info->alloc) {
267 if (!info->alloc(def, chunk_dst, i,
268 fr_ext_ptr(chunk_src, ext_src_offsets[i], info->has_hdr),
269 fr_ext_len(def, chunk_src, i))) goto no_copy;
270 /*
271 * If there's no special alloc function
272 * we just allocate a chunk of the same
273 * size.
274 */
275 } else {
276 fr_ext_alloc_size(def, chunk_dst, i, fr_ext_len(def, chunk_src, i));
277 }
278 ext_new_alloc[i] = true;
279 ext_dst_offsets = fr_ext_offsets(def, *chunk_dst); /* Grab new offsets, chunk might have changed */
280 }
281
282 /*
283 * Phase 2 populates the extension memory.
284 *
285 * We do this in two phases to avoid invalidating
286 * any pointers from extensions back to the extended
287 * talloc chunk.
288 */
289 for (i = 0; i < def->max; i++) {
290 fr_ext_info_t const *info = &def->info[i];
291
292 if (!ext_src_offsets[i] || !ext_dst_offsets[i]) continue;
293
294 if (!ext_new_alloc[i]) {
295 if (info->fixup &&
296 info->fixup(i, *chunk_dst,
297 fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
298 fr_ext_len(def, *chunk_dst, i)) < 0) return -1;
299 continue;
300 }
301 if (!info->can_copy) continue;
302
303 if (info->copy) {
304 if (info->copy(i,
305 *chunk_dst,
306 fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
307 fr_ext_len(def, *chunk_dst, i),
308 chunk_src,
309 fr_ext_ptr(chunk_src, ext_src_offsets[i], info->has_hdr),
310 fr_ext_len(def, chunk_src, i)) < 0) return -1;
311 /*
312 * If there's no special copy function
313 * we just copy the data from the old
314 * extension to the new one.
315 */
316 } else {
317 memcpy(fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
318 fr_ext_ptr(chunk_src, ext_src_offsets[i], info->has_hdr),
319 fr_ext_len(def, *chunk_dst, i));
320 }
321 }
322
323 return 0;
324}
325
326/** Print out all extensions and hexdump their contents
327 *
328 * This function is intended to be called from interactive debugging
329 * sessions only. It does not use the normal logging infrastructure.
330 *
331 * @param[in] def Extension definitions.
332 * @param[in] name the identifier of the structure
333 * being debugged i.e da->name.
334 * @param[in] chunk to debug.
335 */
336void fr_ext_debug(fr_ext_t const *def, char const *name, void const *chunk)
337{
338 int i;
339
340 FR_FAULT_LOG("%s ext total_len=%zu", name, talloc_get_size(chunk));
341 for (i = 0; i < (int)def->max; i++) {
342 uint8_t *ext_offsets = fr_ext_offsets(def, chunk);
343
344 if (ext_offsets[i]) {
345 void *ext = fr_ext_ptr(chunk, ext_offsets[i], def->info[i].has_hdr);
346 size_t ext_len = fr_ext_len(def, chunk, i);
347 char const *ext_name = fr_table_ordered_str_by_num(def->name_table,
348 *def->name_table_len,
349 i, "<INVALID>");
350
351 if (ext_len > 1024) {
352 FR_FAULT_LOG("%s ext id=%s - possibly bad length %zu - limiting dump to 1024",
353 name, ext_name, ext_len);
354 ext_len = 1024;
355 }
356
357 FR_FAULT_LOG("%s ext id=%s start=%p end=%p len=%zu",
358 name, ext_name, ext, ((uint8_t *)ext) + ext_len, ext_len);
359 FR_FAULT_LOG_HEX(ext, ext_len);
360 }
361 }
362}
#define RCSID(id)
Definition build.h:483
#define unlikely(_x)
Definition build.h:381
#define FR_FAULT_LOG_HEX(_data, _data_len)
Definition debug.h:50
#define FR_FAULT_LOG(_fmt,...)
Definition debug.h:49
void * fr_ext_copy(fr_ext_t const *def, TALLOC_CTX **chunk_dst, TALLOC_CTX const *chunk_src, int ext)
Copy extension data from one attribute to another.
Definition ext.c:160
size_t fr_ext_len(fr_ext_t const *def, TALLOC_CTX const *chunk, int ext)
Return the length of an extension.
Definition ext.c:128
void fr_ext_debug(fr_ext_t const *def, char const *name, void const *chunk)
Print out all extensions and hexdump their contents.
Definition ext.c:336
int fr_ext_copy_all(fr_ext_t const *def, TALLOC_CTX **chunk_dst, TALLOC_CTX const *chunk_src)
Copy all the extensions from one attribute to another.
Definition ext.c:245
void * fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext_len)
Add a variable length extension to a talloc chunk.
Definition ext.c:55
bool can_copy
Copying this extension between structs is allowed.
Definition ext.h:116
int max
The highest extension value.
Definition ext.h:130
uint8_t data[]
Extension data.
Definition ext.h:139
bool has_hdr
Additional metadata should be allocated before the extension data to record the exact length of the e...
Definition ext.h:113
size_t * name_table_len
How many extensions there are in the table.
Definition ext.h:129
static void * fr_ext_ptr(TALLOC_CTX const *chunk, size_t offset, bool has_hdr)
Return a pointer to an extension in a chunk.
Definition ext.h:150
fr_table_num_ordered_t const * name_table
String identifiers for the extensions.
Definition ext.h:128
size_t len
Length of extension data.
Definition ext.h:138
fr_ext_copy_t copy
Override the normal copy operation with a callback.
Definition ext.h:119
fr_ext_alloc_t alloc
Override the normal alloc operation with a callback.
Definition ext.h:118
fr_ext_fixup_t fixup
Callback for fixing up internal consistency issues.
Definition ext.h:120
#define FR_EXT_ALIGNMENT
The alignment of object extension structures.
Definition ext.h:54
size_t min
Minimum size of extension.
Definition ext.h:112
static uint8_t * fr_ext_offsets(fr_ext_t const *def, TALLOC_CTX const *chunk)
Definition ext.h:142
fr_ext_info_t const * info
Additional information about each extension.
Definition ext.h:131
Optional extension header struct.
Definition ext.h:137
Additional information for a given extension.
Definition ext.h:111
Structure to define a set of extensions.
Definition ext.h:126
#define ROUND_UP_POW2(_num, _mul)
Round up - Only works if _mul is a power of 2 but avoids division.
Definition math.h:144
#define ROUND_UP_DIV(_x, _y)
Get the ceiling value of integer division.
Definition math.h:153
unsigned char uint8_t
#define UINT8_MAX
static char const * name
fr_aka_sim_id_type_t type
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
char const * fr_table_ordered_str_by_num(fr_table_num_ordered_t const *table, size_t table_len, int number, char const *def)
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223