2 * Copyright (c) 2012 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
41 #include <str_error.h>
42 #include <fibril_synch.h>
43 #include <inet/iplink_srv.h>
46 #include <nic_iface.h>
50 #include "ethip_nic.h"
53 static errno_t
ethip_nic_open(service_id_t sid
);
54 static void ethip_nic_cb_conn(ipc_call_t
*icall
, void *arg
);
56 static LIST_INITIALIZE(ethip_nic_list
);
57 static FIBRIL_MUTEX_INITIALIZE(ethip_discovery_lock
);
59 static errno_t
ethip_nic_check_new(void)
62 category_id_t iplink_cat
;
67 fibril_mutex_lock(ðip_discovery_lock
);
69 rc
= loc_category_get_id("nic", &iplink_cat
, IPC_FLAG_BLOCKING
);
71 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed resolving category 'nic'.");
72 fibril_mutex_unlock(ðip_discovery_lock
);
76 rc
= loc_category_get_svcs(iplink_cat
, &svcs
, &count
);
78 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed getting list of IP links.");
79 fibril_mutex_unlock(ðip_discovery_lock
);
83 for (i
= 0; i
< count
; i
++) {
84 already_known
= false;
86 list_foreach(ethip_nic_list
, link
, ethip_nic_t
, nic
) {
87 if (nic
->svc_id
== svcs
[i
]) {
94 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Found NIC '%lu'",
95 (unsigned long) svcs
[i
]);
96 rc
= ethip_nic_open(svcs
[i
]);
98 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Could not open NIC.");
102 fibril_mutex_unlock(ðip_discovery_lock
);
106 static ethip_nic_t
*ethip_nic_new(void)
108 ethip_nic_t
*nic
= calloc(1, sizeof(ethip_nic_t
));
110 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed allocating NIC structure. "
115 link_initialize(&nic
->link
);
116 list_initialize(&nic
->addr_list
);
121 static ethip_link_addr_t
*ethip_nic_addr_new(inet_addr_t
*addr
)
123 ethip_link_addr_t
*laddr
= calloc(1, sizeof(ethip_link_addr_t
));
125 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed allocating NIC address structure. "
130 link_initialize(&laddr
->link
);
136 static void ethip_nic_delete(ethip_nic_t
*nic
)
138 if (nic
->svc_name
!= NULL
)
144 static void ethip_link_addr_delete(ethip_link_addr_t
*laddr
)
149 static errno_t
ethip_nic_open(service_id_t sid
)
151 bool in_list
= false;
152 nic_address_t nic_address
;
154 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_open()");
155 ethip_nic_t
*nic
= ethip_nic_new();
159 errno_t rc
= loc_service_get_name(sid
, &nic
->svc_name
);
161 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed getting service name.");
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
);
173 rc
= nic_callback_create(nic
->sess
, ethip_nic_cb_conn
, nic
);
175 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed creating callback connection "
176 "from '%s'", nic
->svc_name
);
180 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Opened NIC '%s'", nic
->svc_name
);
181 list_append(&nic
->link
, ðip_nic_list
);
184 rc
= ethip_iplink_init(nic
);
188 rc
= nic_get_address(nic
->sess
, &nic_address
);
190 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Error getting MAC address of NIC '%s'.",
195 addr48(nic_address
.address
, nic
->mac_addr
);
197 rc
= nic_set_state(nic
->sess
, NIC_STATE_ACTIVE
);
199 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Error activating NIC '%s'.",
204 rc
= nic_broadcast_set_mode(nic
->sess
, NIC_BROADCAST_ACCEPTED
);
206 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Error enabling "
207 "reception of broadcast frames on '%s'.", nic
->svc_name
);
211 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Initialized IP link service,");
217 list_remove(&nic
->link
);
219 if (nic
->sess
!= NULL
)
220 async_hangup(nic
->sess
);
222 ethip_nic_delete(nic
);
226 static void ethip_nic_cat_change_cb(void *arg
)
228 (void) ethip_nic_check_new();
231 static void ethip_nic_addr_changed(ethip_nic_t
*nic
, ipc_call_t
*call
)
237 rc
= async_data_write_accept((void **) &addr
, false, 0, 0, 0, &size
);
239 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "data_write_accept() failed");
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]);
247 memcpy(&nic
->mac_addr
, addr
, sizeof(nic
->mac_addr
));
249 rc
= iplink_ev_change_addr(&nic
->iplink
, &nic
->mac_addr
);
251 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "iplink_ev_change_addr() failed");
256 async_answer_0(call
, EOK
);
259 static void ethip_nic_received(ethip_nic_t
*nic
, ipc_call_t
*call
)
265 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_received() nic=%p", nic
);
267 rc
= async_data_write_accept(&data
, false, 0, 0, 0, &size
);
269 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "data_write_accept() failed");
273 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Ethernet PDU contents (%zu bytes)",
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");
281 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_received() done, rc=%s", str_error_name(rc
));
282 async_answer_0(call
, rc
);
285 static void ethip_nic_device_state(ethip_nic_t
*nic
, ipc_call_t
*call
)
287 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_device_state()");
288 async_answer_0(call
, ENOTSUP
);
291 static void ethip_nic_cb_conn(ipc_call_t
*icall
, void *arg
)
293 ethip_nic_t
*nic
= (ethip_nic_t
*)arg
;
295 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethnip_nic_cb_conn()");
299 async_get_call(&call
);
301 if (!IPC_GET_IMETHOD(call
)) {
302 /* TODO: Handle hangup */
306 switch (IPC_GET_IMETHOD(call
)) {
307 case NIC_EV_ADDR_CHANGED
:
308 ethip_nic_addr_changed(nic
, &call
);
310 case NIC_EV_RECEIVED
:
311 ethip_nic_received(nic
, &call
);
313 case NIC_EV_DEVICE_STATE
:
314 ethip_nic_device_state(nic
, &call
);
317 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "unknown IPC method: %" PRIun
, IPC_GET_IMETHOD(call
));
318 async_answer_0(&call
, ENOTSUP
);
323 errno_t
ethip_nic_discovery_start(void)
325 errno_t rc
= loc_register_cat_change_cb(ethip_nic_cat_change_cb
, NULL
);
327 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed registering callback for NIC "
328 "discovery: %s.", str_error(rc
));
332 return ethip_nic_check_new();
335 ethip_nic_t
*ethip_nic_find_by_iplink_sid(service_id_t iplink_sid
)
337 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_find_by_iplink_sid(%u)",
338 (unsigned) iplink_sid
);
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
);
348 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_find_by_iplink_sid - not found");
352 errno_t
ethip_nic_send(ethip_nic_t
*nic
, void *data
, size_t size
)
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
));
361 /** Setup accepted multicast addresses
363 * Currently the set of accepted multicast addresses is
364 * determined only based on IPv6 addresses.
367 static errno_t
ethip_nic_setup_multicast(ethip_nic_t
*nic
)
369 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_setup_multicast()");
371 /* Count the number of multicast addresses */
375 list_foreach(nic
->addr_list
, link
, ethip_link_addr_t
, laddr
) {
376 ip_ver_t ver
= inet_addr_get(&laddr
->addr
, NULL
, NULL
);
382 return nic_multicast_set_mode(nic
->sess
, NIC_MULTICAST_BLOCKED
,
385 nic_address_t
*mac_list
= calloc(count
, sizeof(nic_address_t
));
386 if (mac_list
== NULL
)
389 /* Create the multicast MAC list */
393 list_foreach(nic
->addr_list
, link
, ethip_link_addr_t
, laddr
) {
395 ip_ver_t ver
= inet_addr_get(&laddr
->addr
, NULL
, &v6
);
402 addr48_solicited_node(v6
, mac
);
404 /* Avoid duplicate addresses in the list */
408 for (size_t j
= 0; j
< i
; j
++) {
409 if (addr48_compare(mac_list
[j
].address
, mac
)) {
416 addr48(mac
, mac_list
[i
].address
);
422 /* Setup the multicast MAC list */
424 errno_t rc
= nic_multicast_set_mode(nic
->sess
, NIC_MULTICAST_LIST
,
431 errno_t
ethip_nic_addr_add(ethip_nic_t
*nic
, inet_addr_t
*addr
)
433 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_addr_add()");
435 ethip_link_addr_t
*laddr
= ethip_nic_addr_new(addr
);
439 list_append(&laddr
->link
, &nic
->addr_list
);
441 return ethip_nic_setup_multicast(nic
);
444 errno_t
ethip_nic_addr_remove(ethip_nic_t
*nic
, inet_addr_t
*addr
)
446 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_addr_remove()");
448 ethip_link_addr_t
*laddr
= ethip_nic_addr_find(nic
, addr
);
452 list_remove(&laddr
->link
);
453 ethip_link_addr_delete(laddr
);
455 return ethip_nic_setup_multicast(nic
);
458 ethip_link_addr_t
*ethip_nic_addr_find(ethip_nic_t
*nic
,
461 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_addr_find()");
463 list_foreach(nic
->addr_list
, link
, ethip_link_addr_t
, laddr
) {
464 if (inet_addr_compare(addr
, &laddr
->addr
))