ZeroMQ is not working over unicast IPv6 - c++

I'm in a trouble with ZeroMQ and IPv6. When I use a connection through IPv4 or if I use "tcp://[::1]:5558", it connects like a charm. However, if I use the server full IPv6 address (on my local host or remote host) it connects, but don't get data on the other endpoint.
Here is my code sample:
client.cpp
#include <stdio.h>
#include <zmq.h>
int main(int argc, char** argv)
{
void* context = zmq_ctx_new();
void* socket = zmq_socket(context, ZMQ_SUB);
int ipv6 = 1;
zmq_setsockopt(socket, ZMQ_IPV6, &ipv6, 4);
zmq_connect(socket, "tcp://[fe80::52e5:49ff:fef8:dbc6]:5558");
//zmq_connect(socket, "tcp://[::1]:5558");
zmq_setsockopt(socket, ZMQ_SUBSCRIBE, "pub", 3);
zmq_msg_t message;
do {
zmq_msg_init (&message);
zmq_msg_recv (&message, socket, 0);
printf("%s\n", (char*)zmq_msg_data(&message));
zmq_msg_close(&message);
} while (zmq_msg_more(&message));
}
And server.cpp
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <zmq.h>
int main(int argc, char**argv)
{
void* context = zmq_ctx_new();
void* publisher = zmq_socket(context, ZMQ_PUB);
int ipv6 = 1;
zmq_setsockopt(publisher, ZMQ_IPV6, &ipv6, sizeof(int));
zmq_bind(publisher, "tcp://*:5558");
char buffer[4] = "pub";
unsigned tries = 0;
while(tries < 10) {
zmq_send(publisher, &buffer, strlen(buffer), 0);
tries++;
sleep(1);
}
return 0;
}
I'm using ZeroMQ 4.0.0 RC, but it is also happening on 3.2. I'm on linux (slackware) and installed it from sources. I also tested using a java server using jeroMQ and the problem is the same. I did another test using a REQ-REP connection and the problem is the same.
Thanks in advance for any help.

fe80* addresses are link local, you must specify the local hosts link name: e.g. fe80...:1%eth1
fe80::/10 — Addresses in the link-local prefix are only valid and
unique on a single link. Within this prefix only one subnet is
allocated (54 zero bits), yielding an effective format of fe80::/64.
The least significant 64 bits are usually chosen as the interface
hardware address constructed in modified EUI-64 format. A link-local
address is required on every IPv6-enabled interface—in other words,
applications may rely on the existence of a link-local address even
when there is no IPv6 routing. These addresses are comparable to the
auto-configuration addresses 169.254.0.0/16 of IPv4.
http://en.wikipedia.org/wiki/IPv6_address#Local_addresses

Related

ZeroMQ: Using EPGM transport

I am trying to use the epgm transport in my simple publisher-subscriber program, but I am unable to do so. From what I understand, I am unable to supply a correct address string in bind and connect statements.
The publisher and subscriber can be running on same or different machines.
Below is the required code which usees tcp transport and works correctly. It uses cppzmq: https://github.com/zeromq/cppzmq.
Publisher code:
#include <zmq.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
int main () {
zmq::context_t context (1);
zmq::socket_t publisher (context, ZMQ_PUB);
publisher.bind("tcp://10.1.1.8:5000");
int i = 0;
while (1) {
int topic = 101;
zmq::message_t message(50);
snprintf ((char *) message.data(), 50, "%03d %10d %10d", topic, i, i);
//fprintf(stderr, "message: %s\n", (char *) message.data());
publisher.send(message);
++i;
}
return 0;
}
Subscriber code:
#include <zmq.hpp>
#include <iostream>
#include <sstream>
#include <unistd.h>
#include <cassert>
int main (int argc, char *argv[]) {
zmq::context_t context (1);
zmq::socket_t subscriber (context, ZMQ_SUB);
subscriber.connect("tcp://10.1.1.8:5000");
const char *filter = "101 ";
subscriber.setsockopt(ZMQ_SUBSCRIBE, filter, strlen (filter));
zmq::message_t tp;
int maxx = 0;
for (int i = 0; i < 1000; ++i) {
zmq::message_t update;
int topic, a, b;
if(subscriber.krecv(&update, ZMQ_DONTWAIT)) {
//fprintf(stderr, "size of data received: %zd\n", sizeof(update.data()));
std::istringstream iss(static_cast<char*>(update.data()));
iss >> topic >> a >> b;
assert(a == b);
}
else {
--i;
}
maxx = a > maxx ? a : maxx;
}
fprintf(stderr, "maxx = %d\n", maxx);
return 0;
}
krecv method that is used in subscriber:
inline bool krecv (message_t *msg_, int flags_ = 0) {
int nbytes = zmq_msg_recv (&(msg_->msg), ptr, flags_);
if (nbytes >= 0)
return true;
if (zmq_errno () == EAGAIN)
return false;
return false;
}
I tried changing the bind statement in publisher to following:
publisher.bind("epgm://10.1.1.8:5000");
publisher.bind("epgm://224.1.1.1:5000");
publisher.bind("epgm://eth0;224.1.1.1:5000");
publisher.bind("epgm://10.1.1.8;224.1.1.1:5000");
publisher.bind("epgm://localhost:5000");
For all 5 cases, the program crashes with Assertion failed: false (src/pgm_socket.cpp:165). For the 5th case (epgm://localhost:5000), I also receive following warnings along with the crash:
Warn: Interface lo reports as a loopback device.
Warn: Interface lo reports as a non-multicast capable device.
How can I resolve this issue? I am guessing that the address change will be same in both publisher and subscriber?
I am using libpgm 5.2.122 with zeromq-4.1.3.
Note that the machine has following interfaces:
eth0 (Ethernet) -- inet addr:10.1.1.8
ib0 (InfiniBand) -- inet addr:10.1.3.8
lo (Local Loopback) -- inet addr:127.0.0.1
Try a 239.0.0.0/8 IP in your bind:
publisher.bind("epgm://;239.0.0.1:5000");
Wikipedia:
The 239.0.0.0/8 range is assigned by RFC 2365 for private use within an organization. From the RFC, packets destined to administratively scoped IPv4 multicast addresses do not cross administratively defined organizational boundaries, and administratively scoped IPv4 multicast addresses are locally assigned and do not have to be globally unique.
I have used epgm with zeromq on linux and it's tricky to configure correctly
Assuming you are using linux the read below, if not I have no experience with windows so disregard:
epgm does not work with the loopback adapter on linux so forget that.
Your eth0 should work. Is MCAST definitely enabled (check ifconfg)?
Port usage, is the port already in use?
I link zeromq with openpgm and there are some rather special differences in the way port reuse works between different linux kernels.
I added some code to the openpgm repo to fix my issues with rhel7
https://github.com/steve-o/openpgm/pull/52
James

recvfrom works with INADDR_ANY, but specifying certain interface doesn't work

I wrote a program that join source specific multicast group and receive udp multicast packets:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <net/if.h>
typedef unsigned int UINT32;
int join_ssm_group(int s, UINT32 group, UINT32 source, UINT32 inter) {
struct ip_mreq_source imr;
imr.imr_multiaddr.s_addr = group;
imr.imr_sourceaddr.s_addr = source;
imr.imr_interface.s_addr = inter;
return setsockopt(s, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *) &imr, sizeof(imr));
}
UINT32 LISTEN_INTERFACE = inet_addr("10.10.1.2");
int main(int argc, char *argv[]) {
if (argc<3) {
printf(" Use: %s <group> <source> <port>", argv[0]);
return 1;
}
// Make socket
int sd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
struct sockaddr_in Sender;
socklen_t SenderAddrSize = sizeof( Sender );
struct sockaddr_in binda;
// Bind it to listen appropriate UDP port
binda.sin_family = AF_INET;
binda.sin_port = htons( atoi(argv[3]));
= INADDR_ANY;
// binda.sin_addr.s_addr = LISTEN_INTERFACE;
bind(sd,(struct sockaddr*)&binda, sizeof(binda));
// Join to group
join_ssm_group( sd, inet_addr(argv[1]),
inet_addr(argv[2]),
INADDR_ANY );
char buf[65536];
UINT32 seq;
while(1) {
printf("try receive\n");
int res=recvfrom(sd,(char*)buf,sizeof(buf),0, (struct sockaddr *)& Sender, &SenderAddrSize);
printf("received\n");
seq = *(UINT32*)buf;
printf("scr=:%12s;\tseq=%6d;\tlen=%4d\n", inet_ntoa(Sender.sin_addr), seq, res);
}
return 0;
}
It works fine but note that I'm using binda.sin_addr.s_addr = INADDR_ANY;. netstat shows this:
netstat -a | grep 16002
udp 0 0 0.0.0.0:16002 0.0.0.0:*
When I change it to binda.sin_addr.s_addr = LISTEN_INTERFACE; program stops working - it can not recieve packets, it hangs in recvfrom. netstat shows this:
netstat -a | grep 16002
udp 0 0 localhost.localdo:16002 0.0.0.0:*
In both cases tcpdump shows that data is online, so the problem is that I can not receive data on the specific interface, only on ALL interfaces. I'm using RHEL 7, teaming, and LISTEN_INTERFACE is the IP of the corresponding VLAN. Why my code doesn't work and how to troubleshoot it? I do not want to use INADDR_ANY for performance reasons - listening ALL interfaces would be more expensive than listeining certain interface.
upd passing LISTEN_INTERFACE to both join_ssm_group and and binda.sin_addr.s_addr doesn't work too. BTW similar Windows version of such code works on the same PC under Windows Server 2008 R2, but it doesn't work in RHEL 7. I guess I should check these:
if RHEL 7 receives data on the requreid interface on the required port (answer is Yes, proved by tcpdump)
if socket is listening on the required interface on the required port (answer is Yes, proved by netstat?)
if both answers above are Yes then how is it possible that call to recvfrom doesn't receive data?
Well probably this question more about RHEL 7 now, than about c++.
When you join the multicast group you need to specify the same interface that you are listening on, or join it via all interfaces in a loop.
However listening on all interfaces is the norm. It is not 'slow', and it is a 'good idea', unless you have a specific reason to restrict who can connect.

Creating a basic C/C++ TCP socket writer

Below is the following basic socket code I came up with:
//General includes:
#include <iostream>
#include <stdio.h>
#include <string>
//Network related includes:
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
//Target host details:
#define PORT 1234
#define HOST "74.74.74.74"
using namespace std;
//Function prototypes:
string MessageFormat(int, char**);
void MessageSend(string);
int main(int argc, char *argv[])
{
//Parse arguments and format message:
string message = MessageFormat(argc, argv);
//Send the message out:
MessageSend(message);
return 0;
}
string MessageFormat(int argc, char *argv[])
{
//Massage the command line parameters
// into my desired payload format.
return message;
}
void MessageSend(string message)
{
int sd, ret;
struct sockaddr_in server;
struct in_addr ipv4addr;
struct hostent *hp;
sd = socket(AF_INET,SOCK_DGRAM,0);
server.sin_family = AF_INET;
inet_pton(AF_INET, HOST, &ipv4addr);
hp = gethostbyaddr(&ipv4addr, sizeof ipv4addr, AF_INET);
//hp = gethostbyname(HOST);
bcopy(hp->h_addr, &(server.sin_addr.s_addr), hp->h_length);
server.sin_port = htons(PORT);
connect(sd, (const sockaddr *)&server, sizeof(server));
send(sd, (char *)message.c_str(), strlen((char *)message.c_str()), 0);
}
This is quite basic, and does in fact work. HOWEVER, it's sending UDP packets instead of TCP packets, so the target host expecting TCP rejects these. Also, by inspecting connect/send values and watching my interfaces with ngrep I can 100% verify the packet is going out, so that's not the issue.
I'm only interested in modifying what I have, not creating a full featured server with boost asio. How can I tweak this so that it operates in terms of TCP instead of UDP?
Following are changes you need to make to transfer data via TCP
While creating socket pass correct parameters .In above example you passed SOCK_DGRAM instead pass SOCK_STREAM.
After binding server should go into listen mode (check the manual page of listen)
while Client Side should connect after socket creation.
Then accept in server side after listen.
Final Read and write to transfer data
Diagram attached will give you a clear picture of TCP connection
You can check manual pages for detailed info on all functions or refer beej's guide for socket programming ( use this link )
Replace SOCK_DGRAM with SOCK_STREAM.
Also, read the manual or get a good book.

To get IP Address from interface name in Windows

I like to know, winapi from which i can get ipaddress using interface name. The Linux version of which is as below.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <unistd.h>
#include <arpa/inet.h>
int main()
{
int fd;
struct ifreq ifr;
char iface[] = "eth0";
fd = socket(AF_INET, SOCK_DGRAM, 0);
//Type of address to retrieve - IPv4 IP address
ifr.ifr_addr.sa_family = AF_INET;
//Copy the interface name in the ifreq structure
strncpy(ifr.ifr_name , iface , IFNAMSIZ-1);
ioctl(fd, SIOCGIFADDR, &ifr);
close(fd);
//display result
printf("%s - %s\n" , iface , inet_ntoa(( (struct sockaddr_in *)&ifr.ifr_addr )->sin_addr) );
return 0;
}
I am looking similar functionality ( as code above) but for windows in C++.
may be you can tweak it to your needs...
http://kodeyard.blogspot.in/2009/09/get-ip-address-in-cwindows.html
See sample of GetAdaptersAddresses function. FriendlyName and FirstUnicastAddress are fields you need.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365915%28v=vs.85%29.aspx
See also: Get network interface name from IPv4 address
SIGAR is a cross-platform library for getting system info (CPU, RAM, DISK, and Network). It will allow you to list all network interfaces on Mac / Windows / Linux and get any other info you may need.
http://support.hyperic.com/display/SIGAR/Home
If you don't want to use the whole library, I am sure you could inspect the code to find just the piece you need.
You have to initialize WinSock first, and only then use gethostname, gethostbyname and inet_ntoa functions. The following link will help you.

how to scan wireless network and display the list of all computers and devices connected

I need to build a tool (c++) very much like "Wireless Network Watcher" which is a small utility that scans your wireless network and displays the list of all computers and devices that are currently connected to your network.
here's the existing tool http://www.nirsoft.net/utils/wireless_network_watcher.html
I need to know what are the win32 sdk functions to use to build this kind of functionality: scan the wireless network I am connected to and display all computers and devices connected to it.
ok, it seems is done this way: first sent an ARP request packet to each possible IP address in the network (you calculate them based on the net mask and the interface ip), for this step you can use SendARP functions. Then you have to call getnameinfo for each IP that responded previously, or you can send an NetBios request packet (port 137) to retreive the name of the device, if it has one, or know how to respond to that request.
for some networks this can take awhile (very long time).
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/ip_icmp.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
// Define the Packet Constants
// ping packet size
#define PING_PKT_S 64
// Automatic port number
#define PORT_NO 0
// Automatic port number
#define PING_SLEEP_RATE 1000000
// Gives the timeout delay for receiving packets
// in seconds
#define RECV_TIMEOUT 1
// Performs a DNS lookup
char* dns_lookup(char* addr_host, struct sockaddr_in* addr_con) {
// printf("\nResolving DNS..\n");
struct hostent* host_entity;
char* ip = (char*)malloc(NI_MAXHOST * sizeof(char));
int i;
if ((host_entity = gethostbyname(addr_host)) == NULL) {
// No ip found for hostname
return NULL;
}
// filling up address structure
strcpy(ip, inet_ntoa(*(struct in_addr*)host_entity->h_addr));
(*addr_con).sin_family = host_entity->h_addrtype;
(*addr_con).sin_port = htons(PORT_NO);
(*addr_con).sin_addr.s_addr = *(long*)host_entity->h_addr;
return ip;
}
// Resolves the reverse lookup of the hostname
char* reverse_dns_lookup(char* ip_addr) {
struct sockaddr_in temp_addr;
socklen_t len;
char buf[NI_MAXHOST], *ret_buf;
temp_addr.sin_family = AF_INET;
temp_addr.sin_addr.s_addr = inet_addr(ip_addr);
len = sizeof(struct sockaddr_in);
if (getnameinfo((struct sockaddr*)&temp_addr, len, buf, sizeof(buf), NULL, 0,
NI_NAMEREQD)) {
// printf("Could not resolve reverse lookup of hostname\n");
return NULL;
}
ret_buf = (char*)malloc((strlen(buf) + 1) * sizeof(char));
strcpy(ret_buf, buf);
return ret_buf;
}
// Driver Code
int main(int argc, char* argv[]) {
int sockfd;
char *ip_addr, *reverse_hostname;
struct sockaddr_in addr_con;
int addrlen = sizeof(addr_con);
char net_buf[NI_MAXHOST];
int i = 0;
for (int i = 1; i < 255; ++i) {
char ip[80];
sprintf(ip, "192.168.2.%d", i);
ip_addr = dns_lookup(ip, &addr_con);
if (ip_addr == NULL) {
// printf("\nDNS lookup failed! Could not resolve hostname!\n");
continue;
}
reverse_hostname = reverse_dns_lookup(ip_addr);
if (reverse_hostname == NULL) {
// printf("\nDNS lookup failed! Could not resolve hostname!\n");
continue;
}
// printf("\nTrying to connect to '%s' IP: %s\n",ip, ip_addr);
printf("\nReverse Lookup domain: %s", reverse_hostname);
printf("\n %s \n", ip);
}
return 0;
}
result:
Reverse Lookup domain: router.asus.com
192.168.2.1
Reverse Lookup domain: DESKTOP-CMK0J2S
192.168.2.10
Reverse Lookup domain: User255
192.168.2.14
Very vague question, there is no single "find all devices" feature to Windows, wireless or even networking in general. You need to scan fer certain services like netbios (139), UPNP, etc. Also, none of this is specific to wireless conenctions.