最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - How to receive VRRP multicast packets on port 112? - Stack Overflow

programmeradmin3浏览0评论

I'm trying an experiment with keepalived and the VRRP protocol. I have keepalived running on computer A, and from computer B I want to read the multicast VRRP messages to find the the Virtual IP address that computer A is advertising. The computers are connected to the same network switch. Similar questions here on SO are answered by "call setsocketopt with IP_ADD_MEMBERSHIP", that is not the problem here.

My code for reading on a multicast socket is not working for the VRRP multicast IP and port (224.0.0.18:112). Note, it works if I try to read mDNS (224.0.0.251:5353), but for VRRP doesn't ever receive any data, it just hangs in recvfrom.

I have confirmed with tcpdump (sudo tcpdump -i enp39s0 net 224.0.0.18) that computer B is actually receiving the messages.

I realize 112 is in the privileged port range, so I run the app with sudo. It seems that if sudo tcpdump -i enp39s0 net 224.0.0.18 can see the messages, this code should be able to receive the messages as well.

When running my app I can see that the port is open and the multicast group has been joined:

$ netstat -lu | grep 112; netstat -g | grep vrrp
udp        0      0 0.0.0.0:112             0.0.0.0:*
enp39s0         2      vrrp.mcast

What am I missing?

Code for receiving multicast messages (error handling removed for simplicity). This code is obviously for Linux, but I've also tried the equivalent on Windows with the same result.

#include <cstring>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define VRRP_MULTICAST_ADDR "224.0.0.18" // VRRP multicast address
#define VRRP_PORT 112                    // VRRP default port

//#define VRRP_MULTICAST_ADDR "224.0.0.251"
//#define VRRP_PORT 5353

int main()
{
    sockaddr_in addr;
    ip_mreq mreq;
    char buffer[256];

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    unsigned int opt_on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*) &opt_on, sizeof(opt_on));

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(VRRP_PORT);

    bind(sockfd, (sockaddr *)&addr, sizeof(addr));
    
    mreq.imr_multiaddr.s_addr = inet_addr(VRRP_MULTICAST_ADDR);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    
    setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
    
    ssize_t nbytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);

    printf("Received %ld bytes\n", nbytes);
    // HexDump(buffer, nbytes); // HexDump from 
    
    close(sockfd);
    return 0;
}

I'm trying an experiment with keepalived and the VRRP protocol. I have keepalived running on computer A, and from computer B I want to read the multicast VRRP messages to find the the Virtual IP address that computer A is advertising. The computers are connected to the same network switch. Similar questions here on SO are answered by "call setsocketopt with IP_ADD_MEMBERSHIP", that is not the problem here.

My code for reading on a multicast socket is not working for the VRRP multicast IP and port (224.0.0.18:112). Note, it works if I try to read mDNS (224.0.0.251:5353), but for VRRP doesn't ever receive any data, it just hangs in recvfrom.

I have confirmed with tcpdump (sudo tcpdump -i enp39s0 net 224.0.0.18) that computer B is actually receiving the messages.

I realize 112 is in the privileged port range, so I run the app with sudo. It seems that if sudo tcpdump -i enp39s0 net 224.0.0.18 can see the messages, this code should be able to receive the messages as well.

When running my app I can see that the port is open and the multicast group has been joined:

$ netstat -lu | grep 112; netstat -g | grep vrrp
udp        0      0 0.0.0.0:112             0.0.0.0:*
enp39s0         2      vrrp.mcast

What am I missing?

Code for receiving multicast messages (error handling removed for simplicity). This code is obviously for Linux, but I've also tried the equivalent on Windows with the same result.

#include <cstring>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define VRRP_MULTICAST_ADDR "224.0.0.18" // VRRP multicast address
#define VRRP_PORT 112                    // VRRP default port

//#define VRRP_MULTICAST_ADDR "224.0.0.251"
//#define VRRP_PORT 5353

int main()
{
    sockaddr_in addr;
    ip_mreq mreq;
    char buffer[256];

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    unsigned int opt_on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*) &opt_on, sizeof(opt_on));

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(VRRP_PORT);

    bind(sockfd, (sockaddr *)&addr, sizeof(addr));
    
    mreq.imr_multiaddr.s_addr = inet_addr(VRRP_MULTICAST_ADDR);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    
    setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
    
    ssize_t nbytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);

    printf("Received %ld bytes\n", nbytes);
    // HexDump(buffer, nbytes); // HexDump from https://stackoverflow/a/73630740/443766
    
    close(sockfd);
    return 0;
}
Share Improve this question asked Mar 18 at 15:14 MatthewMatthew 1,6302 gold badges13 silver badges25 bronze badges 1
  • I've never had a question downvoted. What's wrong with this one? – Matthew Commented Mar 19 at 21:11
Add a comment  | 

1 Answer 1

Reset to default 0

The trick is VRRP (112) is the protocol, it is not UDP, so raw sockets have to be used.

// VRRP is the protocol, not UDP.
int sockfd = socket(AF_INET, SOCK_RAW, VRRP_PORT);
发布评论

评论列表(0)

  1. 暂无评论