source: mainline/uspace/srv/net/ethip/ethip_nic.c@ 80d9d83

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 80d9d83 was 83781a22, checked in by Martin Decky <martin@…>, 12 years ago

setup NIC multicast address filter based on the configured IPv6 addresses
(multicast functionality is required for NDP)

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