linux内核协议栈 IP地址数据结构

本文详细探讨了Linux内核中的IP地址数据结构,包括struct net_device中的ip_ptr字段,struct in_device和struct in_ifaddr的组织关系,IP地址作用域的定义,以及IP配置块的创建与销毁过程。inet_select_addr()函数在源IP地址选择中的作用也被重点提及。
摘要由CSDN通过智能技术生成

目录

1 网络设备 struct net_device

2 IP配置块 struct in_device

2.1 IP地址 struct in_ifaddr

2.2 IP地址作用域

2.3 系统配置 struct ipv4_devconf

3 IP配置块的创建与销毁inetdev_init()/inetdev_destroy()

4 源IP地址选择 inet_select_addr()


1 网络设备 struct net_device

IP地址是和主机相关的,但是在linux内核中,IP地址却是配置到网卡的,即struct net_device中,在网络设备结构中,字段ip_ptr和ip6_ptr分别指向该网络设备的IP配置块和IPv6配置块,下面我们只关注IP配置块,ip_ptr指针实际指向的结构是struct in_device对象。

struct net_device
{
...
	void *ip_ptr;	/* IPv4 specific data	*/  
	void *ip6_ptr;       /* IPv6 specific data */
...
}

2 IP配置块 struct in_device

struct in_device
{
	struct net_device *dev; // 回指网络设备
	atomic_t refcnt;
	// 当一个IP配置块将要被销毁时,先设置该标记为1,当引用计数为0时再执行内存回收
	int	dead;
	// 每个网络设备可以配置多个IP地址,所以是列表
	struct in_ifaddr *ifa_list;	/* IP ifaddr chain */
	// 多播相关的结构
	rwlock_t mc_list_lock;
	struct ip_mc_list	*mc_list;	/* IP multicast filter chain    */
	spinlock_t		mc_tomb_lock;
	struct ip_mc_list	*mc_tomb;
	unsigned long		mr_v1_seen;
	unsigned long		mr_v2_seen;
	unsigned long		mr_maxdelay;
	unsigned char		mr_qrv;
	unsigned char		mr_gq_running;
	unsigned char		mr_ifc_count;
	struct timer_list	mr_gq_timer;	/* general query timer */
	struct timer_list	mr_ifc_timer;	/* interface change timer */

	// 网络设备定义的邻居子系统配置参数
	struct neigh_parms	*arp_parms;
	// 影响该网络设备的IP系统配置
	struct ipv4_devconf	cnf;
	// 使用RCU机制回收IP配置块
	struct rcu_head	rcu_head;
};

网络设备与IP配置块的组织关系如下图:
net_device与IP配置块

2.1 IP地址 struct in_ifaddr

结构in_ifaddr保存了一个IP地址相关的配置信息。

struct in_ifaddr
{
	struct in_ifaddr *ifa_next; // 同一个网络设备上配置的IP地址组成成链表
	struct in_device *ifa_dev; // 回指网络设备
	struct rcu_head	rcu_head; // 使用RCU保护本IP地址的修改
	// 对于支持广播的网络,ifa_local和ifa_address一样保存的都是本端地址;
	// 对于点对点网络,ifa_address保存的是对端IP地址
	__be32	ifa_local;
	__be32	ifa_address;
	__be32	ifa_mask; // 子网掩码
	__be32	ifa_broadcast; // 广播地址
	__be32	ifa_anycast;
	unsigned char ifa_scope; // IP地址作用域,见下文
	unsigned char ifa_flags;
	unsigned char ifa_prefixlen; // 子网掩码长度
	char ifa_label[IFNAMSIZ]; // 网络地址别名
};

2.2 IP地址作用域

IP地址的作用域指定了该IP地址可以出现的范围,系统定义了如下IP地址作用域,值越大,生效范围越小。

/* rtm_scope

   Really it is not scope, but sort of distance to the destination.
   NOWHERE are reserved for not existing destinations, HOST is our
   local addresses, LINK are destinations, located on directly attached
   link and UNIVERSE is everywhere in the Universe.

   Intermediate values are also possible f.e. interior routes
   could be assigned a value between UNIVERSE and LINK.
*/
enum rt_scope_t
{
	RT_SCOPE_UNIVERSE=0, // 该IP地址可以出现再任意地方
/* User defined values  */
	RT_SCOPE_SITE=200, // 不理解和RT_SCOPE_LINK的区别
	RT_SCOPE_LINK=253, // 该IP地址只能出现在直连的局域网内部
	RT_SCOPE_HOST=254, // 该IP地址只能出现在本主机内部
	RT_SCOPE_NOWHERE=255 // 该IP地址不可用
};

从代码注释上看,作用是路由的概念,但是IP地址复用了这些定义。

2.3 系统配置 struct ipv4_devconf

全局变量 ipv4_devconf 保存了系统级别的IPv4网络配置,如上,每个网络设备也有自己的IPv4网络配置。

enum
{
	NET_IPV4_CONF_FORWARDING=1,
	NET_IPV4_CONF_MC_FORWARDING=2,
	NET_IPV4_CONF_PROXY_ARP=3,
	NET_IPV4_CONF_ACCEPT_REDIRECTS=4,
	NET_IPV4_CONF_SECURE_REDIRECTS=5,
	NET_IPV4_CONF_SEND_REDIRECTS=6,
	NET_IPV4_CONF_SHARED_MEDIA=7,
	NET_IPV4_CONF_RP_FILTER=8,
	NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE=9,
	NET_IPV4_CONF_BOOTP_RELAY=10,
	NET_IPV4_CONF_LOG_MARTIANS=11,
	NET_IPV4_CONF_TAG=12,
	NET_IPV4_CONF_ARPFILTER=13,
	NET_IPV4_CONF_MEDIUM_ID=14,
	NET_IPV4_CONF_NOXFRM=15,
	NET_IPV4_CONF_NOPOLICY=16,
	NET_IPV4_CONF_FORCE_IGMP_VERSION=17,
	NET_IPV4_CONF_ARP_ANNOUNCE=18,
	NET_IPV4_CONF_ARP_IGNORE=19,
	NET_IPV4_CONF_PROMOTE_SECONDARIES=20,
	NET_IPV4_CONF_ARP_ACCEPT=21,
	__NET_IPV4_CONF_MAX
};

struct ipv4_devconf
{
	void	*sysctl;
	int	data[__NET_IPV4_CONF_MAX - 1];
	DECLARE_BITMAP(state, __NET_IPV4_CONF_MAX - 1);
};

3 IP配置块的创建与销毁inetdev_init()/inetdev_destroy()

当首次为设备配置IP地址时,会先为网络设备分配IP配置块,对应的函数是inetdev_init()。

static struct in_device *inetdev_init(struct net_device *dev)
{
	struct in_device *in_dev;

	// 修改网络设备内容,持有信号量
	ASSERT_RTNL();
	// 分配IP配置块并进行初始化
	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
	if (!in_dev)
		goto out;
	INIT_RCU_HEAD(&in_dev->rcu_head);
	// 将全局的IPv4配置信息拷贝到网络设备中,作为该网络设备的默认配置
	memcpy(&in_dev->cnf, dev->nd_net->ipv4.devconf_dflt, sizeof(in_dev->cnf));
	in_dev->cnf.sysctl = NULL;
	in_dev->dev = dev;
	// 将ARP协议规定的邻居参数拷贝到网络设备
	if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL)
		goto out_kfree;
	// IP配置块持有网络设备的引用计数
	dev_hold(dev);
	// 网络设备持有IP配置块的引用计数
	in_dev_hold(in_dev);
	// 将ARP相关的邻居配置参数、网络设备级别的IPv4配置块配置到IPv4目录下
	devinet_sysctl_register(in_dev);
	// 多播初始化
	ip_mc_init_dev(in_dev);
	if (dev->flags & IFF_UP)
		ip_mc_up(in_dev);

	// 将IP配置块绑定到网络设备
	rcu_assign_pointer(dev->ip_ptr, in_dev);
out:
	return in_dev;
out_kfree:
	kfree(in_dev);
	in_dev = NULL;
	goto out;
}

对IP地址的销毁由函数inetdev_destroy()完成,通常在网络设备被注销时执行,这里不再分析。

4 源IP地址选择 inet_select_addr()

当使用网络设备发送报文时,如果此时还没有选定源IP地址,那么会调用inet_select_addr()完成这一工作。

// 判断addr和ifa中指定的IP地址是否在同一子网
static __inline__ int inet_ifa_match(__be32 addr, struct in_ifaddr *ifa)
{
	return !((addr^ifa->ifa_address)&ifa->ifa_mask);
}

@dev: 出口设备
@dst:目的地址
@scope:指示报文将被送往哪里,比如RT_SCOPE_HOST表示报文将被送往本地
__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
{
	__be32 addr = 0;
	struct in_device *in_dev;

	rcu_read_lock();
	// 找到网络设备的IP配置块
	in_dev = __in_dev_get_rcu(dev);
	if (!in_dev)
		goto no_in_dev;
	// 遍历该网络设备上的主IP地址(所谓主IP地址,是指同一作用域中第一个配置到网络设备的IP地址)
	for_primary_ifa(in_dev) {
		// 本机IP地址的作用域不能比目的地小
		if (ifa->ifa_scope > scope)
			continue;
		// 目的地址为0,或者目的地址和ifa地址属于同一子网,那么选择该地址
		if (!dst || inet_ifa_match(dst, ifa)) {
			addr = ifa->ifa_local;
			break;
		}
		// 该逻辑保证总是会选择一个本机IP地址,即使本机地址和目的地址不在同一个子网
		if (!addr)
			addr = ifa->ifa_local;
	} endfor_ifa(in_dev);
no_in_dev:
	rcu_read_unlock();
	// 找到后返回
	if (addr)
		goto out;

	/* Not loopback addresses on loopback should be preferred
	   in this case. It is importnat that lo is the first interface
	   in dev_base list.
	 */
	// 指定的网络设备上没有符合的IP地址,尝试从其它设备上选择
	read_lock(&dev_base_lock);
	rcu_read_lock();
	for_each_netdev(&init_net, dev) {
		if ((in_dev = __in_dev_get_rcu(dev)) == NULL)
			continue;

		for_primary_ifa(in_dev) {
			if (ifa->ifa_scope != RT_SCOPE_LINK && ifa->ifa_scope <= scope) {
				addr = ifa->ifa_local;
				goto out_unlock_both;
			}
		} endfor_ifa(in_dev);
	}
out_unlock_both:
	read_unlock(&dev_base_lock);
	rcu_read_unlock();
out:
	return addr;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值