[libc-commits] [libc] [libc] Implement ether_ntoa and ether_ntoa_r (PR #233876)

Pavel Labath via libc-commits libc-commits at lists.llvm.org
Wed Oct 21 02:11:56 PDT 2026


https://github.com/labath updated https://github.com/llvm/llvm-project/pull/233876

>From 515f097b11661acb0b4efcbe2f444f1aa3a5f458 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Tue, 20 Oct 2026 14:24:32 +0000
Subject: [PATCH 1/2] [libc] Implement ether_ntoa and ether_ntoa_r

This adds <net/ethernet.h> (with struct ether_addr) and
<netinet/ether.h> (with ether_ntoa, ether_ntoa_r).

A few implementation notes:
- following glibc and musl (and the linux man page), octets < 0x10 are
  formatted without a leading zero. This differs from some BSD
  implementations.
- like inet_ntoa (but unlike GLIBC), ether_ntoa uses a thread-local
  static buffer

Assisted-by: Gemini
---
 libc/config/linux/aarch64/entrypoints.txt     |  4 ++
 libc/config/linux/aarch64/headers.txt         |  2 +
 libc/config/linux/riscv/entrypoints.txt       |  4 ++
 libc/config/linux/riscv/headers.txt           |  2 +
 libc/config/linux/x86_64/entrypoints.txt      |  4 ++
 libc/config/linux/x86_64/headers.txt          |  2 +
 libc/hdr/types/CMakeLists.txt                 |  9 ++++
 libc/hdr/types/struct_ether_addr.h            | 27 ++++++++++
 libc/include/CMakeLists.txt                   | 18 +++++++
 libc/include/llvm-libc-types/CMakeLists.txt   |  1 +
 .../llvm-libc-types/struct_ether_addr.h       | 23 +++++++++
 libc/include/net/ethernet.yaml                | 10 ++++
 libc/include/netinet/ether.yaml               | 22 ++++++++
 libc/src/netinet/CMakeLists.txt               |  2 +
 libc/src/netinet/ether/CMakeLists.txt         | 42 ++++++++++++++++
 libc/src/netinet/ether/ether_ntoa.cpp         | 29 +++++++++++
 libc/src/netinet/ether/ether_ntoa.h           | 26 ++++++++++
 libc/src/netinet/ether/ether_ntoa_r.cpp       | 32 ++++++++++++
 libc/src/netinet/ether/ether_ntoa_r.h         | 26 ++++++++++
 libc/src/netinet/ether/ether_utils.h          | 50 +++++++++++++++++++
 libc/test/src/netinet/CMakeLists.txt          |  2 +
 libc/test/src/netinet/ether/CMakeLists.txt    | 23 +++++++++
 .../src/netinet/ether/ether_ntoa_r_test.cpp   | 36 +++++++++++++
 .../src/netinet/ether/ether_ntoa_test.cpp     | 42 ++++++++++++++++
 24 files changed, 438 insertions(+)
 create mode 100644 libc/hdr/types/struct_ether_addr.h
 create mode 100644 libc/include/llvm-libc-types/struct_ether_addr.h
 create mode 100644 libc/include/net/ethernet.yaml
 create mode 100644 libc/include/netinet/ether.yaml
 create mode 100644 libc/src/netinet/ether/CMakeLists.txt
 create mode 100644 libc/src/netinet/ether/ether_ntoa.cpp
 create mode 100644 libc/src/netinet/ether/ether_ntoa.h
 create mode 100644 libc/src/netinet/ether/ether_ntoa_r.cpp
 create mode 100644 libc/src/netinet/ether/ether_ntoa_r.h
 create mode 100644 libc/src/netinet/ether/ether_utils.h
 create mode 100644 libc/test/src/netinet/ether/CMakeLists.txt
 create mode 100644 libc/test/src/netinet/ether/ether_ntoa_r_test.cpp
 create mode 100644 libc/test/src/netinet/ether/ether_ntoa_test.cpp

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index f19427f507b037..8570a82b0000e4 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1161,6 +1161,10 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.net.if_indextoname
     libc.src.net.if_nametoindex
 
+    # netinet/ether.h entrypoints
+    libc.src.netinet.ether.ether_ntoa
+    libc.src.netinet.ether.ether_ntoa_r
+
     # netinet/in.h entrypoints
     libc.src.netinet.in6addr_any
     libc.src.netinet.in6addr_loopback
diff --git a/libc/config/linux/aarch64/headers.txt b/libc/config/linux/aarch64/headers.txt
index 3e96e6637da940..a3301926f2e9b6 100644
--- a/libc/config/linux/aarch64/headers.txt
+++ b/libc/config/linux/aarch64/headers.txt
@@ -27,7 +27,9 @@ set(TARGET_PUBLIC_HEADERS
     libc.include.malloc
     libc.include.math
     libc.include.memory
+    libc.include.net_ethernet
     libc.include.net_if
+    libc.include.netinet_ether
     libc.include.netinet_in
     libc.include.netinet_tcp
     libc.include.netinet_udp
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index f9156b9fd6e4ad..93415cbf86489b 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -72,6 +72,10 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.net.if_indextoname
     libc.src.net.if_nametoindex
 
+    # netinet/ether.h entrypoints
+    libc.src.netinet.ether.ether_ntoa
+    libc.src.netinet.ether.ether_ntoa_r
+
     # netinet/in.h entrypoints
     libc.src.netinet.in6addr_any
     libc.src.netinet.in6addr_loopback
diff --git a/libc/config/linux/riscv/headers.txt b/libc/config/linux/riscv/headers.txt
index a3c65e50e4e605..ad9998880ef906 100644
--- a/libc/config/linux/riscv/headers.txt
+++ b/libc/config/linux/riscv/headers.txt
@@ -27,7 +27,9 @@ set(TARGET_PUBLIC_HEADERS
     libc.include.malloc
     libc.include.math
     libc.include.memory
+    libc.include.net_ethernet
     libc.include.net_if
+    libc.include.netinet_ether
     libc.include.netinet_in
     libc.include.netinet_tcp
     libc.include.netinet_udp
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 7125b2248a8752..a0dd937168bf1e 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -72,6 +72,10 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.net.if_indextoname
     libc.src.net.if_nametoindex
 
+    # netinet/ether.h entrypoints
+    libc.src.netinet.ether.ether_ntoa
+    libc.src.netinet.ether.ether_ntoa_r
+
     # netinet/in.h entrypoints
     libc.src.netinet.in6addr_any
     libc.src.netinet.in6addr_loopback
diff --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt
index fa37b19e50987f..3815a3d996a6ba 100644
--- a/libc/config/linux/x86_64/headers.txt
+++ b/libc/config/linux/x86_64/headers.txt
@@ -27,7 +27,9 @@ set(TARGET_PUBLIC_HEADERS
     libc.include.malloc
     libc.include.math
     libc.include.memory
+    libc.include.net_ethernet
     libc.include.net_if
+    libc.include.netinet_ether
     libc.include.netinet_in
     libc.include.netinet_tcp
     libc.include.netinet_udp
diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 605540a10dbb87..fdd0ca67da3396 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -788,6 +788,15 @@ add_proxy_header_library(
     libc.include.netinet_udp
 )
 
+add_proxy_header_library(
+  struct_ether_addr
+  HDRS
+    struct_ether_addr.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.struct_ether_addr
+    libc.include.net_ethernet
+)
+
 add_proxy_header_library(
   struct_sysinfo
   HDRS
diff --git a/libc/hdr/types/struct_ether_addr.h b/libc/hdr/types/struct_ether_addr.h
new file mode 100644
index 00000000000000..6cb544c4e44597
--- /dev/null
+++ b/libc/hdr/types/struct_ether_addr.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Proxy header for struct ether_addr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TYPES_STRUCT_ETHER_ADDR_H
+#define LLVM_LIBC_HDR_TYPES_STRUCT_ETHER_ADDR_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/struct_ether_addr.h"
+
+#else
+
+#include <net/ethernet.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_STRUCT_ETHER_ADDR_H
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 73c65cbcb90594..73920ce0d7670e 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -1135,6 +1135,15 @@ add_header_macro(
 
 file(MAKE_DIRECTORY ${LIBC_INCLUDE_DIR}/net)
 
+add_header_macro(
+  net_ethernet
+  ../libc/include/net/ethernet.yaml
+  net/ethernet.h
+  DEPENDS
+    .llvm-libc-types.struct_ether_addr
+    .llvm_libc_common_h
+)
+
 add_header_macro(
   net_if
   ../libc/include/net/if.yaml
@@ -1149,6 +1158,15 @@ add_header_macro(
 # TODO: This should be conditional on POSIX networking being included.
 file(MAKE_DIRECTORY ${LIBC_INCLUDE_DIR}/netinet)
 
+add_header_macro(
+  netinet_ether
+  ../libc/include/netinet/ether.yaml
+  netinet/ether.h
+  DEPENDS
+    .llvm-libc-types.struct_ether_addr
+    .llvm_libc_common_h
+)
+
 add_header_macro(
   netinet_in
   ../libc/include/netinet/in.yaml
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index dd183643e4243b..46a1a5bcfe6ee9 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -312,6 +312,7 @@ add_header(struct_cmsghdr HDR struct_cmsghdr.h DEPENDS .size_t)
 add_header(struct_msghdr HDR struct_msghdr.h DEPENDS .size_t .socklen_t .struct_iovec)
 add_header(struct_mmsghdr HDR struct_mmsghdr.h DEPENDS .struct_msghdr)
 add_header(struct_udphdr HDR struct_udphdr.h DEPENDS libc.include.llvm-libc-macros.stdint_macros)
+add_header(struct_ether_addr HDR struct_ether_addr.h DEPENDS libc.include.llvm-libc-macros.stdint_macros)
 add_header(ACTION HDR ACTION.h)
 add_header(ENTRY HDR ENTRY.h)
 add_header(VISIT HDR VISIT.h)
diff --git a/libc/include/llvm-libc-types/struct_ether_addr.h b/libc/include/llvm-libc-types/struct_ether_addr.h
new file mode 100644
index 00000000000000..4af8e7f3f8eb91
--- /dev/null
+++ b/libc/include/llvm-libc-types/struct_ether_addr.h
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definition of struct ether_addr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_STRUCT_ETHER_ADDR_H
+#define LLVM_LIBC_TYPES_STRUCT_ETHER_ADDR_H
+
+#include "../llvm-libc-macros/stdint-macros.h"
+
+struct ether_addr {
+  uint8_t ether_addr_octet[6];
+};
+
+#endif // LLVM_LIBC_TYPES_STRUCT_ETHER_ADDR_H
diff --git a/libc/include/net/ethernet.yaml b/libc/include/net/ethernet.yaml
new file mode 100644
index 00000000000000..b6de7563f23ba7
--- /dev/null
+++ b/libc/include/net/ethernet.yaml
@@ -0,0 +1,10 @@
+header: net/ethernet.h
+standards:
+  - bsd
+macros: []
+types:
+  - type_name: struct_ether_addr
+enums: []
+objects: []
+functions: []
+
diff --git a/libc/include/netinet/ether.yaml b/libc/include/netinet/ether.yaml
new file mode 100644
index 00000000000000..f838b51c556ffb
--- /dev/null
+++ b/libc/include/netinet/ether.yaml
@@ -0,0 +1,22 @@
+header: netinet/ether.h
+standards:
+  - bsd
+  - gnu
+macros: []
+types: []
+enums: []
+objects: []
+functions:
+  - name: ether_ntoa
+    standards:
+      - bsd
+    return_type: char *
+    arguments:
+      - type: const struct ether_addr *
+  - name: ether_ntoa_r
+    standards:
+      - gnu
+    return_type: char *
+    arguments:
+      - type: const struct ether_addr *
+      - type: char *
diff --git a/libc/src/netinet/CMakeLists.txt b/libc/src/netinet/CMakeLists.txt
index 868e09f7a34319..d4dd2f0e307987 100644
--- a/libc/src/netinet/CMakeLists.txt
+++ b/libc/src/netinet/CMakeLists.txt
@@ -1,3 +1,5 @@
+add_subdirectory(ether)
+
 add_entrypoint_object(
   in6addr_any
   SRCS
diff --git a/libc/src/netinet/ether/CMakeLists.txt b/libc/src/netinet/ether/CMakeLists.txt
new file mode 100644
index 00000000000000..4b92f54f6b22a0
--- /dev/null
+++ b/libc/src/netinet/ether/CMakeLists.txt
@@ -0,0 +1,42 @@
+add_header_library(
+  ether_utils
+  HDRS
+    ether_utils.h
+  DEPENDS
+    libc.hdr.stdint_proxy
+    libc.hdr.types.size_t
+    libc.hdr.types.struct_ether_addr
+    libc.src.__support.CPP.span
+    libc.src.__support.common
+    libc.src.__support.ctype_utils
+    libc.src.__support.macros.config
+)
+
+add_entrypoint_object(
+  ether_ntoa
+  SRCS
+    ether_ntoa.cpp
+  HDRS
+    ether_ntoa.h
+  DEPENDS
+    .ether_utils
+    libc.hdr.types.struct_ether_addr
+    libc.src.__support.common
+    libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
+)
+
+add_entrypoint_object(
+  ether_ntoa_r
+  SRCS
+    ether_ntoa_r.cpp
+  HDRS
+    ether_ntoa_r.h
+  DEPENDS
+    .ether_utils
+    libc.hdr.types.struct_ether_addr
+    libc.src.__support.CPP.span
+    libc.src.__support.common
+    libc.src.__support.macros.config
+    libc.src.__support.macros.null_check
+)
diff --git a/libc/src/netinet/ether/ether_ntoa.cpp b/libc/src/netinet/ether/ether_ntoa.cpp
new file mode 100644
index 00000000000000..140e8c5f9c95c5
--- /dev/null
+++ b/libc/src/netinet/ether/ether_ntoa.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of ether_ntoa function.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/netinet/ether/ether_ntoa.h"
+#include "hdr/types/struct_ether_addr.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/netinet/ether/ether_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(char *, ether_ntoa, (const struct ether_addr *addr)) {
+  LIBC_CRASH_ON_NULLPTR(addr);
+  static LIBC_THREAD_LOCAL char buffer[net::ETHER_ADDR_STR_LEN];
+  return net::ether_ntoa_r(*addr, buffer);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/netinet/ether/ether_ntoa.h b/libc/src/netinet/ether/ether_ntoa.h
new file mode 100644
index 00000000000000..0620288518fdfe
--- /dev/null
+++ b/libc/src/netinet/ether/ether_ntoa.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header of ether_ntoa.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_NETINET_ETHER_ETHER_NTOA_H
+#define LLVM_LIBC_SRC_NETINET_ETHER_ETHER_NTOA_H
+
+#include "hdr/types/struct_ether_addr.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+char *ether_ntoa(const struct ether_addr *addr);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_NETINET_ETHER_ETHER_NTOA_H
diff --git a/libc/src/netinet/ether/ether_ntoa_r.cpp b/libc/src/netinet/ether/ether_ntoa_r.cpp
new file mode 100644
index 00000000000000..b467a92f3d3867
--- /dev/null
+++ b/libc/src/netinet/ether/ether_ntoa_r.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of ether_ntoa_r function.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/netinet/ether/ether_ntoa_r.h"
+#include "hdr/types/struct_ether_addr.h"
+#include "src/__support/CPP/span.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/netinet/ether/ether_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(char *, ether_ntoa_r,
+                   (const struct ether_addr *addr, char *buf)) {
+  LIBC_CRASH_ON_NULLPTR(addr);
+  LIBC_CRASH_ON_NULLPTR(buf);
+  return net::ether_ntoa_r(*addr,
+                           cpp::span<char>(buf, net::ETHER_ADDR_STR_LEN));
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/netinet/ether/ether_ntoa_r.h b/libc/src/netinet/ether/ether_ntoa_r.h
new file mode 100644
index 00000000000000..19af032485019d
--- /dev/null
+++ b/libc/src/netinet/ether/ether_ntoa_r.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header of ether_ntoa_r.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_NETINET_ETHER_ETHER_NTOA_R_H
+#define LLVM_LIBC_SRC_NETINET_ETHER_ETHER_NTOA_R_H
+
+#include "hdr/types/struct_ether_addr.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+char *ether_ntoa_r(const struct ether_addr *addr, char *buf);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_NETINET_ETHER_ETHER_NTOA_R_H
diff --git a/libc/src/netinet/ether/ether_utils.h b/libc/src/netinet/ether/ether_utils.h
new file mode 100644
index 00000000000000..da906c87e6e97b
--- /dev/null
+++ b/libc/src/netinet/ether/ether_utils.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Internal utilities for Ethernet address manipulation.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_NETINET_ETHER_ETHER_UTILS_H
+#define LLVM_LIBC_SRC_NETINET_ETHER_ETHER_UTILS_H
+
+#include "hdr/stdint_proxy.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/struct_ether_addr.h"
+#include "src/__support/CPP/span.h"
+#include "src/__support/common.h"
+#include "src/__support/ctype_utils.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace net {
+
+/// Maximum length of an Ethernet address string "xx:xx:xx:xx:xx:xx\0".
+inline constexpr size_t ETHER_ADDR_STR_LEN = 18;
+
+/// Formats \p addr into \p buf (which must be at least ETHER_ADDR_STR_LEN
+/// bytes) as a colon-separated lowercase hexadecimal string and returns
+/// `buf.data()`.
+LIBC_INLINE char *ether_ntoa_r(const struct ether_addr &addr,
+                               cpp::span<char> buf) {
+  size_t pos = 0;
+  for (size_t i = 0; i < 6; ++i) {
+    uint8_t val = addr.ether_addr_octet[i];
+    if (val >= 0x10)
+      buf[pos++] = internal::int_to_b36_char(val >> 4);
+    buf[pos++] = internal::int_to_b36_char(val & 0xf);
+    buf[pos++] = i < 5 ? ':' : '\0';
+  }
+  return buf.data();
+}
+
+} // namespace net
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_NETINET_ETHER_ETHER_UTILS_H
diff --git a/libc/test/src/netinet/CMakeLists.txt b/libc/test/src/netinet/CMakeLists.txt
index 68499c648cd622..79382b73d0427b 100644
--- a/libc/test/src/netinet/CMakeLists.txt
+++ b/libc/test/src/netinet/CMakeLists.txt
@@ -1,5 +1,7 @@
 add_custom_target(libc_netinet_unittests)
 
+add_subdirectory(ether)
+
 add_libc_test(
   in_test
   SUITE
diff --git a/libc/test/src/netinet/ether/CMakeLists.txt b/libc/test/src/netinet/ether/CMakeLists.txt
new file mode 100644
index 00000000000000..66cd925e384161
--- /dev/null
+++ b/libc/test/src/netinet/ether/CMakeLists.txt
@@ -0,0 +1,23 @@
+add_custom_target(libc_netinet_ether_unittests)
+
+add_libc_test(
+  ether_ntoa_test
+  SUITE
+    libc_netinet_ether_unittests
+  SRCS
+    ether_ntoa_test.cpp
+  DEPENDS
+    libc.hdr.types.struct_ether_addr
+    libc.src.netinet.ether.ether_ntoa
+)
+
+add_libc_test(
+  ether_ntoa_r_test
+  SUITE
+    libc_netinet_ether_unittests
+  SRCS
+    ether_ntoa_r_test.cpp
+  DEPENDS
+    libc.hdr.types.struct_ether_addr
+    libc.src.netinet.ether.ether_ntoa_r
+)
diff --git a/libc/test/src/netinet/ether/ether_ntoa_r_test.cpp b/libc/test/src/netinet/ether/ether_ntoa_r_test.cpp
new file mode 100644
index 00000000000000..1e3851e9f4343d
--- /dev/null
+++ b/libc/test/src/netinet/ether/ether_ntoa_r_test.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for ether_ntoa_r.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/types/struct_ether_addr.h"
+#include "src/netinet/ether/ether_ntoa_r.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcEtherNtoaRTest, BasicConversion) {
+  char buf[18];
+
+  struct ether_addr zeroes = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+  EXPECT_EQ(buf, LIBC_NAMESPACE::ether_ntoa_r(&zeroes, buf));
+  EXPECT_STREQ("0:0:0:0:0:0", buf);
+
+  struct ether_addr broadcast = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
+  EXPECT_EQ(buf, LIBC_NAMESPACE::ether_ntoa_r(&broadcast, buf));
+  EXPECT_STREQ("ff:ff:ff:ff:ff:ff", buf);
+
+  struct ether_addr mixed1 = {{0x01, 0x23, 0x45, 0x67, 0x89, 0xab}};
+  EXPECT_EQ(buf, LIBC_NAMESPACE::ether_ntoa_r(&mixed1, buf));
+  EXPECT_STREQ("1:23:45:67:89:ab", buf);
+
+  struct ether_addr mixed2 = {{0xcd, 0xef, 0x0a, 0x10, 0x00, 0x9f}};
+  EXPECT_EQ(buf, LIBC_NAMESPACE::ether_ntoa_r(&mixed2, buf));
+  EXPECT_STREQ("cd:ef:a:10:0:9f", buf);
+}
diff --git a/libc/test/src/netinet/ether/ether_ntoa_test.cpp b/libc/test/src/netinet/ether/ether_ntoa_test.cpp
new file mode 100644
index 00000000000000..1176d0b3cbcc68
--- /dev/null
+++ b/libc/test/src/netinet/ether/ether_ntoa_test.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for ether_ntoa.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/types/struct_ether_addr.h"
+#include "src/netinet/ether/ether_ntoa.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcEtherNtoaTest, BasicConversion) {
+  struct ether_addr zeroes = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+  EXPECT_STREQ("0:0:0:0:0:0", LIBC_NAMESPACE::ether_ntoa(&zeroes));
+
+  struct ether_addr broadcast = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
+  EXPECT_STREQ("ff:ff:ff:ff:ff:ff", LIBC_NAMESPACE::ether_ntoa(&broadcast));
+
+  struct ether_addr mixed1 = {{0x01, 0x23, 0x45, 0x67, 0x89, 0xab}};
+  EXPECT_STREQ("1:23:45:67:89:ab", LIBC_NAMESPACE::ether_ntoa(&mixed1));
+
+  struct ether_addr mixed2 = {{0xcd, 0xef, 0x0a, 0x10, 0x00, 0x9f}};
+  EXPECT_STREQ("cd:ef:a:10:0:9f", LIBC_NAMESPACE::ether_ntoa(&mixed2));
+}
+
+TEST(LlvmLibcEtherNtoaTest, BufferReuseAndOverwrite) {
+  struct ether_addr addr1 = {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}};
+  char *ptr1 = LIBC_NAMESPACE::ether_ntoa(&addr1);
+  EXPECT_STREQ("11:22:33:44:55:66", ptr1);
+
+  struct ether_addr addr2 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}};
+  char *ptr2 = LIBC_NAMESPACE::ether_ntoa(&addr2);
+  EXPECT_EQ(ptr1, ptr2);
+  EXPECT_STREQ("1:2:3:4:5:6", ptr1);
+  EXPECT_STREQ("1:2:3:4:5:6", ptr2);
+}

>From c17c37d0e772e95582259fc9070acd07363c22c0 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Wed, 21 Oct 2026 09:11:34 +0000
Subject: [PATCH 2/2] fixes

---
 libc/hdr/types/CMakeLists.txt   | 1 -
 libc/include/netinet/ether.yaml | 1 -
 2 files changed, 2 deletions(-)

diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index fdd0ca67da3396..4c65ebc1393795 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -794,7 +794,6 @@ add_proxy_header_library(
     struct_ether_addr.h
   FULL_BUILD_DEPENDS
     libc.include.llvm-libc-types.struct_ether_addr
-    libc.include.net_ethernet
 )
 
 add_proxy_header_library(
diff --git a/libc/include/netinet/ether.yaml b/libc/include/netinet/ether.yaml
index f838b51c556ffb..99a65a781c11b9 100644
--- a/libc/include/netinet/ether.yaml
+++ b/libc/include/netinet/ether.yaml
@@ -1,7 +1,6 @@
 header: netinet/ether.h
 standards:
   - bsd
-  - gnu
 macros: []
 types: []
 enums: []



More information about the libc-commits mailing list