raw_socket用mac地址進行通信實例(C語言)

因爲實驗室項目的需求需求是需要用socket寫出一個用mac地址通信的鏈接,因爲不是很瞭解socket在網絡上找了一些相關代碼進行修改,自該成下列代碼,裏面又很多代碼被我註釋掉並且又一些代碼沒有用到也沒刪掉主要用於之後可能進行的修改,因爲目前這個代碼是否符合要求不是很有信心,寫本篇博客主要是因爲相關要求的代碼實例很少。

需求:

系統linux,相互通信的雙方必定直接連接,本人是將兩臺電腦網口經過雙絞線直接接起來的,集線器應該也可以,但是路由器不知行不行。

代碼:C

代碼需求:希望可以使用mac地址來進行通信,尤其是廣播通信,但是又希望沒有多餘的頭部,所以有了這麼一個奇怪的要求

代碼簡單分爲client和server:

server代碼:

//捕獲二層包,並把數據輸出
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include<memory.h>
#include<stdlib.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h> // sockaddr_ll
#include<arpa/inet.h>
#include<netinet/if_ether.h>
//#include<iomanip>
#include<errno.h>
#include<netinet/ether.h>
#include<net/if.h>
#include<string.h>
//#include<iostream>
//using namespace std;
int main(int argc, char **argv) {
	int sock, n;
	char buffer[1024];


	if ((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0)
	{
		perror("socket");
		exit(1);
	}

	struct sockaddr_ll client;
	socklen_t addr_length = sizeof(struct sockaddr_ll);
	uint8_t sendbuffer[1024];

	while (1) {
		n = recvfrom(sock, buffer,1024,0, (struct sockaddr *)&client, &addr_length);
		if (n < 14) {
			continue;
		}
		//cout << AF_PACKET << "   " << client.sll_family << endl;
		//cout << htons(6) << "    " << client.sll_halen << endl;

		printf("受到消息%d個\n",n-14);
		int num = n-14;
		memcpy(sendbuffer, buffer, n);
		char data[1024];data[24]='\0';
                memcpy(data,sendbuffer+14,num);
		printf("%s\n",data);
       		//printf("masg is %x,%x,%x,%x,%x,%x to %x,%x,%x,%x,%x,%x\n",buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5],buffer[6],buffer[7],buffer[8],buffer[9],buffer[10],buffer[11]);
		
		/*memcpy(sendbuffer, buffer + 6, 6);
		memcpy(sendbuffer + 6, buffer, 6);

		memcpy(sendbuffer + 14 + 12, buffer + 14 + 16, 4);
		memcpy(sendbuffer + 14 + 16, buffer + 14 + 12, 4);

		//memset(sendbuffer + 14 + 20, 0, n - 14 - 20);

		n = sendto(sock, sendbuffer, n, 0, (struct sockaddr *) &client, sizeof(client));
		cout << n << endl;*/

	}

}

client代碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>				//struct ifreq
#include <sys/ioctl.h>			//ioctl、SIOCGIFADDR
#include <sys/socket.h>
#include <netinet/ether.h>		//ETH_P_ALL
#include <netpacket/packet.h>	//struct sockaddr_ll
 #include <arpa/inet.h>
  #include <sys/types.h>
#include<unistd.h>    //close
unsigned short checksum(unsigned short *buf, int nword);//校驗和函數
int main(int argc, char *argv[])
{
	//1.創建通信用的原始套接字
	int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	
	//2.根據各種協議首部格式構建發送數據報
	unsigned char send_msg[1024] = {
		//--------------組MAC--------14------
		0x8c, 0xec, 0x4b, 0x73, 0xae, 0x04, //dst_mac: E8-6A-64-17-CB-5C目的地址
		0x8c, 0xec, 0x4b, 0x73, 0x79, 0xb5, //src_mac: 8c:ec:4b:73:79:b5源mac地址
		0x08, 0x00,                         //類型:0x0800 IP協議
	/*	//--------------組IP---------20------
		0x45, 0x00, 0x00, 0x00,             //版本號:4, 首部長度:20字節, TOS:0, --總長度--:
		0x00, 0x00, 0x00, 0x00,				//16位標識、3位標誌、13位片偏移都設置0
		0x80, 17,   0x00, 0x00,				//TTL:128、協議:UDP(17)、16位首部校驗和
		10,  221,   20,  11,				//src_ip: 10.221.20.11
		10,  221,   20,  10,				//dst_ip: 10.221.20.10
		//--------------組UDP--------8+78=86------
		0x1f, 0x90, 0x1f, 0x90,             //src_port:0x1f90(8080), dst_port:0x1f90(8080)
		0x00, 0x00, 0x00, 0x00,               //#--16位UDP長度--30個字節、#16位校驗和*/
	};
	
	int len = sprintf(send_msg+14, "%s", "this is for the udp test");
/*	if(len % 2 == 1)//判斷len是否爲奇數
	{
		len++;//如果是奇數,len就應該加1(因爲UDP的數據部分如果不爲偶數需要用0填補)
	}
	
	*((unsigned short *)&send_msg[16]) = htons(20+8+len);//IP總長度 = 20 + 8 + len
	*((unsigned short *)&send_msg[14+20+4]) = htons(8+len);//udp總長度 = 8 + len
	//3.UDP僞頭部
	unsigned char pseudo_head[1024] = {
		//------------UDP僞頭部--------12--
		192,  168,   0,  1,				//src_ip: 10.221.20.11
		192,  168,   0,  255,				//dst_ip: 10.221.20.10
		0x00, 17,   0x00, 0x00,             	//0,17,#--16位UDP長度--20個字節
	};
	
	*((unsigned short *)&pseudo_head[10]) = htons(8 + len);//爲頭部中的udp長度(和真實udp長度是同一個值)
	//4.構建udp校驗和需要的數據報 = udp僞頭部 + udp數據報
	memcpy(pseudo_head+12, send_msg+34, 8+len);//--計算udp校驗和時需要加上僞頭部--
	//5.對IP首部進行校驗
	*((unsigned short *)&send_msg[24]) = htons(checksum((unsigned short *)(send_msg+14),20/2));
	//6.--對UDP數據進行校驗--
	*((unsigned short *)&send_msg[40]) = htons(checksum((unsigned short *)pseudo_head,(12+8+len)/2));
	
	*/
	//發送數據
	struct sockaddr_ll sll;					//原始套接字地址結構
	struct ifreq req;					//網絡接口地址
	
	strncpy(req.ifr_name, "enp0s31f6", IFNAMSIZ);			//指定網卡名稱
	if(-1 == ioctl(sock_raw_fd, SIOCGIFINDEX, &req))	//獲取網絡接口
	{
		perror("ioctl");
		close(sock_raw_fd);
		exit(-1);
	}
	
	/*將網絡接口賦值給原始套接字地址結構*/
	bzero(&sll, sizeof(sll));
	sll.sll_ifindex = req.ifr_ifindex;
	len = sendto(sock_raw_fd, send_msg, 14+len, 0 , (struct sockaddr *)&sll, sizeof(sll));
	if(len == -1)
	{
		perror("sendto");
	}
	return 0;
}
 
unsigned short checksum(unsigned short *buf, int nword)
{
	unsigned long sum;
	for(sum = 0; nword > 0; nword--)
	{
		sum += htons(*buf);
		buf++;
	}
	sum = (sum>>16) + (sum&0xffff);
	sum += (sum>>16);
	return ~sum;
}

最終達到可以輸出傳輸數據的效果,但是接收端會接收很多的無關消息而且因爲一些電腦機制的問題,長度最小是60,大家有什麼建議可以提出來。

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