這是在網卡上的的兩個特殊的模式,簡而言之,都是將網卡的過濾器關閉。linux
這是咱們經常提到的sniffer mode。它用於無線網絡中,無線網卡開啓監聽模式,監聽空氣中的全部數據包,其中它還能夠切換channel。若是設置得當,能夠同時監控全部信道的幀(切換式,或者同時多個網卡監聽)。在這個模式下面,STA是沒有鏈接到AP的。除了可以獲得數據包以外,還能夠獲得控制幀和管理幀。對於debug 802.11的問題(omnipeek or wireshark in linux)或者攻擊一個802.11的網絡(aircrack、reaver),經常會使網卡進入到這個模式。此文重點在於promiscuous mode,802.11的monitor mode再也不贅述。git
不處於promiscuous mode的網卡只會收取DA會自身的數據包或者BC/MC的數據包。在promiscuos mode下面,網卡會收到DA不是自身的包,在進入這個模式的時候,經常會開啓一個raw socket,來接收網卡傳遞上來的數據。github
翻閱libpcap的源碼,能夠整理出這兩種方式,能夠打開promiscuous mode:ubuntu
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
,須要設備的驅動支持。
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
下面的路徑就和上一種方法同樣了,再也不贅述。能夠了解到,在底層的操做,兩個作法都是同樣的,都是關閉了網卡的過濾器。
既然網卡驅動放行了全部的數據包而且將數據包上傳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裏面。