/src/freeradius-server/src/protocols/radius/list.c
Line | Count | Source |
1 | | /* |
2 | | * This library is free software; you can redistribute it and/or |
3 | | * modify it under the terms of the GNU Lesser General Public |
4 | | * License as published by the Free Software Foundation; either |
5 | | * version 2.1 of the License, or (at your option) any later version. |
6 | | * |
7 | | * This library 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 GNU |
10 | | * Lesser General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU Lesser General Public |
13 | | * License along with this library; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** |
18 | | * $Id: 23c34a82cf085a4570b3d63f01ab92fa520b588e $ |
19 | | * |
20 | | * @file protocols/radius/list.c |
21 | | * @brief Functions to deal with outgoing lists / sets of packets. |
22 | | * |
23 | | * @copyright 2000-2017 The FreeRADIUS server project |
24 | | */ |
25 | | |
26 | | RCSID("$Id: 23c34a82cf085a4570b3d63f01ab92fa520b588e $") |
27 | | |
28 | | #include "radius.h" |
29 | | #include "tcp.h" |
30 | | #include "list.h" |
31 | | |
32 | | #include <fcntl.h> |
33 | | #include <freeradius-devel/util/udp.h> |
34 | | #include <freeradius-devel/util/syserror.h> |
35 | | |
36 | | /* |
37 | | * See if two packets are identical. |
38 | | * |
39 | | * Note that we do NOT compare the authentication vectors. |
40 | | * That's because if the authentication vector is different, |
41 | | * it means that the NAS has given up on the earlier request. |
42 | | */ |
43 | | int8_t fr_packet_cmp(void const *a_v, void const *b_v) |
44 | 0 | { |
45 | 0 | fr_packet_t const *a = a_v, *b = b_v; |
46 | 0 | int8_t ret; |
47 | | |
48 | | /* |
49 | | * 256-way fanout for RADIUS IDs, then by FD. And then |
50 | | * since there are sometimes multiple clients for one FD, |
51 | | * source port. The comparison on dst_port is largely |
52 | | * redundant with the comparison on FD, but it's not |
53 | | * _completely_ redundant. |
54 | | */ |
55 | 0 | CMP_RETURN(a, b, id); |
56 | 0 | CMP_RETURN(a, b, socket.fd); |
57 | 0 | CMP_RETURN(a, b, socket.inet.src_port); |
58 | 0 | CMP_RETURN(a, b, socket.inet.dst_port); |
59 | | |
60 | | /* |
61 | | * Usually many client IPs, and few server IPs |
62 | | */ |
63 | 0 | ret = fr_ipaddr_cmp(&a->socket.inet.src_ipaddr, &b->socket.inet.src_ipaddr); |
64 | 0 | if (ret != 0) return ret; |
65 | | |
66 | 0 | return fr_ipaddr_cmp(&a->socket.inet.dst_ipaddr, &b->socket.inet.dst_ipaddr); |
67 | 0 | } |
68 | | |
69 | | /* |
70 | | * Create a fake "request" from a reply, for later lookup. |
71 | | */ |
72 | | void fr_request_from_reply(fr_packet_t *request, |
73 | | fr_packet_t const *reply) |
74 | 0 | { |
75 | 0 | fr_socket_addr_swap(&request->socket, &reply->socket); |
76 | 0 | request->id = reply->id; |
77 | 0 | } |
78 | | |
79 | | /* |
80 | | * We need to keep track of the socket & it's IP/port. |
81 | | */ |
82 | | typedef struct { |
83 | | fr_socket_t socket; |
84 | | |
85 | | int src_any; |
86 | | int dst_any; |
87 | | void *ctx; |
88 | | |
89 | | uint32_t num_outgoing; |
90 | | |
91 | | bool dont_use; |
92 | | |
93 | | uint8_t id[32]; |
94 | | |
95 | | uint8_t *buffer; |
96 | | size_t bufsize; |
97 | | size_t used; |
98 | | } fr_packet_socket_t; |
99 | | |
100 | | |
101 | 0 | #define FNV_MAGIC_PRIME (0x01000193) |
102 | 0 | #define MAX_SOCKETS (256) |
103 | 0 | #define SOCKOFFSET_MASK (MAX_SOCKETS - 1) |
104 | 0 | #define SOCK2OFFSET(_sockfd) ((_sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK) |
105 | | |
106 | | /* |
107 | | * Structure defining a list of packets (incoming or outgoing) |
108 | | * that should be managed. |
109 | | */ |
110 | | struct fr_packet_list_s { |
111 | | fr_rb_tree_t *tree; |
112 | | |
113 | | int alloc_id; |
114 | | uint32_t num_outgoing; |
115 | | int last_recv; |
116 | | int num_sockets; |
117 | | |
118 | | fr_packet_socket_t sockets[MAX_SOCKETS]; |
119 | | }; |
120 | | |
121 | | |
122 | | /* |
123 | | * Ugh. Doing this on every sent/received packet is not nice. |
124 | | */ |
125 | | static fr_packet_socket_t *fr_socket_find(fr_packet_list_t *pl, int sockfd) |
126 | 0 | { |
127 | 0 | int i, start; |
128 | |
|
129 | 0 | i = start = SOCK2OFFSET(sockfd); |
130 | |
|
131 | 0 | do { /* make this hack slightly more efficient */ |
132 | 0 | if (pl->sockets[i].socket.fd == sockfd) return &pl->sockets[i]; |
133 | | |
134 | 0 | i = (i + 1) & SOCKOFFSET_MASK; |
135 | 0 | } while (i != start); |
136 | | |
137 | 0 | return NULL; |
138 | 0 | } |
139 | | |
140 | | bool fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd) |
141 | 0 | { |
142 | 0 | fr_packet_socket_t *ps; |
143 | |
|
144 | 0 | if (!pl) { |
145 | 0 | fr_strerror_const("Invalid argument"); |
146 | 0 | return false; |
147 | 0 | } |
148 | | |
149 | 0 | ps = fr_socket_find(pl, sockfd); |
150 | 0 | if (!ps) { |
151 | 0 | fr_strerror_const("No such socket"); |
152 | 0 | return false; |
153 | 0 | } |
154 | | |
155 | 0 | ps->dont_use = true; |
156 | 0 | return true; |
157 | 0 | } |
158 | | |
159 | | bool fr_packet_list_socket_thaw(fr_packet_list_t *pl, int sockfd) |
160 | 0 | { |
161 | 0 | fr_packet_socket_t *ps; |
162 | |
|
163 | 0 | if (!pl) return false; |
164 | | |
165 | 0 | ps = fr_socket_find(pl, sockfd); |
166 | 0 | if (!ps) return false; |
167 | | |
168 | 0 | ps->dont_use = false; |
169 | 0 | return true; |
170 | 0 | } |
171 | | |
172 | | |
173 | | bool fr_packet_list_socket_del(fr_packet_list_t *pl, int sockfd) |
174 | 0 | { |
175 | 0 | fr_packet_socket_t *ps; |
176 | |
|
177 | 0 | if (!pl) return false; |
178 | | |
179 | 0 | ps = fr_socket_find(pl, sockfd); |
180 | 0 | if (!ps) return false; |
181 | | |
182 | 0 | if (ps->num_outgoing != 0) return false; |
183 | | |
184 | 0 | TALLOC_FREE(ps->buffer); |
185 | |
|
186 | 0 | ps->socket.fd = -1; |
187 | 0 | pl->num_sockets--; |
188 | |
|
189 | 0 | return true; |
190 | 0 | } |
191 | | |
192 | | |
193 | | bool fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd, int proto, |
194 | | fr_ipaddr_t *dst_ipaddr, uint16_t dst_port, |
195 | | void *ctx) |
196 | 0 | { |
197 | 0 | int i, start; |
198 | 0 | struct sockaddr_storage src; |
199 | 0 | socklen_t sizeof_src; |
200 | 0 | fr_packet_socket_t *ps; |
201 | |
|
202 | 0 | if (!pl || !dst_ipaddr || (dst_ipaddr->af == AF_UNSPEC)) { |
203 | 0 | fr_strerror_const("Invalid argument"); |
204 | 0 | return false; |
205 | 0 | } |
206 | | |
207 | 0 | if (pl->num_sockets >= MAX_SOCKETS) { |
208 | 0 | fr_strerror_const("Too many open sockets"); |
209 | 0 | return false; |
210 | 0 | } |
211 | | |
212 | 0 | ps = NULL; |
213 | 0 | i = start = SOCK2OFFSET(sockfd); |
214 | |
|
215 | 0 | do { |
216 | 0 | if (pl->sockets[i].socket.fd == -1) { |
217 | 0 | ps = &pl->sockets[i]; |
218 | 0 | break; |
219 | 0 | } |
220 | | |
221 | 0 | i = (i + 1) & SOCKOFFSET_MASK; |
222 | 0 | } while (i != start); |
223 | |
|
224 | 0 | if (!ps) { |
225 | 0 | fr_strerror_const("All socket entries are full"); |
226 | 0 | return false; |
227 | 0 | } |
228 | | |
229 | 0 | memset(ps, 0, sizeof(*ps)); |
230 | 0 | ps->socket.fd = -1; |
231 | 0 | ps->ctx = ctx; |
232 | 0 | ps->socket.type = (proto == IPPROTO_TCP) ? SOCK_STREAM : SOCK_DGRAM; |
233 | | |
234 | | /* |
235 | | * Get address family, etc. first, so we know if we |
236 | | * need to do udpfromto. |
237 | | * |
238 | | * FIXME: udpfromto also does this, but it's not |
239 | | * a critical problem. |
240 | | */ |
241 | 0 | sizeof_src = sizeof(src); |
242 | 0 | memset(&src, 0, sizeof_src); |
243 | 0 | if (getsockname(sockfd, (struct sockaddr *) &src, &sizeof_src) < 0) { |
244 | 0 | fr_strerror_printf("%s", fr_syserror(errno)); |
245 | 0 | return false; |
246 | 0 | } |
247 | | |
248 | 0 | if (fr_ipaddr_from_sockaddr(&ps->socket.inet.src_ipaddr, &ps->socket.inet.src_port, &src, sizeof_src) < 0) { |
249 | 0 | fr_strerror_const("Failed to get IP"); |
250 | 0 | return false; |
251 | 0 | } |
252 | | |
253 | 0 | ps->socket.inet.dst_ipaddr = *dst_ipaddr; |
254 | 0 | ps->socket.inet.dst_port = dst_port; |
255 | |
|
256 | 0 | ps->src_any = fr_ipaddr_is_inaddr_any(&ps->socket.inet.src_ipaddr); |
257 | 0 | if (ps->src_any < 0) return false; |
258 | | |
259 | 0 | ps->dst_any = fr_ipaddr_is_inaddr_any(&ps->socket.inet.dst_ipaddr); |
260 | 0 | if (ps->dst_any < 0) return false; |
261 | | |
262 | 0 | if (proto == IPPROTO_TCP) { |
263 | 0 | ps->buffer = talloc_array(pl, uint8_t, RADIUS_MAX_PACKET_SIZE); |
264 | 0 | if (ps->buffer) return false; |
265 | | |
266 | 0 | ps->bufsize = RADIUS_MAX_PACKET_SIZE; |
267 | 0 | ps->used = 0; |
268 | 0 | } |
269 | | |
270 | | /* |
271 | | * As the last step before returning. |
272 | | */ |
273 | 0 | ps->socket.fd = sockfd; |
274 | 0 | pl->num_sockets++; |
275 | |
|
276 | 0 | return true; |
277 | 0 | } |
278 | | |
279 | | void fr_packet_list_free(fr_packet_list_t *pl) |
280 | 0 | { |
281 | 0 | if (!pl) return; |
282 | | |
283 | 0 | talloc_free(pl->tree); |
284 | 0 | talloc_free(pl); |
285 | 0 | } |
286 | | |
287 | | |
288 | | /* |
289 | | * Caller is responsible for managing the packet entries. |
290 | | */ |
291 | | fr_packet_list_t *fr_packet_list_create(int alloc_id) |
292 | 0 | { |
293 | 0 | int i; |
294 | 0 | fr_packet_list_t *pl; |
295 | |
|
296 | 0 | pl = talloc_zero(NULL, fr_packet_list_t); |
297 | 0 | if (!pl) return NULL; |
298 | 0 | pl->tree = fr_rb_inline_alloc(pl, fr_packet_t, node, fr_packet_cmp, NULL); /* elements not talloc safe */ |
299 | 0 | if (!pl->tree) { |
300 | 0 | fr_packet_list_free(pl); |
301 | 0 | return NULL; |
302 | 0 | } |
303 | | |
304 | 0 | for (i = 0; i < MAX_SOCKETS; i++) { |
305 | 0 | pl->sockets[i].socket.fd = -1; |
306 | 0 | } |
307 | |
|
308 | 0 | pl->alloc_id = alloc_id; |
309 | |
|
310 | 0 | return pl; |
311 | 0 | } |
312 | | |
313 | | |
314 | | /* |
315 | | * If pl->alloc_id is set, then fr_packet_list_id_alloc() MUST |
316 | | * be called before inserting the packet into the list! |
317 | | */ |
318 | | bool fr_packet_list_insert(fr_packet_list_t *pl, |
319 | | fr_packet_t *request) |
320 | 0 | { |
321 | 0 | if (!pl || !request) return 0; |
322 | | |
323 | 0 | return fr_rb_insert(pl->tree, request); |
324 | 0 | } |
325 | | |
326 | | fr_packet_t *fr_packet_list_find(fr_packet_list_t *pl, fr_packet_t *request) |
327 | 0 | { |
328 | 0 | if (!pl || !request) return 0; |
329 | | |
330 | 0 | return fr_rb_find(pl->tree, request); |
331 | 0 | } |
332 | | |
333 | | |
334 | | /* |
335 | | * This presumes that the reply has dst_ipaddr && dst_port set up |
336 | | * correctly (i.e. real IP, or "*"). |
337 | | */ |
338 | | fr_packet_t *fr_packet_list_find_byreply(fr_packet_list_t *pl, fr_packet_t *reply) |
339 | 0 | { |
340 | 0 | fr_packet_t my_request, *request; |
341 | 0 | fr_packet_socket_t *ps; |
342 | |
|
343 | 0 | if (!pl || !reply) return NULL; |
344 | | |
345 | 0 | ps = fr_socket_find(pl, reply->socket.fd); |
346 | 0 | if (!ps) return NULL; |
347 | | |
348 | | /* |
349 | | * TCP sockets are always bound to the correct src/dst IP/port |
350 | | */ |
351 | 0 | if (ps->socket.type == SOCK_STREAM) { |
352 | 0 | fr_socket_addr_swap(&reply->socket, &ps->socket); |
353 | 0 | my_request.socket = ps->socket; |
354 | 0 | } else { |
355 | 0 | my_request.socket = ps->socket; |
356 | |
|
357 | 0 | if (!ps->src_any) my_request.socket.inet.src_ipaddr = reply->socket.inet.dst_ipaddr; |
358 | 0 | my_request.socket.inet.dst_ipaddr = reply->socket.inet.src_ipaddr; |
359 | 0 | my_request.socket.inet.dst_port = reply->socket.inet.src_port; |
360 | 0 | } |
361 | | |
362 | | /* |
363 | | * Initialize request from reply, AND from the source |
364 | | * IP & port of this socket. The client may have bound |
365 | | * the socket to 0, in which case it's some random port, |
366 | | * that is NOT in the original request->socket.inet.src_port. |
367 | | */ |
368 | 0 | my_request.socket.fd = reply->socket.fd; |
369 | 0 | my_request.id = reply->id; |
370 | 0 | request = &my_request; |
371 | |
|
372 | 0 | return fr_rb_find(pl->tree, request); |
373 | 0 | } |
374 | | |
375 | | |
376 | | bool fr_packet_list_yank(fr_packet_list_t *pl, fr_packet_t *request) |
377 | 0 | { |
378 | 0 | if (!pl || !request) return false; |
379 | | |
380 | 0 | return fr_rb_delete(pl->tree, request); |
381 | 0 | } |
382 | | |
383 | | uint32_t fr_packet_list_num_elements(fr_packet_list_t *pl) |
384 | 0 | { |
385 | 0 | if (!pl) return 0; |
386 | | |
387 | 0 | return fr_rb_num_elements(pl->tree); |
388 | 0 | } |
389 | | |
390 | | |
391 | | /* |
392 | | * 1 == ID was allocated & assigned |
393 | | * 0 == couldn't allocate ID. |
394 | | * |
395 | | * Note that this ALSO assigns a socket to use, and updates |
396 | | * packet->request->socket.inet.src_ipaddr && packet->request->socket.inet.src_port |
397 | | * |
398 | | * In multi-threaded systems, the calls to id_alloc && id_free |
399 | | * should be protected by a mutex. This does NOT have to be |
400 | | * the same mutex as the one protecting the insert/find/yank |
401 | | * calls! |
402 | | * |
403 | | * We assume that the packet has dst_ipaddr && dst_port |
404 | | * already initialized. We will use those to find an |
405 | | * outgoing socket. The request MAY also have src_ipaddr set. |
406 | | * |
407 | | * We also assume that the sender doesn't care which protocol |
408 | | * should be used. |
409 | | */ |
410 | | bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto, |
411 | | fr_packet_t *request, void **pctx) |
412 | 0 | { |
413 | 0 | int i, j, k, fd, id, start_i, start_j, start_k; |
414 | 0 | int src_any = 0; |
415 | 0 | int type; |
416 | 0 | fr_packet_socket_t *ps= NULL; |
417 | |
|
418 | 0 | if ((request->socket.inet.dst_ipaddr.af == AF_UNSPEC) || |
419 | 0 | (request->socket.inet.dst_port == 0)) { |
420 | 0 | fr_strerror_const("No destination address/port specified"); |
421 | 0 | return false; |
422 | 0 | } |
423 | | |
424 | | /* |
425 | | * Special case: unspec == "don't care" |
426 | | */ |
427 | 0 | if (request->socket.inet.src_ipaddr.af == AF_UNSPEC) { |
428 | 0 | memset(&request->socket.inet.src_ipaddr, 0, sizeof(request->socket.inet.src_ipaddr)); |
429 | 0 | request->socket.inet.src_ipaddr.af = request->socket.inet.dst_ipaddr.af; |
430 | 0 | } |
431 | |
|
432 | 0 | src_any = fr_ipaddr_is_inaddr_any(&request->socket.inet.src_ipaddr); |
433 | 0 | if (src_any < 0) { |
434 | 0 | fr_strerror_const("Can't check src_ipaddr"); |
435 | 0 | return false; |
436 | 0 | } |
437 | | |
438 | | /* |
439 | | * MUST specify a destination address. |
440 | | */ |
441 | 0 | if (fr_ipaddr_is_inaddr_any(&request->socket.inet.dst_ipaddr) != 0) { |
442 | 0 | fr_strerror_const("Must specify a dst_ipaddr"); |
443 | 0 | return false; |
444 | 0 | } |
445 | | |
446 | | /* |
447 | | * FIXME: Go to an LRU system. This prevents ID reuse |
448 | | * for as long as possible. The main problem with that |
449 | | * approach is that it requires us to populate the |
450 | | * LRU/FIFO when we add a new socket, or a new destination, |
451 | | * which can be expensive. |
452 | | * |
453 | | * The LRU can be avoided if the caller takes care to free |
454 | | * Id's only when all responses have been received, OR after |
455 | | * a timeout. |
456 | | * |
457 | | * Right now, the random approach is almost OK... it's |
458 | | * brute-force over all of the available ID's, BUT using |
459 | | * random numbers for everything spreads the load a bit. |
460 | | * |
461 | | * The old method had a hash lookup on allocation AND |
462 | | * on free. The new method has brute-force on allocation, |
463 | | * and near-zero cost on free. |
464 | | */ |
465 | | |
466 | 0 | id = fd = -1; |
467 | 0 | if (request->id >= 0 && request->id < 256) |
468 | 0 | id = request->id; |
469 | 0 | start_i = fr_rand() & SOCKOFFSET_MASK; |
470 | |
|
471 | 0 | type = (proto == IPPROTO_TCP) ? SOCK_STREAM : SOCK_DGRAM; |
472 | |
|
473 | 0 | #define ID_i ((i + start_i) & SOCKOFFSET_MASK) |
474 | 0 | for (i = 0; i < MAX_SOCKETS; i++) { |
475 | 0 | if (pl->sockets[ID_i].socket.fd == -1) continue; /* paranoia */ |
476 | | |
477 | 0 | ps = &(pl->sockets[ID_i]); |
478 | | |
479 | | /* |
480 | | * This socket is marked as "don't use for new |
481 | | * packets". But we can still receive packets |
482 | | * that are outstanding. |
483 | | */ |
484 | 0 | if (ps->dont_use) continue; |
485 | | |
486 | | /* |
487 | | * All IDs are allocated: ignore it. |
488 | | */ |
489 | 0 | if (ps->num_outgoing == 256) continue; |
490 | | |
491 | 0 | if (ps->socket.type != type) continue; |
492 | | |
493 | | /* |
494 | | * Address families don't match, skip it. |
495 | | */ |
496 | 0 | if (ps->socket.inet.src_ipaddr.af != request->socket.inet.dst_ipaddr.af) continue; |
497 | | |
498 | | /* |
499 | | * MUST match dst port, if we have one. |
500 | | */ |
501 | 0 | if ((ps->socket.inet.dst_port != 0) && |
502 | 0 | (ps->socket.inet.dst_port != request->socket.inet.dst_port)) continue; |
503 | | |
504 | | /* |
505 | | * MUST match requested src port, if one has been given. |
506 | | */ |
507 | 0 | if ((request->socket.inet.src_port != 0) && |
508 | 0 | (ps->socket.inet.src_port != request->socket.inet.src_port)) continue; |
509 | | |
510 | | /* |
511 | | * We don't care about the source IP, but this |
512 | | * socket is link local, and the requested |
513 | | * destination is not link local. Ignore it. |
514 | | */ |
515 | 0 | if (src_any && (ps->socket.inet.src_ipaddr.af == AF_INET) && |
516 | 0 | (((ps->socket.inet.src_ipaddr.addr.v4.s_addr >> 24) & 0xff) == 127) && |
517 | 0 | (((request->socket.inet.dst_ipaddr.addr.v4.s_addr >> 24) & 0xff) != 127)) continue; |
518 | | |
519 | | /* |
520 | | * We're sourcing from *, and they asked for a |
521 | | * specific source address: ignore it. |
522 | | */ |
523 | 0 | if (ps->src_any && !src_any) continue; |
524 | | |
525 | | /* |
526 | | * We're sourcing from a specific IP, and they |
527 | | * asked for a source IP that isn't us: ignore |
528 | | * it. |
529 | | */ |
530 | 0 | if (!ps->src_any && !src_any && |
531 | 0 | (fr_ipaddr_cmp(&request->socket.inet.src_ipaddr, |
532 | 0 | &ps->socket.inet.src_ipaddr) != 0)) continue; |
533 | | |
534 | | /* |
535 | | * UDP sockets are allowed to match |
536 | | * destination IPs exactly, OR a socket |
537 | | * with destination * is allowed to match |
538 | | * any requested destination. |
539 | | * |
540 | | * TCP sockets must match the destination |
541 | | * exactly. They *always* have dst_any=0, |
542 | | * so the first check always matches. |
543 | | */ |
544 | 0 | if (!ps->dst_any && |
545 | 0 | (fr_ipaddr_cmp(&request->socket.inet.dst_ipaddr, |
546 | 0 | &ps->socket.inet.dst_ipaddr) != 0)) continue; |
547 | | |
548 | | /* |
549 | | * Otherwise, this socket is OK to use. |
550 | | */ |
551 | | |
552 | | /* |
553 | | * An explicit ID was requested |
554 | | */ |
555 | | |
556 | 0 | if (id != -1) { |
557 | 0 | if ((ps->id[(id >> 3) & 0x1f] & (1 << (id & 0x07))) != 0) continue; |
558 | | |
559 | 0 | ps->id[(id >> 3) & 0x1f] |= (1 << (id & 0x07)); |
560 | 0 | fd = i; |
561 | 0 | break; |
562 | 0 | } |
563 | | |
564 | | /* |
565 | | * Look for a free Id, starting from a random number. |
566 | | */ |
567 | 0 | start_j = fr_rand() & 0x1f; |
568 | 0 | #define ID_j ((j + start_j) & 0x1f) |
569 | 0 | for (j = 0; j < 32; j++) { |
570 | 0 | if (ps->id[ID_j] == 0xff) continue; |
571 | | |
572 | | |
573 | 0 | start_k = fr_rand() & 0x07; |
574 | 0 | #define ID_k ((k + start_k) & 0x07) |
575 | 0 | for (k = 0; k < 8; k++) { |
576 | 0 | if ((ps->id[ID_j] & (1 << ID_k)) != 0) continue; |
577 | | |
578 | 0 | ps->id[ID_j] |= (1 << ID_k); |
579 | 0 | id = (ID_j * 8) + ID_k; |
580 | 0 | fd = i; |
581 | 0 | break; |
582 | 0 | } |
583 | 0 | if (fd >= 0) break; |
584 | 0 | } |
585 | 0 | #undef ID_i |
586 | 0 | #undef ID_j |
587 | 0 | #undef ID_k |
588 | 0 | break; |
589 | 0 | } |
590 | | |
591 | | /* |
592 | | * Ask the caller to allocate a new ID. |
593 | | */ |
594 | 0 | if (fd < 0) { |
595 | 0 | fr_strerror_const("Failed finding socket, caller must allocate a new one"); |
596 | 0 | return false; |
597 | 0 | } |
598 | | |
599 | | /* |
600 | | * Set the ID, source IP, and source port. |
601 | | */ |
602 | 0 | request->id = id; |
603 | |
|
604 | 0 | request->socket.fd = ps->socket.fd; |
605 | 0 | request->socket.inet.src_ipaddr = ps->socket.inet.src_ipaddr; |
606 | 0 | request->socket.inet.src_port = ps->socket.inet.src_port; |
607 | | |
608 | | /* |
609 | | * If we managed to insert it, we're done. |
610 | | */ |
611 | 0 | if (fr_packet_list_insert(pl, request)) { |
612 | 0 | if (pctx) *pctx = ps->ctx; |
613 | 0 | ps->num_outgoing++; |
614 | 0 | pl->num_outgoing++; |
615 | 0 | return true; |
616 | 0 | } |
617 | | |
618 | | /* |
619 | | * Mark the ID as free. This is the one line from |
620 | | * id_free() that we care about here. |
621 | | */ |
622 | 0 | ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07)); |
623 | |
|
624 | 0 | request->id = -1; |
625 | 0 | request->socket.fd = -1; |
626 | 0 | request->socket.inet.src_ipaddr.af = AF_UNSPEC; |
627 | 0 | request->socket.inet.src_port = 0; |
628 | |
|
629 | 0 | return false; |
630 | 0 | } |
631 | | |
632 | | /* |
633 | | * Should be called AFTER yanking it from the list, so that |
634 | | * any newly inserted entries don't collide with this one. |
635 | | */ |
636 | | bool fr_packet_list_id_free(fr_packet_list_t *pl, |
637 | | fr_packet_t *request, bool yank) |
638 | 0 | { |
639 | 0 | fr_packet_socket_t *ps; |
640 | |
|
641 | 0 | if (!pl || !request) return false; |
642 | | |
643 | 0 | if (yank && !fr_packet_list_yank(pl, request)) return false; |
644 | | |
645 | 0 | ps = fr_socket_find(pl, request->socket.fd); |
646 | 0 | if (!ps) return false; |
647 | | |
648 | 0 | ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07)); |
649 | |
|
650 | 0 | ps->num_outgoing--; |
651 | 0 | pl->num_outgoing--; |
652 | |
|
653 | 0 | request->id = -1; |
654 | 0 | request->socket.inet.src_ipaddr.af = AF_UNSPEC; /* id_alloc checks this */ |
655 | 0 | request->socket.inet.src_port = 0; |
656 | |
|
657 | 0 | return true; |
658 | 0 | } |
659 | | |
660 | | int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set) |
661 | 0 | { |
662 | 0 | int i, maxfd; |
663 | |
|
664 | 0 | if (!pl || !set) return 0; |
665 | | |
666 | 0 | maxfd = -1; |
667 | |
|
668 | 0 | for (i = 0; i < MAX_SOCKETS; i++) { |
669 | 0 | if (pl->sockets[i].socket.fd == -1) continue; |
670 | 0 | FD_SET(pl->sockets[i].socket.fd, set); |
671 | 0 | if (pl->sockets[i].socket.fd > maxfd) { |
672 | 0 | maxfd = pl->sockets[i].socket.fd; |
673 | 0 | } |
674 | 0 | } |
675 | |
|
676 | 0 | if (maxfd < 0) return -1; |
677 | | |
678 | 0 | return maxfd + 1; |
679 | 0 | } |
680 | | |
681 | | /* |
682 | | * Round-robins the receivers, without priority. |
683 | | * |
684 | | * FIXME: Add socket.fd, if -1, do round-robin, else do socket.fd |
685 | | * IF in fdset. |
686 | | */ |
687 | | int fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set, TALLOC_CTX *ctx, fr_packet_t **packet_p, uint32_t max_attributes, bool require_message_authenticator) |
688 | 0 | { |
689 | 0 | int start; |
690 | 0 | fr_packet_t *packet; |
691 | |
|
692 | 0 | if (!pl || !set) return 0; |
693 | | |
694 | 0 | start = pl->last_recv; |
695 | 0 | do { |
696 | 0 | fr_packet_socket_t *ps; |
697 | |
|
698 | 0 | start++; |
699 | 0 | start &= SOCKOFFSET_MASK; |
700 | |
|
701 | 0 | if (pl->sockets[start].socket.fd == -1) continue; |
702 | | |
703 | 0 | if (!FD_ISSET(pl->sockets[start].socket.fd, set)) continue; |
704 | | |
705 | 0 | if (pl->sockets[start].socket.type == SOCK_STREAM) { |
706 | 0 | int rcode; |
707 | 0 | ps = &pl->sockets[start]; |
708 | |
|
709 | 0 | rcode = fr_tcp_read_packet(ps->socket.fd, ps->buffer, ps->bufsize, |
710 | 0 | &ps->used, max_attributes, require_message_authenticator); |
711 | 0 | if (rcode <= 0) return rcode; |
712 | | |
713 | 0 | fr_assert(ps->used >= RADIUS_HEADER_LENGTH); |
714 | |
|
715 | 0 | packet = fr_packet_alloc(ctx, false); |
716 | 0 | if (!packet) return -1; |
717 | | |
718 | 0 | packet->data = talloc_memdup(packet, ps->buffer, rcode); |
719 | 0 | if (!packet->data) { |
720 | 0 | talloc_free(packet); |
721 | 0 | return -1; |
722 | 0 | } |
723 | | |
724 | 0 | packet->data_len = rcode; |
725 | 0 | packet->socket = ps->socket; |
726 | |
|
727 | 0 | if (ps->used == (size_t) rcode) { |
728 | 0 | ps->used = 0; |
729 | 0 | } else { |
730 | 0 | fr_assert(ps->used > (size_t) rcode); |
731 | |
|
732 | 0 | memmove(ps->buffer, ps->buffer + rcode, ps->used - rcode); |
733 | 0 | ps->used -= rcode; |
734 | 0 | } |
735 | |
|
736 | 0 | } else { |
737 | 0 | packet = fr_packet_recv(ctx, pl->sockets[start].socket.fd, UDP_FLAGS_NONE, |
738 | 0 | max_attributes, require_message_authenticator); |
739 | 0 | } |
740 | 0 | if (!packet) continue; |
741 | | |
742 | | /* |
743 | | * Call fr_packet_list_find_byreply(). If it |
744 | | * doesn't find anything, discard the reply. |
745 | | */ |
746 | | |
747 | 0 | pl->last_recv = start; |
748 | 0 | packet->socket.type = pl->sockets[start].socket.type; |
749 | 0 | *packet_p = packet; |
750 | 0 | return 0; |
751 | 0 | } while (start != pl->last_recv); |
752 | | |
753 | 0 | return 0; |
754 | 0 | } |
755 | | |
756 | | uint32_t fr_packet_list_num_incoming(fr_packet_list_t *pl) |
757 | 0 | { |
758 | 0 | uint32_t num_elements; |
759 | |
|
760 | 0 | if (!pl) return 0; |
761 | | |
762 | 0 | num_elements = fr_rb_num_elements(pl->tree); |
763 | 0 | if (num_elements < pl->num_outgoing) return 0; /* panic! */ |
764 | | |
765 | 0 | return num_elements - pl->num_outgoing; |
766 | 0 | } |
767 | | |
768 | | uint32_t fr_packet_list_num_outgoing(fr_packet_list_t *pl) |
769 | 0 | { |
770 | 0 | if (!pl) return 0; |
771 | | |
772 | 0 | return pl->num_outgoing; |
773 | 0 | } |