网络配置过程分析(linux网络协议栈笔记)

本文深入探讨Linux网络配置过程,从ifconfig命令的内部操作开始,详细解析socket系统调用和ioctl的实现。通过strace跟踪ifconfig,揭示了配置IP地址时涉及的系统接口,包括socket的创建、数据结构和内核交互。同时,介绍了netlink和rtnetlink在路由配置中的角色,为理解Linux网络协议栈提供了深入见解。
摘要由CSDN通过智能技术生成

网络配置过程分析

在本篇,我们先介绍配置普通设备的IP地址的内部过程,接着再转到loopback接口的配置过程,这两个过程有相似之处,所以一起解说。然后再转入FIB系统,讲解路由系统,并用图例演示路由表的变化。最后介绍接口状态的变化,这对于驱动程序开发人员来说也是比较重要的。
.1)配置是如何下达到内核的?
我们假设在安装我们的Linux系统时,没有配置IP地址,也没有挂上网线,完完全全是一台“裸机”,这样方便我们跟踪系统到底做了什么。安装完系统之后,我们可以使用ifconfig命令,查看设备信息。我想读者们应该都知道这么一个命令吧。在Windows上相应的操作是ipconfig。带上”-a”参数表示要查看详细的配置,包括loopbcack设备。

    #ifconfiga  
    eth0        Link encap:Ethernet HWaddr 00:80:C8:EB:2A:39 
                BROADCAST MULTICAST MTU:1500 Metric:1  
                RX packets:0 errors:0 dropped:0 overruns:0 frame:0  
                TX packets:0 errors:0 dropped:0 overruns:0 carrier:0  
                collisions:0 txqueuelen:1000  
                RX bytes:0 (0.0 B) TX bytes:0 ( 0.0 b) Interrupt:5  
     lo         Link encap:Local Loopback  
                inet addr:127.0.0.1 Mask:255.0.0.0  
                UP LOOPBACK RUNNING MTU:16436 Metric:1  
                RX packets:6 errors:0 dropped:0 overruns:0 frame:0  
                TX packets:6 errors:0 dropped:0 overruns:0 carrier:0  
                collisions:0 txqueuelen:0  
                RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)  

不同的机器和网卡会显示一些不同。在我的另外一台机器,其eth0的Interrupt等于3。
现在运行 #strace ifconfig eth0 192.168.18.2 netmask 255.255.255.0,配置好ip地址和网络掩码。为什么要运行strace?在大部分系统上是没有ifconfig的源代码的,那么为了查看ifconfig内部完成了什么操作时,可以用strace命令查看。它收集应用程序执行的系统调用,甚至参数都能记录下来。通过对这个命令的输出进行整理,我们可以把ifconfig内部调用的系统接口整理如下:

   main(int argc, char **argv )
1. {
2. struct sockaddr sa;
3. struct sockaddr_in sin;
4. char host[128];
5. struct aftype *ap;
6. struct hwtype *hw;
7. struct ifreq ifr;
8. char **spp;
9. int fd;
10.
11. fd = socket
12. ifr.ifr_name = “eth0”; (PF_INET, SOCK_DGRAM, IPPROTO_IP);
13. ap = inet_aftype =
14. {
15. "inet", NULL, /*"DARPA Internet", */ AF_INET, sizeof(unsigned long),
16. INET_print, INET_sprint, INET_input, INET_reserror,
17. NULL /*INET_rprint */ , NULL /*INET_rinput */ ,
18. INET_getnetmask,
19. -1, /* 这个值会被赋成fd,即刚才打开的socket */
20. NULL
21. }; 
22. host = “192.168.18.2”;
23. ap->input(0, host, &sa);  /* 在此sa->sa_family=AF_INET,af->sa_data已经被设置成192.168.1.1 */
24. memcpy((char *) &ifr.ifr_addr, (char *) &sa, sizeof(struct sockaddr));
25. ioctl (fd, SIOCSIFADDR, &ifr);
26. ioctl (skfd, SIOCGIFFLAGS, &ifr);
27. ioctl
28. /* 开始第二次遍历命令行*/ (skfd, SIOCSIFFLAGS, &ifr);
29. host = “255.255.255.0”;
30. ioctl
31. (skfd, SIOCSIFNETMASK, ifr);
32. }

以上就是ifconfig这个命令实际内部“伪”代码,为了减轻复杂度,我们不必把整个代码列出来。从上面的代码中可以看出ifconfig实际调用了2个系统函数:socket和ioctl。4表示为socket打开的文件描述符,姑且认为这是应用程序到达系统内核的一个钥匙吧,后面会介绍一些相关知识。Socket的参数以后也会详细介绍。然后是ioctl这个系统调用。在Linux相关的项目中,ioctl是用户层与内核或设备驱动程序进行配置的一个有效手段。在这里我们要配置系统的地址,则必定要通过ioctl。在ifconfig的程序中依次调用6个ioctl,完成了对系统地址的配置。
下面我们就对出现的系统调用进行分析,首先是socket。

socket系统调用

从上图可以看到一个已安装的Linux操作系统究竟支持几种文件系统类型由文件系统的注册链表决定。
在BSD socket层内使用msghdr{ }结构保存数据;在INET socket层以下都使用sk_buff{ }数据结构保存数据。
这一部分处理BSD socket相关操作,每个socket在内核中以struct socket结构体现

struct socket {  
    struct proto_ops *ops;  
    struct file *file;  
    struct sock *sk;  
    wait_queue_head_t wait;  
    short type;  
    …… 
}; 

应用层中的操作对象是socket文件描述符,通过文件系统定义的通用接口,使用系统调用从用户空间切换到内核空间,控制socket文件描述符对应的就是对BSD socket的操作,从而进入到BSD socket层的操作。在BSD socket层中,操作对象是socket{ }结构。每一个这样的结构对应的是一个网络连接。通过网络地址族的不同来区分不同的操作方法,判断是否应该进入到INET socket层,这一层的数据存放在msghdr{ }结构中。
在INET socket层中,根据建立连接的类型,分成面向连接的和面向无连接两种类型,这是区分TCP和UDP协议的主要原则。这一层的操作对象主要是sock{ }类型的数据,而数据存放在sk_buff{ }结构中。
从INET socket层到IP层,主要是路由过程,发送时根据发送的目标地址确定需要使用的网络设备接口和下一需要传送的机器地址。
在内核中与socket对应的系统调用是sys_soceket,所谓的创建套接口,就是在sockfs这个文件系统中创建一个节点,从Linux/Unix的角度来看,该节点是一个文件,不过这个文件具有非普通文件的属性,于是起了一个独特的名字——socket。由于sockfs文件系统是系统初始化时就保存在全局指针sock_mnt中的,所以申请一个inode的过程便以sock_mnt为参数。从进程角度看,一个套接口就是一个特殊的已打开文件。现在将socket结构的宿主inode与文件系统挂上钩,就是分配文件号以及file结构在目录数中分配一个dentry结构。指向inode使file->f_dentry指向inode建立目录项和索引节点(即套接口的节点名) 现在我们来看看每个进程是如何对待其打开的文件及socket的:
这里写图片描述

从上图中可以看到,标准输入接口(std in)的文件描述符占据了进程的用户空间的文件描述符数组的第一个单元,这是系统内定的。通过内核的组织,其在内核中的描述符数组的第一个单元存放了指向其inode的地址,以此类推,标准输出接口(std out)占据第二个单元,错误输出接口(std err)占据第三个单元(上图没有画出)。而用户自己打开的文件或socket,则依次排列到系统已经内定的错误输出接口的fd单元后面,比如,如果某进程第一次打开了一个常规文件,它的fd必定是3,当再打开别的文件时,fd就是4,这里3和4就是用户空间中的fd数组下标。同样的道理,socket也可以看作是一个文件。每次调用socket返回的fd值也是指用户空间的fd数组下标。
现在我们来看看socket函数本身,经过glibc库对其封装,它将通过int 0x80<

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值