Accept IPv4 and IPv6 together in boost::asio - c++

Short and simple question: I am new to boost::asio and I was wondering if it is possible to create a tcp::acceptor listening for both, IPv4 and IPv6 connections together. The tutorials on boost's homepage show something like this:
_acceptor = new tcp::acceptor(_ioService, tcp::endpoint(tcp::v4(), 3456));
where the endpoint is always specified with a specific protocol. Is it not possible to listen for IPv4 and IPv6 on the same port at the same time?

If you create a IPv6 acceptor, it will accept both IPv4 and IPv6 connections if IPV6_V6ONLY socket option is cleared. IPv4 addresses will be presented as IPv6 addresses, in the IPv4-mapped format.
Problems arise mainly around whether IPV6_V6ONLY is available or what the default value is (turned on or off). So I find it's better to set it explicitly to what you want.
Also Windows XP doesn't support the option at all.
So if you want to be compatible across systems, it's recommended to create two sockets, one for v4 and one for v6 setting IPV6_V6ONLY.

Related

using IPv6 and IPv4 at the same application

I have a SNMP Manager written in C++ using the MG Soft SDK. Till now it only sends Get and receives Trap with IPv4. Now a second SNMP Agent shall be connected but this one has IPv6. Is it possible with one application to get a connection to one remote system with IPv4 and to another one with IPv6 at the same time with the same networkcard? or do I need 2 networkcards, one for IPv4 and the other for IPv6?
Yes, it is possible. You just need to open 2 separate connections from within your application - one using IPv4, the other using IPv6 (of course this requires you to implement support for both IPv4 and IPv6 protocols in your application).
It works the same way as if you wanted to open multiple IPv4 connections from within the same application.
For instace, web browsers open separate connections for separate webpages you visit. They, of course, can connect to one web server using IPv4, while at the same time being connected to another web server via IPv6.

Receiving multicast on linux host with multiple interfaces

I have a host running Ubuntu 16.04 connected to one network via the primary wired network interface, and to another network via a USB-to-Ethernet adapter. Using tcpdump, I am able to verify incoming multicast packets on both network interfaces. However, my application does not receive any multicast data from the secondary interface. If I disconnect the cable to the primary interface, and then restart my application, then I do receive the data from the secondary interface. It is only with both interfaces connected that the application does not receive from the secondary interface.
I found a similar problem (Raspberry Pi Zero with USB-to-Ethernet adaptor, fails to respond to mDNS queries). To work out if your problem is the same, does does your app correctly receive multicast traffic whilst running tcpdump at the same time? And does running tcpdump with --no-promiscuous-mode not see multicast traffic?
If your answer is yes to both, then the workaround I've found is simply ip link set eth0 promisc on. I don't know if it is a hardware bug (I'm using a Kontron DM9601 adaptor, ID 0FE6:9700) or a driver bug, but either way, enabling promiscuous mode seems to fix multicast reception for me. Alternatively you could try a better USB-to-ethernet adaptor.
The ip_mreq stucture is passed as the option value for the IP_ADD_MEMBERSHIP socket option to join a multicast group. From the Multicast programming HOWTO from the The Linux Documentation Project:
The first member, imr_multiaddr, holds the group address you want to join. Remember that memberships are also associated with interfaces, not just groups. This is the reason you have to provide a value for the second member: imr_interface. This way, if you are in a multihomed host, you can join the same group in several interfaces. You can always fill this last member with the wildcard address (INADDR_ANY) and then the kernel will deal with the task of choosing the interface.
The IP_MULTICAT_IF socket option is also relevant on a multihomed host to set the outbound interface for multicast data sent via the socket. More information on these socket options, the ip_mreq structure, and the newer ip_mreqn stucture is found here.
For those using Boost on a multihomed host, you will need to use the native handle to join the group on specific interfaces. As of Boost 1.58 running on Ubuntu 16.04, the socket option abstraction ip::multiast::join_group() joins the group on an interface of the kernel's choosing and does not allow the developer to specify an interface. The socket option abstraction ip::multicast::outbound_interface() controls the outbound interface but does not affect which interface the socket receives on.
Here is a code sample to join a group on a specific interface based on the local interface IP address:
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(discovery_ip);
mreq.imr_interface.s_addr = inet_addr(local_interface_ip);
if(setsockopt(socket_.native_handle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))) {
... handle error ...
}

Boost-asio listening to multiple IP Addresses on a single (TCP) acceptor

A boost TCP acceptor can be wired up by using an endpoint constructor that only takes a port number as it's argument, in which case it will listen to all IP addresses/NIC's.
Is it possible to get the acceptor to listen to select IP addresses ? Or will I have to create an acceptor for each IP address I am interested in ? Looking through the documentation I couldn't find any indications of this being a possibility.
I haven't looked at the socket API for a few years, but I guess the API doesn't directly allow this.
there's unbound listening and bound one. unbound means you listen to all NICs, bound - to specific one. There's no possibility to select some of NICs, I think because the same can be achived by dedicated acceptors for each of them

How to support both IPv4 and IPv6 connections

I'm currently working on a UDP socket application and I need to build in support so that IPV4 and IPV6 connections can send packets to a server.
I was hoping that someone could help me out and point me in the right direction; the majority of the documentation that I found was not complete. It'd also be helpful if you could point out any differences between Winsock and BSD sockets.
Thanks in advance!
The best approach is to create an IPv6 server socket that can also accept IPv4 connections. To do so, create a regular IPv6 socket, turn off the socket option IPV6_V6ONLY, bind it to the "any" address, and start receiving. IPv4 addresses will be presented as IPv6 addresses, in the IPv4-mapped format.
The major difference across systems is whether IPV6_V6ONLY is a) available, and b) turned on or off by default. It is turned off by default on Linux (i.e. allowing dual-stack sockets without setsockopt), and is turned on on most other systems.
In addition, the IPv6 stack on Windows XP doesn't support that option. In these cases, you will need to create two separate server sockets, and place them into select or into multiple threads.
The socket API is governed by IETF RFCs and should be the same on all platforms including windows WRT IPv6.
For IPv4/IPv6 applications it's ALL about getaddrinfo() and getnameinfo(). getaddrinfo is a genius - looks at DNS, port names and capabilities of the client to resolve the eternal question of “can I use IPv4, IPv6 or both to reach a particular destination?” Or if you're going the dual-stack route and want it to return IPv4-mapped IPv6 addresses, it will do that too.
It provides a direct sockaddr * structure that can be plugged into bind(), recvfrom(), sendto() and the address family for socket()… In many cases this means no messy sockaddr_in(6) structures to fill out and deal with.
For UDP implementations I would be careful about setting dual-stack sockets or, more generally, binding to all interfaces (INADDR_ANY). The classic issue is that, when addresses are not locked down (see bind()) to specific interfaces and the system has multiple interfaces requests, responses may transit from different addresses for computers with multiple addresses based on the whims of the OS routing table, confusing application protocols—especially any systems with authentication requirements.
For UDP implementations where this is not a problem, or TCP, dual stack sockets can save a lot of time when IPv*-enabling your system. One must be careful to not rely entirely on dual-stack where it`s not absolutely necessary as there are no shortage of reasonable platforms (Old Linux, BSD, Windows 2003) deployed with IPv6 stacks not capable of dual stack sockets.
I've been playing with this under Windows and it actually does appear to be a security issue there, if you bind to the loopback address then the IPv6 socket is correctly bound to [::1] but the mapped IPv4 socket is bound to INADDR_ANY, so your (supposedly) safely local-only app is actually exposed to the world.
The RFCs don't really specify the existence of the IPV6_V6ONLY socket option, but, if it is absent, the RFCs are pretty clear that the implementation should be as though that option is FALSE.
Where the option is present, I would argue that it should default FALSE, but, for reasons passing understanding, BSD and Windows implementations default to TRUE. There is a bizarre claim that this is a security concern because an unknowing IPv6 programmer could bind thinking they were binding only to IN6ADDR_ANY for only IPv6 and accidentally accept an IPv4 connection causing a security problem. I think this is both far-fetched and absurd in addition to a surprise to anyone expecting an RFC-compliant implementation.
In the case of Windows, non-compiance won't usually be a surprise. In the case of BSD, this is unfortunate at best.
As Craig M. Brandenburg observes, getaddrinfo does all the heavy lifting to make dual IPv4/IPv6 possible. I have an experimental server and client on my localhost. I use this in the server:
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
...
The client can then connect to the server using any kind of address:
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
host_port = "4950"; // whatever
// All of these work.
host_ip = "127.0.0.1"; // Pure IPv4 address
host_ip = "::ffff:127.0.0.1"; // IPv4 address expressed as IPv6
host_ip = "::1"; // Pure IPv6 address
host_ip = "localhost"; // Domain name
int rv = getaddrinfo(host_ip, host_port, &hints, &result);
...

is ipv6 backward compatible with ipv4?

I've got a little udp example program written using ipv4. If I alter the code to ipv6 would I still be able to communicate with anyone using the listener with an ipv4 address? I was looking at porting examples at
http://ou800doc.caldera.com/en/SDK_netapi/sockC.PortIPv4appIPv6.html
I'm not sure if simply altering the code would ensure that it worked or if I'd have to write it in duel-stack mode.
Yes and no... IPv6 does contain completely different addressing, so you'll have to recode your app to use the alternative headers and structure sizes.
However, the IPv4 address range is available within IPv6, the syntax is to add two colons before the standard address (eg ::10.11.12.13). You can also embed IPv4 addresses within IPv6 packets.
Not without the assistance of an IPv4/IPv6 gateway in the network, and even then communication will be limited by the typical problems introduced by network address translating gateways. The traditional advice for programmers facing decisions like this is to recommend supporting both IPv4 and IPv6 at the same time.
IPv4 and IPv6 are inherently incompatible with each other.
A few basic reasons:
the address space is completely different (IPv6 has 128 bit addresses, IPv4 has 32 bit addresses)
the protocol header of IPv6 looks nothing like the protocol header of IPv4. if you try to parse an IPv6 packet as IPv4 you'll get nonsense.
The obvious result of these is that if you open an IPv6 socket you can't listen to it using an IPv4 socket.