The realization of forging udp package and binding network card to send data under linux

1. Include header file

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <stdbool.h>
#include <net/if.h>

2. Set src and dest, and define a global socket at the same time. We take the single column mode as the sub column

#define DEST_IP "192.168.1.53"
#define DEST_PORT 996
#define SRC_IP "192.168.1.56"
#define SRC_PORT 998
#define TEST_MESSAGE "This test udp message!"
#define NETWORK_CAP "ens33"
unsigned int m_OutSocket;
struct sockaddr_in m_SocketOut;

DEST: destination IP, to which IP

SRC: native IP, that is, native IP. This determines which IP is selected to send packets and which network card is bound to this IP

3. Set initialization function

void init_udp_socket(){
    

}

3.1 initialize socket first

m_OutSocket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

AF_ INET: communication protocol of IPv4

sock_raw: receive tcp udp icmp sctp protocol, and do not receive the response of data packets sent by the local machine. Secondly, do not receive packets that do not belong to the local IP

ipproto_udp:udp protocol

3.2 set the socket attribute to construct a hand for yourself and tell the socket not to construct a hand header for us

bool flag = true;
setsockopt(m_OutSocket, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag));

IPPROTO_IP:ipv4 protocol

IP_HDRINCL: the user needs to construct the protocol header. Here, the user can specify which IP to use. If this protocol is not used, the socket will automatically select an IP to use

3.3 setting send delay time

It should be noted that the send time parameter under linux is a structure

struct timeval nTimeOver={10,0};
setsockopt(m_OutSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&nTimeOver,sizeof(struct timeval));

3.4 specify dest ip and dest port of the current socket

In fact, you can not specify it, but you can't do it without specifying it, because we need to construct the hand ourselves. If you don't specify it in this step, you can't construct dest ip and prot. This is the protocol specification of socket sending packets

We used SOCK_RAW protocol, so it will not receive feedback packets. This is just to comply with the socket specification

m_SocketOut.sin_family = AF_INET;
m_SocketOut.sin_addr.s_addr = inet_addr(DEST_IP);
m_SocketOut.sin_port = (u_short)DEST_PORT;

3.5 binding network card

 struct ifreq ifr;
 memset(&ifr, 0, sizeof(ifr));
 strncpy(ifr.ifr_name, NETWORK_CAP, strlen(NETWORK_CAP));
 setsockopt(m_OutSocket, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifr, sizeof(ifr));

At this point, the initialization function is finished

Next, we write the send function

The sending function is complicated because we need to construct the udp hand package

4. Write sending function

Function prototype

void send(const char* buff,int length){

    if( buff == NULL){
        return;
    }

}

Before we start, we define the hand package of IP and UDP:

IP:

typedef struct stuIPHEADER
{
        unsigned char h_lenver;			// 8bit 4bit version + 4bit header length (h_lenver &0xf) * 4
        unsigned char tos;				// 8bit service type
        unsigned short total_len;		// Total length of 16bit (bytes)
        unsigned short ident;			// 16bit identification
        unsigned short frag_and_flags;	// 16bit 3bit Flag + 13bit chip offset
        unsigned char ttl;				// 8bit lifetime (TTL)
        unsigned char proto;			// 8bit upper layer protocol
        unsigned short checksum;		// 16bit inspection and
        unsigned int sourceIP;			// 32bit source IP address
        unsigned int destIP;			// 32bit destination IP address
}IPHEADER, *LPIPHEADER;

UDP:

typedef struct _UDP_HEADER {
    unsigned short    nSourPort ;            // Source port number
    unsigned short    nDestPort ;            // Destination port number
    unsigned short    nLength ;				 // Packet length
    unsigned short    nCheckSum ;            // Checksum
} UDP_HEADER, *PUDP_HEADER ;

Secondly, there are PSD:

typedef struct _PSD_HEADER{
	unsigned long         saddr;	//Source IP address               
	unsigned long         daddr;	//Destination IP address
	char                  mbz;		//Empty (0)
	char                  ptcl;		//Protocol type
	unsigned short        plen;     //Length of TCP/UDP packet (i.e. the length from the TCP/UDP header to the end of the packet, unit: bytes)
} UDP_PSDHEADER,*PUDP_PSDHEADER ;

Then define a sender

#define MAX_UDP_DATA_LEN 65536
char SendBuf[MAX_UDP_DATA_LEN];

Then the construct is defined in the function body and constructed

IPHEADER ipHeader;
memset(&ipHeader,0,sizeof(IPHEADER));

Construction Code:

void ConstructIPHeader(IPHEADER *pIpHeader, int dataLength)
{

	pIpHeader->h_lenver			= 0x45; 	//ip v4
	pIpHeader->tos				= 0;
	pIpHeader->total_len			= htons(dataLength);
	pIpHeader->ident            		= htons(rand());
	pIpHeader->frag_and_flags		= 0;
	pIpHeader->ttl				= 128;
	pIpHeader->proto			= IPPROTO_UDP;
	pIpHeader->checksum			= 0;
	pIpHeader->sourceIP        		= inet_addr(SRC_IP);
	pIpHeader->destIP			= inet_addr(DEST_IP);
}

Construct UDP package:

UDP_HEADER udpHeader;
memset(&udpHeader,0,sizeof(UDP_HEADER));

Construction Code:

void ConstructUdpHeader(UDP_HEADER *pUdpHeader, int dataLength)
{
    pUdpHeader->nSourPort	= htons((unsigned short)(SRC_PORT));
    pUdpHeader->nDestPort	= htons((unsigned short)(DEST_PORT));
    pUdpHeader->nLength	= htons(sizeof(UDP_HEADER) + dataLength);
    pUdpHeader->nCheckSum	= 0;
}

Initialize the sender and include hand, buff and copy

 memset(SendBuf, 0, MAX_UDP_DATA_LEN);
 memcpy(SendBuf, &ipHeader, sizeof(IPHEADER));
 memcpy(SendBuf+sizeof(IPHEADER), &udpHeader, sizeof(UDP_HEADER));
 memcpy(SendBuf+sizeof(IPHEADER)+sizeof(UDP_HEADER), buff, length);	

Finally, construct the message

This message code is not my original, but is directly obtained from the udp message code constructed in the open source of socket programming.

Construction message code:

unsigned short CalculateChecksum(char *buffer1, int len1, char *buffer2, int len2)
{
	unsigned long checksum=0;
	unsigned short* buffer;
	int i=0;
	buffer = (unsigned short*) buffer1;
	for (i=0; i<int(len1/sizeof(unsigned short)); i++)
		checksum += buffer[i];
	
	buffer = (unsigned short*) buffer2;
	for (i=0; i<int(len2/sizeof(unsigned short)); i++)
		checksum += buffer[i];
	
	if ((len2 & 0x1) != 0) 
        checksum += (unsigned char) buffer2[len2-1];
	
	checksum = (checksum >> 16) + (checksum & 0xffff);
	checksum += (checksum >>16); 
	return (unsigned short)(~checksum); 
}

Then write IP message and UDP message based on message function

Construct IP message function:

void FinalIPHeader(char *pIpAndDataBuffer, int length)
{
	IPHEADER* pIpHeader = (IPHEADER*) pIpAndDataBuffer;
	char* pDataBuffer = pIpAndDataBuffer + sizeof(IPHEADER);
	int dataLen = length - sizeof(IPHEADER);
	pIpHeader->checksum = CalculateChecksum(pIpAndDataBuffer, sizeof(IPHEADER),         pDataBuffer, dataLen);
	pIpHeader->checksum = CalculateChecksum(pIpAndDataBuffer, sizeof(IPHEADER), pDataBuffer, 0);
}

Function for constructing UDP message:

void CRawSocket::FinalUdpHeader(char *pUdpAndDataBuffer, int length)
{
	UDP_PSDHEADER UDP_PSD_HEADER;
	memset(&UDP_PSD_HEADER,0,sizeof(UDP_PSDHEADER));
	UDP_HEADER* pUdpHeader = (UDP_HEADER*) pUdpAndDataBuffer;
	char* pDataBuffer = pUdpAndDataBuffer + sizeof(UDP_HEADER);
	int dataLen = length - sizeof(UDP_HEADER);
	
	UDP_PSD_HEADER.saddr = inet_addr(SRC_IP);
	UDP_PSD_HEADER.daddr = inet_addr(DEST_IP);
	UDP_PSD_HEADER.mbz   = 0; 
	UDP_PSD_HEADER.ptcl  = IPPROTO_UDP; 
	UDP_PSD_HEADER.plen  = ::htons(length); 
	
	pUdpHeader->nCheckSum = CalculateChecksum((char*) &UDP_PSD_HEADER, 
    sizeof(UDP_PSD_HEADER), pUdpAndDataBuffer, length);

}

Then we construct:

    FinalIPHeader(SendBuf,length+sizeof(IPHEADER)+sizeof(UDP_HEADER));
	FinalUdpHeader(SendBuf+sizeof(IPHEADER),length+sizeof(UDP_HEADER));

Then we can call send_to send it to us because send_to cannot construct a hand package, so send must be used here_ to

sendto(m_OutSocket,SendBuf,length+sizeof(IPHEADER)+sizeof(UDP_HEADER), 0,(struct sockaddr*) &m_SocketOut,sizeof(struct sockaddr_in));

That's it.

Complete code + demo:

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <stdbool.h>
#include <net/if.h>

#define DEST_IP "192.168.1.53"
#define DEST_PORT 996
#define SRC_IP "192.168.1.56"
#define SRC_PORT 998
#define TEST_MESSAGE "This test udp message!"
#define NETWORK_CAP "ens33"
unsigned int m_OutSocket;
struct sockaddr_in m_SocketOut;

typedef struct stuIPHEADER
{
        unsigned char h_lenver;			// 8bit 4bit version + 4bit header length
        						// (h_lenver &0xf) * 4
        unsigned char tos;				// 8bit service type
        unsigned short total_len;		// Total length of 16bit (bytes)
        unsigned short ident;			// 16bit identification
        unsigned short frag_and_flags;	// 16bit 3bit Flag + 13bit chip offset
        unsigned char ttl;				// 8bit lifetime (TTL)
        unsigned char proto;			// 8bit upper layer protocol
        unsigned short checksum;		// And 16bit inspection
        unsigned int sourceIP;			// 32bit source IP address
        unsigned int destIP;			// 32bit destination IP address
}IPHEADER, *LPIPHEADER;

typedef struct _UDP_HEADER {
    unsigned short    nSourPort ;            // Source port number
    unsigned short    nDestPort ;            // Destination port number
    unsigned short    nLength ;				 // Packet length
    unsigned short    nCheckSum ;            // Checksum
} UDP_HEADER, *PUDP_HEADER ;

typedef struct _PSD_HEADER{
	unsigned long         saddr;	//Source IP address               
	unsigned long         daddr;	//Destination IP address
	char                  mbz;		//Empty (0)
	char                  ptcl;		//Protocol type
	unsigned short        plen;     //Length of TCP/UDP packet (i.e. the length from the TCP/UDP header to the end of the packet, unit: bytes)
} UDP_PSDHEADER,*PUDP_PSDHEADER ;

#define MAX_UDP_DATA_LEN 65536
char SendBuf[MAX_UDP_DATA_LEN];

void init_udp_socket(){

       m_OutSocket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
       
       bool flag = true;
       setsockopt(m_OutSocket, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag));
       
       struct timeval nTimeOver={10,0};
       setsockopt(m_OutSocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&nTimeOver,sizeof(struct timeval));

       struct ifreq ifr;
       memset(&ifr, 0, sizeof(ifr));
       strncpy(ifr.ifr_name, NETWORK_CAP, strlen(NETWORK_CAP));
       setsockopt(m_OutSocket, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifr, sizeof(ifr));
    
       m_SocketOut.sin_family = AF_INET;
       m_SocketOut.sin_addr.s_addr = inet_addr(DEST_IP);
       m_SocketOut.sin_port = (u_short)DEST_PORT;
      
}

unsigned short CalculateChecksum(char *buffer1, int len1, char *buffer2, int len2)
{
	unsigned long checksum=0;
	unsigned short* buffer;
	int i=0;
	buffer = (unsigned short*) buffer1;
	for (i=0; i<int(len1/sizeof(unsigned short)); i++)
		checksum += buffer[i];
	
	buffer = (unsigned short*) buffer2;
	for (i=0; i<int(len2/sizeof(unsigned short)); i++)
		checksum += buffer[i];
	
	if ((len2 & 0x1) != 0) 
        checksum += (unsigned char) buffer2[len2-1];
	
	checksum = (checksum >> 16) + (checksum & 0xffff);
	checksum += (checksum >>16); 
	return (unsigned short)(~checksum); 
}

void FinalIPHeader(char *pIpAndDataBuffer, int length)
{
	IPHEADER* pIpHeader = (IPHEADER*) pIpAndDataBuffer;
	char* pDataBuffer = pIpAndDataBuffer + sizeof(IPHEADER);
	int dataLen = length - sizeof(IPHEADER);
	pIpHeader->checksum = CalculateChecksum(pIpAndDataBuffer, sizeof(IPHEADER),         pDataBuffer, dataLen);
	pIpHeader->checksum = CalculateChecksum(pIpAndDataBuffer, sizeof(IPHEADER), pDataBuffer, 0);
}


void FinalUdpHeader(char *pUdpAndDataBuffer, int length)
{
	UDP_PSDHEADER UDP_PSD_HEADER;
	memset(&UDP_PSD_HEADER,0,sizeof(UDP_PSDHEADER));
	UDP_HEADER* pUdpHeader = (UDP_HEADER*) pUdpAndDataBuffer;
	char* pDataBuffer = pUdpAndDataBuffer + sizeof(UDP_HEADER);
	int dataLen = length - sizeof(UDP_HEADER);
	
	UDP_PSD_HEADER.saddr = inet_addr(SRC_IP);
	UDP_PSD_HEADER.daddr = inet_addr(DEST_IP);
	UDP_PSD_HEADER.mbz   = 0; 
	UDP_PSD_HEADER.ptcl  = IPPROTO_UDP; 
	UDP_PSD_HEADER.plen  = htons(length); 
	
	pUdpHeader->nCheckSum = CalculateChecksum((char*) &UDP_PSD_HEADER, sizeof(UDP_PSD_HEADER), pUdpAndDataBuffer, length);

}

void ConstructUdpHeader(UDP_HEADER *pUdpHeader, int dataLength)
{
    pUdpHeader->nSourPort	= htons((unsigned short)(SRC_PORT));
    pUdpHeader->nDestPort	= htons((unsigned short)(DEST_PORT));
    pUdpHeader->nLength	= htons(sizeof(UDP_HEADER) + dataLength);
    pUdpHeader->nCheckSum	= 0;
}

void ConstructIPHeader(IPHEADER *pIpHeader, int dataLength)
{

	pIpHeader->h_lenver			= 0x45; 	//ip v4
	pIpHeader->tos				= 0;
	pIpHeader->total_len			= htons(dataLength);
	pIpHeader->ident            		= htons(rand());
	pIpHeader->frag_and_flags		= 0;
	pIpHeader->ttl				= 128;
	pIpHeader->proto			= IPPROTO_UDP;
	pIpHeader->checksum			= 0;
	pIpHeader->sourceIP        		= inet_addr(SRC_IP);
	pIpHeader->destIP			= inet_addr(DEST_IP);
}

void send_my(const char* buff,int length){

    if( buff == NULL){
        return;
    }

   IPHEADER ipHeader;
   memset(&ipHeader,0,sizeof(IPHEADER));
   ConstructIPHeader(&ipHeader,length+sizeof(IPHEADER)+sizeof(UDP_HEADER));

   UDP_HEADER udpHeader;
   memset(&udpHeader,0,sizeof(UDP_HEADER));
   ConstructUdpHeader(&udpHeader,length);
   
   memset(SendBuf, 0, MAX_UDP_DATA_LEN);
   memcpy(SendBuf, &ipHeader, sizeof(IPHEADER));
   memcpy(SendBuf+sizeof(IPHEADER), &udpHeader, sizeof(UDP_HEADER));
   memcpy(SendBuf+sizeof(IPHEADER)+sizeof(UDP_HEADER), buff, length);	
  
   FinalIPHeader(SendBuf,length+sizeof(IPHEADER)+sizeof(UDP_HEADER));
   FinalUdpHeader(SendBuf+sizeof(IPHEADER),length+sizeof(UDP_HEADER));

   
   sendto(m_OutSocket,SendBuf,(length+sizeof(IPHEADER)+sizeof(UDP_HEADER)), 0,(struct sockaddr*) &m_SocketOut,sizeof(struct sockaddr_in));
}

int main(){
 
	init_udp_socket();
	while(1){
		send_my(TEST_MESSAGE,strlen(TEST_MESSAGE));
		usleep(200);
	}

}

When the program is running, you can use tcpdump to grab packets, or wireshark to grab packets

Here, in order to facilitate you to watch the demonstration, I use wireshark to capture the package and demonstrate to you:

Because it is a virtual machine and has only one network card, I use one network card for demonstration:

Set Src in macro definition_ Change the IP to the corresponding IP of the network card:

#define SRC_IP "192.168.1.56"

Then test to see:

You can also see in the network card address:

Network card address in wireshark:

This indicates that we have not been optimized by the udp optimal network packet algorithm.

There will be an optimal network packet algorithm in udp. When you use udp to contract, even if you bind the IP of network card A, udp will call the algorithm in the protocol stack to determine which network card is most suitable for sending. The route is not congested. Even if the IP does not change, the network card address will change, that is, the IP of network card A you bind, However, the protocol packet contains the physical address of the B network card, which will cause the physical link layer to select the B network card to send your packet.

Keywords: Linux socket udp

Added by pppppp on Wed, 09 Feb 2022 22:38:02 +0200