source: mainline/uspace/srv/net/udp/assoc.c@ 2989c7e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2989c7e was 2989c7e, checked in by Jiri Svoboda <jiri@…>, 10 years ago

Association map / portrange prototype.

  • Property mode set to 100644
File size: 12.1 KB
Line 
1/*
2 * Copyright (c) 2015 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup udp
30 * @{
31 */
32
33/**
34 * @file UDP associations
35 */
36
37#include <adt/list.h>
38#include <errno.h>
39#include <stdbool.h>
40#include <fibril_synch.h>
41#include <inet/endpoint.h>
42#include <io/log.h>
43#include <nettl/amap.h>
44#include <stdlib.h>
45
46#include "assoc.h"
47#include "msg.h"
48#include "pdu.h"
49#include "udp_inet.h"
50#include "udp_type.h"
51
52static LIST_INITIALIZE(assoc_list);
53static FIBRIL_MUTEX_INITIALIZE(assoc_list_lock);
54static amap_t *amap;
55
56static udp_assoc_t *udp_assoc_find_ref(inet_ep2_t *);
57static int udp_assoc_queue_msg(udp_assoc_t *, inet_ep2_t *, udp_msg_t *);
58static bool udp_ep_match(inet_ep_t *, inet_ep_t *);
59static bool udp_ep2_match(inet_ep2_t *, inet_ep2_t *);
60
61/** Initialize associations. */
62int udp_assocs_init(void)
63{
64 int rc;
65
66 rc = amap_create(&amap);
67 if (rc != EOK) {
68 assert(rc == ENOMEM);
69 return ENOMEM;
70 }
71
72 return EOK;
73}
74
75/** Create new association structure.
76 *
77 * @param epp Endpoint pair (will be copied)
78 * @param cb Callbacks
79 * @param cb_arg Callback argument
80 * @return New association or NULL
81 */
82udp_assoc_t *udp_assoc_new(inet_ep2_t *epp, udp_assoc_cb_t *cb, void *cb_arg)
83{
84 udp_assoc_t *assoc = NULL;
85
86 /* Allocate association structure */
87 assoc = calloc(1, sizeof(udp_assoc_t));
88 if (assoc == NULL)
89 goto error;
90
91 fibril_mutex_initialize(&assoc->lock);
92
93 /* One for the user */
94 atomic_set(&assoc->refcnt, 1);
95
96 /* Initialize receive queue */
97 list_initialize(&assoc->rcv_queue);
98 fibril_condvar_initialize(&assoc->rcv_queue_cv);
99
100 if (epp != NULL)
101 assoc->ident = *epp;
102
103 assoc->cb = cb;
104 assoc->cb_arg = cb_arg;
105 return assoc;
106error:
107 return NULL;
108}
109
110/** Destroy association structure.
111 *
112 * Association structure should be destroyed when the folowing conditions
113 * are met:
114 * (1) user has deleted the association
115 * (2) nobody is holding references to the association
116 *
117 * This happens when @a assoc->refcnt is zero as we count (1)
118 * as an extra reference.
119 *
120 * @param assoc Association
121 */
122static void udp_assoc_free(udp_assoc_t *assoc)
123{
124 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_free(%p)", assoc->name, assoc);
125
126 while (!list_empty(&assoc->rcv_queue)) {
127 link_t *link = list_first(&assoc->rcv_queue);
128 udp_rcv_queue_entry_t *rqe = list_get_instance(link,
129 udp_rcv_queue_entry_t, link);
130 list_remove(link);
131
132 udp_msg_delete(rqe->msg);
133 free(rqe);
134 }
135
136 free(assoc);
137}
138
139/** Add reference to association.
140 *
141 * Increase association reference count by one.
142 *
143 * @param assoc Association
144 */
145void udp_assoc_addref(udp_assoc_t *assoc)
146{
147 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: upd_assoc_addref(%p)", assoc->name, assoc);
148 atomic_inc(&assoc->refcnt);
149}
150
151/** Remove reference from association.
152 *
153 * Decrease association reference count by one.
154 *
155 * @param assoc Association
156 */
157void udp_assoc_delref(udp_assoc_t *assoc)
158{
159 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_delref(%p)", assoc->name, assoc);
160
161 if (atomic_predec(&assoc->refcnt) == 0)
162 udp_assoc_free(assoc);
163}
164
165/** Delete association.
166 *
167 * The caller promises not make no further references to @a assoc.
168 * UDP will free @a assoc eventually.
169 *
170 * @param assoc Association
171 */
172void udp_assoc_delete(udp_assoc_t *assoc)
173{
174 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_delete(%p)", assoc->name, assoc);
175
176 assert(assoc->deleted == false);
177 udp_assoc_delref(assoc);
178 assoc->deleted = true;
179}
180
181/** Enlist association.
182 *
183 * Add association to the association map.
184 */
185int udp_assoc_add(udp_assoc_t *assoc)
186{
187 inet_ep2_t aepp;
188 int rc;
189
190 udp_assoc_addref(assoc);
191 fibril_mutex_lock(&assoc_list_lock);
192
193 rc = amap_insert(amap, &assoc->ident, assoc, af_allow_system, &aepp);
194 if (rc != EOK) {
195 udp_assoc_delref(assoc);
196 fibril_mutex_unlock(&assoc_list_lock);
197 return rc;
198 }
199
200 assoc->ident = aepp;
201 list_append(&assoc->link, &assoc_list);
202 fibril_mutex_unlock(&assoc_list_lock);
203
204 return EOK;
205}
206
207/** Delist association.
208 *
209 * Remove association from the association map.
210 */
211void udp_assoc_remove(udp_assoc_t *assoc)
212{
213 fibril_mutex_lock(&assoc_list_lock);
214 amap_remove(amap, &assoc->ident);
215 list_remove(&assoc->link);
216 fibril_mutex_unlock(&assoc_list_lock);
217 udp_assoc_delref(assoc);
218}
219
220/** Set IP link in association.
221 *
222 * @param assoc Association
223 * @param iplink IP link
224 */
225void udp_assoc_set_iplink(udp_assoc_t *assoc, service_id_t iplink)
226{
227 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_iplink(%p, %zu)",
228 assoc, iplink);
229 fibril_mutex_lock(&assoc->lock);
230 assoc->ident.local_link = iplink;
231 fibril_mutex_unlock(&assoc->lock);
232}
233
234/** Send message to association.
235 *
236 * @param assoc Association
237 * @param remote Remote endpoint or NULL not to override @a assoc
238 * @param msg Message
239 *
240 * @return EOK on success
241 * EINVAL if remote endpoint is not set
242 * ENOMEM if out of resources
243 * EIO if no route to destination exists
244 */
245int udp_assoc_send(udp_assoc_t *assoc, inet_ep_t *remote, udp_msg_t *msg)
246{
247 udp_pdu_t *pdu;
248 inet_ep2_t epp;
249 int rc;
250
251 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send(%p, %p, %p)",
252 assoc, remote, msg);
253
254 /* @a remote can be used to override the remote endpoint */
255 epp = assoc->ident;
256 if (remote != NULL)
257 epp.remote = *remote;
258
259 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send - check addr any");
260
261 if ((inet_addr_is_any(&epp.remote.addr)) ||
262 (epp.remote.port == inet_port_any))
263 return EINVAL;
264
265 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send - check version");
266
267 if (epp.remote.addr.version != epp.local.addr.version)
268 return EINVAL;
269
270 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send - encode pdu");
271
272 rc = udp_pdu_encode(&epp, msg, &pdu);
273 if (rc != EOK)
274 return ENOMEM;
275
276 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send - transmit");
277
278 rc = udp_transmit_pdu(pdu);
279 udp_pdu_delete(pdu);
280
281 if (rc != EOK)
282 return EIO;
283
284 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send - success");
285 return EOK;
286}
287
288/** Get a received message.
289 *
290 * Pull one message from the association's receive queue.
291 */
292int udp_assoc_recv(udp_assoc_t *assoc, udp_msg_t **msg, inet_ep_t *remote)
293{
294 link_t *link;
295 udp_rcv_queue_entry_t *rqe;
296
297 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_recv()");
298
299 fibril_mutex_lock(&assoc->lock);
300 while (list_empty(&assoc->rcv_queue) && !assoc->reset) {
301 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - waiting");
302 fibril_condvar_wait(&assoc->rcv_queue_cv, &assoc->lock);
303 }
304
305 if (assoc->reset) {
306 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - association was reset");
307 fibril_mutex_unlock(&assoc->lock);
308 return ENXIO;
309 }
310
311 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_recv() - got a message");
312 link = list_first(&assoc->rcv_queue);
313 rqe = list_get_instance(link, udp_rcv_queue_entry_t, link);
314 list_remove(link);
315 fibril_mutex_unlock(&assoc->lock);
316
317 *msg = rqe->msg;
318 *remote = rqe->epp.remote;
319 free(rqe);
320
321 return EOK;
322}
323
324/** Message received.
325 *
326 * Find the association to which the message belongs and queue it.
327 */
328void udp_assoc_received(inet_ep2_t *repp, udp_msg_t *msg)
329{
330 udp_assoc_t *assoc;
331 int rc;
332
333 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_received(%p, %p)", repp, msg);
334
335 assoc = udp_assoc_find_ref(repp);
336 if (assoc == NULL) {
337 log_msg(LOG_DEFAULT, LVL_NOTE, "No association found. Message dropped.");
338 /* XXX Generate ICMP error. */
339 /* XXX Might propagate error directly by error return. */
340 udp_msg_delete(msg);
341 return;
342 }
343
344 if (0) {
345 rc = udp_assoc_queue_msg(assoc, repp, msg);
346 if (rc != EOK) {
347 log_msg(LOG_DEFAULT, LVL_DEBUG, "Out of memory. Message dropped.");
348 /* XXX Generate ICMP error? */
349 }
350 }
351
352 log_msg(LOG_DEFAULT, LVL_NOTE, "call assoc->cb->recv_msg");
353 assoc->cb->recv_msg(assoc->cb_arg, repp, msg);
354 udp_assoc_delref(assoc);
355}
356
357/** Reset association.
358 *
359 * This causes any pendingreceive operations to return immediately with
360 * UDP_ERESET.
361 */
362void udp_assoc_reset(udp_assoc_t *assoc)
363{
364 fibril_mutex_lock(&assoc->lock);
365 assoc->reset = true;
366 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
367 fibril_mutex_unlock(&assoc->lock);
368}
369
370static int udp_assoc_queue_msg(udp_assoc_t *assoc, inet_ep2_t *epp,
371 udp_msg_t *msg)
372{
373 udp_rcv_queue_entry_t *rqe;
374
375 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_queue_msg(%p, %p, %p)",
376 assoc, epp, msg);
377
378 rqe = calloc(1, sizeof(udp_rcv_queue_entry_t));
379 if (rqe == NULL)
380 return ENOMEM;
381
382 link_initialize(&rqe->link);
383 rqe->epp = *epp;
384 rqe->msg = msg;
385
386 fibril_mutex_lock(&assoc->lock);
387 list_append(&rqe->link, &assoc->rcv_queue);
388 fibril_mutex_unlock(&assoc->lock);
389
390 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
391
392 return EOK;
393}
394
395/** Match endpoint with pattern. */
396static bool udp_ep_match(inet_ep_t *ep, inet_ep_t *patt)
397{
398 char *sa, *pa;
399
400 sa = pa = (char *)"?";
401 (void) inet_addr_format(&ep->addr, &sa);
402 (void) inet_addr_format(&patt->addr, &pa);
403
404 log_msg(LOG_DEFAULT, LVL_NOTE,
405 "udp_ep_match(ep=(%s,%u), pat=(%s,%u))",
406 sa, ep->port, pa, patt->port);
407
408 if ((!inet_addr_is_any(&patt->addr)) &&
409 (!inet_addr_compare(&patt->addr, &ep->addr)))
410 return false;
411
412 log_msg(LOG_DEFAULT, LVL_NOTE, "addr OK");
413
414 if ((patt->port != inet_port_any) &&
415 (patt->port != ep->port))
416 return false;
417
418 log_msg(LOG_DEFAULT, LVL_NOTE, " -> match");
419
420 return true;
421}
422
423/** Match endpoint pair with pattern. */
424static bool udp_ep2_match(inet_ep2_t *epp, inet_ep2_t *pattern)
425{
426 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_ep2_match(%p, %p)", epp, pattern);
427
428 if (!udp_ep_match(&epp->local, &pattern->local))
429 return false;
430
431 if (!udp_ep_match(&epp->remote, &pattern->remote))
432 return false;
433
434 log_msg(LOG_DEFAULT, LVL_DEBUG, "Endpoint pair matched.");
435 return true;
436}
437
438
439/** Find association structure for specified endpoint pair.
440 *
441 * An association is uniquely identified by an endpoint pair. Look up our
442 * association map and return association structure based on endpoint pair.
443 * The association reference count is bumped by one.
444 *
445 * @param epp Endpoint pair
446 * @return Association structure or NULL if not found.
447 */
448static udp_assoc_t *udp_assoc_find_ref(inet_ep2_t *epp)
449{
450 char *la, *ra;
451
452 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_find_ref(%p)", epp);
453
454 fibril_mutex_lock(&assoc_list_lock);
455
456 log_msg(LOG_DEFAULT, LVL_NOTE, "associations:");
457 list_foreach(assoc_list, link, udp_assoc_t, assoc) {
458 inet_ep2_t *aepp = &assoc->ident;
459
460 la = ra = NULL;
461
462 (void) inet_addr_format(&aepp->local.addr, &la);
463 (void) inet_addr_format(&aepp->remote.addr, &ra);
464
465 log_msg(LOG_DEFAULT, LVL_NOTE, "find_ref:aepp=%p la=%s ra=%s",
466 aepp, la, ra);
467 /* Skip unbound associations */
468 if (aepp->local.port == inet_port_any) {
469 log_msg(LOG_DEFAULT, LVL_NOTE, "skip unbound");
470 continue;
471 }
472
473 if (udp_ep2_match(epp, aepp)) {
474 log_msg(LOG_DEFAULT, LVL_DEBUG, "Returning assoc %p", assoc);
475 udp_assoc_addref(assoc);
476 fibril_mutex_unlock(&assoc_list_lock);
477 return assoc;
478 } else {
479 log_msg(LOG_DEFAULT, LVL_NOTE, "not matched");
480 }
481 }
482
483 log_msg(LOG_DEFAULT, LVL_NOTE, "associations END");
484 fibril_mutex_unlock(&assoc_list_lock);
485 return NULL;
486}
487
488/**
489 * @}
490 */
Note: See TracBrowser for help on using the repository browser.