IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 【JokerのZYNQ7020】USER_TYPE_ETH_LINUX -> 正文阅读

[网络协议]【JokerのZYNQ7020】USER_TYPE_ETH_LINUX

软件环境:vivado 2017.4? ? ? ? 硬件平台:XC7Z020


之前已经说了在linux下,怎么建立UDP、TCP-server、TCP-client的应用,而这些都是带有特定协议的以太网数据包,所以就好奇啦,有没有办法发送自定义type类型的以太网数据包呢?当然啦,是有的,切入点就是

int socket( int af, int type, int protocol);

?建立socket套接字函数中的第二个参数type,它常用的有以下几种,分别含义如下。

参数名称解释
SOCK_STREAMTCP连接,提供序列化的、可靠的、双向连接的字节流,支持带外数据传输
SOCK_DGRAMUDP,支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务
SOCK_SEQPACKET序列化包,提供一个序列化的、可靠的、双向的基本连接的数据传输通道,数据长度定常。每次调用读系统调用时数据需要将全部数据读出
SOCK_RAWRAW类型,提供原始网络协议访问
SOCK_RDM提供可靠的数据报文,不过可能数据会有乱序
SOCK_PACKET这是一个专用类型,直接从设备驱动接收数据

今天要举例说明的就是通过SOCK_RAW来发送自定义type类型的以太网数据包。

当然,工程不变。

?接下来,上代码。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>				
#include <string.h>				
#include <netdb.h>				
#include <sys/types.h>			
#include <sys/socket.h>			
#include <netinet/in.h>			
#include <netinet/ip.h>			
#include <netinet/ip_icmp.h>	
#include <arpa/inet.h>			
#include <sys/ioctl.h>			
#include <bits/ioctls.h>		
#include <net/if.h>				
#include <linux/if_ether.h>		
#include <linux/if_packet.h>	
#include <net/ethernet.h>			
#include <errno.h>				

#define ETH_USER_PROTOCOL 0x6678		//自定义的以太网协议type

int main (int argc, char **argv)
{
	int i, datalen,frame_length, sd, bytes;
	char *interface="eth0";
	uint8_t data[IP_MAXPACKET];
	uint8_t src_mac[6];
	uint8_t dst_mac[6];
	uint8_t ether_frame[IP_MAXPACKET];
	struct sockaddr_ll device;
	struct ifreq ifr;
	
	//第一次创建socket是为了获取本地网卡信息
	if((sd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0)
	{
		perror("socket() failed to get socket descriptor for using ioctl() ");
		exit(EXIT_FAILURE);
	}
	
	memset(&ifr, 0, sizeof (ifr));
	snprintf(ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface);
	
	//寻找interface所对应的本地网络接口的MAC地址
	if(ioctl (sd, SIOCGIFHWADDR, &ifr) < 0)
	{
		perror("ioctl() failed to get source MAC address ");
		return(EXIT_FAILURE);
	}
	
	close(sd);
	
	//将获取到的MAC地址,给src_mac变量
	memcpy(src_mac, ifr.ifr_hwaddr.sa_data, 6);
	
	printf("MAC address for interface %s is ", interface);
	for(i=0;i<5;i++)
	{
		printf("%02x:", src_mac[i]);
	}	
	printf("%02x\n", src_mac[5]);
	
	memset(&device, 0, sizeof (device));
	
	//获取interface变量所指向的本地网络接口的索引号
	if((device.sll_ifindex = if_nametoindex (interface)) == 0)
	{
		perror("if_nametoindex() failed to obtain interface index ");
		exit(EXIT_FAILURE);
	}
	
	printf("Index for interface %s is %i\n", interface, device.sll_ifindex);
	
	//设置目的网卡地址
	dst_mac[0] = 0x10;
	dst_mac[1] = 0x78;
	dst_mac[2] = 0xd2;
	dst_mac[3] = 0xc6;
	dst_mac[4] = 0x2f;
	dst_mac[5] = 0x89;
	
	//填充device结构体其余必要参数
	device.sll_family = AF_PACKET;
	memcpy(device.sll_addr, src_mac, 6);
	device.sll_halen = htons(6);
	
	//发送的data,最小数据长度为46,不足的会自动补零处理
	datalen = 12;
	data[0] = 'h';
	data[1] = 'e';
	data[2] = 'l';
	data[3] = 'l';
	data[4] = 'o';
	data[5] = ' ';
	data[6] = 'j';
	data[7] = 'o';
	data[8] = 'k';
	data[9] = 'e';
	data[10] = 'r';
	data[11] = '!';
	
	//发送帧总长计算,DST_MAC + SRC_MAC + ETH_USER_PROTOCOL + DATA
	frame_length = 6+6+2+datalen;
	
	//发送帧填充
	memcpy(ether_frame, dst_mac, 6);
	memcpy(ether_frame + 6, src_mac, 6);
	
	ether_frame[12] = ETH_USER_PROTOCOL / 256;
	ether_frame[13] = ETH_USER_PROTOCOL % 256;
	
	//填充data字段
	memcpy(ether_frame + 14 , data, datalen);
	
	//创建正真发送的socket
	if((sd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0)
	{
		perror("socket() failed ");
		exit(EXIT_FAILURE);
	}
	
	//发送自定义以太网包
	if((bytes = sendto (sd, ether_frame, frame_length, 0, (struct sockaddr *) &device, sizeof (device))) <= 0)
	{
		perror("sendto() failed ");
		exit(EXIT_FAILURE);
	}
	
	printf("send num=%d,success send num=%d\n",frame_length,bytes);
	
	close(sd);
	return (EXIT_SUCCESS);
}

对代码部分解释说明如下:

1.第一次使用socket()函数,是为了获取本地网卡信息,什么意思呢,就是说由于是发送自定义类型、自定义格式的以太网包,在无法获知ARP路由信息的情况下,需预先告知自定义的以太网包数据,从哪个网络接口上发出去。

2.另外,在本地板卡包含多个网络接口时,也需要告诉函数,我由interface参数指定的接口名称的网卡,是否在本地存在,如果存在,这个接口的MAC地址是什么(因为最基本的目的MAC + 源MAC + TYPE这种最基本的包格式还是要遵循的)。

3.紧接着,通过ioctl (sd, SIOCGIFHWADDR, &ifr),将在本地搜索ifr.ifr_name名称对应的网络接口,并将接口信息初始化给ifr结构体,其中,MAC地址就包含在ifr.ifr_hwaddr.sa_data中。

4.从这之后,device结构体才是sendto()时需要真正利用的参数,填充好类型、源MAC地址、地址长度等参数后,就可以开始组自定义数据字段内容了。

5.最后,利用sendto()将组好的包含自定义type和自定义data的以太网数据包发出。

6.这里,在建立socket时,多次使用了ETH_P_ALL这个参数,是什么意思呢,举个例子

socket(PF_PACKET, SOCK_RAW, htons(0x6678));

由于type类型为0x6678的报文没有在if_ether.h中定义,所以套接字在遇见此类型的报文时,就不知道该如何处理,此函数的调用,其实就是告诉套接字,sd在遇到type类型为0x6678的报文时,可以使用sendto()及recvfrom()对此以太网包进行收发,而ETH_P_ALL如字面意思,任何类型的协议包,都进行处理。

将上述代码编译运行在板卡上后,得到的调试打印信息如下。

与此同时,通过wireshark抓包,可以看到,PC端接收到自定义类型的数据包了,且与组包内容相一致。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-11 12:47:32  更:2021-08-11 12:49:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:45:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码