[[SEO技术]] 发包服务器流程解析是什么?

[复制链接]
查看: 121|回复: 0
发表于 2023-6-24 11:56:16 | 显示全部楼层 | 阅读模式
易博V9下载

代码文件


  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<sys/socket.h>
  4. #include<sys/types.h>
  5. #include<netinet/in.h>
  6. #define length 40
  7. int mAIn()
  8. {
  9. struct sockaddr_in serv;
  10. char buff[length];
  11. int sockfd,n,i;
  12. for(i=0;i<length;i++)
  13. buff[i]=i+’0’+5;
  14. if((sockfd=socket(PF_INET,SOCK_DGRAM,0)) < 0){
  15. printf(“socket create error ”);
  16. return -1;
  17. }
  18. bzero(&serv, sizeof(serv));
  19. serv.sin_family = AF_INET;
  20. serv.sin_addr.s_addr=inet_addr(“115.239.210.27”);
  21. serv.sin_port = htons(13); /* daytime server */
  22. sendto(sockfd,buff,length,0,(structsockaddr *)&serv,16);
  23. n=recvfrom(sockfd,buff,150,0,NULL,NULL);
  24. buff[n-2]=0;
  25. printf(“buff=?n”,buff);
  26. return0;
  27. }

该程序是一个简单的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查看这种组合下发出去的包的内容,先有个直观的感觉:

  1. 11:31:39.553588 IP 192.168.1.109.55527 > 115.239.210.27.13: UDP, length 40
  2. 0x0000: 0023 cd5b ead6 1c65 9d2c fef7 0800 4500 .#.[…e.,….E.
  3. 0x0010: 0044 0000 4000 4011 3289 c0a8 016d 73ef .D..@.@.2….ms.
  4. 0x0020: d21b d8e7 000d 0030 78bf 3536 3738 393a …….0x.56789:
  5. 0x0030: 3b3c 3d3e 3f40 4142 4344 4546 4748 494a ;<=>?@ABCDEFGHIJ
  6. 0x0040: 4b4c 4d4e 4f50 5152 5354 5556 5758 595a KLMNOPQRSTUVWXYZ
  7. 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包,然后调用该函数,看能否正常发出

  1. static int create_init(void)
  2. {
  3. struct net_device *dev,*dev_tmp;
  4. int hh_len;
  5. int alloc_len;
  6. char *data_addr;
  7. struct iphdr *iph;
  8. struct ethhdr *eth;
  9. struct udphdr *uh;
  10. struct sk_buff *skb;
  11. for_each_netdev(&init_net, dev)
  12. if(strncmp(dev->name,”wlan0″,5)==0)
  13. dev=dev_tmp;
  14. hh_len= LL_RESERVED_SPACE(dev);
  15. init_dest_mac(dest_mac);
  16. /* based on __ip_append_data */
  17. alloc_len=LOAD_SIZE+L4_SIZE+L3_SIZE+hh_len+15;
  18. skb=alloc_skb(alloc_len, GFP_ATOMIC);
  19. if(!skb)
  20. return -1;
  21. skb->dev=dev;
  22. skb_reserve(skb, hh_len);//date+=hh_len;tail+=hh_len
  23. skb_put(skb,LOAD_SIZE+L4_SIZE+L3_SIZE);//tal+= ; skb->len+=
  24. skb_set_network_header(skb, 0);
  25. skb->transport_header = (skb->network_header + L3_SIZE);
  26. data_addr=skb->data+L3_SIZE+L4_SIZE;
  27. skb_fill_load(data_addr,LOAD_SIZE);
  28. /* based on __ip_make_skb */
  29. iph = (struct iphdr *)skb->data;
  30. iph->version = 4;
  31. iph->ihl = 5;
  32. iph->tos = 0;
  33. iph->frag_off = htons(IP_DF);
  34. iph->ttl = 0x40;
  35. iph->protocol = IPPROTO_UDP; //0x11
  36. iph->tot_len=htons(0x44);
  37. iph->saddr = SOURCE_IP;
  38. iph->daddr = DEST_IP;
  39. iph->id=0;
  40. /* based on udp_send_skb */
  41. uh = udp_hdr(skb);
  42. uh->source = htons(SOURCE_PORT);
  43. uh->dest = htons(DEST_PORT);
  44. uh->len = htons(skb->len – L3_SIZE);
  45. uh->check = 0;
  46. /* based on eth_header */
  47. eth = (struct ethhdr *)skb_push(skb, L2_SIZE);//skb->data -= ;skb->len +=
  48. eth->h_proto =htons(ETH_P_IP); //0x0800
  49. memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
  50. memcpy(eth->h_dest, dest_mac, ETH_ALEN);
  51. dev_queue_xmit(skb);
  52. return 0;
  53. }

这样构造的包是能够正常发送的,因此我们主要关注协议栈是如何自动完成上面模块中手动参与制定的部分。

1:net_device如何选择,用户态代码中只有目的ip,如何决定使用哪个net_device,系统中可能有多个网卡。

通过jprobe可以确定源IP以及net_device是在udp_sendmsg函数中的这段代码中实现的:

  1. if (rt == NULL) {
  2. struct net *net = sock_net(sk);
  3. fl4 = &fl4_stack;
  4. flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos,
  5. RT_SCOPE_UNIVERSE, sk->sk_protocol,
  6. inet_sk_flowi_flags(sk)|FLOWI_FLAG_CAN_SLEEP,
  7. faddr, saddr, dport, inet->inet_sport);
  8. security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
  9. rt = ip_route_output_flow(net, fl4, sk);
  10. if (IS_ERR(rt)) {
  11. err = PTR_ERR(rt);
  12. rt = NULL;
  13. if (err == -ENETUNREACH)
  14. IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
  15. goto out;
  16. }
  17. err = -EACCES;
  18. if ((rt->rt_flags & RTCF_BROADCAST) &&
  19. !sock_flag(sk, SOCK_BROADCAST))
  20. goto out;
  21. if (connected)
  22. sk_dst_set(sk, dst_clone(&rt->dst));
  23. }

首先通过flowi4_init_output函数初始化一个flowi4数据结构,此时源port已经确定,但是源ip还没有:

  1. struct flowi4 {
  2. struct flowi_common __fl_common;
  3. #define flowi4_oif __fl_common.flowic_oif // 0
  4. #define flowi4_iif __fl_common.flowic_iif // 发包流程,该字段为0
  5. #define flowi4_mark __fl_common.flowic_mark // 0
  6. #define flowi4_tos __fl_common.flowic_tos // 0
  7. #define flowi4_scope __fl_common.flowic_scope // RT_SCOPE_UNIVERSE
  8. #define flowi4_proto __fl_common.flowic_proto // IPPROTO_UDP
  9. #define flowi4_flags __fl_common.flowic_flags
  10. #define flowi4_secid __fl_common.flowic_secid //0
  11. __be32 daddr;
  12. __be32 saddr;
  13. union flowi_uli uli;
  14. #define fl4_sport uli.ports.sport
  15. #define fl4_dport uli.ports.dport
  16. #define fl4_icmp_type uli.icmpt.type
  17. #define fl4_icmp_code uli.icmpt.code
  18. #define fl4_ipsec_spi uli.spi
  19. #define fl4_mh_type uli.mht.type
  20. #define fl4_gre_key uli.gre_key
  21. } __attribute__((__aligned__(BITS_PER_LONG/8)));

接着以flowi4为参数进行路由查找:

  1. struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *flp4)
  2. {
  3. struct rtable *rth;
  4. unsigned int hash;
  5. if (!rt_caching(net))
  6. goto slow_output;
  7. hash = rt_hash(flp4->daddr, flp4->saddr, flp4->flowi4_oif, rt_genid(net));
  8. rcu_read_lock_bh();
  9. for (rth = rcu_dereference_bh(rt_hash_table[hash].chain); rth;
  10. rth = rcu_dereference_bh(rth->dst.rt_next)) {
  11. if (rth->rt_key_dst == flp4->daddr &&
  12. rth->rt_key_src == flp4->saddr &&
  13. rt_is_output_route(rth) &&
  14. rth->rt_oif == flp4->flowi4_oif &&
  15. rth->rt_mark == flp4->flowi4_mark &&
  16. !((rth->rt_key_tos ^ flp4->flowi4_tos) &
  17. (IPTOS_RT_MASK | RTO_ONLINK)) &&
  18. net_eq(dev_net(rth->dst.dev), net) &&
  19. !rt_is_expired(rth)) {
  20. dst_use(&rth->dst, jiffies);
  21. RT_CACHE_STAT_INC(out_hit);
  22. rcu_read_unlock_bh();
  23. if (!flp4->saddr)
  24. flp4->saddr = rth->rt_src;
  25. if (!flp4->daddr)
  26. flp4->daddr = rth->rt_dst;
  27. return rth;
  28. }
  29. RT_CACHE_STAT_INC(out_hlist_search);
  30. }
  31. rcu_read_unlock_bh();
  32. slow_output:
  33. return ip_route_output_slow(net, flp4);
  34. }

首先从cache中查找,查不到再走ip_route_output_slow流程

cache查找比较简单,利用flp4->daddr, flp4->saddr, flp4->flowi4_oif, rt_genid(net)这四个字段生成hash值,

根据这个hash值去rt_hash_table全局变量对应的hash表中查找,对比需要比较的字段,满足则返回缓存项。

易博软件介绍
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1、请认真发帖,禁止回复纯表情,纯数字等无意义的内容!帖子内容不要太简单!
2、提倡文明上网,净化网络环境!抵制低俗不良违法有害信息。
3、如果你对主帖作者的帖子不屑一顾的话,请勿回帖。谢谢合作!
3、问答求助区发帖求助后,如有其他用户热心帮您解决问题后,请自觉点击设为最佳答案按钮。

 
 
QQ在线客服
QQ技术支持
工作时间:
8:00-18:00
软著登字:
1361266号
官方微信扫一扫
weixin

QQ|小黑屋|Archiver|慈众营销 ( 粤ICP备15049986号 )|网站地图

自动发帖软件 | 自动发帖器 | 营销推广软件 | 网络营销工具 | 网络营销软件 | 网站推广工具 | 网络推广软件 | 网络推广工具 | 网页推广软件 | 信息发布软件 | 网站推广工具 | 网页推广软件

Powered by Discuz! X3.4   © 2012-2020 Comsenz Inc.  慈众科技 - Collect from 深圳吉宝泰佛文化有限公司 公司地址:罗湖区黄贝街道深南东路集浩大厦A1403

返回顶部 返回列表