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

unix: fix wrong MAC of uv_interface_address #1375

Closed

Conversation

XadillaX
Copy link
Contributor

@XadillaX XadillaX commented Jun 10, 2017

fix a wrong if in uv_interface_address about MAC.

if (ent->ifa_addr->sa_family != AF_LINK)

against

if (ent->ifa_addr->sa_family == AF_LINK)

Fixes: nodejs/node#13581
Refs: https://github.com/libuv/libuv/blob/v1.9.1/src/unix/freebsd.c#L418-L420
Refs: https://github.com/libuv/libuv/blob/v1.9.1/src/unix/darwin.c#L303-L305
Refs: ...

@XadillaX
Copy link
Contributor Author

XadillaX commented Jun 11, 2017

Anyone to review this PR?

/cc @cjihrig

@santigimeno
Copy link
Member

@XadillaX
Copy link
Contributor Author

@santigimeno it seems failed?

@bnoordhuis
Copy link
Member

This won't fix #829 and nodejs/node#13581 because that was about EUI-64 MAC addresses and it almost certainly can't be the fix for nodejs/node#5629 because the file you changed is only used on the BSDs, not Linux.

That aside, the fix looks okay but but you should be able to avoid duplicating uv__ifaddr_exclude() with some more effort, e.g. by passing a flag that says whether or not to include AF_LINK addresses.

@XadillaX
Copy link
Contributor Author

XadillaX commented Jun 12, 2017

For nodejs/node#13581, I think the wrong if returns the wrong result. Because Node.js 4.x is right which uses the right if.

This PR won't fix other 2 issues, but I think it may fix nodejs/node#13581.

@bnoordhuis
Copy link
Member

The point stands though that bsd-ifaddrs.c is not used on Linux.

@santigimeno
Copy link
Member

@XadillaX
Copy link
Contributor Author

sorry, I will update code later to fix linux.

@XadillaX
Copy link
Contributor Author

@bnoordhuis I've updated the code.

Copy link
Member

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

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

A bit too much code duplication to my liking. Here is what you can do. Change:

static int uv__ifaddr_exclude(struct ifaddrs *ent) {

To:

static int uv__ifaddr_exclude(struct ifaddrs *ent, int skip_pf_packet) {

And update:

if (ent->ifa_addr->sa_family == PF_PACKET) return 1;

To:

if (ent->ifa_addr->sa_family == PF_PACKET) return skip_pf_packet;

And then update the call sites accordingly. Bonus points if you use a descriptive enum instead of a plain int.

@XadillaX
Copy link
Contributor Author

@bnoordhuis But in bsd-ifaddr they are different.

#elif defined(__NetBSD__) || defined(__OpenBSD__)
  if (ent->ifa_addr->sa_family != PF_INET)
    return 1;
#endif

against

ent->ifa_addr->sa_family != AF_LINK

@XadillaX XadillaX force-pushed the fix/uv_interface_addresses_bug branch from c204801 to edd7e40 Compare June 14, 2017 14:41
@@ -31,7 +31,12 @@
#include <net/if_dl.h>
#endif

static int uv__ifaddr_exclude(struct ifaddrs *ent) {
typedef enum {
UV__EXCLUDE_IFPHYS = 0,
Copy link
Contributor

Choose a reason for hiding this comment

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

Two space indentation please.

@XadillaX
Copy link
Contributor Author

@bnoordhuis I've updated the code.

typedef enum {
UV__EXCLUDE_IFPHYS = 0,
UV__EXCLUDE_IFADDR = 1
} uv__exclude_type;
Copy link
Member

Choose a reason for hiding this comment

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

Not wrong but you don't need to explicitly assign values. As well, I wouldn't use a typedef, it obscures the type. You use it in more than one file so you can move it to src/unix/internal.h to avoid the duplication.

@@ -42,12 +47,13 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent) {
* devices. We're not interested in this information.
*/
if (ent->ifa_addr->sa_family == AF_LINK)
return 1;
return !!(int)type;
Copy link
Member

Choose a reason for hiding this comment

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

Just return type; (and consider renaming it to exclude_type.)

#elif defined(__NetBSD__) || defined(__OpenBSD__)
if (ent->ifa_addr->sa_family != PF_INET)
if (type == UV__EXCLUDE_IFADDR && ent->ifa_addr->sa_family != PF_INET)
return 1;
Copy link
Member

Choose a reason for hiding this comment

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

You can drop the check and just return !exclude_type; or return exclude_type == UV__EXCLUDE_IFADDR;.

return 1;
return type == UV__EXCLUDE_IFADDR ? 0 : (ent->ifa_addr->sa_family != AF_LINK);
Copy link
Member

Choose a reason for hiding this comment

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

Libuv house style generally doesn't use ternaries.

@XadillaX XadillaX force-pushed the fix/uv_interface_addresses_bug branch from 0e194c7 to 360accf Compare June 16, 2017 14:03
@XadillaX
Copy link
Contributor Author

XadillaX commented Jun 16, 2017

@bnoordhuis Updated. 👌

@XadillaX
Copy link
Contributor Author

/ping @bnoordhuis

Copy link
Member

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

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

Would it be possible to add some checks to e.g. test/test-platform-output.c?

if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
return 1;
if (ent->ifa_addr == NULL)
return 1;
/*
* Just see whether sa_family equals to AF_LINK or not if exclude_type is
* UV__EXCLUDE_IFPHYS
Copy link
Member

Choose a reason for hiding this comment

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

General style nit: please capitalize and punctuate comments.

* UV__EXCLUDE_IFPHYS
*/
if (exclude_type == UV__EXCLUDE_IFPHYS)
return (ent->ifa_addr->sa_family != AF_LINK);
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
/*
* On BSD getifaddrs returns information related to the raw underlying
Copy link
Member

Choose a reason for hiding this comment

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

Since you're assuming that AF_LINK is defined everywhere (and I believe it is), you could drop the #ifdef and reduce this to:

return (exclude_type == UV__EXCLUDE_IFPHYS) ^ (ent->ifa_addr->sa_family == AF_LINK);

I think?

What about the NetBSD/OpenBSD code path? Should it take exclude_type into account? Or just moved a few lines up, before the return?

Copy link
Contributor Author

@XadillaX XadillaX Jun 19, 2017

Choose a reason for hiding this comment

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

If UV__EXCLUDE_IFPHYS, then only returns (ent->ifa_addr->sa_family != AF_LINK) on any system.

Otherwise, it depends on system. If it's OSX / FreeBSD / DragonFly then it depends on AF_LINK, or it depends on PF_INET.

return 1;
return 0;
return exclude_type;
return !exclude_type;
Copy link
Member

Choose a reason for hiding this comment

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

Likewise.

Copy link
Contributor Author

@XadillaX XadillaX Jun 19, 2017

Choose a reason for hiding this comment

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

https://github.com/libuv/libuv/blob/v1.9.1/src/unix/linux-core.c#L901-L912 against https://github.com/libuv/libuv/blob/v1.9.1/src/unix/linux-core.c#L935-L939

if exclude_type equals UV__EXCLUDE_IFPHYS then return ent->ifa_addr->sa_family == PF_PACKET, otherwise return ent->ifa_addr->sa_family != PF_PACKET.

@XadillaX
Copy link
Contributor Author

I'll add some test cases then.

@XadillaX
Copy link
Contributor Author

/ping @bnoordhuis

Copy link
Member

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

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

LGTM with comments and a question.

printf(" name: %s\n", interfaces[i].name);
printf(" internal: %d\n", interfaces[i].is_internal);
printf(" physical address: ");
printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
sprintf(ether, "%02x:%02x:%02x:%02x:%02x:%02x",
Copy link
Member

Choose a reason for hiding this comment

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

Can you use snprintf() here?

(unsigned char)interfaces[i].phys_addr[0],
(unsigned char)interfaces[i].phys_addr[1],
(unsigned char)interfaces[i].phys_addr[2],
(unsigned char)interfaces[i].phys_addr[3],
(unsigned char)interfaces[i].phys_addr[4],
(unsigned char)interfaces[i].phys_addr[5]);
printf("%s\n", ether);

if (strcmp(ether, "00:00:00:00:00:00") != 0)
Copy link
Member

Choose a reason for hiding this comment

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

Why check if it's all zeroes?


if (interfaces[i].address.address4.sin_family == AF_INET) {
uv_ip4_name(&interfaces[i].address.address4, buffer, sizeof(buffer));
} else if (interfaces[i].address.address4.sin_family == AF_INET6) {
uv_ip6_name(&interfaces[i].address.address6, buffer, sizeof(buffer));
}

printf(" address: %s\n", buffer);
printf(" address: %s\n", buffer);
Copy link
Member

Choose a reason for hiding this comment

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

Whitespace change.

@CurlyMoo
Copy link
Contributor

Tested and confirmed working.

@bnoordhuis
Copy link
Member

bnoordhuis commented Jun 30, 2017

Let's see how the CI fares: https://ci.nodejs.org/view/libuv/job/libuv-test-commit/311/

EDIT: Still needs some work. Lots of red.

@XadillaX
Copy link
Contributor Author

XadillaX commented Jul 1, 2017

Anyone has some better test solution?

@XadillaX
Copy link
Contributor Author

XadillaX commented Jul 6, 2017

@bnoordhuis To be honest, I've no better idea than spawning a ifconfig. But the problem is, not every OS has the same behavior of spawning that.

@santigimeno
Copy link
Member

@XadillaX using ip address should work on Linux. I modified your test locally and executed that command instead of ifconfig and the tests passed on my Linux box.

@XadillaX
Copy link
Contributor Author

XadillaX commented Jul 7, 2017

@santigimeno All kinds of Linux distributions?

By another way, ip address won't work under OSX. And I'm not sure if it's working under BSD, etc.

@mattbrun
Copy link

mattbrun commented Jul 7, 2017

AFAIK ip is repalcing ifconfig and the other network utils - such as route, etc. - in Linux.
So for example ip address show enp3s0f1 in my case gives

2: enp3s0f1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 70:4d:7b:3c:16:b0 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.116/24 brd 192.168.1.255 scope global dynamic enp3s0f1
       valid_lft 2774sec preferred_lft 2774sec
    inet6 fe80::4100:1991:751a:d1aa/64 scope link
       valid_lft forever preferred_lft forever

For now in Linux i've solved the issue shelling synchronously the command cat /sys/class/net/enp3s0f1/address, which returns exactly 70:4d:7b:3c:16:b0 without the need of parsing the output reported above.

@bnoordhuis
Copy link
Member

@libuv/collaborators How about we land this without the test? I don't think it's important enough to agonize endlessly over an unreliable test.

@XadillaX
Copy link
Contributor Author

XadillaX commented Jul 7, 2017

+1 with @bnoordhuis

@santigimeno
Copy link
Member

How about we land this without the test? I don't think it's important enough to agonize endlessly over an unreliable test.

👍

WRT the test, apart from the issue that making it work for every platform may not be easy, I think there's a problem in that the args element in the uv_process_options_t hasn't been properly initialized. By doing something like this the tests pass on most Linuxes and FreeBSD:

diff --git a/test/test-platform-output.c b/test/test-platform-output.c
index 5b94212..770a831 100644
--- a/test/test-platform-output.c
+++ b/test/test-platform-output.c
@@ -70,6 +70,7 @@ TEST_IMPL(platform_output) {
   int i;
   int r;
   int err;
+  char* args[2];
 
   err = uv_get_process_title(buffer, sizeof(buffer));
   ASSERT(err == 0);
@@ -145,9 +146,11 @@ TEST_IMPL(platform_output) {
 #ifdef _WIN32
   options.file = "ipconfig";
 #else
-  options.file = "ifconfig";
+  options.file = "/sbin/ifconfig";
 #endif
-  options.args = 0;
+  args[0] = "/sbin/ifconfig";
+  args[1] = NULL;
+  options.args = args;
   options.flags = 0;
   options.exit_cb = exit_cb;
   r = uv_spawn(uv_default_loop(), &process, &options);

@CurlyMoo
Copy link
Contributor

CurlyMoo commented Jul 7, 2017

Why don't we create a tap device with a self-defined hardware address?
http://backreference.org/2010/03/26/tuntap-interface-tutorial/
https://github.com/LaKabane/libtuntap

That last source code should work cross-platform (although not tested yet).

@libuv/collaborators How about we land this without the test? I don't think it's important enough to agonize endlessly over an unreliable test.

It is important for my goals to have proper mac address resolving, because i'm writing to raw sockets. Using the wrong MAC address disconnects the computer from the network my programs are running on (also how i discovered this bug myself). So, my goal is to use libuv as a shared library and therefor i need certain minimal guarantees.


If we are allowed to create tap devices on the test systems, then this could work well, and i can create a test script for it. What platforms should be supported with linux-core.c?

tap0 10.0.1.1 255.255.0.0 aa:bb:cc:dd:ee:ff

old wrong code:

tap0 10.0.1.1 255.255.0.0 00:00:00:00:a8:bb

@@ -145,6 +145,12 @@ enum {
UV_LOOP_BLOCK_SIGPROF = 1
};

/* flags of excluding ifaddr */
enum {
Copy link
Member

Choose a reason for hiding this comment

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

maybe use a typedef?

Copy link
Contributor Author

@XadillaX XadillaX Jul 9, 2017

Choose a reason for hiding this comment

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

@saghul I used to use typedef. But I removed it later referred by #1375 (comment).

@saghul
Copy link
Member

saghul commented Jul 8, 2017

How about we land this without the test? I don't think it's important enough to agonize endlessly over an unreliable test.

Agreed.

@XadillaX
Copy link
Contributor Author

XadillaX commented Jul 9, 2017

/ping @bnoordhuis I've removed the test.

@@ -847,8 +847,8 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent) {
* devices. We're not interested in this information yet.
*/
if (ent->ifa_addr->sa_family == PF_PACKET)
return 1;
return 0;
return exclude_type;
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a bogus trailing space here.

@XadillaX
Copy link
Contributor Author

/ping @bnoordhuis

@CurlyMoo
Copy link
Contributor

A general question about this function. Why do we care if a network device is running or up? Shouldn't we just be able to discover all available network devices?

 if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))

I would say that we just make these flags parameters of the interface struct so the user can decide how to deal with inactive and down network devices. Or is this a cross-platform thing?

@bnoordhuis
Copy link
Member

@CurlyMoo
Copy link
Contributor

@XadillaX the tests all succeeded (the failing ones weren't due to these changes). Can you finish your PR so it can be merged like squashing and a good commit message.

@XadillaX
Copy link
Contributor Author

@CurlyMoo I will rebase it after going back home, now i'm on a train.

fix a wrong `if` in `uv_interface_address` about MAC.

Fixes: nodejs/node#13581
@XadillaX XadillaX force-pushed the fix/uv_interface_addresses_bug branch from d78164b to f34d7e2 Compare July 17, 2017 02:59
@XadillaX
Copy link
Contributor Author

/ping @CurlyMoo, /cc @bnoordhuis I've rebased the code.

@bnoordhuis
Copy link
Member

bnoordhuis pushed a commit that referenced this pull request Jul 17, 2017
fix a wrong `if` in `uv_interface_address` about MAC.

Fixes: nodejs/node#13581
PR-URL: #1375
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
@bnoordhuis
Copy link
Member

Landed in f1e0fc4, thanks @XadillaX.

@bnoordhuis bnoordhuis closed this Jul 17, 2017
@cwalther
Copy link

It seems like this should also go into master, not just v1.x? (I haven’t tested it, but I was just reading the master code and thinking “how on earth is this supposed to work?”)

@santigimeno
Copy link
Member

No worries, we merge v1.x changes into master periodically, so this change will end up in master too.

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.

os.networkInterfaces() returns wrong MAC addresses
8 participants