source: mainline/uspace/srv/net/udp/assoc.c@ 12df1f1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 12df1f1 was a2e3ee6, checked in by Martin Decky <martin@…>, 12 years ago

use new network address infrastructure (towards IPv6 support)

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