source: mainline/uspace/srv/net/ethip/ethip_nic.c@ 5e5498e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5e5498e was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 11.4 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 ethip
30 * @{
31 */
32/**
33 * @file
34 * @brief
35 */
36
37#include <adt/list.h>
38#include <async.h>
39#include <stdbool.h>
40#include <errno.h>
41#include <str_error.h>
42#include <fibril_synch.h>
43#include <inet/iplink_srv.h>
44#include <io/log.h>
45#include <loc.h>
46#include <nic_iface.h>
47#include <stdlib.h>
48#include <mem.h>
49#include "ethip.h"
50#include "ethip_nic.h"
51#include "pdu.h"
52
53static errno_t ethip_nic_open(service_id_t sid);
54static void ethip_nic_cb_conn(ipc_call_t *icall, void *arg);
55
56static LIST_INITIALIZE(ethip_nic_list);
57static FIBRIL_MUTEX_INITIALIZE(ethip_discovery_lock);
58
59static errno_t ethip_nic_check_new(void)
60{
61 bool already_known;
62 category_id_t iplink_cat;
63 service_id_t *svcs;
64 size_t count, i;
65 errno_t rc;
66
67 fibril_mutex_lock(&ethip_discovery_lock);
68
69 rc = loc_category_get_id("nic", &iplink_cat, IPC_FLAG_BLOCKING);
70 if (rc != EOK) {
71 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed resolving category 'nic'.");
72 fibril_mutex_unlock(&ethip_discovery_lock);
73 return ENOENT;
74 }
75
76 rc = loc_category_get_svcs(iplink_cat, &svcs, &count);
77 if (rc != EOK) {
78 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting list of IP links.");
79 fibril_mutex_unlock(&ethip_discovery_lock);
80 return EIO;
81 }
82
83 for (i = 0; i < count; i++) {
84 already_known = false;
85
86 list_foreach(ethip_nic_list, link, ethip_nic_t, nic) {
87 if (nic->svc_id == svcs[i]) {
88 already_known = true;
89 break;
90 }
91 }
92
93 if (!already_known) {
94 log_msg(LOG_DEFAULT, LVL_DEBUG, "Found NIC '%lu'",
95 (unsigned long) svcs[i]);
96 rc = ethip_nic_open(svcs[i]);
97 if (rc != EOK)
98 log_msg(LOG_DEFAULT, LVL_ERROR, "Could not open NIC.");
99 }
100 }
101
102 fibril_mutex_unlock(&ethip_discovery_lock);
103 return EOK;
104}
105
106static ethip_nic_t *ethip_nic_new(void)
107{
108 ethip_nic_t *nic = calloc(1, sizeof(ethip_nic_t));
109 if (nic == NULL) {
110 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed allocating NIC structure. "
111 "Out of memory.");
112 return NULL;
113 }
114
115 link_initialize(&nic->link);
116 list_initialize(&nic->addr_list);
117
118 return nic;
119}
120
121static ethip_link_addr_t *ethip_nic_addr_new(inet_addr_t *addr)
122{
123 ethip_link_addr_t *laddr = calloc(1, sizeof(ethip_link_addr_t));
124 if (laddr == NULL) {
125 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed allocating NIC address structure. "
126 "Out of memory.");
127 return NULL;
128 }
129
130 link_initialize(&laddr->link);
131 laddr->addr = *addr;
132
133 return laddr;
134}
135
136static void ethip_nic_delete(ethip_nic_t *nic)
137{
138 if (nic->svc_name != NULL)
139 free(nic->svc_name);
140
141 free(nic);
142}
143
144static void ethip_link_addr_delete(ethip_link_addr_t *laddr)
145{
146 free(laddr);
147}
148
149static errno_t ethip_nic_open(service_id_t sid)
150{
151 bool in_list = false;
152 nic_address_t nic_address;
153
154 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_open()");
155 ethip_nic_t *nic = ethip_nic_new();
156 if (nic == NULL)
157 return ENOMEM;
158
159 errno_t rc = loc_service_get_name(sid, &nic->svc_name);
160 if (rc != EOK) {
161 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting service name.");
162 goto error;
163 }
164
165 nic->sess = loc_service_connect(sid, INTERFACE_DDF, 0);
166 if (nic->sess == NULL) {
167 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed connecting '%s'", nic->svc_name);
168 goto error;
169 }
170
171 nic->svc_id = sid;
172
173 rc = nic_callback_create(nic->sess, ethip_nic_cb_conn, nic);
174 if (rc != EOK) {
175 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed creating callback connection "
176 "from '%s'", nic->svc_name);
177 goto error;
178 }
179
180 log_msg(LOG_DEFAULT, LVL_DEBUG, "Opened NIC '%s'", nic->svc_name);
181 list_append(&nic->link, &ethip_nic_list);
182 in_list = true;
183
184 rc = ethip_iplink_init(nic);
185 if (rc != EOK)
186 goto error;
187
188 rc = nic_get_address(nic->sess, &nic_address);
189 if (rc != EOK) {
190 log_msg(LOG_DEFAULT, LVL_ERROR, "Error getting MAC address of NIC '%s'.",
191 nic->svc_name);
192 goto error;
193 }
194
195 addr48(nic_address.address, nic->mac_addr);
196
197 rc = nic_set_state(nic->sess, NIC_STATE_ACTIVE);
198 if (rc != EOK) {
199 log_msg(LOG_DEFAULT, LVL_ERROR, "Error activating NIC '%s'.",
200 nic->svc_name);
201 goto error;
202 }
203
204 rc = nic_broadcast_set_mode(nic->sess, NIC_BROADCAST_ACCEPTED);
205 if (rc != EOK) {
206 log_msg(LOG_DEFAULT, LVL_ERROR, "Error enabling "
207 "reception of broadcast frames on '%s'.", nic->svc_name);
208 goto error;
209 }
210
211 log_msg(LOG_DEFAULT, LVL_DEBUG, "Initialized IP link service,");
212
213 return EOK;
214
215error:
216 if (in_list)
217 list_remove(&nic->link);
218
219 if (nic->sess != NULL)
220 async_hangup(nic->sess);
221
222 ethip_nic_delete(nic);
223 return rc;
224}
225
226static void ethip_nic_cat_change_cb(void)
227{
228 (void) ethip_nic_check_new();
229}
230
231static void ethip_nic_addr_changed(ethip_nic_t *nic, ipc_call_t *call)
232{
233 uint8_t *addr;
234 size_t size;
235 errno_t rc;
236
237 rc = async_data_write_accept((void **) &addr, false, 0, 0, 0, &size);
238 if (rc != EOK) {
239 log_msg(LOG_DEFAULT, LVL_DEBUG, "data_write_accept() failed");
240 return;
241 }
242
243 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_addr_changed(): "
244 "new addr=%02x:%02x:%02x:%02x:%02x:%02x",
245 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
246
247 memcpy(&nic->mac_addr, addr, sizeof(nic->mac_addr));
248
249 rc = iplink_ev_change_addr(&nic->iplink, &nic->mac_addr);
250 if (rc != EOK) {
251 log_msg(LOG_DEFAULT, LVL_DEBUG, "iplink_ev_change_addr() failed");
252 return;
253 }
254
255 free(addr);
256 async_answer_0(call, EOK);
257}
258
259static void ethip_nic_received(ethip_nic_t *nic, ipc_call_t *call)
260{
261 errno_t rc;
262 void *data;
263 size_t size;
264
265 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_received() nic=%p", nic);
266
267 rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
268 if (rc != EOK) {
269 log_msg(LOG_DEFAULT, LVL_DEBUG, "data_write_accept() failed");
270 return;
271 }
272
273 log_msg(LOG_DEFAULT, LVL_DEBUG, "Ethernet PDU contents (%zu bytes)",
274 size);
275
276 log_msg(LOG_DEFAULT, LVL_DEBUG, "call ethip_received");
277 rc = ethip_received(&nic->iplink, data, size);
278 log_msg(LOG_DEFAULT, LVL_DEBUG, "free data");
279 free(data);
280
281 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_received() done, rc=%s", str_error_name(rc));
282 async_answer_0(call, rc);
283}
284
285static void ethip_nic_device_state(ethip_nic_t *nic, ipc_call_t *call)
286{
287 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_device_state()");
288 async_answer_0(call, ENOTSUP);
289}
290
291static void ethip_nic_cb_conn(ipc_call_t *icall, void *arg)
292{
293 ethip_nic_t *nic = (ethip_nic_t *)arg;
294
295 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethnip_nic_cb_conn()");
296
297 while (true) {
298 ipc_call_t call;
299 async_get_call(&call);
300
301 if (!IPC_GET_IMETHOD(call)) {
302 /* TODO: Handle hangup */
303 return;
304 }
305
306 switch (IPC_GET_IMETHOD(call)) {
307 case NIC_EV_ADDR_CHANGED:
308 ethip_nic_addr_changed(nic, &call);
309 break;
310 case NIC_EV_RECEIVED:
311 ethip_nic_received(nic, &call);
312 break;
313 case NIC_EV_DEVICE_STATE:
314 ethip_nic_device_state(nic, &call);
315 break;
316 default:
317 log_msg(LOG_DEFAULT, LVL_DEBUG, "unknown IPC method: %" PRIun, IPC_GET_IMETHOD(call));
318 async_answer_0(&call, ENOTSUP);
319 }
320 }
321}
322
323errno_t ethip_nic_discovery_start(void)
324{
325 errno_t rc = loc_register_cat_change_cb(ethip_nic_cat_change_cb);
326 if (rc != EOK) {
327 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering callback for NIC "
328 "discovery: %s.", str_error(rc));
329 return rc;
330 }
331
332 return ethip_nic_check_new();
333}
334
335ethip_nic_t *ethip_nic_find_by_iplink_sid(service_id_t iplink_sid)
336{
337 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_find_by_iplink_sid(%u)",
338 (unsigned) iplink_sid);
339
340 list_foreach(ethip_nic_list, link, ethip_nic_t, nic) {
341 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_find_by_iplink_sid - element");
342 if (nic->iplink_sid == iplink_sid) {
343 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_find_by_iplink_sid - found %p", nic);
344 return nic;
345 }
346 }
347
348 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_find_by_iplink_sid - not found");
349 return NULL;
350}
351
352errno_t ethip_nic_send(ethip_nic_t *nic, void *data, size_t size)
353{
354 errno_t rc;
355 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_send(size=%zu)", size);
356 rc = nic_send_frame(nic->sess, data, size);
357 log_msg(LOG_DEFAULT, LVL_DEBUG, "nic_send_frame -> %s", str_error_name(rc));
358 return rc;
359}
360
361/** Setup accepted multicast addresses
362 *
363 * Currently the set of accepted multicast addresses is
364 * determined only based on IPv6 addresses.
365 *
366 */
367static errno_t ethip_nic_setup_multicast(ethip_nic_t *nic)
368{
369 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_setup_multicast()");
370
371 /* Count the number of multicast addresses */
372
373 size_t count = 0;
374
375 list_foreach(nic->addr_list, link, ethip_link_addr_t, laddr) {
376 ip_ver_t ver = inet_addr_get(&laddr->addr, NULL, NULL);
377 if (ver == ip_v6)
378 count++;
379 }
380
381 if (count == 0)
382 return nic_multicast_set_mode(nic->sess, NIC_MULTICAST_BLOCKED,
383 NULL, 0);
384
385 nic_address_t *mac_list = calloc(count, sizeof(nic_address_t));
386 if (mac_list == NULL)
387 return ENOMEM;
388
389 /* Create the multicast MAC list */
390
391 size_t i = 0;
392
393 list_foreach(nic->addr_list, link, ethip_link_addr_t, laddr) {
394 addr128_t v6;
395 ip_ver_t ver = inet_addr_get(&laddr->addr, NULL, &v6);
396 if (ver != ip_v6)
397 continue;
398
399 assert(i < count);
400
401 addr48_t mac;
402 addr48_solicited_node(v6, mac);
403
404 /* Avoid duplicate addresses in the list */
405
406 bool found = false;
407
408 for (size_t j = 0; j < i; j++) {
409 if (addr48_compare(mac_list[j].address, mac)) {
410 found = true;
411 break;
412 }
413 }
414
415 if (!found) {
416 addr48(mac, mac_list[i].address);
417 i++;
418 } else
419 count--;
420 }
421
422 /* Setup the multicast MAC list */
423
424 errno_t rc = nic_multicast_set_mode(nic->sess, NIC_MULTICAST_LIST,
425 mac_list, count);
426
427 free(mac_list);
428 return rc;
429}
430
431errno_t ethip_nic_addr_add(ethip_nic_t *nic, inet_addr_t *addr)
432{
433 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_addr_add()");
434
435 ethip_link_addr_t *laddr = ethip_nic_addr_new(addr);
436 if (laddr == NULL)
437 return ENOMEM;
438
439 list_append(&laddr->link, &nic->addr_list);
440
441 return ethip_nic_setup_multicast(nic);
442}
443
444errno_t ethip_nic_addr_remove(ethip_nic_t *nic, inet_addr_t *addr)
445{
446 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_addr_remove()");
447
448 ethip_link_addr_t *laddr = ethip_nic_addr_find(nic, addr);
449 if (laddr == NULL)
450 return ENOENT;
451
452 list_remove(&laddr->link);
453 ethip_link_addr_delete(laddr);
454
455 return ethip_nic_setup_multicast(nic);
456}
457
458ethip_link_addr_t *ethip_nic_addr_find(ethip_nic_t *nic,
459 inet_addr_t *addr)
460{
461 log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_addr_find()");
462
463 list_foreach(nic->addr_list, link, ethip_link_addr_t, laddr) {
464 if (inet_addr_compare(addr, &laddr->addr))
465 return laddr;
466 }
467
468 return NULL;
469}
470
471/** @}
472 */
Note: See TracBrowser for help on using the repository browser.