source: mainline/uspace/srv/net/udp/assoc.c@ 2f19103

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

TCP and UDP servers can make use of inet/endpoint.h types internally.

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