You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在端口112接收VRRP组播数据包?排查接收失败问题

问题描述

我正在做keepalived与VRRP协议的实验:计算机A运行keepalived,想在计算机B上读取VRRP组播消息,获取A通告的虚拟IP,两台设备连同一交换机。

我写的组播套接字代码能正常接收mDNS(224.0.0.251:5353)数据,但接收VRRP组播地址及端口(224.0.0.18:112)的消息时,程序会卡在recvfrom处。

已经用tcpdump确认计算机B确实能收到这些消息:

sudo tcpdump -i enp39s0 net 224.0.0.18

知道112是特权端口,所以用sudo运行程序。netstat显示端口已打开且已加入组播组:

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

Stack Overflow上同类问题的方案是设置IP_ADD_MEMBERSHIP,但这不是当前问题所在。请问我忽略了什么?

以下是接收组播消息的代码(简化后,移除错误处理,Linux平台,Windows等效代码也有同样问题):

#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);
    
    close(sockfd);
    return 0;
}
问题排查与解决

你犯了一个核心概念错误:VRRP不是基于UDP的协议。你混淆了IP协议号和UDP端口号——VRRP的IP协议号是112,而非使用UDP端口112。你的UDP套接字根本无法接收到VRRP消息,因为这些消息本质是IP层数据包,不是UDP包。

tcpdump能抓到是因为它直接抓取IP层流量,而你的代码监听的是UDP端口,两者处理的是不同层级的数据包。

修正方案:使用原始套接字接收VRRP包

要接收VRRP消息,需要创建原始套接字,并指定接收IP协议号为112的数据包。修改后的代码如下:

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

#define VRRP_PROTOCOL 112 // VRRP的IP协议号
#define VRRP_MULTICAST_ADDR "224.0.0.18"

// VRRP报文头部简化结构
struct vrrp_header {
    uint8_t version_type;
    uint8_t vrid;
    uint8_t priority;
    uint8_t count_ip;
    uint16_t auth_type;
    uint16_t adver_int;
    uint32_t checksum;
    // 后续为虚拟IP列表,此处简化
};

int main()
{
    // 创建原始套接字,指定接收VRRP协议包
    int sockfd = socket(AF_INET, SOCK_RAW, VRRP_PROTOCOL);
    if (sockfd < 0) {
        perror("socket creation failed");
        return 1;
    }

    // 可选:绑定到指定接口,避免接收其他接口的无关流量
    // const char* iface = "enp39s0";
    // setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface));

    char buffer[1024];
    sockaddr_in src_addr;
    socklen_t addr_len = sizeof(src_addr);

    // 接收VRRP数据包
    ssize_t nbytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, (sockaddr*)&src_addr, &addr_len);
    if (nbytes < 0) {
        perror("recvfrom failed");
        close(sockfd);
        return 1;
    }

    printf("Received %ld bytes from %s\n", nbytes, inet_ntoa(src_addr.sin_addr));

    // 解析IP头部
    iphdr* ip_hdr = (iphdr*)buffer;
    int ip_hdr_len = ip_hdr->ihl * 4; // IP头部长度(单位:字节)

    // 解析VRRP头部
    vrrp_header* vrrp_hdr = (vrrp_header*)(buffer + ip_hdr_len);
    printf("VRRP Version/Type: 0x%02X, VRID: %d, Priority: %d\n",
           vrrp_hdr->version_type, vrrp_hdr->vrid, vrrp_hdr->priority);

    // 提取虚拟IP(假设仅存在一个虚拟IP)
    if (vrrp_hdr->count_ip > 0) {
        uint32_t* virtual_ip = (uint32_t*)(buffer + ip_hdr_len + sizeof(vrrp_header));
        char ip_str[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, virtual_ip, ip_str, sizeof(ip_str));
        printf("Virtual IP: %s\n", ip_str);
    }

    close(sockfd);
    return 0;
}

额外注意事项

  • 运行原始套接字程序需要root权限(用sudo执行)。
  • 原始套接字会接收所有IP协议号为112的数据包,若需过滤可自行判断目标IP是否为224.0.0.18。
  • 不同系统的原始套接字行为略有差异,Linux下需确保内核允许创建原始套接字。

内容的提问来源于stack exchange,提问作者Matthew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 18:57:32