1 /** @file
2  * @brief IPv4 autoconf related functions
3  */
4 
5 /*
6  * Copyright (c) 2017 Matthias Boesl
7  * Copyright (c) 2018 Intel Corporation
8  *
9  * SPDX-License-Identifier: Apache-2.0
10  */
11 
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(net_ipv4_autoconf, CONFIG_NET_IPV4_AUTO_LOG_LEVEL);
14 
15 #include "net_private.h"
16 #include <errno.h>
17 #include "../l2/ethernet/arp.h"
18 #include <zephyr/net/ipv4_autoconf.h>
19 #include <zephyr/net/net_pkt.h>
20 #include <zephyr/net/net_core.h>
21 #include <zephyr/net/net_if.h>
22 #include <zephyr/random/random.h>
23 
24 static struct net_mgmt_event_callback mgmt4_acd_cb;
25 
ipv4_autoconf_addr_set(struct net_if_ipv4_autoconf * ipv4auto)26 static inline void ipv4_autoconf_addr_set(struct net_if_ipv4_autoconf *ipv4auto)
27 {
28 	struct in_addr netmask = { { { 255, 255, 0, 0 } } };
29 
30 	if (ipv4auto->state == NET_IPV4_AUTOCONF_INIT) {
31 		ipv4auto->requested_ip.s4_addr[0] = 169U;
32 		ipv4auto->requested_ip.s4_addr[1] = 254U;
33 		ipv4auto->requested_ip.s4_addr[2] = sys_rand8_get() % 254;
34 		ipv4auto->requested_ip.s4_addr[3] = sys_rand8_get() % 254;
35 	}
36 
37 	NET_DBG("%s: Starting probe for 169.254.%d.%d",
38 		ipv4auto->state == NET_IPV4_AUTOCONF_INIT ? "Init" : "Renew",
39 		ipv4auto->requested_ip.s4_addr[2],
40 		ipv4auto->requested_ip.s4_addr[3]);
41 
42 	/* Add IPv4 address to the interface, this will trigger conflict detection. */
43 	if (!net_if_ipv4_addr_add(ipv4auto->iface, &ipv4auto->requested_ip,
44 				  NET_ADDR_AUTOCONF, 0)) {
45 		NET_DBG("Failed to add IPv4 addr to iface %p",
46 			ipv4auto->iface);
47 		return;
48 	}
49 
50 	net_if_ipv4_set_netmask_by_addr(ipv4auto->iface,
51 					&ipv4auto->requested_ip,
52 					&netmask);
53 
54 	ipv4auto->state = NET_IPV4_AUTOCONF_ASSIGNED;
55 }
56 
acd_event_handler(struct net_mgmt_event_callback * cb,uint32_t mgmt_event,struct net_if * iface)57 static void acd_event_handler(struct net_mgmt_event_callback *cb,
58 			      uint32_t mgmt_event, struct net_if *iface)
59 {
60 	struct net_if_config *cfg;
61 	struct in_addr *addr;
62 
63 	cfg = net_if_get_config(iface);
64 	if (!cfg) {
65 		return;
66 	}
67 
68 	if (cfg->ipv4auto.iface == NULL) {
69 		return;
70 	}
71 
72 	if (mgmt_event != NET_EVENT_IPV4_ACD_SUCCEED &&
73 	    mgmt_event != NET_EVENT_IPV4_ACD_FAILED &&
74 	    mgmt_event != NET_EVENT_IPV4_ACD_CONFLICT) {
75 		return;
76 	}
77 
78 	if (cb->info_length != sizeof(struct in_addr)) {
79 		return;
80 	}
81 
82 	addr = (struct in_addr *)cb->info;
83 
84 	if (!net_ipv4_addr_cmp(&cfg->ipv4auto.requested_ip, addr)) {
85 		return;
86 	}
87 
88 	switch (mgmt_event) {
89 	case NET_EVENT_IPV4_ACD_SUCCEED:
90 		cfg->ipv4auto.state = NET_IPV4_AUTOCONF_ASSIGNED;
91 		break;
92 	case NET_EVENT_IPV4_ACD_CONFLICT:
93 		net_ipv4_autoconf_reset(iface);
94 		__fallthrough;
95 	case NET_EVENT_IPV4_ACD_FAILED:
96 		/* Try new address. */
97 		cfg->ipv4auto.state = NET_IPV4_AUTOCONF_INIT;
98 		ipv4_autoconf_addr_set(&cfg->ipv4auto);
99 		break;
100 	default:
101 		break;
102 	}
103 }
104 
net_ipv4_autoconf_start(struct net_if * iface)105 void net_ipv4_autoconf_start(struct net_if *iface)
106 {
107 	/* Initialize interface and start probing */
108 	struct net_if_config *cfg;
109 
110 	if (!net_if_flag_is_set(iface, NET_IF_IPV4)) {
111 		return;
112 	}
113 
114 	cfg = net_if_get_config(iface);
115 	if (!cfg) {
116 		return;
117 	}
118 
119 	/* Remove the existing registration if found */
120 	if (cfg->ipv4auto.iface == iface) {
121 		net_ipv4_autoconf_reset(iface);
122 	}
123 
124 	cfg->ipv4auto.iface = iface;
125 
126 	NET_DBG("Starting IPv4 autoconf for iface %p", iface);
127 
128 	if (cfg->ipv4auto.state == NET_IPV4_AUTOCONF_ASSIGNED) {
129 		/* Try to reuse previously used address. */
130 		cfg->ipv4auto.state = NET_IPV4_AUTOCONF_RENEW;
131 	} else {
132 		cfg->ipv4auto.state = NET_IPV4_AUTOCONF_INIT;
133 	}
134 
135 	ipv4_autoconf_addr_set(&cfg->ipv4auto);
136 }
137 
net_ipv4_autoconf_reset(struct net_if * iface)138 void net_ipv4_autoconf_reset(struct net_if *iface)
139 {
140 	struct net_if_config *cfg;
141 
142 	cfg = net_if_get_config(iface);
143 	if (!cfg) {
144 		return;
145 	}
146 
147 	net_if_ipv4_addr_rm(iface, &cfg->ipv4auto.requested_ip);
148 
149 	NET_DBG("Autoconf reset for %p", iface);
150 }
151 
net_ipv4_autoconf_init(void)152 void net_ipv4_autoconf_init(void)
153 {
154 	net_mgmt_init_event_callback(&mgmt4_acd_cb, acd_event_handler,
155 				     NET_EVENT_IPV4_ACD_SUCCEED |
156 				     NET_EVENT_IPV4_ACD_FAILED |
157 				     NET_EVENT_IPV4_ACD_CONFLICT);
158 	net_mgmt_add_event_callback(&mgmt4_acd_cb);
159 }
160