1. Original socket usage scenario
The network programming we usually use is to send and receive data at the application layer. Each program can only receive data sent to itself, that is, each program can only receive data from the bound port of the program. The received data usually includes only application-layer data, and the original header information is hidden in the transmission process. In some cases, we need to perform more low-level operations, such as listening to all data sent and received by the native machine, modifying headers, etc., whereas SOCK_STREAM and SOCK_DGRAMZ are usually used in the application layer and cannot meet this requirement.
With raw sockets, we can capture all IP packets sent to the local machine (including IP headers and TCP/UDP/ICMP headers) and all frames received by the local machine (including data link layer protocol headers). A common socket cannot process network packets such as ICMP and IGMP, but SOCK_RAW can. With raw sockets, we can construct our own IP headers.
2. Introduction to primitive socket classification and socket functions
There are two types of raw sockets: one handles data at the IP layer and above it by specifying the socket as the first parameter
AF_INET
To create this socket. The other is to process the data link layer and the data on it by specifying the socket as the first parameter
PF_PACKET
To create this socket.
Socket function:
int socket(int domain, int type, int proto)
#include <sys/types.h>
#include <sys/socket.h>
parameter
options
role
note
domain
AF_INET
Get data starting with the network layer (complete IP packets)
PF_PACKET
Get data from the start of the data link layer (link layer header +IP packet)
. .
Other domain options are not in use
type
SOCK_STREAM
Connection-oriented streaming sockets
SOCK_DGRAM
Connectionless packet socket
SOCK_RAW
The raw socket that receives the underlying data message
SOCK_PACKET
An outdated type is not recommended for the latest version
. .
Other type options are not used for the time being
proto
0
The default value is 0
ETH_P_IP
ETH_P_IP 0x800 Only IP data frames sent to the local MAC are received
ETH_P_ARP
ETH_P_ARP 0x806 Only arp data frames sent to the local MAC are accepted
ETH_P_RARP
ETH_P_RARP 0x8035 Only RARP frames sent to the local MAC are accepted
ETH_P_ALL
ETH_P_ALL 0x3 Receives all types of IP ARP Data frames sent to the local MAC address and all types of data frames sent from the local MAC address.(If promiscuous mode is enabled, non-local FRAMES are received.)
3. Socket usage and application scenarios
Creating a socket and its application scenario
Create a way
Work domain
Send a message
Receive a message
note
socket(AF_INET,SOCK_STREAM, 0)
The network layer
Receiving and sending are corresponding
port
Application layer data of
Received the packet destined for the local IP address
socket(AF_INET, SOCK_DGRAM, 0)
socket(AF_INET, SOCK_RAW, 0)
Only packets containing TCP or UDP headers or other transport protocols can be sent. IP headers and Ethernet frame headers are automatically sealed by the kernel
The user gets the complete IP packet containing the IP header
socket(PF_PACKET, SOCK_DGRAM, 0)
The link layer
Get IPV4 data link layer frames, excluding Ethernet frames (6 source MAC + 6 Destination MAC + 2)
Received the packet destined for the local MAC address. Procedure It is mainly used to obtain underlying data
socket(PF_PACKET, SOCK_RAW, 0)
Get an IPV4 data link layer frame, i.e. data containing Ethernet frame headers (14+20+(8: UDP or 20: TCP))
4. Comparison of two sockets
In the table above we know socket(AF_INET, SOCK_RAW…) Socket (PF_PACKET, SOCK_DGRAM,0) and **socket(PF_PACKET, SOCK_DGRAM,0)** can receive complete IP packets, but what is the difference between the received sockets? Next, a brief explanation:
Knot up is: socket (AF_INET SOCK_RAW, IPPROTO_TCP | IPPROTO_UDP | IPPROTO_ICMP) to send and receive IP packets can * * : ** This socket can receive IP packets destined for the local IP address of the protocol type (TCP UDP ICMP, etc.) ** Cannot: ** Received packets not destined for the local IP address (IP soft filtering discards these packets not destined for the local IP address) ** Cannot: ** You need to organize TCP, UDP, and ICMP headers when receiving packets sent from the host. You can use setsockopt to wrap your own IP header. This socket is suitable for writing a ping program.
The socket (PF_PACKET, SOCK_RAW | SOCK_DGRAM, htons (ETH_P_IP | ETH_P_ARP | ETH_P_ALL)) to send and receive Ethernet data frame. This socket is relatively powerful and can listen for all data frames on the network card
Yes: Receives frames sent to the local MAC address Yes: Receives frames sent from the local PC (the third parameter must be set to ETH_P_ALL) Yes: Receives frames sent from the local PC. ETH_P_IP 0x800 Accepts only IP frames sent to the local MAC address. ETH_P_ARP 0x806 Accepts only ARP frames sent to the local MAC address ETH_P_RARP 0x8035 Accepts only RARP frames sent to the local MAC ETH_P_ALL 0x3 Receives all TYPES of IP ARP data frames sent to the local MAC. Receives all types of data frames sent from the local machine (when promiscuous mode is enabled, non-local MAC data frames will be received)
5. Application example: Grab all IP packets (code reproduced)
#include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <linux/if_packet.h> #include <netinet/if_ether.h> #include <netinet/in.h> typedef Struct _iphdr {unsigned char h_verlen; // 4-bit header length + 4-bit IP version number unsigned char tos; // 8-bit service type TOS unsigned short total_len; //16 bits total length (bytes) unsigned short ident; // unsigned short frag_and_flags; //3 bits unsigned char TTL; //8 bits TTL unsigned char proto; //8 bit protocol (TCP, UDP or other) unsigned short checksum; // 16-bit IP header checksum unsigned int sourceIP; // 32-bit source IP address unsigned int destIP; // 32-bit destination IP address}IP_HEADER; Typedef struct _udphdr // define UDP header {unsigned short uh_sport; // 16-bit source port unsigned short uh_dport; // 16-bit destination port unsigned short uh_len; // 16-bit UDP packet length unsigned short uh_sum; // 16-bit checksum}UDP_HEADER; Typedef struct _tcphdr // define TCP header {unsigned short th_sport; // 16-bit source port unsigned short th_dport; // 16-bit destination port unsigned int th_seq; // Unsigned int th_ack; // Unsigned char th_lenres; //4 bit header length /6 bit reserved word unsigned char th_flag; // unsigned short th_win; // Unsigned short th_sum; // 16-bit checksum unsigned short th_urp; // 16-bit emergency data offset}TCP_HEADER; typedef struct _icmphdr { unsigned char icmp_type; unsigned char icmp_code; /* type sub code */ unsigned short icmp_cksum; unsigned short icmp_id; unsigned short icmp_seq; /* This is not the std header, but we reserve space for time */ unsigned short icmp_timestamp; }ICMP_HEADER; void analyseIP(IP_HEADER *ip); void analyseTCP(TCP_HEADER *tcp); void analyseUDP(UDP_HEADER *udp); void analyseICMP(ICMP_HEADER *icmp); int main(void) { int sockfd; IP_HEADER *ip; char buf[10240]; ssize_t n; /* capture ip datagram without ethernet header */ if ((sockfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)))== -1) { printf("socket error! \n"); return 1; } while (1) { n = recv(sockfd, buf, sizeof(buf), 0); if (n == -1) { printf("recv error! \n"); break; } else if (n==0) continue; // The received data does not include the data link frame header IP = (IP_HEADER *)(buf); analyseIP(ip); size_t iplen = (ip->h_verlen&0x0f)*4; TCP_HEADER *tcp = (TCP_HEADER *)(buf +iplen); if (ip->proto == IPPROTO_TCP) { TCP_HEADER *tcp = (TCP_HEADER *)(buf +iplen); analyseTCP(tcp); } else if (ip->proto == IPPROTO_UDP) { UDP_HEADER *udp = (UDP_HEADER *)(buf + iplen); analyseUDP(udp); } else if (ip->proto == IPPROTO_ICMP) { ICMP_HEADER *icmp = (ICMP_HEADER *)(buf + iplen); analyseICMP(icmp); } else if (ip->proto == IPPROTO_IGMP) { printf("IGMP----\n"); } else { printf("other protocol! \n"); } printf("\n\n"); } close(sockfd); return 0; } void analyseIP(IP_HEADER *ip) { unsigned char* p = (unsigned char*)&ip->sourceIP; printf("Source IP\t: %u.%u.%u.%u\n",p[0],p[1],p[2],p[3]); p = (unsigned char*)&ip->destIP; printf("Destination IP\t: %u.%u.%u.%u\n",p[0],p[1],p[2],p[3]); } void analyseTCP(TCP_HEADER *tcp) { printf("TCP -----\n"); printf("Source port: %u\n", ntohs(tcp->th_sport)); printf("Dest port: %u\n", ntohs(tcp->th_dport)); } void analyseUDP(UDP_HEADER *udp) { printf("UDP -----\n"); printf("Source port: %u\n", ntohs(udp->uh_sport)); printf("Dest port: %u\n", ntohs(udp->uh_dport)); } void analyseICMP(ICMP_HEADER *icmp) { printf("ICMP -----\n"); printf("type: %u\n", icmp->icmp_type); printf("sub code: %u\n", icmp->icmp_code); }Copy the code