ip 协议栈的讲解 第五节

上一节中,我们讲了IP地址的相关内容,以及讲了和IP地址操作相关的函数,这一章我们继续讲解和IP相关的知识点,对于linux系统的使用者来说,配置个IP地址,只需要通过ifconfig命令就可以配置IP地址,ifconfig是net-tools包里的工具,linux系统除了net-tools包,还有一个功能更强大的包,就是iproute2的包,这个包里的工具是通过linux特有的netlink接口对IP地址进行的设置。

1. IP地址的配置

1.1 netlink 接口

linux通过netlink接口操作ip的简化的流程如下:

linux tcp/ip 协议栈的讲解 第五节

用户空间与内核空间netlink交互的示意图

由上图我们看到,用户空间是通过netlink接口向linux内核空间传送数据的,内核拿到用户态的数据后,进行调用不同的处理函数进行IP地址的操作。

1.2 netlink 消息结构

对IP地址进行增删改查的netlink的结构如下所示:

linux tcp/ip 协议栈的讲解 第五节

netlink结构示意图

nlmsghdr 是netlink消息结构的首部,ifaaddrmsg是ip配置的信息结构,由上图可知,ifaaddrmsg和nlmsghdr是挨在一起的,要注意这里的nlmsghdr一定要4字节对齐,否则会出现问题。

ifaaddrmsg 结构的说明如下:

ifa_family:用来表示IP地址所属于的协议族,比如:AF_INET,AF_INET6等。

ifa_prefixlen:地址的掩码长度。

ifa_index:网络设备的索引。

ifa_scope: ip地址的范围。

2. inet_rtm_newaddr 函数

当用户态添加IP地址时,会调用此函数,函数内容如下:

 static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
struct in_ifaddr *ifa;

ASSERT_RTNL();

ifa = rtm_to_ifaddr(net, nlh);
if (IS_ERR(ifa))
return PTR_ERR(ifa);

return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid);
}

从ip配置消息中,获取地址的信息,然后将ip配置到指定的网络设备上。

2.1 inet_rtm_deladdr 函数

当通过netlink接口,传入的操作类型是RTM_DELADDR时,会删除IP地址,此时就会调用该函数。

static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
struct nlattr *tb[IFA_MAX+1];
struct in_device *in_dev;
struct ifaddrmsg *ifm;
struct in_ifaddr *ifa, **ifap;
int err = -EINVAL;

ASSERT_RTNL();

err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
if (err < 0)
goto errout;

ifm = nlmsg_data(nlh);
in_dev = inetdev_by_index(net, ifm->ifa_index);
if (in_dev == NULL) {
err = -ENODEV;
goto errout;
}

__in_dev_put(in_dev);

for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
ifap = &ifa->ifa_next) {
if (tb[IFA_LOCAL] &&
ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
continue;

if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
continue;

if (tb[IFA_ADDRESS] &&
(ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
!inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
continue;

__inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid);
return 0;

}

err = -EADDRNOTAVAIL;
errout:
return err;
}

解析用户态传入的netlink的数据,获取配置参数,然后遍历ip链表,查找匹配的ip配置块。

2.2 inet_insert_ifa函数

326:static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
327: u32 pid)
328:{
329:struct in_device *in_dev = ifa->ifa_dev;
330:struct in_ifaddr *ifa1, **ifap, **last_primary;
331:
332:ASSERT_RTNL();
333:
334:if (!ifa->ifa_local) {
335:inet_free_ifa(ifa);
336:return 0;
337:}
338:
339:ifa->ifa_flags &= ~IFA_F_SECONDARY;
340:last_primary = &in_dev->ifa_list;
341:
342:for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
343: ifap = &ifa1->ifa_next) {
344:if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
345: ifa->ifa_scope <= ifa1->ifa_scope)
346:last_primary = &ifa1->ifa_next;
347:if (ifa1->ifa_mask == ifa->ifa_mask &&
348: inet_ifa_match(ifa1->ifa_address, ifa)) {
349:if (ifa1->ifa_local == ifa->ifa_local) {
350:inet_free_ifa(ifa);
351:return -EEXIST;
352:}
353:if (ifa1->ifa_scope != ifa->ifa_scope) {
354:inet_free_ifa(ifa);
355:return -EINVAL;
355:}
356:ifa->ifa_flags |= IFA_F_SECONDARY;
357:}

358:}
359:
360:if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
361:net_srandom(ifa->ifa_local);
362:ifap = last_primary;
363:}
364:
365:ifa->ifa_next = *ifap;
366:*ifap = ifa;

367:/* Send message first, then call notifier.
368: Notifier will trigger FIB update, so that
369: listeners of netlink will know about new ifaddr */
370:rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid);
371:blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
372:
373:return 0;
374:}

342-355:首先在所有的主IP地址中进行查找,如果主ip地址中存在了相同的ip地址的范围,则将添加的ip地址变成从属ip地址。

360-363: 如果是配置的IP地址,是首个IP,则将其加入到IP配置块中。

370: 向用户态进程发送RTM_NEWADDR的消息。

371: 通过inetaddr_chain通知链,将添加ip地址信息的消息发送给对添加IP地址感兴趣的模块。

1.1 inet_del_ifa函数

\t该函数,一看便知,就是用来删除一个ip地址。
1 static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
2 int destroy, struct nlmsghdr *nlh, u32 pid)
3 {
4 struct in_ifaddr *promote = NULL;

5 struct in_ifaddr *ifa, *ifa1 = *ifap;
6 struct in_ifaddr *last_prim = in_dev->ifa_list;
7 struct in_ifaddr *prev_prom = NULL;

1 static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
2 int destroy, struct nlmsghdr *nlh, u32 pid)
3 {
4 struct in_ifaddr *promote = NULL;
5 struct in_ifaddr *ifa, *ifa1 = *ifap;
6 struct in_ifaddr *last_prim = in_dev->ifa_list;
7 struct in_ifaddr *prev_prom = NULL;
8 int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
9
10 ASSERT_RTNL();
11
12 /* 1. Deleting primary ifaddr forces deletion all secondaries
13 * unless alias promotion is set
14 **/
15
16 if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
17 struct in_ifaddr **ifap1 = &ifa1->ifa_next;
18
19 while ((ifa = *ifap1) != NULL) {
20 if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
21 ifa1->ifa_scope <= ifa->ifa_scope)
22 last_prim = ifa;
23
24 if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
25 ifa1->ifa_mask != ifa->ifa_mask ||
26 !inet_ifa_match(ifa1->ifa_address, ifa)) {
27 ifap1 = &ifa->ifa_next;
28 prev_prom = ifa;
29 continue;
30 }
31
32 if (!do_promote) {
33 *ifap1 = ifa->ifa_next;
34
35 rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid);
36 blocking_notifier_call_chain(&inetaddr_chain,
37 NETDEV_DOWN, ifa);
38 inet_free_ifa(ifa);
39 } else {
40 promote = ifa;
41 break;
42 }
43 }
44 }
45
46 /* 2. Unlink it */

47
48 *ifap = ifa1->ifa_next;
49
50 /* 3. Announce address deletion */
51
52 /* Send message first, then call notifier.
53 At first sight, FIB update triggered by notifier
54 will refer to already deleted ifaddr, that could confuse
55 netlink listeners. It is not true: look, gated sees
56 that route deleted and if it still thinks that ifaddr
57 is valid, it will try to restore deleted routes... Grr.
58 So that, this order is correct.
59 */
60 rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid);
61 blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
62
63 if (promote) {
64
65 if (prev_prom) {
66 prev_prom->ifa_next = promote->ifa_next;
67 promote->ifa_next = last_prim->ifa_next;
68 last_prim->ifa_next = promote;
69 }
70
71 promote->ifa_flags &= ~IFA_F_SECONDARY;
72 rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid);
73 blocking_notifier_call_chain(&inetaddr_chain,
74 NETDEV_UP, promote);
75 for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) {
76 if (ifa1->ifa_mask != ifa->ifa_mask ||
77 !inet_ifa_match(ifa1->ifa_address, ifa))
78 continue;
79 fib_add_ifaddr(ifa);
80 }
81
82 }
83 if (destroy)
84 inet_free_ifa(ifa1);
85 }

19-44: 假设删除的IP地址是主IP地址,并且没有启用promote_secondaries,则会将主IP以及它的从属ip全部被删除,否则会选择它的下一个级别的从属IP地址提升为主IP地址。

60:发送RTM_DELADDR的消息给其他的进程。

61: 通过inetaddr_chain通知链,通知对删除IP地址这个动作感兴趣的内核模块。

63-82:将从属IP提升为主IP。

未完待续。


分享到:


相關文章: