Promiscuous Mode


簡介

Monitor mode 與 promiscuous mode 比較

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

  • 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不再贅述。

  • Promiscuos mode

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

打開promiscuous mode

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

(activate_new) – new way

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

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);

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

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

net/packet/af_packet.c 里面有對應的操作:

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






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM