简体   繁体   中英

Receiving AF_PACKET multicast on a Linux network interface

I'm implementing a non-standard / unpublished Ethernet protocol on Linux. The Ethernet frames have EtherType 0x5eeb and are multicast to 77:68:76:68:76:69 .

I'm struggling to write a simple program that can receive these frames while bound to a single interface. I've attempted this in several languages now; here's the C version:

#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]);
    }
}

If I remove the section titled Bind to the interface then I receive the frames alright, I just receive them from every interface on the system, not just the one I'm trying to listen to. If I change either of the places where eth_type is used to ETH_P_ALL then I get all the frames arriving on the bound interface, not just the ones with my EtherType (and not just multicast frames, for that matter; bind doesn't seem to respect PACKET_MULTICAST in this case).

Is there some way to make bind and specific EtherTypes play nicely together?

I've tried this on Linux 5.11 and 4.9. If anyone wants to do some testing, here's a short Python 3 program that spits out the frames. You need the 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)

The problem here was actually the PACKET_ADD_MULTICAST socket option, which is a SOL_PACKET option, not a SOL_SOCKET option like I was attempting. I'm not exactly sure what SOL_SOCKET + PACKET_ADD_MEMBERSHIP does, but whatever it is, it doesn't return an error and it breaks things.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM