对于两台建立通讯的主机,数据传输都是从本地主机的应用层的协议出发,经过从上至下的协议处理发送给另一台主机;另一台主机物理层接收到数据后,从下至上层层通过协议处理上传至应用层。
这个过程有点像栈的FILO,故把这些层级协议的组合叫做协议栈(也称为协议族,family)。
这里结合netmap实现一个简单的协议栈:
#include <stdio.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#define NETMAP_WITH_LIBS
#include <net/netmap_user.h>
#pragma pack(1) //使结构体按照1字节对齐;
#define ETH_ADDR_LENGTH 6//地址长度
#define PROTO_IP 0x0800
#define PROTO_ARP 0x0806
#define PROTO_UDP 17
#define PROTO_TCP 6
#define PROTO_ICMP 1
/*arp的作用:
1、发送请求
2、收到了回复的arp响应之后,记录到arp表里面
*/
struct ethhdr { //以太网协议头
unsigned char h_dst[ETH_ADDR_LENGTH];//不对内部数据进行计算,用unsigned_char;目的地址
unsigned char h_src[ETH_ADDR_LENGTH];//源地址;
unsigned short h_proto;
};//14
struct iphdr{
unsigned char hdrlen:4,//注意这俩的顺序,version是高位(协议头是前面的是高位)
version:4;//方便将不同功能的位进行分配
unsigned char tos;
unsigned short totlen;//2byte
unsigned short id;//最多65535
unsigned short flag_offset;
unsigned char ttl;//time to live,没经过一个网关-1,为0丢弃,回复TTL不可达
unsigned char type;//协议的类型
unsigned short check;
unsigned int sip;//源ip
unsigned int dip;//目的ip
};//20
struct udphdr{
unsigned short sport;
unsigned short dport;
unsigned short length;
unsigned short check;//校验码
};//8
struct udppkt{
struct ethhdr eh;//14
struct iphdr ip;//20
struct udphdr udp;//8
unsigned char data[0];//1、提前分配好的,不会越界 2、不需要确定,因为本身是变化的-------这是柔性数组或者称0长数组
};//一字节对齐:42
struct arphdr{
unsigned short h_type;
unsigned short h_proto;
unsigned char h_addrlen;
unsigned char h_protolen;
unsigned short oper;
unsigned char smac[ETH_ADDR_LENGTH];//源mac地址
unsigned int sip;
unsigned char dmac[ETH_ADDR_LENGTH];
unsigned int dip;
};
struct arppkt{
struct ethhdr eh;
struct arphdr arp;
};
struct icmphdr{
unsigned char type;
unsigned char code;
unsigned short check;
unsigned int other;
};
struct icmppkt{
struct ethhdr eh;
struct iphdr ip;
struct icmphdr icmp;
unsigned char data[0];
};
struct tcphdr{
unsigned short sport;
unsigned short dport;
unsigned int seqnum;//开始是个随机值,可以从0开始,也可以从5开始,保证递增就行;越界之后从0开始//这个值是指的字节的数量
unsigned int acknum;
unsigned char hdrlen_resv;//高4位为TCP头部长度
unsigned char flag;
unsigned short window;//初始化为1460(以太网头+ip头对齐之后是40字节???)
unsigned short checksum;
unsigned short urgent_pointer;
unsigned int options[0];
};
struct tcppkt{
struct ethhdr eh;
struct iphdr ip;
struct tcphdr tcp;
unsigned char data[0];
};
typedef enum _tcp_status{
TCP_STATUS_CLOSED,
TCP_STATUS_LISTEN,
TCP_STATUS_SYN_REVD,
TCP_STATUS_SYN_SENT,
TCP_STATUS_ESTABLISHED,
TCP_STATUS_FIN_WAIT_1,
TCP_STATUS_FIN_WAIT_2,
TCP_STATUS_CLOSING,
TCP_STATUS_TIME_WAIT,
TCP_STATUS_CLOSE_WAIT,
TCP_STATUS_LAST_ACK
}cp;//控制状态机
#define TCP_CWR_FLAG 0x80
#define TCP_ECE_FLAG 0x40
#define TCP_URG_FLAG 0x20
#define TCP_ACK_FLAG 0x10
#define TCP_PSH_FLAG 0x08//通知应用程序赶紧接受
#define TCP_RST_FLAG 0x04//
#define TCP_SYN_FLAG 0x02
#define TCP_FIN_FLAG 0x01
struct ntcb{
unsigned int sip;
unsigned int dip;
unsigned short sport;
unsigned short dport;
//这里没有写具体协议
unsigned char smac[ETH_ADDR_LENGTH];
unsigned char dmac[ETH_ADDR_LENGTH];//一般放在arp表里面的
unsigned char status;
//没有定义sendbuffer;
};
int str2mac(unsigned char *mac,char *str) {
char *p = str;
unsigned char value = 0x0;
int i = 0;
while (*p != '\0') {
if (*p == ':') {
mac[i++] = value;
value = 0x0;
} else {
unsigned char temp = *p;
if (temp <= '9' && temp >= '0') {
temp -= '0';
} else if (temp <= 'f' && temp >= 'a') {
temp -= 'a';
temp += 10;
} else if (temp <= 'F' && temp >= 'A') {
temp -= 'A';
temp += 10;
} else {
break;
}
value <<= 4;
value |= temp;
}
p ++;
}
mac[i] = value;
return 0;
}
void echo_arp_pkt(struct arppkt *arp,struct arppkt *arp_rt,char *mac){//组织arp的回复包
memcpy(arp_rt , arp, sizeof(struct arppkt));
memcpy(arp_rt->eh.h_dst, arp->eh.h_src, ETH_ADDR_LENGTH);
str2mac(arp_rt->eh.h_src, mac);
arp_rt->eh.h_proto = arp->eh.h_proto;
arp_rt->arp.h_addrlen = 6;
arp_rt->arp.h_protolen= 4;
arp_rt->arp.oper = htons(2);
str2mac(arp_rt->arp.smac, mac);
arp_rt->arp.sip = arp->arp.dip;
memcpy(arp_rt->arp.dmac, arp->arp.smac, ETH_ADDR_LENGTH);
arp_rt->arp.dip = arp->arp.sip;
}
void echo_icmp_pkt(struct icmppkt *icmp,struct icmppkt *icmp_rt){
memcpy(icmp_rt , icmp, sizeof(struct icmppkt));
memcpy(icmp_rt->eh.h_dst, icmp->eh.h_src, ETH_ADDR_LENGTH);
memcpy(icmp_rt->eh.h_src, icmp->eh.h_dst, ETH_ADDR_LENGTH);
icmp_rt->ip.dip = icmp->ip.sip;
icmp_rt->ip.sip = icmp->ip.dip;
icmp_rt->icmp.type = 0;
icmp_rt->icmp.code = 0;
}
int main(){
int tcp_buff_len = 0;
struct nm_pkthdr h;
struct nm_desc *nmr = nm_open("netmap:eth0",NULL,0,NULL);
if(nmr == NULL)return -1;
struct pollfd pfd = {0};
pfd.fd = nmr->fd;
pfd.events = POLLIN;
struct ntcb tcb;
tcb.status = TCP_STATUS_LISTEN;
while (1)
{
int ret = poll(&pfd,1,-1);
if(ret < 0)continue;
if(pfd.revents & POLLIN){
unsigned char *stream = nm_nextpkt(nmr,&h);//将网卡接收到的不同帧组成的ringbuffer地址
struct ethhdr *eh = (struct ethhdr *)stream;//强转为以太网头
if(ntohs(eh->h_proto) == PROTO_IP){
struct udppkt *udp = (struct udppkt *)stream;
if(udp->ip.type == PROTO_UDP){//这个
int udplength = ntohs(udp->udp.length);
udp->data[udplength-8] = '\0';//去掉头的数据的总长度
printf("udp --> %s\n",udp->data);
}else if(udp->ip.type == PROTO_ICMP){//这个协议可以使得ping命令得以实现
struct icmppkt *icmp = (struct icmppkt *)stream;
struct icmppkt icmp_rt;
echo_icmp_pkt(icmp,&icmp_rt);
nm_inject(nmr, &icmp_rt, sizeof(icmp_rt));
printf("\n-----------ICMP已回发----------- \n");
}else if(udp->ip.type == PROTO_TCP){//1h12min处+1.20
struct tcppkt *tcp = (struct tcppkt *)stream;
/*
unsigned int sip = tcp->ip.sip;
unsigned int dip = tcp->ip.dip;
unsigned short sport = tcp->tcp.sport;
unsigned short dport = tcp->tcp.dport;
search_tcb();....
//方便定位tcb,寻找对应的tcb,这里忽略了寻找tcb的过程,假定已经找到了tcb
*/
struct tcppkt tcp_rt;
if(tcb.status == TCP_STATUS_LISTEN){//必须有监听端
if(tcp->tcp.flag & TCP_SYN_FLAG){
tcb.status = TCP_STATUS_SYN_REVD;
//send syn+ack
memcpy(&tcp_rt , tcp, sizeof(struct tcppkt));
memcpy(tcp_rt.eh.h_dst, tcp->eh.h_src, ETH_ADDR_LENGTH);
memcpy(tcp_rt.eh.h_src, tcp->eh.h_dst, ETH_ADDR_LENGTH);
tcp_rt.ip.sip = tcp->ip.dip;
tcp_rt.ip.dip = tcp->ip.sip;
tcp_rt.tcp.acknum = htonl(tcp->tcp.seqnum+1);
tcp_rt.tcp.seqnum = htons(0);
tcp_rt.tcp.dport = tcp->tcp.sport;
tcp_rt.tcp.sport = tcp->tcp.dport;
tcp_rt.ip.totlen = j;
tcp_rt.tcp.flag = (TCP_SYN_FLAG | TCP_ACK_FLAG);
printf("---------------TCP RECV--------------");
nm_inject(nmr, &tcp_rt, sizeof(tcp_rt));
}
}else if(tcb.status == TCP_STATUS_SYN_REVD){
if(tcp->tcp.flag & TCP_ACK_FLAG){
tcb.status = TCP_STATUS_ESTABLISHED;
}
}
}
}else if(ntohs(eh->h_proto) == PROTO_ARP){
struct arppkt *arp = (struct arppkt *)stream;
struct arppkt arp_rt;
//if(arp->arp.dip == inet_addr("192.168.65.3")){
echo_arp_pkt(arp,&arp_rt,(char *)"00:0c:29:e9:79:2c");
nm_inject(nmr, &arp_rt, sizeof(arp_rt));//发出arp响应
printf("\n-----------ARP已回发----------- \n");
//}
}
}
}
return 1;
}
具体的测试建议Assist以及wireshark进行包的分析,得到正确的协议栈功能。