source: mainline/uspace/srv/net/udp/assoc.c@ 25eec4ef

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

Merge mainline chages.

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