2 * Copyright (c) 2024 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.
40 #include <fibril_synch.h>
41 #include <inet/eth_addr.h>
42 #include <inet/iplink_srv.h>
46 #include <nic_iface.h>
49 #include <str_error.h>
51 #include "ethip_nic.h"
54 static errno_t
ethip_nic_open(service_id_t sid
);
55 static void ethip_nic_cb_conn(ipc_call_t
*icall
, void *arg
);
57 static LIST_INITIALIZE(ethip_nic_list
);
58 static FIBRIL_MUTEX_INITIALIZE(ethip_discovery_lock
);
60 static errno_t
ethip_nic_check_new(void)
63 category_id_t iplink_cat
;
68 fibril_mutex_lock(ðip_discovery_lock
);
70 rc
= loc_category_get_id("nic", &iplink_cat
, IPC_FLAG_BLOCKING
);
72 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed resolving category 'nic'.");
73 fibril_mutex_unlock(ðip_discovery_lock
);
77 rc
= loc_category_get_svcs(iplink_cat
, &svcs
, &count
);
79 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed getting list of IP links.");
80 fibril_mutex_unlock(ðip_discovery_lock
);
84 for (i
= 0; i
< count
; i
++) {
85 already_known
= false;
87 list_foreach(ethip_nic_list
, link
, ethip_nic_t
, nic
) {
88 if (nic
->svc_id
== svcs
[i
]) {
95 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Found NIC '%lu'",
96 (unsigned long) svcs
[i
]);
97 rc
= ethip_nic_open(svcs
[i
]);
99 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Could not open NIC.");
103 fibril_mutex_unlock(ðip_discovery_lock
);
107 static ethip_nic_t
*ethip_nic_new(void)
109 ethip_nic_t
*nic
= calloc(1, sizeof(ethip_nic_t
));
111 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed allocating NIC structure. "
116 link_initialize(&nic
->link
);
117 list_initialize(&nic
->addr_list
);
122 static ethip_link_addr_t
*ethip_nic_addr_new(inet_addr_t
*addr
)
124 ethip_link_addr_t
*laddr
= calloc(1, sizeof(ethip_link_addr_t
));
126 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed allocating NIC address structure. "
131 link_initialize(&laddr
->link
);
137 static void ethip_nic_delete(ethip_nic_t
*nic
)
139 if (nic
->svc_name
!= NULL
)
145 static void ethip_link_addr_delete(ethip_link_addr_t
*laddr
)
150 static errno_t
ethip_nic_open(service_id_t sid
)
152 bool in_list
= false;
153 nic_address_t nic_address
;
155 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_open()");
156 ethip_nic_t
*nic
= ethip_nic_new();
160 errno_t rc
= loc_service_get_name(sid
, &nic
->svc_name
);
162 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed getting service name.");
166 nic
->sess
= loc_service_connect(sid
, INTERFACE_DDF
, 0);
167 if (nic
->sess
== NULL
) {
168 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed connecting '%s'", nic
->svc_name
);
174 rc
= nic_callback_create(nic
->sess
, ethip_nic_cb_conn
, nic
);
176 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed creating callback connection "
177 "from '%s'", nic
->svc_name
);
181 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Opened NIC '%s'", nic
->svc_name
);
182 list_append(&nic
->link
, ðip_nic_list
);
185 rc
= ethip_iplink_init(nic
);
189 rc
= nic_get_address(nic
->sess
, &nic_address
);
191 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Error getting MAC address of NIC '%s'.",
196 eth_addr_decode(nic_address
.address
, &nic
->mac_addr
);
198 rc
= nic_set_state(nic
->sess
, NIC_STATE_ACTIVE
);
200 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Error activating NIC '%s'.",
205 rc
= nic_broadcast_set_mode(nic
->sess
, NIC_BROADCAST_ACCEPTED
);
207 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Error enabling "
208 "reception of broadcast frames on '%s'.", nic
->svc_name
);
212 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Initialized IP link service,");
218 list_remove(&nic
->link
);
220 if (nic
->sess
!= NULL
)
221 async_hangup(nic
->sess
);
223 ethip_nic_delete(nic
);
227 static void ethip_nic_cat_change_cb(void *arg
)
229 (void) ethip_nic_check_new();
232 static void ethip_nic_addr_changed(ethip_nic_t
*nic
, ipc_call_t
*call
)
236 eth_addr_str_t saddr
;
239 rc
= async_data_write_accept((void **) &addr
, false, 0, 0, 0, &size
);
241 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "data_write_accept() failed");
245 eth_addr_decode(addr
, &nic
->mac_addr
);
246 eth_addr_format(&nic
->mac_addr
, &saddr
);
248 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_addr_changed(): "
249 "new addr=%s", saddr
.str
);
251 rc
= iplink_ev_change_addr(&nic
->iplink
, &nic
->mac_addr
);
253 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "iplink_ev_change_addr() failed");
258 async_answer_0(call
, EOK
);
261 static void ethip_nic_received(ethip_nic_t
*nic
, ipc_call_t
*call
)
267 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_received() nic=%p", nic
);
269 rc
= async_data_write_accept(&data
, false, 0, 0, 0, &size
);
271 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "data_write_accept() failed");
275 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Ethernet PDU contents (%zu bytes)",
278 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "call ethip_received");
279 rc
= ethip_received(&nic
->iplink
, data
, size
);
280 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "free data");
283 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_received() done, rc=%s", str_error_name(rc
));
284 async_answer_0(call
, rc
);
287 static void ethip_nic_device_state(ethip_nic_t
*nic
, ipc_call_t
*call
)
289 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_device_state()");
290 async_answer_0(call
, ENOTSUP
);
293 static void ethip_nic_cb_conn(ipc_call_t
*icall
, void *arg
)
295 ethip_nic_t
*nic
= (ethip_nic_t
*)arg
;
297 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethnip_nic_cb_conn()");
301 async_get_call(&call
);
303 if (!ipc_get_imethod(&call
)) {
304 async_answer_0(&call
, EOK
);
308 switch (ipc_get_imethod(&call
)) {
309 case NIC_EV_ADDR_CHANGED
:
310 ethip_nic_addr_changed(nic
, &call
);
312 case NIC_EV_RECEIVED
:
313 ethip_nic_received(nic
, &call
);
315 case NIC_EV_DEVICE_STATE
:
316 ethip_nic_device_state(nic
, &call
);
319 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "unknown IPC method: %" PRIun
, ipc_get_imethod(&call
));
320 async_answer_0(&call
, ENOTSUP
);
325 errno_t
ethip_nic_discovery_start(void)
327 errno_t rc
= loc_register_cat_change_cb(ethip_nic_cat_change_cb
, NULL
);
329 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed registering callback for NIC "
330 "discovery: %s.", str_error(rc
));
334 return ethip_nic_check_new();
337 ethip_nic_t
*ethip_nic_find_by_iplink_sid(service_id_t iplink_sid
)
339 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_find_by_iplink_sid(%u)",
340 (unsigned) iplink_sid
);
342 list_foreach(ethip_nic_list
, link
, ethip_nic_t
, nic
) {
343 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_find_by_iplink_sid - element");
344 if (nic
->iplink_sid
== iplink_sid
) {
345 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_find_by_iplink_sid - found %p", nic
);
350 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_find_by_iplink_sid - not found");
354 errno_t
ethip_nic_send(ethip_nic_t
*nic
, void *data
, size_t size
)
357 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_send(size=%zu)", size
);
358 rc
= nic_send_frame(nic
->sess
, data
, size
);
359 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "nic_send_frame -> %s", str_error_name(rc
));
363 /** Setup accepted multicast addresses
365 * Currently the set of accepted multicast addresses is
366 * determined only based on IPv6 addresses.
369 static errno_t
ethip_nic_setup_multicast(ethip_nic_t
*nic
)
371 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_setup_multicast()");
373 /* Count the number of multicast addresses */
377 list_foreach(nic
->addr_list
, link
, ethip_link_addr_t
, laddr
) {
378 ip_ver_t ver
= inet_addr_get(&laddr
->addr
, NULL
, NULL
);
384 return nic_multicast_set_mode(nic
->sess
, NIC_MULTICAST_BLOCKED
,
387 nic_address_t
*mac_list
= calloc(count
, sizeof(nic_address_t
));
388 if (mac_list
== NULL
)
391 /* Create the multicast MAC list */
395 list_foreach(nic
->addr_list
, link
, ethip_link_addr_t
, laddr
) {
397 ip_ver_t ver
= inet_addr_get(&laddr
->addr
, NULL
, &v6
);
404 eth_addr_solicited_node(v6
, &mac
);
406 /* Avoid duplicate addresses in the list */
410 for (size_t j
= 0; j
< i
; j
++) {
411 eth_addr_t mac_entry
;
412 eth_addr_decode(mac_list
[j
].address
, &mac_entry
);
413 if (eth_addr_compare(&mac_entry
, &mac
)) {
420 eth_addr_encode(&mac
, mac_list
[i
].address
);
427 /* Setup the multicast MAC list */
429 errno_t rc
= nic_multicast_set_mode(nic
->sess
, NIC_MULTICAST_LIST
,
436 errno_t
ethip_nic_addr_add(ethip_nic_t
*nic
, inet_addr_t
*addr
)
438 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_addr_add()");
440 ethip_link_addr_t
*laddr
= ethip_nic_addr_new(addr
);
444 list_append(&laddr
->link
, &nic
->addr_list
);
446 return ethip_nic_setup_multicast(nic
);
449 errno_t
ethip_nic_addr_remove(ethip_nic_t
*nic
, inet_addr_t
*addr
)
451 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_addr_remove()");
453 ethip_link_addr_t
*laddr
= ethip_nic_addr_find(nic
, addr
);
457 list_remove(&laddr
->link
);
458 ethip_link_addr_delete(laddr
);
460 return ethip_nic_setup_multicast(nic
);
463 ethip_link_addr_t
*ethip_nic_addr_find(ethip_nic_t
*nic
,
466 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "ethip_nic_addr_find()");
468 list_foreach(nic
->addr_list
, link
, ethip_link_addr_t
, laddr
) {
469 if (inet_addr_compare(addr
, &laddr
->addr
))