目录
3 IP配置块的创建与销毁inetdev_init()/inetdev_destroy()
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配置块的组织关系如下图:
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;
}