in_device和in_ifaddr數據結構

net_device、in_device、in_ifaddr數據結構關係:

一、in_device數據結構:

IP配置塊,網絡設備層與IPv4相關的配置都存放在in_device結構中,應用層可以通過ip或者ifconfig工具來修改這些配置。
該結構實例的地址保存在net_device的in_ptr中,可以通過in_dev_get()訪問它。訪問結束後,必須使用in_dev_put()。
in_dev_get()會遞增該實例的引用計數,in_dev_put()函數會遞減該結構的引用計數,當引用計數爲0時,才真正釋放該實例。

struct in_device {
	struct net_device	*dev;/*指向所屬的網絡設備*/
	atomic_t		refcnt;/*引用計數*/
	int			dead;/*爲1時標識所在的IP配置塊將要被釋放,不允許再訪問其成員*/
	
	/*指向 in_ifaddr架構鏈表,in_ifaddr中存儲了網絡設備的IP地址,
	因爲一個網絡設備可以配置多個IP地址,因此使用鏈表來存儲。*/
	struct in_ifaddr	*ifa_list;

	struct ip_mc_list __rcu	*mc_list;	/* IP multicast filter chain    */
	struct ip_mc_list __rcu	* __rcu *mc_hash;

	/*與組播相關配置*/
	int			mc_count;	/* Number of installed mcasts	*/
	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 */

	/*指向neigh_parms結構實例,存儲一些與ARP相關的參數*/
	struct neigh_parms	*arp_parms;
	
	/*ipv4_devconf相關信息,還不知道幹啥的,以後再看*/
	struct ipv4_devconf	cnf;
	
	/*RCU機制使用,實現互斥,如果鎖一般*/
	struct rcu_head		rcu_head;
};

二、in_ifaddr數據結構:

IP地址塊,存儲主機的IP地址,子網掩碼,廣播地址等信息。一個網絡設備有多少個IP地址,就有多少個IP地址塊。 

struct in_ifaddr {
	struct hlist_node	hash;
	struct in_ifaddr	*ifa_next;//in_ifaddr鏈表
	struct in_device	*ifa_dev;//指向所屬的in_device結構
	struct rcu_head		rcu_head;
	__be32			ifa_local;//本地IP地址
	__be32			ifa_address;//本地IP地址或對端IP地址
	__be32			ifa_mask;//子網掩碼
	__be32			ifa_broadcast;//廣播地址
	unsigned char		ifa_scope;//尋址範圍
	unsigned char		ifa_prefixlen;//子網掩碼長度
	__u32			ifa_flags;//IP地址屬性
	char			ifa_label[IFNAMSIZ];//網絡設備名

	/* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */
	__u32			ifa_valid_lft;
	__u32			ifa_preferred_lft;
	unsigned long		ifa_cstamp; /* created timestamp */
	unsigned long		ifa_tstamp; /* updated timestamp */
};

現在的in_ifaddr結構中加了hash結點hlist_node,上面圖應該有點小變化吧,這個結點鏈接到哪裏去了?

ifa_local和ifa_address的區別:

https://www.cnblogs.com/wanpengcoder/p/7385274.html

/*
* Important comment:
* IFA_ADDRESS is prefix address, rather than local interface address.
* It makes no difference for normally configured broadcast interfaces,
* but for point-to-point IFA_ADDRESS is DESTINATION address,
* local address is supplied in IFA_LOCAL attribute.
*/

1.ifa_local始終表示本地IP地址

2.如果設備配置了支持廣播,ifa_address和if_local一樣,也是本地IP地址;如果點對點鏈路,ifa_address表示對端的IP地址。

ifa_scope:

http://www.bubuko.com/infodetail-948730.html

/* 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,
/* User defined values  */
	RT_SCOPE_SITE=200,
	RT_SCOPE_LINK=253,
	RT_SCOPE_HOST=254,
	RT_SCOPE_NOWHERE=255
};

 HOST:表示該地址只用於主機的內部通信。例如:127.0.0.1

LINK:表示地址僅在局域網內部有意義,例如私有地址?

UNIBERSE:表示地址可以在任何地方使用,普通的外網IP地址?

NOWHERE:表示目的地址不存在

SITE:??

ifa_flags

三、in_device相關操作函數

1. inetdev_init()函數
給網絡設備分配IP配置塊,應該在初始化網絡設備net_device的時候調用的吧。就是創建in_device結構,初始化其成員。

static struct in_device *inetdev_init(struct net_device *dev)
{
	struct in_device *in_dev;
	int err = -ENOMEM;

	ASSERT_RTNL();

	in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);//分配in_device內存
	if (!in_dev)
		goto out;
	
	/*初始化in_device的cnf成員*/
	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
			sizeof(in_dev->cnf));
	in_dev->cnf.sysctl = NULL;
	in_dev->dev = dev;//指向net_device
	
	/*初始化in_device的arp_parms成員*/
	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
	if (!in_dev->arp_parms)
		goto out_kfree;
	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
		dev_disable_lro(dev);
	/* Reference in_dev->dev */
	dev_hold(dev);
	/* Account for reference dev->ip_ptr (below) */
	in_dev_hold(in_dev);

	err = devinet_sysctl_register(in_dev);
	if (err) {
		in_dev->dead = 1;
		in_dev_put(in_dev);
		in_dev = NULL;
		goto out;
	}
	/*初始化igmp模塊*/
	ip_mc_init_dev(in_dev);
	/*如果網絡設備已啓用,則初始化該網絡設備上的組播信息,例如將該網路設備加入224.0.0.1組播組等操作*/
	if (dev->flags & IFF_UP)
		ip_mc_up(in_dev);

	/* we can receive as soon as ip_ptr is set -- do this last */
	/*net_device的ip_ptr指針指向in_device*/
	rcu_assign_pointer(dev->ip_ptr, in_dev);
out:
	return in_dev ?: ERR_PTR(err);
out_kfree:
	kfree(in_dev);
	in_dev = NULL;
	goto out;
}

看看devinet_sysctl_register()函數都幹了啥

devinet_sysctl_register()函數:

static int devinet_sysctl_register(struct in_device *idev)
{
	int err;
	
	/*判斷網絡設備的名稱是否合法,不能爲default,all*/
	if (!sysctl_dev_name_is_allowed(idev->dev->name))
		return -EINVAL;

	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
	if (err)
		return err;
	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
					&idev->cnf);
	if (err)
		neigh_sysctl_unregister(idev->arp_parms);
	return err;
}

sysctl_dev_name_is_allowed()函數:

static inline bool sysctl_dev_name_is_allowed(const char *name)
{
	return strcmp(name, "default") != 0  && strcmp(name, "all") != 0;
}

 鄰居子系統的知識以後再補上,現在還不會鄰居子系統。

2. inetdev_destroy()函數

通常在設備被註銷時調用,釋放指定的IP配置塊。

這函數沒意思。

static void inetdev_destroy(struct in_device *in_dev)
{
	struct in_ifaddr *ifa;
	struct net_device *dev;

	ASSERT_RTNL();

	dev = in_dev->dev;

	in_dev->dead = 1;

	ip_mc_destroy_dev(in_dev);

	while ((ifa = in_dev->ifa_list) != NULL) {
		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
		inet_free_ifa(ifa);
	}

	RCU_INIT_POINTER(dev->ip_ptr, NULL);

	devinet_sysctl_unregister(in_dev);
	neigh_parms_release(&arp_tbl, in_dev->arp_parms);
	arp_ifdown(dev);

	call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
}

3.in_dev_get()函數
這個函數比較簡單,就是獲取in_device結構,將in_device結構引用計數+1,返回其指針。

static inline struct in_device *in_dev_get(const struct net_device *dev)
{
	struct in_device *in_dev;

	rcu_read_lock();
	in_dev = __in_dev_get_rcu(dev);
	if (in_dev)
		atomic_inc(&in_dev->refcnt);
	rcu_read_unlock();
	return in_dev;
}

static inline struct in_device *__in_dev_get_rcu(const struct net_device *dev)
{
	return rcu_dereference(dev->ip_ptr);
}

 4. in_dev_put()函數

也比較簡單,將in_device實例的應用計數減1,當應用計數爲0時,釋放該實例。

static inline void in_dev_put(struct in_device *idev)
{
	if (atomic_dec_and_test(&idev->refcnt))
		in_dev_finish_destroy(idev);
}

5. inetdev_dy_index()

根據net_device的ifindex查找該設備的in_device結構。

/* Caller must hold RCU or RTNL :
 * We dont take a reference on found in_device
 */
struct in_device *inetdev_by_index(struct net *net, int ifindex)
{
	struct net_device *dev;
	struct in_device *in_dev = NULL;

	rcu_read_lock();
	dev = dev_get_by_index_rcu(net, ifindex);
	if (dev)
		in_dev = rcu_dereference_rtnl(dev->ip_ptr);
	rcu_read_unlock();
	return in_dev;
}

 

發佈了159 篇原創文章 · 獲贊 108 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章