register_pernet_subsys 笔记

pernet ops

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* register_pernet_subsys - register a network namespace subsystem
* @ops: pernet operations structure for the subsystem
*
* Register a subsystem which has init and exit functions
* that are called when network namespaces are created and
* destroyed respectively.
*
* When registered all network namespace init functions are
* called for every existing network namespace. Allowing kernel
* modules to have a race free view of the set of network namespaces.
*
* When a new network namespace is created all of the init
* methods are called in the order in which they were registered.
*
* When a network namespace is destroyed all of the exit methods
* are called in the reverse of the order with which they were
* registered.
*/
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
int register_pernet_subsys(struct pernet_operations *ops)
{
int error;
mutex_lock(&net_mutex);
error = register_pernet_operations(first_device, ops);
mutex_unlock(&net_mutex);
return error;
}
===>static int register_pernet_operations(struct list_head *list,
struct pernet_operations *ops)
{
error = __register_pernet_operations(list, ops);
}

======>#ifdef CONFIG_NET_NS
static int __register_pernet_operations(struct list_head *list,
struct pernet_operations *ops)
LIST_HEAD(net_exit_list);

list_add_tail(&ops->list, list);
if (ops->init || (ops->id && ops->size)) {
for_each_net(net) {
error = ops_init(ops, net);
if (error)
goto out_undo;
list_add_tail(&net->exit_list, &net_exit_list);
}
}
return 0;


========>#ifdef CONFIG_NET_NS
static int __register_pernet_operations(struct list_head *list,
struct pernet_operations *ops)
struct net *net;
int error;
LIST_HEAD(net_exit_list);

list_add_tail(&ops->list, list);
if (ops->init || (ops->id && ops->size)) {
for_each_net(net) {
=============> error = ops_init(ops, net);
if (error)
goto out_undo;
=============> list_add_tail(&net->exit_list, &net_exit_list);<<< confused?!!! net_exit_list局部变量?

}
}
return 0;


=============>static int ops_init(const struct pernet_operations *ops, struct net *net)
{
int err;
if (ops->id && ops->size) {
void *data = kzalloc(ops->size, GFP_KERNEL);
if (!data)
return -ENOMEM;

err = net_assign_generic(net, *ops->id, data);
if (err) {
kfree(data);
return err;
}
}
if (ops->init)
return ops->init(net);<====== the ops->init will be called.
return 0;
}

####Fox example
inet6_init in pernet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static struct pernet_operations inet6_net_ops = { 
.init = inet6_net_init,
.exit = inet6_net_exit,
};
static int __init inet6_init(void)
{
.....
err = register_pernet_subsys(&inet6_net_ops);
if (err)
goto register_pernet_fail;
.....
}
call: ops->init(net);<====== the ops->init will be called.
equal with ======= .init = inet6_net_init,
inet6_net_init(net);

PROMISC in net device->flag

summary

promisc is one bit of struct net_device’s flag, which is used to indicate if a device is in promisc status.

1
2
3
4
5
6
7
8
9
10
11
12
30 /* Standard interface flags (netdevice->flags). */
31 #define IFF_UP 0x1 /* interface is up */
32 #define IFF_BROADCAST 0x2 /* broadcast address valid */
33 #define IFF_DEBUG 0x4 /* turn on debugging */
34 #define IFF_LOOPBACK 0x8 /* is a loopback net */
35 #define IFF_POINTOPOINT 0x10 /* interface is has p-p link */
36 #define IFF_NOTRAILERS 0x20 /* avoid use of trailers */
37 #define IFF_RUNNING 0x40 /* interface RFC2863 OPER_UP */
38 #define IFF_NOARP 0x80 /* no ARP protocol */
39 #define IFF_PROMISC 0x100 /* receive all packets */
40 #define IFF_ALLMULTI 0x200 /* receive all multicast packets*/
...

There are two kinds of operataion, could cause a NIC enter/leave promisc status.

  1. ip command
    run mutli on command, just need one off to recover.

    1
    2
      	ip link set dev eth0 promisc on
    ip link set dev eth0 promisc off
  2. tcpdump command
    When tcpdump starts, it let dev to promisc,
    and just before exit, tcpdump let dev left promisc.
    All these is done by call kernel api dev_set_promiscuity.

Read More

Autotools

##步骤简要表述:

  1. 创建 configure.ac 文件
  2. 在根目录及各个子目录下依次创建Makefile.am文件。
  3. 使用命令“autoreconf –install” 命令自动生成confiugre文件
  4. 执行新生成的脚本 configure
  5. 执行make文件。

Read More

创建req scoket时的三个长度检查

创建半链接时的三个长度检查

在处理TCP-SYN首包时候, tcp_conn_request函数里, 会有三个不同条件的长度检查。

  • inet_csk_reqsk_queue_is_full 半链接的个数超过sk_max_ack_backlog, 则丢包。
  • sk_acceptq_is_full: accept 队列长度超过sk_max_ack_backlog,则丢包。
  • sysctl_tcp_syncookies禁用(值为0)时, sysctl_max_syn_backloginet_csk_reqsk_queue_len: 队列长度如果超过sysctl_max_syn_backlog的3/4则丢包

其中,

  • sysctl_max_syn_backlog: 初始化时,最小 128。如果ehash_entries/128比128大,取最大值。ehash_entries是 tcp 的 hash 桶个数。
  • sysctl_tcp_syncookies: 初始值为 1

判断1: 半链接队列是否溢出 inet_csk_reqsk_queue_is_full

虽然不再维护半链接队列了, 但是每次创建req socket后,这个统计值都是在增加的。
因此如果半链接个数超过了最大值sk_max_ack_backlog,则启用cookie(sysctl_tcp_syncookies为1或2),如果不支持cookie,则丢弃。

1
2
3
4
5
6
7
8
9
278 static inline int inet_csk_reqsk_queue_len(const struct sock *sk)
279 {
280 return reqsk_queue_len(&inet_csk(sk)->icsk_accept_queue);
281 }
282
283 static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk)
284 {
285 return inet_csk_reqsk_queue_len(sk) > READ_ONCE(sk->sk_max_ack_backlog);
286 }

当前内核默认启用syncookie机制(sysctl_tcp_syncookies为1),队列溢出会触发synccookie 机制。
只有关闭了tcpcookie(0)后,才会在队列溢出时候丢弃syn报文。

Read More

ifconfig通过别名给网口配置多个IP地址

内核如何管理 ip 地址

内核如何管理多网口多 IP 地址

命令行操作

有时候我们需要给一个网口配置的有个 IP 地址,这时候我们有两种配置方法:

  • 使用 ifconfig 配置到网口别名上。
  • 使用 ip addr 命令直接配置到网口上。

两种方法最终在内核实现是一样的,存储位置也一样,并且可以相互读写配置结果。

比如将9.9.9.199/24配置到 lo 口上,并起个别名lo:9

1
2
3
4
5
6
[root@VM-0-12-centos ~]# ifconfig  lo:9 9.9.9.199/24
[root@VM-0-12-centos ~]# ifconfig lo:9
lo:9: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 9.9.9.199 netmask 255.255.255.0
loop txqueuelen 1000 (Local Loopback)
[root@VM-0-12-centos ~]#

ifconfig命令的配置结果,也可以通用ip link命令来查看

1
2
3
4
5
6
7
8
9
[root@VM-0-12-centos ~]# ip a show dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet 9.9.9.199/24 scope global lo:9
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever

网口的别名在ip命令里被当做label输出,放在scope字段后面

Read More

网口状态标志位解析: part1

IP link命令结果里的网口状态标志位

我们先来看几个场景里,不同状态的网口,输出的标志位有哪些不一样。

正常状态下物理网口eth0 的输出

命令行ip link show dev eth0会输出网口eth0的一些状态标志位。
这里注意一个现象, ‘ifconfig’命令显示的结果里有’RUNNING’标志位, 但是’ip link’命令的结果里没有这个标志位。
图1: 正常网口的ip link 命令和ifconfig命令结果

场景2:拔掉物理口的网线后, 网口的状态变化。

网口状态标志位里有个明显变化,多了一个’NO-CARRIER’
图2: 拔掉网线后的网口输出结果

Read More

网口状态标志位解析part2: 内核如何维护网卡carrier的状态

内核如何维护网卡设备的RUNNING 状态

流程概述

主要几个部分:

  1. 网卡驱动有个看门狗:不同网卡驱动实现可能不太一样 ,功能负责监控网卡硬件上网线的状态, 当网线状态变换的时候,会激发内核的 carrier 处理函数。
  2. 内核两个通用的处理函数:netif_carrier_onnetif_carrier_off。这个函数会
    • 设置或者清除netdev->state上的__LINK_STATE_NOCARRIER标志位。
    • 发送事件消息给linkwatch,做后续处理.
    • 如果是网线插好了状态, 会启动一个通用的看门狗,这个看门狗是负责检测tx队列是否’HUNG’了, 如果’HUNG’了就调用网卡对应的处理函数ndo_tx_timeout, 做一些应急补救,比如对网卡队列复位等操作。这里的看门狗跟网卡驱动里的看门狗还不是同一个看门狗。具体差别待研究。
  3. linkwath模块:linkwatch本身是个workqueue 队列,对接受到的消息按照,分为紧急和非紧急两类。紧急的决定立即处理,非紧急的则挂到一个链表里,等定时器超时后,再集中处理。所有事件处理,最终都交给linkwatch_do_dev(struct net_device *dev)函数进行处理。 该函数更新netdev->operate标志位。同时调用通用的dev_activate或者dev_deactivate对网卡做网卡队列进行处理。 我们这里重点关注跟网卡状态位有管的部分,忽略跟网卡队列的处理。
    这里有两个重要函数rfc2863_policydefault_operstate 后面我们重点介绍。

carrier on 和 off 函数

netif_carrier_onnetif_carrier_off: 内核里的两个通用的处理函数,功能基本对称

carrier标志位

总结:
dev->state下的__LINK_STATE_NOCARRIER是 carrier是否OK 的唯一判断标准。

Read More

tcpdump如何实现参数-报文抓取长度

用法

tcpdump -s len

原理

tcpdump会把这个长度值编译到ebpf代码中。 ebpf代码执行完后,如果filter成功,则作为返回值(return)返回给调用方(内核协议栈),内核协议栈根据返回值,修改 skb报文到指定的长度,并借助 af socket 返回给用户空间。

tcpdump命令解析

tcpdump snaplen参数: ebpf指令对比

Read More

2024-07-24-struct-group

__struct_group

浏览IPv6代码时候,看到这样一个新玩法,
__struct_group

1
2
3
4
5
6
7
8
9
118 struct ipv6hdr {
...
132 __u8 hop_limit;
133
134 __struct_group(/* no tag */, addrs, /* no attrs */,
135 struct in6_addr saddr;
136 struct in6_addr daddr;
137 );
138 };

###用法
再看一下用法:

 922 /* copy IPv6 saddr & daddr to flow_keys, possibly using 64bit load/store
 923  * Equivalent to :      flow->v6addrs.src = iph->saddr;
 924  *                      flow->v6addrs.dst = iph->daddr;
 925  */
 926 static inline void iph_to_flow_copy_v6addrs(struct flow_keys *flow,
 927                                             const struct ipv6hdr *iph)
 928 {
 ...
 932         memcpy(&flow->addrs.v6addrs, &iph->addrs, sizeof(flow->addrs.v6addrs));
 ...
 
1
2
3

###

11 /** 12 * __struct_group() - Create a mirrored named and anonyomous struct 13 * 14 * @TAG: The tag name for the named sub-struct (usually empty) 15 * @NAME: The identifier name of the mirrored sub-struct 16 * @ATTRS: Any struct attributes (usually empty) 17 * @MEMBERS: The member declarations for the mirrored structs 18 * 19 * Used to create an anonymous union of two structs with identical layout 20 * and size: one anonymous and one named. The former's members can be used 21 * normally without sub-struct naming, and the latter can be used to 22 * reason about the start, end, and size of the group of struct members. 23 * The named struct can also be explicitly tagged for layer reuse, as well 24 * as both having struct attributes appended. 25 */ 26 #define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \ 27 union { \ 28 struct { MEMBERS } ATTRS; \ 29 struct TAG { MEMBERS } ATTRS NAME; \ 30 } ATTRS ```