Skip to content

Commit cd54c34

Browse files
committed
Updating the compare API
- IPv4 compatible and embedded addresses can now be compared - Port and mask can be ignored as part of comparison - New tests added for comparing different types of addresses - Fixed a bug where embedded addresses were being considered 'compat' - Updated the trace macros and build process
1 parent 816019a commit cd54c34

5 files changed

Lines changed: 296 additions & 85 deletions

File tree

CMakeLists.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ target_include_directories(ipv6-parse PUBLIC ${IPV6_CONFIG_HEADER_PATH})
8787
set_target_properties(ipv6-parse PROPERTIES COMPILE_FLAGS ${ipv6_target_compile_flags})
8888

8989
if (PARSE_TRACE)
90-
message("Parse tracing enabled")
91-
set_target_properties(ipv6-test PROPERTIES COMPILE_FLAGS "-DPARSE_TRACE=1")
92-
set_target_properties(ipv6-cmd PROPERTIES COMPILE_FLAGS "-DPARSE_TRACE=1")
93-
set_target_properties(ipv6-parse PROPERTIES COMPILE_FLAGS "-DPARSE_TRACE=1")
94-
endif ()
90+
message("Address parse tracing enabled")
91+
set_target_properties(ipv6-parse PROPERTIES COMPILE_DEFINITIONS PARSE_TRACE=1)
92+
set_target_properties(ipv6-test PROPERTIES COMPILE_DEFINITIONS PARSE_TRACE=1)
93+
set_target_properties(ipv6-cmd PROPERTIES COMPILE_DEFINITIONS PARSE_TRACE=1)
94+
endif ()

cmdline.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ int main (int argc, const char** argv) {
6666
return 4;
6767
}
6868

69-
if (0 != ipv6_compare(&addr, &addr2)) {
69+
// Allow embedded and compatible addresses to compare as equal
70+
if (IPV6_COMPARE_OK != ipv6_compare(&addr, &addr2, IPV6_FLAG_IPV4_COMPAT)) {
7071
printf("- failed to compare: '%s' != '%s'\n", str, buffer);
7172
return 5;
7273
}
@@ -77,3 +78,4 @@ int main (int argc, const char** argv) {
7778
return 0;
7879
}
7980

81+

ipv6.c

Lines changed: 84 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "ipv6.h"
22
#include "ipv6_config.h"
33

4+
45
#ifdef HAVE_STDIO_H
56
#include <stdio.h>
67
#endif
@@ -14,9 +15,9 @@
1415
#endif
1516

1617
#if defined(PARSE_TRACE)
17-
#define TRACE(...) printf(__VA_ARGS__)
18+
#define IPV6_TRACE(...) printf(__VA_ARGS__)
1819
#else
19-
#define TRACE(...)
20+
#define IPV6_TRACE(...)
2021
#endif
2122

2223
#ifdef HAVE__SNPRINTF_S
@@ -76,7 +77,7 @@ typedef enum {
7677
} ipv6_reader_state_flag_t;
7778

7879
//
79-
// Reader state encapsulates the process of tokenizing and processing
80+
// Reader state encapsulates the process of token-izing and processing
8081
// the incoming address specification
8182
//
8283
typedef struct ipv6_reader_state_t {
@@ -148,20 +149,20 @@ static const char* eventclass_str (eventclass_t input)
148149
// Update the current state logging the transition
149150
//
150151
#define CHANGE_STATE(value) \
151-
TRACE(" * %s -> %s %s:%u\n", \
152+
IPV6_TRACE(" * %s -> %s %s:%u\n", \
152153
state_str(state->current), state_str(value), __FILE__, (uint32_t)__LINE__); \
153154
state->current = value;
154155

155156
#define BEGIN_TOKEN(offset) \
156-
TRACE(" * %s: token begin at %u\n", state_str(state->current), state->position + offset); \
157+
IPV6_TRACE(" * %s: token begin at %u\n", state_str(state->current), state->position + offset); \
157158
state->token_position = state->position + offset; \
158159
state->token_len = 0; \
159160

160161
//
161162
// Indicate the presence of an invalid event class character for the current state
162163
//
163164
#define INVALID_INPUT() \
164-
TRACE("invalid input class (%d) in state: %s at position %d of '%s' (%c)\n", \
165+
IPV6_TRACE("invalid input class (%d) in state: %s at position %d of '%s' (%c)\n", \
165166
input, state_str(state->current), state->position, state->input, state->input[state->position]); \
166167
ipv6_error(state, IPV6_DIAG_INVALID_INPUT, "Invalid input"); \
167168
return;
@@ -171,7 +172,7 @@ static const char* eventclass_str (eventclass_t input)
171172
//
172173
#define VALIDATE(msg, diag, cond, action) \
173174
if (!(cond)) { \
174-
TRACE(" failed '!" #cond "' in state: %s at position %d of '%s'\n\n", \
175+
IPV6_TRACE(" failed '!" #cond "' in state: %s at position %d of '%s'\n\n", \
175176
state_str(state->current), state->position, state->input); \
176177
ipv6_error(state, diag, msg); \
177178
action; \
@@ -265,7 +266,7 @@ static int32_t read_hexidecimal_token (ipv6_reader_state_t* state)
265266
static void ipv6_parse_component (ipv6_reader_state_t* state) {
266267
int32_t component = read_hexidecimal_token(state);
267268

268-
TRACE(" * ipv6 address component %4x (%d)\n", (uint16_t)component, component);
269+
IPV6_TRACE(" * ipv6 address component %4x (%d)\n", (uint16_t)component, component);
269270

270271
VALIDATE("Only 8 16bit components are allowed",
271272
IPV6_DIAG_V6_BAD_COMPONENT_COUNT,
@@ -288,7 +289,7 @@ static void ipv6_parse_component (ipv6_reader_state_t* state) {
288289
static void ipv4_parse_component (ipv6_reader_state_t* state) {
289290
int32_t octet = read_decimal_token(state);
290291

291-
TRACE(" * ipv4 address octet %2x (%d)\n", (uint8_t)octet, octet);
292+
IPV6_TRACE(" * ipv4 address octet %2x (%d)\n", (uint8_t)octet, octet);
292293

293294
VALIDATE("Only 4 8bit components are allowed in an IPv4 embedding",
294295
IPV6_DIAG_V4_BAD_COMPONENT_COUNT,
@@ -363,7 +364,7 @@ static void ipv6_state_transition (
363364
ipv6_reader_state_t* state,
364365
eventclass_t input)
365366
{
366-
TRACE(" * transition input: %s <- %s\n", state_str(state->current), eventclass_str(input));
367+
IPV6_TRACE(" * transition input: %s <- %s\n", state_str(state->current), eventclass_str(input));
367368

368369
switch (state->current) {
369370
default:
@@ -455,7 +456,7 @@ static void ipv6_state_transition (
455456
return);
456457

457458
// Backwards compatibility marker for pure IPv4 address
458-
if (state->components == 0) {
459+
if (!(state->flags & READER_FLAG_ZERORUN) && state->components == 0) {
459460
state->flags |= READER_FLAG_IPV4_COMPAT;
460461
}
461462

@@ -500,7 +501,7 @@ static void ipv6_state_transition (
500501
state->zerorun = state->components;
501502
state->flags |= READER_FLAG_ZERORUN;
502503

503-
TRACE(" * zero run index: %d\n", state->zerorun);
504+
IPV6_TRACE(" * zero run index: %d\n", state->zerorun);
504505
break;
505506

506507
case EC_WHITESPACE:
@@ -657,7 +658,7 @@ bool IPV6_API_DEF(ipv6_from_str_diag) (
657658
state.address_full = out;
658659

659660
while (*cp && cp < ep) {
660-
TRACE(
661+
IPV6_TRACE(
661662
" * parse state: %s, cp: '%c' (%02x) position: %d, flags: %08x\n",
662663
state_str(state.current),
663664
*cp,
@@ -726,6 +727,18 @@ bool IPV6_API_DEF(ipv6_from_str_diag) (
726727
// Treat the end of input as whitespace to simplify state transitions
727728
ipv6_state_transition(&state, EC_WHITESPACE);
728729

730+
// Early out if there was an error processing the string
731+
if ((state.flags & READER_FLAG_ERROR) != 0) {
732+
return false;
733+
}
734+
735+
// If an IPv4 compatible address was specified the rest of the IPv6 collapsing
736+
// rules can be skipped
737+
if ((state.flags & READER_FLAG_IPV4_COMPAT) != 0) {
738+
state.address_full->flags |= IPV6_FLAG_IPV4_COMPAT;
739+
return true;
740+
}
741+
729742
// Mark the presence of embedded IPv4 addresses
730743
if (state.flags & READER_FLAG_IPV4_EMBEDDING) {
731744
if (state.v4_octets != 4) {
@@ -736,18 +749,6 @@ bool IPV6_API_DEF(ipv6_from_str_diag) (
736749
}
737750
}
738751

739-
// Early out if there was an error processing the string
740-
if ((state.flags & READER_FLAG_ERROR) != 0) {
741-
return false;
742-
}
743-
744-
// If an IPv4 compatible address was specified the rest of the IPv6 collapsing
745-
// rules can be skipped
746-
if ((state.flags & READER_FLAG_IPV4_COMPAT) != 0) {
747-
state.address_full->flags |= IPV6_FLAG_IPV4_COMPAT;
748-
return true;
749-
}
750-
751752
// If there was no abbreviated run all components should be specified
752753
if ((state.flags & READER_FLAG_ZERORUN) == 0) {
753754
if (state.components < IPV6_NUM_COMPONENTS) {
@@ -765,11 +766,11 @@ bool IPV6_API_DEF(ipv6_from_str_diag) (
765766
int32_t move_count = state.components - state.zerorun;
766767
int32_t target = IPV6_NUM_COMPONENTS - move_count;
767768
if (move_count < 0 || move_count > IPV6_NUM_COMPONENTS) {
768-
TRACE("invalid move_count: %d\n", move_count);
769+
IPV6_TRACE("invalid move_count: %d\n", move_count);
769770
return false;
770771
}
771772
if (target < 0 || target + move_count > IPV6_NUM_COMPONENTS) {
772-
TRACE("invalid target location: %d:%d\n", target, move_count);
773+
IPV6_TRACE("invalid target location: %d:%d\n", target, move_count);
773774
return false;
774775
}
775776

@@ -806,7 +807,7 @@ bool IPV6_API_DEF(ipv6_from_str) (
806807
}
807808

808809
#define OUTPUT_TRUNCATED() \
809-
TRACE(" ! buffer truncated at position %u\n", (uint32_t)(wp - out)); \
810+
IPV6_TRACE(" ! buffer truncated at position %u\n", (uint32_t)(wp - out)); \
810811
*out = '\0';
811812

812813
//--------------------------------------------------------------------------------
@@ -833,7 +834,7 @@ char* IPV6_API_DEF(ipv6_to_str) (
833834
if (in->flags & IPV6_FLAG_IPV4_COMPAT) {
834835
const uint32_t host_ipv4 = components[0] << 16 | components[1];
835836
if (in->flags & IPV6_FLAG_HAS_PORT) {
836-
platform_snprintf(token, sizeof(token), "%d.%d.%d.%d:%d",
837+
platform_snprintf(token, sizeof(token), "%d.%d.%d.%d:%d",
837838
(uint8_t)(host_ipv4 >> 24),
838839
(uint8_t)(host_ipv4 >> 16),
839840
(uint8_t)(host_ipv4 >> 8),
@@ -969,39 +970,72 @@ char* IPV6_API_DEF(ipv6_to_str) (
969970
//--------------------------------------------------------------------------------
970971
int32_t IPV6_API_DEF(ipv6_compare) (
971972
const ipv6_address_full_t* a,
972-
const ipv6_address_full_t* b)
973+
const ipv6_address_full_t* b,
974+
uint32_t ignore_flags)
973975
{
974-
int32_t compare;
976+
const uint16_t* a_components;
977+
const uint16_t* b_components;
978+
uint32_t num_components;
979+
980+
// Mask out flags for comparison
981+
uint32_t compare_flags = (IPV6_FLAG_HAS_MASK | IPV6_FLAG_HAS_PORT) & ~ignore_flags;
982+
983+
// Treat any compatibility or embedded address as IPv4
984+
#define HAS_IPV4(flags) (((flags) & (IPV6_FLAG_IPV4_COMPAT|IPV6_FLAG_IPV4_EMBED)) != 0)
985+
986+
// Special case format selection:
987+
//
988+
// Allow comparison between embedded and compatible addresses
989+
//
990+
if (0 != (ignore_flags & (IPV6_FLAG_IPV4_EMBED|IPV6_FLAG_IPV4_COMPAT))
991+
&& (HAS_IPV4(a->flags) || HAS_IPV4(b->flags)))
992+
{
993+
num_components = IPV4_NUM_COMPONENTS;
994+
if (a->flags & IPV6_FLAG_IPV4_COMPAT)
995+
a_components = &a->address.components[0];
996+
else
997+
a_components = &a->address.components[IPV4_EMBED_INDEX];
998+
if (b->flags & IPV6_FLAG_IPV4_COMPAT)
999+
b_components = &b->address.components[0];
1000+
else
1001+
b_components = &b->address.components[IPV4_EMBED_INDEX];
1002+
}
1003+
else {
1004+
compare_flags |= (IPV6_FLAG_IPV4_EMBED | IPV6_FLAG_IPV4_COMPAT);
1005+
num_components = IPV6_NUM_COMPONENTS;
1006+
a_components = &a->address.components[0];
1007+
b_components = &b->address.components[0];
1008+
}
9751009

976-
// First compare the components in order
977-
for (uint32_t i = 0; i < IPV6_NUM_COMPONENTS; ++i) {
978-
compare = a->address.components[i] - b->address.components[i];
979-
if (compare != 0) {
980-
return compare;
981-
}
1010+
// Make sure desired features are the same
1011+
if ((a->flags & compare_flags) != (b->flags & compare_flags)) {
1012+
return IPV6_COMPARE_FORMAT_MISMATCH;
9821013
}
9831014

984-
// Make sure features are the same
985-
compare = a->flags - b->flags;
986-
if (compare != 0) {
987-
return compare;
1015+
// Compare the desired number of components in order
1016+
for (uint32_t i = 0; i < num_components; ++i) {
1017+
if (a_components[i] != b_components[i]) {
1018+
return IPV6_COMPARE_ADDRESS_MISMATCH;
1019+
}
9881020
}
9891021

9901022
// Compare port
991-
if (a->flags & IPV6_FLAG_HAS_PORT) {
992-
compare = a->port - b->port;
993-
if (compare != 0) {
994-
return compare;
1023+
if ( ((ignore_flags & IPV6_FLAG_HAS_PORT) == 0)
1024+
&& ((a->flags & IPV6_FLAG_HAS_PORT) || (b->flags & IPV6_FLAG_HAS_PORT)))
1025+
{
1026+
if (a->port != b->port) {
1027+
return IPV6_COMPARE_PORT_MISMATCH;
9951028
}
9961029
}
9971030

9981031
// Compare mask
999-
if (a->flags & IPV6_FLAG_HAS_MASK) {
1000-
compare = a->mask - b->mask;
1001-
if (compare != 0) {
1002-
return compare;
1032+
if ( ((ignore_flags & IPV6_FLAG_HAS_MASK) == 0)
1033+
&& ((a->flags & IPV6_FLAG_HAS_MASK) || (b->flags & IPV6_FLAG_HAS_MASK)))
1034+
{
1035+
if (a->mask != b->mask) {
1036+
return IPV6_COMPARE_MASK_MISMATCH;
10031037
}
10041038
}
10051039

1006-
return 0;
1040+
return IPV6_COMPARE_OK;
10071041
}

ipv6.h

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@
2626
// - Careful use of strings and pointers
2727
// - Comprehensive positive and negative tests
2828
//
29+
//
30+
// # IPv4 Compatibility Mode
31+
//
32+
// See test.c: test_api_use_loopback_const
33+
// 127.111.2.1
34+
// uint16_t components[IPV6_NUM_COMPONENTS] = {
35+
// 0x7f6f,
36+
// 0x0201 }
37+
// - Addresses can be constructed directly in code and support the full two-way functionality
38+
//
39+
// # Building / Debugging
40+
//
41+
// Full tracing can be enabled by running cmake -DPARSE_TRACE=1
42+
//
2943

3044
#include <stddef.h>
3145
#include <stdint.h>
@@ -49,8 +63,8 @@ extern const uint32_t IPV6_STRING_SIZE;
4963
typedef enum {
5064
IPV6_FLAG_HAS_PORT = 0x00000001, // the address specifies a port setting
5165
IPV6_FLAG_HAS_MASK = 0x00000002, // the address specifies a CIDR mask
52-
IPV6_FLAG_IPV4_EMBED = 0x00000002, // the address has an embedded IPv4 address in the last 32bits
53-
IPV6_FLAG_IPV4_COMPAT = 0x00000004, // the address is IPv4 compatible (1.2.3.4:5555)
66+
IPV6_FLAG_IPV4_EMBED = 0x00000004, // the address has an embedded IPv4 address in the last 32bits
67+
IPV6_FLAG_IPV4_COMPAT = 0x00000008, // the address is IPv4 compatible (1.2.3.4:5555)
5468
} ipv6_flag_t;
5569
// ~~~~
5670

@@ -65,6 +79,8 @@ typedef enum {
6579
//
6680
// ~~~~
6781
#define IPV6_NUM_COMPONENTS 8
82+
#define IPV4_NUM_COMPONENTS 2
83+
#define IPV4_EMBED_INDEX 6
6884
typedef struct {
6985
uint16_t components[IPV6_NUM_COMPONENTS];
7086
} ipv6_address_t;
@@ -90,6 +106,22 @@ typedef struct {
90106
} ipv6_address_full_t;
91107
// ~~~~
92108

109+
110+
// *ipv6_compare_t*
111+
// ===
112+
//
113+
// Result of ipv6_compare of two addresses
114+
//
115+
// ~~~~
116+
typedef enum {
117+
IPV6_COMPARE_OK = 0,
118+
IPV6_COMPARE_FORMAT_MISMATCH, // address differ in their
119+
IPV6_COMPARE_MASK_MISMATCH, // the CIDR mask does not match
120+
IPV6_COMPARE_PORT_MISMATCH, // the port does not match
121+
IPV6_COMPARE_ADDRESS_MISMATCH, // address components do not match
122+
} ipv6_compare_t;
123+
// ~~~~
124+
93125
// *ipv6_diag_event_t*
94126
// ===
95127
//
@@ -197,12 +229,20 @@ char* IPV6_API_DECL(ipv6_to_str) (
197229
// *ipv6_compare*
198230
// ===
199231
//
200-
// Compare two addresses, 0 if equal, 1 if a greater, -1 if a lesser
232+
// Compare two addresses, 0 if equal else ipv6_compare_result_t.
233+
//
234+
// Use IPV6_FLAG_HAS_MASK, IPV6_FLAG_HAS_PORT in ignore_flags to
235+
// ignore mask or port in comparisons.
236+
//
237+
// IPv4 embed and IPv4 compatible addresses will be compared as
238+
// equal if IPV6_FLAG_IPV4_EMBED or IPV6_FLAG_IPV4_COMPAT
239+
// is passed in ignore_flags.
201240
//
202241
// ~~~~
203242
int32_t IPV6_API_DECL(ipv6_compare) (
204243
const ipv6_address_full_t* a,
205-
const ipv6_address_full_t* b);
244+
const ipv6_address_full_t* b,
245+
uint32_t ignore_flags);
206246
// ~~~~
207247

208248
#ifdef __cplusplus

0 commit comments

Comments
 (0)