source: mainline/uspace/srv/net/udp/assoc.c@ 58e9dec

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

Definitions for RFC 6335 port number ranges.

  • Property mode set to 100644
File size: 12.7 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 <stdlib.h>
44
45#include "assoc.h"
46#include "msg.h"
47#include "pdu.h"
48#include "ucall.h"
49#include "udp_inet.h"
50#include "udp_type.h"
51
52LIST_INITIALIZE(assoc_list);
53FIBRIL_MUTEX_INITIALIZE(assoc_list_lock);
54
55static udp_assoc_t *udp_assoc_find_ref(inet_ep2_t *);
56static int udp_assoc_queue_msg(udp_assoc_t *, inet_ep2_t *, udp_msg_t *);
57static bool udp_ep_match(inet_ep_t *, inet_ep_t *);
58static bool udp_ep2_match(inet_ep2_t *, inet_ep2_t *);
59
60/** Create new association structure.
61 *
62 * @param epp Endpoint pair (will be copied)
63 * @param cb Callbacks
64 * @param cb_arg Callback argument
65 * @return New association or NULL
66 */
67udp_assoc_t *udp_assoc_new(inet_ep2_t *epp, udp_assoc_cb_t *cb, void *cb_arg)
68{
69 udp_assoc_t *assoc = NULL;
70
71 /* Allocate association structure */
72 assoc = calloc(1, sizeof(udp_assoc_t));
73 if (assoc == NULL)
74 goto error;
75
76 fibril_mutex_initialize(&assoc->lock);
77
78 /* One for the user */
79 atomic_set(&assoc->refcnt, 1);
80
81 /* Initialize receive queue */
82 list_initialize(&assoc->rcv_queue);
83 fibril_condvar_initialize(&assoc->rcv_queue_cv);
84
85 if (epp != NULL)
86 assoc->ident = *epp;
87
88 assoc->cb = cb;
89 assoc->cb_arg = cb_arg;
90 return assoc;
91error:
92 return NULL;
93}
94
95/** Destroy association structure.
96 *
97 * Association structure should be destroyed when the folowing conditions
98 * are met:
99 * (1) user has deleted the association
100 * (2) nobody is holding references to the association
101 *
102 * This happens when @a assoc->refcnt is zero as we count (1)
103 * as an extra reference.
104 *
105 * @param assoc Association
106 */
107static void udp_assoc_free(udp_assoc_t *assoc)
108{
109 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_free(%p)", assoc->name, assoc);
110
111 while (!list_empty(&assoc->rcv_queue)) {
112 link_t *link = list_first(&assoc->rcv_queue);
113 udp_rcv_queue_entry_t *rqe = list_get_instance(link,
114 udp_rcv_queue_entry_t, link);
115 list_remove(link);
116
117 udp_msg_delete(rqe->msg);
118 free(rqe);
119 }
120
121 free(assoc);
122}
123
124/** Add reference to association.
125 *
126 * Increase association reference count by one.
127 *
128 * @param assoc Association
129 */
130void udp_assoc_addref(udp_assoc_t *assoc)
131{
132 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: upd_assoc_addref(%p)", assoc->name, assoc);
133 atomic_inc(&assoc->refcnt);
134}
135
136/** Remove reference from association.
137 *
138 * Decrease association reference count by one.
139 *
140 * @param assoc Association
141 */
142void udp_assoc_delref(udp_assoc_t *assoc)
143{
144 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_delref(%p)", assoc->name, assoc);
145
146 if (atomic_predec(&assoc->refcnt) == 0)
147 udp_assoc_free(assoc);
148}
149
150/** Delete association.
151 *
152 * The caller promises not make no further references to @a assoc.
153 * UDP will free @a assoc eventually.
154 *
155 * @param assoc Association
156 */
157void udp_assoc_delete(udp_assoc_t *assoc)
158{
159 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_delete(%p)", assoc->name, assoc);
160
161 assert(assoc->deleted == false);
162 udp_assoc_delref(assoc);
163 assoc->deleted = true;
164}
165
166/** Enlist association.
167 *
168 * Add association to the association map.
169 */
170void udp_assoc_add(udp_assoc_t *assoc)
171{
172 udp_assoc_addref(assoc);
173 fibril_mutex_lock(&assoc_list_lock);
174 list_append(&assoc->link, &assoc_list);
175 fibril_mutex_unlock(&assoc_list_lock);
176}
177
178/** Delist association.
179 *
180 * Remove association from the association map.
181 */
182void udp_assoc_remove(udp_assoc_t *assoc)
183{
184 fibril_mutex_lock(&assoc_list_lock);
185 list_remove(&assoc->link);
186 fibril_mutex_unlock(&assoc_list_lock);
187 udp_assoc_delref(assoc);
188}
189
190/** Set IP link in association.
191 *
192 * @param assoc Association
193 * @param iplink IP link
194 */
195void udp_assoc_set_iplink(udp_assoc_t *assoc, service_id_t iplink)
196{
197 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_iplink(%p, %zu)",
198 assoc, iplink);
199 fibril_mutex_lock(&assoc->lock);
200 assoc->ident.local_link = iplink;
201 fibril_mutex_unlock(&assoc->lock);
202}
203
204/** Set remote endpoint in association.
205 *
206 * @param assoc Association
207 * @param remote Remote endpoint (deeply copied)
208 */
209void udp_assoc_set_remote(udp_assoc_t *assoc, inet_ep_t *remote)
210{
211 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_remote(%p, %p)", assoc, remote);
212 fibril_mutex_lock(&assoc->lock);
213 assoc->ident.remote = *remote;
214 fibril_mutex_unlock(&assoc->lock);
215}
216
217/** Set local endpoint in association.
218 *
219 * @param assoc Association
220 * @param local Local endpoint (deeply copied)
221 *
222 */
223void udp_assoc_set_local(udp_assoc_t *assoc, inet_ep_t *local)
224{
225 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_local(%p, %p)", assoc, local);
226 fibril_mutex_lock(&assoc->lock);
227 assoc->ident.local = *local;
228 fibril_mutex_unlock(&assoc->lock);
229}
230
231/** Set local port in association.
232 *
233 * @param assoc Association
234 * @param lport Local port
235 *
236 */
237void udp_assoc_set_local_port(udp_assoc_t *assoc, uint16_t lport)
238{
239 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_local(%p, %" PRIu16 ")", assoc, lport);
240 fibril_mutex_lock(&assoc->lock);
241 assoc->ident.local.port = lport;
242 fibril_mutex_unlock(&assoc->lock);
243}
244
245/** Send message to association.
246 *
247 * @param assoc Association
248 * @param remote Remote endpoint or NULL not to override @a assoc
249 * @param msg Message
250 *
251 * @return EOK on success
252 * EINVAL if remote endpoint is not set
253 * ENOMEM if out of resources
254 * EIO if no route to destination exists
255 */
256int udp_assoc_send(udp_assoc_t *assoc, inet_ep_t *remote, udp_msg_t *msg)
257{
258 udp_pdu_t *pdu;
259 inet_ep2_t epp;
260 int rc;
261
262 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send(%p, %p, %p)",
263 assoc, remote, msg);
264
265 /* @a remote can be used to override the remote endpoint */
266 epp = assoc->ident;
267 if (remote != NULL)
268 epp.remote = *remote;
269
270 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send - check addr any");
271
272 if ((inet_addr_is_any(&epp.remote.addr)) ||
273 (epp.remote.port == inet_port_any))
274 return EINVAL;
275
276 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send - check version");
277
278 if (epp.remote.addr.version != epp.local.addr.version)
279 return EINVAL;
280
281 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send - encode pdu");
282
283 rc = udp_pdu_encode(&epp, msg, &pdu);
284 if (rc != EOK)
285 return ENOMEM;
286
287 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send - transmit");
288
289 rc = udp_transmit_pdu(pdu);
290 udp_pdu_delete(pdu);
291
292 if (rc != EOK)
293 return EIO;
294
295 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send - success");
296 return EOK;
297}
298
299/** Get a received message.
300 *
301 * Pull one message from the association's receive queue.
302 */
303int udp_assoc_recv(udp_assoc_t *assoc, udp_msg_t **msg, inet_ep_t *remote)
304{
305 link_t *link;
306 udp_rcv_queue_entry_t *rqe;
307
308 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_recv()");
309
310 fibril_mutex_lock(&assoc->lock);
311 while (list_empty(&assoc->rcv_queue) && !assoc->reset) {
312 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - waiting");
313 fibril_condvar_wait(&assoc->rcv_queue_cv, &assoc->lock);
314 }
315
316 if (assoc->reset) {
317 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - association was reset");
318 fibril_mutex_unlock(&assoc->lock);
319 return ENXIO;
320 }
321
322 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_recv() - got a message");
323 link = list_first(&assoc->rcv_queue);
324 rqe = list_get_instance(link, udp_rcv_queue_entry_t, link);
325 list_remove(link);
326 fibril_mutex_unlock(&assoc->lock);
327
328 *msg = rqe->msg;
329 *remote = rqe->epp.remote;
330 free(rqe);
331
332 return EOK;
333}
334
335/** Message received.
336 *
337 * Find the association to which the message belongs and queue it.
338 */
339void udp_assoc_received(inet_ep2_t *repp, udp_msg_t *msg)
340{
341 udp_assoc_t *assoc;
342 int rc;
343
344 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_received(%p, %p)", repp, msg);
345
346 assoc = udp_assoc_find_ref(repp);
347 if (assoc == NULL) {
348 log_msg(LOG_DEFAULT, LVL_NOTE, "No association found. Message dropped.");
349 /* XXX Generate ICMP error. */
350 /* XXX Might propagate error directly by error return. */
351 udp_msg_delete(msg);
352 return;
353 }
354
355 if (0) {
356 rc = udp_assoc_queue_msg(assoc, repp, msg);
357 if (rc != EOK) {
358 log_msg(LOG_DEFAULT, LVL_DEBUG, "Out of memory. Message dropped.");
359 /* XXX Generate ICMP error? */
360 }
361 }
362
363 log_msg(LOG_DEFAULT, LVL_NOTE, "call assoc->cb->recv_msg");
364 assoc->cb->recv_msg(assoc->cb_arg, repp, msg);
365 udp_assoc_delref(assoc);
366}
367
368/** Reset association.
369 *
370 * This causes any pendingreceive operations to return immediately with
371 * UDP_ERESET.
372 */
373void udp_assoc_reset(udp_assoc_t *assoc)
374{
375 fibril_mutex_lock(&assoc->lock);
376 assoc->reset = true;
377 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
378 fibril_mutex_unlock(&assoc->lock);
379}
380
381static int udp_assoc_queue_msg(udp_assoc_t *assoc, inet_ep2_t *epp,
382 udp_msg_t *msg)
383{
384 udp_rcv_queue_entry_t *rqe;
385
386 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_queue_msg(%p, %p, %p)",
387 assoc, epp, msg);
388
389 rqe = calloc(1, sizeof(udp_rcv_queue_entry_t));
390 if (rqe == NULL)
391 return ENOMEM;
392
393 link_initialize(&rqe->link);
394 rqe->epp = *epp;
395 rqe->msg = msg;
396
397 fibril_mutex_lock(&assoc->lock);
398 list_append(&rqe->link, &assoc->rcv_queue);
399 fibril_mutex_unlock(&assoc->lock);
400
401 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
402
403 return EOK;
404}
405
406/** Match endpoint with pattern. */
407static bool udp_ep_match(inet_ep_t *ep, inet_ep_t *patt)
408{
409 char *sa, *pa;
410
411 sa = pa = (char *)"?";
412 (void) inet_addr_format(&ep->addr, &sa);
413 (void) inet_addr_format(&patt->addr, &pa);
414
415 log_msg(LOG_DEFAULT, LVL_NOTE,
416 "udp_ep_match(ep=(%s,%u), pat=(%s,%u))",
417 sa, ep->port, pa, patt->port);
418
419 if ((!inet_addr_is_any(&patt->addr)) &&
420 (!inet_addr_compare(&patt->addr, &ep->addr)))
421 return false;
422
423 log_msg(LOG_DEFAULT, LVL_NOTE, "addr OK");
424
425 if ((patt->port != inet_port_any) &&
426 (patt->port != ep->port))
427 return false;
428
429 log_msg(LOG_DEFAULT, LVL_NOTE, " -> match");
430
431 return true;
432}
433
434/** Match endpoint pair with pattern. */
435static bool udp_ep2_match(inet_ep2_t *epp, inet_ep2_t *pattern)
436{
437 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_ep2_match(%p, %p)", epp, pattern);
438
439 if (!udp_ep_match(&epp->local, &pattern->local))
440 return false;
441
442 if (!udp_ep_match(&epp->remote, &pattern->remote))
443 return false;
444
445 log_msg(LOG_DEFAULT, LVL_DEBUG, "Endpoint pair matched.");
446 return true;
447}
448
449
450/** Find association structure for specified endpoint pair.
451 *
452 * An association is uniquely identified by an endpoint pair. Look up our
453 * association map and return association structure based on endpoint pair.
454 * The association reference count is bumped by one.
455 *
456 * @param epp Endpoint pair
457 * @return Association structure or NULL if not found.
458 */
459static udp_assoc_t *udp_assoc_find_ref(inet_ep2_t *epp)
460{
461 char *la, *ra;
462
463 log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_find_ref(%p)", epp);
464
465 fibril_mutex_lock(&assoc_list_lock);
466
467 log_msg(LOG_DEFAULT, LVL_NOTE, "associations:");
468 list_foreach(assoc_list, link, udp_assoc_t, assoc) {
469 inet_ep2_t *aepp = &assoc->ident;
470
471 la = ra = NULL;
472
473 (void) inet_addr_format(&aepp->local.addr, &la);
474 (void) inet_addr_format(&aepp->remote.addr, &ra);
475
476 log_msg(LOG_DEFAULT, LVL_NOTE, "find_ref:aepp=%p la=%s ra=%s",
477 aepp, la, ra);
478 /* Skip unbound associations */
479 if (aepp->local.port == inet_port_any) {
480 log_msg(LOG_DEFAULT, LVL_NOTE, "skip unbound");
481 continue;
482 }
483
484 if (udp_ep2_match(epp, aepp)) {
485 log_msg(LOG_DEFAULT, LVL_DEBUG, "Returning assoc %p", assoc);
486 udp_assoc_addref(assoc);
487 fibril_mutex_unlock(&assoc_list_lock);
488 return assoc;
489 } else {
490 log_msg(LOG_DEFAULT, LVL_NOTE, "not matched");
491 }
492 }
493
494 log_msg(LOG_DEFAULT, LVL_NOTE, "associations END");
495 fibril_mutex_unlock(&assoc_list_lock);
496 return NULL;
497}
498
499/**
500 * @}
501 */
Note: See TracBrowser for help on using the repository browser.