Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sys/net/ipv6: Simplify type #14759

Closed
wants to merge 3 commits into from
Closed

sys/net/ipv6: Simplify type #14759

wants to merge 3 commits into from

Conversation

maribu
Copy link
Member

@maribu maribu commented Aug 14, 2020

Contribution description

Make ipv6_addr_t a simple byte array.

Motivation

Using complex types makes interoperability of functions using this type with external code or even with sock_udp_ep_t harder, especially due to the alignment requirements. Using a simple type allows using these functions in the Sock API or in external code (specifically in a pkg I want to update) and thus prevents code duplication and increases maintainability.

Testing procedure

Luckily, @miri64 wrote many unit tests that already helped to fix many bugs this PR previously contained. In addition to automatic tests, testing IPv6 communication would nake sense.

Issues/PRs references

#14737

@maribu maribu added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Aug 14, 2020
@maribu maribu requested a review from kaspar030 as a code owner August 14, 2020 08:23
@benpicco benpicco requested a review from miri64 August 14, 2020 08:45
@maribu maribu added the State: waiting for other PR State: The PR requires another PR to be merged first label Aug 14, 2020
Also update accesses to that type
@maribu maribu removed the State: waiting for other PR State: The PR requires another PR to be merged first label Aug 14, 2020
sys/include/net/ipv6/addr.h Outdated Show resolved Hide resolved
@cgundogan cgundogan added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Area: arduino API Area: Arduino wrapper API and removed CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Aug 15, 2020
@maribu maribu added Area: sys Area: System Area: network Area: Networking and removed Area: arduino API Area: Arduino wrapper API labels Aug 15, 2020
@miri64
Copy link
Member

miri64 commented Aug 26, 2020

I compiled a selection of boards using

BOARD="<board>" BUILD_IN_DOCKER=1 RIOT_CI_BUILD=1 DOCKER_MAKE_ARGS=-j DOCKER_ENVIRONMENT_CMDLINE="-e BINDIRBASE='<ref>-bin'" make -C tests/unittests/ tests-ipv6_addr

For some I needed to remove tests/unittests/Makefile.ci as that file is only for the whole unittests application.

Here are the results:

text	data	bss	dec	BOARD/BINDIRBASE

-1224	154	0	-1070	arduino-mega2560
26168	6304	1363	33835	5d43c5b~1-bin
24944	6458	1363	32765	pr14759-bin

0	0	0	0	iotlab-m3
36264	740	52160	89164	5d43c5b~1-bin
36264	740	52160	89164	pr14759-bin

0	0	0	0	msba2
36264	740	52160	89164	5d43c5b~1-bin
36264	740	52160	89164	pr14759-bin

0	0	0	0	nrf52dk
36264	740	52160	89164	5d43c5b~1-bin
36264	740	52160	89164	pr14759-bin

0	0	0	0	samr21-xpro
36264	740	52160	89164	5d43c5b~1-bin
36264	740	52160	89164	pr14759-bin

-468	304	0	-164	z1
29941	858	1082	31881	5d43c5b~1-bin
29473	1162	1082	31717	pr14759-bin

While for ARM the binary size remains unchanged, there seems to be some weird ROM/RAM trade-off happening for MSP430 and AVR. While the size overall decreases due to big improvements in the ROM usage, the usage in the data segment increases (most likely due to the public constants used by ipv6_addr). Is there maybe a way to lower the size of the data segment again and keep the simplification? Otherwise, I'm uncertain if this should be merged.

@miri64
Copy link
Member

miri64 commented Aug 26, 2020

I observed similar behavior for esp* and RISC-V but since I do not have the toolchains installed locally, the table was not nicely generated, so I omitted those results xP

@maribu
Copy link
Member Author

maribu commented Aug 26, 2020

Is there maybe a way to lower the size of the data segment again

On AVR the solution would be to use PROGMEM. But it is not that trivial to expose this feature in a platform independent way (so that it still uses the advantage of von-Neumann machines) while also not adding boiler plat code.

I'm not sure why this PR trades RAM for ROM for the MSP430.

Otherwise, I'm uncertain if this should be merged.

The reasoning for this PR is not that it improves ROM/RAM consumption, but that the type can be used more consistently. E.g. sock_udp_ep_t a.k.a. struct _sock_tl_ep did not make use of this type - I assume due to its complexity. Even if the struct _sock_tl_ep would be adapted to use ipv6_addr_t for the IPv6 address without simplifying the type, external code in packages won't be compatible. However, the simple byte array type is something I think every IPv6 representation is compatible with. Thus, functions like ipv6_addr_is_multicast() would become reusable.

@miri64
Copy link
Member

miri64 commented Aug 26, 2020

E.g. sock_udp_ep_t a.k.a. struct _sock_tl_ep did not make use of this type - I assume due to its complexity

Actually it does not use that type to decrease dependencies. sock was originally designed to also be used in operating systems other than RIOT (including Linux, early versions of nanocoap where able to run on Linux because of that).

@miri64
Copy link
Member

miri64 commented Aug 26, 2020

The reasoning for this PR is not that it improves ROM/RAM consumption, but that the type can be used more consistently.

I am aware of that. The question I am pondering is, if that re-usability justifies the significant increase in RAM usage.


TEST_ASSERT_EQUAL_INT(0, a.u64[0].u64); /* Don't trust the macro ;) */
TEST_ASSERT_EQUAL_INT(1, byteorder_ntohll(a.u64[1]));
TEST_ASSERT_EQUAL_INT(true, ipv6_addr_is_loopback(&a));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test also tests the function ipv6_addr_is_loopback() so just using memcmp() here somewhat nullifies the purpose of the test.

Comment on lines +693 to +699
static const ipv6_addr_t solicited_node_addr = {
.u8 = {
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00
}
};
*out = solicited_node_addr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the already predefined prefix here?

Suggested change
static const ipv6_addr_t solicited_node_addr = {
.u8 = {
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00
}
};
*out = solicited_node_addr;
*out = ipv6_addr_solicited_node_prefix;

@@ -246,15 +249,14 @@ static size_t _iphc_ipv6_decode(const uint8_t *iphc_hdr,

case IPHC_SAC_SAM_16:
ipv6_addr_set_link_local_prefix(&ipv6_hdr->src);
ipv6_hdr->src.u32[2] = byteorder_htonl(0x000000ff);
ipv6_hdr->src.u16[6] = byteorder_htons(0xfe00);
memcpy(&ipv6_hdr->src.u8[8], addr_fffe, sizeof(addr_fffe));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ipv6_hdr was completely zeroed in l. 166 so it might make more sense to just set the non-zero values here and remove addr_fffe altogether:

Suggested change
memcpy(&ipv6_hdr->src.u8[8], addr_fffe, sizeof(addr_fffe));
ipv6_hdr->src.u8[11] = 0xff;
ipv6_hdr->src.u8[12] = 0xfe;

@@ -276,8 +278,7 @@ static size_t _iphc_ipv6_decode(const uint8_t *iphc_hdr,

case IPHC_SAC_SAM_CTX_16:
assert(ctx != NULL);
ipv6_hdr->src.u32[2] = byteorder_htonl(0x000000ff);
ipv6_hdr->src.u16[6] = byteorder_htons(0xfe00);
memcpy(&ipv6_hdr->src.u8[8], addr_fffe, sizeof(addr_fffe));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Suggested change
memcpy(&ipv6_hdr->src.u8[8], addr_fffe, sizeof(addr_fffe));
ipv6_hdr->src.u8[11] = 0xff;
ipv6_hdr->src.u8[12] = 0xfe;

@@ -330,15 +331,14 @@ static size_t _iphc_ipv6_decode(const uint8_t *iphc_hdr,

case IPHC_M_DAC_DAM_U_16:
ipv6_addr_set_link_local_prefix(&ipv6_hdr->dst);
ipv6_hdr->dst.u32[2] = byteorder_htonl(0x000000ff);
ipv6_hdr->dst.u16[6] = byteorder_htons(0xfe00);
memcpy(&ipv6_hdr->dst.u8[8], addr_fffe, sizeof(addr_fffe));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

Suggested change
memcpy(&ipv6_hdr->dst.u8[8], addr_fffe, sizeof(addr_fffe));
ipv6_hdr->dst.u8[11] = 0xff;
ipv6_hdr->dst.u8[12] = 0xfe;

@@ -355,8 +355,7 @@ static size_t _iphc_ipv6_decode(const uint8_t *iphc_hdr,
break;

case IPHC_M_DAC_DAM_U_CTX_16:
ipv6_hdr->dst.u32[2] = byteorder_htonl(0x000000ff);
ipv6_hdr->dst.u16[6] = byteorder_htons(0xfe00);
memcpy(&ipv6_hdr->dst.u8[8], addr_fffe, sizeof(addr_fffe));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

Suggested change
memcpy(&ipv6_hdr->dst.u8[8], addr_fffe, sizeof(addr_fffe));
ipv6_hdr->dst.u8[11] = 0xff;
ipv6_hdr->dst.u8[12] = 0xfe;

if ((ipv6_hdr->dst.u16[1].u16 == 0) &&
(ipv6_hdr->dst.u32[1].u32 == 0) &&
(ipv6_hdr->dst.u16[4].u16 == 0)) {
if (!memcpy(&ipv6_hdr->dst.u8[2], zeros8, 8)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just from reading the code its not clear where zeros8 comes from, I'd rather compare byte-wise.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you're saying that this

    if (!memcpy(ipv6_hdr->dest.u8[2], zeros8, 8)) {
        /* ... */
    }

is less readable than this

    int is_all_zero = 1;
    for (size_t i = 2; i < 10; i++) {
        if (ipv6_hdr->dst.u8[2] != 0) {
            is_zero = 0;
            break;
        }
    }
    if (!is_all_zero) {
        /* ... */
    }

?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, but both your proposals are more obscure than

if ((ipv6_hdr->dest.u8[2] == 0) && (ipv6_hdr->dest.u8[3] == 0) &&
    (ipv6_hdr->dest.u8[4] == 0) && (ipv6_hdr->dest.u8[5] == 0) &&
    (ipv6_hdr->dest.u8[6] == 0) && (ipv6_hdr->dest.u8[7] == 0) &&
    (ipv6_hdr->dest.u8[8] == 0) && (ipv6_hdr->dest.u8[9] == 0)) {

(I think even you reversed the logic in your second proposal ;-))

(ipv6_hdr->dst.u32[2].u32 == 0) &&
(ipv6_hdr->dst.u16[6].u16 == 0) &&
(ipv6_hdr->dst.u8[14] == 0)) {
!memcmp(&ipv6_hdr->dst.u8[8], zeros8, 7)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

/* 8 bits. The address is derived using 8 bits carried inline */
iphc_hdr[IPHC2_IDX] |= IPHC_M_DAC_DAM_M_8;
iphc_hdr[inline_pos++] = ipv6_hdr->dst.u8[15];
addr_comp = true;
}
/* if multicast address is of format ffXX::XX:XXXX */
else if ((ipv6_hdr->dst.u16[5].u16 == 0) &&
(ipv6_hdr->dst.u8[12] == 0)) {
else if (!memcmp(&ipv6_hdr->dst.u8[10], &zeros8, 3)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Especially here.

@@ -1184,7 +1175,7 @@ static size_t _iphc_ipv6_encode(gnrc_pktsnip_t *pkt,
}
iphc_hdr[inline_pos++] = ipv6_hdr->dst.u8[1];
iphc_hdr[inline_pos++] = ipv6_hdr->dst.u8[2];
memcpy(iphc_hdr + inline_pos, ipv6_hdr->dst.u16 + 6, 4);
memcpy(iphc_hdr + inline_pos, ipv6_hdr->dst.u8 + 12, 4);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
memcpy(iphc_hdr + inline_pos, ipv6_hdr->dst.u8 + 12, 4);
memcpy(iphc_hdr + inline_pos, &ipv6_hdr->dst.u8[12], 4);

_context_overlaps_iid(dst_ctx, &(ipv6_hdr->dst), &iid)) {
/* 0 bits. The address is derived using the link-layer address */
iphc_hdr[IPHC2_IDX] |= IPHC_M_DAC_DAM_U_L2;
addr_comp = true;
}
else if ((byteorder_ntohl(ipv6_hdr->dst.u32[2]) == 0x000000ff) &&
(byteorder_ntohs(ipv6_hdr->dst.u16[6]) == 0xfe00)) {
else if (!memcmp(&ipv6_hdr->dst.u8[8], addr_fffe, sizeof(addr_fffe))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...

@stale
Copy link

stale bot commented Mar 19, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions.

@stale stale bot added the State: stale State: The issue / PR has no activity for >185 days label Mar 19, 2021
@stale stale bot closed this Jun 3, 2021
@maribu maribu deleted the ipv6_addr_t branch January 21, 2023 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: network Area: Networking Area: sys Area: System CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR State: stale State: The issue / PR has no activity for >185 days
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants