|  | /* | 
|  | * Copyright 2025 NXP | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include "openthread/platform/infra_if.h" | 
|  | #include "icmpv6.h" | 
|  | #include "ipv6.h" | 
|  | #include "openthread_border_router.h" | 
|  | #include <common/code_utils.hpp> | 
|  | #include <platform-zephyr.h> | 
|  | #include <route.h> | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/net/ethernet.h> | 
|  | #include <zephyr/net/icmp.h> | 
|  | #include <zephyr/net/mld.h> | 
|  | #include <zephyr/net/net_if.h> | 
|  | #include <zephyr/net/net_ip.h> | 
|  | #include <zephyr/net/net_pkt.h> | 
|  | #include <zephyr/net/openthread.h> | 
|  | #include <zephyr/net/socket.h> | 
|  | #include <zephyr/net/icmp.h> | 
|  | #include <icmpv6.h> | 
|  |  | 
|  | static struct otInstance *ot_instance; | 
|  | static struct net_if *ail_iface_ptr; | 
|  | static uint32_t ail_iface_index; | 
|  | static struct net_icmp_ctx ra_ctx; | 
|  | static struct net_icmp_ctx rs_ctx; | 
|  | static struct net_icmp_ctx na_ctx; | 
|  |  | 
|  | static void infra_if_handle_backbone_icmp6(struct otbr_msg_ctx *msg_ctx_ptr); | 
|  | static void handle_ra_from_ot(const uint8_t *buffer, uint16_t buffer_length); | 
|  |  | 
|  | otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, const otIp6Address *aDestAddress, | 
|  | const uint8_t *aBuffer, uint16_t aBufferLength) | 
|  | { | 
|  | otError error = OT_ERROR_NONE; | 
|  | struct net_pkt *pkt = NULL; | 
|  | struct in6_addr dst = {0}; | 
|  | const struct in6_addr *src; | 
|  |  | 
|  | if (aBuffer[0] == NET_ICMPV6_RA) { | 
|  | handle_ra_from_ot(aBuffer, aBufferLength); | 
|  | } | 
|  |  | 
|  | memcpy(&dst, aDestAddress, sizeof(otIp6Address)); | 
|  |  | 
|  | src = net_if_ipv6_select_src_addr(ail_iface_ptr, &dst); | 
|  | VerifyOrExit(!net_ipv6_is_addr_unspecified(src), error = OT_ERROR_FAILED); | 
|  |  | 
|  | pkt = net_pkt_alloc_with_buffer(ail_iface_ptr, aBufferLength, AF_INET6, IPPROTO_ICMPV6, | 
|  | K_MSEC(100)); | 
|  | VerifyOrExit(pkt, error = OT_ERROR_FAILED); | 
|  |  | 
|  | net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_ND_HOP_LIMIT); | 
|  |  | 
|  | VerifyOrExit(net_ipv6_create(pkt, src, &dst) == 0, error = OT_ERROR_FAILED); | 
|  | VerifyOrExit(net_pkt_write(pkt, aBuffer, aBufferLength) == 0, error = OT_ERROR_FAILED); | 
|  | net_pkt_cursor_init(pkt); | 
|  |  | 
|  | VerifyOrExit(net_ipv6_finalize(pkt, IPPROTO_ICMPV6) == 0, error = OT_ERROR_FAILED); | 
|  | VerifyOrExit(net_send_data(pkt) == 0, error = OT_ERROR_FAILED); | 
|  |  | 
|  | exit: | 
|  | if (error == OT_ERROR_FAILED) { | 
|  | if (pkt != NULL) { | 
|  | net_pkt_unref(pkt); | 
|  | pkt = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | return error; | 
|  | } | 
|  |  | 
|  | otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex) | 
|  | { | 
|  | OT_UNUSED_VARIABLE(aInfraIfIndex); | 
|  |  | 
|  | return OT_ERROR_NOT_IMPLEMENTED; | 
|  | } | 
|  |  | 
|  | bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddress) | 
|  | { | 
|  | struct net_if_addr *ifaddr = NULL; | 
|  | struct in6_addr addr = {0}; | 
|  |  | 
|  | memcpy(addr.s6_addr, aAddress->mFields.m8, sizeof(otIp6Address)); | 
|  |  | 
|  | STRUCT_SECTION_FOREACH(net_if, tmp) { | 
|  | if (net_if_get_by_iface(tmp) != aInfraIfIndex) { | 
|  | continue; | 
|  | } | 
|  | ifaddr = net_if_ipv6_addr_lookup_by_iface(tmp, &addr); | 
|  | if (ifaddr != NULL) { | 
|  | return true; | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | otError otPlatGetInfraIfLinkLayerAddress(otInstance *aInstance, uint32_t aIfIndex, | 
|  | otPlatInfraIfLinkLayerAddress *aInfraIfLinkLayerAddress) | 
|  | { | 
|  | OT_UNUSED_VARIABLE(aInstance); | 
|  | struct net_if *iface = net_if_get_by_index(aIfIndex); | 
|  | struct net_linkaddr *link_addr = net_if_get_link_addr(iface); | 
|  |  | 
|  | aInfraIfLinkLayerAddress->mLength = link_addr->len; | 
|  | memcpy(aInfraIfLinkLayerAddress->mAddress, link_addr->addr, link_addr->len); | 
|  |  | 
|  | return OT_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | otError infra_if_init(otInstance *instance, struct net_if *ail_iface) | 
|  | { | 
|  | otError error = OT_ERROR_NONE; | 
|  | struct in6_addr addr = {0}; | 
|  |  | 
|  | ot_instance = instance; | 
|  | ail_iface_ptr = ail_iface; | 
|  | ail_iface_index = (uint32_t)net_if_get_by_iface(ail_iface_ptr); | 
|  | net_ipv6_addr_create_ll_allrouters_mcast(&addr); | 
|  | VerifyOrExit(net_ipv6_mld_join(ail_iface, &addr) == 0, error = OT_ERROR_FAILED); | 
|  |  | 
|  | exit: | 
|  | return error; | 
|  | } | 
|  |  | 
|  | static void handle_ra_from_ot(const uint8_t *buffer, uint16_t buffer_length) | 
|  | { | 
|  | struct net_if *ot_iface = net_if_get_first_by_type(&NET_L2_GET_NAME(OPENTHREAD)); | 
|  | struct net_if_ipv6_prefix *prefix_added = NULL; | 
|  | struct net_route_entry *route_added = NULL; | 
|  | struct in6_addr rio_prefix = {0}; | 
|  | struct net_if_addr *ifaddr = NULL; | 
|  | struct in6_addr addr_to_add_from_pio = {0}; | 
|  | struct in6_addr nexthop = {0}; | 
|  | uint8_t i = sizeof(struct net_icmp_hdr) + sizeof(struct net_icmpv6_ra_hdr); | 
|  |  | 
|  | while (i + sizeof(struct net_icmpv6_nd_opt_hdr) <= buffer_length) { | 
|  | const struct net_icmpv6_nd_opt_hdr *opt_hdr = | 
|  | (const struct net_icmpv6_nd_opt_hdr *)&buffer[i]; | 
|  |  | 
|  | i += sizeof(struct net_icmpv6_nd_opt_hdr); | 
|  | switch (opt_hdr->type) { | 
|  | case NET_ICMPV6_ND_OPT_PREFIX_INFO: | 
|  | const struct net_icmpv6_nd_opt_prefix_info *pio = | 
|  | (const struct net_icmpv6_nd_opt_prefix_info *)&buffer[i]; | 
|  | prefix_added = net_if_ipv6_prefix_add(ail_iface_ptr, | 
|  | (struct in6_addr *)pio->prefix, | 
|  | pio->prefix_len, pio->valid_lifetime); | 
|  | i += sizeof(struct net_icmpv6_nd_opt_prefix_info); | 
|  | net_ipv6_addr_generate_iid( | 
|  | ail_iface_ptr, (struct in6_addr *)pio->prefix, | 
|  | COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE, | 
|  | ((uint8_t *)&ail_iface_ptr->config.ip.ipv6->network_counter), | 
|  | (NULL)), COND_CODE_1(CONFIG_NET_IPV6_IID_STABLE, | 
|  | (sizeof(ail_iface_ptr->config.ip.ipv6->network_counter)), | 
|  | (0U)), 0U, &addr_to_add_from_pio, | 
|  | net_if_get_link_addr(ail_iface_ptr)); | 
|  | ifaddr = net_if_ipv6_addr_lookup(&addr_to_add_from_pio, NULL); | 
|  | if (ifaddr != NULL) { | 
|  | net_if_addr_set_lf(ifaddr, true); | 
|  | } | 
|  | net_if_ipv6_addr_add(ail_iface_ptr, &addr_to_add_from_pio, | 
|  | NET_ADDR_AUTOCONF, pio->valid_lifetime); | 
|  | break; | 
|  | case NET_ICMPV6_ND_OPT_ROUTE: | 
|  | uint8_t prefix_field_len = (opt_hdr->len - 1) * 8; | 
|  | const otIp6Address *br_omr_addr = get_ot_slaac_address(ot_instance); | 
|  |  | 
|  | const struct net_icmpv6_nd_opt_route_info *rio = | 
|  | (const struct net_icmpv6_nd_opt_route_info *)&buffer[i]; | 
|  |  | 
|  | i += sizeof(struct net_icmpv6_nd_opt_route_info); | 
|  | memcpy(&rio_prefix.s6_addr, &buffer[i], prefix_field_len); | 
|  | if (!otIp6IsAddressUnspecified(br_omr_addr)) { | 
|  | memcpy(&nexthop.s6_addr, br_omr_addr->mFields.m8, | 
|  | sizeof(br_omr_addr->mFields.m8)); | 
|  | net_ipv6_nbr_add(ot_iface, &nexthop, net_if_get_link_addr(ot_iface), | 
|  | false, NET_IPV6_NBR_STATE_STALE); | 
|  | route_added = net_route_add(ot_iface, &rio_prefix, rio->prefix_len, | 
|  | &nexthop, rio->route_lifetime, | 
|  | rio->flags.prf); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static int handle_icmp6_input(struct net_icmp_ctx *ctx, struct net_pkt *pkt, | 
|  | struct net_icmp_ip_hdr *hdr, | 
|  | struct net_icmp_hdr *icmp_hdr, void *user_data) | 
|  | { | 
|  | uint16_t length = net_pkt_get_len(pkt); | 
|  | struct otbr_msg_ctx *req = NULL; | 
|  | otError error = OT_ERROR_NONE; | 
|  |  | 
|  | VerifyOrExit(openthread_border_router_allocate_message((void **)&req) == OT_ERROR_NONE, | 
|  | error = OT_ERROR_FAILED); | 
|  |  | 
|  | if (net_buf_linearize(req->buffer, sizeof(req->buffer), | 
|  | pkt->buffer, 0, length) == length) { | 
|  | req->length = length; | 
|  | memcpy(&req->addr, hdr->ipv6->src, sizeof(req->addr)); | 
|  | req->cb = infra_if_handle_backbone_icmp6; | 
|  |  | 
|  | openthread_border_router_post_message(req); | 
|  | } else { | 
|  | openthread_border_router_deallocate_message((void *)req); | 
|  | ExitNow(error = OT_ERROR_FAILED); | 
|  | } | 
|  |  | 
|  | exit: | 
|  | if (error == OT_ERROR_NONE) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static void infra_if_handle_backbone_icmp6(struct otbr_msg_ctx *msg_ctx_ptr) | 
|  | { | 
|  | otPlatInfraIfRecvIcmp6Nd( | 
|  | ot_instance, ail_iface_index, &msg_ctx_ptr->addr, | 
|  | (const uint8_t *)&msg_ctx_ptr->buffer[sizeof(struct net_ipv6_hdr)], | 
|  | msg_ctx_ptr->length); | 
|  | } | 
|  |  | 
|  | otError infra_if_start_icmp6_listener(void) | 
|  | { | 
|  | otError error = OT_ERROR_NONE; | 
|  |  | 
|  | VerifyOrExit(net_icmp_init_ctx(&ra_ctx, NET_ICMPV6_RA, 0, handle_icmp6_input) == 0, | 
|  | error = OT_ERROR_FAILED); | 
|  | VerifyOrExit(net_icmp_init_ctx(&rs_ctx, NET_ICMPV6_RS, 0, handle_icmp6_input) == 0, | 
|  | error = OT_ERROR_FAILED); | 
|  | VerifyOrExit(net_icmp_init_ctx(&na_ctx, NET_ICMPV6_NA, 0, handle_icmp6_input) == 0, | 
|  | error = OT_ERROR_FAILED); | 
|  |  | 
|  | exit: | 
|  | return error; | 
|  | } | 
|  |  | 
|  | void infra_if_stop_icmp6_listener(void) | 
|  | { | 
|  | (void)net_icmp_cleanup_ctx(&ra_ctx); | 
|  | (void)net_icmp_cleanup_ctx(&rs_ctx); | 
|  | (void)net_icmp_cleanup_ctx(&na_ctx); | 
|  | } |