用户态的程序:
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#define length 40
int main()
{
struct sockaddr_in serv;
char buff[length];
int sockfd,n,i;
for(i=0;i<length;i++)
buff[i]=i+'0'+5;
if((sockfd=socket(PF_INET,SOCK_DGRAM,0)) < 0){
printf("socket create error\n");
return -1;
}
bzero(&serv, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_addr.s_addr=inet_addr("115.239.210.27");
serv.sin_port = htons(13); /* daytime server */
sendto(sockfd,buff,length,0,(struct sockaddr *)&serv,16);
n=recvfrom(sockfd,buff,150,0,NULL,NULL);
buff[n-2]=0;
printf("buff=%s\n",buff);
return 0;
}
该程序是一个简单的udp程序,向服务器发送时间信息请求,如果服务器开启了相应的服务,
就会返回类似的输出:buff=07 DEC 2013 11:40:40 CST
本文主要分析sendto函数具体做了什么。
本机的信息:ip:192.168.1.109 mac:1c:65:9d:2c:fe:f7
通过路由器连入网络,路由器信息:ip:192.168.1.1 mac:00:23:cd:5b:ea:d6
发送的包有40个字节的信息,内容为0x35到0x5c
首先通过tcpdump查看这种组合下发出去的包的内容,先有个直观的感觉:
11:31:39.553588 IP 192.168.1.109.55527 > 115.239.210.27.13: UDP, length 40
0x0000: 0023 cd5b ead6 1c65 9d2c fef7 0800 4500 .#.[...e.,....E.
0x0010: 0044 0000 4000 4011 3289 c0a8 016d 73ef .D..@.@.2....ms.
0x0020: d21b d8e7 000d 0030 78bf 3536 3738 393a .......0x.56789:
0x0030: 3b3c 3d3e 3f40 4142 4344 4546 4748 494a ;<=>?@ABCDEFGHIJ
0x0040: 4b4c 4d4e 4f50 5152 5354 5556 5758 595a KLMNOPQRSTUVWXYZ
0x0050: 5b5c [\
要读懂这串信息,需要知道L2 L3 L4的头结构
ethernet header长度为14字节
ip header长度为20字节(不考虑option)
udp header 长度为8个字节
tcpdump抓到的总共为82个字节(L2:14 + L3:20 + L4:8 + LOAD:40)
用户态的代码中只指定了服务器的ip地址和端口号以及负载的内容,通过内核相应的处理后,填充了L2,L3以及L4的头结构,
其中包含本地的ip地址,MAC地址,本地端口号,路由器的MAC地址,以及类型相关的内容。
后面这些分量的赋值涉及linux内核的整个网络协议。
首先略过整个内核协议栈,手动构造发送的包,看需要做什么操作才能把包发出去。
内核调用dev_queue_xmit(skb)函数实现发包流程,因此手动构造skb包,然后调用该函数,看能否正常发出
static int create_init(void)
{
struct net_device *dev,*dev_tmp;
int hh_len;
int alloc_len;
char *data_addr;
struct iphdr *iph;
struct ethhdr *eth;
struct udphdr *uh;
struct sk_buff *skb;
for_each_netdev(&init_net, dev)
if(strncmp(dev->name,"wlan0",5)==0)
dev=dev_tmp;
hh_len= LL_RESERVED_SPACE(dev);
init_dest_mac(dest_mac);
/* based on __ip_append_data */
alloc_len=LOAD_SIZE+L4_SIZE+L3_SIZE+hh_len+15;
skb=alloc_skb(alloc_len, GFP_ATOMIC);
if(!skb)
return -1;
skb->dev=dev;
skb_reserve(skb, hh_len);//date+=hh_len;tail+=hh_len
skb_put(skb,LOAD_SIZE+L4_SIZE+L3_SIZE);//tal+= ; skb->len+=
skb_set_network_header(skb, 0);
skb->transport_header = (skb->network_header + L3_SIZE);
data_addr=skb->data+L3_SIZE+L4_SIZE;
skb_fill_load(data_addr,LOAD_SIZE);
/* based on __ip_make_skb */
iph = (struct iphdr *)skb->data;
iph->version = 4;
iph->ihl = 5;
iph->tos = 0;
iph->frag_off = htons(IP_DF);
iph->ttl = 0x40;
iph->protocol