Never mind. In case anyone was wondering, PACKET_ADD_MEMBERSHIP is a SOL_PACKET option, not SOL_SOCKET. Only took me two days to spot!
On Tue, Apr 6, 2021 at 8:13 PM Tom Cook <tom.k.c...@gmail.com> wrote: > > Can someone please suggest why the code below doesn't do as expected? > I expect it to bind an AF_PACKET socket to an interface and receive > packets with ethertype 0x5eeb that arrive at multicast MAC address > 77:68:76:68:76:69 on that interface. In practice, nothing arrives. > > If I comment out the call to bind(), it receives packets with > ethertype 0x5eeb that are addressed to 77:68:76:68:76:69 and are > received on any interface on the system, not just eth0. (There are no > packets with ethertype 0x5eeb sent to any other address, so this may > be coincidence.) > > If I change either use of ether_type to be ETH_P_ALL instead (and > re-instate the bind() call), then it receives all ethernet frames > received on eth0. > > Is this a bug? Or is it as expected and I have to use some other > mechanism (BPF?) to filter the frames? > > Thanks for any assistance, > Tom > > Code: > > #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() { > int fd = socket(AF_PACKET, SOCK_RAW, htons(eth_type)); > if (fd < 0) { > perror("socket"); > exit(1); > } > > 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); > > struct sockaddr_ll addr = {0}; > addr.sll_family = AF_PACKET; > addr.sll_ifindex = ifr.ifr_ifindex; > addr.sll_protocol = htons(eth_type); > addr.sll_pkttype = PACKET_MULTICAST; > if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { > perror("bind"); > exit(1); > } > > unsigned char mcast[ETH_ALEN] = {0x77, 0x68, 0x76, 0x68, 0x76, 0x69}; > struct packet_mreq mreq = {0}; > mreq.mr_ifindex = ifr.ifr_ifindex; > mreq.mr_type = PACKET_MR_MULTICAST; > memcpy(mreq.mr_address, mcast, ETH_ALEN); > mreq.mr_alen = ETH_ALEN; > if(setsockopt(fd, SOL_SOCKET, PACKET_ADD_MEMBERSHIP, &mreq, > sizeof(mreq)) < 0) { > perror("setsockopt"); > exit(1); > } > > 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]); > } > } > > And here is a short Python3 programme to generate such frames (install > pyroute2 package and run as `sudo python3 test.py eth0`): > > 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)