我們先回顧一下平常是如何使用 ip
指令來操作 network namespace
和 interface
。
首先我們要先回憶一下,我們平常使用ip指令來操作network namespace和interface的方式
net
和 net_device
資料結構接下來,我們進入 Linux Kernel 的網路子系統,這看看兩個與 network namespace
和 interface
密切相關的重要資料結構:struct net
和 struct net_device
。
邊提到的網路子系統主要對應到 source code 中的兩個部分,網路命名空間與網路設備。
net
資料結構代表了 Linux 中的 network namespace
。當我們使用容器時,會利用 network namespace
來隔離容器網路,並提供獨立的網路堆疊。在 Linux Kernel 中,network namespace
是透過 net
結構來管理的。
net_device
資料結構則代表網路設備,也就是我們常說的介面(interface)。當我們使用 ip link list
指令列出所有的網路設備時,這些資訊就是從 net_device
結構中讀取出來的。
net_device
會在後續進行介紹,今天只會聚焦在 net
結構上。
net
的結構在 Linux Kernel 中,所有的 net
結構會被串成一個雙向鏈結串列 double linked list,透過我們在前一天介紹過的 list_head
來維護。在 net
結構中,我們可以看到 struct list_head list;
這個欄位,它用來串連所有的 network namespace
。
// include/net/net_namespace.h
struct net {
...
struct list_head list; /* list of network namespaces */
...
struct list_head dev_base_head;
...
struct hlist_head *dev_name_head;
struct hlist_head *dev_index_head;
...
struct net_device *loopback_dev; /* The loopback */
...
}
net_device
net
結構中,有三個欄位用來保存 network namespace
中的網路設備(net_device
):
dev_base_head
:double link list,用來保存所有的網路設備。dev_name_head
和 dev_index_head
:這兩個欄位則是使用 hash list 來維護網路設備,因為在日常操作中,我們經常需要透過網路介面的名稱或 Index 來查找設備。Linux Kernel 提供了 dev_get_by_name
和 dev_get_by_index
這兩個函數,分別用來透過名稱或索引值查找網路設備,並使用 hash list 優化查找速度。另外,我們還可以看到一個特殊的指標 loopback_dev
,它指向了 lo
這個特殊的 loopback 介面。
// include/net/net_namespace.h
struct net {
...
struct ns_common ns;
...
}
// include/linux/ns_common.h
struct ns_common {
atomic_long_t stashed;
const struct proc_ns_operations *ops;
unsigned int inum;
refcount_t count;
};
在 net
裡面有一個特殊的子結構 struct ns_common ns
,ns_common
是所有 Linux namespace (包含網路、mount, cgroup, ...) 都會使用到的一個通用結構。ns_common
保存了用於 /proc
檔案系統的資料,透過 ls /proc/<pid>/ns -l
,我們可以看到每個 process 使用的不同 Linux namespace。每個 namespace 的檔案都會有一個 id,比如下列範例中 cgroup 的 id 是 4026531835,這個 id 便是保存在 ns_common.inum
這個欄位的數值。
> ls /proc/1/ns -l
lrwxrwxrwx 1 root root 0 九 15 16:28 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 九 15 16:28 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 九 15 16:28 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 root root 0 九 15 16:28 net -> 'net:[4026531992]'
lrwxrwxrwx 1 root root 0 九 15 16:28 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 九 15 16:28 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 九 15 16:28 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 九 15 16:28 uts -> 'uts:[4026531838]'
ns_common
和 /proc
的 ns 檔案是 user space 的 application 要操作 network namespace 時很重要的一個方式。下列是一個範例的程式碼,它打開了 process 123 的 network namespace 檔案,並呼叫 setns
這個函數。這個程式使用的 network namespace 就會被切換成與 process 123 相同的。
int main() {
int fd = open("/proc/123/ns/net", O_RDONLY | O_CLOEXEC);
setns(fd, CLONE_NEWNET);
/* 做點甚麼 */
return 0;
}
這邊先提到 struct ns_common ns
這個欄位的存在,後面我們會聊到這個切換機制是如何實現的,到時候這個欄位就會再次出現。
今天我們初步介紹了 net
和 net_device
這兩個在 Linux Kernel 中極為重要的資料結構,以及 net
結構如何在 Kernel 內部組織與運作。接下來的幾天,我們將深入探討 net
結構的建立與管理。