Linux 网络编程——原始套接字实例:发送 UDP 数据包

以太网报文格式:

详细的说明,请看《MAC 头部报文分析》。

IP 报文格式:

详细的说明,请看《IP 数据报格式详解》。

UDP 报文格式:

详细的说明,请看《UDP
数据报格式详解》。

校验和函数:

/*******************************************************
功能:
	校验和函数
参数:
	buf: 需要校验数据的首地址
	nword: 需要校验数据长度的一半
返回值:
	校验和
*******************************************************/
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;
}

这里是在 ubuntu 下通过原始套接字组一个 udp 数据包,给 PC 机的网络调试助手发送信息:

#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

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------
		0x74, 0x27, 0xea, 0xb5, 0xef, 0xd8, //dst_mac: 74-27-EA-B5-FF-D8
		0xc8, 0x9c, 0xdc, 0xb7, 0x0f, 0x19, //src_mac: c8:9c:dc:b7:0f:19
		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+42, "%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--
		10,  221,   20,  11,				//src_ip: 10.221.20.11
		10,  221,   20,  10,				//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));

	//6.发送数据
	struct sockaddr_ll sll;					//原始套接字地址结构
	struct ifreq ethreq;					//网络接口地址

	strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ);			//指定网卡名称
	if(-1 == ioctl(sock_raw_fd, SIOCGIFINDEX, ðreq))	//获取网络接口
	{
		perror("ioctl");
		close(sock_raw_fd);
		exit(-1);
	}

	/*将网络接口赋值给原始套接字地址结构*/
	bzero(&sll, sizeof(sll));
	sll.sll_ifindex = ethreq.ifr_ifindex;
	len = sendto(sock_raw_fd, send_msg, 14+20+8+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;
}

运行结果如下:

源代码下载请点此处。

时间: 2024-10-10 02:54:20

Linux 网络编程——原始套接字实例:发送 UDP 数据包的相关文章

Linux网络编程——原始套接字实例:简单版网络数据分析器

通过<Linux网络编程--原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 MAC 头部(有线局域网) 注意:CRC.PAD 在组包时可以忽略 链路层数据包的其中一种情况: unsigned char msg[1024] = { //--------------组MAC--------14------ 0xb8, 0x88, 0xe3, 0xe1, 0x10, 0xe6, // dst

Linux网络编程——原始套接字实例:MAC 头部报文分析

通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 MAC 头部(有线局域网) 注意:CRC.PAD 在组包时可以忽略 链路层数据包的其中一种情况: 1 unsigned char msg[1024] = { 2 //--------------组MAC--------14------ 3 0xb8, 0x88, 0xe3, 0xe1, 0x10, 0xe6,

Linux 网络编程——原始套接字实例:MAC 地址扫描器

如果 A (192.168.1.1 )向 B (192.168.1.2 )发送一个数据包,那么需要的条件有 ip.port.使用的协议(TCP/UDP)之外还需要 MAC 地址,因为在以太网数据包中 MAC 地址是必须要有的.那么怎样才能知道对方的 MAC 地址?答案是:它通过 ARP 协议来获取对方的 MAC 地址. ARP(Address Resolution Protocol,地址解析协议),是 TCP/IP 协议族中的一个,主要用于查询指定 ip 所对应的的 MAC(通过 ip 找 MA

Linux网络编程——原始套接字编程

原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个套接字接收数据或者发送数据.区别在于,原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有的数据帧(数据包).另外,必须在管理员权限下才能使用原始套接字. 原始套接字的创建: int socket ( int family, int type, int protocol ); 参数: family:协议族 这里写 PF_PACKET type:  套接字类,这里写 SOCK_RAW pr

Linux网络编程——原始套接字能干什么?

通常情况下程序员接所接触到的套接字(Socket)为两类: (1)流式套接字(SOCK_STREAM):一种面向连接的 Socket,针对于面向连接的TCP 服务应用: (2)数据报式套接字(SOCK_DGRAM):一种无连接的 Socket,对应于无连接的 UDP 服务应用. 从用户的角度来看,SOCK_STREAM.SOCK_DGRAM 这两类套接字似乎的确涵盖了 TCP/IP 应用的全部,因为基于 TCP/IP 的应用,从协议栈的层次上讲,在传输层的确只可能建立于 TCP 或 UDP 协议

LINUX 网络编程 原始套接字

一 原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGRAM的套接字,它实现于系统核心.然而,原始套接字能做什么呢?首先来说,普通的套接字无法处理ICMP.IGMP等网络报文,而SOCK_RAW可以:其次,SOCK_RAW也可以处理特殊的IPv4报文:此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头.总体来说,SOCK_RAW可以处理普通的网络报文之外,还可以处理一些特殊协议报文以及操作IP层及其以上的数据. 既然SOCK_R

Linux网络编程和套接字

1.套接字概述 套接字的本意是插座,在网络中用来描述计算机中不同程序与其他计算机程序的通信方式. 常用的套接字类型有3种: 1)流套接字(SOCK--STREAM):使用了面向连接的可靠的数据通信方式,即TCP套接字: 2)数据报套接字(Raw Sockets):使用了不面向连接的数据传输方式,即UDP套接字: 3)原始套接字(SOCK--RAW):没有经过处理的IP数据包,可以根据自己程序的要求进行封装. 2.常用函数 1.创建套接字函数:成功时返回文件描述符,失败时返回-1 int sock

【转】网络编程原始套接字

转自:http://www.cnblogs.com/hnrainll/archive/2011/09/20/2182423.html SOCKET_STREAM 流式套接字      SOCKET_DGRAM        SOCKET_RAW 原始套接字    IPPROTO_IP IP协议    IPPROTO_ICMP INTERNET控制消息协议,配合原始套接字可以实现ping的功能    IPPROTO_IGMP INTERNET 网关服务协议,在多播中用到 在AF_INET地址族下,

Linux网络编程--自定义套接字描述符判定函数issockettype

套接字描述符和通用文件描述符在形式上没有区别,那么如何判断一个文件描述符是否是套接字描述符呢?下面我们就简单的自定义一个函数issockettype,用于套接字描述符判定. #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> i