A self-contained embeddable address parsing library with full RFC compliance.
Author: [email protected] | License: MIT
Interactive Web Demo: https://jrepp.github.io/ipv6-parse/
Test IPv6/IPv4 address parsing in your browser with our modern WebAssembly-powered demo featuring:
- Real-time parsing and validation
- Batch testing mode with 60+ example addresses
- Visual error diagnostics with position indicators
- Zero dependencies, runs entirely in your browser
Install however works best for your project:
- NPM Package - Node.js applications (async + sync APIs)
- WebAssembly - Browser applications (live demo)
- Linux Packages - Debian/Ubuntu (.deb), Fedora/RHEL/CentOS (.rpm)
- Homebrew - macOS and Linux
- Conan - Cross-platform C/C++ projects
- vcpkg - Visual Studio and CMake projects
- CMake Integration - pkg-config support, find_package()
- Source Code - Single-header embedding in C/C++ projects
Full IPv6/IPv4 Support:
- Zero compression:
::1,2001:db8::1 - IPv4-embedded:
::ffff:192.0.2.1 - CIDR notation:
2001:db8::/32,10.0.0.0/8 - Port notation:
[::1]:8080,192.0.2.1:443 - Zone IDs (RFC 4007):
fe80::1%eth0 - IPv4 addresses:
192.0.2.1:8080 - Shortened IPv4:
10.1→10.0.0.1 - Complex combinations:
[2001:db8::1/64%eth0]:443
Quality & Reliability:
- RFC Compliant - RFC 4291, RFC 5952, RFC 4007
- Memory Safe - No dynamic allocation, bounds checking
- Round-trip Conversion - Parse → Structure → String → Parse
- Rich Diagnostics - Detailed error reporting with position information
- Comprehensive Tests - 100+ test cases with fuzz testing
- CI/CD Pipeline - Multi-compiler testing (GCC, Clang, MSVC, AppleClang)
- Cross-platform - Linux, macOS, Windows
Validated by comprehensive benchmarks on real hardware:
Native C (Apple Silicon M-series):
- Parsing: 3.6M operations/sec (0.28 μs/parse)
- Formatting: 3.3M operations/sec (0.30 μs/format)
- Comparison: 8.3M operations/sec (0.12 μs/compare)
- Overall: 4.3M operations/sec across all functions
NPM/WASM (Browser/Node.js):
- 1.75M+ parses/second, 570ns latency
- See README_WASM.md for detailed benchmarks
Run your own benchmarks: ./build/bin/ipv6-fuzz 100000
Quick Links: RFC Conformance • Quick Start • Installation • Overview • API Reference • Building
This library implements the following IETF RFCs:
| RFC | Title | Implementation |
|---|---|---|
| RFC 4291 | IPv6 Addressing Architecture | ✅ Basic format, zero compression, IPv4 embedding, CIDR notation |
| RFC 5952 | IPv6 Text Representation | ✅ Lowercase hex, leading zero suppression, longest zero run compression |
| RFC 4007 | IPv6 Scoped Address Architecture | ✅ Zone identifiers with % delimiter, numeric and textual zone IDs |
- ✅ CI/CD Pipeline - Multi-compiler testing (GCC 12-14, Clang 15-18, MSVC, AppleClang)
- ✅ Code Coverage - Tracked with lcov and Codecov
- ✅ Memory Safety - Valgrind testing for memory leaks
- ✅ Warnings as Errors - Strict compilation standards
- ✅ Cross-platform - Linux, macOS, Windows
#include "ipv6.h"
const char *input = "[2001:db8::1/64]:8080";
ipv6_address_full_t addr = {0};
if (ipv6_from_str(input, strlen(input), &addr)) {
printf("Port: %u, CIDR: %u\n", addr.port, addr.mask);
char output[IPV6_STRING_SIZE];
ipv6_to_str(&addr, output, sizeof(output));
printf("Formatted: %s\n", output);
}import { parse, isValid } from 'ipv6-parse';
// Parse and destructure
const { formatted, port, mask } = await parse('[2001:db8::1/64]:8080');
console.log({ formatted, port, mask }); // { formatted: "2001:db8::1", port: 8080, mask: 64 }
// Validate and handle errors
const input = userInput.trim();
if (await isValid(input)) {
const addr = await parse(input);
// Use addr.formatted, addr.components, etc.
}High-performance sync API: See README_NPM.md for 1.75M+ ops/sec synchronous parsing.
Browser usage: See README_WASM.md for WebAssembly integration.
npm install ipv6-parseconst ipv6 = require('ipv6-parse');
const addr = await ipv6.parse('2001:db8::1');
console.log(addr.formatted); // "2001:db8::1"See README_NPM.md for complete NPM documentation.
Download the latest WASM release or try the interactive demo.
<script src="ipv6-parse.js"></script>
<script src="ipv6-parse-api.js"></script>
<script>
createIPv6Module().then(module => {
const parser = new IPv6Parser(module);
const addr = parser.parse('2001:db8::1');
console.log(addr.formatted);
});
</script>See README_WASM.md for complete WASM documentation.
Debian/Ubuntu:
# Download from releases page
wget https://github.com/jrepp/ipv6-parse/releases/latest/download/ipv6-parse-*-Linux.deb
sudo dpkg -i ipv6-parse-*-Linux.debFedora/RHEL/CentOS:
# Download from releases page
wget https://github.com/jrepp/ipv6-parse/releases/latest/download/ipv6-parse-*-Linux.rpm
sudo rpm -i ipv6-parse-*-Linux.rpm# Install from HEAD (latest master)
brew install --HEAD https://raw.githubusercontent.com/jrepp/ipv6-parse/master/Formula/ipv6-parse.rbOptional: Build with WASM support:
brew install ipv6-parse --with-emscripten# Add to your conanfile.txt
[requires]
ipv6-parse/1.2.1
# Or install directly
conan create . ipv6-parse/1.2.1@# After copying port to vcpkg/ports/ipv6-parse
vcpkg install ipv6-parseSee README_PACKAGE_MANAGERS.md for complete package manager documentation.
Static library (default):
mkdir build && cd build
cmake ..
make
sudo make installShared library:
mkdir build && cd build
cmake -DBUILD_SHARED_LIBS=ON ..
make
sudo make installWebAssembly:
# Requires Emscripten SDK
./build_wasm.shAfter installation, the library can be used with:
- pkg-config:
gcc $(pkg-config --cflags --libs ipv6-parse) myapp.c -o myapp - CMake:
find_package(ipv6-parse REQUIRED)andtarget_link_libraries(myapp PRIVATE ipv6-parse::ipv6-parse)
graph TB
subgraph "Input Formats"
IN1["IPv6: 2001:db8::1"]
IN2["IPv4: 192.0.2.1"]
IN3["CIDR: ::1/128"]
IN4["Port: [::1]:8080"]
IN5["Zone ID: fe80::1%eth0"]
IN6["IPv4-embed: ::ffff:192.0.2.1"]
IN7["Combined: [2001:db8::1/64%eth0]:443"]
end
subgraph "Parser - ipv6_from_str()"
PARSE["State Machine Parser<br/>RFC 4291, 5952, 4007"]
VALIDATE["Validation & Diagnostics<br/>• Component count<br/>• Range checks<br/>• Format validation"]
end
subgraph "Internal Representation"
STRUCT["ipv6_address_full_t<br/>━━━━━━━━━━━━━<br/>address: [8 × uint16_t]<br/>port: uint16_t<br/>mask: uint32_t<br/>iface: const char*<br/>iface_len: uint32_t<br/>flags: uint32_t"]
end
subgraph "Formatter - ipv6_to_str()"
FORMAT["RFC 5952 Formatter<br/>• Lowercase hex<br/>• Zero compression (::)<br/>• Shortest representation"]
end
subgraph "Output Formats"
OUT1["2001:db8::1"]
OUT2["::1/128"]
OUT3["[::1]:8080"]
OUT4["fe80::1%eth0"]
OUT5["Round-trip preserves:<br/>• Zone IDs<br/>• CIDR masks<br/>• Port numbers"]
end
IN1 --> PARSE
IN2 --> PARSE
IN3 --> PARSE
IN4 --> PARSE
IN5 --> PARSE
IN6 --> PARSE
IN7 --> PARSE
PARSE --> VALIDATE
VALIDATE --> STRUCT
STRUCT --> FORMAT
FORMAT --> OUT1
FORMAT --> OUT2
FORMAT --> OUT3
FORMAT --> OUT4
FORMAT --> OUT5
STRUCT -.->|"Round-trip<br/>ipv6_compare()"| STRUCT
style PARSE fill:#e1f5ff
style FORMAT fill:#e1f5ff
style STRUCT fill:#fff4e1
style VALIDATE fill:#ffe1e1
This library implements RFC 4007 for IPv6 scoped addresses with zone identifiers (zone IDs). Zone IDs are used to identify the zone (typically a network interface) for addresses with non-global scope, such as link-local addresses.
Zone IDs use the % delimiter followed by an implementation-dependent zone identifier:
<address>%<zone_id>
ipv6_address_full_t addr;
// Link-local address with interface name
ipv6_from_str("fe80::1%eth0", 15, &addr);
// Numeric zone ID
ipv6_from_str("fe80::1%1", 10, &addr);
// Zone ID with CIDR notation
ipv6_from_str("fe80::1/64%eth0", 18, &addr);
// Zone ID with port notation (brackets required)
ipv6_from_str("[fe80::1%eth0]:8080", 22, &addr);
// Access zone ID from parsed address
printf("Interface: %.*s\n", addr.iface_len, addr.iface);
// Round-trip conversion preserves zone ID
char output[IPV6_STRING_SIZE];
ipv6_to_str(&addr, output, sizeof(output));
// Result: "fe80::1%eth0"- MUST use
%as delimiter (Section 11.2) - MUST validate interface name length < 16 characters (IFNAMSIZ)
- MAY be used with any scoped address, not just link-local (Section 6)
- Zone IDs are implementation-dependent (numeric or textual)
- Full compatibility with bracket notation for ports (Section 11.7)
- Supports combination with CIDR masks
The ipv6_address_full_t structure includes zone ID support:
typedef struct {
ipv6_address_t address; // address components
uint16_t port; // port binding
uint16_t pad0; // first padding
uint32_t mask; // CIDR mask bits
const char* iface; // pointer to zone ID in input string
uint32_t iface_len; // length of zone ID string
uint32_t flags; // feature flags
} ipv6_address_full_t;Note: The iface pointer references the original input string. Ensure the input string remains valid while accessing the zone ID.
See test.c: test_api_use_loopback_const
127.111.2.1
uint16_t components[IPV6_NUM_COMPONENTS] = {
0x7f6f,
0x0201 }- Addresses can be constructed directly in code and support the full two-way functionality
Full tracing can be enabled by running cmake -DPARSE_TRACE=1
Flags are used to communicate which fields are filled out in the address structure after parsing. Address components are assumed.
typedef enum {
IPV6_FLAG_HAS_PORT = 0x00000001, // the address specifies a port setting
IPV6_FLAG_HAS_MASK = 0x00000002, // the address specifies a CIDR mask
IPV6_FLAG_IPV4_EMBED = 0x00000004, // the address has an embedded IPv4 address in the last 32bits
IPV6_FLAG_IPV4_COMPAT = 0x00000008, // the address is IPv4 compatible (1.2.3.4:5555)
} ipv6_flag_t;Simplified address structure where the components are represented in machine format, left to right [0..7]
e.g. little-endian x86 machines:
aa11:bb22:: -> 0xaa11, 0xbb22, 0x0000, ...
#define IPV6_NUM_COMPONENTS 8
#define IPV4_NUM_COMPONENTS 2
#define IPV4_EMBED_INDEX 6
typedef struct {
uint16_t components[IPV6_NUM_COMPONENTS];
} ipv6_address_t;ipv6_address_full_t
Input / output type for round trip parsing and converting to string.
Features are indicated using flags, see ipv6_flag_t.
typedef struct {
ipv6_address_t address; // address components
uint16_t port; // port binding
uint16_t pad0; // first padding
uint32_t mask; // number of mask bits N specified for example in ::1/N
const char* iface; // pointer to place in address string where interface is defined
uint32_t iface_len; // number of bytes in the name of the interface
uint32_t flags; // flags indicating features of address
} ipv6_address_full_t;Result of ipv6_compare of two addresses
typedef enum {
IPV6_COMPARE_OK = 0,
IPV6_COMPARE_FORMAT_MISMATCH, // address differ in their
IPV6_COMPARE_MASK_MISMATCH, // the CIDR mask does not match
IPV6_COMPARE_PORT_MISMATCH, // the port does not match
IPV6_COMPARE_ADDRESS_MISMATCH, // address components do not match
} ipv6_compare_result_t;Event type emitted from diagnostic function
typedef enum {
IPV6_DIAG_STRING_SIZE_EXCEEDED = 0,
IPV6_DIAG_INVALID_INPUT = 1,
IPV6_DIAG_INVALID_INPUT_CHAR = 2,
IPV6_DIAG_TRAILING_ZEROES = 3,
IPV6_DIAG_V6_BAD_COMPONENT_COUNT = 4,
IPV6_DIAG_V4_BAD_COMPONENT_COUNT = 5,
IPV6_DIAG_V6_COMPONENT_OUT_OF_RANGE = 6,
IPV6_DIAG_V4_COMPONENT_OUT_OF_RANGE = 7,
IPV6_DIAG_INVALID_PORT = 8,
IPV6_DIAG_INVALID_CIDR_MASK = 9,
IPV6_DIAG_IPV4_REQUIRED_BITS = 10,
IPV6_DIAG_IPV4_INCORRECT_POSITION = 11,
IPV6_DIAG_INVALID_BRACKETS = 12,
IPV6_DIAG_INVALID_ABBREV = 13,
IPV6_DIAG_INVALID_DECIMAL_TOKEN = 14,
IPV6_DIAG_INVALID_HEX_TOKEN = 15,
} ipv6_diag_event_t;Structure that carriers information about the diagnostic message
typedef struct {
const char* message; // English ascii debug message
const char* input; // Input string that generated the diagnostic
uint32_t position; // Position in input that caused the diagnostic
uint32_t pad0;
} ipv6_diag_info_t;A diagnostic function that receives information from parsing the address
typedef void (*ipv6_diag_func_t) (
ipv6_diag_event_t event,
const ipv6_diag_info_t* info,
void* user_data);Read an IPv6 address from a string, handles parsing a variety of format information from the spec. Will also handle IPv4 address passed in without any embedding information.
bool IPV6_API_DECL(ipv6_from_str) (
const char* input,
size_t input_bytes,
ipv6_address_full_t* out);Additional functionality parser that receives diagnostics information from parsing the address, including errors.
bool IPV6_API_DECL(ipv6_from_str_diag) (
const char* input,
size_t input_bytes,
ipv6_address_full_t* out,
ipv6_diag_func_t func,
void* user_data);Convert an IPv6 structure to an ASCII string.
The conversion will flatten zero address components according to the address formatting specification. For example: ffff:0:0:0:0:0:0:1 -> ffff::1
Requires output_bytes Returns the size in bytes of the string minus the nul byte.
size_t IPV6_API_DECL(ipv6_to_str) (
const ipv6_address_full_t* in,
char* output,
size_t output_bytes);Compare two addresses, 0 (IPV6_COMPARE_OK) if equal, else ipv6_compare_result_t.
Use IPV6_FLAG_HAS_MASK, IPV6_FLAG_HAS_PORT in ignore_flags to ignore mask or port in comparisons.
IPv4 embed and IPv4 compatible addresses will be compared as equal if either IPV6_FLAG_IPV4_EMBED or IPV6_FLAG_IPV4_COMPAT flags are passed in ignore_flags.
ipv6_compare_result_t IPV6_API_DECL(ipv6_compare) (
const ipv6_address_full_t* a,
const ipv6_address_full_t* b,
uint32_t ignore_flags);