Skip to content

aix,ibmi: use uv_interface_addresses instead of getifaddrs#4222

Merged
bnoordhuis merged 8 commits into
libuv:v1.xfrom
abmusse:aix-workaround-lack-of-getifaddrs
Dec 22, 2023
Merged

aix,ibmi: use uv_interface_addresses instead of getifaddrs#4222
bnoordhuis merged 8 commits into
libuv:v1.xfrom
abmusse:aix-workaround-lack-of-getifaddrs

Conversation

@abmusse

@abmusse abmusse commented Nov 15, 2023

Copy link
Copy Markdown
Contributor

Use uv_interface_addresses to get the interfaces addresses on AIX and IBM i as ifaddrs is not available on these platforms.
After retrieving the interfaces we can then set the ipv6 link local scope id.

libuv/src/unix/tcp.c

Lines 226 to 248 in 54d8364

static int uv__ipv6_link_local_scope_id(void) {
struct sockaddr_in6* a6;
struct ifaddrs* ifa;
struct ifaddrs* p;
int rv;
if (getifaddrs(&ifa))
return 0;
for (p = ifa; p != NULL; p = p->ifa_next)
if (p->ifa_addr != NULL)
if (uv__is_ipv6_link_local(p->ifa_addr))
break;
rv = 0;
if (p != NULL) {
a6 = (struct sockaddr_in6*) p->ifa_addr;
rv = a6->sin6_scope_id;
}
freeifaddrs(ifa);
return rv;
}

AIX does not have getifaddrs but we do have code in uv_interface_addresses to get the interface addresses.

libuv/src/unix/aix.c

Lines 1116 to 1293 in f01219d

int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
uv_interface_address_t* address;
int sockfd, sock6fd, inet6, i, r, size = 1;
struct ifconf ifc;
struct ifreq *ifr, *p, flg;
struct in6_ifreq if6;
struct sockaddr_dl* sa_addr;
ifc.ifc_req = NULL;
sock6fd = -1;
r = 0;
*count = 0;
*addresses = NULL;
if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) {
r = UV__ERR(errno);
goto cleanup;
}
if (0 > (sock6fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP))) {
r = UV__ERR(errno);
goto cleanup;
}
if (ioctl(sockfd, SIOCGSIZIFCONF, &size) == -1) {
r = UV__ERR(errno);
goto cleanup;
}
ifc.ifc_req = (struct ifreq*)uv__malloc(size);
if (ifc.ifc_req == NULL) {
r = UV_ENOMEM;
goto cleanup;
}
ifc.ifc_len = size;
if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) {
r = UV__ERR(errno);
goto cleanup;
}
#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p))
/* Count all up and running ipv4/ipv6 addresses */
ifr = ifc.ifc_req;
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
p = ifr;
ifr = (struct ifreq*)
((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr));
if (!(p->ifr_addr.sa_family == AF_INET6 ||
p->ifr_addr.sa_family == AF_INET))
continue;
memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) {
r = UV__ERR(errno);
goto cleanup;
}
if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING))
continue;
(*count)++;
}
if (*count == 0)
goto cleanup;
/* Alloc the return interface structs */
*addresses = uv__calloc(*count, sizeof(**addresses));
if (!(*addresses)) {
r = UV_ENOMEM;
goto cleanup;
}
address = *addresses;
ifr = ifc.ifc_req;
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
p = ifr;
ifr = (struct ifreq*)
((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr));
if (!(p->ifr_addr.sa_family == AF_INET6 ||
p->ifr_addr.sa_family == AF_INET))
continue;
inet6 = (p->ifr_addr.sa_family == AF_INET6);
memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1)
goto syserror;
if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING))
continue;
/* All conditions above must match count loop */
address->name = uv__strdup(p->ifr_name);
if (inet6)
address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr);
else
address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr);
if (inet6) {
memset(&if6, 0, sizeof(if6));
r = uv__strscpy(if6.ifr_name, p->ifr_name, sizeof(if6.ifr_name));
if (r == UV_E2BIG)
goto cleanup;
r = 0;
memcpy(&if6.ifr_Addr, &p->ifr_addr, sizeof(if6.ifr_Addr));
if (ioctl(sock6fd, SIOCGIFNETMASK6, &if6) == -1)
goto syserror;
address->netmask.netmask6 = *((struct sockaddr_in6*) &if6.ifr_Addr);
/* Explicitly set family as the ioctl call appears to return it as 0. */
address->netmask.netmask6.sin6_family = AF_INET6;
} else {
if (ioctl(sockfd, SIOCGIFNETMASK, p) == -1)
goto syserror;
address->netmask.netmask4 = *((struct sockaddr_in*) &p->ifr_addr);
/* Explicitly set family as the ioctl call appears to return it as 0. */
address->netmask.netmask4.sin_family = AF_INET;
}
address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0;
address++;
}
/* Fill in physical addresses. */
ifr = ifc.ifc_req;
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
p = ifr;
ifr = (struct ifreq*)
((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr));
if (p->ifr_addr.sa_family != AF_LINK)
continue;
address = *addresses;
for (i = 0; i < *count; i++) {
if (strcmp(address->name, p->ifr_name) == 0) {
sa_addr = (struct sockaddr_dl*) &p->ifr_addr;
memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
}
address++;
}
}
#undef ADDR_SIZE
goto cleanup;
syserror:
uv_free_interface_addresses(*addresses, *count);
*addresses = NULL;
*count = 0;
r = UV_ENOSYS;
cleanup:
if (sockfd != -1)
uv__close(sockfd);
if (sock6fd != -1)
uv__close(sock6fd);
uv__free(ifc.ifc_req);
return r;
}
void uv_free_interface_addresses(uv_interface_address_t* addresses,
int count) {
int i;
for (i = 0; i < count; ++i) {
uv__free(addresses[i].name);
}
uv__free(addresses);
}

Looks like uv_interface_addresses uses getifaddrs on Linux

libuv/src/unix/linux.c

Lines 1927 to 2012 in f01219d

int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
#ifndef HAVE_IFADDRS_H
*count = 0;
*addresses = NULL;
return UV_ENOSYS;
#else
struct ifaddrs *addrs, *ent;
uv_interface_address_t* address;
int i;
struct sockaddr_ll *sll;
*count = 0;
*addresses = NULL;
if (getifaddrs(&addrs))
return UV__ERR(errno);
/* Count the number of interfaces */
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
continue;
(*count)++;
}
if (*count == 0) {
freeifaddrs(addrs);
return 0;
}
/* Make sure the memory is initiallized to zero using calloc() */
*addresses = uv__calloc(*count, sizeof(**addresses));
if (!(*addresses)) {
freeifaddrs(addrs);
return UV_ENOMEM;
}
address = *addresses;
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
continue;
address->name = uv__strdup(ent->ifa_name);
if (ent->ifa_addr->sa_family == AF_INET6) {
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
} else {
address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr);
}
if (ent->ifa_netmask->sa_family == AF_INET6) {
address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask);
} else {
address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask);
}
address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK);
address++;
}
/* Fill in physical addresses for each interface */
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
continue;
address = *addresses;
for (i = 0; i < (*count); i++) {
size_t namelen = strlen(ent->ifa_name);
/* Alias interface share the same physical address */
if (strncmp(address->name, ent->ifa_name, namelen) == 0 &&
(address->name[namelen] == 0 || address->name[namelen] == ':')) {
sll = (struct sockaddr_ll*)ent->ifa_addr;
memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr));
}
address++;
}
}
freeifaddrs(addrs);
return 0;
#endif
}

@bnoordhuis is there a reason why we shouldn't use uv_interface_addresses in uv__ipv6_link_local_scope_id?

CC
@richardlau

Part of #4117

@bnoordhuis

Copy link
Copy Markdown
Member

is there a reason why we shouldn't use uv_interface_addresses in uv__ipv6_link_local_scope_id?

Yes, unnecessary extra memory allocations. Both getifaddrs() and uv_interface_addresses() malloc.

@abmusse

abmusse commented Nov 15, 2023

Copy link
Copy Markdown
Contributor Author

Ah that makes sense. In that case, maybe we should check if ifaddrs is available if so use else fallback to call uv_interface_addresses? Are there any existing variables we can check if ifaddrs is available?

@abmusse

abmusse commented Nov 15, 2023

Copy link
Copy Markdown
Contributor Author

Ran Test Build on AIX CI:

https://ci.nodejs.org/job/libuv-test-commit-aix/nodes=aix72-ppc64/2275/console

not ok 309 - tcp_connect6_link_local
# exit code 393222
# Output from process `tcp_connect6_link_local`:
# Assertion failed in test/test-tcp-connect6-error.c on line 102: `uv_tcp_connect(&req, &server, (struct sockaddr*) &addr, connect_cb) == 0` (-68 == 0)

68 being returned here looks to map to EADDRNOTAVAIL

FWIW Test build on Linux was successful:

https://ci.nodejs.org/job/libuv-test-commit-linux/nodes=fedora-latest-x64/2372/console

@richardlau

Copy link
Copy Markdown
Member

Ran Test Build on AIX CI:

https://ci.nodejs.org/job/libuv-test-commit-aix/nodes=aix72-ppc64/2275/console

not ok 309 - tcp_connect6_link_local
# exit code 393222
# Output from process `tcp_connect6_link_local`:
# Assertion failed in test/test-tcp-connect6-error.c on line 102: `uv_tcp_connect(&req, &server, (struct sockaddr*) &addr, connect_cb) == 0` (-68 == 0)

68 being returned here looks to map to EADDRNOTAVAIL

FWIW from the same job the only interface on the machine with an IPv6 address is lo0:

# uv_interface_addresses:
#   name: en0
#   internal: 0
#   physical address: 2a:b0:26:58:5b:02
#   address: 10.1.0.6
#   netmask: 255.255.254.0
#   name: en1
#   internal: 0
#   physical address: 2a:b0:26:58:5b:03
#   address: 140.211.9.101
#   netmask: 255.255.255.0
#   name: lo0
#   internal: 1
#   physical address: 00:00:00:00:00:00
#   address: 127.0.0.1
#   netmask: 255.0.0.0
#   name: lo0
#   internal: 1
#   physical address: 00:00:00:00:00:00
#   address: ::1
#   netmask: ffff:ffff:ffff:ffff::

Comment thread src/unix/tcp.c Outdated
This is wip PR to test if uv_interface_addresses could be used instead of getifaddrs in `uv__ipv6_link_local_scope_id`

https://github.com/libuv/libuv/blob/54d8364c2406758b572621af381f1d83e01ae46c/src/unix/tcp.c#L226-L248

AIX does not have getifaddrs but we do have code in `uv_interface_addresses` to get the interface addresses.

https://github.com/libuv/libuv/blob/f01219dfb716ceac9cc7dbc70022a197b20d27b3/src/unix/aix.c#L1116-L1293

Looks like `uv_interface_addresses` uses getifaddrs on Linux

https://github.com/libuv/libuv/blob/f01219dfb716ceac9cc7dbc70022a197b20d27b3/src/unix/linux.c#L1927-L2012

Part of #4117
@abmusse

abmusse commented Nov 17, 2023

Copy link
Copy Markdown
Contributor Author

I found that after my I opened my PR there was another another commit 54d8364 was added in the test code that checks if the link local traffic is supported .

So I re-based this PR and re-ran the AIX CI and got back the following:

https://ci.nodejs.org/job/libuv-test-commit-aix/nodes=aix72-ppc64/2277/console

ok 309 - tcp_connect6_link_local # SKIP IPv6 link-local traffic not supported

So yeah the error I was getting before on AIX in #4222 (comment) looks to be caused by the link local address not being supported on the machine.

Comment thread src/unix/tcp.c
Comment thread src/unix/tcp.c Outdated
Comment thread src/unix/tcp.c Outdated
Comment thread src/unix/tcp.c Outdated
bnoordhuis pushed a commit that referenced this pull request Nov 18, 2023
AIX does not implement ifaddrs and when retrieving the network
interfaces with uv_interface_addresses there was a test failure in
tcp_connect6_link_local.

For now disable ipv6 link local on aix to:

1) fix broken aix build
2) stop blocking libuv upgrade in node

Refs: #4222 (comment)
Refs: nodejs/node#50650
aduh95 pushed a commit to nodejs/node that referenced this pull request Dec 4, 2023
Original commit message:
  aix: disable ipv6 link local (#4229)

  AIX does not implement ifaddrs and when retrieving the network
  interfaces with uv_interface_addresses there was a test failure in
  tcp_connect6_link_local.

  For now disable ipv6 link local on aix to:

  1) fix broken aix build
  2) stop blocking libuv upgrade in node

  Refs: libuv/libuv#4222 (comment)
  Refs: #50650

Refs: libuv/libuv@bfbe4e3
nodejs-github-bot pushed a commit to nodejs/node that referenced this pull request Dec 4, 2023
Original commit message:
  aix: disable ipv6 link local (#4229)

  AIX does not implement ifaddrs and when retrieving the network
  interfaces with uv_interface_addresses there was a test failure in
  tcp_connect6_link_local.

  For now disable ipv6 link local on aix to:

  1) fix broken aix build
  2) stop blocking libuv upgrade in node

  Refs: libuv/libuv#4222 (comment)
  Refs: #50650

Refs: libuv/libuv@bfbe4e3
PR-URL: #50650
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Richard Lau <rlau@redhat.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
abmusse and others added 2 commits December 14, 2023 15:51
Co-authored-by: Ben Noordhuis <info@bnoordhuis.nl>
@abmusse abmusse changed the title wip: use uv_interface_addresses instead of getifaddrs aix,ibmi: use uv_interface_addresses instead of getifaddrs Dec 14, 2023
@abmusse

abmusse commented Dec 14, 2023

Copy link
Copy Markdown
Contributor Author

Test Build @ ce65f1d

AIX: https://ci.nodejs.org/job/libuv-test-commit-aix/nodes=aix72-ppc64/2281/console
IBM i: https://ci.nodejs.org/job/libuv-test-commit-ibmi/nodes=ibmi73-ppc64/1218/console

These are looking good.

I need to rebase on latest v1.x to fixup the IBM i failure: #4197
The AIX CI fails on the known issue: #4231

RafaelGSS pushed a commit to nodejs/node that referenced this pull request Dec 15, 2023
Original commit message:
  aix: disable ipv6 link local (#4229)

  AIX does not implement ifaddrs and when retrieving the network
  interfaces with uv_interface_addresses there was a test failure in
  tcp_connect6_link_local.

  For now disable ipv6 link local on aix to:

  1) fix broken aix build
  2) stop blocking libuv upgrade in node

  Refs: libuv/libuv#4222 (comment)
  Refs: #50650

Refs: libuv/libuv@bfbe4e3
PR-URL: #50650
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Richard Lau <rlau@redhat.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
@abmusse

abmusse commented Dec 20, 2023

Copy link
Copy Markdown
Contributor Author

Latest Test builds:

See: #4222 (comment)

I will now work on re-basing this PR to fix up the conflicts

@abmusse

abmusse commented Dec 21, 2023

Copy link
Copy Markdown
Contributor Author

@abmusse

abmusse commented Dec 21, 2023

Copy link
Copy Markdown
Contributor Author

After review, please squash and merge these commits into 1 commit.

Comment thread src/unix/tcp.c
@abmusse

abmusse commented Dec 22, 2023

Copy link
Copy Markdown
Contributor Author

@bnoordhuis bnoordhuis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thanks!

@bnoordhuis

Copy link
Copy Markdown
Member

One question: the comments and the commit log say "aix and ibmi" but the ifdef guard only checks AIX. Is this PR just for AIX or for both platforms?

@abmusse

abmusse commented Dec 22, 2023

Copy link
Copy Markdown
Contributor Author

One question: the comments and the commit log say "aix and ibmi" but the ifdef guard only checks AIX. Is this PR just for AIX or for both platforms?

Good question, This PR would apply to both platforms because _AIX is also defined in PASE (IBM i)

@bnoordhuis bnoordhuis merged commit 8861a97 into libuv:v1.x Dec 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants