2 * Copyright (c) 2012 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
34 * @file UDP associations
39 #include <fibril_synch.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
));
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
);
83 assoc
->ident
.local
= *lsock
;
85 assoc
->ident
.foreign
= *fsock
;
92 /** Destroy association structure.
94 * Association structure should be destroyed when the folowing conditions
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
);
114 udp_msg_delete(rqe
->msg
);
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
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
)
230 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "udp_assoc_send(%p, %p, %p)",
233 /* @a fsock can be used to override the foreign socket */
238 if (sp
.foreign
.addr
.ipv4
== 0 || sp
.foreign
.port
== 0)
241 rc
= udp_pdu_encode(&sp
, msg
, &pdu
);
245 rc
= udp_transmit_pdu(pdu
);
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
)
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
);
275 fibril_mutex_unlock(&assoc
->lock
);
278 *fsock
= rqe
->sp
.foreign
;
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
)
293 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "udp_assoc_received(%p, %p)", rsp
, msg
);
295 assoc
= udp_assoc_find_ref(rsp
);
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. */
303 rc
= udp_assoc_queue_msg(assoc
, rsp
, msg
);
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
,
313 udp_rcv_queue_entry_t
*rqe
;
315 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "udp_assoc_queue_msg(%p, %p, %p)",
318 rqe
= calloc(1, sizeof(udp_rcv_queue_entry_t
));
322 link_initialize(&rqe
->link
);
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
);
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
)
345 if (patt
->port
!= UDP_PORT_ANY
&&
346 patt
->port
!= sock
->port
)
349 log_msg(LOG_DEFAULT
, LVL_DEBUG
, " -> match");
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
))
362 if (!udp_socket_match(&sp
->foreign
, &pattern
->foreign
))
365 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Socket pair matched.");
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
)
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
);
404 fibril_mutex_unlock(&assoc_list_lock
);