Linux network layer 与socket

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;
}
Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计