原始套接字的學習(用原始套接字 發送普通udp信息)

用原始套接字 發送普通udp信息

ubuntu 發送udp數據 到windows上
在這裏插入圖片描述

1、組mac頭部:

unsigned char msg[1024]="";
	//1、組mac頭部
	struct ether_header *eth_addr = (struct ether_header *)msg;
	//賦值目的mac地址
	memcpy(eth_addr->ether_dhost, dst_mac, 6);
	//賦值源mac地址
	memcpy(eth_addr->ether_shost, src_mac, 6);
	//賦值幀類型 
	eth_addr->ether_type = htons(0x0800);

2、組IP頭

struct iphdr;      //所在位置:/usr/include/netinet/ip.h #include <netinet/ip.h>

在這裏插入圖片描述

struct iphdr *ip_hdr = (struct iphdr *)(msg+14);
	ip_hdr->version = 4;//IPv4版本
	ip_hdr->ihl = 5;//IP頭部長度 單位4B 所以賦值5其實就是5*4=20B
	ip_hdr->tos = 0;//服務類型
	ip_hdr->tot_len = htons(20+8+data_len);//總長度=IP首部長度+IP數據長度
	ip_hdr->id = htons(0);//標識
	ip_hdr->frag_off = htons(0);//標誌 + 片偏移
	ip_hdr->ttl = 128;//64或128都可以 生命週期
	ip_hdr->protocol = 17;//udp 17   tcp 6
	ip_hdr->check = htons(0);//首部校驗???? 後續校驗
	memcpy(&ip_hdr->saddr, src_ip, 4);
	memcpy(&ip_hdr->daddr, dst_ip, 4);
	//ip報文頭部校驗
	ip_hdr->check = checksum(ip_hdr, 20);

3、組UDP頭

struct udphdr ;   //所在位置:/usr/include/netinet/udp.h #include <netinet/udp.h>

在這裏插入圖片描述
UDP校驗方式:
1、在對UDP校驗的時候需要在UDP報文之間加上僞頭部。
IP校驗的時候不需要僞頭部
在這裏插入圖片描述
僞頭部中的源ip、目的IP必須和 IP報文中源ip、目的IP 一致。

typedef struct
{
	u_int32_t saddr;//源IP
	u_int32_t daddr;//目的IP
	u_int8_t flag;//標記(0)
	u_int8_t type;//udp協議 17
	u_int16_t len;//長度
}WEIHDR;
unsigned short checksum(unsigned short *buf, int len)
{
	int nword = len/2;
	unsigned long sum;

	if(len%2 == 1)
		nword++;
	for(sum = 0; nword > 0; nword--)
	{
		sum += *buf;
		buf++;
	}
	sum = (sum>>16) + (sum&0xffff);
	sum += (sum>>16);
	return ~sum;
}
//3、udp頭部
	struct udphdr *udp_hdr = (struct udphdr *)(msg+14+20);
	udp_hdr->source = htons(8000);//源端口
	udp_hdr->dest = htons(9000);//目的端口
	udp_hdr->len = htons(8+data_len);//udp總長度=udp報文頭+數據長
	udp_hdr->check = htons(0);//???? udp校驗
	//將data拷貝到udp的數據部分
	memcpy(msg+14+20+8, data, data_len);
	
	//準備udp校驗
	unsigned char wei_head[256]="";
	WEIHDR *wei_hdr = (WEIHDR *)wei_head;
	memcpy(&wei_hdr->saddr, src_ip, 4);//源IP
	memcpy(&wei_hdr->daddr, dst_ip, 4);//目的IP
	wei_hdr->flag = 0;
	wei_hdr->type = 17;//協議
	wei_hdr->len = htons(8+data_len);//udp的總長度
	//將msg中的udp頭部信息以及data數據 拷貝到爲頭部後方
	memcpy(wei_head+12, udp_hdr, 8+data_len);
	
	//校驗udp: 爲頭部+udp頭部+data部分
	udp_hdr->check = checksum(wei_head, 12+8+data_len);

完整代碼如下:

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/ether.h>
#include <sys/ioctl.h>//ioctl
#include <net/if.h>//struct ifreq
#include <netpacket/packet.h>//struct sockaddr_ll
#include<unistd.h>//_exit
#include<string.h>//strncpy
#include <net/ethernet.h>//struct ether_header
#include <net/if_arp.h>//struct arphdr
#include <netinet/ip.h>//struct iphdr
#include <netinet/udp.h>//struct udphdr
void my_sendto(int sockfd, char *out, unsigned char *msg, int msg_len);
typedef struct
{
	u_int32_t saddr;//源IP
	u_int32_t daddr;//目的IP
	u_int8_t flag;//標記(0)
	u_int8_t type;//udp協議 17
	u_int16_t len;//長度
}WEIHDR;
unsigned short checksum(unsigned short *buf, int len)
{
	int nword = len/2;
	unsigned long sum;

	if(len%2 == 1)
		nword++;
	for(sum = 0; nword > 0; nword--)
	{
		sum += *buf;
		buf++;
	}
	sum = (sum>>16) + (sum&0xffff);
	sum += (sum>>16);
	return ~sum;
}
int main()
{
	//1、創建原始套接字
	int sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	if(sockfd < 0)
	{
		perror("socket");
		return 0;
	}
	//獲取要發送的消息
	printf("請輸入要發送的消息:");
	char data[128]="";
	fgets(data,sizeof(data),stdin);
	data[strlen(data)-1]=0;//去掉回車
	int data_len = strlen(data);
	//如果data_len不是偶數 補0爲偶數
	if(data_len%2 != 0)//奇數
		data_len++;
	
	/*目的mac地址 XPmac*/
	unsigned char dst_mac[6]={0x70,0x5A,0x0F,0x63,0xF5,0x9D};//win_mac
	unsigned char dst_ip[4]={192,168,0,110};//win_ip
	unsigned char src_mac[6]={0x00,0x0c,0x29,0x79,0xf9,0x7f};//ubuntu_mac
	unsigned char src_ip[4]={192,168,0,111};//ubuntu_ip
	
	
	unsigned char msg[1024]="";
	//1、組mac頭部
	struct ether_header *eth_addr = (struct ether_header *)msg;
	//賦值目的mac地址
	memcpy(eth_addr->ether_dhost, dst_mac, 6);
	//賦值源mac地址
	memcpy(eth_addr->ether_shost, src_mac, 6);
	//賦值幀類型 
	eth_addr->ether_type = htons(0x0800);
	
	//2、組IP報文
	struct iphdr *ip_hdr = (struct iphdr *)(msg+14);
	ip_hdr->version = 4;//IPv4版本
	ip_hdr->ihl = 5;//IP頭部長度 單位4B 所以賦值5其實就是5*4=20B
	ip_hdr->tos = 0;//服務類型
	ip_hdr->tot_len = htons(20+8+data_len);//總長度=IP首部長度+IP數據長度
	ip_hdr->id = htons(0);//標識
	ip_hdr->frag_off = htons(0);//標誌 + 片偏移
	ip_hdr->ttl = 128;//64或128都可以 生命週期
	ip_hdr->protocol = 17;//udp 17   tcp 6
	ip_hdr->check = htons(0);//首部校驗???? 後續校驗
	memcpy(&ip_hdr->saddr, src_ip, 4);//源IP
	memcpy(&ip_hdr->daddr, dst_ip, 4);//目的IP
	//ip報文頭部校驗
	ip_hdr->check = checksum(ip_hdr, 20);
	
	//3、udp頭部
	struct udphdr *udp_hdr = (struct udphdr *)(msg+14+20);
	udp_hdr->source = htons(8000);//源端口
	udp_hdr->dest = htons(9000);//目的端口
	udp_hdr->len = htons(8+data_len);//udp總長度=udp報文頭+數據長
	udp_hdr->check = htons(0);//???? udp校驗
	//將data拷貝到udp的數據部分
	memcpy(msg+14+20+8, data, data_len);
	
	//準備udp校驗
	unsigned char wei_head[256]="";
	WEIHDR *wei_hdr = (WEIHDR *)wei_head;
	memcpy(&wei_hdr->saddr, src_ip, 4);//源IP
	memcpy(&wei_hdr->daddr, dst_ip, 4);//目的IP
	wei_hdr->flag = 0;
	wei_hdr->type = 17;//協議
	wei_hdr->len = htons(8+data_len);//udp的總長度
	//將msg中的udp頭部信息以及data數據 拷貝到爲頭部後方
	memcpy(wei_head+12, udp_hdr, 8+data_len);
	
	//校驗udp: 爲頭部+udp頭部+data部分
	udp_hdr->check = checksum(wei_head, 12+8+data_len);
	
	//發送arp請求幀數據
	my_sendto(sockfd, "eth0",msg, 14+20+8+data_len);
	
	close(sockfd);
	return 0;
}
void my_sendto(int sockfd, char *out, unsigned char *msg, int msg_len)
{
	//通過ioctl得到網絡接口
	struct ifreq ethreq;
	strncpy(ethreq.ifr_name, out, IFNAMSIZ);
	if(-1 == ioctl(sockfd, SIOCGIFINDEX, &ethreq))
	{
		perror("ioctl");
		close(sockfd);
		_exit(-1);
	}
	
	//幀數據 出去的本地接口
	struct sockaddr_ll sll;
	bzero(&sll,sizeof(sll));
	sll.sll_ifindex = ethreq.ifr_ifindex;
	//2、發送組好報文的幀數據
	sendto(sockfd, msg, msg_len, 0, (struct sockaddr *)&sll, sizeof(sll));
}

運行結果:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章