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

Last change on this file was b9be9b0, checked in by Jiri Svoboda <jiri@…>, 3 years ago

Fix handling of UDP default destination in udp_assoc_send_msg()

Fixes netecho failing to send any message.

  • Property mode set to 100644
File size: 11.1 KB
RevLine 
[ee603c4]1/*
[2f19103]2 * Copyright (c) 2015 Jiri Svoboda
[ee603c4]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>
[fab2746]38#include <errno.h>
[3e6a98c5]39#include <stdbool.h>
[ee603c4]40#include <fibril_synch.h>
[58e9dec]41#include <inet/endpoint.h>
[ee603c4]42#include <io/log.h>
[2989c7e]43#include <nettl/amap.h>
[ee603c4]44#include <stdlib.h>
45
46#include "assoc.h"
47#include "msg.h"
48#include "pdu.h"
49#include "udp_type.h"
50
[2989c7e]51static LIST_INITIALIZE(assoc_list);
52static FIBRIL_MUTEX_INITIALIZE(assoc_list_lock);
53static amap_t *amap;
[ee603c4]54
[2f19103]55static udp_assoc_t *udp_assoc_find_ref(inet_ep2_t *);
[b7fd2a0]56static errno_t udp_assoc_queue_msg(udp_assoc_t *, inet_ep2_t *, udp_msg_t *);
[89ba88c]57static udp_assocs_dep_t *assocs_dep;
[92b42442]58
[2989c7e]59/** Initialize associations. */
[89ba88c]60errno_t udp_assocs_init(udp_assocs_dep_t *dep)
[2989c7e]61{
[b7fd2a0]62 errno_t rc;
[2989c7e]63
64 rc = amap_create(&amap);
65 if (rc != EOK) {
66 assert(rc == ENOMEM);
67 return ENOMEM;
68 }
69
[89ba88c]70 assocs_dep = dep;
[2989c7e]71 return EOK;
72}
73
[16b0ac3]74/** Finalize associations. */
75void udp_assocs_fini(void)
76{
77 assert(list_empty(&assoc_list));
78
79 amap_destroy(amap);
80 amap = NULL;
81}
82
[ee603c4]83/** Create new association structure.
84 *
[2f19103]85 * @param epp Endpoint pair (will be copied)
86 * @param cb Callbacks
87 * @param cb_arg Callback argument
[ee603c4]88 * @return New association or NULL
89 */
[2f19103]90udp_assoc_t *udp_assoc_new(inet_ep2_t *epp, udp_assoc_cb_t *cb, void *cb_arg)
[ee603c4]91{
92 udp_assoc_t *assoc = NULL;
93
[92b42442]94 /* Allocate association structure */
[ee603c4]95 assoc = calloc(1, sizeof(udp_assoc_t));
96 if (assoc == NULL)
97 goto error;
98
99 fibril_mutex_initialize(&assoc->lock);
100
101 /* One for the user */
[498ced1]102 refcount_init(&assoc->refcnt);
[ee603c4]103
104 /* Initialize receive queue */
105 list_initialize(&assoc->rcv_queue);
106 fibril_condvar_initialize(&assoc->rcv_queue_cv);
107
[2f19103]108 if (epp != NULL)
109 assoc->ident = *epp;
[ee603c4]110
[fab2746]111 assoc->cb = cb;
112 assoc->cb_arg = cb_arg;
[ee603c4]113 return assoc;
114error:
115 return NULL;
116}
117
118/** Destroy association structure.
119 *
120 * Association structure should be destroyed when the folowing conditions
121 * are met:
122 * (1) user has deleted the association
123 * (2) nobody is holding references to the association
124 *
125 * This happens when @a assoc->refcnt is zero as we count (1)
126 * as an extra reference.
127 *
[92b42442]128 * @param assoc Association
[ee603c4]129 */
130static void udp_assoc_free(udp_assoc_t *assoc)
131{
[a1a101d]132 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_free(%p)", assoc->name, assoc);
[ee603c4]133
134 while (!list_empty(&assoc->rcv_queue)) {
135 link_t *link = list_first(&assoc->rcv_queue);
[92b42442]136 udp_rcv_queue_entry_t *rqe = list_get_instance(link,
137 udp_rcv_queue_entry_t, link);
[ee603c4]138 list_remove(link);
[92b42442]139
140 udp_msg_delete(rqe->msg);
141 free(rqe);
[ee603c4]142 }
143
144 free(assoc);
145}
146
147/** Add reference to association.
148 *
149 * Increase association reference count by one.
150 *
151 * @param assoc Association
152 */
153void udp_assoc_addref(udp_assoc_t *assoc)
154{
[a1a101d]155 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: upd_assoc_addref(%p)", assoc->name, assoc);
[498ced1]156 refcount_up(&assoc->refcnt);
[ee603c4]157}
158
159/** Remove reference from association.
160 *
161 * Decrease association reference count by one.
162 *
163 * @param assoc Association
164 */
165void udp_assoc_delref(udp_assoc_t *assoc)
166{
[a1a101d]167 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_delref(%p)", assoc->name, assoc);
[ee603c4]168
[498ced1]169 if (refcount_down(&assoc->refcnt))
[ee603c4]170 udp_assoc_free(assoc);
171}
172
173/** Delete association.
174 *
175 * The caller promises not make no further references to @a assoc.
176 * UDP will free @a assoc eventually.
177 *
178 * @param assoc Association
179 */
180void udp_assoc_delete(udp_assoc_t *assoc)
181{
[a1a101d]182 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: udp_assoc_delete(%p)", assoc->name, assoc);
[ee603c4]183
184 assert(assoc->deleted == false);
185 assoc->deleted = true;
[16b0ac3]186 udp_assoc_delref(assoc);
[ee603c4]187}
188
189/** Enlist association.
190 *
191 * Add association to the association map.
192 */
[b7fd2a0]193errno_t udp_assoc_add(udp_assoc_t *assoc)
[ee603c4]194{
[2989c7e]195 inet_ep2_t aepp;
[b7fd2a0]196 errno_t rc;
[2989c7e]197
[ee603c4]198 udp_assoc_addref(assoc);
199 fibril_mutex_lock(&assoc_list_lock);
[2989c7e]200
201 rc = amap_insert(amap, &assoc->ident, assoc, af_allow_system, &aepp);
202 if (rc != EOK) {
203 udp_assoc_delref(assoc);
204 fibril_mutex_unlock(&assoc_list_lock);
205 return rc;
206 }
207
208 assoc->ident = aepp;
[ee603c4]209 list_append(&assoc->link, &assoc_list);
210 fibril_mutex_unlock(&assoc_list_lock);
[2989c7e]211
212 return EOK;
[ee603c4]213}
214
215/** Delist association.
216 *
217 * Remove association from the association map.
218 */
219void udp_assoc_remove(udp_assoc_t *assoc)
220{
221 fibril_mutex_lock(&assoc_list_lock);
[2989c7e]222 amap_remove(amap, &assoc->ident);
[ee603c4]223 list_remove(&assoc->link);
224 fibril_mutex_unlock(&assoc_list_lock);
225 udp_assoc_delref(assoc);
226}
227
[695b6ff]228/** Set IP link in association.
229 *
230 * @param assoc Association
231 * @param iplink IP link
232 */
233void udp_assoc_set_iplink(udp_assoc_t *assoc, service_id_t iplink)
234{
235 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_iplink(%p, %zu)",
236 assoc, iplink);
237 fibril_mutex_lock(&assoc->lock);
[2f19103]238 assoc->ident.local_link = iplink;
[695b6ff]239 fibril_mutex_unlock(&assoc->lock);
240}
241
[ee603c4]242/** Send message to association.
243 *
244 * @param assoc Association
[b9be9b0]245 * @param remote Remote endpoint or inet_addr_any/inet_port_any
246 * not to override association's remote endpoint
[ee603c4]247 * @param msg Message
248 *
249 * @return EOK on success
[2f19103]250 * EINVAL if remote endpoint is not set
[ee603c4]251 * ENOMEM if out of resources
252 * EIO if no route to destination exists
253 */
[b7fd2a0]254errno_t udp_assoc_send(udp_assoc_t *assoc, inet_ep_t *remote, udp_msg_t *msg)
[ee603c4]255{
[2f19103]256 inet_ep2_t epp;
[b7fd2a0]257 errno_t rc;
[ee603c4]258
[c3f7d37]259 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send(%p, %p, %p)",
[2f19103]260 assoc, remote, msg);
[92b42442]261
[2f19103]262 /* @a remote can be used to override the remote endpoint */
263 epp = assoc->ident;
[b9be9b0]264 if (!inet_addr_is_any(&remote->addr) &&
265 remote->port != inet_port_any)
[2f19103]266 epp.remote = *remote;
[ee603c4]267
[c3f7d37]268 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - check addr any");
[fab2746]269
[2f19103]270 if ((inet_addr_is_any(&epp.remote.addr)) ||
[58e9dec]271 (epp.remote.port == inet_port_any))
[ee603c4]272 return EINVAL;
273
[58e8646]274 /* This association has no local address set. Need to determine one. */
275 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - check no local addr");
276 if (inet_addr_is_any(&epp.local.addr) && !assoc->nolocal) {
277 log_msg(LOG_DEFAULT, LVL_DEBUG, "Determine local address.");
[89ba88c]278 rc = (*assocs_dep->get_srcaddr)(&epp.remote.addr, 0,
279 &epp.local.addr);
[58e8646]280 if (rc != EOK) {
281 log_msg(LOG_DEFAULT, LVL_DEBUG, "Cannot determine "
282 "local address.");
283 return EINVAL;
284 }
285 }
286
[c3f7d37]287 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - check version");
[fab2746]288
[58e8646]289 if (!inet_addr_is_any(&epp.local.addr) &&
290 epp.remote.addr.version != epp.local.addr.version)
[fab2746]291 return EINVAL;
292
[c3f7d37]293 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - transmit");
[89ba88c]294 rc = (*assocs_dep->transmit_msg)(&epp, msg);
[071a2c60]295
[ee603c4]296 if (rc != EOK)
297 return EIO;
298
[c3f7d37]299 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - success");
[ee603c4]300 return EOK;
301}
302
[92b42442]303/** Get a received message.
304 *
305 * Pull one message from the association's receive queue.
306 */
[b7fd2a0]307errno_t udp_assoc_recv(udp_assoc_t *assoc, udp_msg_t **msg, inet_ep_t *remote)
[92b42442]308{
309 link_t *link;
310 udp_rcv_queue_entry_t *rqe;
311
[c3f7d37]312 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv()");
[92b42442]313
314 fibril_mutex_lock(&assoc->lock);
[141a20d]315 while (list_empty(&assoc->rcv_queue) && !assoc->reset) {
[a1a101d]316 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - waiting");
[92b42442]317 fibril_condvar_wait(&assoc->rcv_queue_cv, &assoc->lock);
318 }
319
[141a20d]320 if (assoc->reset) {
[a1e2df13]321 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - association was reset");
[141a20d]322 fibril_mutex_unlock(&assoc->lock);
[fab2746]323 return ENXIO;
[141a20d]324 }
325
[c3f7d37]326 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_recv() - got a message");
[92b42442]327 link = list_first(&assoc->rcv_queue);
328 rqe = list_get_instance(link, udp_rcv_queue_entry_t, link);
329 list_remove(link);
330 fibril_mutex_unlock(&assoc->lock);
331
332 *msg = rqe->msg;
[2f19103]333 *remote = rqe->epp.remote;
[7094e196]334 free(rqe);
[92b42442]335
336 return EOK;
337}
338
339/** Message received.
340 *
341 * Find the association to which the message belongs and queue it.
342 */
[2f19103]343void udp_assoc_received(inet_ep2_t *repp, udp_msg_t *msg)
[92b42442]344{
345 udp_assoc_t *assoc;
[b7fd2a0]346 errno_t rc;
[92b42442]347
[c3f7d37]348 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_received(%p, %p)", repp, msg);
[92b42442]349
[2f19103]350 assoc = udp_assoc_find_ref(repp);
[92b42442]351 if (assoc == NULL) {
[c3f7d37]352 log_msg(LOG_DEFAULT, LVL_DEBUG, "No association found. Message dropped.");
[92b42442]353 /* XXX Generate ICMP error. */
354 /* XXX Might propagate error directly by error return. */
[071a2c60]355 udp_msg_delete(msg);
[92b42442]356 return;
357 }
358
[fab2746]359 if (0) {
[2f19103]360 rc = udp_assoc_queue_msg(assoc, repp, msg);
[fab2746]361 if (rc != EOK) {
362 log_msg(LOG_DEFAULT, LVL_DEBUG, "Out of memory. Message dropped.");
[ae7d03c]363 /* XXX Generate ICMP error? */
[fab2746]364 }
[92b42442]365 }
[fab2746]366
[c3f7d37]367 log_msg(LOG_DEFAULT, LVL_DEBUG, "call assoc->cb->recv_msg");
[2f19103]368 assoc->cb->recv_msg(assoc->cb_arg, repp, msg);
[99ea91b2]369 udp_assoc_delref(assoc);
[92b42442]370}
371
[141a20d]372/** Reset association.
373 *
374 * This causes any pendingreceive operations to return immediately with
375 * UDP_ERESET.
376 */
377void udp_assoc_reset(udp_assoc_t *assoc)
378{
379 fibril_mutex_lock(&assoc->lock);
380 assoc->reset = true;
381 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
382 fibril_mutex_unlock(&assoc->lock);
383}
384
[b7fd2a0]385static errno_t udp_assoc_queue_msg(udp_assoc_t *assoc, inet_ep2_t *epp,
[92b42442]386 udp_msg_t *msg)
387{
388 udp_rcv_queue_entry_t *rqe;
389
[a1a101d]390 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_queue_msg(%p, %p, %p)",
[2f19103]391 assoc, epp, msg);
[92b42442]392
393 rqe = calloc(1, sizeof(udp_rcv_queue_entry_t));
394 if (rqe == NULL)
395 return ENOMEM;
396
397 link_initialize(&rqe->link);
[2f19103]398 rqe->epp = *epp;
[92b42442]399 rqe->msg = msg;
400
401 fibril_mutex_lock(&assoc->lock);
402 list_append(&rqe->link, &assoc->rcv_queue);
403 fibril_mutex_unlock(&assoc->lock);
404
405 fibril_condvar_broadcast(&assoc->rcv_queue_cv);
406
407 return EOK;
408}
409
[2f19103]410/** Find association structure for specified endpoint pair.
[92b42442]411 *
[2f19103]412 * An association is uniquely identified by an endpoint pair. Look up our
413 * association map and return association structure based on endpoint pair.
[92b42442]414 * The association reference count is bumped by one.
415 *
[2f19103]416 * @param epp Endpoint pair
[92b42442]417 * @return Association structure or NULL if not found.
418 */
[2f19103]419static udp_assoc_t *udp_assoc_find_ref(inet_ep2_t *epp)
[92b42442]420{
[b7fd2a0]421 errno_t rc;
[8d48c7e]422 void *arg;
423 udp_assoc_t *assoc;
[fab2746]424
[c3f7d37]425 log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_find_ref(%p)", epp);
[92b42442]426 fibril_mutex_lock(&assoc_list_lock);
[2f19103]427
[8d48c7e]428 rc = amap_find_match(amap, epp, &arg);
429 if (rc != EOK) {
[443a0bc]430 assert(rc == ENOENT);
[8d48c7e]431 fibril_mutex_unlock(&assoc_list_lock);
432 return NULL;
[92b42442]433 }
[2f19103]434
[8d48c7e]435 assoc = (udp_assoc_t *)arg;
436 udp_assoc_addref(assoc);
437
[92b42442]438 fibril_mutex_unlock(&assoc_list_lock);
[8d48c7e]439 return assoc;
[92b42442]440}
441
[ee603c4]442/**
443 * @}
444 */
Note: See TracBrowser for help on using the repository browser.