Get rid of log_log_msg()
[helenos.git] / uspace / srv / net / udp / assoc.c
blobfdf95d587bc3f5477772da068a6f1b242ef07543
1 /*
2 * Copyright (c) 2012 Jiri Svoboda
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 /** @addtogroup udp
30 * @{
33 /**
34 * @file UDP associations
37 #include <adt/list.h>
38 #include <bool.h>
39 #include <fibril_synch.h>
40 #include <io/log.h>
41 #include <stdlib.h>
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"
50 LIST_INITIALIZE(assoc_list);
51 FIBRIL_MUTEX_INITIALIZE(assoc_list_lock);
53 static udp_assoc_t *udp_assoc_find_ref(udp_sockpair_t *);
54 static int udp_assoc_queue_msg(udp_assoc_t *, udp_sockpair_t *, udp_msg_t *);
55 static bool udp_socket_match(udp_sock_t *, udp_sock_t *);
56 static bool udp_sockpair_match(udp_sockpair_t *, udp_sockpair_t *);
58 /** Create new association structure.
60 * @param lsock Local socket (will be deeply copied)
61 * @param fsock Foreign socket (will be deeply copied)
62 * @return New association or NULL
64 udp_assoc_t *udp_assoc_new(udp_sock_t *lsock, udp_sock_t *fsock)
66 udp_assoc_t *assoc = NULL;
68 /* Allocate association structure */
69 assoc = calloc(1, sizeof(udp_assoc_t));
70 if (assoc == NULL)
71 goto error;
73 fibril_mutex_initialize(&assoc->lock);
75 /* One for the user */
76 atomic_set(&assoc->refcnt, 1);
78 /* Initialize receive queue */
79 list_initialize(&assoc->rcv_queue);
80 fibril_condvar_initialize(&assoc->rcv_queue_cv);
82 if (lsock != NULL)
83 assoc->ident.local = *lsock;
84 if (fsock != NULL)
85 assoc->ident.foreign = *fsock;
87 return assoc;
88 error:
89 return NULL;
92 /** Destroy association structure.
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
99 * This happens when @a assoc->refcnt is zero as we count (1)
100 * as an extra reference.
102 * @param assoc Association
104 static void udp_assoc_free(udp_assoc_t *assoc)
106 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_free(%p)", assoc->name, assoc);
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);
114 udp_msg_delete(rqe->msg);
115 free(rqe);
118 free(assoc);
121 /** Add reference to association.
123 * Increase association reference count by one.
125 * @param assoc Association
127 void udp_assoc_addref(udp_assoc_t *assoc)
129 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: upd_assoc_addref(%p)", assoc->name, assoc);
130 atomic_inc(&assoc->refcnt);
133 /** Remove reference from association.
135 * Decrease association reference count by one.
137 * @param assoc Association
139 void udp_assoc_delref(udp_assoc_t *assoc)
141 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_delref(%p)", assoc->name, assoc);
143 if (atomic_predec(&assoc->refcnt) == 0)
144 udp_assoc_free(assoc);
147 /** Delete association.
149 * The caller promises not make no further references to @a assoc.
150 * UDP will free @a assoc eventually.
152 * @param assoc Association
154 void udp_assoc_delete(udp_assoc_t *assoc)
156 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_delete(%p)", assoc->name, assoc);
158 assert(assoc->deleted == false);
159 udp_assoc_delref(assoc);
160 assoc->deleted = true;
163 /** Enlist association.
165 * Add association to the association map.
167 void udp_assoc_add(udp_assoc_t *assoc)
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);
175 /** Delist association.
177 * Remove association from the association map.
179 void udp_assoc_remove(udp_assoc_t *assoc)
181 fibril_mutex_lock(&assoc_list_lock);
182 list_remove(&assoc->link);
183 fibril_mutex_unlock(&assoc_list_lock);
184 udp_assoc_delref(assoc);
187 /** Set foreign socket in association.
189 * @param assoc Association
190 * @param fsock Foreign socket (deeply copied)
192 void udp_assoc_set_foreign(udp_assoc_t *assoc, udp_sock_t *fsock)
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);
200 /** Set local socket in association.
202 * @param assoc Association
203 * @param fsock Foreign socket (deeply copied)
205 void udp_assoc_set_local(udp_assoc_t *assoc, udp_sock_t *lsock)
207 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_local(%p, %p)", assoc, lsock);
208 fibril_mutex_lock(&assoc->lock);
209 assoc->ident.local = *lsock;
210 fibril_mutex_unlock(&assoc->lock);
213 /** Send message to association.
215 * @param assoc Association
216 * @param fsock Foreign socket or NULL not to override @a assoc
217 * @param msg Message
219 * @return EOK on success
220 * EINVAL if foreign socket is not set
221 * ENOMEM if out of resources
222 * EIO if no route to destination exists
224 int udp_assoc_send(udp_assoc_t *assoc, udp_sock_t *fsock, udp_msg_t *msg)
226 udp_pdu_t *pdu;
227 udp_sockpair_t sp;
228 int rc;
230 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send(%p, %p, %p)",
231 assoc, fsock, msg);
233 /* @a fsock can be used to override the foreign socket */
234 sp = assoc->ident;
235 if (fsock != NULL)
236 sp.foreign = *fsock;
238 if (sp.foreign.addr.ipv4 == 0 || sp.foreign.port == 0)
239 return EINVAL;
241 rc = udp_pdu_encode(&sp, msg, &pdu);
242 if (rc != EOK)
243 return ENOMEM;
245 rc = udp_transmit_pdu(pdu);
246 if (rc != EOK)
247 return EIO;
249 udp_pdu_delete(pdu);
251 return EOK;
254 /** Get a received message.
256 * Pull one message from the association's receive queue.
258 int udp_assoc_recv(udp_assoc_t *assoc, udp_msg_t **msg, udp_sock_t *fsock)
260 link_t *link;
261 udp_rcv_queue_entry_t *rqe;
263 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv()");
265 fibril_mutex_lock(&assoc->lock);
266 while (list_empty(&assoc->rcv_queue)) {
267 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - waiting");
268 fibril_condvar_wait(&assoc->rcv_queue_cv, &assoc->lock);
271 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - got a message");
272 link = list_first(&assoc->rcv_queue);
273 rqe = list_get_instance(link, udp_rcv_queue_entry_t, link);
274 list_remove(link);
275 fibril_mutex_unlock(&assoc->lock);
277 *msg = rqe->msg;
278 *fsock = rqe->sp.foreign;
279 free(rqe);
281 return EOK;
284 /** Message received.
286 * Find the association to which the message belongs and queue it.
288 void udp_assoc_received(udp_sockpair_t *rsp, udp_msg_t *msg)
290 udp_assoc_t *assoc;
291 int rc;
293 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_received(%p, %p)", rsp, msg);
295 assoc = udp_assoc_find_ref(rsp);
296 if (assoc == NULL) {
297 log_msg(LOG_DEFAULT, LVL_DEBUG, "No association found. Message dropped.");
298 /* XXX Generate ICMP error. */
299 /* XXX Might propagate error directly by error return. */
300 return;
303 rc = udp_assoc_queue_msg(assoc, rsp, msg);
304 if (rc != EOK) {
305 log_msg(LOG_DEFAULT, LVL_DEBUG, "Out of memory. Message dropped.");
306 /* XXX Generate ICMP error? */
310 static int udp_assoc_queue_msg(udp_assoc_t *assoc, udp_sockpair_t *sp,
311 udp_msg_t *msg)
313 udp_rcv_queue_entry_t *rqe;
315 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_queue_msg(%p, %p, %p)",
316 assoc, sp, msg);
318 rqe = calloc(1, sizeof(udp_rcv_queue_entry_t));
319 if (rqe == NULL)
320 return ENOMEM;
322 link_initialize(&rqe->link);
323 rqe->sp = *sp;
324 rqe->msg = msg;
326 fibril_mutex_lock(&assoc->lock);
327 list_append(&rqe->link, &assoc->rcv_queue);
328 fibril_mutex_unlock(&assoc->lock);
330 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
332 return EOK;
335 /** Match socket with pattern. */
336 static bool udp_socket_match(udp_sock_t *sock, udp_sock_t *patt)
338 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_socket_match(sock=(%x,%u), pat=(%x,%u))",
339 sock->addr.ipv4, sock->port, patt->addr.ipv4, patt->port);
341 if (patt->addr.ipv4 != UDP_IPV4_ANY &&
342 patt->addr.ipv4 != sock->addr.ipv4)
343 return false;
345 if (patt->port != UDP_PORT_ANY &&
346 patt->port != sock->port)
347 return false;
349 log_msg(LOG_DEFAULT, LVL_DEBUG, " -> match");
351 return true;
354 /** Match socket pair with pattern. */
355 static bool udp_sockpair_match(udp_sockpair_t *sp, udp_sockpair_t *pattern)
357 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sockpair_match(%p, %p)", sp, pattern);
359 if (!udp_socket_match(&sp->local, &pattern->local))
360 return false;
362 if (!udp_socket_match(&sp->foreign, &pattern->foreign))
363 return false;
365 log_msg(LOG_DEFAULT, LVL_DEBUG, "Socket pair matched.");
366 return true;
370 /** Find association structure for specified socket pair.
372 * An association is uniquely identified by a socket pair. Look up our
373 * association map and return association structure based on socket pair.
374 * The association reference count is bumped by one.
376 * @param sp Socket pair
377 * @return Association structure or NULL if not found.
379 static udp_assoc_t *udp_assoc_find_ref(udp_sockpair_t *sp)
381 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_find_ref(%p)", sp);
383 fibril_mutex_lock(&assoc_list_lock);
385 list_foreach(assoc_list, link) {
386 udp_assoc_t *assoc = list_get_instance(link, udp_assoc_t, link);
387 udp_sockpair_t *asp = &assoc->ident;
388 log_msg(LOG_DEFAULT, LVL_DEBUG, "compare with assoc (f:(%x,%u), l:(%x,%u))",
389 asp->foreign.addr.ipv4, asp->foreign.port,
390 asp->local.addr.ipv4, asp->local.port);
392 /* Skip unbound associations */
393 if (asp->local.port == UDP_PORT_ANY)
394 continue;
396 if (udp_sockpair_match(sp, asp)) {
397 log_msg(LOG_DEFAULT, LVL_DEBUG, "Returning assoc %p", assoc);
398 udp_assoc_addref(assoc);
399 fibril_mutex_unlock(&assoc_list_lock);
400 return assoc;
404 fibril_mutex_unlock(&assoc_list_lock);
405 return NULL;
410 * @}