您的位置:首页 > 理论基础 > 计算机网络

linux网路工具、网络、网络编程、网络库、网络内核学习(一)

2017-06-04 12:53 309 查看
1.域名服务

    1.首先家里是无线网,ifconfig(interface接口参数,好记),可以看到三个接口的参数,eth0以太网,lo0(loopback环回),wlan0看名字就是无线网啦。

    inet 地址:192.168.1.110  广播:192.168.1.255  掩码:255.255.255.0。这是无线网的ip地址相关说明,其实有了掩码和inet ipv4地址就能算出广播地址:

    in_addr ipaddr,subaddr,bdaddr;

    assert(inet_aton("192.168.1.110",&ipaddr) && inet_aton("255.255.255.0",&subaddr)); //inet_aton\inet_ntoa全部都是用的网络字节序

    bdaddr.s_addr = (ipaddr.s_addr & subaddr.s_addr) | (~(subaddr.s_addr));

    printf("%s\n",inet_ntoa(bdaddr)); //inet_ntoa不可重入实际运用中可以考虑把其内置静态字符数组拷贝出

    打印出192.168.1.255。

    对于192.168.1.110这个地址,172.16...\192.168...\10...都是内网地址。独一无二的ip地址很宝贵,早在tcp\ip详解成书的二十多年前,b类ip地址已经用完一半了。看外网地址可以上百度查询ip地址,我的返回是“本机IP: 121.71.63.95北京市北京市 鹏博士宽带 ”。内外网ip显然是多对一的关系,大概的实现原理找了篇http://www.cnblogs.com/dongzhuangdian/p/5105844.html,知道了个关键字nat。

    有了wlan0这个接口名,就可以sudo tcpdump -i wlan0来看wlan0的输入输出了。不过在此之前先看下域名服务器配置:

    cat /etc/resolv.conf

    # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)

    #     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN

        nameserver 127.0.1.1

    看注释估计resolvconf和127.0.1.1之间在进行某种交易。man resolvconf,可知resolvconf是作为本机域名解析器调用来存放自身信息的一个命令。先看看本地的域名解析器:

    sudo netstat -nlp|grep '127.0.1.1'

    tcp        0      0 127.0.1.1:53            0.0.0.0:*               LISTEN      1514/dnsmasq

    udp        0      0 127.0.1.1:53            0.0.0.0:*                           1514/dnsmasq

    看来是dnsmasq。vim /etc/services查看知名端口列表:

    domain      53/tcp              # Domain Name Server

    domain      53/udp

    可知53就是域名服务器,关于服务器和解析器(按照tcpip详解的说法),等下再看看。

    lrwxrwxrwx 1 root root 29 11月 23  2014 /etc/resolv.conf -> ../run/resolvconf/resolv.conf ,原来是到某同名文件的软链接。

    resolvconf命令相关:resolvconf -u可以更新/etc/resolv.conf,which resolvconf找到/usr/resolvconf,是个shell脚本,-u执行了/etc/resolvconf/update.d/*下面的所有脚本(run-parts),其中libc脚本就是生成/etc/resolv.conf的。resolvconf会从/run/resolvconf/interface/目录下面读取所有的文件,并把nameserver 127.0.1.1这种格式的行给滤出写到resolv.conf里。

    getaddrinfo函数相关:简言之就是根据ip和port获取域名和服务信息的函数,预感和resolvconf会有关。下载glibc源码跟下,源码实在有些晦涩难懂,大概是用宏和函数命名去追求代码复用,结果导致了用ctags很难找到定义,好吧,最终还是找到了。__nss_database_lookup函数调用nss_parse_file(当编译glibc时__OPTION_EGLIBC_NSSWITCH被设置,我的ubuntu上设置了,检查设置与否除了通过头文件追还可以去/usr/include/i386-linux-gnu内grep),函数打开/etc/nsswitch.conf文件,搜下stackoverflow,相关条目很多,看来是重要的文件。稍微查下资料可知重点行在hosts:         
files myhostname mdns4_minimal [NOTFOUND=return] dns。看代码+gdb可知首先函数会从文件files两种去查询,具体代码在_nss_files_gethostbyname3_r中,打开的文件就是/etc/hosts(#define DATAFILE    "/etc/" DATABASE,打开的文件是个static的文件内静态变量,每次重查只会rewind一次,所以应该支持动态的修改)。以“www.baidu.com”为例子,/etc/hosts没有找到继续,myhostname
mdns4_minimal这两个对应的函数_nss_myhostname_gethostbyname4_r、_nss_mdns4_minimal_gethostbyname2_r。先查下myhostname,是个传说中的插件。ubuntu下dpkg-query -L libnss-myhostname,/lib/i386-linux-gnu/libnss_myhostname.so.2下已安装,readelf -a -W /lib/i386-linux-gnu/libnss_myhostname.so.2,得到26:
00000e80   867 FUNC    GLOBAL DEFAULT   11 _nss_myhostname_gethostbyname4_r,so内有这个函数符号。此外,gdb断点b _nss_myhostname_gethostbyname4_r,此时lsof -p gdb进程号的确能看到so被当作文件被进程打开了,gdb     12696 liu3    9r   REG        8,7    13688 2229297 /lib/i386-linux-gnu/libnss_myhostname.so.2,文件描述符为9。源码下下来看,代码很少,关键行if(strcasecmp(name,
hn) != 0),搜索的机器名name和hn hostname本机名不一致直接就返回了,剩下的代码大约是用c网络编程库函数去得到本机的ip地址。同理,也可以用gdb找到/lib/i386-linux-gnu/libnss_mdns4_minimal.so.2,    29: 00001070   717 FUNC    GLOBAL DEFAULT   12 _nss_mdns4_minimal_gethostbyname2_r@@NSSMDNS_0,于是下载nss-mdns源码找找,源码下载后照例先看看sof。这次有点失策看不懂,就看百度,找到关键字组播dns(摘要:mDNS主要实现了在没有传统DNS服务器的情况下使局域网内的主机实现相互发现和通信)、5353、avahi,本机也有udp       
0      0 0.0.0.0:5353            0.0.0.0:*                           737/avahi-daemon: r,avahi-daemon。上wiki查了下mdns定义,了解了下,发现默认是对以.local结尾的域名起作用,与本例子不符合,就先放一边了知道有这么回事。最后dns终于出场了,_nss_dns_gethostbyname4_r。这个是glibc自带的。_nss_dns_gethostbyname4_r->__libc_res_nsearch->__libc_res_nquerydomain->__libc_res_nquery,...包了这么多层终于到传统意义的网络编程了。换个行详解下:

    首先是域名服务的头部,和网络编程14章里面描述的完全一样。标识字段取得是当前时间的秒和微秒运算和和前一个标识之和的低16位,感觉倒是比较随机但是可能会重复。rd选项在我的简单调用getaddrinfo("www.baidu.com",NULL,NULL,&result)中是设置的,实际上设置了802c1=RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH | RES_NOIP6DOTINT,RES_RECURSE就是rd选项设置;rcode因为是请求所以设为0。opcode比书中多了两种,4是时区变换通知,5也是个啥时区相关的,并且因为我的请求中没有制定ip版本,所以T_UNSPEC会致使生成两个查询问题,ipv4和v6的地址。__libc_res_nsend把组装好的请求发送,在一个全局的变量里面已经初始化了一个域名服务器,{sin_family
= 2, sin_port = 13568, sin_addr = {s_addr = 16842879}, sin_zero = "\000\000\000\000\000\000\000"},s_addr是127.0.1.1,sin_port用htons转换后就是53,就是上述的dnsmasq。得追下这个全局变量是哪儿初始化的,在getaddrinfo没有初始化参数前提下,函数__res_vinit,重试次数2,重试时间5秒,初始id设为pid低位等等。域名先看环境变量LOCALDOMAIN是否设置,我机器上没有,接下来打开#define
_PATH_RESCONF        "/etc/resolv.conf",终于找到和resolvconf相关信息了,原来是写死的,不在此久留了,反正知道这个函数是读resolv.conf的入口。最多三个域名服务器,设置了某些选项后(RES_ROTATE?)还能实现一种负载均衡。还有个自定义的请求函数钩子qhook,本例没有。关键的两行sr = send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL);*thisresplenp = recvfrom(pfd[0].fd,
(char*)*thisansp,*thisanssizp, 0,(struct sockaddr *)&from, &fromlen)。

    确定了getaddrinfo先和本机dnsmasp申请后,tcpdump同时监控lo和wlan0:

    环回输出:

    11:47:58.846105 IP localhost.41714 > liu3-CW65S.domain: 40063+ A? www.baidu.com. (31)

    11:47:58.846128 IP localhost.41714 > liu3-CW65S.domain: 23604+ AAAA? www.baidu.com. (31)

    11:47:58.850913 IP liu3-CW65S.domain > localhost.41714: 40063 3/5/5 CNAME www.a.shifen.com., A 119.75.217.109, A 119.75.218.70 (260)

    11:47:58.851002 IP liu3-CW65S.domain > localhost.41714: 23604 1/1/0 CNAME www.a.shifen.com. (115)

    如果加一个-ttt选项会打印时间间隔,好看一点。第一二行间隔20微秒程序请求dnsmasq两次,40063和23604都是标识,在三四行的返回里面返回同样的标志。代码在recvfrom后会检查受到的数据报id字段是否和发出的相同,不同则认为是旧请求回应丢弃,并goto wait,在wait代码区会调用poll等待接口返回,如果超时会重传或者返回失败。数字后面的+表示递归期望rd标志打开,前面已经说明,就是应用程序对dnsmasq说,“必须给我找出结果!别给我其他域名服务器”。31字节的长度:12个字节的dns请求报文的首部(可以用gdb验证,p
((HEADER *)buf)->rd,得1,说明前面的+递归调用成立,aa为0,貌似只有某个域内的权威域名服务器返回才aa为1);接下来是问题部分,问题部分以查询名开始,gdb中p *(buf+12),得到3,3个w,之后是5,baidu,最后是3,com,以一个0结尾;最后是查询类型,A ipv4查询对应1, p ntohs(*((unsigned short *)(buf+27))),gdb打印结果为1也证实了;同理也证实了接下来的查询类1;总共31个字节。第二行的31字节请求唯一不同的就是查询类型“AAAA”,查wiki确定为28,在tcp\ip详解一书中AAAA还没有给出。

    tcpdump也能把查询报文全部打出,这样比gdb调试方便一点,不过当前目的是结合代码看。3\4行的3 5 5以及1 1 0是回答数、授权数以及附加信息数。tcpdump没有全部打印出来,用host -v www.baidu.com可以查看详细内容。QUERY: 1, ANSWER: 3, AUTHORITY: 5, ADDITIONAL: 5,CNAME表示www.baidu.com其实是www.a.shifen.com的别名,就像小沈阳叫多了他的真名只有身份证才知道了。百度有两个外网ip。5个授权记录之一是a.shifen.com.  
     39    INNS    ns1.a.shifen.com.,意思是说尽管这次域名服务器迭代请求获取了相关ip,但是真正管控www.baidu.com域名解析的是ns1.a.shifen.com.等五个域名,下次可以直接访问。并且在五个附加信息中把五个授权域名服务器的ip给出ns1.a.shifen.com.    11    IN    A    61.135.165.224。至于一个问题13个资源记录是怎么放到260个字节内的,这个在tcpip详解14章中有详细介绍,里面的指针思想我在之前的工作中见识过,能很大程度减少网络数据量。

    至于dnsmasq和真正的域名服务器交互。如下:

    00:00:00.000000 IP liu3-CW65S.4455 > 124.207.160.106.domain: 46487+ A? www.baidu.com. (31)

    00:00:00.000042 IP liu3-CW65S.4455 > bjdns.drpeng.com.cn.domain: 46487+ A? www.baidu.com. (31)

    00:00:00.050699 IP bjdns.drpeng.com.cn.domain > liu3-CW65S.4455: 46487 3/5/5 CNAME www.a.shifen.com., A 119.75.217.109, A 119.75.218.70 (260)

    00:00:00.000046 IP 124.207.160.106.domain > liu3-CW65S.4455: 46487 3/5/5 CNAME www.a.shifen.com., A 119.75.218.70, A 119.75.217.109 (260)

    00:00:00.000978 IP liu3-CW65S.34384 > bjdns.drpeng.com.cn.domain: 4619+ AAAA? www.a.shifen.com. (34)

    00:00:00.099794 IP bjdns.drpeng.com.cn.domain > liu3-CW65S.34384: 4619 0/1/0 (97)

    00:00:00.000388 IP liu3-CW65S.53999 > bjdns.drpeng.com.cn.domain: 33076+ MX? www.a.shifen.com. (34)

    00:00:00.027004 IP bjdns.drpeng.com.cn.domain > liu3-CW65S.53999: 33076 0/1/0 (97)

    00:00:00.337096 IP liu3-CW65S.33266 > bjdns.drpeng.com.cn.domain: 3114+ PTR? 106.160.207.124.in-addr.arpa. (46)

    00:00:04.487467 ARP, Request who-has 192.168.1.1 tell liu3-CW65S, length 28

    00:00:00.002267 ARP, Reply 192.168.1.1 is-at bc:d1:77:28:04:d0 (oui Unknown), length 28

    00:00:00.515388 IP liu3-CW65S.33266 > 124.207.160.106.domain: 3114+ PTR? 106.160.207.124.in-addr.arpa. (46)

    00:00:00.000035 IP liu3-CW65S.33266 > bjdns.drpeng.com.cn.domain: 3114+ PTR? 106.160.207.124.in-addr.arpa. (46)

    00:00:05.005208 IP liu3-CW65S.26625 > 124.207.160.106.domain: 16622+ PTR? 42.26.239.219.in-addr.arpa. (44)

    00:00:00.000015 IP liu3-CW65S.26625 > bjdns.drpeng.com.cn.domain: 16622+ PTR? 42.26.239.219.in-addr.arpa. (44)

    00:00:00.010244 IP 124.207.160.106.domain > liu3-CW65S.26625: 16622- 1/0/0 PTR bjdns.drpeng.com.cn. (77)

    00:00:00
c233
.000346 IP liu3-CW65S.25917 > 124.207.160.106.domain: 61037+ PTR? 1.1.168.192.in-addr.arpa. (42)

    00:00:00.010750 IP bjdns.drpeng.com.cn.domain > liu3-CW65S.26625: 16622- 1/0/0 PTR bjdns.drpeng.com.cn. (77)

    00:00:00.000019 IP liu3-CW65S > bjdns.drpeng.com.cn: ICMP liu3-CW65S udp port 26625 unreachable, length 113

    00:00:00.003761 IP 124.207.160.106.domain > liu3-CW65S.25917: 61037 NXDomain* 0/1/0 (77)

    00:00:00.472929 IP liu3-CW65S.50340 > 124.207.160.106.domain: 47855+ PTR? 250.255.255.239.in-addr.arpa. (46)

    00:00:00.007274 IP 124.207.160.106.domain > liu3-CW65S.50340: 47855 NXDomain* 0/1/0 (103)

    首先确定下前四行,124.207.160.106和bjdns.drpeng.com.cn是个啥联系,因为id都相同,请求和回复看起来也一模一样。看下dnsmasq怎么配置的,毕竟是它发的请求,cat /proc/进程号/cmdline得到了启动参数/usr/sbin/dnsmasq--no-resolv--keep-in-foreground--no-hosts--bind-interfaces--pid-file=/run/sendsigs.omit.d/network-manager.dnsmasq.pid--listen-address=127.0.1.1--conf-file=/var/run/NetworkManager/dnsmasq.conf--cache-size=0--proxy-dnssec--enable-dbus=org.freedesktop.NetworkManager.dnsmasq--conf-dir=/etc/NetworkManager/dnsmasq.d。然而conf都是空的。sudo
kill -SIGUSR1 1701,这样dnsmasq能够打印状态到syslog。server 124.207.160.106#53: queries sent 4310, retried or failed 7,server 219.239.26.42#53: queries sent 3614, retried or failed 6,倒是有两个上游的服务器。host  219.239.26.42也的确是bjdns.drpeng这个。继续grep syslog可以得到启动信息,warning: no
upstream servers configured和setting upstream servers from DBus,得到关键字DBus。其实前面的启动选项也有--enable-dbus=org.freedesktop.NetworkManager.dnsmasq,

    NetworkManager\dbus\dnsmasq,有必要了解下,顺便能把我ubuntu老断网的问题解决了更好。man+wiki+baidu+sof...wiki上查到了nmcli,先试试查看NetWorkManager的状态。nmcli con down uuid '1a3a1a3c-6e6a-4883-a3c5-3674000ae53e'把我的wifi禁掉后,nmcli con up uuid '1a3a1a3c-6e6a-4883-a3c5-3674000ae53e'再重启,tail -f
/var/log/syslog看看,日志打得相当清晰(值得工作中借鉴)。接下来竟然有意外的收获:

    Jun  2 17:46:26 liu3-CW65S NetworkManager[787]: <info> (wlan0): DHCPv4 state changed preinit -> reboot

    Jun  2 17:46:26 liu3-CW65S NetworkManager[787]: <info>   address 192.168.1.103

    Jun  2 17:46:26 liu3-CW65S NetworkManager[787]: <info>   prefix 24 (255.255.255.0)

    Jun  2 17:46:26 liu3-CW65S NetworkManager[787]: <info>   gateway 192.168.1.1

    Jun  2 17:46:26 liu3-CW65S NetworkManager[787]: <info>   nameserver '124.207.160.106'

    Jun  2 17:46:26 liu3-CW65S NetworkManager[787]: <info>   nameserver '219.239.26.42'

    Jun  2 17:46:26 liu3-CW65S NetworkManager[787]: <info> Activation (wlan0) Stage 5 of 5 (IPv4 Configure Commit) scheduled...

    Jun  2 17:46:27 liu3-CW65S NetworkManager[787]: <info> Writing DNS information to /sbin/resolvconf

    反正域名服务器,本机ip都是路由器告知的。是在链接路由器的第五个阶段(看日志分别是Device Prepare、Device Configure、IP Configure Start、第四阶段没打印、IPv4 Configure Commit),协议名叫DHCPv4。

    现在大胆假设,nmcli通过dbus发送命令给NetworkManager要求关闭或启动wifi,重启后,后者用bootp和路由器联系通过DHCP的67 68端口联系,取得动态的配置,完事后NetworkManager调用resolveconf命令更新resolve.conf填入dnsmasq的信息,之后给dbus发wifi重启的信号或者是重启dnsmasq反正通知到dnsmasq,dnsmasq再从NetworkManager处更新上游域名服务器信息。当然,猜测是猜测,最好一步一步验证。

    nmcli肯定是和NetworkManager联系的,因为执行相关up down命令后者会在syslog里面打日志,二者怎么通信?...to be continue

    基于bootp的dhcpv4,tcpip详解16章专门写了bootp。先nmcli con list得到电脑链接的路由器信息,TP-LINK_2804D0            1a3a1a3c-6e6a-4883-a3c5-3674000ae53e   802-11-wireless   2017年06月04日 星期日 10时14分00秒。再sudo tcpdump -vvn -i wlan0 port 68,打印详细信息(vv),用ip打印(n),坚挺wlan0无限网络的68(bootp服务)。再tail
syslog。再nmcli con up uuid '1a3a1a3c-6e6a-4883-a3c5-3674000ae53e'。up在man中被解释为重新开工,syslog里面也显示有一个先disconnect路由器再到connected的过程。tcpdump显示本机bootp服务器(和想的正好相反)从本机初始网络地址0.0.0.0向受限广播(255.255.255.255,见tcpip12章)发起BOOTP/DHCP查询请求。这将导致本地网络的所有机器截获此包一直到ip层或者udp曾才发现自己不能处理才丢弃,如果是有线网这一步很好理解,但因为是无线网有些细节没搞清楚,为什么只有我的路由器才回了?是因为本地局域网的受限广播有个唯一的广播地址?后话不表。请求和应答的格式和书里面大体相当,只不过利用厂商字段(vendor)来承载实际的DHCP内容。在请求中有一行Requested-IP
Option 50, length 4: 192.168.1.103,我猜(只能猜了,扒源码来不及)是说本请求是个重新申请的请求,我之前已经有一个ip了是103。于是客户端(有点别扭)的返回直接填入了ip地址192.168.1.1.67 > 192.168.1.103.68,因为是本地网路,只要mac地址对了就能送达目的主机。告知了ip信息,还是之前的103,一起的还有路由器ip,子网掩码,广播地址,DHCP客户ip(和路由器ip一致),域名服务器ip,mtu(居然才567)。

    一句话,tcpdump告诉我,nmcli con up这个命令在链接本来就在工作时,是重新确认的过程;syslog告诉我是断开链接和重新链接的过程。大师richard stevens用调tcpdump方法写书时一定很爽。

    继续事实,让我的华为手机链接网络。mac地址f8:23:b2:46:f8:b6广播了两个请求,以0.0.0.0向255.255.255.255,也都是DHCP,第一个是Discover(探测?),第二个是Request,看样子是要求续租192.168.1.104以及更新其他如掩码域名服务等信息。但是为啥回复没有打印?难道是mac地址不是广播不是本地的就不打印?那样和书里的不一致啊。查了下的确有个ifconfig wlan0 promisc调混杂模式的,这个模式估计对ip压力就大了,并且我的机器设置失败说是不支持。我猜ifconfig之后tcpdump还得调下才能使得不被ip内核过滤掉。

    没关系继续监听本机的...接下来的见(二)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: