簡體   English   中英

在 Linux 網絡接口上接收 AF_PACKET 多播

[英]Receiving AF_PACKET multicast on a Linux network interface

我正在 Linux 上實現非標准/未發布的以太網協議。 以太網幀的 EtherType 0x5eeb並被多播到77:68:76:68:76:69

我正在努力編寫一個簡單的程序,該程序可以在綁定到單個接口時接收這些幀。 我現在用幾種語言嘗試過這個; 這是 C 版本:

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

const unsigned short eth_type = 0x5eeb;

int main() {
    /* Open a socket */
    int fd = socket(AF_PACKET, SOCK_RAW, htons(eth_type));
    if (fd < 0) {
        perror("socket");
        exit(1);
    }

    /* Find out the interface index number */
    struct ifreq ifr;
    const char * if_name = "eth0";
    size_t if_name_len = strlen (if_name);
    memcpy(ifr.ifr_name, if_name, if_name_len);
    ioctl(fd, SIOCGIFINDEX, &ifr);
    printf("Interface has index %d\n", ifr.ifr_ifindex);

    /* Add the interface to the multicast group */
    char mcast[] = {0x77, 0x68, 0x76, 0x68, 0x76, 0x69, 0, 0};
    struct packet_mreq mreq = {0};
    mreq.mr_ifindex = ifr.ifr_ifindex;
    mreq.mr_type = PACKET_MR_MULTICAST;
    memcpy(mreq.mr_address, mcast, 8);
    mreq.mr_alen = 6;
    if(setsockopt(fd, SOL_SOCKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
        perror("setsockopt");
        exit(1);
    }

    /* Bind to the interface */    
    struct sockaddr_ll addr = {0};
    addr.sll_family = AF_PACKET;
    addr.sll_ifindex = ifr.ifr_ifindex;
    addr.sll_protocol = htons(eth_type);  // Defaults to the socket protocol anyway
    addr.sll_pkttype = PACKET_MULTICAST;
    if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(1);
    }

    /* Receive a frame and output some details */
    char buf [2048];
    struct sockaddr_ll src_addr;
    socklen_t src_addr_len = sizeof(src_addr);
    ssize_t count = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&src_addr, &src_addr_len);
    if (count == -1) {
        perror("recvfrom");
        exit(1);
    } else {
        printf("Received frame.\n");
        printf("Dest MAC: ");
        for (int ii = 0; ii < 5; ii++) {
            printf("%02hhx:", buf[ii]);
        }
        printf("%02hhx\n", buf[5]);
        printf("Src MAC: ");
        for (int ii = 6; ii < 11; ii++) {
            printf("%02hhx:", buf[ii]);
        }
        printf("%02hhx\n", buf[11]);
    }
}

如果我刪除標題為Bind to the interface的部分,那么我會收到幀,我只是從系統上的每個接口接收它們,而不僅僅是我嘗試收聽的接口。 如果我將使用eth_type的任何地方更改為ETH_P_ALL ,那么我會得到所有到達綁定接口的幀,而不僅僅是帶有我的 EtherType 的幀(就此而言,不僅僅是多播幀; bind似乎不尊重PACKET_MULTICAST在這種情況下)。

有什么方法可以讓bind和特定的 EtherTypes 很好地結合在一起嗎?

我在 Linux 5.11 和 4.9 上試過這個。 如果有人想做一些測試,這里有一個簡短的 Python 3 程序,它會吐出幀。 您需要pyroute2 package。

import socket
from pyroute2 import IPDB
import sys
import struct
import binascii
import time

ip = IPDB()

SMAC=bytes.fromhex(ip.interfaces[sys.argv[1]]['address'].replace(':', ''))
DMAC=bytes.fromhex('776876687669')

s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
s.bind((sys.argv[1], 0x5eeb))
#s.bind((sys.argv[1], 0))

dgram = struct.pack("!6s6sHH", DMAC, SMAC, 0x5eeb, 0x7668)
print(' '.join('{:02x}'.format(x) for x in dgram))

while True:
    s.send(dgram)
    time.sleep(0.1)

這里的問題實際上是PACKET_ADD_MULTICAST套接字選項,它是一個SOL_PACKET選項,而不是像我嘗試的SOL_SOCKET選項。 我不完全確定SOL_SOCKET + PACKET_ADD_MEMBERSHIP做了什么,但無論它是什么,它都不會返回錯誤並且會破壞事情。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM