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

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

Crude DHCP client prototype.

  • Property mode set to 100644
File size: 11.5 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 if (rc != EOK)
278 return EIO;
279
280 udp_pdu_delete(pdu);
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 return;
338 }
339
340 rc = udp_assoc_queue_msg(assoc, rsp, msg);
341 if (rc != EOK) {
342 log_msg(LOG_DEFAULT, LVL_DEBUG, "Out of memory. Message dropped.");
343 /* XXX Generate ICMP error? */
344 }
345}
346
347/** Reset association.
348 *
349 * This causes any pendingreceive operations to return immediately with
350 * UDP_ERESET.
351 */
352void udp_assoc_reset(udp_assoc_t *assoc)
353{
354 fibril_mutex_lock(&assoc->lock);
355 assoc->reset = true;
356 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
357 fibril_mutex_unlock(&assoc->lock);
358}
359
360static int udp_assoc_queue_msg(udp_assoc_t *assoc, udp_sockpair_t *sp,
361 udp_msg_t *msg)
362{
363 udp_rcv_queue_entry_t *rqe;
364
365 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_queue_msg(%p, %p, %p)",
366 assoc, sp, msg);
367
368 rqe = calloc(1, sizeof(udp_rcv_queue_entry_t));
369 if (rqe == NULL)
370 return ENOMEM;
371
372 link_initialize(&rqe->link);
373 rqe->sp = *sp;
374 rqe->msg = msg;
375
376 fibril_mutex_lock(&assoc->lock);
377 list_append(&rqe->link, &assoc->rcv_queue);
378 fibril_mutex_unlock(&assoc->lock);
379
380 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
381
382 return EOK;
383}
384
385/** Match socket with pattern. */
386static bool udp_socket_match(udp_sock_t *sock, udp_sock_t *patt)
387{
388 log_msg(LOG_DEFAULT, LVL_DEBUG,
389 "udp_socket_match(sock=(%u), pat=(%u))", sock->port, patt->port);
390
391 if ((!inet_addr_is_any(&patt->addr)) &&
392 (!inet_addr_compare(&patt->addr, &sock->addr)))
393 return false;
394
395 if ((patt->port != UDP_PORT_ANY) &&
396 (patt->port != sock->port))
397 return false;
398
399 log_msg(LOG_DEFAULT, LVL_DEBUG, " -> match");
400
401 return true;
402}
403
404/** Match socket pair with pattern. */
405static bool udp_sockpair_match(udp_sockpair_t *sp, udp_sockpair_t *pattern)
406{
407 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sockpair_match(%p, %p)", sp, pattern);
408
409 if (!udp_socket_match(&sp->local, &pattern->local))
410 return false;
411
412 if (!udp_socket_match(&sp->foreign, &pattern->foreign))
413 return false;
414
415 log_msg(LOG_DEFAULT, LVL_DEBUG, "Socket pair matched.");
416 return true;
417}
418
419
420/** Find association structure for specified socket pair.
421 *
422 * An association is uniquely identified by a socket pair. Look up our
423 * association map and return association structure based on socket pair.
424 * The association reference count is bumped by one.
425 *
426 * @param sp Socket pair
427 * @return Association structure or NULL if not found.
428 */
429static udp_assoc_t *udp_assoc_find_ref(udp_sockpair_t *sp)
430{
431 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_find_ref(%p)", sp);
432
433 fibril_mutex_lock(&assoc_list_lock);
434
435 list_foreach(assoc_list, link, udp_assoc_t, assoc) {
436 udp_sockpair_t *asp = &assoc->ident;
437
438 /* Skip unbound associations */
439 if (asp->local.port == UDP_PORT_ANY)
440 continue;
441
442 if (udp_sockpair_match(sp, asp)) {
443 log_msg(LOG_DEFAULT, LVL_DEBUG, "Returning assoc %p", assoc);
444 udp_assoc_addref(assoc);
445 fibril_mutex_unlock(&assoc_list_lock);
446 return assoc;
447 }
448 }
449
450 fibril_mutex_unlock(&assoc_list_lock);
451 return NULL;
452}
453
454/**
455 * @}
456 */
Note: See TracBrowser for help on using the repository browser.