ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
ip_address.h
Go to the documentation of this file.
1#pragma once
3#ifdef USE_NETWORK
4#include <cstdint>
5#include <string>
6#include <cstdio>
7#include <array>
8#include <cstring>
10#include "esphome/core/macros.h"
11
12#if defined(USE_ESP32) || defined(USE_LIBRETINY) || USE_ARDUINO_VERSION_CODE > VERSION_CODE(3, 0, 0)
13#include <lwip/ip_addr.h>
14#endif
15#if USE_ARDUINO
16#include <Arduino.h>
17#include <IPAddress.h>
18#endif /* USE_ARDUINO */
19
20#ifdef USE_HOST
21#include <arpa/inet.h>
22#if USE_NETWORK_IPV6
23using ip4_addr_t = struct in_addr;
24using ip6_addr_t = struct in6_addr;
25struct ip_addr_t { // NOLINT(readability-identifier-naming) mirrors the lwIP name the other platforms use
26 union {
27 struct in6_addr ip6;
28 struct in_addr ip4;
30 uint8_t type;
31};
32enum : uint8_t { IPADDR_TYPE_V4 = 0, IPADDR_TYPE_V6 = 6 };
33static inline int ipaddr_aton(const char *cp, ip_addr_t *addr) {
34 if (strchr(cp, ':') != nullptr) {
35 if (inet_pton(AF_INET6, cp, &addr->u_addr.ip6) != 1) {
36 return 0;
37 }
38 addr->type = IPADDR_TYPE_V6;
39 return 1;
40 }
41 if (inet_aton(cp, &addr->u_addr.ip4) != 1) {
42 return 0;
43 }
44 addr->type = IPADDR_TYPE_V4;
45 return 1;
46}
47#else
48using ip_addr_t = in_addr;
49using ip4_addr_t = in_addr;
50#define ipaddr_aton(x, y) inet_aton((x), (y))
51#endif // USE_NETWORK_IPV6
52#endif // USE_HOST
53
54#ifdef USE_ZEPHYR
55#include <zephyr/net/net_ip.h>
56#include <zephyr/net/socket.h>
57#include <zephyr/posix/arpa/inet.h>
58#ifndef CONFIG_NET_IPV6
59#error "Zephyr builds need CONFIG_NET_IPV6"
60#endif
61using ip_addr_t = struct in6_addr;
62static inline int ipaddr_aton(const char *cp, ip_addr_t *addr) {
63#if defined(CONFIG_NET_IPV4)
64 // Dual-stack: a plain IPv4 literal must still parse -- store it as a v4-mapped
65 // IPv6 address (::ffff:a.b.c.d), matching the 4-octet constructor below.
66 struct in_addr addr4;
67 if (inet_pton(AF_INET, cp, &addr4) == 1) {
68 net_ipv6_addr_create_v4_mapped(&addr4, addr);
69 return 1;
70 }
71#endif /* CONFIG_NET_IPV4 */
72 return inet_pton(AF_INET6, cp, addr) == 1 ? 1 : 0;
73}
74#endif /* USE_ZEPHYR */
75
76#if USE_ESP32_FRAMEWORK_ARDUINO
77#define arduino_ns Arduino_h
78#elif USE_LIBRETINY
79#define arduino_ns arduino
80#elif USE_ARDUINO
81#define arduino_ns
82#endif /* USE_ESP32_FRAMEWORK_ARDUINO */
83
84#ifdef USE_ESP32
85#include <esp_netif.h>
86#endif /* USE_ESP32 */
87
89
91static constexpr size_t IP_ADDRESS_BUFFER_SIZE =
92#if defined(USE_ZEPHYR)
93 // Mainline Zephyr's inet_ntop() (net_addr_ntop() in subsys/net/ip/utils.c) never checks
94 // its size argument, so an undersized buffer overflows instead of failing cleanly. Size
95 // for the worst case: an IPv4-mapped IPv6 address needs up to INET6_ADDRSTRLEN (46).
96 46;
97#else
98 40;
99#endif
100
102inline void lowercase_ip_str(char *buf) {
103 for (char *p = buf; *p; ++p) {
104 if (*p >= 'A' && *p <= 'F')
105 *p += 32;
106 }
107}
108
109struct IPAddress {
110 public:
111#if defined(USE_ZEPHYR)
112 // With CONFIG_NET_IPV4 (dual stack), IPv4 addresses are stored v4-mapped (::ffff:a.b.c.d).
113 IPAddress() { memset(&ip_addr_, 0, sizeof(ip_addr_)); }
114#if defined(CONFIG_NET_IPV4)
115 IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) {
116 struct in_addr addr4{
117 .s_addr = htonl(((uint32_t) first << 24) | ((uint32_t) second << 16) | ((uint32_t) third << 8) | fourth)};
118 net_ipv6_addr_create_v4_mapped(&addr4, &ip_addr_);
119 }
120#endif /* CONFIG_NET_IPV4 */
121 IPAddress(const std::string &in_address) : ip_addr_{} { ipaddr_aton(in_address.c_str(), &ip_addr_); }
122 IPAddress(const struct in6_addr *other_ip) { ip_addr_ = *other_ip; }
123 IPAddress(const struct sockaddr_in6 *addr) { ip_addr_ = addr->sin6_addr; }
124
125 operator struct in6_addr() const { return ip_addr_; }
126
127 bool is_set() const {
128#if defined(CONFIG_NET_IPV4)
129 if (net_ipv6_addr_is_v4_mapped(&ip_addr_))
130 return ip_addr_.s6_addr32[3] != 0;
131#endif /* CONFIG_NET_IPV4 */
132 return !net_ipv6_is_addr_unspecified(&ip_addr_);
133 }
134#if defined(CONFIG_NET_IPV4)
135 // V4-mapped (::ffff:a.b.c.d) addresses represent IPv4 traffic carried over the AF_INET6 socket
136 // Zephyr always uses when CONFIG_NET_IPV6 is on; anything else is real IPv6.
137 bool is_ip4() const { return this->is_set() && net_ipv6_addr_is_v4_mapped(&ip_addr_); }
138 bool is_ip6() const { return this->is_set() && !net_ipv6_addr_is_v4_mapped(&ip_addr_); }
139#else
140 bool is_ip4() const { return false; }
141 bool is_ip6() const { return this->is_set(); }
142#endif /* CONFIG_NET_IPV4 */
143 bool is_multicast() const {
144#if defined(CONFIG_NET_IPV4)
145 if (net_ipv6_addr_is_v4_mapped(&ip_addr_)) {
146 struct in_addr addr4{};
147 addr4.s_addr = ip_addr_.s6_addr32[3];
148 return net_ipv4_is_addr_mcast(&addr4);
149 }
150#endif /* CONFIG_NET_IPV4 */
151 return net_ipv6_is_addr_mcast(&ip_addr_);
152 }
153 char *str_to(char *buf) const {
154#if defined(CONFIG_NET_IPV4)
155 if (this->is_ip4()) {
156 struct in_addr addr4{};
157 addr4.s_addr = ip_addr_.s6_addr32[3];
158 if (inet_ntop(AF_INET, &addr4, buf, IP_ADDRESS_BUFFER_SIZE) == nullptr)
159 buf[0] = '\0';
160 return buf;
161 }
162#endif /* CONFIG_NET_IPV4 */
163 if (inet_ntop(AF_INET6, &ip_addr_, buf, IP_ADDRESS_BUFFER_SIZE) == nullptr)
164 buf[0] = '\0';
165 return buf;
166 }
167 bool operator==(const IPAddress &other) const { return net_ipv6_addr_cmp(&ip_addr_, &other.ip_addr_); }
168 bool operator!=(const IPAddress &other) const { return !net_ipv6_addr_cmp(&ip_addr_, &other.ip_addr_); }
169
170#elif defined(USE_HOST)
171#if USE_NETWORK_IPV6
172 IPAddress() { memset(&this->ip_addr_, 0, sizeof(this->ip_addr_)); }
173 IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) {
174 memset(&this->ip_addr_, 0, sizeof(this->ip_addr_));
175 this->ip_addr_.u_addr.ip4.s_addr =
176 htonl(((uint32_t) first << 24) | ((uint32_t) second << 16) | ((uint32_t) third << 8) | fourth);
178 }
179 IPAddress(const char *in_address) {
180 memset(&this->ip_addr_, 0, sizeof(this->ip_addr_));
181 ipaddr_aton(in_address, &this->ip_addr_);
182 }
183 IPAddress(const std::string &in_address) : IPAddress(in_address.c_str()) {}
184 IPAddress(const ip_addr_t *other_ip) { memcpy(&this->ip_addr_, other_ip, sizeof(ip_addr_t)); }
186 memset(&this->ip_addr_, 0, sizeof(this->ip_addr_));
187 this->ip_addr_.u_addr.ip4 = *other_ip;
189 }
191 memset(&this->ip_addr_, 0, sizeof(this->ip_addr_));
192 this->ip_addr_.u_addr.ip6 = *other_ip;
194 }
195 operator ip_addr_t() const { return this->ip_addr_; }
196 bool is_set() const {
197 if (this->ip_addr_.type == IPADDR_TYPE_V6) {
198 static constexpr uint8_t ZERO[sizeof(struct in6_addr)] = {};
199 return memcmp(this->ip_addr_.u_addr.ip6.s6_addr, ZERO, sizeof(ZERO)) != 0;
200 }
201 return this->ip_addr_.u_addr.ip4.s_addr != 0;
202 }
203 bool is_ip4() const { return this->ip_addr_.type == IPADDR_TYPE_V4; }
204 bool is_ip6() const { return this->ip_addr_.type == IPADDR_TYPE_V6; }
205 bool is_multicast() const {
206 if (this->ip_addr_.type == IPADDR_TYPE_V6) {
207 return this->ip_addr_.u_addr.ip6.s6_addr[0] == 0xff;
208 }
209 return (ntohl(this->ip_addr_.u_addr.ip4.s_addr) & 0xF0000000UL) == 0xE0000000UL;
210 }
211 char *str_to(char *buf) const {
212 if (this->ip_addr_.type == IPADDR_TYPE_V6) {
213 inet_ntop(AF_INET6, &this->ip_addr_.u_addr.ip6, buf, IP_ADDRESS_BUFFER_SIZE);
214 } else {
215 inet_ntop(AF_INET, &this->ip_addr_.u_addr.ip4, buf, IP_ADDRESS_BUFFER_SIZE);
216 }
217 lowercase_ip_str(buf);
218 return buf;
219 }
220 bool operator==(const IPAddress &other) const {
221 if (this->ip_addr_.type != other.ip_addr_.type) {
222 return false;
223 }
224 if (this->ip_addr_.type == IPADDR_TYPE_V6) {
225 return memcmp(&this->ip_addr_.u_addr.ip6, &other.ip_addr_.u_addr.ip6, sizeof(struct in6_addr)) == 0;
226 }
227 return this->ip_addr_.u_addr.ip4.s_addr == other.ip_addr_.u_addr.ip4.s_addr;
228 }
229 bool operator!=(const IPAddress &other) const { return !(*this == other); }
230 IPAddress &operator+=(uint8_t increase) {
231 if (this->ip_addr_.type == IPADDR_TYPE_V4) {
232 (((uint8_t *) (&this->ip_addr_.u_addr.ip4))[3]) += increase;
233 }
234 return *this;
235 }
236#else
237 IPAddress() { ip_addr_.s_addr = 0; }
238 IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) {
239 this->ip_addr_.s_addr = htonl((first << 24) | (second << 16) | (third << 8) | fourth);
240 }
241 IPAddress(const std::string &in_address) { inet_aton(in_address.c_str(), &ip_addr_); }
242 IPAddress(const ip_addr_t *other_ip) { ip_addr_ = *other_ip; }
243 bool is_ip4() const { return true; }
244 bool is_ip6() const { return false; }
246 char *str_to(char *buf) const {
247 inet_ntop(AF_INET, &ip_addr_, buf, IP_ADDRESS_BUFFER_SIZE);
248 return buf; // IPv4 only, no hex letters to lowercase
249 }
250#endif // USE_NETWORK_IPV6
251#else // LWIP
252 IPAddress() { ip_addr_set_zero(&ip_addr_); }
253#if LWIP_IPV4
254 IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) {
255 IP_ADDR4(&ip_addr_, first, second, third, fourth);
256 }
257#endif /* LWIP_IPV4 */
258 IPAddress(const ip_addr_t *other_ip) { ip_addr_copy(ip_addr_, *other_ip); }
259 IPAddress(const char *in_address) { ipaddr_aton(in_address, &ip_addr_); }
260 IPAddress(const std::string &in_address) { ipaddr_aton(in_address.c_str(), &ip_addr_); }
261#if LWIP_IPV4
263 memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip4_addr_t));
264#if LWIP_IPV6
266#endif /* LWIP_IPV6 */
267 }
268#endif /* LWIP_IPV4 */
269#if USE_ARDUINO
270 IPAddress(const arduino_ns::IPAddress &other_ip) { ip_addr_set_ip4_u32(&ip_addr_, other_ip); }
271#endif /* USE_ARDUINO */
272#if LWIP_IPV6
274 memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip6_addr_t));
275 IP_SET_TYPE_VAL(ip_addr_, IPADDR_TYPE_V6);
276 }
277#endif /* LWIP_IPV6 */
278
279#ifdef USE_ESP32
280#if LWIP_IPV6
281 IPAddress(esp_ip6_addr_t *other_ip) {
282 memcpy((void *) ip_2_ip6(&ip_addr_), (void *) other_ip, sizeof(esp_ip6_addr_t));
283 IP_SET_TYPE_VAL(ip_addr_, IPADDR_TYPE_V6);
284 }
285#endif /* LWIP_IPV6 */
286#if LWIP_IPV4
287 IPAddress(esp_ip4_addr_t *other_ip) {
288 memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(esp_ip4_addr_t));
289#if LWIP_IPV6
291#endif /* LWIP_IPV6 */
292 }
293#endif /* LWIP_IPV4 */
294 IPAddress(esp_ip_addr_t *other_ip) {
295#if LWIP_IPV6
296 memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip_addr_));
297#elif LWIP_IPV4
298 memcpy((void *) &ip_addr_, (void *) &other_ip->u_addr.ip4, sizeof(ip_addr_));
299#endif /* LWIP_IPV6 */
300 }
301 operator esp_ip_addr_t() const {
302 esp_ip_addr_t tmp{};
303#if LWIP_IPV6
304 memcpy((void *) &tmp, (void *) &ip_addr_, sizeof(ip_addr_));
305#if !LWIP_IPV4
306 tmp.type = IPADDR_TYPE_V6;
307#endif /* !LWIP_IPV4 */
308#elif LWIP_IPV4
309 memcpy((void *) &tmp.u_addr.ip4, (void *) &ip_addr_, sizeof(ip_addr_));
310 tmp.type = IPADDR_TYPE_V4;
311#else
312#error "Neither LWIP_IPV4 nor LWIP_IPV6 is enabled"
313#endif /* LWIP_IPV6 */
314 return tmp;
315 }
316#if LWIP_IPV4
317 operator esp_ip4_addr_t() const {
318 esp_ip4_addr_t tmp;
319#if LWIP_IPV6
320 memcpy((void *) &tmp, (void *) &ip_addr_.u_addr.ip4, sizeof(esp_ip4_addr_t));
321#else
322 memcpy((void *) &tmp, (void *) &ip_addr_, sizeof(ip_addr_));
323#endif /* LWIP_IPV6 */
324 return tmp;
325 }
326#endif /* LWIP_IPV4 */
327#endif /* USE_ESP32 */
328
329 operator ip_addr_t() const { return ip_addr_; }
330#if LWIP_IPV6 && LWIP_IPV4
331 operator ip4_addr_t() const { return *ip_2_ip4(&ip_addr_); }
332#endif /* LWIP_IPV6 && LWIP_IPV4 */
333
334#if USE_ARDUINO
335 operator arduino_ns::IPAddress() const { return ip_addr_get_ip4_u32(&ip_addr_); }
336#endif /* USE_ARDUINO */
337
338 bool is_set() const { return !ip_addr_isany(&ip_addr_); } // NOLINT(readability-simplify-boolean-expr)
339 bool is_ip4() const { return IP_IS_V4(&ip_addr_); }
340 bool is_ip6() const { return IP_IS_V6(&ip_addr_); }
341 bool is_multicast() const { return ip_addr_ismulticast(&ip_addr_); }
344 char *str_to(char *buf) const {
345 ipaddr_ntoa_r(&ip_addr_, buf, IP_ADDRESS_BUFFER_SIZE);
346 lowercase_ip_str(buf);
347 return buf;
348 }
349 bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
350 bool operator!=(const IPAddress &other) const { return !ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
351#if LWIP_IPV4
352 IPAddress &operator+=(uint8_t increase) {
353 if (IP_IS_V4(&ip_addr_)) {
354#if LWIP_IPV6
355 (((u8_t *) (&ip_addr_.u_addr.ip4))[3]) += increase;
356#else
357 (((u8_t *) (&ip_addr_.addr))[3]) += increase;
358#endif /* LWIP_IPV6 */
359 }
360 return *this;
361 }
362#endif /* LWIP_IPV4 */
363#endif // LWIP
364
365 protected:
367};
368
369using IPAddresses = std::array<IPAddress, 5>;
370
371} // namespace esphome::network
372#endif /* NETWORK */
uint8_t second
@ IPADDR_TYPE_V6
Definition ip_address.h:32
@ IPADDR_TYPE_V4
Definition ip_address.h:32
struct in6_addr ip6_addr_t
Definition ip_address.h:24
in_addr ip_addr_t
Definition ip_address.h:48
struct in_addr ip4_addr_t
Definition ip_address.h:23
void lowercase_ip_str(char *buf)
Lowercase hex digits in IP address string (A-F -> a-f for IPv6 per RFC 5952)
Definition ip_address.h:102
std::array< IPAddress, 5 > IPAddresses
Definition ip_address.h:369
static void uint32_t
IPAddress(const struct sockaddr_in6 *addr)
Definition ip_address.h:123
IPAddress(const char *in_address)
Definition ip_address.h:179
IPAddress(ip6_addr_t *other_ip)
Definition ip_address.h:190
bool operator!=(const IPAddress &other) const
Definition ip_address.h:168
bool operator==(const IPAddress &other) const
Definition ip_address.h:167
char * str_to(char *buf) const
Definition ip_address.h:153
IPAddress(esp_ip_addr_t *other_ip)
Definition ip_address.h:294
IPAddress(const ip_addr_t *other_ip)
Definition ip_address.h:184
IPAddress(esp_ip6_addr_t *other_ip)
Definition ip_address.h:281
IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth)
Definition ip_address.h:115
IPAddress(const struct in6_addr *other_ip)
Definition ip_address.h:122
IPAddress & operator+=(uint8_t increase)
Definition ip_address.h:230
IPAddress(esp_ip4_addr_t *other_ip)
Definition ip_address.h:287
IPAddress(ip4_addr_t *other_ip)
Definition ip_address.h:185
IPAddress(const std::string &in_address)
Definition ip_address.h:121
IPAddress(const arduino_ns::IPAddress &other_ip)
Definition ip_address.h:270
struct in6_addr ip6
Definition ip_address.h:27
struct in_addr ip4
Definition ip_address.h:28
union ip_addr_t::@150 u_addr
uint8_t type
Definition ip_address.h:30
struct in6_addr sin6_addr
Definition headers.h:89