Feature #4222
closedIPv6 UDP multicast transport
100%
Description
UDP multicast transport currently only allows IPv4 addressing. On Android platform, IPv4 multicast is poorly implemented, but IPv6 multicast is well supported on recent devices. Enabling IPv6 multicast makes communication easier with WiFi Direct.
Updated by Junxiao Shi over 7 years ago
- Subject changed from Add support for IPv6 UDP multicast faces to IPv6 UDP multicast transport
- Description updated (diff)
- Category set to Faces
- Start date deleted (
08/04/2017) - Estimated time set to 15.00 h
An old email from 2013 stated the reason of not having IPv6 multicast:
UDP multicast is also hard to implement.
ccnd uses two separate ports, because there is no cross-platform way to determine whether a packet is received on multicast address or unicast address. In linux, destination address can be retrieved with recvmsg() syscall with IP_PKTINFO option (this is used in udpsum.c on VNL app server). In FreeBSD, destination address can be retrieved with recvmsg() syscall with IP_RECVDSTADDR option, but this is not implemented for IPv6 ( http://www.freebsd.org/cgi/query-pr.cgi?pr=118880&cat= ).
This email refers to NDNFD, the predecessor of NFD. UDP multicast is of course useful, but we only implemented IPv4 in ccnd's way. IPv6 had issues on FreeBSD as described above, but the bug is now closed.
Updated by Alex Afanasyev over 7 years ago
Yes, I remember that. It just with Android restrictions the situation changed and we need support for IPv6 multicast now.
Updated by Davide Pesavento over 7 years ago
What's the default IPv6 multicast address for NDN?
Updated by Junxiao Shi over 7 years ago
- Related to Task #1747: Request IPv4 and IPv6 multicast addresses from IANA added
Updated by Davide Pesavento over 7 years ago
Another question is: how do we generate a Transport::EndpointId
, which is currently 64-bit wide, from an IPv6 address + port number? Should the transport maintain a mapping, or can we use a hash function?
Updated by Junxiao Shi over 7 years ago
Reply to note-6:
The original design of EndpointId
intends the Transport
to maintain an internal mapping.
However, given the multicast is one hop, if subnet is /64 or smaller, it's probably fine to take the lower 64 bits of the address.
Updated by Md Ashiqur Rahman about 7 years ago
- Assignee set to Md Ashiqur Rahman
Updated by Md Ashiqur Rahman about 7 years ago
I see we currently have the IPv6 udp unicast. So I believe it's going to be quiet similar to this, except for the multicast setting (which is also similar to the IPv4 udp multicast). Please correct me if I'm wrong.
Also, can we use 128 bits for the Transport::EndpointId
? This should support both IPv6 address + port.
Updated by Eric Newberry about 7 years ago
Md Ashiqur Rahman wrote:
Also, can we use 128 bits for the
Transport::EndpointId
? This should support both IPv6 address + port.
How are you planning to differentiate ports with the same address, given that IPv6 addresses are 128 bits by themselves? Additionally, if it turns out that we need more than 128 bits, I don't know if there is a uint256_t field.
Updated by Davide Pesavento about 7 years ago
I see we currently have the IPv6 udp unicast. So I believe it's going to be quiet similar to this
No, unicast and multicast are substantially different in their implementations.
except for the multicast setting (which is also similar to the IPv4 udp multicast).
Yes, ipv6 multicast will be somewhat similar to ipv4 multicast. Some socket options will be different. Maybe something else too.
Additionally, if it turns out that we need more than 128 bits, I don't know if there is a uint256_t field.
There isn't even a standard 128-bit integer type for that matter...
Updated by Md Ashiqur Rahman about 7 years ago
Additionally, if it turns out that we need more than 128 bits, I don't know if there is a uint256_t field.
There isn't even a standard 128-bit integer type for that matter...
How about STL bitset [http://www.cplusplus.com/reference/bitset/bitset/ ]? we could use a 256 bit length of it (for keeping 2n )?
Updated by Davide Pesavento about 7 years ago
Md Ashiqur Rahman wrote:
How about STL bitset [http://www.cplusplus.com/reference/bitset/bitset/ ]? we could use a 256 bit length of it (for keeping 2n )?
That's definitely not the intended use of std::bitset
. Btw, Junxiao already said what to do with EndpointId
in note-7: "The original design of EndpointId
intends the Transport to maintain an internal mapping."
And anyway, this is a very minor issue. Let's try to have a working multicast transport first, figuring out the EndpointId
is secondary.
Updated by Md Ashiqur Rahman about 7 years ago
In the UDP factory, MulticastConfig
has udp::Endpoint group = udp::getDefaultMulticastGroup();
However, getDefaultMulticastGroup()
only returns the IPv4 multicast group. I was thinking if we can pass some parameter to getDefaultMulticastGroup( param )
to return ipv6 multicast group. Any suggestions? (I was thinking of passing the ip address of endpoint, but got stuck here).
I've written separate tests for now for IPv6 multicast just for testing purposes.
Also, for the EndpointId calculation as Junxiao mentioned in note-7, I took the last 64 bits of the IPv6 address.
auto bytes = ep.address().to_v6().to_bytes();
uint64_t subnet64 = 0;
for (int i=8, sB = 54; i<16; i++, sB-=8) {
subnet64 += (bytes[i] << sB);
}
return (static_cast<uint64_t>(ep.port()) << 32) |
static_cast<uint64_t>(subnet64);
Updated by Davide Pesavento about 7 years ago
- Target version set to v0.7
Md Ashiqur Rahman wrote:
In the UDP factory,
MulticastConfig
hasudp::Endpoint group = udp::getDefaultMulticastGroup();
However,getDefaultMulticastGroup()
only returns the IPv4 multicast group. I was thinking if we can pass some parameter togetDefaultMulticastGroup( param )
to return ipv6 multicast group. Any suggestions? (I was thinking of passing the ip address of endpoint, but got stuck here).
Just introduce another function for the v6 endpoint...
Also, for the EndpointId calculation as Junxiao mentioned in note-7, I took the last 64 bits of the IPv6 address.
auto bytes = ep.address().to_v6().to_bytes(); uint64_t subnet64 = 0; for (int i=8, sB = 54; i<16; i++, sB-=8) { subnet64 += (bytes[i] << sB); } return (static_cast<uint64_t>(ep.port()) << 32) | static_cast<uint64_t>(subnet64);
You're combining a 16-bit and a 64-bit quantity into a 64-bit number, which 1) will have collisions, and 2) a bitwise OR is not the proper way of doing it.
Updated by Md Ashiqur Rahman about 7 years ago
Hi, my unit tests for IPv6 multicast passes (Integration test is on it's way). However, in udp-factory, the isCanonical()
check fails for IPv6 address. In the developer guide (page-50, 2nd last paragraph), I found the description for and address to be canonical, but no example for IPv6 and I also couldn't find the method. Thus, in the udp-factory I only checked IPv4 to be canonical and not IPv6. Any suggestions for doing it properly?
For testing purposes as mentioned in (https://redmine.named-data.net/issues/1747#note-5) I used this multicast address:port for IPv6: FF02:0:0:0:0:0:0:1234:65506
Davide Pesavento wrote:
Md Ashiqur Rahman wrote:
Also, for the EndpointId calculation as Junxiao mentioned in note-7, I took the last 64 bits of the IPv6 address.
auto bytes = ep.address().to_v6().to_bytes(); uint64_t subnet64 = 0; for (int i=8, sB = 54; i<16; i++, sB-=8) { subnet64 += (bytes[i] << sB); } return (static_cast<uint64_t>(ep.port()) << 32) | static_cast<uint64_t>(subnet64);
You're combining a 16-bit and a 64-bit quantity into a 64-bit number, which 1) will have collisions, and 2) a bitwise OR is not the proper way of doing it.
I did this in the same way as for IPv4, please suggest possible alternatives:
if (ep.address().is_v4()) {
return (static_cast<uint64_t>(ep.port()) << 32) |
static_cast<uint64_t>(ep.address().to_v4().to_ulong());
}
Updated by Davide Pesavento about 7 years ago
Md Ashiqur Rahman wrote:
Hi, my unit tests for IPv6 multicast passes (Integration test is on it's way). However, in udp-factory, the
isCanonical()
check fails for IPv6 address. In the developer guide (page-50, 2nd last paragraph), I found the description for and address to be canonical, but no example for IPv6 and I also couldn't find the method. Thus, in the udp-factory I only checked IPv4 to be canonical and not IPv6. Any suggestions for doing it properly?
I'm not sure what you're talking about... isCanonical()
is used to check the FaceUri of a faces/create
request, but multicast faces cannot be created via management, so that code path should never be entered.
For testing purposes as mentioned in (https://redmine.named-data.net/issues/1747#note-5) I used this multicast address:port for IPv6: FF02:0:0:0:0:0:0:1234:65506
Why are you changing the port number?
I did this in the same way as for IPv4, please suggest possible alternatives:
There's a substantial difference between v4 and v6. An ipv4 address is 32 bits, add the port number (16 bits) and you still get a quantity that is uniquely representable in 64 bits (the size of EndpointId
). This is no longer true for ipv6 addresses, even if you take only the last 64 bits of the address. This is the whole point behind my question in note-6.
You have to maintain a mapping in the transport, like Junxiao said.
Updated by Md Ashiqur Rahman about 7 years ago
Davide Pesavento wrote:
I'm not sure what you're talking about...
isCanonical()
is used to check the FaceUri of afaces/create
request, but multicast faces cannot be created via management, so that code path should never be entered.
My bad, I accidentally made a V6 test in createFace(). Removed it just now.
For testing purposes as mentioned in (https://redmine.named-data.net/issues/1747#note-5) I used this multicast address:port for IPv6: FF02:0:0:0:0:0:0:1234:65506
Why are you changing the port number?
Just for testing purposes. Fixed it right now.
I did this in the same way as for IPv4, please suggest possible alternatives:
There's a substantial difference between v4 and v6. An ipv4 address is 32 bits, add the port number (16 bits) and you still get a quantity that is uniquely representable in 64 bits (the size of
EndpointId
). This is no longer true for ipv6 addresses, even if you take only the last 64 bits of the address. This is the whole point behind my question in note-6.
You have to maintain a mapping in the transport, like Junxiao said.
I am a bit confused about the internal mapping, so for the moment, I went for the second possible scenario from Junxiao in note-7. I tested only with the lower 64 bits of the v6 address [with port (wrong) and without port]. Need help with the internal mapping.
Updated by Md Ashiqur Rahman about 7 years ago
Hi, I had a working transport with on V6 with hardcoded link local V6 address. As it turns out, link local V6 address access had been recently solved and I updated my local git repo. Now I'm getting the V4 remote address in my V6 transport and having really hard time to figure out. This is the error I get:
1508408603.004100 INFO: [MulticastUdpTransport] [id=0,local=udp6://[fe80::a00:27ff:fe0d:2d40%eth0]:7001,remote=udp4://230.15.19.47:7070] Creating transport
unknown location(0): fatal error in "StaticPropertiesV6<N3nfd4face5tests18IpTransportFixtureINS1_28MulticastUdpTransportFixtureELNS1_13AddressFamilyE2ELNS1_12AddressScopeE2ELNS1_18MulticastInterfaceE1EEE>": memory access violation at address: 0x00000038: no mapping at fault address
../tests/daemon/face/transport-test-common.hpp(47): last checkpoint
Also, I am having trouble with understanding the new multicast-udp-transport.t.cpp implementation. Can someone please help me here?
As for the EndpointId, I am using a Jenkins one-at-a-time hash for converting 128 + 16 = 144 bits (on 18 bytes) to 64 bit EndpointId. Thus full IPv6 address with port can now be used. This got me all unit test passed with my previous local repo (though local IPv6 had to be hardcoded for testing).
boost::array<unsigned char, 18> v6key;
auto bytes = ep.address().to_v6().to_bytes();
for (int i=0; i<16; i++) {
v6key[i] = bytes[i];
}
unsigned short epPort = ep.port();
v6key[16] = (uint8_t) (epPort ^ 255);
v6key[17] = (uint8_t) (epPort >> 8);
// Jenkins one-at-a-time hash
uint64_t hash, i;
for(hash = i = 0; i < 18; i++)
{
hash += v6key[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return static_cast<uint64_t>(hash);
Updated by Md Ashiqur Rahman about 7 years ago
Oh well! solved the issue. The call for initialization was rerouted from test file itself to test-commons which I was not getting. Fixed the issue, working now. 1st patch pushed.
Updated by Davide Pesavento about 7 years ago
- Status changed from New to Code review
- % Done changed from 0 to 80
Updated by Davide Pesavento almost 7 years ago
Design question: when NFD starts, should it create one UDP multicast face for each address on each multicast-capable netif, or at most one udp4 and one udp6 multicast face for each multicast-capable netif (assuming suitable addresses exist on the netif)?
Updated by Junxiao Shi almost 7 years ago
The purpose of multicast faces is to connect to devices within one hop. Having one face is sufficient for this purpose.
Updated by Davide Pesavento almost 7 years ago
- Status changed from Code review to Closed