Promiscuous Mode

簡介

Monitor mode 與 promiscuous mode 比較

這是在網卡上的的兩個特殊的模式,簡而言之,都是將網卡的過濾器關閉。linux

  • Monitor mode

這是咱們經常提到的sniffer mode。它用於無線網絡中,無線網卡開啓監聽模式,監聽空氣中的全部數據包,其中它還能夠切換channel。若是設置得當,能夠同時監控全部信道的幀(切換式,或者同時多個網卡監聽)。在這個模式下面,STA是沒有鏈接到AP的。除了可以獲得數據包以外,還能夠獲得控制幀和管理幀。對於debug 802.11的問題(omnipeek or wireshark in linux)或者攻擊一個802.11的網絡(aircrack、reaver),經常會使網卡進入到這個模式。此文重點在於promiscuous mode,802.11的monitor mode再也不贅述。git

  • Promiscuos mode

不處於promiscuous mode的網卡只會收取DA會自身的數據包或者BC/MC的數據包。在promiscuos mode下面,網卡會收到DA不是自身的包,在進入這個模式的時候,經常會開啓一個raw socket,來接收網卡傳遞上來的數據。github

打開promiscuous mode

翻閱libpcap的源碼,能夠整理出這兩種方式,能夠打開promiscuous mode:ubuntu

(activate_new) – new way

1.Try to open a packet socket using the new kernel PF_PACKET interface
2.Select promiscuous mode onwindows

struct packet_mreq  mr; memset(&mr, 0, sizeof(mr));
mr.mr_ifindex = handlep->ifindex;
mr.mr_type    = PACKET_MR_PROMISC;
sock_fd =  socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
setsockopt(sock_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr))

setsockopt 原型以下:網絡

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);socket

對應於內核的 /include/net/sock.h測試

(setsockopt)(struct sock sk, int level, int optname, char __user *optval, unsigned int optlen);ui

net/packet/af_packet.c 裏面有對應的操做:debug

static int
packet_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
{
    struct sock *sk = sock->sk;
    struct packet_sock *po = pkt_sk(sk);
    int ret;

    if (level != SOL_PACKET)
        return -ENOPROTOOPT;

    switch (optname) {
    case PACKET_ADD_MEMBERSHIP:
    case PACKET_DROP_MEMBERSHIP:
    {
        struct packet_mreq_max mreq;
        int len = optlen;
        memset(&mreq, 0, sizeof(mreq));
        if (len  sizeof(mreq))
            len = sizeof(mreq);
        if (copy_from_user(&mreq, optval, len))
            return -EFAULT;
        if (len < (mreq.mr_alen + offsetof(struct packet_mreq, mr_address)))
            return -EINVAL;
        if (optname == PACKET_ADD_MEMBERSHIP)
            ret = packet_mc_add(sk, &mreq);
        else
            ret = packet_mc_drop(sk, &mreq);
        return ret;
    }

調用流程:

packet_mc_add -> packet_dev_mc -> dev_set_promiscuity(net/core/dev.c)

dev_set_promiscuity的定義以下:

int dev_set_promiscuity(struct net_device *dev, int inc)
{
    unsigned int old_flags = dev->flags;
    int err;

    err = __dev_set_promiscuity(dev, inc, true);
    if (err <0 return err if dev->flags != old_flags)
        dev_set_rx_mode(dev);
    return err;
}
__dev_set_promiscuity =>
dev->flags |= IFF_PROMISC;
dev_change_rx_flags(dev, IFF_PROMISC);
ops(net_device_ops)->ndo_change_rx_flags(dev, flags);
dev_set_rx_mode(dev); ==>
ops(net_device_ops)->ndo_set_rx_mode(dev);

驅動基本上只實現了ndo_set_rx_mode

選擇一個Ethernet驅動做爲例子:

const struct net_device_ops ei_netdev_ops = {
     ....
    .ndo_open        = ei_open,
    .ndo_stop        = ei_close,
    .ndo_start_xmit        = ei_start_xmit,
    .ndo_set_rx_mode    = ei_set_multicast_list,
};
ei_set_multicast_list -> 
__ei_set_multicast_list -> 
do_set_multicast_list
static void do_set_multicast_list(struct net_device *dev)
{
    ....
    if (dev->flags&IFF_PROMISC) {
        memset(ei_local->mcfilter, 0xFF, 8);
        ei_outb_p(E8390_RXCONFIG | 0x18, e8390_base + EN0_RXCR);
    }
}

這邊能夠看到驅動設置了EN0_RXCR,含義猜想是RX config register,將RX的接收狀態設置爲accept all。因此說,設置設備進入promiscuous mode,其實是設置硬件寄存器ndo_set_rx_mode,須要設備的驅動支持。

(activate_old) – old way

struct ifreq ifr;
ifr.ifr_flags |= IFF_PROMISC;
ioctl(handle->fd, SIOCSIFFLAGS, &ifr)

這種開啓方法,多個開啓混雜模式的程序可能會干擾,容易出問題,不推薦使用
在 net/core/dev_ioctl.c

=> dev_ifsioc
=> dev_change_flags
=> __dev_change_flags
=> dev_set_rx_mode

下面的路徑就和上一種方法同樣了,再也不贅述。能夠了解到,在底層的操做,兩個作法都是同樣的,都是關閉了網卡的過濾器。

如何判斷一個機器位於promiscuous mode

既然網卡驅動放行了全部的數據包而且將數據包上傳TCPIP協議棧,那麼這裏可使用一個技巧。將進入promiscuous mode的主機設爲A。從同一網段的B主機僞造一個目標MAC地址不是A,可是目標ip地址是A的包。當正常的主機收到這個包的時候,網卡驅動或者網卡的asic就會將它丟棄,當這個主機進入promiscuous mode以後,網卡驅動放行這個包,TCPIP協議棧檢查這個包的目標地址是A,因此會產生一個迴應到B主機。固然了,若是主機A特地將TCPIP協議棧的收發包路徑關掉,那麼就沒法偵測到了。

另外,若是我僞造了一個不存在的mac地址,那麼交換機就不知道這個mac地址是位於哪一個端口,它也會幫忙轉發到全部端口。

好比我從B主機僞造一個錯誤MAC地址的ICMP包給A主機,判斷A主機是否迴應,這樣就能夠判斷出它是否處於promiscuous mode。

MAC IP Payload
局域網內沒有出現過的mac地址 192.168.0.5 xxxxxxxx

作一個測試

測試環境: ubuntu 12.4 + win10
測試網卡:Ethernet 網卡
測試軟件:wireshark,ICMP c語言測試程序
兩端的ip:192.168.0.4,192.168.0.5
測試方式:直連,家用路由器LAN口轉發

下面是測試的過程。首先我在目標機器上面(win10)開啓了wireshark,而且監聽本地鏈接。這時候我僞造了一個錯誤的MAC DA,可是其餘參數,好比對方的ip地址是填寫正確的。這時候本機的wireshark能夠看到對方的ICMP迴應。緊接着我關閉win10上面的wireshark,ICMP迴應消失。隨後我又開啓wireshark,又能夠獲得ICMP迴應了。測試的時候,兩臺機器使用網線直連或者經過家用路由器的LAN口轉發都是成功的,可是使用無線網絡的時候是失敗的,多是無線網卡不支持promiscuous mode。注意到,由於是要僞造MAC地址,因此須要使用PF_PACKET來操做RAW socket

下圖是在ubuntu上面的wireshark截取的

另外,若是兩個機器直連測試,須要在windows上面設置靜態ip地址(192.168.0.5/24),在linux機器上面關閉界面上的網絡鏈接,而且輸入測試網絡是否連通:

tanhangbo@ThinkPad:~$ sudo ifconfig eth0 up
tanhangbo@ThinkPad:~$ sudo ifconfig eth0 192.168.0.4
tanhangbo@ThinkPad:~$ ping 192.168.0.5
PING 192.168.0.5 (192.168.0.5) 56(84) bytes of data.
64 bytes from 192.168.0.5: icmp_req=1 ttl=128 time=0.312 ms
64 bytes from 192.168.0.5: icmp_req=2 ttl=128 time=0.384 ms
64 bytes from 192.168.0.5: icmp_req=3 ttl=128 time=0.408 ms

實際做用

通常來講,進入這個模式,就是爲了多收一些包,進行網絡診斷。通常開啓wireshark的時候就會幫你打開promiscuous mode。底層操做在libpcap裏面。

  • 參考資料:
  1. libpcap和linux kernel 源碼
  2. http://stackoverflow.com/questions/6666257/what-is-the-purpose-of-net-device-uc-promisc-field



相關文章
相關標籤/搜索