/src/freeradius-server/src/protocols/radius/list.c
Line | Count | Source (jump to first uncovered line) |
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: bf55a435d74e0f704ebbb808ac21622739e7bc11 $ |
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: bf55a435d74e0f704ebbb808ac21622739e7bc11 $") |
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_radius_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_radius_packet_t *request, |
73 | | fr_radius_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 | | } fr_packet_socket_t; |
95 | | |
96 | | |
97 | 0 | #define FNV_MAGIC_PRIME (0x01000193) |
98 | 0 | #define MAX_SOCKETS (256) |
99 | 0 | #define SOCKOFFSET_MASK (MAX_SOCKETS - 1) |
100 | 0 | #define SOCK2OFFSET(_sockfd) ((_sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK) |
101 | | |
102 | | /* |
103 | | * Structure defining a list of packets (incoming or outgoing) |
104 | | * that should be managed. |
105 | | */ |
106 | | struct fr_packet_list_s { |
107 | | fr_rb_tree_t *tree; |
108 | | |
109 | | int alloc_id; |
110 | | uint32_t num_outgoing; |
111 | | int last_recv; |
112 | | int num_sockets; |
113 | | |
114 | | fr_packet_socket_t sockets[MAX_SOCKETS]; |
115 | | }; |
116 | | |
117 | | |
118 | | /* |
119 | | * Ugh. Doing this on every sent/received packet is not nice. |
120 | | */ |
121 | | static fr_packet_socket_t *fr_socket_find(fr_packet_list_t *pl, int sockfd) |
122 | 0 | { |
123 | 0 | int i, start; |
124 | |
|
125 | 0 | i = start = SOCK2OFFSET(sockfd); |
126 | |
|
127 | 0 | do { /* make this hack slightly more efficient */ |
128 | 0 | if (pl->sockets[i].socket.fd == sockfd) return &pl->sockets[i]; |
129 | | |
130 | 0 | i = (i + 1) & SOCKOFFSET_MASK; |
131 | 0 | } while (i != start); |
132 | | |
133 | 0 | return NULL; |
134 | 0 | } |
135 | | |
136 | | bool fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd) |
137 | 0 | { |
138 | 0 | fr_packet_socket_t *ps; |
139 | |
|
140 | 0 | if (!pl) { |
141 | 0 | fr_strerror_const("Invalid argument"); |
142 | 0 | return false; |
143 | 0 | } |
144 | | |
145 | 0 | ps = fr_socket_find(pl, sockfd); |
146 | 0 | if (!ps) { |
147 | 0 | fr_strerror_const("No such socket"); |
148 | 0 | return false; |
149 | 0 | } |
150 | | |
151 | 0 | ps->dont_use = true; |
152 | 0 | return true; |
153 | 0 | } |
154 | | |
155 | | bool fr_packet_list_socket_thaw(fr_packet_list_t *pl, int sockfd) |
156 | 0 | { |
157 | 0 | fr_packet_socket_t *ps; |
158 | |
|
159 | 0 | if (!pl) return false; |
160 | | |
161 | 0 | ps = fr_socket_find(pl, sockfd); |
162 | 0 | if (!ps) return false; |
163 | | |
164 | 0 | ps->dont_use = false; |
165 | 0 | return true; |
166 | 0 | } |
167 | | |
168 | | |
169 | | bool fr_packet_list_socket_del(fr_packet_list_t *pl, int sockfd) |
170 | 0 | { |
171 | 0 | fr_packet_socket_t *ps; |
172 | |
|
173 | 0 | if (!pl) return false; |
174 | | |
175 | 0 | ps = fr_socket_find(pl, sockfd); |
176 | 0 | if (!ps) return false; |
177 | | |
178 | 0 | if (ps->num_outgoing != 0) return false; |
179 | | |
180 | 0 | ps->socket.fd = -1; |
181 | 0 | pl->num_sockets--; |
182 | |
|
183 | 0 | return true; |
184 | 0 | } |
185 | | |
186 | | |
187 | | bool fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd, int proto, |
188 | | fr_ipaddr_t *dst_ipaddr, uint16_t dst_port, |
189 | | void *ctx) |
190 | 0 | { |
191 | 0 | int i, start; |
192 | 0 | struct sockaddr_storage src; |
193 | 0 | socklen_t sizeof_src; |
194 | 0 | fr_packet_socket_t *ps; |
195 | |
|
196 | 0 | if (!pl || !dst_ipaddr || (dst_ipaddr->af == AF_UNSPEC)) { |
197 | 0 | fr_strerror_const("Invalid argument"); |
198 | 0 | return false; |
199 | 0 | } |
200 | | |
201 | 0 | if (pl->num_sockets >= MAX_SOCKETS) { |
202 | 0 | fr_strerror_const("Too many open sockets"); |
203 | 0 | return false; |
204 | 0 | } |
205 | | |
206 | 0 | ps = NULL; |
207 | 0 | i = start = SOCK2OFFSET(sockfd); |
208 | |
|
209 | 0 | do { |
210 | 0 | if (pl->sockets[i].socket.fd == -1) { |
211 | 0 | ps = &pl->sockets[i]; |
212 | 0 | break; |
213 | 0 | } |
214 | | |
215 | 0 | i = (i + 1) & SOCKOFFSET_MASK; |
216 | 0 | } while (i != start); |
217 | | |
218 | 0 | if (!ps) { |
219 | 0 | fr_strerror_const("All socket entries are full"); |
220 | 0 | return false; |
221 | 0 | } |
222 | | |
223 | 0 | memset(ps, 0, sizeof(*ps)); |
224 | 0 | ps->ctx = ctx; |
225 | 0 | ps->socket.proto = proto; |
226 | | |
227 | | /* |
228 | | * Get address family, etc. first, so we know if we |
229 | | * need to do udpfromto. |
230 | | * |
231 | | * FIXME: udpfromto also does this, but it's not |
232 | | * a critical problem. |
233 | | */ |
234 | 0 | sizeof_src = sizeof(src); |
235 | 0 | memset(&src, 0, sizeof_src); |
236 | 0 | if (getsockname(sockfd, (struct sockaddr *) &src, &sizeof_src) < 0) { |
237 | 0 | fr_strerror_printf("%s", fr_syserror(errno)); |
238 | 0 | return false; |
239 | 0 | } |
240 | | |
241 | 0 | if (fr_ipaddr_from_sockaddr(&ps->socket.inet.src_ipaddr, &ps->socket.inet.src_port, &src, sizeof_src) < 0) { |
242 | 0 | fr_strerror_const("Failed to get IP"); |
243 | 0 | return false; |
244 | 0 | } |
245 | | |
246 | 0 | ps->socket.inet.dst_ipaddr = *dst_ipaddr; |
247 | 0 | ps->socket.inet.dst_port = dst_port; |
248 | |
|
249 | 0 | ps->src_any = fr_ipaddr_is_inaddr_any(&ps->socket.inet.src_ipaddr); |
250 | 0 | if (ps->src_any < 0) return false; |
251 | | |
252 | 0 | ps->dst_any = fr_ipaddr_is_inaddr_any(&ps->socket.inet.dst_ipaddr); |
253 | 0 | if (ps->dst_any < 0) return false; |
254 | | |
255 | | /* |
256 | | * As the last step before returning. |
257 | | */ |
258 | 0 | ps->socket.fd = sockfd; |
259 | 0 | pl->num_sockets++; |
260 | |
|
261 | 0 | return true; |
262 | 0 | } |
263 | | |
264 | | void fr_packet_list_free(fr_packet_list_t *pl) |
265 | 0 | { |
266 | 0 | if (!pl) return; |
267 | | |
268 | 0 | talloc_free(pl->tree); |
269 | 0 | talloc_free(pl); |
270 | 0 | } |
271 | | |
272 | | |
273 | | /* |
274 | | * Caller is responsible for managing the packet entries. |
275 | | */ |
276 | | fr_packet_list_t *fr_packet_list_create(int alloc_id) |
277 | 0 | { |
278 | 0 | int i; |
279 | 0 | fr_packet_list_t *pl; |
280 | |
|
281 | 0 | pl = talloc_zero(NULL, fr_packet_list_t); |
282 | 0 | if (!pl) return NULL; |
283 | 0 | pl->tree = fr_rb_inline_alloc(pl, fr_radius_packet_t, node, fr_packet_cmp, NULL); /* elements not talloc safe */ |
284 | 0 | if (!pl->tree) { |
285 | 0 | fr_packet_list_free(pl); |
286 | 0 | return NULL; |
287 | 0 | } |
288 | | |
289 | 0 | for (i = 0; i < MAX_SOCKETS; i++) { |
290 | 0 | pl->sockets[i].socket.fd = -1; |
291 | 0 | } |
292 | |
|
293 | 0 | pl->alloc_id = alloc_id; |
294 | |
|
295 | 0 | return pl; |
296 | 0 | } |
297 | | |
298 | | |
299 | | /* |
300 | | * If pl->alloc_id is set, then fr_packet_list_id_alloc() MUST |
301 | | * be called before inserting the packet into the list! |
302 | | */ |
303 | | bool fr_packet_list_insert(fr_packet_list_t *pl, |
304 | | fr_radius_packet_t *request) |
305 | 0 | { |
306 | 0 | if (!pl || !request) return 0; |
307 | | |
308 | 0 | return fr_rb_insert(pl->tree, request); |
309 | 0 | } |
310 | | |
311 | | fr_radius_packet_t *fr_packet_list_find(fr_packet_list_t *pl, fr_radius_packet_t *request) |
312 | 0 | { |
313 | 0 | if (!pl || !request) return 0; |
314 | | |
315 | 0 | return fr_rb_find(pl->tree, request); |
316 | 0 | } |
317 | | |
318 | | |
319 | | /* |
320 | | * This presumes that the reply has dst_ipaddr && dst_port set up |
321 | | * correctly (i.e. real IP, or "*"). |
322 | | */ |
323 | | fr_radius_packet_t *fr_packet_list_find_byreply(fr_packet_list_t *pl, fr_radius_packet_t *reply) |
324 | 0 | { |
325 | 0 | fr_radius_packet_t my_request, *request; |
326 | 0 | fr_packet_socket_t *ps; |
327 | |
|
328 | 0 | if (!pl || !reply) return NULL; |
329 | | |
330 | 0 | ps = fr_socket_find(pl, reply->socket.fd); |
331 | 0 | if (!ps) return NULL; |
332 | | |
333 | | /* |
334 | | * TCP sockets are always bound to the correct src/dst IP/port |
335 | | */ |
336 | 0 | if (ps->socket.proto == IPPROTO_TCP) { |
337 | 0 | fr_socket_addr_swap(&reply->socket, &ps->socket); |
338 | 0 | my_request.socket = ps->socket; |
339 | 0 | } else { |
340 | 0 | my_request.socket = ps->socket; |
341 | |
|
342 | 0 | if (!ps->src_any) my_request.socket.inet.src_ipaddr = reply->socket.inet.dst_ipaddr; |
343 | 0 | my_request.socket.inet.dst_ipaddr = reply->socket.inet.src_ipaddr; |
344 | 0 | my_request.socket.inet.dst_port = reply->socket.inet.src_port; |
345 | 0 | } |
346 | | |
347 | | /* |
348 | | * Initialize request from reply, AND from the source |
349 | | * IP & port of this socket. The client may have bound |
350 | | * the socket to 0, in which case it's some random port, |
351 | | * that is NOT in the original request->socket.inet.src_port. |
352 | | */ |
353 | 0 | my_request.socket.fd = reply->socket.fd; |
354 | 0 | my_request.id = reply->id; |
355 | 0 | request = &my_request; |
356 | |
|
357 | 0 | return fr_rb_find(pl->tree, request); |
358 | 0 | } |
359 | | |
360 | | |
361 | | bool fr_packet_list_yank(fr_packet_list_t *pl, fr_radius_packet_t *request) |
362 | 0 | { |
363 | 0 | if (!pl || !request) return false; |
364 | | |
365 | 0 | return fr_rb_delete(pl->tree, request); |
366 | 0 | } |
367 | | |
368 | | uint32_t fr_packet_list_num_elements(fr_packet_list_t *pl) |
369 | 0 | { |
370 | 0 | if (!pl) return 0; |
371 | | |
372 | 0 | return fr_rb_num_elements(pl->tree); |
373 | 0 | } |
374 | | |
375 | | |
376 | | /* |
377 | | * 1 == ID was allocated & assigned |
378 | | * 0 == couldn't allocate ID. |
379 | | * |
380 | | * Note that this ALSO assigns a socket to use, and updates |
381 | | * packet->request->socket.inet.src_ipaddr && packet->request->socket.inet.src_port |
382 | | * |
383 | | * In multi-threaded systems, the calls to id_alloc && id_free |
384 | | * should be protected by a mutex. This does NOT have to be |
385 | | * the same mutex as the one protecting the insert/find/yank |
386 | | * calls! |
387 | | * |
388 | | * We assume that the packet has dst_ipaddr && dst_port |
389 | | * already initialized. We will use those to find an |
390 | | * outgoing socket. The request MAY also have src_ipaddr set. |
391 | | * |
392 | | * We also assume that the sender doesn't care which protocol |
393 | | * should be used. |
394 | | */ |
395 | | bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto, |
396 | | fr_radius_packet_t *request, void **pctx) |
397 | 0 | { |
398 | 0 | int i, j, k, fd, id, start_i, start_j, start_k; |
399 | 0 | int src_any = 0; |
400 | 0 | fr_packet_socket_t *ps= NULL; |
401 | |
|
402 | 0 | if ((request->socket.inet.dst_ipaddr.af == AF_UNSPEC) || |
403 | 0 | (request->socket.inet.dst_port == 0)) { |
404 | 0 | fr_strerror_const("No destination address/port specified"); |
405 | 0 | return false; |
406 | 0 | } |
407 | | |
408 | | /* |
409 | | * Special case: unspec == "don't care" |
410 | | */ |
411 | 0 | if (request->socket.inet.src_ipaddr.af == AF_UNSPEC) { |
412 | 0 | memset(&request->socket.inet.src_ipaddr, 0, sizeof(request->socket.inet.src_ipaddr)); |
413 | 0 | request->socket.inet.src_ipaddr.af = request->socket.inet.dst_ipaddr.af; |
414 | 0 | } |
415 | |
|
416 | 0 | src_any = fr_ipaddr_is_inaddr_any(&request->socket.inet.src_ipaddr); |
417 | 0 | if (src_any < 0) { |
418 | 0 | fr_strerror_const("Can't check src_ipaddr"); |
419 | 0 | return false; |
420 | 0 | } |
421 | | |
422 | | /* |
423 | | * MUST specify a destination address. |
424 | | */ |
425 | 0 | if (fr_ipaddr_is_inaddr_any(&request->socket.inet.dst_ipaddr) != 0) { |
426 | 0 | fr_strerror_const("Must specify a dst_ipaddr"); |
427 | 0 | return false; |
428 | 0 | } |
429 | | |
430 | | /* |
431 | | * FIXME: Go to an LRU system. This prevents ID re-use |
432 | | * for as long as possible. The main problem with that |
433 | | * approach is that it requires us to populate the |
434 | | * LRU/FIFO when we add a new socket, or a new destination, |
435 | | * which can be expensive. |
436 | | * |
437 | | * The LRU can be avoided if the caller takes care to free |
438 | | * Id's only when all responses have been received, OR after |
439 | | * a timeout. |
440 | | * |
441 | | * Right now, the random approach is almost OK... it's |
442 | | * brute-force over all of the available ID's, BUT using |
443 | | * random numbers for everything spreads the load a bit. |
444 | | * |
445 | | * The old method had a hash lookup on allocation AND |
446 | | * on free. The new method has brute-force on allocation, |
447 | | * and near-zero cost on free. |
448 | | */ |
449 | | |
450 | 0 | id = fd = -1; |
451 | 0 | if (request->id >= 0 && request->id < 256) |
452 | 0 | id = request->id; |
453 | 0 | start_i = fr_rand() & SOCKOFFSET_MASK; |
454 | |
|
455 | 0 | #define ID_i ((i + start_i) & SOCKOFFSET_MASK) |
456 | 0 | for (i = 0; i < MAX_SOCKETS; i++) { |
457 | 0 | if (pl->sockets[ID_i].socket.fd == -1) continue; /* paranoia */ |
458 | | |
459 | 0 | ps = &(pl->sockets[ID_i]); |
460 | | |
461 | | /* |
462 | | * This socket is marked as "don't use for new |
463 | | * packets". But we can still receive packets |
464 | | * that are outstanding. |
465 | | */ |
466 | 0 | if (ps->dont_use) continue; |
467 | | |
468 | | /* |
469 | | * All IDs are allocated: ignore it. |
470 | | */ |
471 | 0 | if (ps->num_outgoing == 256) continue; |
472 | | |
473 | 0 | if (ps->socket.proto != proto) continue; |
474 | | |
475 | | /* |
476 | | * Address families don't match, skip it. |
477 | | */ |
478 | 0 | if (ps->socket.inet.src_ipaddr.af != request->socket.inet.dst_ipaddr.af) continue; |
479 | | |
480 | | /* |
481 | | * MUST match dst port, if we have one. |
482 | | */ |
483 | 0 | if ((ps->socket.inet.dst_port != 0) && |
484 | 0 | (ps->socket.inet.dst_port != request->socket.inet.dst_port)) continue; |
485 | | |
486 | | /* |
487 | | * MUST match requested src port, if one has been given. |
488 | | */ |
489 | 0 | if ((request->socket.inet.src_port != 0) && |
490 | 0 | (ps->socket.inet.src_port != request->socket.inet.src_port)) continue; |
491 | | |
492 | | /* |
493 | | * We don't care about the source IP, but this |
494 | | * socket is link local, and the requested |
495 | | * destination is not link local. Ignore it. |
496 | | */ |
497 | 0 | if (src_any && (ps->socket.inet.src_ipaddr.af == AF_INET) && |
498 | 0 | (((ps->socket.inet.src_ipaddr.addr.v4.s_addr >> 24) & 0xff) == 127) && |
499 | 0 | (((request->socket.inet.dst_ipaddr.addr.v4.s_addr >> 24) & 0xff) != 127)) continue; |
500 | | |
501 | | /* |
502 | | * We're sourcing from *, and they asked for a |
503 | | * specific source address: ignore it. |
504 | | */ |
505 | 0 | if (ps->src_any && !src_any) continue; |
506 | | |
507 | | /* |
508 | | * We're sourcing from a specific IP, and they |
509 | | * asked for a source IP that isn't us: ignore |
510 | | * it. |
511 | | */ |
512 | 0 | if (!ps->src_any && !src_any && |
513 | 0 | (fr_ipaddr_cmp(&request->socket.inet.src_ipaddr, |
514 | 0 | &ps->socket.inet.src_ipaddr) != 0)) continue; |
515 | | |
516 | | /* |
517 | | * UDP sockets are allowed to match |
518 | | * destination IPs exactly, OR a socket |
519 | | * with destination * is allowed to match |
520 | | * any requested destination. |
521 | | * |
522 | | * TCP sockets must match the destination |
523 | | * exactly. They *always* have dst_any=0, |
524 | | * so the first check always matches. |
525 | | */ |
526 | 0 | if (!ps->dst_any && |
527 | 0 | (fr_ipaddr_cmp(&request->socket.inet.dst_ipaddr, |
528 | 0 | &ps->socket.inet.dst_ipaddr) != 0)) continue; |
529 | | |
530 | | /* |
531 | | * Otherwise, this socket is OK to use. |
532 | | */ |
533 | | |
534 | | /* |
535 | | * An explicit ID was requested |
536 | | */ |
537 | | |
538 | 0 | if (id != -1) { |
539 | 0 | if ((ps->id[(id >> 3) & 0x1f] & (1 << (id & 0x07))) != 0) continue; |
540 | | |
541 | 0 | ps->id[(id >> 3) & 0x1f] |= (1 << (id & 0x07)); |
542 | 0 | fd = i; |
543 | 0 | break; |
544 | 0 | } |
545 | | |
546 | | /* |
547 | | * Look for a free Id, starting from a random number. |
548 | | */ |
549 | 0 | start_j = fr_rand() & 0x1f; |
550 | 0 | #define ID_j ((j + start_j) & 0x1f) |
551 | 0 | for (j = 0; j < 32; j++) { |
552 | 0 | if (ps->id[ID_j] == 0xff) continue; |
553 | | |
554 | | |
555 | 0 | start_k = fr_rand() & 0x07; |
556 | 0 | #define ID_k ((k + start_k) & 0x07) |
557 | 0 | for (k = 0; k < 8; k++) { |
558 | 0 | if ((ps->id[ID_j] & (1 << ID_k)) != 0) continue; |
559 | | |
560 | 0 | ps->id[ID_j] |= (1 << ID_k); |
561 | 0 | id = (ID_j * 8) + ID_k; |
562 | 0 | fd = i; |
563 | 0 | break; |
564 | 0 | } |
565 | 0 | if (fd >= 0) break; |
566 | 0 | } |
567 | 0 | #undef ID_i |
568 | 0 | #undef ID_j |
569 | 0 | #undef ID_k |
570 | 0 | break; |
571 | 0 | } |
572 | | |
573 | | /* |
574 | | * Ask the caller to allocate a new ID. |
575 | | */ |
576 | 0 | if (fd < 0) { |
577 | 0 | fr_strerror_const("Failed finding socket, caller must allocate a new one"); |
578 | 0 | return false; |
579 | 0 | } |
580 | | |
581 | | /* |
582 | | * Set the ID, source IP, and source port. |
583 | | */ |
584 | 0 | request->id = id; |
585 | |
|
586 | 0 | request->socket.fd = ps->socket.fd; |
587 | 0 | request->socket.inet.src_ipaddr = ps->socket.inet.src_ipaddr; |
588 | 0 | request->socket.inet.src_port = ps->socket.inet.src_port; |
589 | | |
590 | | /* |
591 | | * If we managed to insert it, we're done. |
592 | | */ |
593 | 0 | if (fr_packet_list_insert(pl, request)) { |
594 | 0 | if (pctx) *pctx = ps->ctx; |
595 | 0 | ps->num_outgoing++; |
596 | 0 | pl->num_outgoing++; |
597 | 0 | return true; |
598 | 0 | } |
599 | | |
600 | | /* |
601 | | * Mark the ID as free. This is the one line from |
602 | | * id_free() that we care about here. |
603 | | */ |
604 | 0 | ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07)); |
605 | |
|
606 | 0 | request->id = -1; |
607 | 0 | request->socket.fd = -1; |
608 | 0 | request->socket.inet.src_ipaddr.af = AF_UNSPEC; |
609 | 0 | request->socket.inet.src_port = 0; |
610 | |
|
611 | 0 | return false; |
612 | 0 | } |
613 | | |
614 | | /* |
615 | | * Should be called AFTER yanking it from the list, so that |
616 | | * any newly inserted entries don't collide with this one. |
617 | | */ |
618 | | bool fr_packet_list_id_free(fr_packet_list_t *pl, |
619 | | fr_radius_packet_t *request, bool yank) |
620 | 0 | { |
621 | 0 | fr_packet_socket_t *ps; |
622 | |
|
623 | 0 | if (!pl || !request) return false; |
624 | | |
625 | 0 | if (yank && !fr_packet_list_yank(pl, request)) return false; |
626 | | |
627 | 0 | ps = fr_socket_find(pl, request->socket.fd); |
628 | 0 | if (!ps) return false; |
629 | | |
630 | 0 | ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07)); |
631 | |
|
632 | 0 | ps->num_outgoing--; |
633 | 0 | pl->num_outgoing--; |
634 | |
|
635 | 0 | request->id = -1; |
636 | 0 | request->socket.inet.src_ipaddr.af = AF_UNSPEC; /* id_alloc checks this */ |
637 | 0 | request->socket.inet.src_port = 0; |
638 | |
|
639 | 0 | return true; |
640 | 0 | } |
641 | | |
642 | | int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set) |
643 | 0 | { |
644 | 0 | int i, maxfd; |
645 | |
|
646 | 0 | if (!pl || !set) return 0; |
647 | | |
648 | 0 | maxfd = -1; |
649 | |
|
650 | 0 | for (i = 0; i < MAX_SOCKETS; i++) { |
651 | 0 | if (pl->sockets[i].socket.fd == -1) continue; |
652 | 0 | FD_SET(pl->sockets[i].socket.fd, set); |
653 | 0 | if (pl->sockets[i].socket.fd > maxfd) { |
654 | 0 | maxfd = pl->sockets[i].socket.fd; |
655 | 0 | } |
656 | 0 | } |
657 | |
|
658 | 0 | if (maxfd < 0) return -1; |
659 | | |
660 | 0 | return maxfd + 1; |
661 | 0 | } |
662 | | |
663 | | /* |
664 | | * Round-robins the receivers, without priority. |
665 | | * |
666 | | * FIXME: Add socket.fd, if -1, do round-robin, else do socket.fd |
667 | | * IF in fdset. |
668 | | */ |
669 | | fr_radius_packet_t *fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set, uint32_t max_attributes, bool require_ma) |
670 | 0 | { |
671 | 0 | int start; |
672 | 0 | fr_radius_packet_t *packet; |
673 | |
|
674 | 0 | if (!pl || !set) return NULL; |
675 | | |
676 | 0 | start = pl->last_recv; |
677 | 0 | do { |
678 | 0 | start++; |
679 | 0 | start &= SOCKOFFSET_MASK; |
680 | |
|
681 | 0 | if (pl->sockets[start].socket.fd == -1) continue; |
682 | | |
683 | 0 | if (!FD_ISSET(pl->sockets[start].socket.fd, set)) continue; |
684 | | |
685 | 0 | if (pl->sockets[start].socket.proto == IPPROTO_TCP) { |
686 | 0 | packet = fr_tcp_recv(pl->sockets[start].socket.fd, false); |
687 | 0 | } else |
688 | 0 | packet = fr_radius_packet_recv(NULL, pl->sockets[start].socket.fd, UDP_FLAGS_NONE, |
689 | 0 | max_attributes, require_ma); |
690 | 0 | if (!packet) continue; |
691 | | |
692 | | /* |
693 | | * Call fr_packet_list_find_byreply(). If it |
694 | | * doesn't find anything, discard the reply. |
695 | | */ |
696 | | |
697 | 0 | pl->last_recv = start; |
698 | 0 | packet->socket.proto = pl->sockets[start].socket.proto; |
699 | 0 | return packet; |
700 | 0 | } while (start != pl->last_recv); |
701 | | |
702 | 0 | return NULL; |
703 | 0 | } |
704 | | |
705 | | uint32_t fr_packet_list_num_incoming(fr_packet_list_t *pl) |
706 | 0 | { |
707 | 0 | uint32_t num_elements; |
708 | |
|
709 | 0 | if (!pl) return 0; |
710 | | |
711 | 0 | num_elements = fr_rb_num_elements(pl->tree); |
712 | 0 | if (num_elements < pl->num_outgoing) return 0; /* panic! */ |
713 | | |
714 | 0 | return num_elements - pl->num_outgoing; |
715 | 0 | } |
716 | | |
717 | | uint32_t fr_packet_list_num_outgoing(fr_packet_list_t *pl) |
718 | 0 | { |
719 | 0 | if (!pl) return 0; |
720 | | |
721 | 0 | return pl->num_outgoing; |
722 | 0 | } |
723 | | |
724 | | /* |
725 | | * Debug the packet if requested. |
726 | | */ |
727 | | void fr_packet_header_log(fr_log_t const *log, fr_radius_packet_t *packet, bool received) |
728 | 0 | { |
729 | 0 | char src_ipaddr[FR_IPADDR_STRLEN]; |
730 | 0 | char dst_ipaddr[FR_IPADDR_STRLEN]; |
731 | 0 | #ifdef WITH_IFINDEX_NAME_RESOLUTION |
732 | 0 | char if_name[IFNAMSIZ]; |
733 | 0 | #endif |
734 | |
|
735 | 0 | if (!log) return; |
736 | 0 | if (!packet) return; |
737 | | |
738 | | /* |
739 | | * Client-specific debugging re-prints the input |
740 | | * packet into the client log. |
741 | | * |
742 | | * This really belongs in a utility library |
743 | | */ |
744 | 0 | if (FR_RADIUS_PACKET_CODE_VALID(packet->code)) { |
745 | 0 | fr_log(log, L_DBG, __FILE__, __LINE__, |
746 | 0 | "%s %s Id %i from %s%s%s:%i to %s%s%s:%i " |
747 | 0 | #ifdef WITH_IFINDEX_NAME_RESOLUTION |
748 | 0 | "%s%s%s" |
749 | 0 | #endif |
750 | 0 | "length %zu\n", |
751 | 0 | received ? "Received" : "Sent", |
752 | 0 | fr_radius_packet_names[packet->code], |
753 | 0 | packet->id, |
754 | 0 | packet->socket.inet.src_ipaddr.af == AF_INET6 ? "[" : "", |
755 | 0 | fr_inet_ntop(src_ipaddr, sizeof(src_ipaddr), &packet->socket.inet.src_ipaddr), |
756 | 0 | packet->socket.inet.src_ipaddr.af == AF_INET6 ? "]" : "", |
757 | 0 | packet->socket.inet.src_port, |
758 | 0 | packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "[" : "", |
759 | 0 | fr_inet_ntop(dst_ipaddr, sizeof(dst_ipaddr), &packet->socket.inet.dst_ipaddr), |
760 | 0 | packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "]" : "", |
761 | 0 | packet->socket.inet.dst_port, |
762 | 0 | #ifdef WITH_IFINDEX_NAME_RESOLUTION |
763 | 0 | received ? "via " : "", |
764 | 0 | received ? fr_ifname_from_ifindex(if_name, packet->socket.inet.ifindex) : "", |
765 | 0 | received ? " " : "", |
766 | 0 | #endif |
767 | 0 | packet->data_len); |
768 | 0 | } else { |
769 | 0 | fr_log(log, L_DBG, __FILE__, __LINE__, |
770 | 0 | "%s code %u Id %i from %s%s%s:%i to %s%s%s:%i " |
771 | 0 | #ifdef WITH_IFINDEX_NAME_RESOLUTION |
772 | 0 | "%s%s%s" |
773 | 0 | #endif |
774 | 0 | "length %zu\n", |
775 | 0 | received ? "Received" : "Sent", |
776 | 0 | packet->code, |
777 | 0 | packet->id, |
778 | 0 | packet->socket.inet.src_ipaddr.af == AF_INET6 ? "[" : "", |
779 | 0 | fr_inet_ntop(src_ipaddr, sizeof(src_ipaddr), &packet->socket.inet.src_ipaddr), |
780 | 0 | packet->socket.inet.src_ipaddr.af == AF_INET6 ? "]" : "", |
781 | 0 | packet->socket.inet.src_port, |
782 | 0 | packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "[" : "", |
783 | 0 | fr_inet_ntop(dst_ipaddr, sizeof(dst_ipaddr), &packet->socket.inet.dst_ipaddr), |
784 | 0 | packet->socket.inet.dst_ipaddr.af == AF_INET6 ? "]" : "", |
785 | 0 | packet->socket.inet.dst_port, |
786 | 0 | #ifdef WITH_IFINDEX_NAME_RESOLUTION |
787 | 0 | received ? "via " : "", |
788 | 0 | received ? fr_ifname_from_ifindex(if_name, packet->socket.inet.ifindex) : "", |
789 | 0 | received ? " " : "", |
790 | 0 | #endif |
791 | 0 | packet->data_len); |
792 | 0 | } |
793 | 0 | } |
794 | | |
795 | | /* |
796 | | * Debug the packet header and all attributes |
797 | | */ |
798 | | void fr_packet_log(fr_log_t const *log, fr_radius_packet_t *packet, fr_pair_list_t *list, bool received) |
799 | 0 | { |
800 | 0 | fr_packet_header_log(log, packet, received); |
801 | 0 | if (fr_debug_lvl >= L_DBG_LVL_1) fr_pair_list_log(log, 4, list); |
802 | 0 | #ifndef NDEBUG |
803 | 0 | if (fr_debug_lvl >= L_DBG_LVL_4) fr_radius_packet_log_hex(log, packet); |
804 | 0 | #endif |
805 | 0 | } |