| /** @file |
| * @brief IPv4 autoconf related functions |
| */ |
| |
| /* |
| * Copyright (c) 2017 Matthias Boesl |
| * Copyright (c) 2018 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(net_ipv4_autoconf, CONFIG_NET_IPV4_AUTO_LOG_LEVEL); |
| |
| #include "net_private.h" |
| #include <errno.h> |
| #include "../l2/ethernet/arp.h" |
| #include <zephyr/net/ipv4_autoconf.h> |
| #include <zephyr/net/net_pkt.h> |
| #include <zephyr/net/net_core.h> |
| #include <zephyr/net/net_if.h> |
| #include <zephyr/random/random.h> |
| |
| static struct net_mgmt_event_callback mgmt4_acd_cb; |
| |
| static inline void ipv4_autoconf_addr_set(struct net_if_ipv4_autoconf *ipv4auto) |
| { |
| struct in_addr netmask = { { { 255, 255, 0, 0 } } }; |
| |
| if (ipv4auto->state == NET_IPV4_AUTOCONF_INIT) { |
| ipv4auto->requested_ip.s4_addr[0] = 169U; |
| ipv4auto->requested_ip.s4_addr[1] = 254U; |
| ipv4auto->requested_ip.s4_addr[2] = sys_rand8_get() % 254; |
| ipv4auto->requested_ip.s4_addr[3] = sys_rand8_get() % 254; |
| } |
| |
| NET_DBG("%s: Starting probe for 169.254.%d.%d", |
| ipv4auto->state == NET_IPV4_AUTOCONF_INIT ? "Init" : "Renew", |
| ipv4auto->requested_ip.s4_addr[2], |
| ipv4auto->requested_ip.s4_addr[3]); |
| |
| /* Add IPv4 address to the interface, this will trigger conflict detection. */ |
| if (!net_if_ipv4_addr_add(ipv4auto->iface, &ipv4auto->requested_ip, |
| NET_ADDR_AUTOCONF, 0)) { |
| NET_DBG("Failed to add IPv4 addr to iface %p", |
| ipv4auto->iface); |
| return; |
| } |
| |
| net_if_ipv4_set_netmask_by_addr(ipv4auto->iface, |
| &ipv4auto->requested_ip, |
| &netmask); |
| |
| ipv4auto->state = NET_IPV4_AUTOCONF_ASSIGNED; |
| } |
| |
| static void acd_event_handler(struct net_mgmt_event_callback *cb, |
| uint32_t mgmt_event, struct net_if *iface) |
| { |
| struct net_if_config *cfg; |
| struct in_addr *addr; |
| |
| cfg = net_if_get_config(iface); |
| if (!cfg) { |
| return; |
| } |
| |
| if (cfg->ipv4auto.iface == NULL) { |
| return; |
| } |
| |
| if (mgmt_event != NET_EVENT_IPV4_ACD_SUCCEED && |
| mgmt_event != NET_EVENT_IPV4_ACD_FAILED && |
| mgmt_event != NET_EVENT_IPV4_ACD_CONFLICT) { |
| return; |
| } |
| |
| if (cb->info_length != sizeof(struct in_addr)) { |
| return; |
| } |
| |
| addr = (struct in_addr *)cb->info; |
| |
| if (!net_ipv4_addr_cmp(&cfg->ipv4auto.requested_ip, addr)) { |
| return; |
| } |
| |
| switch (mgmt_event) { |
| case NET_EVENT_IPV4_ACD_SUCCEED: |
| cfg->ipv4auto.state = NET_IPV4_AUTOCONF_ASSIGNED; |
| break; |
| case NET_EVENT_IPV4_ACD_CONFLICT: |
| net_ipv4_autoconf_reset(iface); |
| __fallthrough; |
| case NET_EVENT_IPV4_ACD_FAILED: |
| /* Try new address. */ |
| cfg->ipv4auto.state = NET_IPV4_AUTOCONF_INIT; |
| ipv4_autoconf_addr_set(&cfg->ipv4auto); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void net_ipv4_autoconf_start(struct net_if *iface) |
| { |
| /* Initialize interface and start probing */ |
| struct net_if_config *cfg; |
| |
| if (!net_if_flag_is_set(iface, NET_IF_IPV4)) { |
| return; |
| } |
| |
| cfg = net_if_get_config(iface); |
| if (!cfg) { |
| return; |
| } |
| |
| /* Remove the existing registration if found */ |
| if (cfg->ipv4auto.iface == iface) { |
| net_ipv4_autoconf_reset(iface); |
| } |
| |
| cfg->ipv4auto.iface = iface; |
| |
| NET_DBG("Starting IPv4 autoconf for iface %p", iface); |
| |
| if (cfg->ipv4auto.state == NET_IPV4_AUTOCONF_ASSIGNED) { |
| /* Try to reuse previously used address. */ |
| cfg->ipv4auto.state = NET_IPV4_AUTOCONF_RENEW; |
| } else { |
| cfg->ipv4auto.state = NET_IPV4_AUTOCONF_INIT; |
| } |
| |
| ipv4_autoconf_addr_set(&cfg->ipv4auto); |
| } |
| |
| void net_ipv4_autoconf_reset(struct net_if *iface) |
| { |
| struct net_if_config *cfg; |
| |
| cfg = net_if_get_config(iface); |
| if (!cfg) { |
| return; |
| } |
| |
| net_if_ipv4_addr_rm(iface, &cfg->ipv4auto.requested_ip); |
| |
| NET_DBG("Autoconf reset for %p", iface); |
| } |
| |
| void net_ipv4_autoconf_init(void) |
| { |
| net_mgmt_init_event_callback(&mgmt4_acd_cb, acd_event_handler, |
| NET_EVENT_IPV4_ACD_SUCCEED | |
| NET_EVENT_IPV4_ACD_FAILED | |
| NET_EVENT_IPV4_ACD_CONFLICT); |
| net_mgmt_add_event_callback(&mgmt4_acd_cb); |
| } |