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。

未完待續。


分享到:


相關文章: