It doesn't appear Go has a cross-platform way of checking the PROMISC flag (I can't even find out for sure if such a flag exists for windows.) Here is a way to get it on linux, which I'm guessing you're on:
package main
import (
"fmt"
"net"
"os"
"syscall"
"unsafe"
)
func GetPromiscuous(i net.Interface) (bool, error) {
tab, err := syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC)
if err != nil {
return false, os.NewSyscallError("netlinkrib", err)
}
msgs, err := syscall.ParseNetlinkMessage(tab)
if err != nil {
return false, os.NewSyscallError("parsenetlinkmessage", err)
}
loop:
for _, m := range msgs {
switch m.Header.Type {
case syscall.NLMSG_DONE:
break loop
case syscall.RTM_NEWLINK:
ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0]))
if ifim.Index == int32(i.Index) {
return (ifim.Flags & syscall.IFF_PROMISC) != 0, nil
}
}
}
return false, os.ErrNotExist
}
func main() {
ints, err := net.Interfaces()
if err != nil {
panic(err)
}
for _, i := range ints {
p, err := GetPromiscuous(i)
if err != nil {
panic(err)
}
fmt.Println(i.Name, p)
}
}
This is based of the interfaceTable
function in the standard library. It uses rtnetlink
to get the flags of the interface. Unless you want to roll your own syscall.NetlinkRIB
function, this code will always pull the information for every network device and filter out the requested one.
A bit less magical way to get the flag you want is to use cgo and ioctl:
package main
/*
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
bool is_promisc(char *name) {
int s = socket(AF_INET, SOCK_STREAM, 0);
struct ifreq *i = malloc(sizeof *i);
strncpy((char *)&(i->ifr_name), name, IFNAMSIZ);
ioctl(s, SIOCGIFFLAGS, i);
bool p = (i->ifr_flags & IFF_PROMISC) != 0;
free(i);
return p;
}
*/
import "C"
import (
"fmt"
"net"
)
func GetPromiscuous(i net.Interface) (bool, error) {
set, err := C.is_promisc(C.CString(i.Name))
return bool(set), err
}
func main() {
ints, err := net.Interfaces()
if err != nil {
panic(err)
}
for _, i := range ints {
p, err := GetPromiscuous(i)
if err != nil {
panic(err)
}
fmt.Println(i.Name, p)
}
}
A final note is that either way may not always tell you correctly whether an interface is actually in promiscuous mode or not. See this thread for more details.
From what I'm reading using the netlink route should work correctly, but another post says we should be checking the promiscuous count. Please let me know if someone knows how to do that, because I can't find how to. The only stackoverflow question on the matter has gone unanswered.
I think either of these methods will work as long as you're not doing any crazy networking stuff (bridge, vlan interfaces, macvtap, etc.) The code definitely works if you use iproute2 tools to turn promisc on and off on an interface.