mdlayher / raw

Package raw enables reading and writing data at the device driver level for a network interface. MIT Licensed.
MIT License
424 stars 73 forks source link

MAC Layer Multicast Group Joining #10

Open bpillatsch opened 7 years ago

bpillatsch commented 7 years ago

I'd like to submit a PR for Multicast group joining, I have an initial hack at the Linux implementation however I am running into a question of how to proceed for BSD. Because Linux implements multicast join as a setsockopt, that is not an option on the BSD side. There does not appear to be any way with BPF (there are filter commands but they do not add the mcast addr to the interface so they capture nothing... tcpdump -i emx -np ether multicast shows no output until manual addition of mcast groups). BSD does have mtest which takes commands and sends them to ioctl via a AF_LINK socket.

Do you think this should be implemented by opening an AF_LINK socket simply to add the mcast then closing the socket? Also this is a perm add so we'd have to defer a mcast group deletion on thread close.

Example (considering some changes) on the Linux side...

// JoinMulticast joins or drops membership of datalink layer multicast address on interface,
// allowing it to recieve multicast traffic.
func (p *packetConn) SetMulticast(b bool, addr net.Addr) error {
    // Ensure correct Addr type
    a, ok := addr.(*Addr)
    if !ok || len(a.HardwareAddr) < 6 {
        return syscall.EINVAL
    }

    // Convert hardware address back to byte array form
    var baddr [8]byte

    copy(baddr[:], a.HardwareAddr)

    mreq := unix.PacketMreq{
        Ifindex: int32(p.ifi.Index),
        Type:    unix.PACKET_MR_MULTICAST,
        Alen:    uint16(len(a.HardwareAddr)),
        Address: baddr,
    }

    membership := unix.PACKET_ADD_MEMBERSHIP
    if !b {
        membership = unix.PACKET_DROP_MEMBERSHIP
    }

    return p.s.SetSockopt(unix.SOL_PACKET, membership, unsafe.Pointer(&mreq), unix.SizeofPacketMreq)
}
mdlayher commented 7 years ago

Your proposed changes for Linux look fine to me

Do you think this should be implemented by opening an AF_LINK socket simply to add the mcast then closing the socket? Also this is a perm add so we'd have to defer a mcast group deletion on thread close.

Assuming this will provide more or less the same behavior we see on Linux, this also seems reasonable. That said, if it's going to be non-trivial in BSD, I'd be okay with a sentence in the doc comment stating that SetMulticast only works on Linux.

bpillatsch commented 7 years ago

I think i can implement it alright in BSD though making sure it works across BSD platforms would be the trick. At the moment I'm just testing in FreeBSD.

bpillatsch commented 7 years ago

Also, in the above Linux implementation I retained your hardware address function argument from your WriteTo() method. However it seems accepting a string and then using net.ParseMAC() might be a more elegant solution, is there a specific reason you chose to implement WriteTo()'s hardware address type acceptance?

mdlayher commented 7 years ago

Yes, because the type must implement https://golang.org/pkg/net/#PacketConn.

IMO it probably makes the most sense to have the parameter be of type net.HardwareAddr.