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