/src/freeradius-server/src/lib/io/master.c
Line | Count | Source |
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 | | /** |
18 | | * $Id: d086eae2cbb722dad9f4ac42d7f2c2b574b4ee3e $ |
19 | | * @file io/master.c |
20 | | * @brief Master IO handler |
21 | | * |
22 | | * @copyright 2018 Alan DeKok (aland@freeradius.org) |
23 | | */ |
24 | | #include <freeradius-devel/io/listen.h> |
25 | | #include <freeradius-devel/io/master.h> |
26 | | |
27 | | |
28 | | #include <freeradius-devel/util/debug.h> |
29 | | |
30 | | #include <freeradius-devel/util/syserror.h> |
31 | | |
32 | | typedef struct { |
33 | | fr_event_list_t *el; //!< event list, for the master socket. |
34 | | fr_network_t *nr; //!< network for the master socket |
35 | | |
36 | | fr_trie_t *trie; //!< trie of clients |
37 | | fr_heap_t *pending_clients; //!< heap of pending clients |
38 | | fr_heap_t *alive_clients; //!< heap of active dynamic clients |
39 | | |
40 | | fr_listen_t *listen; //!< The master IO path |
41 | | fr_listen_t *child; //!< The child (app_io) IO path |
42 | | fr_schedule_t *sc; //!< the scheduler |
43 | | |
44 | | // @todo - count num_nak_clients, and num_nak_connections, too |
45 | | uint32_t num_connections; //!< number of dynamic connections |
46 | | uint32_t num_pending_packets; //!< number of pending packets |
47 | | uint64_t client_id; //!< Unique client identifier. |
48 | | |
49 | | struct { |
50 | | fr_rate_limit_t accept_failed; |
51 | | fr_rate_limit_t alloc_failed; |
52 | | fr_rate_limit_t bad_type; |
53 | | fr_rate_limit_t conn_alloc_failed; |
54 | | fr_rate_limit_t max_connections; |
55 | | fr_rate_limit_t queue_full; |
56 | | fr_rate_limit_t repeat_nak; |
57 | | fr_rate_limit_t too_many_pending; |
58 | | fr_rate_limit_t tracking_failed; |
59 | | fr_rate_limit_t unknown_client; |
60 | | } rate_limit; |
61 | | } fr_io_thread_t; |
62 | | |
63 | | /** A saved packet |
64 | | * |
65 | | */ |
66 | | typedef struct { |
67 | | fr_heap_index_t heap_id; |
68 | | uint32_t priority; |
69 | | fr_time_t recv_time; |
70 | | fr_io_track_t *track; |
71 | | uint8_t *buffer; |
72 | | size_t buffer_len; |
73 | | } fr_io_pending_packet_t; |
74 | | |
75 | | |
76 | | /** Client states |
77 | | * |
78 | | */ |
79 | | typedef enum { |
80 | | PR_CLIENT_INVALID = 0, |
81 | | PR_CLIENT_STATIC, //!< static / global clients |
82 | | PR_CLIENT_NAK, //!< negative cache entry |
83 | | PR_CLIENT_DYNAMIC, //!< dynamically defined client |
84 | | PR_CLIENT_CONNECTED, //!< dynamically defined client in a connected socket |
85 | | PR_CLIENT_PENDING, //!< dynamic client pending definition |
86 | | } fr_io_client_state_t; |
87 | | |
88 | | /* |
89 | | * Dynamic clients are run through the normal src/process/foo state machine. |
90 | | * |
91 | | * request->async->packet_ctx is an fr_io_track_t |
92 | | * |
93 | | * track->dynamic is set to a non-zero value. |
94 | | * |
95 | | * The dynamic client code returns a buffer of 1 byte for a NAK. |
96 | | * |
97 | | * If the client creation is successful, then it does talloc(NULL, fr_client_t), |
98 | | * fills out the structure, and sends the pointer in the buffer (8 bytes). |
99 | | * |
100 | | * This code will take over ownership of the structure, and |
101 | | * create the dynamic client. |
102 | | */ |
103 | | |
104 | | typedef struct fr_io_connection_s fr_io_connection_t; |
105 | | |
106 | | /** Client definitions for master IO |
107 | | * |
108 | | */ |
109 | | struct fr_io_client_s { |
110 | | fr_io_connection_t *connection; //!< parent connection |
111 | | fr_io_client_state_t state; //!< state of this client |
112 | | fr_ipaddr_t src_ipaddr; //!< packets come from this address |
113 | | fr_ipaddr_t network; //!< network for dynamic clients |
114 | | fr_client_t *radclient; //!< old-style definition of this client |
115 | | |
116 | | int packets; //!< number of packets using this client |
117 | | fr_heap_index_t pending_id; //!< for pending clients |
118 | | fr_heap_index_t alive_id; //!< for all clients |
119 | | |
120 | | bool use_connected; //!< does this client allow connected sub-sockets? |
121 | | bool ready_to_delete; //!< are we ready to delete this client? |
122 | | bool in_trie; //!< is the client in the trie? |
123 | | |
124 | | fr_io_instance_t const *inst; //!< parent instance for master IO handler |
125 | | fr_io_thread_t *thread; |
126 | | fr_timer_t *ev; //!< when we clean up the client |
127 | | fr_rb_tree_t *table; //!< tracking table for packets |
128 | | |
129 | | fr_heap_t *pending; //!< pending packets for this client |
130 | | fr_hash_table_t *addresses; //!< list of src/dst addresses used by this client |
131 | | |
132 | | pthread_mutex_t mutex; //!< for parent / child signaling |
133 | | fr_hash_table_t *ht; //!< for tracking connected sockets |
134 | | }; |
135 | | |
136 | | /** Track a connection |
137 | | * |
138 | | * This structure contains information about the connection, |
139 | | * a pointer to the library instance so that we can clean up on exit, |
140 | | * and the listener. |
141 | | * |
142 | | * It also points to a client structure which is for this connection, |
143 | | * and only this connection. |
144 | | * |
145 | | * Finally, a pointer to the parent client, so that the child can |
146 | | * tell the parent it's alive, and the parent can push packets to the |
147 | | * child. |
148 | | */ |
149 | | struct fr_io_connection_s { |
150 | | char const *name; //!< taken from proto_FOO_TRANSPORT |
151 | | int packets; //!< number of packets using this connection |
152 | | fr_io_address_t *address; //!< full information about the connection. |
153 | | fr_listen_t *listen; //!< master listener for this socket |
154 | | fr_listen_t *child; //!< child listener (app_io) for this socket |
155 | | fr_io_client_t *client; //!< our local client (pending or connected). |
156 | | fr_io_client_t *parent; //!< points to the parent client. |
157 | | module_instance_t *mi; //!< for submodule |
158 | | |
159 | | bool dead; //!< roundabout way to get the network side to close a socket |
160 | | bool paused; //!< event filter doesn't like resuming something that isn't paused |
161 | | bool in_parent_hash; //!< for tracking thread issues |
162 | | fr_event_list_t *el; //!< event list for this connection |
163 | | fr_network_t *nr; //!< network for this connection |
164 | | }; |
165 | | |
166 | | static fr_event_update_t pause_read[] = { |
167 | | FR_EVENT_SUSPEND(fr_event_io_func_t, read), |
168 | | { 0 } |
169 | | }; |
170 | | |
171 | | static fr_event_update_t resume_read[] = { |
172 | | FR_EVENT_RESUME(fr_event_io_func_t, read), |
173 | | { 0 } |
174 | | }; |
175 | | |
176 | | static int track_free(fr_io_track_t *track) |
177 | 0 | { |
178 | 0 | FR_TIMER_DELETE_RETURN(&track->ev); |
179 | 0 | talloc_free_children(track); |
180 | |
|
181 | 0 | fr_assert(track->client->packets > 0); |
182 | 0 | track->client->packets--; |
183 | |
|
184 | 0 | return 0; |
185 | 0 | } |
186 | | |
187 | | static int track_dedup_free(fr_io_track_t *track) |
188 | 0 | { |
189 | 0 | fr_assert(track->client->table != NULL); |
190 | 0 | fr_assert(fr_rb_find(track->client->table, track) != NULL); |
191 | |
|
192 | 0 | if (!fr_rb_delete(track->client->table, track)) { |
193 | 0 | fr_assert(0); |
194 | 0 | } |
195 | |
|
196 | 0 | return track_free(track); |
197 | 0 | } |
198 | | |
199 | | /* |
200 | | * Return negative numbers to put 'one' at the top of the heap. |
201 | | * Return positive numbers to put 'two' at the top of the heap. |
202 | | */ |
203 | | static int8_t pending_packet_cmp(void const *one, void const *two) |
204 | 0 | { |
205 | 0 | fr_io_pending_packet_t const *a = talloc_get_type_abort_const(one, fr_io_pending_packet_t); |
206 | 0 | fr_io_pending_packet_t const *b = talloc_get_type_abort_const(two, fr_io_pending_packet_t); |
207 | 0 | int ret; |
208 | | |
209 | | /* |
210 | | * Higher priority elements are larger than lower |
211 | | * priority elements. So if "a" is larger than "b", we |
212 | | * wish to prefer "a". |
213 | | */ |
214 | 0 | ret = CMP_PREFER_LARGER(a->priority, b->priority); |
215 | 0 | if (ret != 0) return ret; |
216 | | |
217 | | /* |
218 | | * Smaller numbers mean packets were received earlier. |
219 | | * We want to process packets in time order. So if "a" |
220 | | * is smaller than "b", we wish to prefer "a". |
221 | | * |
222 | | * After that, it doesn't really matter what order the |
223 | | * packets go in. Since we'll never have two identical |
224 | | * "recv_time" values, the code should never get here. |
225 | | */ |
226 | 0 | return CMP_PREFER_SMALLER(fr_time_unwrap(a->recv_time), fr_time_unwrap(b->recv_time)); |
227 | 0 | } |
228 | | |
229 | | /* |
230 | | * Order clients in the pending_clients heap, based on the |
231 | | * packets that they contain. |
232 | | */ |
233 | | static int8_t pending_client_cmp(void const *one, void const *two) |
234 | 0 | { |
235 | 0 | fr_io_pending_packet_t const *a; |
236 | 0 | fr_io_pending_packet_t const *b; |
237 | |
|
238 | 0 | fr_io_client_t const *c1 = talloc_get_type_abort_const(one, fr_io_client_t); |
239 | 0 | fr_io_client_t const *c2 = talloc_get_type_abort_const(two, fr_io_client_t); |
240 | |
|
241 | 0 | a = fr_heap_peek(c1->pending); |
242 | 0 | b = fr_heap_peek(c2->pending); |
243 | |
|
244 | 0 | fr_assert(a != NULL); |
245 | 0 | fr_assert(b != NULL); |
246 | |
|
247 | 0 | return pending_packet_cmp(a, b); |
248 | 0 | } |
249 | | |
250 | | |
251 | | static int8_t address_cmp(void const *one, void const *two) |
252 | 0 | { |
253 | 0 | fr_io_address_t const *a = talloc_get_type_abort_const(one, fr_io_address_t); |
254 | 0 | fr_io_address_t const *b = talloc_get_type_abort_const(two, fr_io_address_t); |
255 | 0 | int8_t ret; |
256 | |
|
257 | 0 | CMP_RETURN(a, b, socket.inet.src_port); |
258 | 0 | CMP_RETURN(a, b, socket.inet.dst_port); |
259 | 0 | CMP_RETURN(a, b, socket.inet.ifindex); |
260 | | |
261 | 0 | ret = fr_ipaddr_cmp(&a->socket.inet.src_ipaddr, &b->socket.inet.src_ipaddr); |
262 | 0 | if (ret != 0) return ret; |
263 | | |
264 | 0 | return fr_ipaddr_cmp(&a->socket.inet.dst_ipaddr, &b->socket.inet.dst_ipaddr); |
265 | 0 | } |
266 | | |
267 | | static uint32_t connection_hash(void const *ctx) |
268 | 0 | { |
269 | 0 | uint32_t hash; |
270 | 0 | fr_io_connection_t const *c = talloc_get_type_abort_const(ctx, fr_io_connection_t); |
271 | |
|
272 | 0 | hash = fr_hash(&c->address->socket.inet.src_ipaddr, sizeof(c->address->socket.inet.src_ipaddr)); |
273 | 0 | hash = fr_hash_update(&c->address->socket.inet.src_port, sizeof(c->address->socket.inet.src_port), hash); |
274 | |
|
275 | 0 | hash = fr_hash_update(&c->address->socket.inet.ifindex, sizeof(c->address->socket.inet.ifindex), hash); |
276 | |
|
277 | 0 | hash = fr_hash_update(&c->address->socket.inet.dst_ipaddr, sizeof(c->address->socket.inet.dst_ipaddr), hash); |
278 | 0 | return fr_hash_update(&c->address->socket.inet.dst_port, sizeof(c->address->socket.inet.dst_port), hash); |
279 | 0 | } |
280 | | |
281 | | static int8_t connection_cmp(void const *one, void const *two) |
282 | 0 | { |
283 | 0 | fr_io_connection_t const *a = talloc_get_type_abort_const(one, fr_io_connection_t); |
284 | 0 | fr_io_connection_t const *b = talloc_get_type_abort_const(two, fr_io_connection_t); |
285 | |
|
286 | 0 | return address_cmp(a->address, b->address); |
287 | 0 | } |
288 | | |
289 | | |
290 | | static int8_t track_cmp(void const *one, void const *two) |
291 | 0 | { |
292 | 0 | fr_io_track_t const *a = talloc_get_type_abort_const(one, fr_io_track_t); |
293 | 0 | fr_io_track_t const *b = talloc_get_type_abort_const(two, fr_io_track_t); |
294 | 0 | int ret; |
295 | |
|
296 | 0 | fr_assert(a->client != NULL); |
297 | 0 | fr_assert(b->client != NULL); |
298 | 0 | fr_assert(a->client == b->client); /* tables are per-client */ |
299 | |
|
300 | 0 | fr_assert(!a->client->connection); |
301 | 0 | fr_assert(!b->client->connection); |
302 | | |
303 | | /* |
304 | | * Unconnected sockets must check src/dst ip/port. |
305 | | */ |
306 | 0 | ret = address_cmp(a->address, b->address); |
307 | 0 | if (ret != 0) return ret; |
308 | | |
309 | | /* |
310 | | * Call the per-protocol comparison function. |
311 | | */ |
312 | 0 | ret = a->client->inst->app_io->track_compare(a->client->inst->app_io_instance, |
313 | 0 | a->client->thread->child->thread_instance, |
314 | 0 | a->client->radclient, |
315 | 0 | a->packet, b->packet); |
316 | 0 | return CMP(ret, 0); |
317 | 0 | } |
318 | | |
319 | | |
320 | | static int8_t track_connected_cmp(void const *one, void const *two) |
321 | 0 | { |
322 | 0 | fr_io_track_t const *a = talloc_get_type_abort_const(one, fr_io_track_t); |
323 | 0 | fr_io_track_t const *b = talloc_get_type_abort_const(two, fr_io_track_t); |
324 | 0 | int ret; |
325 | |
|
326 | 0 | fr_assert(a->client != NULL); |
327 | 0 | fr_assert(b->client != NULL); |
328 | |
|
329 | 0 | fr_assert(a->client->connection); |
330 | 0 | fr_assert(b->client->connection); |
331 | 0 | fr_assert(a->client == b->client); |
332 | 0 | fr_assert(a->client->connection == b->client->connection); |
333 | | |
334 | | /* |
335 | | * Note that we pass the connection "client", as |
336 | | * we may do negotiation specific to this connection. |
337 | | */ |
338 | 0 | ret = a->client->inst->app_io->track_compare(a->client->inst->app_io_instance, |
339 | 0 | a->client->connection->child->thread_instance, |
340 | 0 | a->client->connection->client->radclient, |
341 | 0 | a->packet, b->packet); |
342 | 0 | return CMP(ret, 0); |
343 | 0 | } |
344 | | |
345 | | |
346 | | static fr_io_pending_packet_t *pending_packet_pop(fr_io_thread_t *thread) |
347 | 0 | { |
348 | 0 | fr_io_client_t *client; |
349 | 0 | fr_io_pending_packet_t *pending; |
350 | |
|
351 | 0 | client = fr_heap_pop(&thread->pending_clients); |
352 | 0 | if (!client) { |
353 | 0 | fr_assert(thread->num_pending_packets == 0); |
354 | | |
355 | | /* |
356 | | * 99% of the time we don't have pending clients. |
357 | | * So we might as well free this, so that the |
358 | | * caller doesn't keep checking us for every packet. |
359 | | */ |
360 | 0 | talloc_free(thread->pending_clients); |
361 | 0 | thread->pending_clients = NULL; |
362 | 0 | return NULL; |
363 | 0 | } |
364 | | |
365 | 0 | pending = fr_heap_pop(&client->pending); |
366 | 0 | fr_assert(pending != NULL); |
367 | | |
368 | | /* |
369 | | * If the client has more packets pending, add it back to |
370 | | * the heap. |
371 | | */ |
372 | 0 | if (fr_heap_num_elements(client->pending) > 0) { |
373 | 0 | if (fr_heap_insert(&thread->pending_clients, client) < 0) { |
374 | 0 | fr_assert(0 == 1); |
375 | 0 | } |
376 | 0 | } |
377 | |
|
378 | 0 | fr_assert(thread->num_pending_packets > 0); |
379 | 0 | thread->num_pending_packets--; |
380 | |
|
381 | 0 | return pending; |
382 | 0 | } |
383 | | |
384 | | static fr_client_t *radclient_clone(TALLOC_CTX *ctx, fr_client_t const *parent) |
385 | 0 | { |
386 | 0 | fr_client_t *c; |
387 | |
|
388 | 0 | if (!parent) return NULL; |
389 | | |
390 | 0 | c = talloc_zero(ctx, fr_client_t); |
391 | 0 | if (!c) return NULL; |
392 | | |
393 | | /* |
394 | | * Do NOT set ipaddr or src_ipaddr. The caller MUST do this! |
395 | | */ |
396 | | |
397 | 0 | #define DUP_FIELD(_x) do { if (parent->_x) {c->_x = talloc_strdup(c, parent->_x); if (!c->_x) {goto error;}}} while (0) |
398 | 0 | #define COPY_FIELD(_x) c->_x = parent->_x |
399 | | |
400 | 0 | DUP_FIELD(longname); |
401 | 0 | DUP_FIELD(shortname); |
402 | 0 | DUP_FIELD(secret); |
403 | 0 | DUP_FIELD(nas_type); |
404 | 0 | DUP_FIELD(server); |
405 | | |
406 | 0 | COPY_FIELD(require_message_authenticator); |
407 | 0 | COPY_FIELD(require_message_authenticator_is_set); |
408 | | #ifdef NAS_VIOLATES_RFC |
409 | | COPY_FIELD(allow_vulnerable_clients); |
410 | | #endif |
411 | 0 | COPY_FIELD(limit_proxy_state); |
412 | 0 | COPY_FIELD(limit_proxy_state_is_set); |
413 | 0 | COPY_FIELD(received_message_authenticator); |
414 | 0 | COPY_FIELD(first_packet_no_proxy_state); |
415 | | /* dynamic MUST be false */ |
416 | 0 | COPY_FIELD(server_cs); |
417 | 0 | COPY_FIELD(cs); |
418 | 0 | COPY_FIELD(proto); |
419 | 0 | COPY_FIELD(active); |
420 | |
|
421 | 0 | COPY_FIELD(use_connected); |
422 | |
|
423 | 0 | #ifdef WITH_TLS |
424 | 0 | COPY_FIELD(tls_required); |
425 | 0 | #endif |
426 | |
|
427 | 0 | c->ipaddr = parent->ipaddr; |
428 | 0 | c->src_ipaddr = parent->src_ipaddr; |
429 | |
|
430 | 0 | return c; |
431 | | |
432 | | /* |
433 | | * @todo - fill in other fields, too! |
434 | | */ |
435 | | |
436 | 0 | error: |
437 | 0 | talloc_free(c); |
438 | 0 | return NULL; |
439 | 0 | } |
440 | | #undef COPY_FIELD |
441 | | #undef DUP_FIELD |
442 | | |
443 | | |
444 | | /** Count the number of connections used by active clients. |
445 | | * |
446 | | * Unfortunately, we also count NAK'd connections, too, even if they |
447 | | * are closed. The alternative is to walk through all connections |
448 | | * for each client, which would be a long time. |
449 | | */ |
450 | | static int count_connections(UNUSED uint8_t const *key, UNUSED size_t keylen, void *data, void *ctx) |
451 | 0 | { |
452 | 0 | fr_io_client_t *client = talloc_get_type_abort(data, fr_io_client_t); |
453 | 0 | int connections; |
454 | |
|
455 | 0 | pthread_mutex_lock(&client->mutex); |
456 | |
|
457 | 0 | if (!client->ht) { |
458 | 0 | pthread_mutex_unlock(&client->mutex); |
459 | 0 | return 0; |
460 | 0 | } |
461 | | |
462 | 0 | connections = fr_hash_table_num_elements(client->ht); |
463 | 0 | pthread_mutex_unlock(&client->mutex); |
464 | |
|
465 | 0 | fr_assert(client->use_connected); |
466 | 0 | *((uint32_t *) ctx) += connections; |
467 | |
|
468 | 0 | return 0; |
469 | 0 | } |
470 | | |
471 | | |
472 | | static int _client_free(fr_io_client_t *client) |
473 | 0 | { |
474 | 0 | if (client->use_connected) (void) pthread_mutex_destroy(&client->mutex); |
475 | |
|
476 | 0 | TALLOC_FREE(client->pending); |
477 | |
|
478 | 0 | return 0; |
479 | 0 | } |
480 | | |
481 | | static void client_pending_free(fr_io_client_t *client) |
482 | 0 | { |
483 | 0 | size_t num; |
484 | |
|
485 | 0 | fr_assert(!client->connection); |
486 | |
|
487 | 0 | if (!client->pending) return; |
488 | | |
489 | 0 | num = fr_heap_num_elements(client->pending); |
490 | |
|
491 | 0 | fr_assert(client->thread->num_pending_packets >= num); |
492 | 0 | client->thread->num_pending_packets -= num; |
493 | |
|
494 | 0 | TALLOC_FREE(client->pending); |
495 | 0 | } |
496 | | |
497 | | |
498 | | static int connection_free(fr_io_connection_t *connection) |
499 | 0 | { |
500 | | /* |
501 | | * This is it's own talloc context, as there are |
502 | | * thousands of packets associated with it. |
503 | | */ |
504 | 0 | TALLOC_FREE(connection->client); |
505 | |
|
506 | 0 | return 0; |
507 | 0 | } |
508 | | |
509 | | /** Create a new connection. |
510 | | * |
511 | | * Called ONLY from the master socket. |
512 | | */ |
513 | | static fr_io_connection_t *fr_io_connection_alloc(fr_io_instance_t const *inst, |
514 | | fr_io_thread_t *thread, |
515 | | fr_io_client_t *client, int fd, |
516 | | fr_io_address_t *address, |
517 | | fr_io_connection_t *nak) |
518 | 0 | { |
519 | 0 | int ret; |
520 | 0 | fr_io_connection_t *connection; |
521 | 0 | module_instance_t *mi = NULL; |
522 | 0 | fr_listen_t *li; |
523 | 0 | fr_client_t *radclient; |
524 | | |
525 | | |
526 | | /* |
527 | | * Reload the app_io module as a "new" library. This |
528 | | * causes the link count for the library to be correct. |
529 | | * It also allocates a new instance data for it, too. |
530 | | * Passing CONF_SECTION of NULL ensures that there's no |
531 | | * config for it, as we'll just clone it's contents from |
532 | | * the original. It also means that detach should be |
533 | | * called when the instance data is freed. |
534 | | */ |
535 | 0 | if (!nak) { |
536 | 0 | CONF_SECTION *cs; |
537 | 0 | char *inst_name; |
538 | |
|
539 | 0 | if (inst->max_connections || client->radclient->limit.max_connections) { |
540 | 0 | uint32_t max_connections = inst->max_connections ? inst->max_connections : client->radclient->limit.max_connections; |
541 | | |
542 | | /* |
543 | | * We've hit the connection limit. Walk |
544 | | * over all clients with connections, and |
545 | | * count the number of connections used. |
546 | | */ |
547 | 0 | if (thread->num_connections >= max_connections) { |
548 | 0 | thread->num_connections = 0; |
549 | |
|
550 | 0 | (void) fr_trie_walk(thread->trie, &thread->num_connections, count_connections); |
551 | |
|
552 | 0 | if ((thread->num_connections + 1) >= max_connections) { |
553 | 0 | RATE_LIMIT_LOCAL(&thread->rate_limit.max_connections, INFO, |
554 | 0 | "proto_%s - Ignoring connection from client %s - 'max_connections' limit reached.", |
555 | 0 | inst->app->common.name, client->radclient->shortname); |
556 | 0 | if (fd >= 0) close(fd); |
557 | 0 | return NULL; |
558 | 0 | } |
559 | 0 | } |
560 | 0 | } |
561 | | |
562 | | /* |
563 | | * Add a client module into a sublist |
564 | | */ |
565 | 0 | inst_name = talloc_asprintf(NULL, "%"PRIu64, thread->client_id++); |
566 | 0 | mi = module_instance_copy(inst->clients, inst->submodule, inst_name); |
567 | 0 | talloc_free(inst_name); |
568 | |
|
569 | 0 | cs = cf_section_dup(mi, NULL, inst->submodule->conf, |
570 | 0 | cf_section_name1(inst->submodule->conf), |
571 | 0 | cf_section_name2(inst->submodule->conf), false); |
572 | 0 | if (module_instance_conf_parse(mi, cs) < 0) { |
573 | 0 | cf_log_err(inst->server_cs, "Failed parsing module config"); |
574 | 0 | goto cleanup; |
575 | 0 | } |
576 | | |
577 | | /* Thread local module lists never run bootstrap */ |
578 | 0 | if (module_instantiate(mi) < 0) { |
579 | 0 | cf_log_err(inst->server_cs, "Failed instantiating module"); |
580 | 0 | goto cleanup; |
581 | 0 | } |
582 | | |
583 | 0 | if (module_thread_instantiate(mi, mi, thread->el) < 0) { |
584 | 0 | cf_log_err(inst->server_cs, "Failed instantiating module"); |
585 | 0 | goto cleanup; |
586 | 0 | } |
587 | | |
588 | | /* |
589 | | * FIXME - Instantiate the new module?! |
590 | | */ |
591 | 0 | fr_assert(mi != NULL); |
592 | 0 | } else { |
593 | 0 | mi = talloc_init_const("nak"); |
594 | 0 | } |
595 | | |
596 | 0 | MEM(connection = talloc_zero(mi, fr_io_connection_t)); |
597 | 0 | MEM(connection->address = talloc_memdup(connection, address, sizeof(*address))); |
598 | 0 | (void) talloc_set_name_const(connection->address, "fr_io_address_t"); |
599 | |
|
600 | 0 | connection->parent = client; |
601 | 0 | connection->mi = mi; |
602 | |
|
603 | 0 | MEM(connection->client = talloc_named(NULL, sizeof(fr_io_client_t), "fr_io_client_t")); |
604 | 0 | memset(connection->client, 0, sizeof(*connection->client)); |
605 | |
|
606 | 0 | MEM(connection->client->radclient = radclient = radclient_clone(connection->client, client->radclient)); |
607 | |
|
608 | 0 | talloc_set_destructor(connection->client, _client_free); |
609 | 0 | talloc_set_destructor(connection, connection_free); |
610 | |
|
611 | 0 | connection->client->pending_id = FR_HEAP_INDEX_INVALID; |
612 | 0 | connection->client->alive_id = FR_HEAP_INDEX_INVALID; |
613 | 0 | connection->client->connection = connection; |
614 | | |
615 | | /* |
616 | | * Create the packet tracking table for this client. |
617 | | * |
618 | | * #todo - unify the code with static clients? |
619 | | */ |
620 | 0 | if (inst->app_io->track_duplicates) { |
621 | 0 | MEM(connection->client->table = fr_rb_inline_talloc_alloc(client, fr_io_track_t, node, |
622 | 0 | track_connected_cmp, NULL)); |
623 | 0 | } |
624 | | |
625 | | /* |
626 | | * Set this radclient to be dynamic, and active. |
627 | | */ |
628 | 0 | radclient->dynamic = true; |
629 | 0 | radclient->active = true; |
630 | | |
631 | | /* |
632 | | * address->socket.inet.client points to a "static" client. We want |
633 | | * to clean up everything associated with the connection |
634 | | * when it closes. So we need to point to our own copy |
635 | | * of the client here. |
636 | | */ |
637 | 0 | connection->address->radclient = connection->client->radclient; |
638 | 0 | connection->client->inst = inst; |
639 | 0 | connection->client->thread = thread; |
640 | | |
641 | | /* |
642 | | * Create a heap for packets which are pending for this |
643 | | * client. |
644 | | */ |
645 | 0 | MEM(connection->client->pending = fr_heap_alloc(connection->client, pending_packet_cmp, |
646 | 0 | fr_io_pending_packet_t, heap_id, 0)); |
647 | | |
648 | | /* |
649 | | * Clients for connected sockets are always a /32 or /128. |
650 | | */ |
651 | 0 | connection->client->src_ipaddr = address->socket.inet.src_ipaddr; |
652 | 0 | connection->client->network = address->socket.inet.src_ipaddr; |
653 | | |
654 | | /* |
655 | | * Don't initialize mutex or hash table. |
656 | | * Connections cannot spawn other connections. |
657 | | */ |
658 | | |
659 | | /* |
660 | | * If this client state is pending, then the connection |
661 | | * state is pending, too. That allows NAT gateways to be |
662 | | * defined dynamically, AND for them to have multiple |
663 | | * connections, each with a different client. This |
664 | | * allows for different shared secrets to be used for |
665 | | * different connections. Once the client gets defined |
666 | | * for this connection, it will be either "connected" or |
667 | | * not. If connected, then the parent client remains |
668 | | * PENDING. Otherwise, the parent client is moved to |
669 | | * DYNAMIC |
670 | | * |
671 | | * If this client state is static or dynamic, |
672 | | * then we're just using connected sockets behind |
673 | | * that client. The connections here all use the |
674 | | * same shared secret, but they use different |
675 | | * sockets, so they allow for sharing of IO |
676 | | * across CPUs / threads. |
677 | | */ |
678 | 0 | switch (client->state) { |
679 | 0 | case PR_CLIENT_PENDING: |
680 | 0 | connection->client->state = PR_CLIENT_PENDING; |
681 | | |
682 | | /* |
683 | | * Needed for rlm_radius, which refuses to proxy packets |
684 | | * that define a dynamic client. |
685 | | */ |
686 | 0 | radclient->active = false; |
687 | 0 | break; |
688 | | |
689 | 0 | case PR_CLIENT_STATIC: |
690 | 0 | case PR_CLIENT_DYNAMIC: |
691 | 0 | connection->client->state = PR_CLIENT_CONNECTED; |
692 | 0 | break; |
693 | | |
694 | 0 | case PR_CLIENT_INVALID: |
695 | 0 | case PR_CLIENT_NAK: |
696 | 0 | case PR_CLIENT_CONNECTED: |
697 | 0 | fr_assert(0 == 1); |
698 | 0 | goto cleanup; |
699 | 0 | } |
700 | | |
701 | 0 | if (!nak) { |
702 | | /* |
703 | | * Get the child listener. |
704 | | */ |
705 | 0 | MEM(li = connection->child = talloc(connection, fr_listen_t)); |
706 | 0 | memcpy(li, thread->listen, sizeof(*li)); |
707 | | |
708 | | /* |
709 | | * Glue in the actual app_io |
710 | | */ |
711 | 0 | li->connected = true; |
712 | 0 | li->app_io = thread->child->app_io; |
713 | 0 | li->cs = inst->app_io_conf; |
714 | 0 | li->thread_instance = connection; |
715 | 0 | li->app_io_instance = mi->data; |
716 | 0 | li->track_duplicates = thread->child->app_io->track_duplicates; |
717 | | |
718 | | /* |
719 | | * Create writable thread instance data. |
720 | | */ |
721 | 0 | connection->child->thread_instance = talloc_zero_array(NULL, uint8_t, |
722 | 0 | inst->app_io->common.thread_inst_size); |
723 | 0 | talloc_set_destructor(connection->child, fr_io_listen_free); |
724 | 0 | talloc_set_name(connection->child->thread_instance, "proto_%s_thread_t", |
725 | 0 | inst->app_io->common.name); |
726 | | |
727 | | /* |
728 | | * This is "const", and the user can't |
729 | | * touch it. So we just reuse the same |
730 | | * configuration everywhere. |
731 | | */ |
732 | 0 | connection->child->app_io_instance = inst->app_io_instance; |
733 | | |
734 | | /* |
735 | | * Create the listener, based on our listener. |
736 | | */ |
737 | 0 | MEM(li = connection->listen = talloc(connection, fr_listen_t)); |
738 | | |
739 | | /* |
740 | | * Note that our instance is effectively 'const'. |
741 | | * |
742 | | * i.e. we can't add things to it. Instead, we have to |
743 | | * put all variable data into the connection. |
744 | | */ |
745 | 0 | memcpy(li, thread->listen, sizeof(*li)); |
746 | | |
747 | | /* |
748 | | * Glue in the connection to the listener. |
749 | | */ |
750 | 0 | fr_assert(li->app_io == &fr_master_app_io); |
751 | |
|
752 | 0 | li->connected = true; |
753 | 0 | li->thread_instance = connection; |
754 | 0 | li->cs = inst->app_io_conf; |
755 | 0 | li->app_io_instance = li->thread_instance; |
756 | 0 | li->track_duplicates = thread->child->app_io->track_duplicates; |
757 | | |
758 | | /* |
759 | | * Instantiate the child, and open the socket. |
760 | | */ |
761 | 0 | fr_assert(inst->app_io->connection_set != NULL); |
762 | |
|
763 | 0 | if (inst->app_io->connection_set(connection->child, connection->address) < 0) { |
764 | 0 | DEBUG("proto_%s - Failed setting connection for socket.", inst->app->common.name); |
765 | 0 | goto cleanup; |
766 | 0 | } |
767 | | |
768 | | /* |
769 | | * UDP sockets: open a new socket, and then |
770 | | * connect it to the client. This emulates the |
771 | | * behavior of accept(). |
772 | | * |
773 | | * Note that there is a small window between the |
774 | | * bind() and connect() where UDP packets for the |
775 | | * wildcard socket can get received by this |
776 | | * socket. We hope that this time frame is as |
777 | | * small as possible. |
778 | | * |
779 | | * i.e. we ignore the problem, and don't |
780 | | * currently check dst ip/port for UDP packets |
781 | | * received on connected sockets. |
782 | | */ |
783 | 0 | if (fd < 0) { |
784 | 0 | socklen_t salen; |
785 | 0 | struct sockaddr_storage src; |
786 | |
|
787 | 0 | if (fr_ipaddr_to_sockaddr(&src, &salen, |
788 | 0 | &connection->address->socket.inet.src_ipaddr, |
789 | 0 | connection->address->socket.inet.src_port) < 0) { |
790 | 0 | DEBUG("proto_%s - Failed getting IP address", inst->app->common.name); |
791 | 0 | talloc_free(mi); |
792 | 0 | return NULL; |
793 | 0 | } |
794 | | |
795 | 0 | if (inst->app_io->open(connection->child) < 0) { |
796 | 0 | DEBUG("proto_%s - Failed opening connected socket.", inst->app->common.name); |
797 | 0 | talloc_free(mi); |
798 | 0 | return NULL; |
799 | 0 | } |
800 | | |
801 | 0 | fd = connection->child->fd; |
802 | |
|
803 | 0 | if (connect(fd, (struct sockaddr *) &src, salen) < 0) { |
804 | 0 | ERROR("proto_%s - Failed in connect: %s", inst->app->common.name, fr_syserror(errno)); |
805 | 0 | goto cleanup; |
806 | 0 | } |
807 | 0 | } else { |
808 | 0 | connection->child->fd = fd; |
809 | 0 | } |
810 | | |
811 | | /* |
812 | | * Set the new FD, and get the module to set it's connection name. |
813 | | */ |
814 | 0 | if (inst->app_io->fd_set(connection->child, fd) < 0) { |
815 | 0 | DEBUG3("Failed setting FD to %s", inst->app_io->common.name); |
816 | 0 | goto cleanup; |
817 | 0 | } |
818 | | |
819 | 0 | li->fd = fd; |
820 | |
|
821 | 0 | if (!inst->app_io->get_name) { |
822 | 0 | connection->name = fr_asprintf(connection, "proto_%s from client %pV port " |
823 | 0 | "%u to server %pV port %u", |
824 | 0 | inst->app->common.name, |
825 | 0 | fr_box_ipaddr(connection->address->socket.inet.src_ipaddr), |
826 | 0 | connection->address->socket.inet.src_port, |
827 | 0 | fr_box_ipaddr(connection->address->socket.inet.dst_ipaddr), |
828 | 0 | connection->address->socket.inet.dst_port); |
829 | 0 | } else { |
830 | 0 | connection->name = inst->app_io->get_name(connection->child); |
831 | 0 | } |
832 | | |
833 | | /* |
834 | | * Set the names for the listeners. |
835 | | */ |
836 | 0 | connection->listen->name = connection->name; |
837 | 0 | connection->child->name = connection->name; |
838 | 0 | } |
839 | | |
840 | | /* |
841 | | * Add the connection to the set of connections for this |
842 | | * client. |
843 | | */ |
844 | 0 | pthread_mutex_lock(&client->mutex); |
845 | 0 | if (client->ht) { |
846 | 0 | if (nak) (void) fr_hash_table_delete(client->ht, nak); |
847 | 0 | ret = fr_hash_table_insert(client->ht, connection); |
848 | 0 | client->ready_to_delete = false; |
849 | 0 | connection->in_parent_hash = true; |
850 | |
|
851 | 0 | if (!ret) { |
852 | 0 | pthread_mutex_unlock(&client->mutex); |
853 | 0 | ERROR("proto_%s - Failed inserting connection into tracking table. " |
854 | 0 | "Closing it, and discarding all packets for connection %s.", |
855 | 0 | inst->app_io->common.name, connection->name); |
856 | 0 | goto cleanup; |
857 | 0 | } |
858 | 0 | } |
859 | 0 | pthread_mutex_unlock(&client->mutex); |
860 | | |
861 | | /* |
862 | | * It's a NAK client. Set the state to NAK, and don't |
863 | | * add it to the scheduler. |
864 | | */ |
865 | 0 | if (nak) { |
866 | 0 | INFO("proto_%s - Verification failed for packet from dynamic client %pV - adding IP address to the NAK cache", |
867 | 0 | inst->app_io->common.name, fr_box_ipaddr(client->src_ipaddr)); |
868 | |
|
869 | 0 | connection->name = talloc_strdup(connection, nak->name); |
870 | 0 | connection->client->state = PR_CLIENT_NAK; |
871 | 0 | connection->el = nak->el; |
872 | 0 | return connection; |
873 | 0 | } |
874 | | |
875 | 0 | DEBUG("proto_%s - starting connection %s", inst->app_io->common.name, connection->name); |
876 | 0 | connection->nr = fr_schedule_listen_add(thread->sc, connection->listen); |
877 | 0 | if (!connection->nr) { |
878 | 0 | ERROR("proto_%s - Failed inserting connection into scheduler. " |
879 | 0 | "Closing it, and diuscarding all packets for connection %s.", |
880 | 0 | inst->app_io->common.name, connection->name); |
881 | 0 | pthread_mutex_lock(&client->mutex); |
882 | 0 | if (client->ht) (void) fr_hash_table_delete(client->ht, connection); |
883 | 0 | pthread_mutex_unlock(&client->mutex); |
884 | |
|
885 | 0 | cleanup: |
886 | 0 | if (fd >= 0) close(fd); |
887 | 0 | talloc_free(mi); |
888 | 0 | return NULL; |
889 | 0 | } |
890 | | |
891 | | /* |
892 | | * We have one more connection. Note that we do |
893 | | * NOT decrement this counter when a connection |
894 | | * closes, as the close is done in a child |
895 | | * thread. Instead, we just let counter hit the |
896 | | * limit, and then walk over the clients to reset |
897 | | * the count. |
898 | | */ |
899 | 0 | thread->num_connections++; |
900 | |
|
901 | 0 | return connection; |
902 | 0 | } |
903 | | |
904 | | |
905 | | /* |
906 | | * And here we go into the rabbit hole... |
907 | | * |
908 | | * @todo future - have a similar structure |
909 | | * fr_io_connection_io, which will duplicate some code, |
910 | | * but may make things simpler? |
911 | | */ |
912 | | static void get_inst(fr_listen_t *li, fr_io_instance_t const **inst, fr_io_thread_t **thread, |
913 | | fr_io_connection_t **connection, fr_listen_t **child) |
914 | 0 | { |
915 | 0 | if (!li->connected) { |
916 | 0 | *inst = li->app_io_instance; |
917 | 0 | if (thread) *thread = li->thread_instance; |
918 | 0 | *connection = NULL; |
919 | 0 | if (child) *child = ((fr_io_thread_t *)li->thread_instance)->child; |
920 | |
|
921 | 0 | } else { |
922 | 0 | fr_assert(connection != NULL); |
923 | |
|
924 | 0 | *connection = li->thread_instance; |
925 | 0 | *inst = (*connection)->client->inst; |
926 | 0 | if (thread) *thread = NULL; |
927 | 0 | if (child) *child = (*connection)->child; |
928 | 0 | } |
929 | 0 | } |
930 | | |
931 | | |
932 | | static fr_client_t *radclient_alloc(TALLOC_CTX *ctx, int ipproto, fr_io_address_t *address) |
933 | 0 | { |
934 | 0 | fr_client_t *radclient; |
935 | 0 | char *shortname; |
936 | |
|
937 | 0 | MEM(radclient = talloc_zero(ctx, fr_client_t)); |
938 | |
|
939 | 0 | fr_value_box_aprint(radclient, &shortname, fr_box_ipaddr(address->socket.inet.src_ipaddr), NULL); |
940 | 0 | radclient->longname = radclient->shortname = shortname; |
941 | |
|
942 | 0 | radclient->secret = radclient->nas_type = talloc_strdup(radclient, ""); |
943 | |
|
944 | 0 | radclient->ipaddr = address->socket.inet.src_ipaddr; |
945 | |
|
946 | 0 | radclient->src_ipaddr = address->socket.inet.dst_ipaddr; |
947 | |
|
948 | 0 | radclient->proto = ipproto; |
949 | 0 | radclient->dynamic = true; |
950 | |
|
951 | 0 | return radclient; |
952 | 0 | } |
953 | | |
954 | | /* |
955 | | * Remove a client from the list of "live" clients. |
956 | | * |
957 | | * This function is only used for the "main" socket. Clients |
958 | | * from connections do not use it. |
959 | | */ |
960 | | static int _client_live_free(fr_io_client_t *client) |
961 | 0 | { |
962 | 0 | talloc_get_type_abort(client, fr_io_client_t); |
963 | |
|
964 | 0 | fr_assert(client->in_trie); |
965 | 0 | fr_assert(!client->connection); |
966 | 0 | fr_assert(client->thread); |
967 | |
|
968 | 0 | if (client->pending) client_pending_free(client); |
969 | |
|
970 | 0 | (void) fr_trie_remove_by_key(client->thread->trie, &client->src_ipaddr.addr, client->src_ipaddr.prefix); |
971 | |
|
972 | 0 | if (client->thread->alive_clients) { |
973 | 0 | fr_assert(fr_heap_num_elements(client->thread->alive_clients) > 0); |
974 | 0 | (void) fr_heap_extract(&client->thread->alive_clients, client); |
975 | 0 | } |
976 | |
|
977 | 0 | if (client->use_connected) (void) pthread_mutex_destroy(&client->mutex); |
978 | |
|
979 | 0 | return 0; |
980 | 0 | } |
981 | | |
982 | | /** Allocate a dynamic client. |
983 | | * |
984 | | */ |
985 | | static fr_io_client_t *client_alloc(TALLOC_CTX *ctx, fr_io_client_state_t state, |
986 | | fr_io_instance_t const *inst, fr_io_thread_t *thread, fr_client_t *radclient, |
987 | | fr_ipaddr_t const *network) |
988 | 0 | { |
989 | 0 | fr_io_client_t *client; |
990 | | |
991 | | /* |
992 | | * Create our own local client. This client |
993 | | * holds our state which really shouldn't go into |
994 | | * fr_client_t. |
995 | | * |
996 | | * Note that we create a new top-level talloc |
997 | | * context for this client, as there may be tens |
998 | | * of thousands of packets associated with this |
999 | | * client. And we want to avoid problems with |
1000 | | * O(N) issues in talloc. |
1001 | | */ |
1002 | 0 | MEM(client = talloc_named(ctx, sizeof(fr_io_client_t), "fr_io_client_t")); |
1003 | 0 | memset(client, 0, sizeof(*client)); |
1004 | |
|
1005 | 0 | client->state = state; |
1006 | 0 | client->src_ipaddr = radclient->ipaddr; |
1007 | 0 | client->radclient = radclient; |
1008 | 0 | client->inst = inst; |
1009 | 0 | client->thread = thread; |
1010 | |
|
1011 | 0 | if (network) { |
1012 | 0 | client->network = *network; |
1013 | 0 | } else { |
1014 | 0 | client->network = client->src_ipaddr; |
1015 | 0 | } |
1016 | | |
1017 | | /* |
1018 | | * At this point, this variable can only be true |
1019 | | * for STATIC clients. PENDING clients may set |
1020 | | * it to true later, after they've been defined. |
1021 | | */ |
1022 | 0 | client->use_connected = radclient->use_connected; |
1023 | | |
1024 | | /* |
1025 | | * Create the pending heap for pending clients. |
1026 | | */ |
1027 | 0 | if (state == PR_CLIENT_PENDING) { |
1028 | 0 | MEM(client->pending = fr_heap_alloc(client, pending_packet_cmp, |
1029 | 0 | fr_io_pending_packet_t, heap_id, 0)); |
1030 | 0 | } |
1031 | | |
1032 | | /* |
1033 | | * Create the packet tracking table for this client. |
1034 | | */ |
1035 | 0 | if (inst->app_io->track_duplicates) { |
1036 | 0 | fr_assert(inst->app_io->track_compare != NULL); |
1037 | 0 | MEM(client->table = fr_rb_inline_talloc_alloc(client, fr_io_track_t, node, track_cmp, NULL)); |
1038 | 0 | } |
1039 | | |
1040 | | /* |
1041 | | * Allow connected sockets to be set on a |
1042 | | * per-client basis. |
1043 | | */ |
1044 | 0 | if (client->use_connected) { |
1045 | 0 | fr_assert(client->state == PR_CLIENT_STATIC); |
1046 | |
|
1047 | 0 | (void) pthread_mutex_init(&client->mutex, NULL); |
1048 | 0 | MEM(client->ht = fr_hash_table_alloc(client, connection_hash, connection_cmp, NULL)); |
1049 | 0 | } |
1050 | | |
1051 | | /* |
1052 | | * Add the newly defined client to the trie of |
1053 | | * allowed clients. |
1054 | | */ |
1055 | 0 | if (fr_trie_insert_by_key(thread->trie, &client->src_ipaddr.addr, client->src_ipaddr.prefix, client)) { |
1056 | 0 | ERROR("proto_%s - Failed inserting client %s into tracking table. Discarding client, and all packets for it.", |
1057 | 0 | inst->app_io->common.name, client->radclient->shortname); |
1058 | 0 | if (client->use_connected) (void) pthread_mutex_destroy(&client->mutex); |
1059 | 0 | talloc_free(client); |
1060 | 0 | return NULL; |
1061 | 0 | } |
1062 | | |
1063 | 0 | client->in_trie = true; |
1064 | | |
1065 | | /* |
1066 | | * It's a static client. Don't insert it into the list of alive clients, as those are only for |
1067 | | * dynamic clients. |
1068 | | */ |
1069 | 0 | if (state == PR_CLIENT_STATIC) return client; |
1070 | | |
1071 | 0 | fr_assert(thread->alive_clients != NULL); |
1072 | | |
1073 | | /* |
1074 | | * Track the live clients so that we can clean |
1075 | | * them up. |
1076 | | */ |
1077 | 0 | (void) fr_heap_insert(&thread->alive_clients, client); |
1078 | 0 | client->pending_id = FR_HEAP_INDEX_INVALID; |
1079 | | |
1080 | | /* |
1081 | | * Now that we've inserted it into the heap and |
1082 | | * incremented the numbers, set the destructor |
1083 | | * function. |
1084 | | */ |
1085 | 0 | talloc_set_destructor(client, _client_live_free); |
1086 | |
|
1087 | 0 | return client; |
1088 | 0 | } |
1089 | | |
1090 | | |
1091 | | static fr_io_track_t *fr_io_track_add(fr_listen_t const *li, fr_io_client_t *client, |
1092 | | fr_io_address_t *address, |
1093 | | uint8_t const *packet, size_t packet_len, |
1094 | | fr_time_t recv_time, bool *is_dup) |
1095 | 0 | { |
1096 | 0 | size_t len; |
1097 | 0 | fr_io_track_t *track, *old; |
1098 | |
|
1099 | 0 | *is_dup = false; |
1100 | | |
1101 | | /* |
1102 | | * Allocate a new tracking structure. Most of the time |
1103 | | * there are no duplicates, so this is fine. |
1104 | | */ |
1105 | 0 | if (client->connection) { |
1106 | 0 | MEM(track = talloc_zero_pooled_object(client, fr_io_track_t, 1, sizeof(*track) + 64)); |
1107 | 0 | track->address = client->connection->address; |
1108 | 0 | } else { |
1109 | 0 | fr_io_address_t *my_address; |
1110 | |
|
1111 | 0 | MEM(track = talloc_zero_pooled_object(client, fr_io_track_t, 1, sizeof(*track) + sizeof(*track->address) + 64)); |
1112 | 0 | MEM(track->address = my_address = talloc(track, fr_io_address_t)); |
1113 | |
|
1114 | 0 | *my_address = *address; |
1115 | 0 | my_address->radclient = client->radclient; |
1116 | 0 | } |
1117 | |
|
1118 | 0 | track->li = li; |
1119 | 0 | track->client = client; |
1120 | |
|
1121 | 0 | track->timestamp = recv_time; |
1122 | 0 | track->packets = 1; |
1123 | | |
1124 | | /* |
1125 | | * We're not tracking duplicates, so just return the |
1126 | | * tracking entry. This tracks src/dst IP/port, client, |
1127 | | * receive time, etc. |
1128 | | */ |
1129 | 0 | if (!client->inst->app_io->track_duplicates) { |
1130 | 0 | client->packets++; |
1131 | 0 | talloc_set_destructor(track, track_free); |
1132 | 0 | return track; |
1133 | 0 | } |
1134 | | |
1135 | | /* |
1136 | | * We are checking for duplicates, see if there is a dup |
1137 | | * already in the tree. |
1138 | | */ |
1139 | 0 | track->packet = client->inst->app_io->track_create(client->inst->app_io_instance, |
1140 | 0 | client->thread->child->thread_instance, |
1141 | 0 | client->radclient, |
1142 | 0 | track, packet, packet_len); |
1143 | 0 | if (!track->packet) { |
1144 | 0 | talloc_free(track); |
1145 | 0 | return NULL; |
1146 | 0 | } |
1147 | | |
1148 | | /* |
1149 | | * No existing duplicate. Return the new tracking entry. |
1150 | | */ |
1151 | 0 | old = fr_rb_find(client->table, track); |
1152 | 0 | if (!old) goto do_insert; |
1153 | | |
1154 | 0 | fr_assert(old->client == client); |
1155 | | |
1156 | | /* |
1157 | | * It cannot be both in the free list and in the tracking table. |
1158 | | * |
1159 | | * 2020-08-17, this assertion fails randomly in travis. |
1160 | | * Which means that "track" was in the free list, *and* |
1161 | | * in the rbtree. |
1162 | | */ |
1163 | 0 | fr_assert(old != track); |
1164 | | |
1165 | | /* |
1166 | | * The new packet has the same dedup fields as the old |
1167 | | * one, BUT it may be a conflicting packet. Check for |
1168 | | * that via a simple memcmp(). |
1169 | | * |
1170 | | * It's an exact duplicate. Drop the new one and |
1171 | | * use the old one. |
1172 | | * |
1173 | | * If there's a cached reply, the caller will take care |
1174 | | * of sending it to the network layer. |
1175 | | */ |
1176 | 0 | len = talloc_array_length(old->packet); |
1177 | 0 | if ((len == talloc_array_length(track->packet)) && |
1178 | 0 | (memcmp(old->packet, track->packet, len) == 0)) { |
1179 | 0 | fr_assert(old != track); |
1180 | | |
1181 | | /* |
1182 | | * Ignore duplicates while the client is |
1183 | | * still pending. |
1184 | | */ |
1185 | 0 | if (client->state == PR_CLIENT_PENDING) { |
1186 | 0 | DEBUG("Ignoring duplicate packet while client %s is still pending dynamic definition", |
1187 | 0 | client->radclient->shortname); |
1188 | 0 | talloc_free(track); |
1189 | 0 | return NULL; |
1190 | 0 | } |
1191 | | |
1192 | 0 | *is_dup = true; |
1193 | 0 | old->packets++; |
1194 | 0 | talloc_free(track); |
1195 | | |
1196 | | /* |
1197 | | * Retransmits can sit in the outbound queue for |
1198 | | * a while. We don't want to time out this |
1199 | | * struct while the packet is in the outbound |
1200 | | * queue. |
1201 | | */ |
1202 | 0 | FR_TIMER_DISARM(old->ev); |
1203 | 0 | return old; |
1204 | 0 | } |
1205 | | |
1206 | | /* |
1207 | | * Else it's a conflicting packet. Which is OK if we |
1208 | | * already have a reply. We just delete the old entry, |
1209 | | * and insert the new one. |
1210 | | * |
1211 | | * If there's no reply, then the old request is still |
1212 | | * "live". Delete the old one from the tracking tree, |
1213 | | * and return the new one. |
1214 | | */ |
1215 | 0 | if (old->reply_len || old->do_not_respond) { |
1216 | 0 | talloc_free(old); |
1217 | |
|
1218 | 0 | } else { |
1219 | 0 | fr_assert(client == old->client); |
1220 | |
|
1221 | 0 | if (!fr_rb_delete(client->table, old)) { |
1222 | 0 | fr_assert(0); |
1223 | 0 | } |
1224 | 0 | FR_TIMER_DELETE(&old->ev); |
1225 | |
|
1226 | 0 | talloc_set_destructor(old, track_free); |
1227 | |
|
1228 | 0 | old->discard = true; /* don't send any reply, there's nowhere for it to go */ |
1229 | 0 | } |
1230 | |
|
1231 | 0 | do_insert: |
1232 | 0 | if (!fr_rb_insert(client->table, track)) { |
1233 | 0 | fr_assert(0); |
1234 | 0 | } |
1235 | |
|
1236 | 0 | client->packets++; |
1237 | 0 | talloc_set_destructor(track, track_dedup_free); |
1238 | 0 | return track; |
1239 | 0 | } |
1240 | | |
1241 | | |
1242 | | static int pending_free(fr_io_pending_packet_t *pending) |
1243 | 0 | { |
1244 | 0 | fr_io_track_t *track = pending->track; |
1245 | | |
1246 | | /* |
1247 | | * Note that we don't check timestamps, replies, etc. If |
1248 | | * a packet is pending, then any conflicting packet gets |
1249 | | * the "pending" entry marked as such, and a new entry |
1250 | | * added. Any duplicate packet gets suppressed. And |
1251 | | * because the packets are pending, track->reply MUST be |
1252 | | * NULL. |
1253 | | */ |
1254 | 0 | fr_assert(track->packets > 0); |
1255 | 0 | track->packets--; |
1256 | | |
1257 | | /* |
1258 | | * No more packets using this tracking entry, |
1259 | | * delete it. |
1260 | | */ |
1261 | 0 | if (track->packets == 0) talloc_free(track); |
1262 | |
|
1263 | 0 | return 0; |
1264 | 0 | } |
1265 | | |
1266 | | static fr_io_pending_packet_t *fr_io_pending_alloc(fr_io_connection_t *connection, fr_io_client_t *client, |
1267 | | uint8_t const *buffer, size_t packet_len, |
1268 | | fr_io_track_t *track, |
1269 | | int priority) |
1270 | 0 | { |
1271 | 0 | fr_io_pending_packet_t *pending; |
1272 | |
|
1273 | 0 | MEM(pending = talloc_zero(client->pending, fr_io_pending_packet_t)); |
1274 | |
|
1275 | 0 | MEM(pending->buffer = talloc_memdup(pending, buffer, packet_len)); |
1276 | 0 | pending->buffer_len = packet_len; |
1277 | 0 | pending->priority = priority; |
1278 | 0 | pending->track = track; |
1279 | 0 | pending->recv_time = track->timestamp; /* there can only be one */ |
1280 | |
|
1281 | 0 | talloc_set_destructor(pending, pending_free); |
1282 | | |
1283 | | /* |
1284 | | * Insert the pending packet for this client. If it |
1285 | | * fails, silently discard the packet. |
1286 | | */ |
1287 | 0 | if (fr_heap_insert(&client->pending, pending) < 0) { |
1288 | 0 | talloc_free(pending); |
1289 | 0 | return NULL; |
1290 | 0 | } |
1291 | | |
1292 | | /* |
1293 | | * We only track pending packets for the |
1294 | | * main socket. For connected sockets, |
1295 | | * we pause the FD, so the number of |
1296 | | * pending packets will always be small. |
1297 | | */ |
1298 | 0 | if (!connection) client->thread->num_pending_packets++; |
1299 | |
|
1300 | 0 | return pending; |
1301 | 0 | } |
1302 | | |
1303 | | |
1304 | | /* |
1305 | | * Order clients in the alive_clients heap, based on their IP |
1306 | | * address. |
1307 | | * |
1308 | | * This function is only used for the "main" socket. Clients |
1309 | | * from connections do not use it. |
1310 | | */ |
1311 | | static int8_t alive_client_cmp(void const *one, void const *two) |
1312 | 0 | { |
1313 | 0 | fr_io_client_t const *a = talloc_get_type_abort_const(one, fr_io_client_t); |
1314 | 0 | fr_io_client_t const *b = talloc_get_type_abort_const(two, fr_io_client_t); |
1315 | |
|
1316 | 0 | return fr_ipaddr_cmp(&a->src_ipaddr, &b->src_ipaddr); |
1317 | 0 | } |
1318 | | |
1319 | | /** Implement 99% of the read routines. |
1320 | | * |
1321 | | * The app_io->read does the transport-specific data read. |
1322 | | */ |
1323 | | static ssize_t mod_read(fr_listen_t *li, void **packet_ctx, fr_time_t *recv_time_p, |
1324 | | uint8_t *buffer, size_t buffer_len, size_t *leftover) |
1325 | 0 | { |
1326 | 0 | fr_io_instance_t const *inst; |
1327 | 0 | fr_io_thread_t *thread; |
1328 | 0 | ssize_t packet_len = -1; |
1329 | 0 | fr_time_t recv_time = fr_time_wrap(0); |
1330 | 0 | fr_io_client_t *client; |
1331 | 0 | fr_io_address_t address; |
1332 | 0 | fr_io_connection_t my_connection, *connection; |
1333 | 0 | fr_io_pending_packet_t *pending = NULL; |
1334 | 0 | fr_io_track_t *track; |
1335 | 0 | fr_listen_t *child; |
1336 | 0 | int value, accept_fd = -1; |
1337 | 0 | uint32_t priority = PRIORITY_NORMAL; |
1338 | | |
1339 | | /** Log that we ignore clients in debug mode, or when it's enabled for a listener |
1340 | | */ |
1341 | 0 | #define LOG_IGNORED_CLIENTS(_inst) ((_inst)->log_ignored_clients || fr_debug_lvl >= 1) |
1342 | |
|
1343 | 0 | get_inst(li, &inst, &thread, &connection, &child); |
1344 | |
|
1345 | 0 | track = NULL; |
1346 | | |
1347 | | /* |
1348 | | * There was data left over from the previous read, go |
1349 | | * get the rest of it now. We MUST do this instead of |
1350 | | * popping a pending packet, because the leftover bytes |
1351 | | * are already in the output buffer. |
1352 | | */ |
1353 | 0 | if (*leftover) goto do_read; |
1354 | | |
1355 | 0 | redo: |
1356 | | /* |
1357 | | * Read one pending packet. The packet may be pending |
1358 | | * because of dynamic client definitions, or because it's |
1359 | | * for a connected UDP socket, and was sent over by the |
1360 | | * "master" UDP socket. |
1361 | | */ |
1362 | 0 | if (connection) { |
1363 | | /* |
1364 | | * The connection is dead. Tell the network side |
1365 | | * to close it. |
1366 | | */ |
1367 | 0 | if (connection->dead) { |
1368 | 0 | DEBUG("Dead connection %s", connection->name); |
1369 | 0 | return -1; |
1370 | 0 | } |
1371 | | |
1372 | 0 | pending = fr_heap_pop(&connection->client->pending); |
1373 | |
|
1374 | 0 | } else if (thread->pending_clients) { |
1375 | 0 | pending = pending_packet_pop(thread); |
1376 | |
|
1377 | 0 | } else { |
1378 | 0 | pending = NULL; |
1379 | 0 | } |
1380 | | |
1381 | 0 | if (pending) { |
1382 | 0 | fr_assert(buffer_len >= pending->buffer_len); |
1383 | 0 | track = pending->track; |
1384 | | |
1385 | | /* |
1386 | | * Clear the destructor as we now own the |
1387 | | * tracking entry. |
1388 | | */ |
1389 | 0 | talloc_set_destructor(pending, NULL); |
1390 | | |
1391 | | /* |
1392 | | * We received a conflicting packet while this |
1393 | | * packet was pending. Discard this entry and |
1394 | | * try to get another one. |
1395 | | * |
1396 | | * Note that the pending heap is *simple*. We |
1397 | | * just track priority and recv_time. This means |
1398 | | * it's fast, but also that it's hard to look up |
1399 | | * random packets in the pending heap. |
1400 | | */ |
1401 | 0 | if (fr_time_neq(pending->recv_time, track->timestamp)) { |
1402 | 0 | DEBUG3("Discarding old packet"); |
1403 | 0 | TALLOC_FREE(pending); |
1404 | 0 | goto redo; |
1405 | 0 | } |
1406 | | |
1407 | | /* |
1408 | | * We have a valid packet. Copy it over to the |
1409 | | * caller, and return. |
1410 | | */ |
1411 | 0 | *packet_ctx = track; |
1412 | 0 | *leftover = 0; |
1413 | 0 | recv_time = *recv_time_p = pending->recv_time; |
1414 | 0 | client = track->client; |
1415 | |
|
1416 | 0 | memcpy(buffer, pending->buffer, pending->buffer_len); |
1417 | 0 | packet_len = pending->buffer_len; |
1418 | | |
1419 | | /* |
1420 | | * Shouldn't be necessary, but what the heck... |
1421 | | */ |
1422 | 0 | memcpy(&address, track->address, sizeof(address)); |
1423 | 0 | TALLOC_FREE(pending); |
1424 | | |
1425 | | /* |
1426 | | * Skip over all kinds of logic to find / |
1427 | | * allocate the client, when we don't need to do |
1428 | | * it any more. |
1429 | | */ |
1430 | 0 | goto have_client; |
1431 | |
|
1432 | 0 | } else if (!connection && (inst->ipproto == IPPROTO_TCP)) { |
1433 | 0 | struct sockaddr_storage saremote; |
1434 | 0 | socklen_t salen; |
1435 | |
|
1436 | 0 | salen = sizeof(saremote); |
1437 | | |
1438 | | /* |
1439 | | * We're a TCP socket but are NOT connected. We |
1440 | | * must be the master socket. Accept the new |
1441 | | * connection, and figure out src/dst IP/port. |
1442 | | */ |
1443 | 0 | accept_fd = accept(child->fd, |
1444 | 0 | (struct sockaddr *) &saremote, &salen); |
1445 | | |
1446 | | /* |
1447 | | * Couldn't open a NEW socket, but THIS ONE is |
1448 | | * OK. So don't return -1. |
1449 | | */ |
1450 | 0 | if (accept_fd < 0) { |
1451 | 0 | RATE_LIMIT_LOCAL(&thread->rate_limit.accept_failed, |
1452 | 0 | INFO, "proto_%s - failed to accept new socket: %s", |
1453 | 0 | inst->app->common.name, fr_syserror(errno)); |
1454 | 0 | return 0; |
1455 | 0 | } |
1456 | | |
1457 | | /* |
1458 | | * Set the new descriptor to be non-blocking. |
1459 | | */ |
1460 | 0 | (void) fr_nonblock(accept_fd); |
1461 | |
|
1462 | | #ifdef STATIC_ANALYZER |
1463 | | saremote.ss_family = AF_INET; /* static analyzer doesn't know that accept() initializes this */ |
1464 | | #endif |
1465 | | |
1466 | | /* |
1467 | | * Get IP addresses only if we have IP addresses. |
1468 | | */ |
1469 | 0 | if ((saremote.ss_family == AF_INET) || (saremote.ss_family == AF_INET6)) { |
1470 | 0 | memset(&address.socket, 0, sizeof(address.socket)); |
1471 | 0 | (void) fr_ipaddr_from_sockaddr(&address.socket.inet.src_ipaddr, &address.socket.inet.src_port, |
1472 | 0 | &saremote, salen); |
1473 | 0 | salen = sizeof(saremote); |
1474 | | |
1475 | | /* |
1476 | | * @todo - only if the local listen address is "*". |
1477 | | */ |
1478 | 0 | (void) getsockname(accept_fd, (struct sockaddr *) &saremote, &salen); |
1479 | 0 | (void) fr_ipaddr_from_sockaddr(&address.socket.inet.dst_ipaddr, &address.socket.inet.dst_port, |
1480 | 0 | &saremote, salen); |
1481 | 0 | address.socket.type = (inst->ipproto == IPPROTO_TCP) ? SOCK_STREAM : SOCK_DGRAM; |
1482 | 0 | address.socket.fd = accept_fd; |
1483 | 0 | } |
1484 | |
|
1485 | 0 | } else { |
1486 | 0 | fr_io_address_t *local_address; |
1487 | | |
1488 | | /* |
1489 | | * We're either not a TCP socket, or we are a |
1490 | | * connected TCP socket. Just read it. |
1491 | | */ |
1492 | 0 | do_read: |
1493 | 0 | local_address = &address; |
1494 | | |
1495 | | /* |
1496 | | * @todo - For connected TCP sockets which are |
1497 | | * dynamically defined, the app_io read() |
1498 | | * function should stop reading the socket if the |
1499 | | * server is busy. That change puts TCP |
1500 | | * backpressure on the client. |
1501 | | * |
1502 | | * @todo TLS - for TLS and dynamic sockets, do |
1503 | | * the SSL setup here, but have a structure which |
1504 | | * describes the TLS data and run THAT through |
1505 | | * the dynamic client definition, instead of |
1506 | | * using normal packets. Or, rely on the app_io |
1507 | | * read() function to do all TLS work? Given |
1508 | | * that some protocols have "starttls" beginning |
1509 | | * after a clear-text exchange, it's likely best |
1510 | | * to have yet another layer of trampoline |
1511 | | * functions which do all of the TLS work. |
1512 | | */ |
1513 | 0 | packet_len = inst->app_io->read(child, (void **) &local_address, &recv_time, |
1514 | 0 | buffer, buffer_len, leftover); |
1515 | 0 | if (packet_len <= 0) { |
1516 | 0 | return packet_len; |
1517 | 0 | } |
1518 | | |
1519 | | /* |
1520 | | * Not allowed? Discard it. The priority() |
1521 | | * function has done any complaining, if |
1522 | | * necessary. |
1523 | | */ |
1524 | 0 | if (inst->app->priority) { |
1525 | 0 | value = inst->app->priority(inst->app_instance, buffer, packet_len); |
1526 | 0 | if (value <= 0) { |
1527 | 0 | static fr_rate_limit_t bad_type; |
1528 | | |
1529 | | /* |
1530 | | * @todo - unix sockets. We need to use |
1531 | | * the "name" of the socket, in the |
1532 | | * listener? |
1533 | | */ |
1534 | 0 | if (LOG_IGNORED_CLIENTS(inst)) { |
1535 | 0 | RATE_LIMIT_LOCAL(thread ? &thread->rate_limit.bad_type : &bad_type, INFO, |
1536 | 0 | "proto_%s - ignoring packet from IP %pV. It is not configured as 'type = ...'", |
1537 | 0 | inst->app_io->common.name, fr_box_ipaddr(address.socket.inet.src_ipaddr)); |
1538 | 0 | } |
1539 | 0 | return 0; |
1540 | 0 | } |
1541 | 0 | priority = value; |
1542 | 0 | } |
1543 | | |
1544 | | /* |
1545 | | * If the connection is pending, pause reading of |
1546 | | * more packets. If mod_write() accepts the |
1547 | | * connection, it will resume reading. |
1548 | | * Otherwise, it will close the socket without |
1549 | | * resuming it. |
1550 | | */ |
1551 | 0 | if (connection && |
1552 | 0 | (connection->client->state == PR_CLIENT_PENDING)) { |
1553 | 0 | fr_assert(!connection->paused); |
1554 | |
|
1555 | 0 | connection->paused = true; |
1556 | 0 | (void) fr_event_filter_update(connection->el, |
1557 | 0 | child->fd, |
1558 | 0 | FR_EVENT_FILTER_IO, pause_read); |
1559 | 0 | } |
1560 | 0 | } |
1561 | | |
1562 | | /* |
1563 | | * Look up the client, unless we already have one (for a |
1564 | | * connected socket). |
1565 | | */ |
1566 | 0 | if (!connection) { |
1567 | 0 | client = fr_trie_lookup_by_key(thread->trie, |
1568 | 0 | &address.socket.inet.src_ipaddr.addr, address.socket.inet.src_ipaddr.prefix); |
1569 | 0 | fr_assert(!client || !client->connection); |
1570 | |
|
1571 | 0 | } else { |
1572 | 0 | client = connection->client; |
1573 | | |
1574 | | /* |
1575 | | * We don't care what the read function says |
1576 | | * about address. We have it already. |
1577 | | */ |
1578 | 0 | address = *connection->address; |
1579 | 0 | } |
1580 | | |
1581 | | /* |
1582 | | * Negative cache entry. Drop the packet. |
1583 | | */ |
1584 | 0 | if (client && client->state == PR_CLIENT_NAK) { |
1585 | 0 | if (accept_fd >= 0) close(accept_fd); |
1586 | 0 | return 0; |
1587 | 0 | } |
1588 | | |
1589 | | /* |
1590 | | * If there's no client, try to pull one from the global |
1591 | | * / static client list. Or if dynamic clients are |
1592 | | * allowed, try to define a dynamic client. |
1593 | | */ |
1594 | 0 | if (!client) { |
1595 | 0 | fr_client_t *radclient = NULL; |
1596 | 0 | fr_io_client_state_t state; |
1597 | 0 | fr_ipaddr_t const *network = NULL; |
1598 | 0 | char const *error; |
1599 | | |
1600 | | /* |
1601 | | * We MUST be the master socket. |
1602 | | */ |
1603 | 0 | fr_assert(!connection); |
1604 | |
|
1605 | 0 | radclient = inst->app_io->client_find(thread->child, &address.socket.inet.src_ipaddr, inst->ipproto); |
1606 | 0 | if (radclient) { |
1607 | 0 | state = PR_CLIENT_STATIC; |
1608 | | |
1609 | | /* |
1610 | | * Make our own copy that we can modify it. |
1611 | | */ |
1612 | 0 | MEM(radclient = radclient_clone(thread, radclient)); |
1613 | 0 | radclient->active = true; |
1614 | |
|
1615 | 0 | } else if (inst->dynamic_clients) { |
1616 | 0 | if (inst->max_clients && (fr_heap_num_elements(thread->alive_clients) >= inst->max_clients)) { |
1617 | 0 | error = "Too many dynamic clients have been defined"; |
1618 | 0 | goto ignore; |
1619 | 0 | } |
1620 | | |
1621 | | /* |
1622 | | * Look up the allowed networks. |
1623 | | */ |
1624 | 0 | network = fr_trie_lookup_by_key(inst->networks, &address.socket.inet.src_ipaddr.addr, |
1625 | 0 | address.socket.inet.src_ipaddr.prefix); |
1626 | 0 | if (!network) { |
1627 | 0 | error = "Address is outside of the the 'allow' network range"; |
1628 | 0 | goto ignore; |
1629 | 0 | } |
1630 | | |
1631 | | /* |
1632 | | * It exists, but it's a "deny" rule, ignore it. |
1633 | | */ |
1634 | 0 | if (network->af == AF_UNSPEC) { |
1635 | 0 | error = "Address is forbidden by the 'deny' network range"; |
1636 | 0 | goto ignore; |
1637 | 0 | } |
1638 | | |
1639 | | /* |
1640 | | * Allocate our local radclient as a |
1641 | | * placeholder for the dynamic client. |
1642 | | */ |
1643 | 0 | radclient = radclient_alloc(thread, inst->ipproto, &address); |
1644 | 0 | state = PR_CLIENT_PENDING; |
1645 | |
|
1646 | 0 | } else { |
1647 | 0 | char const *msg; |
1648 | |
|
1649 | 0 | error = "No matching 'client' definition was found"; |
1650 | |
|
1651 | 0 | ignore: |
1652 | 0 | if (accept_fd < 0) { |
1653 | 0 | msg = "packet"; |
1654 | 0 | } else { |
1655 | 0 | msg = "connection attempt"; |
1656 | 0 | close(accept_fd); |
1657 | 0 | } |
1658 | |
|
1659 | 0 | if (LOG_IGNORED_CLIENTS(inst)) { |
1660 | 0 | static fr_rate_limit_t unknown_client; |
1661 | 0 | RATE_LIMIT_LOCAL(thread ? &thread->rate_limit.unknown_client : &unknown_client, |
1662 | 0 | ERROR, "proto_%s - Ignoring %s from IP address %pV - %s", |
1663 | 0 | inst->app_io->common.name, msg, fr_box_ipaddr(address.socket.inet.src_ipaddr), |
1664 | 0 | error); |
1665 | 0 | } |
1666 | |
|
1667 | 0 | return 0; |
1668 | 0 | } |
1669 | | |
1670 | 0 | MEM(client = client_alloc(thread, state, inst, thread, radclient, network)); |
1671 | | |
1672 | | /* |
1673 | | * Parent the dynamic client radclient off the client - it |
1674 | | * is the client which gets freed by the dynamic client timers. |
1675 | | */ |
1676 | 0 | if (state == PR_CLIENT_PENDING) talloc_steal(client, radclient); |
1677 | 0 | } |
1678 | | |
1679 | 0 | have_client: |
1680 | 0 | fr_assert(client->state != PR_CLIENT_INVALID); |
1681 | 0 | fr_assert(client->state != PR_CLIENT_NAK); |
1682 | | |
1683 | | /* |
1684 | | * We've accepted a new connection. Go allocate it, and |
1685 | | * let it read from the socket. |
1686 | | */ |
1687 | 0 | if (accept_fd >= 0) { |
1688 | 0 | if (!fr_io_connection_alloc(inst, thread, client, accept_fd, &address, NULL)) { |
1689 | 0 | static fr_rate_limit_t alloc_failed; |
1690 | |
|
1691 | 0 | RATE_LIMIT_LOCAL(thread ? &thread->rate_limit.conn_alloc_failed : &alloc_failed, |
1692 | 0 | ERROR, "Failed to allocate connection from client %s", client->radclient->shortname); |
1693 | 0 | } |
1694 | |
|
1695 | 0 | return 0; |
1696 | 0 | } |
1697 | | |
1698 | | /* |
1699 | | * No connected sockets, OR we are the connected socket. |
1700 | | * |
1701 | | * Track this packet and return it if necessary. |
1702 | | */ |
1703 | 0 | if (connection || !client->use_connected) { |
1704 | 0 | fr_io_track_t *to_free = NULL; |
1705 | | |
1706 | | /* |
1707 | | * Add the packet to the tracking table, if it's |
1708 | | * not already there. Pending packets will be in |
1709 | | * the tracking table, but won't be counted as |
1710 | | * "live" packets. |
1711 | | */ |
1712 | 0 | if (!track) { |
1713 | 0 | static fr_rate_limit_t tracking_failed; |
1714 | 0 | bool is_dup = false; |
1715 | |
|
1716 | 0 | track = fr_io_track_add(li, client, &address, buffer, packet_len, recv_time, &is_dup); |
1717 | 0 | if (!track) { |
1718 | 0 | RATE_LIMIT_LOCAL(thread ? &thread->rate_limit.tracking_failed : &tracking_failed, |
1719 | 0 | ERROR, "Failed tracking packet from client %s - discarding it", |
1720 | 0 | client->radclient->shortname); |
1721 | 0 | return 0; |
1722 | 0 | } |
1723 | | |
1724 | | /* |
1725 | | * If there's a cached reply, just send that and don't do anything else. |
1726 | | */ |
1727 | 0 | if (is_dup) { |
1728 | 0 | fr_network_t *nr; |
1729 | |
|
1730 | 0 | if (track->do_not_respond) { |
1731 | 0 | DEBUG("Ignoring retransmit from client %s - we are not responding to this request", client->radclient->shortname); |
1732 | 0 | return 0; |
1733 | 0 | } |
1734 | | |
1735 | 0 | if (track->discard) { |
1736 | 0 | DEBUG("Ignoring transmit from client %s - we previously received a newer / conflicting packet", client->radclient->shortname); |
1737 | 0 | return 0; |
1738 | 0 | } |
1739 | | |
1740 | 0 | if (!track->reply) { |
1741 | 0 | fr_assert(!track->finished); |
1742 | 0 | DEBUG("Ignoring retransmit from client %s - we are still processing the request", client->radclient->shortname); |
1743 | 0 | return 0; |
1744 | 0 | } |
1745 | | |
1746 | 0 | if (connection) { |
1747 | 0 | nr = connection->nr; |
1748 | 0 | } else { |
1749 | 0 | nr = thread->nr; |
1750 | 0 | } |
1751 | | |
1752 | | /* |
1753 | | * @todo - mark things up so that we know to keep 'track' around |
1754 | | * until the packet is actually written to the network. OR, add |
1755 | | * a network API so that the talloc_free() function can remove |
1756 | | * the packet from the queue of packets to be retransmitted. |
1757 | | * |
1758 | | * Perhaps via having fr_network_listen_write() return a pointer |
1759 | | * to the localized message, and then caching that in the tracking |
1760 | | * structure. |
1761 | | */ |
1762 | 0 | DEBUG("Sending duplicate reply to client %s", client->radclient->shortname); |
1763 | 0 | fr_network_listen_write(nr, li, track->reply, track->reply_len, |
1764 | 0 | track, track->timestamp); |
1765 | 0 | return 0; |
1766 | 0 | } |
1767 | | |
1768 | | /* |
1769 | | * Got to free this if we don't process the packet. |
1770 | | */ |
1771 | 0 | to_free = track; |
1772 | 0 | } |
1773 | | |
1774 | | /* |
1775 | | * This is a pending dynamic client. See if we |
1776 | | * have to either run the dynamic client code to |
1777 | | * define the client, OR to push the packet onto |
1778 | | * the pending queue for this client. |
1779 | | */ |
1780 | 0 | if (client->state == PR_CLIENT_PENDING) { |
1781 | | /* |
1782 | | * Track pending packets for the master |
1783 | | * socket. Connected sockets are paused |
1784 | | * as soon as they are defined, so we |
1785 | | * won't be reading any more packets from |
1786 | | * them. |
1787 | | * |
1788 | | * Since we don't have pending packets |
1789 | | * for connected sockets, we don't need |
1790 | | * to track pending packets. |
1791 | | */ |
1792 | 0 | if (!connection && inst->max_pending_packets && (thread->num_pending_packets >= inst->max_pending_packets)) { |
1793 | 0 | RATE_LIMIT_LOCAL(&thread->rate_limit.too_many_pending, |
1794 | 0 | ERROR, "Too many pending dynamic client packets for listener - discarding packet from %pV", |
1795 | 0 | fr_box_ipaddr(client->src_ipaddr)); |
1796 | |
|
1797 | 0 | discard: |
1798 | 0 | talloc_free(to_free); |
1799 | 0 | return 0; |
1800 | 0 | } |
1801 | | |
1802 | | /* |
1803 | | * Allocate the pending packet structure. |
1804 | | */ |
1805 | 0 | pending = fr_io_pending_alloc(connection, client, buffer, packet_len, |
1806 | 0 | track, priority); |
1807 | 0 | if (!pending) { |
1808 | 0 | static fr_rate_limit_t alloc_failed; |
1809 | 0 | RATE_LIMIT_LOCAL(thread ? &thread->rate_limit.alloc_failed : &alloc_failed, |
1810 | 0 | ERROR, "proto_%s - Failed allocating space for dynamic client %pV - discarding packet", |
1811 | 0 | inst->app_io->common.name, fr_box_ipaddr(client->src_ipaddr)); |
1812 | 0 | goto discard; |
1813 | 0 | } |
1814 | | |
1815 | 0 | if (fr_heap_num_elements(client->pending) > 1) { |
1816 | 0 | DEBUG("Verification is still pending for dynamic client %pV - queuing additional packet(s)", |
1817 | 0 | fr_box_ipaddr(client->src_ipaddr)); |
1818 | 0 | return 0; |
1819 | 0 | } |
1820 | | |
1821 | | /* |
1822 | | * Tell this packet that it's defining a |
1823 | | * dynamic client. |
1824 | | */ |
1825 | 0 | track->dynamic = recv_time; |
1826 | |
|
1827 | 0 | INFO("proto_%s - Verification started for packet from dynamic client %pV - queuing new packets", |
1828 | 0 | inst->app_io->common.name, fr_box_ipaddr(client->src_ipaddr)); |
1829 | 0 | } |
1830 | | |
1831 | | /* |
1832 | | * Remove all cleanup timers for the client / |
1833 | | * connection. It's still in use, so we don't |
1834 | | * want to clean it up. |
1835 | | */ |
1836 | 0 | if (fr_timer_armed(client->ev)) { |
1837 | 0 | FR_TIMER_DELETE_RETURN(&client->ev); |
1838 | 0 | client->ready_to_delete = false; |
1839 | 0 | } |
1840 | | |
1841 | | /* |
1842 | | * Remove cleanup timers for the connection parent. |
1843 | | */ |
1844 | 0 | if (connection && fr_timer_armed(connection->parent->ev)) { |
1845 | 0 | FR_TIMER_DELETE_RETURN(&connection->parent->ev); |
1846 | 0 | connection->parent->ready_to_delete = false; |
1847 | 0 | } |
1848 | | |
1849 | | /* |
1850 | | * Return the packet. |
1851 | | */ |
1852 | 0 | *recv_time_p = track->timestamp; |
1853 | 0 | *packet_ctx = track; |
1854 | 0 | return packet_len; |
1855 | 0 | } |
1856 | | |
1857 | | /* |
1858 | | * |
1859 | | */ |
1860 | 0 | fr_assert(!pending); |
1861 | | |
1862 | | /* |
1863 | | * This must be the main UDP socket which creates |
1864 | | * connections. |
1865 | | */ |
1866 | 0 | fr_assert(inst->ipproto == IPPROTO_UDP); |
1867 | | |
1868 | | /* |
1869 | | * We're using connected sockets, but this socket isn't |
1870 | | * connected. It must be the master socket. The master |
1871 | | * can either be STATIC, DYNAMIC, or PENDING. Whatever |
1872 | | * the state, the child socket will take care of handling |
1873 | | * the packet. e.g. dynamic clients, etc. |
1874 | | */ |
1875 | 0 | { |
1876 | 0 | bool nak = false; |
1877 | |
|
1878 | 0 | my_connection.address = &address; |
1879 | |
|
1880 | 0 | pthread_mutex_lock(&client->mutex); |
1881 | 0 | connection = fr_hash_table_find(client->ht, &my_connection); |
1882 | 0 | if (connection) nak = (connection->client->state == PR_CLIENT_NAK); |
1883 | 0 | pthread_mutex_unlock(&client->mutex); |
1884 | | |
1885 | | /* |
1886 | | * The connection is in NAK state, ignore packets |
1887 | | * for it. |
1888 | | */ |
1889 | 0 | if (nak) { |
1890 | 0 | RATE_LIMIT_LOCAL(&thread->rate_limit.repeat_nak, ERROR, "proto_%s - Discarding repeated packet from NAK'd dynamic client %pV", |
1891 | 0 | inst->app_io->common.name, fr_box_ipaddr(address.socket.inet.src_ipaddr)); |
1892 | |
|
1893 | 0 | DEBUG("Discarding packet to NAKed connection %s", connection->name); |
1894 | 0 | return 0; |
1895 | 0 | } |
1896 | 0 | } |
1897 | | |
1898 | | /* |
1899 | | * No existing connection, create one. |
1900 | | */ |
1901 | 0 | if (!connection) { |
1902 | 0 | connection = fr_io_connection_alloc(inst, thread, client, -1, &address, NULL); |
1903 | 0 | if (!connection) { |
1904 | 0 | RATE_LIMIT_LOCAL(&thread->rate_limit.conn_alloc_failed, |
1905 | 0 | ERROR, "Failed to allocate connection from client %s. Discarding packet.", client->radclient->shortname); |
1906 | 0 | return 0; |
1907 | 0 | } |
1908 | 0 | } |
1909 | | |
1910 | 0 | DEBUG("Sending packet to connection %s", connection->name); |
1911 | | |
1912 | | /* |
1913 | | * Inject the packet into the connected socket. It will |
1914 | | * process the packet as if it came in from the network. |
1915 | | * |
1916 | | * @todo future - after creating the connection, put the |
1917 | | * current packet into connection->pending, instead of |
1918 | | * inject?, and then call fr_network_listen_read() from |
1919 | | * the child's instantiation routine??? |
1920 | | * |
1921 | | * @todo TCP - for ACCEPT sockets, we don't have a |
1922 | | * packet, so don't do this. Instead, the connection |
1923 | | * will take care of figuring out what to do. |
1924 | | * |
1925 | | * We don't need "to_free" after this, as it will be |
1926 | | * tracked in the connected socket. |
1927 | | */ |
1928 | 0 | if (fr_network_listen_inject(connection->nr, connection->listen, |
1929 | 0 | buffer, packet_len, recv_time) < 0) { |
1930 | 0 | RATE_LIMIT_LOCAL(&thread->rate_limit.queue_full, PERROR, |
1931 | 0 | "proto_%s - Discarding packet from dynamic client %pV - cannot push packet to connected socket", |
1932 | 0 | inst->app_io->common.name, fr_box_ipaddr(address.socket.inet.src_ipaddr)); |
1933 | | /* |
1934 | | * Don't return an error, because that will cause the listener to close its socket. |
1935 | | */ |
1936 | 0 | } |
1937 | |
|
1938 | 0 | return 0; |
1939 | 0 | } |
1940 | | |
1941 | | /** Inject a packet to a connection. |
1942 | | * |
1943 | | * Always called in the context of the network. |
1944 | | */ |
1945 | | static int mod_inject(fr_listen_t *li, uint8_t const *buffer, size_t buffer_len, fr_time_t recv_time) |
1946 | 0 | { |
1947 | 0 | fr_io_instance_t const *inst; |
1948 | 0 | int priority; |
1949 | 0 | bool is_dup = false; |
1950 | 0 | fr_io_connection_t *connection; |
1951 | 0 | fr_io_pending_packet_t *pending; |
1952 | 0 | fr_io_track_t *track; |
1953 | |
|
1954 | 0 | get_inst(li, &inst, NULL, &connection, NULL); |
1955 | |
|
1956 | 0 | if (!connection) { |
1957 | 0 | DEBUG2("Received injected packet for an unconnected socket."); |
1958 | 0 | return -1; |
1959 | 0 | } |
1960 | | |
1961 | 0 | if (inst->app->priority) { |
1962 | 0 | priority = inst->app->priority(inst->app_instance, buffer, buffer_len); |
1963 | 0 | if (priority <= 0) { |
1964 | 0 | return -1; |
1965 | 0 | } |
1966 | 0 | } else { |
1967 | 0 | priority = PRIORITY_NORMAL; |
1968 | 0 | } |
1969 | | |
1970 | | /* |
1971 | | * Track this packet, because that's what mod_read expects. |
1972 | | */ |
1973 | 0 | track = fr_io_track_add(li, connection->client, connection->address, |
1974 | 0 | buffer, buffer_len, recv_time, &is_dup); |
1975 | 0 | if (!track) { |
1976 | 0 | DEBUG2("Failed injecting packet to tracking table"); |
1977 | 0 | return -1; |
1978 | 0 | } |
1979 | | |
1980 | 0 | talloc_get_type_abort(track, fr_io_track_t); |
1981 | | |
1982 | | /* |
1983 | | * @todo future - what to do with duplicates? |
1984 | | */ |
1985 | 0 | fr_assert(!is_dup); |
1986 | | |
1987 | | /* |
1988 | | * Remember to restore this packet later. |
1989 | | */ |
1990 | 0 | pending = fr_io_pending_alloc(connection, connection->client, buffer, buffer_len, |
1991 | 0 | track, priority); |
1992 | 0 | if (!pending) { |
1993 | 0 | DEBUG2("Failed injecting packet due to allocation error"); |
1994 | 0 | return -1; |
1995 | 0 | } |
1996 | | |
1997 | 0 | return 0; |
1998 | 0 | } |
1999 | | |
2000 | | /** Open a new listener |
2001 | | * |
2002 | | */ |
2003 | | static int mod_open(fr_listen_t *li) |
2004 | 0 | { |
2005 | 0 | fr_io_thread_t *thread; |
2006 | 0 | fr_io_instance_t const *inst; |
2007 | |
|
2008 | 0 | thread = li->thread_instance; |
2009 | 0 | inst = li->app_io_instance; |
2010 | |
|
2011 | 0 | if (inst->app_io->open(thread->child) < 0) return -1; |
2012 | | |
2013 | 0 | li->fd = thread->child->fd; /* copy this back up */ |
2014 | | |
2015 | | /* |
2016 | | * Set the name of the socket. |
2017 | | */ |
2018 | 0 | if (!li->app_io->get_name) { |
2019 | 0 | li->name = li->app_io->common.name; |
2020 | 0 | } else { |
2021 | 0 | li->name = li->app_io->get_name(li); |
2022 | 0 | } |
2023 | | |
2024 | | /* |
2025 | | * Note that we're opening a child socket, so we don't |
2026 | | * put it into the list of global listeners. |
2027 | | */ |
2028 | |
|
2029 | 0 | return 0; |
2030 | 0 | } |
2031 | | |
2032 | | |
2033 | | /** Set the event list for a new socket |
2034 | | * |
2035 | | * @param[in] li the listener |
2036 | | * @param[in] el the event list |
2037 | | * @param[in] nr context from the network side |
2038 | | */ |
2039 | | static void mod_event_list_set(fr_listen_t *li, fr_event_list_t *el, void *nr) |
2040 | 0 | { |
2041 | 0 | fr_io_instance_t const *inst; |
2042 | 0 | fr_io_connection_t *connection; |
2043 | 0 | fr_io_thread_t *thread; |
2044 | 0 | fr_listen_t *child; |
2045 | |
|
2046 | 0 | get_inst(li, &inst, &thread, &connection, &child); |
2047 | | |
2048 | | /* |
2049 | | * We're not doing IO, so there are no timers for |
2050 | | * cleaning up packets, dynamic clients, or connections. |
2051 | | */ |
2052 | 0 | if (!inst->submodule) return; |
2053 | | |
2054 | 0 | if (inst->app_io->event_list_set) { |
2055 | 0 | inst->app_io->event_list_set(child, el, nr); |
2056 | 0 | } |
2057 | | |
2058 | | /* |
2059 | | * Set event list and network side for this socket. |
2060 | | */ |
2061 | 0 | if (!connection) { |
2062 | 0 | thread->el = el; |
2063 | 0 | thread->nr = nr; |
2064 | |
|
2065 | 0 | } else { |
2066 | 0 | connection->el = el; |
2067 | 0 | connection->nr = nr; |
2068 | 0 | } |
2069 | 0 | } |
2070 | | |
2071 | | |
2072 | | static void client_expiry_timer(fr_timer_list_t *tl, fr_time_t now, void *uctx) |
2073 | 0 | { |
2074 | 0 | fr_io_client_t *client = talloc_get_type_abort(uctx, fr_io_client_t); |
2075 | 0 | fr_io_instance_t const *inst; |
2076 | 0 | fr_io_connection_t *connection; |
2077 | 0 | fr_time_delta_t delay; |
2078 | 0 | int connections; |
2079 | | |
2080 | | /* |
2081 | | * No event list? We don't need to expire the client. |
2082 | | */ |
2083 | 0 | if (!tl) return; |
2084 | | |
2085 | | // @todo - print out what we plan on doing next |
2086 | 0 | connection = client->connection; |
2087 | 0 | inst = client->inst; |
2088 | |
|
2089 | 0 | fr_assert(client->state != PR_CLIENT_STATIC); |
2090 | | |
2091 | | /* |
2092 | | * Called from the read or write functions with |
2093 | | * now==0, to signal that we have to *set* the timer. |
2094 | | */ |
2095 | 0 | if (fr_time_eq(now, fr_time_wrap(0))) { |
2096 | | /* |
2097 | | * The timer is already set, don't do anything. |
2098 | | */ |
2099 | 0 | if (fr_timer_armed(client->ev)) return; |
2100 | | |
2101 | 0 | switch (client->state) { |
2102 | 0 | case PR_CLIENT_CONNECTED: |
2103 | 0 | fr_assert(connection != NULL); |
2104 | 0 | delay = inst->idle_timeout; |
2105 | 0 | if (fr_time_delta_ispos(client->radclient->limit.idle_timeout) && |
2106 | 0 | (fr_time_delta_lt(client->radclient->limit.idle_timeout, inst->idle_timeout))) { |
2107 | 0 | delay = client->radclient->limit.idle_timeout; |
2108 | 0 | } |
2109 | 0 | break; |
2110 | | |
2111 | 0 | case PR_CLIENT_DYNAMIC: |
2112 | 0 | delay = inst->dynamic_timeout; |
2113 | 0 | break; |
2114 | | |
2115 | 0 | case PR_CLIENT_NAK: |
2116 | 0 | delay = inst->nak_lifetime; |
2117 | 0 | break; |
2118 | | |
2119 | 0 | default: |
2120 | 0 | fr_assert(0 == 1); |
2121 | 0 | return; |
2122 | 0 | } |
2123 | | |
2124 | 0 | DEBUG("TIMER - setting idle timeout to %pVs for connection from client %s", fr_box_time_delta(delay), client->radclient->shortname); |
2125 | |
|
2126 | 0 | goto reset_timer; |
2127 | 0 | } |
2128 | | |
2129 | | /* |
2130 | | * It's a negative cache entry. Just delete it. |
2131 | | */ |
2132 | 0 | if (client->state == PR_CLIENT_NAK) { |
2133 | 0 | INFO("proto_%s - Expiring NAK'd dynamic client %pV - permitting new packets to be verified", |
2134 | 0 | inst->app_io->common.name, fr_box_ipaddr(client->src_ipaddr)); |
2135 | |
|
2136 | 0 | delete_client: |
2137 | 0 | fr_assert(client->packets == 0); |
2138 | | |
2139 | | /* |
2140 | | * It's a connected socket. Remove it from the |
2141 | | * parents list of connections, and delete it. |
2142 | | */ |
2143 | 0 | if (connection) { |
2144 | 0 | pthread_mutex_lock(&connection->parent->mutex); |
2145 | 0 | if (connection->in_parent_hash) { |
2146 | 0 | connection->in_parent_hash = false; |
2147 | 0 | (void) fr_hash_table_delete(connection->parent->ht, connection); |
2148 | 0 | } |
2149 | 0 | pthread_mutex_unlock(&connection->parent->mutex); |
2150 | | |
2151 | | /* |
2152 | | * Mark the connection as dead, and tell |
2153 | | * the network side to stop reading from |
2154 | | * it. |
2155 | | */ |
2156 | 0 | connection->dead = true; |
2157 | 0 | fr_network_listen_read(connection->nr, connection->listen); |
2158 | 0 | return; |
2159 | 0 | } |
2160 | | |
2161 | 0 | talloc_free(client); |
2162 | 0 | return; |
2163 | 0 | } |
2164 | | |
2165 | 0 | DEBUG2("TIMER - checking status of dynamic client %s %pV", client->radclient->shortname, fr_box_ipaddr(client->src_ipaddr)); |
2166 | | |
2167 | | /* |
2168 | | * It's a dynamically defined client. If no one is using |
2169 | | * it, clean it up after an idle timeout. |
2170 | | */ |
2171 | 0 | if ((client->state == PR_CLIENT_DYNAMIC) || |
2172 | 0 | (client->state == PR_CLIENT_CONNECTED)) { |
2173 | 0 | if (client->packets > 0) { |
2174 | 0 | client->ready_to_delete = false; |
2175 | 0 | return; |
2176 | 0 | } |
2177 | | |
2178 | | /* |
2179 | | * No packets, check / set idle timeout. |
2180 | | */ |
2181 | 0 | goto idle_timeout; |
2182 | 0 | } |
2183 | | |
2184 | | /* |
2185 | | * The client is pending definition. It's either a |
2186 | | * dynamic client which has timed out, OR it's a |
2187 | | * "place-holder" client for connected sockets. |
2188 | | */ |
2189 | 0 | fr_assert(client->state == PR_CLIENT_PENDING); |
2190 | | |
2191 | | /* |
2192 | | * This is a dynamic client pending definition. |
2193 | | * But it's taken too long to define, so we just |
2194 | | * delete the client, and all packets for it. A |
2195 | | * new packet will cause the dynamic definition |
2196 | | * to be run again. |
2197 | | */ |
2198 | 0 | if (!client->use_connected) { |
2199 | 0 | if (!client->packets) { |
2200 | 0 | DEBUG("proto_%s - No packets are using unconnected socket", inst->app_io->common.name); |
2201 | 0 | goto delete_client; |
2202 | 0 | } |
2203 | | |
2204 | | /* |
2205 | | * Tell the writer to NOT dynamically define the |
2206 | | * client. We've run into a problem. Then, |
2207 | | * return. The writer will take care of calling |
2208 | | * us again when it notices that a PENDING client |
2209 | | * is ready to delete. |
2210 | | * |
2211 | | * TBH... that shouldn't happen? We should rely |
2212 | | * on the write to do this all of the time... |
2213 | | */ |
2214 | 0 | client->ready_to_delete = true; |
2215 | 0 | return; |
2216 | 0 | } |
2217 | | |
2218 | 0 | fr_assert(!connection); |
2219 | | |
2220 | | /* |
2221 | | * Find out how many connections are using this |
2222 | | * client. |
2223 | | */ |
2224 | 0 | pthread_mutex_lock(&client->mutex); |
2225 | 0 | fr_assert(client->ht != NULL); |
2226 | 0 | connections = fr_hash_table_num_elements(client->ht); |
2227 | 0 | pthread_mutex_unlock(&client->mutex); |
2228 | | |
2229 | | /* |
2230 | | * No connections are using this client. If |
2231 | | * we've passed the idle timeout, then just |
2232 | | * delete it. Otherwise, set an idle timeout (as |
2233 | | * above); |
2234 | | */ |
2235 | 0 | if (!connections) { |
2236 | 0 | idle_timeout: |
2237 | | /* |
2238 | | * We didn't receive any packets during the |
2239 | | * idle_timeout, just delete it. |
2240 | | */ |
2241 | 0 | if (client->ready_to_delete) { |
2242 | 0 | if (connection) { |
2243 | 0 | DEBUG("proto_%s - idle timeout for connection %s", inst->app_io->common.name, connection->name); |
2244 | 0 | } else { |
2245 | 0 | DEBUG("proto_%s - idle timeout for client %s", inst->app_io->common.name, client->radclient->shortname); |
2246 | 0 | } |
2247 | 0 | goto delete_client; |
2248 | 0 | } |
2249 | | |
2250 | | /* |
2251 | | * No packets and no idle timeout set, go set |
2252 | | * idle timeut. |
2253 | | */ |
2254 | 0 | client->ready_to_delete = true; |
2255 | 0 | delay = client->state == PR_CLIENT_DYNAMIC ? inst->dynamic_timeout : inst->idle_timeout; |
2256 | 0 | goto reset_timer; |
2257 | 0 | } |
2258 | | |
2259 | | /* |
2260 | | * There are live sub-connections. Poll again after a |
2261 | | * long period of time. Once all of the connections are |
2262 | | * closed, we can then delete this client. |
2263 | | * |
2264 | | * @todo - maybe just leave it? we want to be able to |
2265 | | * clean up this client after a while tho... especially |
2266 | | * if the total number of clients is limited. |
2267 | | */ |
2268 | 0 | client->ready_to_delete = false; |
2269 | 0 | delay = inst->check_interval; |
2270 | |
|
2271 | 0 | reset_timer: |
2272 | 0 | if (fr_timer_in(client, tl, &client->ev, |
2273 | 0 | delay, false, client_expiry_timer, client) < 0) { |
2274 | 0 | ERROR("proto_%s - Failed adding timeout for dynamic client %s. It will be permanent!", |
2275 | 0 | inst->app_io->common.name, client->radclient->shortname); |
2276 | 0 | return; |
2277 | 0 | } |
2278 | | |
2279 | 0 | return; |
2280 | 0 | } |
2281 | | |
2282 | | |
2283 | | /* |
2284 | | * Expire cached packets after cleanup_delay time |
2285 | | */ |
2286 | | static void packet_expiry_timer(fr_timer_list_t *tl, fr_time_t now, void *uctx) |
2287 | 0 | { |
2288 | 0 | fr_io_track_t *track = talloc_get_type_abort(uctx, fr_io_track_t); |
2289 | 0 | fr_io_client_t *client = track->client; |
2290 | 0 | fr_io_instance_t const *inst = client->inst; |
2291 | | |
2292 | | /* |
2293 | | * Insert the timer if requested. |
2294 | | * |
2295 | | * On duplicates this also extends the expiry timer. |
2296 | | */ |
2297 | 0 | if (fr_time_eq(now, fr_time_wrap(0)) && !track->discard && inst->app_io->track_duplicates) { |
2298 | 0 | fr_assert(fr_time_delta_ispos(inst->cleanup_delay)); |
2299 | 0 | fr_assert(track->do_not_respond || track->reply_len); |
2300 | |
|
2301 | 0 | track->expires = fr_time_add(fr_time(), inst->cleanup_delay); |
2302 | | |
2303 | | /* |
2304 | | * if the timer succeeds, then "track" |
2305 | | * will be cleaned up when the timer |
2306 | | * fires. |
2307 | | */ |
2308 | 0 | if (fr_timer_at(track, tl, &track->ev, |
2309 | 0 | track->expires, |
2310 | 0 | false, packet_expiry_timer, track) == 0) { |
2311 | 0 | DEBUG("proto_%s - cleaning up request in %.6fs", inst->app_io->common.name, |
2312 | 0 | fr_time_delta_unwrap(inst->cleanup_delay) / (double)NSEC); |
2313 | 0 | return; |
2314 | 0 | } |
2315 | | |
2316 | 0 | DEBUG("proto_%s - Failed adding cleanup_delay for packet. Discarding packet immediately", |
2317 | 0 | inst->app_io->common.name); |
2318 | 0 | } |
2319 | | |
2320 | | /* |
2321 | | * So that all cleanup paths can come here, not just the |
2322 | | * timeout ones. |
2323 | | */ |
2324 | 0 | if (fr_time_neq(now, fr_time_wrap(0))) { |
2325 | 0 | DEBUG2("TIMER - proto_%s - cleanup delay", inst->app_io->common.name); |
2326 | 0 | } else { |
2327 | 0 | DEBUG2("proto_%s - cleaning up", inst->app_io->common.name); |
2328 | 0 | } |
2329 | | |
2330 | | /* |
2331 | | * Delete the tracking entry. |
2332 | | */ |
2333 | 0 | talloc_free(track); |
2334 | | |
2335 | | /* |
2336 | | * The client isn't dynamic, stop here. |
2337 | | */ |
2338 | 0 | if (client->state == PR_CLIENT_STATIC) return; |
2339 | | |
2340 | 0 | fr_assert(client->state != PR_CLIENT_NAK); |
2341 | 0 | fr_assert(client->state != PR_CLIENT_PENDING); |
2342 | | |
2343 | | /* |
2344 | | * If necessary, call the client expiry timer to clean up |
2345 | | * the client. |
2346 | | */ |
2347 | 0 | if (client->packets == 0) { |
2348 | 0 | client_expiry_timer(tl, now, client); |
2349 | 0 | } |
2350 | 0 | } |
2351 | | |
2352 | | static void update_client(fr_io_client_t *client, fr_client_t *radclient) |
2353 | 0 | { |
2354 | | |
2355 | | /* |
2356 | | * The new client is mostly OK. Copy the various fields |
2357 | | * over. |
2358 | | */ |
2359 | 0 | #define COPY_FIELD(_dest, _x) _dest->radclient->_x = radclient->_x |
2360 | 0 | #define DUP_FIELD(_dest, _x) _dest->radclient->_x = talloc_strdup(_dest->radclient, radclient->_x) |
2361 | | |
2362 | | /* |
2363 | | * Only these two fields are set. Other strings in |
2364 | | * radclient are copies of these ones. |
2365 | | */ |
2366 | 0 | talloc_const_free(client->radclient->shortname); |
2367 | 0 | talloc_const_free(client->radclient->secret); |
2368 | |
|
2369 | 0 | DUP_FIELD(client, longname); |
2370 | 0 | DUP_FIELD(client, shortname); |
2371 | 0 | DUP_FIELD(client, secret); |
2372 | 0 | DUP_FIELD(client, nas_type); |
2373 | |
|
2374 | 0 | COPY_FIELD(client, ipaddr); |
2375 | 0 | COPY_FIELD(client, src_ipaddr); |
2376 | 0 | COPY_FIELD(client, require_message_authenticator); |
2377 | 0 | COPY_FIELD(client, require_message_authenticator_is_set); |
2378 | | #ifdef NAS_VIOLATES_RFC |
2379 | | COPY_FIELD(client, allow_vulnerable_clients); |
2380 | | #endif |
2381 | 0 | COPY_FIELD(client, limit_proxy_state); |
2382 | 0 | COPY_FIELD(client, limit_proxy_state_is_set); |
2383 | 0 | COPY_FIELD(client, use_connected); |
2384 | 0 | COPY_FIELD(client, cs); |
2385 | 0 | } |
2386 | | |
2387 | | static ssize_t mod_write(fr_listen_t *li, void *packet_ctx, fr_time_t request_time, |
2388 | | uint8_t *buffer, size_t buffer_len, size_t written) |
2389 | 0 | { |
2390 | 0 | fr_io_instance_t const *inst; |
2391 | 0 | fr_io_thread_t *thread; |
2392 | 0 | fr_io_connection_t *connection; |
2393 | 0 | fr_io_track_t *track = talloc_get_type_abort(packet_ctx, fr_io_track_t); |
2394 | 0 | fr_io_client_t *client; |
2395 | 0 | fr_client_t *radclient; |
2396 | 0 | fr_listen_t *child; |
2397 | 0 | fr_event_list_t *el; |
2398 | 0 | char const *name; |
2399 | |
|
2400 | 0 | get_inst(li, &inst, &thread, &connection, &child); |
2401 | |
|
2402 | 0 | client = track->client; |
2403 | 0 | if (connection) { |
2404 | 0 | el = connection->el; |
2405 | 0 | name = connection->name; |
2406 | 0 | } else { |
2407 | 0 | el = thread->el; |
2408 | 0 | name = li->name; |
2409 | 0 | } |
2410 | |
|
2411 | 0 | DEBUG3("Processing reply for %s", name); |
2412 | | |
2413 | | /* |
2414 | | * A fully defined client means that we just send the reply. |
2415 | | */ |
2416 | 0 | if (client->state != PR_CLIENT_PENDING) { |
2417 | 0 | ssize_t packet_len; |
2418 | |
|
2419 | 0 | track->finished = true; |
2420 | | |
2421 | | /* |
2422 | | * The request received a conflicting packet, so we |
2423 | | * discard this one. |
2424 | | */ |
2425 | 0 | if (fr_time_neq(track->timestamp, request_time) || track->discard) { |
2426 | 0 | fr_assert(track->packets > 0); |
2427 | 0 | track->packets--; |
2428 | 0 | DEBUG3("Suppressing reply as we have a newer / conflicing packet from the same source"); |
2429 | 0 | track->discard = true; |
2430 | 0 | goto setup_timer; |
2431 | 0 | } |
2432 | | |
2433 | | /* |
2434 | | * We have a NAK packet, or the request has timed |
2435 | | * out, or it was discarded due to a conflicting |
2436 | | * packet. We don't respond, but we do cache the |
2437 | | * "do not respond" reply for a period of time. |
2438 | | */ |
2439 | 0 | if ((buffer_len == 1) || track->do_not_respond) { |
2440 | 0 | DEBUG3("Not sending response to request - it is marked as 'do not respond'"); |
2441 | 0 | track->do_not_respond = true; |
2442 | 0 | goto setup_timer; |
2443 | 0 | } |
2444 | | |
2445 | | /* |
2446 | | * We have a real packet, write it to the network |
2447 | | * via the underlying transport write. |
2448 | | */ |
2449 | 0 | packet_len = inst->app_io->write(child, track, request_time, |
2450 | 0 | buffer, buffer_len, written); |
2451 | 0 | if (packet_len <= 0) { |
2452 | 0 | ERROR("Failed writing the reply - not sending any response on %s", name); |
2453 | 0 | track->discard = true; |
2454 | 0 | packet_expiry_timer(el->tl, fr_time_wrap(0), track); |
2455 | 0 | return packet_len; |
2456 | 0 | } |
2457 | | |
2458 | | /* |
2459 | | * Only a partial write. The network code will |
2460 | | * take care of calling us again, and we will set |
2461 | | * the expiry timer at that point. |
2462 | | */ |
2463 | 0 | if ((size_t) packet_len < buffer_len) { |
2464 | 0 | DEBUG3("Partial write (%zd < %zu)", packet_len, buffer_len); |
2465 | 0 | return packet_len; |
2466 | 0 | } |
2467 | | |
2468 | | /* |
2469 | | * We're not tracking duplicates, so just expire |
2470 | | * the packet now. |
2471 | | */ |
2472 | 0 | if (!inst->app_io->track_duplicates) { |
2473 | 0 | DEBUG3("Not tracking duplicates - expiring the request"); |
2474 | 0 | goto setup_timer; |
2475 | 0 | } |
2476 | | |
2477 | | /* |
2478 | | * Cache the reply packet if we're doing dedup. |
2479 | | * |
2480 | | * On resend duplicate reply, the reply is |
2481 | | * already filled out. So we don't do that twice. |
2482 | | */ |
2483 | 0 | if (!track->reply) { |
2484 | 0 | DEBUG3("Caching reply"); |
2485 | 0 | MEM(track->reply = talloc_memdup(track, buffer, buffer_len)); |
2486 | 0 | track->reply_len = buffer_len; |
2487 | 0 | } |
2488 | | |
2489 | | /* |
2490 | | * Set the timer to expire the packet. |
2491 | | * |
2492 | | * On dedup this also extends the timer. |
2493 | | */ |
2494 | 0 | setup_timer: |
2495 | 0 | packet_expiry_timer(el->tl, fr_time_wrap(0), track); |
2496 | 0 | return buffer_len; |
2497 | 0 | } |
2498 | | |
2499 | | /* |
2500 | | * The client is pending, so we MUST have dynamic clients. |
2501 | | * |
2502 | | * If there's a connected socket and no dynamic clients, then the |
2503 | | * client state is set to CONNECTED when the client is created. |
2504 | | */ |
2505 | 0 | fr_assert(inst->dynamic_clients); |
2506 | 0 | fr_assert(client->pending != NULL); |
2507 | | |
2508 | | /* |
2509 | | * The request failed trying to define the dynamic |
2510 | | * client. Discard the client and all pending packets. |
2511 | | */ |
2512 | 0 | if ((buffer_len == 1) && (*buffer == true)) { |
2513 | 0 | DEBUG("Request failed trying to define a new client. Discarding client and pending packets."); |
2514 | |
|
2515 | 0 | if (!connection) { |
2516 | 0 | talloc_free(client); |
2517 | 0 | return buffer_len; |
2518 | 0 | } |
2519 | | |
2520 | | /* |
2521 | | * Free pending packets and tracking table. |
2522 | | * The table is parented by connection->parent, so won't |
2523 | | * be auto-freed when connection->client is freed. |
2524 | | */ |
2525 | 0 | TALLOC_FREE(client->pending); |
2526 | 0 | if (client->table) TALLOC_FREE(client->table); |
2527 | | |
2528 | | /* |
2529 | | * Remove from parent's hash table so new packets won't |
2530 | | * be routed to this connection. |
2531 | | */ |
2532 | 0 | pthread_mutex_lock(&connection->parent->mutex); |
2533 | 0 | if (connection->in_parent_hash) { |
2534 | 0 | connection->in_parent_hash = false; |
2535 | 0 | (void) fr_hash_table_delete(connection->parent->ht, connection); |
2536 | 0 | } |
2537 | 0 | pthread_mutex_unlock(&connection->parent->mutex); |
2538 | | |
2539 | | /* |
2540 | | * Mark the connection as dead, then trigger the |
2541 | | * standard cleanup path via fr_network_listen_read(). |
2542 | | * This calls mod_read(), which sees connection->dead, |
2543 | | * returns -1, and the network layer closes the |
2544 | | * connection through its normal error handling. |
2545 | | */ |
2546 | 0 | connection->dead = true; |
2547 | 0 | fr_network_listen_read(connection->nr, connection->listen); |
2548 | |
|
2549 | 0 | return buffer_len; |
2550 | 0 | } |
2551 | | |
2552 | | /* |
2553 | | * The dynamic client was NOT defined. Set it's state to |
2554 | | * NAK, delete all pending packets, and close the |
2555 | | * tracking table. |
2556 | | */ |
2557 | 0 | if (buffer_len == 1) { |
2558 | 0 | INFO("proto_%s - Verification failed for packet from dynamic client %pV - adding IP address to the NAK cache", |
2559 | 0 | inst->app_io->common.name, fr_box_ipaddr(client->src_ipaddr)); |
2560 | |
|
2561 | 0 | client->state = PR_CLIENT_NAK; |
2562 | 0 | if (!connection) { |
2563 | 0 | client_pending_free(client); |
2564 | 0 | } else { |
2565 | 0 | TALLOC_FREE(client->pending); |
2566 | 0 | } |
2567 | 0 | if (client->table) TALLOC_FREE(client->table); |
2568 | 0 | fr_assert(client->packets == 0); |
2569 | | |
2570 | | /* |
2571 | | * If we're a connected UDP socket, allocate a |
2572 | | * new connection which is the place-holder for |
2573 | | * the NAK. We will reject packets from from the |
2574 | | * src/dst IP/port. |
2575 | | * |
2576 | | * The timer will take care of deleting the NAK |
2577 | | * connection (which doesn't have any FDs |
2578 | | * associated with it). The network side will |
2579 | | * call mod_close() when the original connection |
2580 | | * is done, which will then free that connection, |
2581 | | * too. |
2582 | | */ |
2583 | 0 | if (connection && (inst->ipproto == IPPROTO_UDP)) { |
2584 | 0 | MEM(connection = fr_io_connection_alloc(inst, thread, client, -1, connection->address, connection)); |
2585 | 0 | client_expiry_timer(el->tl, fr_time_wrap(0), connection->client); |
2586 | |
|
2587 | 0 | errno = ECONNREFUSED; |
2588 | 0 | return -1; |
2589 | 0 | } |
2590 | | |
2591 | | /* |
2592 | | * For connected TCP sockets, we just call the |
2593 | | * expiry timer, which will close and free the |
2594 | | * connection. |
2595 | | */ |
2596 | 0 | client_expiry_timer(el->tl, fr_time_wrap(0), client); |
2597 | 0 | return buffer_len; |
2598 | 0 | } |
2599 | | |
2600 | 0 | fr_assert(buffer_len == sizeof(radclient)); |
2601 | |
|
2602 | 0 | memcpy(&radclient, buffer, sizeof(radclient)); |
2603 | |
|
2604 | 0 | if (!connection) { |
2605 | 0 | fr_ipaddr_t ipaddr; |
2606 | | |
2607 | | /* |
2608 | | * Check the encapsulating network against the |
2609 | | * address that the user wants to use, but only |
2610 | | * for unconnected sockets. |
2611 | | */ |
2612 | 0 | if (client->network.af != radclient->ipaddr.af) { |
2613 | 0 | DEBUG("Client IP address %pV IP family does not match the source network %pV of the packet.", |
2614 | 0 | fr_box_ipaddr(radclient->ipaddr), fr_box_ipaddr(client->network)); |
2615 | 0 | goto error; |
2616 | 0 | } |
2617 | | |
2618 | | /* |
2619 | | * Network prefix is more restrictive than the one given |
2620 | | * by the client... that's bad. |
2621 | | */ |
2622 | 0 | if (client->network.prefix > radclient->ipaddr.prefix) { |
2623 | 0 | DEBUG("Client IP address %pV is not within the prefix with the defined network %pV", |
2624 | 0 | fr_box_ipaddr(radclient->ipaddr), fr_box_ipaddr(client->network)); |
2625 | 0 | goto error; |
2626 | 0 | } |
2627 | | |
2628 | 0 | ipaddr = radclient->ipaddr; |
2629 | 0 | fr_ipaddr_mask(&ipaddr, client->network.prefix); |
2630 | 0 | if (fr_ipaddr_cmp(&ipaddr, &client->network) != 0) { |
2631 | 0 | DEBUG("Client IP address %pV is not within the defined network %pV.", |
2632 | 0 | fr_box_ipaddr(radclient->ipaddr), fr_box_ipaddr(client->network)); |
2633 | 0 | goto error; |
2634 | 0 | } |
2635 | | |
2636 | | /* |
2637 | | * We can't define dynamic clients as networks (for now). |
2638 | | * |
2639 | | * @todo - If we did allow it, we would have to remove |
2640 | | * this client from the trie, update it's IP address, and |
2641 | | * re-add it. We can PROBABLY do this if this client |
2642 | | * isn't already connected, AND radclient->use_connected |
2643 | | * is true. But that's for later... |
2644 | | */ |
2645 | 0 | if (((radclient->ipaddr.af == AF_INET) && |
2646 | 0 | (radclient->ipaddr.prefix != 32)) || |
2647 | 0 | ((radclient->ipaddr.af == AF_INET6) && |
2648 | 0 | (radclient->ipaddr.prefix != 128))) { |
2649 | 0 | ERROR("Cannot define a dynamic client as a network"); |
2650 | |
|
2651 | 0 | error: |
2652 | 0 | talloc_free(radclient); |
2653 | | |
2654 | | /* |
2655 | | * Remove the pending client from the trie. |
2656 | | */ |
2657 | 0 | fr_assert(!connection); |
2658 | 0 | talloc_free(client); |
2659 | 0 | return buffer_len; |
2660 | 0 | } |
2661 | 0 | } |
2662 | | |
2663 | 0 | update_client(client, radclient); |
2664 | | |
2665 | | // @todo - fill in other fields? |
2666 | |
|
2667 | 0 | talloc_free(radclient); |
2668 | |
|
2669 | 0 | radclient = client->radclient; /* laziness */ |
2670 | 0 | radclient->server_cs = inst->server_cs; |
2671 | 0 | radclient->server = cf_section_name2(inst->server_cs); |
2672 | | |
2673 | | /* |
2674 | | * This is a connected socket, and it's just been |
2675 | | * allowed. Go poke the network side to read from the |
2676 | | * socket. |
2677 | | */ |
2678 | 0 | if (connection) { |
2679 | 0 | fr_assert(connection != NULL); |
2680 | 0 | fr_assert(connection->client == client); |
2681 | 0 | fr_assert(client->connection != NULL); |
2682 | |
|
2683 | 0 | client->state = PR_CLIENT_CONNECTED; |
2684 | | |
2685 | | /* |
2686 | | * Connections can't spawn new connections. |
2687 | | */ |
2688 | 0 | client->use_connected = radclient->use_connected = false; |
2689 | | |
2690 | | /* |
2691 | | * If we were paused. resume reading from the |
2692 | | * connection. |
2693 | | * |
2694 | | * Note that the event list doesn't like resuming |
2695 | | * a connection that isn't paused. It just sets |
2696 | | * the read function to NULL. |
2697 | | */ |
2698 | 0 | if (connection->paused) { |
2699 | 0 | (void) fr_event_filter_update(el, child->fd, |
2700 | 0 | FR_EVENT_FILTER_IO, resume_read); |
2701 | 0 | } |
2702 | |
|
2703 | 0 | connection->parent->radclient->active = true; |
2704 | 0 | fr_assert(connection->parent->state == PR_CLIENT_PENDING); |
2705 | 0 | connection->parent->state = PR_CLIENT_DYNAMIC; |
2706 | |
|
2707 | 0 | update_client(connection->parent, radclient); |
2708 | | |
2709 | | /* |
2710 | | * Re-parent the conf section used to build this client |
2711 | | * so its lifetime is linked to parent client |
2712 | | */ |
2713 | 0 | talloc_steal(connection->parent->radclient, connection->parent->radclient->cs); |
2714 | | |
2715 | | /* |
2716 | | * The client has been allowed. |
2717 | | */ |
2718 | 0 | client->state = PR_CLIENT_DYNAMIC; |
2719 | 0 | client->radclient->active = true; |
2720 | |
|
2721 | 0 | INFO("proto_%s - Verification succeeded for packet from dynamic client %pV - processing queued packets", |
2722 | 0 | inst->app_io->common.name, fr_box_ipaddr(client->src_ipaddr)); |
2723 | 0 | goto finish; |
2724 | 0 | } else { |
2725 | | /* |
2726 | | * Re-parent the conf section used to build this client |
2727 | | * so its lifetime is linked to the client |
2728 | | */ |
2729 | 0 | talloc_steal(radclient, radclient->cs); |
2730 | 0 | } |
2731 | | |
2732 | 0 | fr_assert(connection == NULL); |
2733 | 0 | fr_assert(client->use_connected == false); /* we weren't sure until now */ |
2734 | | |
2735 | | /* |
2736 | | * Disallow unsupported configurations. |
2737 | | */ |
2738 | 0 | if (radclient->use_connected && !inst->app_io->connection_set) { |
2739 | 0 | DEBUG("proto_%s - cannot use connected sockets as underlying 'transport = %s' does not support it.", |
2740 | 0 | inst->app_io->common.name, inst->submodule->module->exported->name); |
2741 | 0 | goto error; |
2742 | 0 | } |
2743 | | |
2744 | | |
2745 | | /* |
2746 | | * Dynamic clients can spawn new connections. |
2747 | | */ |
2748 | 0 | client->use_connected = radclient->use_connected; |
2749 | | |
2750 | | /* |
2751 | | * The admin has defined a client which uses connected |
2752 | | * sockets. Go spawn it |
2753 | | */ |
2754 | 0 | if (client->use_connected) { |
2755 | 0 | fr_assert(connection == NULL); |
2756 | | |
2757 | | |
2758 | | /* |
2759 | | * Leave the state as PENDING. Each connection |
2760 | | * will then cause a dynamic client to be |
2761 | | * defined. |
2762 | | */ |
2763 | 0 | (void) pthread_mutex_init(&client->mutex, NULL); |
2764 | 0 | MEM(client->ht = fr_hash_table_alloc(client, connection_hash, connection_cmp, NULL)); |
2765 | |
|
2766 | 0 | } else { |
2767 | | /* |
2768 | | * The client has been allowed. |
2769 | | */ |
2770 | 0 | client->state = PR_CLIENT_DYNAMIC; |
2771 | 0 | client->radclient->active = true; |
2772 | |
|
2773 | 0 | INFO("proto_%s - Verification succeeded for packet from dynamic client %pV - processing %d queued packets", |
2774 | 0 | inst->app_io->common.name, fr_box_ipaddr(client->src_ipaddr), |
2775 | 0 | fr_heap_num_elements(client->pending)); |
2776 | 0 | } |
2777 | | |
2778 | | /* |
2779 | | * Add this client to the master socket, so that |
2780 | | * mod_read() will see the pending client, pop the |
2781 | | * pending packet, and process it. |
2782 | | * |
2783 | | */ |
2784 | 0 | if (!thread->pending_clients) { |
2785 | 0 | MEM(thread->pending_clients = fr_heap_alloc(thread, pending_client_cmp, |
2786 | 0 | fr_io_client_t, pending_id, 0)); |
2787 | 0 | } |
2788 | |
|
2789 | 0 | fr_assert(!fr_heap_entry_inserted((client->pending_id))); |
2790 | 0 | (void) fr_heap_insert(&thread->pending_clients, client); |
2791 | |
|
2792 | 0 | finish: |
2793 | | /* |
2794 | | * Maybe we defined the client, but the original packet |
2795 | | * timed out, so there's nothing more to do. In that case, set up the expiry timers. |
2796 | | */ |
2797 | 0 | if (client->packets == 0) { |
2798 | 0 | client_expiry_timer(el->tl, fr_time_wrap(0), client); |
2799 | 0 | } |
2800 | | |
2801 | | /* |
2802 | | * If there are pending packets (and there should be at |
2803 | | * least one), tell the network socket to call our read() |
2804 | | * function again. |
2805 | | */ |
2806 | 0 | if (fr_heap_num_elements(client->pending) > 0) { |
2807 | 0 | if (connection) { |
2808 | 0 | fr_network_listen_read(connection->nr, connection->listen); |
2809 | 0 | } else { |
2810 | 0 | fr_network_listen_read(thread->nr, thread->listen); |
2811 | 0 | } |
2812 | 0 | } |
2813 | |
|
2814 | 0 | return buffer_len; |
2815 | 0 | } |
2816 | | |
2817 | | /** Close the socket. |
2818 | | * |
2819 | | */ |
2820 | | static int mod_close(fr_listen_t *li) |
2821 | 0 | { |
2822 | 0 | fr_io_instance_t const *inst; |
2823 | 0 | fr_io_connection_t *connection; |
2824 | 0 | fr_listen_t *child; |
2825 | |
|
2826 | 0 | get_inst(li, &inst, NULL, &connection, &child); |
2827 | |
|
2828 | 0 | if (inst->app_io->close) { |
2829 | 0 | int ret; |
2830 | |
|
2831 | 0 | ret = inst->app_io->close(child); |
2832 | 0 | if (ret < 0) return ret; |
2833 | 0 | } else { |
2834 | 0 | close(child->fd); |
2835 | | // child->fd = -1; |
2836 | 0 | } |
2837 | | |
2838 | 0 | if (!connection) return 0; |
2839 | | |
2840 | | /* |
2841 | | * We allocated this, so we're responsible for closing |
2842 | | * it. |
2843 | | */ |
2844 | 0 | DEBUG("Closing connection %s", connection->name); |
2845 | 0 | if (connection->client->pending) { |
2846 | 0 | TALLOC_FREE(connection->client->pending); /* for any pending packets */ |
2847 | 0 | } |
2848 | | |
2849 | | /* |
2850 | | * Remove connection from parent hash table |
2851 | | */ |
2852 | 0 | pthread_mutex_lock(&connection->parent->mutex); |
2853 | 0 | if (connection->in_parent_hash) { |
2854 | 0 | connection->in_parent_hash = false; |
2855 | 0 | (void) fr_hash_table_delete(connection->parent->ht, connection); |
2856 | 0 | } |
2857 | | |
2858 | | /* |
2859 | | * If this is a dynamic client, and the parent has no more connections |
2860 | | * set up the timer to expire the dynamic client. |
2861 | | */ |
2862 | 0 | if ((connection->parent->state == PR_CLIENT_DYNAMIC) && |
2863 | 0 | ((!connection->parent->ht) || (fr_hash_table_num_elements(connection->parent->ht) == 0))) { |
2864 | 0 | client_expiry_timer(connection->el->tl, fr_time_wrap(0), connection->parent); |
2865 | 0 | } |
2866 | 0 | pthread_mutex_unlock(&connection->parent->mutex); |
2867 | |
|
2868 | 0 | talloc_free(connection->mi); |
2869 | |
|
2870 | 0 | return 0; |
2871 | 0 | } |
2872 | | |
2873 | | static int mod_instantiate(module_inst_ctx_t const *mctx) |
2874 | 0 | { |
2875 | 0 | fr_io_instance_t *inst = mctx->mi->data; |
2876 | 0 | CONF_SECTION *conf = mctx->mi->conf; |
2877 | |
|
2878 | 0 | inst->mi = mctx->mi; |
2879 | 0 | inst->app_io = (fr_app_io_t const *) inst->submodule->exported; |
2880 | 0 | inst->app_io_conf = inst->submodule->conf; |
2881 | 0 | inst->app_io_instance = inst->submodule->data; |
2882 | | |
2883 | | /* |
2884 | | * If we're not tracking duplicates then we don't need a |
2885 | | * cleanup delay. |
2886 | | * |
2887 | | * If we are tracking duplicates, then we must have a non-zero cleanup delay. |
2888 | | */ |
2889 | 0 | if (!inst->app_io->track_duplicates) { |
2890 | 0 | inst->cleanup_delay = fr_time_delta_wrap(0); |
2891 | |
|
2892 | 0 | } else { |
2893 | 0 | FR_TIME_DELTA_BOUND_CHECK("cleanup_delay", inst->cleanup_delay, >=, fr_time_delta_from_sec(1)); |
2894 | |
|
2895 | 0 | if (!inst->app_io->track_create) { |
2896 | 0 | cf_log_err(inst->app_io_conf, "Internal error: 'track_duplicates' is set, but there is no 'track create' function"); |
2897 | 0 | return -1; |
2898 | 0 | } |
2899 | 0 | } |
2900 | | |
2901 | | /* |
2902 | | * Get various information after bootstrapping the |
2903 | | * application IO module. |
2904 | | */ |
2905 | 0 | if (inst->app_io->network_get) { |
2906 | 0 | inst->app_io->network_get(&inst->ipproto, &inst->dynamic_clients, &inst->networks, inst->app_io_instance); |
2907 | 0 | } |
2908 | |
|
2909 | 0 | if ((inst->ipproto == IPPROTO_TCP) && !inst->app_io->connection_set) { |
2910 | 0 | cf_log_err(inst->app_io_conf, "Missing 'connection set' API for proto_%s", inst->app_io->common.name); |
2911 | 0 | return -1; |
2912 | 0 | } |
2913 | | |
2914 | | /* |
2915 | | * Ensure that the dynamic client sections exist |
2916 | | */ |
2917 | 0 | if (inst->dynamic_clients) { |
2918 | 0 | CONF_SECTION *server = cf_item_to_section(cf_parent(conf)); |
2919 | |
|
2920 | 0 | if (!cf_section_find(server, "new", "client")) { |
2921 | 0 | cf_log_err(conf, "Cannot use 'dynamic_clients = yes' as the virtual server has no 'new client { ... }' section defined."); |
2922 | 0 | return -1; |
2923 | 0 | } |
2924 | | |
2925 | 0 | if (!cf_section_find(server, "add", "client")) { |
2926 | 0 | cf_log_warn(conf, "No 'add client { ... }' section was defined."); |
2927 | 0 | } |
2928 | |
|
2929 | 0 | if (!cf_section_find(server, "deny", "client")) { |
2930 | 0 | cf_log_warn(conf, "No 'deny client { ... }' section was defined."); |
2931 | 0 | } |
2932 | 0 | } |
2933 | | |
2934 | | /* |
2935 | | * Create a list of client modules. |
2936 | | * |
2937 | | * FIXME - Probably only want to do this for connected sockets? |
2938 | | * |
2939 | | * FIXME - We probably want write protect enabled? |
2940 | | */ |
2941 | 0 | inst->clients = module_list_alloc(inst, &module_list_type_thread_local, "clients", false); |
2942 | 0 | module_list_mask_set(inst->clients, MODULE_INSTANCE_BOOTSTRAPPED); |
2943 | |
|
2944 | 0 | return 0; |
2945 | 0 | } |
2946 | | |
2947 | | |
2948 | | static char const *mod_name(fr_listen_t *li) |
2949 | 0 | { |
2950 | 0 | fr_io_thread_t *thread; |
2951 | 0 | fr_io_connection_t *connection; |
2952 | 0 | fr_listen_t *child; |
2953 | 0 | fr_io_instance_t const *inst; |
2954 | |
|
2955 | 0 | get_inst(li, &inst, &thread, &connection, &child); |
2956 | |
|
2957 | 0 | fr_assert(child != NULL); |
2958 | 0 | return child->app_io->get_name(child); |
2959 | 0 | } |
2960 | | |
2961 | | /** Create a trie from arrays of allow / deny IP addresses |
2962 | | * |
2963 | | * @param ctx the talloc ctx |
2964 | | * @param af the address family to allow |
2965 | | * @param allow the array of IPs / networks to allow. MUST be talloc'd |
2966 | | * @param deny the array of IPs / networks to deny. MAY be NULL, MUST be talloc'd |
2967 | | * @return |
2968 | | * - fr_trie_t on success |
2969 | | * - NULL on error |
2970 | | */ |
2971 | | fr_trie_t *fr_master_io_network(TALLOC_CTX *ctx, int af, fr_ipaddr_t *allow, fr_ipaddr_t *deny) |
2972 | 0 | { |
2973 | 0 | fr_trie_t *trie; |
2974 | 0 | size_t i, num; |
2975 | |
|
2976 | 0 | MEM(trie = fr_trie_alloc(ctx, NULL, NULL)); |
2977 | |
|
2978 | 0 | num = talloc_array_length(allow); |
2979 | 0 | fr_assert(num > 0); |
2980 | |
|
2981 | 0 | for (i = 0; i < num; i++) { |
2982 | 0 | fr_ipaddr_t *network; |
2983 | | |
2984 | | /* |
2985 | | * Can't add v4 networks to a v6 socket, or vice versa. |
2986 | | */ |
2987 | 0 | if (allow[i].af != af) { |
2988 | 0 | fr_strerror_printf("Address family in entry %zd - 'allow = %pV' " |
2989 | 0 | "does not match 'ipaddr'", i + 1, fr_box_ipaddr(allow[i])); |
2990 | 0 | talloc_free(trie); |
2991 | 0 | return NULL; |
2992 | 0 | } |
2993 | | |
2994 | | /* |
2995 | | * Duplicates are bad. |
2996 | | */ |
2997 | 0 | network = fr_trie_match_by_key(trie, |
2998 | 0 | &allow[i].addr, allow[i].prefix); |
2999 | 0 | if (network) { |
3000 | 0 | fr_strerror_printf("Cannot add duplicate entry 'allow = %pV'", |
3001 | 0 | fr_box_ipaddr(allow[i])); |
3002 | 0 | talloc_free(trie); |
3003 | 0 | return NULL; |
3004 | 0 | } |
3005 | | |
3006 | | /* |
3007 | | * Look for overlapping entries. |
3008 | | * i.e. the networks MUST be disjoint. |
3009 | | * |
3010 | | * Note that this catches 192.168.1/24 |
3011 | | * followed by 192.168/16, but NOT the |
3012 | | * other way around. The best fix is |
3013 | | * likely to add a flag to |
3014 | | * fr_trie_alloc() saying "we can only |
3015 | | * have terminal fr_trie_user_t nodes" |
3016 | | */ |
3017 | 0 | network = fr_trie_lookup_by_key(trie, |
3018 | 0 | &allow[i].addr, allow[i].prefix); |
3019 | 0 | if (network && (network->prefix <= allow[i].prefix)) { |
3020 | 0 | fr_strerror_printf("Cannot add overlapping entry 'allow = %pV'", fr_box_ipaddr(allow[i])); |
3021 | 0 | fr_strerror_const("Entry is completely enclosed inside of a previously defined network."); |
3022 | 0 | talloc_free(trie); |
3023 | 0 | return NULL; |
3024 | 0 | } |
3025 | | |
3026 | | /* |
3027 | | * Insert the network into the trie. |
3028 | | * Lookups will return the fr_ipaddr_t of |
3029 | | * the network. |
3030 | | */ |
3031 | 0 | if (fr_trie_insert_by_key(trie, |
3032 | 0 | &allow[i].addr, allow[i].prefix, |
3033 | 0 | &allow[i]) < 0) { |
3034 | 0 | fr_strerror_printf("Failed adding 'allow = %pV' to tracking table", fr_box_ipaddr(allow[i])); |
3035 | 0 | talloc_free(trie); |
3036 | 0 | return NULL; |
3037 | 0 | } |
3038 | 0 | } |
3039 | | |
3040 | | /* |
3041 | | * And now check denied networks. |
3042 | | */ |
3043 | 0 | num = talloc_array_length(deny); |
3044 | 0 | if (!num) return trie; |
3045 | | |
3046 | | /* |
3047 | | * Since the default is to deny, you can only add |
3048 | | * a "deny" inside of a previous "allow". |
3049 | | */ |
3050 | 0 | for (i = 0; i < num; i++) { |
3051 | 0 | fr_ipaddr_t *network; |
3052 | | |
3053 | | /* |
3054 | | * Can't add v4 networks to a v6 socket, or vice versa. |
3055 | | */ |
3056 | 0 | if (deny[i].af != af) { |
3057 | 0 | fr_strerror_printf("Address family in entry %zd - 'deny = %pV' " |
3058 | 0 | "does not match 'ipaddr'", i + 1, fr_box_ipaddr(deny[i])); |
3059 | 0 | talloc_free(trie); |
3060 | 0 | return NULL; |
3061 | 0 | } |
3062 | | |
3063 | | /* |
3064 | | * Duplicates are bad. |
3065 | | */ |
3066 | 0 | network = fr_trie_match_by_key(trie, |
3067 | 0 | &deny[i].addr, deny[i].prefix); |
3068 | 0 | if (network) { |
3069 | 0 | fr_strerror_printf("Cannot add duplicate entry 'deny = %pV'", fr_box_ipaddr(deny[i])); |
3070 | 0 | talloc_free(trie); |
3071 | 0 | return NULL; |
3072 | 0 | } |
3073 | | |
3074 | | /* |
3075 | | * A "deny" can only be within a previous "allow". |
3076 | | */ |
3077 | 0 | network = fr_trie_lookup_by_key(trie, |
3078 | 0 | &deny[i].addr, deny[i].prefix); |
3079 | 0 | if (!network) { |
3080 | 0 | fr_strerror_printf("The network in entry %zd - 'deny = %pV' is not " |
3081 | 0 | "contained within a previous 'allow'", i + 1, fr_box_ipaddr(deny[i])); |
3082 | 0 | talloc_free(trie); |
3083 | 0 | return NULL; |
3084 | 0 | } |
3085 | | |
3086 | | /* |
3087 | | * We hack the AF in "deny" rules. If |
3088 | | * the lookup gets AF_UNSPEC, then we're |
3089 | | * adding a "deny" inside of a "deny". |
3090 | | */ |
3091 | 0 | if (network->af != af) { |
3092 | 0 | fr_strerror_printf("The network in entry %zd - 'deny = %pV' is overlaps " |
3093 | 0 | "with another 'deny' rule", i + 1, fr_box_ipaddr(deny[i])); |
3094 | 0 | talloc_free(trie); |
3095 | 0 | return NULL; |
3096 | 0 | } |
3097 | | |
3098 | | /* |
3099 | | * Insert the network into the trie. |
3100 | | * Lookups will return the fr_ipaddr_t of |
3101 | | * the network. |
3102 | | */ |
3103 | 0 | if (fr_trie_insert_by_key(trie, |
3104 | 0 | &deny[i].addr, deny[i].prefix, |
3105 | 0 | &deny[i]) < 0) { |
3106 | 0 | fr_strerror_printf("Failed adding 'deny = %pV' to tracking table", fr_box_ipaddr(deny[i])); |
3107 | 0 | talloc_free(trie); |
3108 | 0 | return NULL; |
3109 | 0 | } |
3110 | | |
3111 | | /* |
3112 | | * Hack it to make it a deny rule. |
3113 | | */ |
3114 | 0 | deny[i].af = AF_UNSPEC; |
3115 | 0 | } |
3116 | | |
3117 | 0 | return trie; |
3118 | 0 | } |
3119 | | |
3120 | | |
3121 | | int fr_io_listen_free(fr_listen_t *li) |
3122 | 0 | { |
3123 | 0 | if (!li->thread_instance) return 0; |
3124 | | |
3125 | 0 | talloc_free(li->thread_instance); |
3126 | 0 | return 0; |
3127 | 0 | } |
3128 | | |
3129 | | int fr_master_io_listen(fr_io_instance_t *inst, fr_schedule_t *sc, |
3130 | | size_t default_message_size, size_t num_messages) |
3131 | 0 | { |
3132 | 0 | fr_listen_t *li, *child; |
3133 | 0 | fr_io_thread_t *thread; |
3134 | | |
3135 | | /* |
3136 | | * No IO paths, so we don't initialize them. |
3137 | | */ |
3138 | 0 | if (!inst->app_io) { |
3139 | 0 | fr_assert(!inst->dynamic_clients); |
3140 | 0 | return 0; |
3141 | 0 | } |
3142 | | |
3143 | 0 | if (!inst->app_io->common.thread_inst_size) { |
3144 | 0 | fr_strerror_const("IO modules MUST set 'thread_inst_size' when using the master IO handler."); |
3145 | 0 | return -1; |
3146 | 0 | } |
3147 | | |
3148 | | /* |
3149 | | * Build the #fr_listen_t. This describes the complete |
3150 | | * path data takes from the socket to the decoder and |
3151 | | * back again. |
3152 | | */ |
3153 | 0 | MEM(li = talloc_zero(NULL, fr_listen_t)); |
3154 | 0 | talloc_set_destructor(li, fr_io_listen_free); |
3155 | | |
3156 | | /* |
3157 | | * The first listener is the one for the application |
3158 | | * (e.g. RADIUS). However, we mangle the IO path to |
3159 | | * point to the master IO handler. That allows all of |
3160 | | * the high-level work (dynamic client checking, |
3161 | | * connected sockets, etc.) to be handled by the master |
3162 | | * IO handler. |
3163 | | * |
3164 | | * This listener is then passed to the network code, |
3165 | | * which calls our trampoline functions to do the actual |
3166 | | * work. |
3167 | | */ |
3168 | 0 | li->app = inst->app; |
3169 | 0 | li->app_instance = inst->app_instance; |
3170 | 0 | li->server_cs = inst->server_cs; |
3171 | | |
3172 | | /* |
3173 | | * Set configurable parameters for message ring buffer. |
3174 | | */ |
3175 | 0 | li->default_message_size = default_message_size; |
3176 | 0 | li->num_messages = num_messages; |
3177 | | |
3178 | | /* |
3179 | | * Per-socket data lives here. |
3180 | | */ |
3181 | 0 | thread = talloc_zero(NULL, fr_io_thread_t); |
3182 | 0 | thread->listen = li; |
3183 | 0 | thread->sc = sc; |
3184 | | |
3185 | | /* |
3186 | | * Create the trie of clients for this socket. |
3187 | | */ |
3188 | 0 | MEM(thread->trie = fr_trie_alloc(thread, NULL, NULL)); |
3189 | |
|
3190 | 0 | if (inst->dynamic_clients) { |
3191 | 0 | MEM(thread->alive_clients = fr_heap_alloc(thread, alive_client_cmp, |
3192 | 0 | fr_io_client_t, alive_id, 0)); |
3193 | 0 | } |
3194 | | |
3195 | | /* |
3196 | | * Set the listener to call our master trampoline function. |
3197 | | */ |
3198 | 0 | li->cs = inst->app_io_conf; |
3199 | 0 | li->app_io = &fr_master_app_io; |
3200 | 0 | li->thread_instance = thread; |
3201 | 0 | li->app_io_instance = inst; |
3202 | 0 | li->track_duplicates = inst->app_io->track_duplicates; |
3203 | 0 | if (inst->app_io->hexdump_set) inst->app_io->hexdump_set(li, inst->app_io_instance); |
3204 | | |
3205 | | /* |
3206 | | * The child listener points to the *actual* IO path. |
3207 | | * |
3208 | | * We need to create a complete listener here (e.g. |
3209 | | * RADIUS + RADIUS_UDP), because the underlying IO |
3210 | | * functions expect to get passed a full listener. |
3211 | | * |
3212 | | * Once the network side calls us, we will call the child |
3213 | | * listener to do the actual IO. |
3214 | | */ |
3215 | 0 | child = thread->child = talloc_zero(li, fr_listen_t); |
3216 | 0 | memcpy(child, li, sizeof(*child)); |
3217 | | |
3218 | | /* |
3219 | | * Reset these fields to point to the IO instance data. |
3220 | | */ |
3221 | 0 | child->app_io = inst->app_io; |
3222 | 0 | child->track_duplicates = inst->app_io->track_duplicates; |
3223 | |
|
3224 | 0 | if (child->app_io->common.thread_inst_size > 0) { |
3225 | 0 | child->thread_instance = talloc_zero_array(NULL, uint8_t, |
3226 | 0 | inst->app_io->common.thread_inst_size); |
3227 | 0 | talloc_set_destructor(child, fr_io_listen_free); |
3228 | |
|
3229 | 0 | talloc_set_name(child->thread_instance, "proto_%s_thread_t", |
3230 | 0 | inst->app_io->common.name); |
3231 | | |
3232 | | /* |
3233 | | * This is "const", and the user can't |
3234 | | * touch it. So we just reuse the same |
3235 | | * configuration everywhere. |
3236 | | */ |
3237 | 0 | child->app_io_instance = inst->app_io_instance; |
3238 | |
|
3239 | 0 | } else { |
3240 | 0 | child->thread_instance = inst->app_io_instance; |
3241 | 0 | child->app_io_instance = child->thread_instance; |
3242 | 0 | } |
3243 | | |
3244 | | /* |
3245 | | * Don't call connection_set() for the main socket. It's |
3246 | | * not connected. Instead, tell the IO path to open the |
3247 | | * socket for us. |
3248 | | */ |
3249 | 0 | if (inst->app_io->open(child) < 0) { |
3250 | 0 | talloc_free(li); |
3251 | 0 | return -1; |
3252 | 0 | } |
3253 | | |
3254 | 0 | li->fd = child->fd; /* copy this back up */ |
3255 | |
|
3256 | 0 | if (!child->app_io->get_name) { |
3257 | 0 | child->name = child->app_io->common.name; |
3258 | 0 | } else { |
3259 | 0 | child->name = child->app_io->get_name(child); |
3260 | 0 | } |
3261 | 0 | li->name = child->name; |
3262 | | |
3263 | | /* |
3264 | | * Record which socket we opened. |
3265 | | */ |
3266 | 0 | if (child->app_io_addr) { |
3267 | 0 | fr_listen_t *other; |
3268 | |
|
3269 | 0 | other = listen_find_any(thread->child); |
3270 | 0 | if (other) { |
3271 | 0 | cf_log_err(other->cs, "Already opened socket %s", other->name); |
3272 | 0 | cf_log_err(li->cs, "Failed opening duplicate socket - cannot use the same configuration for two different listen sections"); |
3273 | |
|
3274 | 0 | talloc_free(li); |
3275 | 0 | return -1; |
3276 | 0 | } |
3277 | | |
3278 | 0 | (void) listen_record(child); |
3279 | 0 | } |
3280 | | |
3281 | | /* |
3282 | | * Add the socket to the scheduler, where it might end up |
3283 | | * in a different thread. |
3284 | | */ |
3285 | 0 | if (!fr_schedule_listen_add(sc, li)) { |
3286 | 0 | talloc_free(li); |
3287 | 0 | return -1; |
3288 | 0 | } |
3289 | | |
3290 | 0 | return 0; |
3291 | 0 | } |
3292 | | |
3293 | | /* |
3294 | | * Used to create a tracking structure for fr_network_sendto_worker() |
3295 | | */ |
3296 | | fr_io_track_t *fr_master_io_track_alloc(fr_listen_t *li, fr_client_t *radclient, fr_ipaddr_t const *src_ipaddr, int src_port, |
3297 | | fr_ipaddr_t const *dst_ipaddr, int dst_port) |
3298 | | { |
3299 | | fr_io_instance_t const *inst; |
3300 | | fr_io_thread_t *thread; |
3301 | | fr_io_connection_t *connection; |
3302 | | fr_listen_t *child; |
3303 | | fr_io_track_t *track; |
3304 | | fr_io_client_t *client; |
3305 | | fr_io_address_t *address; |
3306 | | fr_listen_t *parent = talloc_parent(li); |
3307 | | |
3308 | | (void) talloc_get_type_abort(parent, fr_listen_t); |
3309 | | |
3310 | | get_inst(parent, &inst, &thread, &connection, &child); |
3311 | | |
3312 | | fr_assert(child == li); |
3313 | | |
3314 | | if (unlikely(!thread)) return NULL; |
3315 | | fr_assert(thread->trie != NULL); |
3316 | | |
3317 | | client = fr_trie_lookup_by_key(thread->trie, &src_ipaddr->addr, src_ipaddr->prefix); |
3318 | | if (!client) { |
3319 | | MEM(client = client_alloc(thread, PR_CLIENT_STATIC, inst, thread, radclient, NULL)); |
3320 | | } |
3321 | | |
3322 | | MEM(track = talloc_zero_pooled_object(client, fr_io_track_t, 1, sizeof(*track) + sizeof(*track->address) + 64)); |
3323 | | MEM(track->address = address = talloc_zero(track, fr_io_address_t)); |
3324 | | |
3325 | | track->li = li; |
3326 | | track->client = client; |
3327 | | |
3328 | | address->socket.inet.src_port = src_port; |
3329 | | address->socket.inet.dst_port = dst_port; |
3330 | | |
3331 | | address->socket.inet.src_ipaddr = *src_ipaddr; |
3332 | | address->socket.inet.dst_ipaddr = *dst_ipaddr; |
3333 | | address->radclient = radclient; |
3334 | | |
3335 | | return track; |
3336 | | } |
3337 | | |
3338 | | |
3339 | | fr_app_io_t fr_master_app_io = { |
3340 | | .common = { |
3341 | | .magic = MODULE_MAGIC_INIT, |
3342 | | .name = "radius_master_io", |
3343 | | |
3344 | | .instantiate = mod_instantiate, |
3345 | | }, |
3346 | | .default_message_size = 4096, |
3347 | | .track_duplicates = true, |
3348 | | |
3349 | | .read = mod_read, |
3350 | | .write = mod_write, |
3351 | | .inject = mod_inject, |
3352 | | |
3353 | | .open = mod_open, |
3354 | | .close = mod_close, |
3355 | | .event_list_set = mod_event_list_set, |
3356 | | .get_name = mod_name, |
3357 | | }; |