一、原始套接字概述
協議棧的原始套接字從實現上可以分為“鏈路層原始套接字”和“網絡層原始套接字”兩大類。
鏈路層原始套接字可以直接用於接收和發送鏈路層的MAC幀,在發送時需要由調用者自行構造和封裝MAC首部。
網絡層原始套接字可以直接用於接收和發送IP層的報文數據,在發送時需要自行構造IP報文頭(取決是否設置IP_HDRINCL選項)。
原始套接字(SOCK_RAW)可以用來自行組裝數據包,可以接收本機網卡上所有的數據幀(數據包),對於監聽網絡流量和分析網絡數據很有作用。
原始套接字是基於IP 數據包的編程(SOCK_PACKET 是基於數據鏈路層的編程)。另外,必須在管理員權限下才能使用原始套接字。
原始套接字(SOCK_RAW)與標准套接字(SOCK_STREAM、SOCK_DGRAM)的區別在於原始套接字直接置“根”於操作系統網絡核心(Network Core),
而 SOCK_STREAM、SOCK_DGRAM 則“懸浮”於 TCP 和 UDP 協議的外圍。
流式套接字只能收發TCP協議的數據,數據報套接字只能收發UDP協議的數據,原始套接字可以收發沒經過內核協議棧的數據包。
原始套接字,指在傳輸層下面使用的套接字。流式套接字和數據報套接字這兩種套接字工作在傳輸層,主要為應用層的應用程序提供服務,
並且在接收和發送時只能操作數據部分,而不能對IP首部或TCP和UDP首部進行操作,通常把這兩種套接字稱為標准套接字。
但是,如果我們開發的是更底層的應用,比如發送一個自定義的IP包、UDP包、TCP包或ICMP包,捕獲所有經過本機網卡的數據包,
偽裝本機的IP,想要操作IP首部或傳輸層協議首部,等等,這些功能對於這兩種套接字就無能為力了。
這些功能需要使用另一種套接字來實現,這種套接字叫作原始套接字,功能更強大,更底層。
原始套接字可作的功能:
- 發送一個自定義的IP 包;
- 發送一個ICMP 協議包;
- 分析所有經過網絡的包,而不管這樣包是否是發給自己的;
- 偽裝本地的IP 地址;
1、 鏈路層原始套接字
鏈路層原始套接字調用socket()函數創建。
第一個參數指定協議族類型為PF_PACKET,第二個參數type可以設置為SOCK_RAW或SOCK_DGRAM,第三個參數是協議類型(該參數只對報文接收有意義)。
socket(PF_PACKET, type, htons(protocol))
a) 參數type設置為SOCK_RAW時,套接字接收和發送的數據都是從MAC首部開始的。
在發送時需要由調用者從MAC首部開始構造和封裝報文數據。type設置為SOCK_RAW的情況應用是比較多的,因為某些項目會使用到自定義的二層報文類型。
socket(PF_PACKET, SOCK_RAW, htons(protocol))
b) 參數type設置為SOCK_DGRAM時,套接字接收到的數據報文會將MAC首部去掉。同時在發送時也不需要再手動構造MAC首部,
只需要從IP首部(或ARP首部,取決於封裝的報文類型)開始構造即可,而MAC首部的填充由內核實現的。若對於MAC首部不關心的場景,可以使用這種類型,這種用法用得比較少。
socket(PF_PACKET, SOCK_DGRAM, htons(protocol))
2、網絡層原始套接字
創建面向連接的TCP和創建面向無連接的UDP套接字,在接收和發送時只能操作數據部分,而不能對IP首部或TCP和UDP首部進行操作。
如果想要操作IP首部或傳輸層協議首部,就需要調用如下socket()函數創建網絡層原始套接字。
第一個參數指定協議族的類型為PF_INET,第二個參數為SOCK_RAW,第三個參數protocol為協議類型。產品線有使用OSPF和RSVP等協議,需要使用這種類型的套接字。
socktet(PF_INET, SOCK_RAW, protocol)
a) 接收報文
網絡層原始套接字接收到的報文數據是從IP首部開始的,即接收到的數據包含了IP首部, TCP/UDP/ICMP等首部, 以及數據部分。
b) 發送報文
網絡層原始套接字發送的報文數據,在默認情況下是從IP首部之后開始的,即需要由調用者自行構造和封裝TCP/UDP等協議首部。
這種套接字也提供了發送時從IP首部開始構造數據的功能,通過setsockopt()給套接字設置上IP_HDRINCL選項,就需要在發送時自行構造IP首部。
原始套接字的創建
int socket ( int family, int type, int protocol );
參數:
family:協議族 這里寫 PF_PACKET
type: 套接字類,這里寫 SOCK_RAW
protocol:協議類別,指定可以接收或發送的數據包類型,不能寫 “0”,取值如下,注意,傳參時需要用 htons() 進行字節序轉換。
ETH_P_IP:IPV4數據包
ETH_P_ARP:ARP數據包
ETH_P_ALL:任何協議類型的數據包
返回值:
成功( >0 ):套接字,這里為鏈路層的套接字
失敗( <0 ):出錯
二、socket之ioctl
1、struct ifreq結構體
ifreq結構定義在/usr/include/net/if.h,用來配置ip地址,激活接口,配置MTU等接口信息的。
其中包含了一個接口的名字和具體內容——(是個共用體,有可能是IP地址,廣播地址,子網掩碼,MAC號,MTU或其他內容)。
ifreq包含在ifconf結構中。而ifconf結構通常是用來保存所有接口的信息的。
// if.h /* * Interface request structure used for socket * ioctl's. All interface ioctl's must have parameter * definitions which begin with ifr_name. The * remainder may be interface specific. */ struct ifreq { #define IFHWADDRLEN 6 union { char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ } ifr_ifrn; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; struct sockaddr ifru_netmask; struct sockaddr ifru_hwaddr; short ifru_flags; int ifru_ivalue; int ifru_mtu; struct ifmap ifru_map; char ifru_slave[IFNAMSIZ]; /* Just fits the size */ char ifru_newname[IFNAMSIZ]; void __user * ifru_data; struct if_settings ifru_settings; } ifr_ifru; }; #define ifr_name ifr_ifrn.ifrn_name /* interface name */ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ #define ifr_addr ifr_ifru.ifru_addr /* address */ #define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */ #define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ #define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ #define ifr_flags ifr_ifru.ifru_flags /* flags */ #define ifr_metric ifr_ifru.ifru_ivalue /* metric */ #define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ #define ifr_map ifr_ifru.ifru_map /* device map */ #define ifr_slave ifr_ifru.ifru_slave /* slave device */ #define ifr_data ifr_ifru.ifru_data /* for use by interface */ #define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */ #define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ #define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ #define ifr_newname ifr_ifru.ifru_newname /* New name */ #define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/
// if.h /* * Structure used in SIOCGIFCONF request. * Used to retrieve interface configuration * for machine (useful for programs which * must know all networks accessible). */ struct ifconf { int ifc_len; /* size of buffer */ union { char __user *ifcu_buf; struct ifreq __user *ifcu_req; } ifc_ifcu; }; #define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ #define ifc_req ifc_ifcu.ifcu_req /* array of structures */
2、用法
3、獲取ip地址舉例
#include <string.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #include <stdio.h> #include <netinet/in.h> #include <arpa/inet.h> int main() { int inet_sock; struct ifreq ifr; inet_sock = socket(AF_INET, SOCK_DGRAM, 0); strcpy(ifr.ifr_name, "eth0"); //SIOCGIFADDR標志代表獲取接口地址 if (ioctl(inet_sock, SIOCGIFADDR, &ifr) < 0) perror("ioctl"); printf("%s\n", inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr)); return 0; }
三、原始套接字編程舉例
#include <sys/types.h> #include <sys/socket.h> #include <linux/if.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <netinet/in.h> #include <netinet/if_ether.h> #include <net/ethernet.h> #include <arpa/inet.h> #include <sys/ioctl.h> #include <stdio.h> #include <string.h> int main() { int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP) ); if (sockfd == -1) { printf("socket error\n"); return 0; } //獲取網卡信息 sockaddr_ll addr_ll; memset(&addr_ll, 0, sizeof(sockaddr_ll)); addr_ll.sll_family = PF_PACKET; ifreq ifr; strcpy(ifr.ifr_name, "ens33"); if (ioctl(sockfd, SIOCGIFINDEX, &ifr) == -1) { printf("error ioctl SIOCGIFINDEX\n"); return 0; } addr_ll.sll_ifindex = ifr.ifr_ifindex; //接口索引 if (ioctl(sockfd, SIOCGIFADDR, &ifr) == -1) { printf("error ioctl SIOCGIFADDR\n"); return 0; } char* ipSrc = inet_ntoa(((struct sockaddr_in*)(&(ifr.ifr_addr)))->sin_addr); printf("ip address : %s\n", ipSrc); //source ip if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) == -1) { printf("error ioctl SIOCGIFHWADDR\n"); return 0; } unsigned char macSrc[ETH_ALEN]; memcpy(macSrc, ifr.ifr_hwaddr.sa_data, ETH_ALEN); //mac address printf("mac address"); for (int i = 0; i < ETH_ALEN; i++) printf(":%02x", macSrc[i]); printf("\n"); //填充以太網首部 和 ARP信息 unsigned char macDst[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; ether_header header; memcpy(header.ether_dhost, macDst, ETH_ALEN); memcpy(header.ether_shost, macSrc, ETH_ALEN); header.ether_type = htons(ETHERTYPE_ARP); ether_arp arp; arp.arp_hrd = htons(ARPHRD_ETHER); arp.arp_pro = htons(ETHERTYPE_IP); arp.arp_hln = ETH_ALEN; arp.arp_pln = 4; //IPv4 arp.arp_op = htons(ARPOP_REQUEST); in_addr src_in_addr, dst_in_addr; inet_pton(AF_INET, ipSrc, &src_in_addr); inet_pton(AF_INET, "192.168.182.132", &dst_in_addr); memcpy(arp.arp_sha, macSrc, ETH_ALEN); memcpy(arp.arp_spa, &src_in_addr, 4); memcpy(arp.arp_tha, macDst, ETH_ALEN); memcpy(arp.arp_tpa, &dst_in_addr, 4); unsigned char sendBuf[sizeof(ether_header) + sizeof(ether_arp) ]; memcpy(sendBuf, &header, sizeof(ether_header) ); memcpy(sendBuf + sizeof(ether_header), &arp, sizeof(ether_arp)); int len = sendto(sockfd, sendBuf, sizeof(sendBuf), 0, (const sockaddr*)&addr_ll, sizeof(addr_ll) ); if (len > 0) { printf("send success\n"); } return 0; }
參考:
https://blog.csdn.net/zhu114wei/article/details/6927513
https://www.icode9.com/content-4-401910.html
https://www.it610.com/article/1297978265012609024.htm
https://www.bbsmax.com/A/KE5Qg6YkzL/
https://blog.csdn.net/pashanhu6402/article/details/96428887
https://blog.csdn.net/panker2008/article/details/46502783
https://baike.baidu.com/item/%E5%A5%97%E6%8E%A5%E5%AD%97/9637606?fromtitle=socket&fromid=281150&fr=aladdin
https://www.jianshu.com/p/066d99da7cbd
https://www.cnblogs.com/huqian23456/archive/2011/02/22/1961822.html
https://blog.csdn.net/will130/article/details/53326740
https://blog.51cto.com/mingtangduyao/1721604
https://blog.csdn.net/giantpoplar/article/details/47657303
https://blog.csdn.net/ljianhui/article/details/10477427#
https://www.cnblogs.com/dapaitou2006/p/6502195.html
https://blog.csdn.net/weixin_43206704/article/details/89327572
https://blog.csdn.net/weixin_43206704/article/details/89293187
http://blog.chinaunix.net/uid-27074062-id-3388166.html
https://blog.csdn.net/aebdm757009/article/details/101498118
https://tennysonsky.blog.csdn.net/article/details/44676377
https://blog.csdn.net/q623702748/article/details/52063019
https://blog.csdn.net/weixin_43288201/article/details/106266418