Kernel Network Layers
| OSI 7 Layer Model |
Linux 网络实现 |
| Application |
User space - Application |
| Presentation |
User space - Application |
| Session |
User space - Application |
| Transport |
Kernel - L4 (TCP/UDP, …) |
| Network |
Kernel - L3 (IPv4, IPv6) |
| Data Link |
Kernel - L2 |
| Physical |
Hardware - Physical |
Data Structure
Network Device
- Defines an instance of network interface
- Tracks the state information of all network interfaces
- Large structure, consisting device parameters like:
- IRQ number
- MAC address
- MTU
- Driver callback operations
- Name of the device (Like eth0, eth1, wifi0)
- Flags of the device (Status of the device like up or down)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// include/linux/netdevice.h
struct net_device {
char name[IFNAMSIZ];
...
int irq;
...
int ifindex;
...
struct net_device_stats stats;
...
const struct net_device_ops *netdev_ops;
...
unsigned int flags;
...
unsigned int mtu;
...
unsigned char *dev_addr;
...
};
|
Register/Unresgiter Callbacks

Network Device APIs
- Allocate and Free network device
- Register and Unregister network device
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#define alloc_netdev(sizeof_priv, name, name_assign_type, setup) \
alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, 1, 1)
struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
unsigned name_assign_type,
void (*setup)(struct net_device *),
unsigned int txqs, unsigned int rxqs);
void free_netdev(struct net_device *dev);
int register_netdev(struct net_device *dev);
void unregister_netdev(struct net_device *dev);
|
Socket Buffer
- sk_buff – Represents an incoming or outgoing packet
- It is the core data structure of Linux network stack.
- Understanding of this structure and its APIs is important.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
struct sk_buff {
...
struct sock *sk;
struct net_device *dev;
...
__u8 pkt_type:3;
...
__be16 protocol;
__u16 transport_header;
__u16 network_header;
__u16 mac_header;
...
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head, *data;
...
};
|

Network Inter Submodule Communication
Socket Initialization
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
// net/socket.c
static int __init sock_init(void)
{
int err;
/*
* Initialize the network sysctl infrastructure.
*/
err = net_sysctl_init();
if (err)
goto out;
/*
* Initialize skbuff SLAB cache
*/
skb_init();
/*
* Initialize the protocols module.
*/
init_inodecache();
err = register_filesystem(&sock_fs_type);
if (err)
goto out_fs;
sock_mnt = kern_mount(&sock_fs_type);
if (IS_ERR(sock_mnt)) {
err = PTR_ERR(sock_mnt);
goto out_mount;
}
...
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
int sock_register(const struct net_proto_family *ops)
{
int err;
if (ops->family >= NPROTO) {
pr_crit("protocol %d >= NPROTO(%d)\n", ops->family, NPROTO);
return -ENOBUFS;
}
spin_lock(&net_family_lock);
if (rcu_dereference_protected(net_families[ops->family],
lockdep_is_held(&net_family_lock)))
err = -EEXIST;
else {
rcu_assign_pointer(net_families[ops->family], ops);
err = 0;
}
spin_unlock(&net_family_lock);
pr_info("NET: Registered protocol family %d\n", ops->family);
return err;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
void sock_unregister(int family)
{
BUG_ON(family < 0 || family >= NPROTO);
spin_lock(&net_family_lock);
RCU_INIT_POINTER(net_families[family], NULL);
spin_unlock(&net_family_lock);
synchronize_rcu();
pr_info("NET: Unregistered protocol family %d\n", family);
}
|
Inet Initialization
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
// net/ipv4/af_inet.c
static int __init inet_init(void)
{
struct inet_protosw *q;
struct list_head *r;
int rc = -EINVAL;
sock_skb_cb_check_size(sizeof(struct inet_skb_parm));
rc = proto_register(&tcp_prot, 1);
if (rc)
goto out;
rc = proto_register(&udp_prot, 1);
if (rc)
goto out_unregister_tcp_proto;
rc = proto_register(&raw_prot, 1);
if (rc)
goto out_unregister_udp_proto;
rc = proto_register(&ping_prot, 1);
if (rc)
goto out_unregister_raw_proto;
/*
* Tell SOCKET that we are alive...
*/
(void)sock_register(&inet_family_ops);
...
static const struct net_proto_family inet_family_ops = {
.family = PF_INET,
.create = inet_create,
.owner = THIS_MODULE,
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
// net/ipv6/af_inet6.c
static int __init inet6_init(void)
{
struct list_head *r;
int err = 0;
sock_skb_cb_check_size(sizeof(struct inet6_skb_parm));
/* Register the socket-side information for inet6_create. */
for (r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r)
INIT_LIST_HEAD(r);
if (disable_ipv6_mod) {
pr_info("Loaded, but administratively disabled, reboot required to enable\n");
goto out;
}
err = proto_register(&tcpv6_prot, 1);
if (err)
goto out;
err = proto_register(&udpv6_prot, 1);
if (err)
goto out_unregister_tcp_proto;
err = proto_register(&udplitev6_prot, 1);
if (err)
goto out_unregister_udp_proto;
err = proto_register(&rawv6_prot, 1);
if (err)
goto out_unregister_udplite_proto;
err = proto_register(&pingv6_prot, 1);
if (err)
goto out_unregister_ping_proto;
/* We MUST register RAW sockets before we create the ICMP6,
* IGMP6, or NDISC control sockets.
*/
err = rawv6_init();
if (err)
goto out_unregister_raw_proto;
/* Register the family here so that the init calls below will
* be able to create sockets. (?? is this dangerous ??)
*/
err = sock_register(&inet6_family_ops);
...
static const struct net_proto_family inet6_family_ops = {
.family = PF_INET6,
.create = inet6_create,
.owner = THIS_MODULE,
};
|
sock_register 的作用是 向 VFS socket 层注册一个协议族(protocol family),让用户态能够通过 socket 系统调用创建该协议族对应的 socket。
Initialization Order
1
2
3
|
core_initcall(sock_init); /* early initcall */
fs_initcall(inet_init);
|
内核模块有一个函数叫module_init, 你可以指定你的内核模块的入口函数, 所以当你在使用insmod命令来加载内核模块的时候,那个函数将被kernel 调用。类似地,如果你把函数指定为initcall或fsinitcall,那就意味着你在告诉内核,当你启动的时候,要调用这个叫这个函数的函数。那么现在,这个面板要怎么决定是先处理sock还是先处理inet呢?
Socket will be initialized first; Next, Inet will will be initialized
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
// include\linux\init.h
/*
* Early initcalls run before initializing SMP.
*
* Only for built-in code, not modules.
*/
#define early_initcall(fn) __define_initcall(fn, early)
/*
* A "pure" initcall has no dependencies on anything else, and purely
* initializes variables that couldn't be statically initialized.
*
* This only exists for built-in code, not for modules.
* Keep main.c:initcall_level_names[] in sync.
*/
#define pure_initcall(fn) __define_initcall(fn, 0)
#define core_initcall(fn) __define_initcall(fn, 1)
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
#define postcore_initcall(fn) __define_initcall(fn, 2)
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
#define arch_initcall(fn) __define_initcall(fn, 3)
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
#define subsys_initcall(fn) __define_initcall(fn, 4)
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
#define fs_initcall(fn) __define_initcall(fn, 5)
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
#define device_initcall(fn) __define_initcall(fn, 6)
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
#define late_initcall(fn) __define_initcall(fn, 7)
#define late_initcall_sync(fn) __define_initcall(fn, 7s)
|
Socket Create
Socket指的是套接字结构,而sock则属于数据包中的内容。sk_buff中包含一个指向sock结构的指针,不是结构体socket.
这个结构体socket也维护了一个sock的参数,但这个套接字用于套接字API和协议,即注册到套接字
struct socket 中有const struct proto_ops *ops, 这里proto_ops是目前已经注册了的 协议 :TCP、UDP等等
但在sock结构体中,这主要与套接字缓冲区相关,并且它和IP层或者链路层是有关联的。当数据包正在被接收或者正在被发送出去的时候,这个sk才会被关联。
同样,如果这个数据包是从你的机器转发出去的,那么这个sk参数就会是空的,如果你的机器不是这里的终端设备也不是这里的节点设备的话:如果它是一台router类型的设备,那么sk这个变量就不会是空的,而是会指向那个套接字。只有当数据包是从这台特定的任务机器上生成时,才会出现这种情况。
所以这两个结构体,在我们维护数据包或者收发数据包的时候,也很重要。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// include/linux/net.h
struct socket {
socket_state state;
kmemcheck_bitfield_begin(type);
short type;
kmemcheck_bitfield_end(type);
unsigned long flags;
...
struct file *file;
struct sock *sk;
const struct proto_ops *ops;
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// include/net/sock.h
struct sock {
struct sk_buff_head sk_receive_queue;
int sk_rcvbuf;
unsigned long sk_flags;
int sk_sndbuf;
struct sk_buff_head sk_write_queue;
...
unsigned int sk_shutdown : 2,
sk_no_check : 2,
sk_protocol : 8,
sk_type : 16;
...
void (*sk_data_ready)(struct sock *sk, int bytes);
void (*sk_write_space)(struct sock *sk);
};
|
Socket Create – Cont
用户空间socket层的一些APIs:套接字绑定、连接、监听、读取、写入、发送。每个调用在内核控制台里都有一个对应的函数。socket 对应 sys_socket,bind 对应 sys_bind,bind映射到sys_bind,connect映射到sys_connect等。所以,每当用户空间去调用对应的socket时,这个函数就会被触发调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// net\socket.c
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
int retval;
struct socket *sock;
int flags;
...
retval = sock_create(family, type, protocol, &sock);
if (retval < 0)
goto out;
...
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
if (retval < 0)
goto out_release;
out:
/* It may be already another descriptor 8) Not kernel problem. */
return retval;
out_release:
sock_release(sock);
return retval;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
// socket 也是一个文件
/**
* sock_alloc - allocate a socket
*
* Allocate a new inode and socket object. The two are bound together
* and initialised. The socket is then returned. If we are out of inodes
* NULL is returned.
*/
static struct socket *sock_alloc(void)
{
struct inode *inode;
struct socket *sock;
inode = new_inode_pseudo(sock_mnt->mnt_sb);
if (!inode)
return NULL;
sock = SOCKET_I(inode);
kmemcheck_annotate_bitfield(sock, type);
inode->i_ino = get_next_ino();
inode->i_mode = S_IFSOCK | S_IRWXUGO;
inode->i_uid = current_fsuid();
inode->i_gid = current_fsgid();
inode->i_op = &sockfs_inode_ops;
this_cpu_add(sockets_in_use, 1);
return sock;
}
|