xref: /openbmc/phosphor-networkd/src/types.hpp (revision b03a760f)
1 #pragma once
2 #include <fmt/core.h>
3 #include <net/ethernet.h>
4 #include <netinet/in.h>
5 
6 #include <algorithm>
7 #include <array>
8 #include <numeric>
9 #include <optional>
10 #include <string>
11 #include <string_view>
12 #include <type_traits>
13 #include <unordered_map>
14 #include <unordered_set>
15 #include <variant>
16 
17 constexpr bool operator==(ether_addr lhs, ether_addr rhs) noexcept
18 {
19     return std::equal(lhs.ether_addr_octet, lhs.ether_addr_octet + 6,
20                       rhs.ether_addr_octet);
21 }
22 
23 constexpr bool operator==(in_addr lhs, in_addr rhs) noexcept
24 {
25     return lhs.s_addr == rhs.s_addr;
26 }
27 
28 constexpr bool operator==(in6_addr lhs, in6_addr rhs) noexcept
29 {
30     return std::equal(lhs.s6_addr32, lhs.s6_addr32 + 4, rhs.s6_addr32);
31 }
32 
33 namespace phosphor
34 {
35 namespace network
36 {
37 
38 // Byte representations for common address types in network byte order
39 using InAddrAny = std::variant<in_addr, in6_addr>;
40 class IfAddr
41 {
42   private:
43     InAddrAny addr;
44     uint8_t pfx;
45 
46     static void invalidPfx(uint8_t pfx);
47 
48   public:
49     constexpr IfAddr() : addr({}), pfx(0)
50     {
51     }
52 
53     constexpr IfAddr(InAddrAny addr, uint8_t pfx) : addr(addr), pfx(pfx)
54     {
55         std::visit(
56             [pfx](auto v) {
57                 if (sizeof(v) * 8 < pfx)
58                 {
59                     invalidPfx(pfx);
60                 }
61             },
62             addr);
63     }
64 
65     constexpr auto getAddr() const
66     {
67         return addr;
68     }
69 
70     constexpr auto getPfx() const
71     {
72         return pfx;
73     }
74 
75     constexpr bool operator==(phosphor::network::IfAddr rhs) const noexcept
76     {
77         return addr == rhs.addr && pfx == rhs.pfx;
78     }
79 };
80 
81 /** @class InterfaceInfo
82  *  @brief Information about interfaces from the kernel
83  */
84 struct InterfaceInfo
85 {
86     unsigned idx;
87     unsigned flags;
88     std::optional<std::string> name = std::nullopt;
89     std::optional<ether_addr> mac = std::nullopt;
90     std::optional<unsigned> mtu = std::nullopt;
91     std::optional<unsigned> parent_idx = std::nullopt;
92     std::optional<std::string> kind = std::nullopt;
93     std::optional<uint16_t> vlan_id = std::nullopt;
94 
95     constexpr bool operator==(const InterfaceInfo& rhs) const noexcept
96     {
97         return idx == rhs.idx && flags == rhs.flags && name == rhs.name &&
98                mac == rhs.mac && mtu == rhs.mtu &&
99                parent_idx == rhs.parent_idx && kind == rhs.kind &&
100                vlan_id == rhs.vlan_id;
101     }
102 };
103 
104 /** @class AddressInfo
105  *  @brief Information about a addresses from the kernel
106  */
107 struct AddressInfo
108 {
109     unsigned ifidx;
110     IfAddr ifaddr;
111     uint8_t scope;
112     uint32_t flags;
113 
114     constexpr bool operator==(const AddressInfo& rhs) const noexcept
115     {
116         return ifidx == rhs.ifidx && ifaddr == rhs.ifaddr &&
117                scope == rhs.scope && flags == rhs.flags;
118     }
119 };
120 
121 /** @class NeighborInfo
122  *  @brief Information about a neighbor from the kernel
123  */
124 struct NeighborInfo
125 {
126     unsigned ifidx;
127     uint16_t state;
128     std::optional<InAddrAny> addr;
129     std::optional<ether_addr> mac;
130 
131     constexpr bool operator==(const NeighborInfo& rhs) const noexcept
132     {
133         return ifidx == rhs.ifidx && state == rhs.state && addr == rhs.addr &&
134                mac == rhs.mac;
135     }
136 };
137 
138 struct string_hash : public std::hash<std::string_view>
139 {
140     using is_transparent = void;
141 };
142 template <typename V>
143 using string_umap =
144     std::unordered_map<std::string, V, string_hash, std::equal_to<>>;
145 using string_uset =
146     std::unordered_set<std::string, string_hash, std::equal_to<>>;
147 
148 constexpr std::size_t hash_multi() noexcept
149 {
150     return 0;
151 }
152 
153 template <typename T, typename... Args>
154 constexpr std::size_t hash_multi(const T& v, const Args&... args) noexcept
155 {
156     const std::size_t seed = hash_multi(args...);
157     return seed ^ (std::hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2));
158 }
159 
160 namespace detail
161 {
162 
163 template <typename T, uint8_t size = sizeof(T)>
164 struct BswapAlign
165 {
166     using type = T;
167 };
168 
169 template <typename T>
170 struct BswapAlign<T, 2>
171 {
172     using type alignas(uint16_t) = T;
173 };
174 
175 template <typename T>
176 struct BswapAlign<T, 4>
177 {
178     using type alignas(uint32_t) = T;
179 };
180 
181 template <typename T>
182 struct BswapAlign<T, 8>
183 {
184     using type alignas(uint64_t) = T;
185 };
186 
187 template <typename T>
188 constexpr T bswapInt(typename BswapAlign<T>::type n) noexcept
189 {
190     static_assert(std::is_trivially_copyable_v<T>);
191     if constexpr (sizeof(T) == 2)
192     {
193         reinterpret_cast<uint16_t&>(n) =
194             __builtin_bswap16(reinterpret_cast<uint16_t&>(n));
195     }
196     else if constexpr (sizeof(T) == 4)
197     {
198         reinterpret_cast<uint32_t&>(n) =
199             __builtin_bswap32(reinterpret_cast<uint32_t&>(n));
200     }
201     else if constexpr (sizeof(T) == 8)
202     {
203         reinterpret_cast<uint64_t&>(n) =
204             __builtin_bswap64(reinterpret_cast<uint64_t&>(n));
205     }
206     else
207     {
208         auto b = reinterpret_cast<std::byte*>(&n);
209         std::reverse(b, b + sizeof(n));
210     }
211     return n;
212 }
213 
214 } // namespace detail
215 
216 template <typename T>
217 constexpr T bswap(T n) noexcept
218 {
219     return detail::bswapInt<T>(n);
220 }
221 
222 template <typename T>
223 constexpr T hton(T n) noexcept
224 {
225     if constexpr (std::endian::native == std::endian::big)
226     {
227         return n;
228     }
229     else if constexpr (std::endian::native == std::endian::little)
230     {
231         return bswap(n);
232     }
233     else
234     {
235         static_assert(std::is_same_v<T, void>);
236     }
237 }
238 
239 template <typename T>
240 constexpr T ntoh(T n) noexcept
241 {
242     return hton(n);
243 }
244 
245 namespace detail
246 {
247 inline constexpr auto charLookup = []() {
248     std::array<int8_t, 256> ret;
249     std::fill(ret.begin(), ret.end(), -1);
250     for (int8_t i = 0; i < 10; ++i)
251     {
252         ret[i + '0'] = i;
253     }
254     for (int8_t i = 0; i < 26; ++i)
255     {
256         ret[i + 'A'] = i + 10;
257         ret[i + 'a'] = i + 10;
258     }
259     return ret;
260 }();
261 inline constexpr auto intLookup = []() {
262     std::array<char, 36> ret;
263     for (int8_t i = 0; i < 10; ++i)
264     {
265         ret[i] = i + '0';
266     }
267     for (int8_t i = 0; i < 26; ++i)
268     {
269         ret[i + 10] = i + 'a';
270     }
271     return ret;
272 }();
273 } // namespace detail
274 
275 template <typename T, uint8_t base>
276 struct DecodeInt
277 {
278     static_assert(base > 1 && base <= 36);
279     static_assert(std::is_unsigned_v<T>);
280 
281     constexpr T operator()(std::string_view str) const
282     {
283         if (str.empty())
284         {
285             throw std::invalid_argument("Empty Str");
286         }
287         constexpr auto max = std::numeric_limits<T>::max();
288         auto ret =
289             std::accumulate(str.begin(), str.end(), T{}, [&](T r, char c) {
290                 auto v = detail::charLookup[c];
291                 if (v < 0 || v >= base)
292                 {
293                     throw std::invalid_argument("Invalid numeral");
294                 }
295                 if constexpr (std::popcount(base) == 1)
296                 {
297                     constexpr auto shift = std::countr_zero(base);
298                     constexpr auto maxshift = max >> shift;
299                     if (r > maxshift)
300                     {
301                         throw std::overflow_error("Integer Decode");
302                     }
303                     return (r << shift) | v;
304                 }
305                 else
306                 {
307                     constexpr auto maxbase = max / base;
308                     if (r > maxbase)
309                     {
310                         throw std::overflow_error("Integer Decode");
311                     }
312                     r *= base;
313                     if (max - v < r)
314                     {
315                         throw std::overflow_error("Integer Decode");
316                     }
317                     return r + v;
318                 }
319             });
320         return ret;
321     }
322 };
323 
324 template <typename T, uint8_t base>
325 struct EncodeInt
326 {
327     static_assert(base > 1 && base <= 36);
328     static_assert(std::is_unsigned_v<T>);
329 
330     static constexpr uint8_t buf_size = []() {
331         T v = std::numeric_limits<T>::max();
332         uint8_t i = 0;
333         for (; v != 0; ++i)
334         {
335             v /= base;
336         }
337         return i;
338     }();
339     using buf_type = std::array<char, buf_size>;
340 
341     constexpr uint8_t reverseFill(char* buf, T v) const noexcept
342     {
343         uint8_t i = 0;
344         do
345         {
346             if constexpr (std::popcount(base) == 1)
347             {
348                 buf[i++] = detail::intLookup[v & 0xf];
349                 v >>= 4;
350             }
351             else
352             {
353                 buf[i++] = detail::intLookup[v % base];
354                 v /= base;
355             }
356         } while (v > 0);
357         return i;
358     }
359 
360     constexpr char* operator()(char* buf, T v) const noexcept
361     {
362         uint8_t i = reverseFill(buf, v);
363         std::reverse(buf, buf + i);
364         return buf + i;
365     }
366 
367     constexpr char* operator()(char* buf, T v, uint8_t min_width) const noexcept
368     {
369         uint8_t i = reverseFill(buf, v);
370         auto end = buf + std::max(i, min_width);
371         std::fill(buf + i, end, '0');
372         std::reverse(buf, end);
373         return end;
374     }
375 };
376 
377 template <typename T>
378 struct ToAddr
379 {
380 };
381 
382 template <>
383 struct ToAddr<ether_addr>
384 {
385     constexpr ether_addr operator()(std::string_view str) const
386     {
387         constexpr DecodeInt<uint8_t, 16> di;
388         ether_addr ret;
389         if (str.size() == 12 && str.find(":") == str.npos)
390         {
391             for (size_t i = 0; i < 6; ++i)
392             {
393                 ret.ether_addr_octet[i] = di(str.substr(i * 2, 2));
394             }
395         }
396         else
397         {
398             for (size_t i = 0; i < 5; ++i)
399             {
400                 auto loc = str.find(":");
401                 ret.ether_addr_octet[i] = di(str.substr(0, loc));
402                 str.remove_prefix(loc == str.npos ? str.size() : loc + 1);
403                 if (str.empty())
404                 {
405                     throw std::invalid_argument("Missing mac data");
406                 }
407             }
408             ret.ether_addr_octet[5] = di(str);
409         }
410         return ret;
411     }
412 };
413 
414 template <>
415 struct ToAddr<in_addr>
416 {
417     constexpr in_addr operator()(std::string_view str) const
418     {
419         constexpr DecodeInt<uint8_t, 10> di;
420         uint32_t addr = {};
421         for (size_t i = 0; i < 3; ++i)
422         {
423             auto loc = str.find(".");
424             addr |= di(str.substr(0, loc));
425             addr <<= 8;
426             str.remove_prefix(loc == str.npos ? str.size() : loc + 1);
427             if (str.empty())
428             {
429                 throw std::invalid_argument("Missing addr data");
430             }
431         }
432         addr |= di(str);
433         return {hton(addr)};
434     }
435 };
436 
437 template <>
438 struct ToAddr<in6_addr>
439 {
440     constexpr in6_addr operator()(std::string_view str) const
441     {
442         constexpr DecodeInt<uint16_t, 16> di;
443         in6_addr ret = {};
444         size_t i = 0;
445         while (i < 8)
446         {
447             auto loc = str.find(':');
448             if (i == 6 && loc == str.npos)
449             {
450                 ret.s6_addr32[3] = ToAddr<in_addr>{}(str).s_addr;
451                 return ret;
452             }
453             if (loc != 0 && !str.empty())
454             {
455                 ret.s6_addr16[i++] = hton(di(str.substr(0, loc)));
456             }
457             if (i < 8 && str.size() > loc + 1 && str[loc + 1] == ':')
458             {
459                 str.remove_prefix(loc + 2);
460                 break;
461             }
462             else if (str.empty())
463             {
464                 throw std::invalid_argument("IPv6 Data");
465             }
466             str.remove_prefix(loc == str.npos ? str.size() : loc + 1);
467         }
468         if (str.starts_with(':'))
469         {
470             throw std::invalid_argument("Extra separator");
471         }
472         size_t j = 7;
473         if (!str.empty() && i < 6 && str.find('.') != str.npos)
474         {
475             auto loc = str.rfind(':');
476             ret.s6_addr32[3] =
477                 ToAddr<in_addr>{}(str.substr(loc == str.npos ? 0 : loc + 1))
478                     .s_addr;
479             str.remove_suffix(loc == str.npos ? str.size() : str.size() - loc);
480             j -= 2;
481         }
482         while (!str.empty() && j > i)
483         {
484             auto loc = str.rfind(':');
485             ret.s6_addr16[j--] =
486                 hton(di(str.substr(loc == str.npos ? 0 : loc + 1)));
487             str.remove_suffix(loc == str.npos ? str.size() : str.size() - loc);
488         }
489         if (!str.empty())
490         {
491             throw std::invalid_argument("Too much data");
492         }
493         return ret;
494     }
495 };
496 
497 template <>
498 struct ToAddr<InAddrAny>
499 {
500     constexpr InAddrAny operator()(std::string_view str) const
501     {
502         if (str.find(':') == str.npos)
503         {
504             return ToAddr<in_addr>{}(str);
505         }
506         return ToAddr<in6_addr>{}(str);
507     }
508 };
509 
510 template <>
511 struct ToAddr<IfAddr>
512 {
513     constexpr IfAddr operator()(std::string_view str) const
514     {
515         auto pos = str.rfind('/');
516         if (pos == str.npos)
517         {
518             throw std::invalid_argument("Invalid IfAddr");
519         }
520         return {ToAddr<InAddrAny>{}(str.substr(0, pos)),
521                 DecodeInt<uint8_t, 10>{}(str.substr(pos + 1))};
522     }
523 };
524 
525 template <typename T>
526 struct ToStr
527 {
528 };
529 
530 template <>
531 struct ToStr<char>
532 {
533     static constexpr uint8_t buf_size = 1;
534     using buf_type = std::array<char, buf_size>;
535 
536     constexpr char* operator()(char* buf, char v) const noexcept
537     {
538         buf[0] = v;
539         return buf + 1;
540     }
541 };
542 
543 template <>
544 struct ToStr<ether_addr>
545 {
546     // 6 octets * 2 hex chars + 5 separators
547     static constexpr uint8_t buf_size = 17;
548     using buf_type = std::array<char, buf_size>;
549 
550     constexpr char* operator()(char* buf, ether_addr v) const noexcept
551     {
552         for (char* ptr = buf + 2; ptr < buf + buf_size; ptr += 3)
553         {
554             *ptr = ':';
555         }
556         for (size_t i = 0; i < 6; ++i)
557         {
558             char* tmp = buf + i * 3;
559             uint8_t byte = v.ether_addr_octet[i];
560             EncodeInt<uint8_t, 16>{}(tmp, byte, 2);
561         }
562         return buf + buf_size;
563     }
564 };
565 
566 template <>
567 struct ToStr<in_addr>
568 {
569     // 4 octets * 3 dec chars + 3 separators
570     static constexpr uint8_t buf_size = 15;
571     using buf_type = std::array<char, buf_size>;
572 
573     constexpr char* operator()(char* buf, in_addr v) const noexcept
574     {
575         auto n = bswap(ntoh(v.s_addr));
576         for (size_t i = 0; i < 3; ++i)
577         {
578             buf = ToStr<char>{}(EncodeInt<uint8_t, 10>{}(buf, n & 0xff), '.');
579             n >>= 8;
580         }
581         return EncodeInt<uint8_t, 10>{}(buf, n & 0xff);
582     }
583 };
584 
585 template <>
586 struct ToStr<in6_addr>
587 {
588     // 8 hextets * 4 hex chars + 7 separators
589     static constexpr uint8_t buf_size = 39;
590     using buf_type = std::array<char, buf_size>;
591 
592     constexpr char* operator()(char* buf, in6_addr v) const noexcept
593     {
594         // IPv4 in IPv6 Addr
595         if (v.s6_addr32[0] == 0 && v.s6_addr32[1] == 0 &&
596             v.s6_addr32[2] == hton(uint32_t(0xffff)))
597         {
598             constexpr auto prefix = std::string_view("::ffff:");
599             return ToStr<in_addr>{}(
600                 std::copy(prefix.begin(), prefix.end(), buf), {v.s6_addr32[3]});
601         }
602 
603         size_t skip_start = 0;
604         size_t skip_size = 0;
605         {
606             size_t new_start = 0;
607             size_t new_size = 0;
608             for (size_t i = 0; i < 9; ++i)
609             {
610                 if (i < 8 && v.s6_addr16[i] == 0)
611                 {
612                     if (new_start + new_size == i)
613                     {
614                         new_size++;
615                     }
616                     else
617                     {
618                         new_start = i;
619                         new_size = 1;
620                     }
621                 }
622                 else if (new_start + new_size == i && new_size > skip_size)
623                 {
624                     skip_start = new_start;
625                     skip_size = new_size;
626                 }
627             }
628         }
629         for (size_t i = 0; i < 8; ++i)
630         {
631             if (i == skip_start && skip_size > 1)
632             {
633                 if (i == 0)
634                 {
635                     *(buf++) = ':';
636                 }
637                 *(buf++) = ':';
638                 i += skip_size - 1;
639                 continue;
640             }
641             buf = EncodeInt<uint16_t, 16>{}(buf, ntoh(v.s6_addr16[i]));
642             if (i < 7)
643             {
644                 *(buf++) = ':';
645             }
646         }
647         return buf;
648     }
649 };
650 
651 template <>
652 struct ToStr<InAddrAny>
653 {
654     // IPv6 is the bigger of the addrs
655     static constexpr uint8_t buf_size = ToStr<in6_addr>::buf_size;
656     using buf_type = std::array<char, buf_size>;
657 
658     constexpr char* operator()(char* buf, InAddrAny v) const noexcept
659     {
660         return std::visit([=](auto v) { return ToStr<decltype(v)>{}(buf, v); },
661                           v);
662     }
663 };
664 
665 template <>
666 struct ToStr<IfAddr>
667 {
668     // InAddrAny + sep + 3 prefix chars
669     static constexpr uint8_t buf_size = ToStr<InAddrAny>::buf_size + 4;
670     using buf_type = std::array<char, buf_size>;
671 
672     constexpr char* operator()(char* buf, IfAddr v) const noexcept
673     {
674         buf = ToStr<InAddrAny>{}(buf, v.getAddr());
675         buf = ToStr<char>{}(buf, '/');
676         return EncodeInt<uint8_t, 10>{}(buf, v.getPfx());
677     }
678 };
679 
680 namespace detail
681 {
682 
683 template <typename T>
684 constexpr bool vcontains() noexcept
685 {
686     return false;
687 }
688 
689 template <typename T, typename V, typename... Vs>
690 constexpr bool vcontains() noexcept
691 {
692     return vcontains<T, Vs...>() || std::is_same_v<T, V>;
693 }
694 
695 template <typename T, typename... Types>
696 constexpr std::enable_if_t<vcontains<T, Types...>(), bool>
697     veq(T t, std::variant<Types...> v) noexcept
698 {
699     return std::visit(
700         [t](auto v) {
701             if constexpr (std::is_same_v<T, decltype(v)>)
702             {
703                 return v == t;
704             }
705             else
706             {
707                 return false;
708             }
709         },
710         v);
711 }
712 
713 template <typename T>
714 struct ToStrBuf
715 {
716   public:
717     constexpr std::string_view operator()(T v) noexcept
718     {
719         return {buf.data(), ToStr<T>{}(buf.data(), v)};
720     }
721 
722   private:
723     typename ToStr<T>::buf_type buf;
724 };
725 
726 template <typename T>
727 struct Format
728 {
729   private:
730     fmt::formatter<std::string_view> formatter;
731 
732   public:
733     template <typename ParseContext>
734     constexpr auto parse(ParseContext& ctx)
735     {
736         return ctx.begin();
737     }
738 
739     template <typename FormatContext>
740     auto format(auto v, FormatContext& ctx) const
741     {
742         return formatter.format(ToStrBuf<T>{}(v), ctx);
743     }
744 };
745 } // namespace detail
746 } // namespace network
747 } // namespace phosphor
748 
749 template <typename... Ts>
750 struct std::hash<std::tuple<Ts...>>
751 {
752     constexpr auto operator()(const std::tuple<Ts...>& t) const noexcept
753     {
754         return std::apply(phosphor::network::hash_multi<Ts...>, t);
755     }
756 };
757 
758 template <>
759 struct std::hash<in_addr>
760 {
761     std::size_t operator()(in_addr addr) const noexcept;
762 };
763 
764 template <>
765 struct std::hash<in6_addr>
766 {
767     std::size_t operator()(in6_addr addr) const noexcept;
768 };
769 
770 template <>
771 struct std::hash<phosphor::network::IfAddr>
772 {
773     std::size_t operator()(phosphor::network::IfAddr addr) const noexcept;
774 };
775 
776 namespace fmt
777 {
778 template <>
779 struct formatter<ether_addr> : phosphor::network::detail::Format<ether_addr>
780 {
781 };
782 template <>
783 struct formatter<in_addr> : phosphor::network::detail::Format<in_addr>
784 {
785 };
786 template <>
787 struct formatter<in6_addr> : phosphor::network::detail::Format<in6_addr>
788 {
789 };
790 template <>
791 struct formatter<phosphor::network::InAddrAny>
792     : phosphor::network::detail::Format<phosphor::network::InAddrAny>
793 {
794 };
795 template <>
796 struct formatter<phosphor::network::IfAddr>
797     : phosphor::network::detail::Format<phosphor::network::IfAddr>
798 {
799 };
800 } // namespace fmt
801 
802 namespace std
803 {
804 string to_string(ether_addr value);
805 string to_string(in_addr value);
806 string to_string(in6_addr value);
807 string to_string(phosphor::network::InAddrAny value);
808 string to_string(phosphor::network::IfAddr value);
809 } // namespace std
810 
811 template <typename T>
812 constexpr std::enable_if_t<!std::is_same_v<phosphor::network::InAddrAny, T>,
813                            bool>
814     operator==(phosphor::network::InAddrAny lhs, T rhs) noexcept
815 {
816     return phosphor::network::detail::veq(rhs, lhs);
817 }
818 
819 auto& operator<<(auto& os, ether_addr v)
820 {
821     return os << phosphor::network::detail::ToStrBuf<ether_addr>{}(v);
822 }
823 
824 auto& operator<<(auto& os, in_addr v)
825 {
826     return os << phosphor::network::detail::ToStrBuf<in_addr>{}(v);
827 }
828 
829 auto& operator<<(auto& os, in6_addr v)
830 {
831     return os << phosphor::network::detail::ToStrBuf<in6_addr>{}(v);
832 }
833 
834 auto& operator<<(auto& os, phosphor::network::InAddrAny v)
835 {
836     phosphor::network::detail::ToStrBuf<phosphor::network::InAddrAny> tsb;
837     return os << tsb(v);
838 }
839 
840 auto& operator<<(auto& os, phosphor::network::IfAddr v)
841 {
842     phosphor::network::detail::ToStrBuf<phosphor::network::IfAddr> tsb;
843     return os << tsb(v);
844 }
845 
846 namespace phosphor::network
847 {
848 
849 /** @brief Contains all of the object information about the interface */
850 struct AllIntfInfo
851 {
852     InterfaceInfo intf;
853     std::optional<in_addr> defgw4 = std::nullopt;
854     std::optional<in6_addr> defgw6 = std::nullopt;
855     std::unordered_map<IfAddr, AddressInfo> addrs = {};
856     std::unordered_map<InAddrAny, NeighborInfo> staticNeighs = {};
857 };
858 
859 } // namespace phosphor::network
860