以太网头部 (ethernet-- 以太网)
struct ether_header #include<net/ethernet.h>
struct ether_header
{
u_int8_t ether_dhost[ETH_ALEN]; //目的MAC地址
u_int8_t ether_shost[ETH_ALEN]; //源MAC地址
u_int16_t ether_type; //帧类型
};
IP报文头
struct iphdr #include <netinet/ip.h>
struct iphdr
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ihl:4; // 首部长度
unsigned int version:4; // 版本
#elif __BYTE_ORDER == __BIG_ENDIAN
unsigned int version:4; //版本
unsigned int ihl:4; //首部长度
#else
# error "Please fix <bits/endian.h>"
#endif
u_int8_t tos; // 服务类型
u_int16_t tot_len; // 总长度
u_int16_t id; // 表识
u_int16_t frag_off; // 标志、片偏移
u_int8_t ttl; // 生存时间
u_int8_t protocol; // 协议
u_int16_t check; // 首部校验和
u_int32_t saddr; // 源地址
u_int32_t daddr; // 目的地址
/*The options start here. */
};
用于生成校验位
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;
}
UDP
1、源端口号:发送方端口号。
2、目的端口号:接收方端口号。
3、长度:UDP用户数据报的长度,最小值是8(仅有首部)。
4、校验和:检测UDP用户数据报在传输中是否有错,有错就丢弃。
5、结构体
struct udphdr #include<netinet/udp.h>
struct udphdr {
u_int16_t source; // 源端口号
u_int16_t dest; // 目的端口号
u_int16_t len; // 长度
u_int16_t check; // 校验和
};
UDP伪头部
IP、ICMP、UDP和TCP报文头都有检验和字段,大小都是16bit。
(1)IP校验和:IP首部。
(2)ICMP校验和:ICMP首部+ICMP数据
(3)UDP、TCP校验和:12个字节伪首部(源IP地址、目的IP地址、协议、TCP/UDP包长)+首部+数据
//自定义结构体,表示UDP伪首部的信息
struct udp_fakehdr{
u_int32_t sip; //源ip
u_int32_t dip; //目的ip
u_int8_t flags; //0
u_int8_t prot; //协议类型-17
u_int16_t len; //UDP报文长度
};
ARP
struct arphdr #include <net/if_arp.h>
struct arphdr{
unsigned short int ar_hrd; // 硬件类型
unsigned short int ar_pro; // 协议类型
unsigned char ar_hln; // 硬件地址长度
unsigned char ar_pln; // 协议地址长度
unsigned short int ar_op; // ARP命令# if 0
/* Ethernet looks like this : This bit is variable sized however . . . */
unsigned char __ar_sha[ETH_ALEN]; // 发送端以太网地址
unsigned char __ar_sip[4]; // 发送端IP地址
unsigned char __ar_tha[ETH_ALEN]; // 目的以太网地址
unsigned char __ar_tip[4]; // 目的IP地址
# endif
};
由于上面条件编译的条件始终为0(假) 所以我们需要自定义ARP协议报文结构体--将条件该为1
struct arp_header {
unsigned short int ar_hrd; //硬件地址类型
unsigned short int ar_pro; //协议地址类型能够
unsigned char ar_hln; //硬件地址长度
unsigned char ar_pln; //协议地址长度
unsigned short int ar_op; //ARP操作码
unsigned char __ar_sha[ETH_ALEN]; //源mac地址
unsigned char __ar_sip[4]; //源ip
unsigned char __ar_tha[ETH_ALEN]; //目的mac
unsigned char __ar_tip[4]; //目的ip
};
TCP:
struct tcphdr #include <netinet/tcp.h>
struct tcphdr {
u_int16_t source; // 源端口号
u_int16_t dest; // 目的端口号
u_int32_t seq; // 序列号
u_int32_t ack_seq; // 确认序列
# if __BYTE_ORDER == __LITTLE_ENDIAN
u_int16_t res1:4; //
u_int16_t doff:4; // 保留:4
u_int16_t fin:1; // 终止FIN
u_int16_t syn:1; // 同步SYN
u_int16_t rst:1; // 复位RST
u_int16_t psh:1; // 推送PSH
u_int16_t ack:1; // 确认ACK
u_int16_t urg:1; // 紧急URG
u_int16_t res2:2; // 保留:2
# elif __BYTE_ORDER == __BIG_ENDIAN
u_int16_t doff:4; // 首部长度
u_int16_t res1:4; // 保留:4
u_int16_t res2:2; // 保留:2
u_int16_t urg:1; // 紧急URG
u_int16_t ack:1; // 确认ACK
u_int16_t psh:1; // 推送PSH
u_int16_t rst:1; // 复位RST
u_int16_t syn:1; // 同步SYN
u_int16_t fin:1; // 终止FIN
# else
# error " Adjust your <bits/endian.h> defines"
# endif
u_int16_t window; // 窗口
u_int16_t check; //校验和
u_int16_t urg_ptr; //紧急指针
};
示例代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<arpa/inet.h>
#include <sys/socket.h>
#include <netinet/ether.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
typedef struct {
unsigned int saddr;
unsigned int daddr;
unsigned char flags;
unsigned char type;
unsigned short len;
}WEI;void sys_err(const char *str){
perror(str);
exit(1);
}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 my_sendto(int sockfd,unsigned char *msg, int len, char *name){
struct ifreq ethreq;
strncpy(ethreq.ifr_name,name,IFNAMSIZ);
if(-1 == ioctl(sockfd,SIOCGIFINDEX,ðreq))
sys_err("ioctl error");struct sockaddr_ll sll;
bzero(&sll,sizeof(sll));
sll.sll_ifindex = ethreq.ifr_ifindex;
int l = sendto(sockfd,msg,len,0,(struct sockaddr *)&sll,sizeof(sll));
return l;
}
int main(int argc,char *argv[]){
int sockfd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));//创建原始套接字
unsigned char buf[1500] = "";
unsigned char data[128] = "";
fgets(data,sizeof(data),stdin);
data[strlen(data) - 1] = 0;
int data_len = strlen(data) + strlen(data)/2;
//mac
unsigned char src_mac[6] = {0x00,0x0c,0x29,0x76,0x05,0xa5};
unsigned char dst_mac[6] = {0x7c,0x67,0xA2,0x94,0x1C,0xDB};
struct ether_header *eth_hd = (struct ether_header *)buf;
memcpy(eth_hd->ether_dhost,dst_mac,6);
memcpy(eth_hd->ether_shost,src_mac,6);
eth_hd->ether_type = htons(0x0800);
//ip
struct iphdr *ip_hd = (struct iphdr *)(buf+14);
ip_hd->version = 4;
ip_hd->ihl = 5;
ip_hd->tos = 0;
ip_hd->tot_len = htons(20 + 8 + data_len);
ip_hd->id = htons(0);
ip_hd->frag_off = htons(0);
ip_hd->ttl = 128;
ip_hd->protocol = 17;
ip_hd->check = 0;
ip_hd->saddr = inet_addr("192.168.10.58");
ip_hd->daddr = inet_addr("192.168.10.59");
ip_hd->check = checksum((unsigned short *)ip_hd,20);
//udp
struct udphdr *udp_hd = (struct udphdr *)(buf + 14 + 20);
udp_hd->source = htons(8000);
udp_hd->dest = htons(9999);
udp_hd->len = htons(8 + data_len);
udp_hd->check = 0;
memcpy(buf+14+20+8,data,data_len);
unsigned char wei_buf[512] = "";
WEI *p = (WEI *)wei_buf;
p->saddr = inet_addr("192.168.10.58");
p->daddr = inet_addr("192.168.10.59");
p->flags = 0; p->type = 17;
p->len = htons(8+data_len);
memcpy(wei_buf+12,udp_hd,8+data_len);
udp_hd->check = checksum((unsigned short *)wei_buf,12+8+data_len);my_sendto(sockfd,buf,14+20+8+data_len,"ens37");
close(sockfd);
return 0;
}
网友评论