source: mainline/uspace/srv/net/udp/assoc.c@ 7947c34

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

Fix leaks in UDP error paths. Allow greater UDP message size. (thx Fan Jinfei)

  • Property mode set to 100644
File size: 11.6 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 IP link in association.
189 *
190 * @param assoc Association
191 * @param iplink IP link
192 */
193void udp_assoc_set_iplink(udp_assoc_t *assoc, service_id_t iplink)
194{
195 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_iplink(%p, %zu)",
196 assoc, iplink);
197 fibril_mutex_lock(&assoc->lock);
198 assoc->ident.iplink = iplink;
199 fibril_mutex_unlock(&assoc->lock);
200}
201
202/** Set foreign socket in association.
203 *
204 * @param assoc Association
205 * @param fsock Foreign socket (deeply copied)
206 */
207void udp_assoc_set_foreign(udp_assoc_t *assoc, udp_sock_t *fsock)
208{
209 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_foreign(%p, %p)", assoc, fsock);
210 fibril_mutex_lock(&assoc->lock);
211 assoc->ident.foreign = *fsock;
212 fibril_mutex_unlock(&assoc->lock);
213}
214
215/** Set local socket in association.
216 *
217 * @param assoc Association
218 * @param lsock Local socket (deeply copied)
219 *
220 */
221void udp_assoc_set_local(udp_assoc_t *assoc, udp_sock_t *lsock)
222{
223 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_local(%p, %p)", assoc, lsock);
224 fibril_mutex_lock(&assoc->lock);
225 assoc->ident.local = *lsock;
226 fibril_mutex_unlock(&assoc->lock);
227}
228
229/** Set local port in association.
230 *
231 * @param assoc Association
232 * @param lport Local port
233 *
234 */
235void udp_assoc_set_local_port(udp_assoc_t *assoc, uint16_t lport)
236{
237 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_local(%p, %" PRIu16 ")", assoc, lport);
238 fibril_mutex_lock(&assoc->lock);
239 assoc->ident.local.port = lport;
240 fibril_mutex_unlock(&assoc->lock);
241}
242
243/** Send message to association.
244 *
245 * @param assoc Association
246 * @param fsock Foreign socket or NULL not to override @a assoc
247 * @param msg Message
248 *
249 * @return EOK on success
250 * EINVAL if foreign socket is not set
251 * ENOMEM if out of resources
252 * EIO if no route to destination exists
253 */
254int udp_assoc_send(udp_assoc_t *assoc, udp_sock_t *fsock, udp_msg_t *msg)
255{
256 udp_pdu_t *pdu;
257 udp_sockpair_t sp;
258 int rc;
259
260 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send(%p, %p, %p)",
261 assoc, fsock, msg);
262
263 /* @a fsock can be used to override the foreign socket */
264 sp = assoc->ident;
265 if (fsock != NULL)
266 sp.foreign = *fsock;
267
268 if ((inet_addr_is_any(&sp.foreign.addr)) ||
269 (sp.foreign.port == UDP_PORT_ANY))
270 return EINVAL;
271
272 rc = udp_pdu_encode(&sp, msg, &pdu);
273 if (rc != EOK)
274 return ENOMEM;
275
276 rc = udp_transmit_pdu(pdu);
277 udp_pdu_delete(pdu);
278
279 if (rc != EOK)
280 return EIO;
281
282 return EOK;
283}
284
285/** Get a received message.
286 *
287 * Pull one message from the association's receive queue.
288 */
289int udp_assoc_recv(udp_assoc_t *assoc, udp_msg_t **msg, udp_sock_t *fsock)
290{
291 link_t *link;
292 udp_rcv_queue_entry_t *rqe;
293
294 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv()");
295
296 fibril_mutex_lock(&assoc->lock);
297 while (list_empty(&assoc->rcv_queue) && !assoc->reset) {
298 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - waiting");
299 fibril_condvar_wait(&assoc->rcv_queue_cv, &assoc->lock);
300 }
301
302 if (assoc->reset) {
303 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - association was reset");
304 fibril_mutex_unlock(&assoc->lock);
305 return ECONNABORTED;
306 }
307
308 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - got a message");
309 link = list_first(&assoc->rcv_queue);
310 rqe = list_get_instance(link, udp_rcv_queue_entry_t, link);
311 list_remove(link);
312 fibril_mutex_unlock(&assoc->lock);
313
314 *msg = rqe->msg;
315 *fsock = rqe->sp.foreign;
316 free(rqe);
317
318 return EOK;
319}
320
321/** Message received.
322 *
323 * Find the association to which the message belongs and queue it.
324 */
325void udp_assoc_received(udp_sockpair_t *rsp, udp_msg_t *msg)
326{
327 udp_assoc_t *assoc;
328 int rc;
329
330 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_received(%p, %p)", rsp, msg);
331
332 assoc = udp_assoc_find_ref(rsp);
333 if (assoc == NULL) {
334 log_msg(LOG_DEFAULT, LVL_DEBUG, "No association found. Message dropped.");
335 /* XXX Generate ICMP error. */
336 /* XXX Might propagate error directly by error return. */
337 udp_msg_delete(msg);
338 return;
339 }
340
341 rc = udp_assoc_queue_msg(assoc, rsp, msg);
342 if (rc != EOK) {
343 log_msg(LOG_DEFAULT, LVL_DEBUG, "Out of memory. Message dropped.");
344 /* XXX Generate ICMP error? */
345 }
346}
347
348/** Reset association.
349 *
350 * This causes any pendingreceive operations to return immediately with
351 * UDP_ERESET.
352 */
353void udp_assoc_reset(udp_assoc_t *assoc)
354{
355 fibril_mutex_lock(&assoc->lock);
356 assoc->reset = true;
357 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
358 fibril_mutex_unlock(&assoc->lock);
359}
360
361static int udp_assoc_queue_msg(udp_assoc_t *assoc, udp_sockpair_t *sp,
362 udp_msg_t *msg)
363{
364 udp_rcv_queue_entry_t *rqe;
365
366 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_queue_msg(%p, %p, %p)",
367 assoc, sp, msg);
368
369 rqe = calloc(1, sizeof(udp_rcv_queue_entry_t));
370 if (rqe == NULL)
371 return ENOMEM;
372
373 link_initialize(&rqe->link);
374 rqe->sp = *sp;
375 rqe->msg = msg;
376
377 fibril_mutex_lock(&assoc->lock);
378 list_append(&rqe->link, &assoc->rcv_queue);
379 fibril_mutex_unlock(&assoc->lock);
380
381 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
382
383 return EOK;
384}
385
386/** Match socket with pattern. */
387static bool udp_socket_match(udp_sock_t *sock, udp_sock_t *patt)
388{
389 log_msg(LOG_DEFAULT, LVL_DEBUG,
390 "udp_socket_match(sock=(%u), pat=(%u))", sock->port, patt->port);
391
392 if ((!inet_addr_is_any(&patt->addr)) &&
393 (!inet_addr_compare(&patt->addr, &sock->addr)))
394 return false;
395
396 if ((patt->port != UDP_PORT_ANY) &&
397 (patt->port != sock->port))
398 return false;
399
400 log_msg(LOG_DEFAULT, LVL_DEBUG, " -> match");
401
402 return true;
403}
404
405/** Match socket pair with pattern. */
406static bool udp_sockpair_match(udp_sockpair_t *sp, udp_sockpair_t *pattern)
407{
408 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sockpair_match(%p, %p)", sp, pattern);
409
410 if (!udp_socket_match(&sp->local, &pattern->local))
411 return false;
412
413 if (!udp_socket_match(&sp->foreign, &pattern->foreign))
414 return false;
415
416 log_msg(LOG_DEFAULT, LVL_DEBUG, "Socket pair matched.");
417 return true;
418}
419
420
421/** Find association structure for specified socket pair.
422 *
423 * An association is uniquely identified by a socket pair. Look up our
424 * association map and return association structure based on socket pair.
425 * The association reference count is bumped by one.
426 *
427 * @param sp Socket pair
428 * @return Association structure or NULL if not found.
429 */
430static udp_assoc_t *udp_assoc_find_ref(udp_sockpair_t *sp)
431{
432 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_find_ref(%p)", sp);
433
434 fibril_mutex_lock(&assoc_list_lock);
435
436 list_foreach(assoc_list, link, udp_assoc_t, assoc) {
437 udp_sockpair_t *asp = &assoc->ident;
438
439 /* Skip unbound associations */
440 if (asp->local.port == UDP_PORT_ANY)
441 continue;
442
443 if (udp_sockpair_match(sp, asp)) {
444 log_msg(LOG_DEFAULT, LVL_DEBUG, "Returning assoc %p", assoc);
445 udp_assoc_addref(assoc);
446 fibril_mutex_unlock(&assoc_list_lock);
447 return assoc;
448 }
449 }
450
451 fibril_mutex_unlock(&assoc_list_lock);
452 return NULL;
453}
454
455/**
456 * @}
457 */
Note: See TracBrowser for help on using the repository browser.