0%

LVS(linux virtual server) 做网关的几种转发模式

DR(direct routing)

路由收包后先给到同vlan的LB,LB根据负载均衡策略选择后端X,将dest mac改为X的mac地址并发送
DR就是化身路由器。而路由器的工作原理一直都是只改dest mac,不动src mac。这个逻辑从来没变过

linux/net/netfilter/ipvs/ip_vs_xmit.c at 7e46731ed902354a86caf3510011eb2b827a8f73 · taikulawo/linux · GitHub
优势:

  • X的response不需要过LB,X的response发给路由,路由送给client
  • LB作为指路人只修改destination mac,并没有其他逻辑,所以性能很高
    劣势
  • LB和X必须处于同vlan
  • 必须依赖ARP发现X的mac
  • 修改MAC层,不对IP/TCP操作,所以不支持端口映射
阅读全文 »

tcp rev + tcp syn send

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
__dev_xmit_skb (\root\codes\kernel-dev\linux\net\core\dev.c:3760)
__dev_queue_xmit (\root\codes\kernel-dev\linux\net\core\dev.c:4310)
dev_queue_xmit (\root\codes\kernel-dev\linux\include\linux\netdevice.h:3082)
neigh_hh_output (\root\codes\kernel-dev\linux\include\net\neighbour.h:529)
neigh_output (\root\codes\kernel-dev\linux\include\net\neighbour.h:543)
ip_finish_output2 (\root\codes\kernel-dev\linux\net\ipv4\ip_output.c:238)
__ip_finish_output (\root\codes\kernel-dev\linux\net\ipv4\ip_output.c:0)
NF_HOOK_COND (\root\codes\kernel-dev\linux\include\linux\netfilter.h:293)
ip_output (\root\codes\kernel-dev\linux\net\ipv4\ip_output.c:439)
dst_output (\root\codes\kernel-dev\linux\include\net\dst.h:458)
ip_local_out (\root\codes\kernel-dev\linux\net\ipv4\ip_output.c:128)
__ip_queue_xmit (\root\codes\kernel-dev\linux\net\ipv4\ip_output.c:543)
ip_queue_xmit (\root\codes\kernel-dev\linux\net\ipv4\ip_output.c:557)
__tcp_transmit_skb (\root\codes\kernel-dev\linux\net\ipv4\tcp_output.c:1415)
__tcp_send_ack (\root\codes\kernel-dev\linux\net\ipv4\tcp_output.c:4082)
tcp_send_ack (\root\codes\kernel-dev\linux\net\ipv4\tcp_output.c:4088)
tcp_rcv_synsent_state_process (\root\codes\kernel-dev\linux\net\ipv4\tcp_input.c:6363)
tcp_rcv_state_process (\root\codes\kernel-dev\linux\net\ipv4\tcp_input.c:6539)
tcp_v4_do_rcv (\root\codes\kernel-dev\linux\net\ipv4\tcp_ipv4.c:1751)
tcp_v4_rcv (\root\codes\kernel-dev\linux\net\ipv4\tcp_ipv4.c:2150)
ip_protocol_deliver_rcu (\root\codes\kernel-dev\linux\net\ipv4\ip_input.c:205)
ip_local_deliver_finish (\root\codes\kernel-dev\linux\net\ipv4\ip_input.c:233)
NF_HOOK (\root\codes\kernel-dev\linux\include\linux\netfilter.h:304)
ip_local_deliver (\root\codes\kernel-dev\linux\net\ipv4\ip_input.c:254)
dst_input (\root\codes\kernel-dev\linux\include\net\dst.h:468)
ip_sublist_rcv_finish (\root\codes\kernel-dev\linux\net\ipv4\ip_input.c:580)
ip_list_rcv_finish (\root\codes\kernel-dev\linux\net\ipv4\ip_input.c:631)
ip_sublist_rcv (\root\codes\kernel-dev\linux\net\ipv4\ip_input.c:639)
ip_list_rcv (\root\codes\kernel-dev\linux\net\ipv4\ip_input.c:674)
__netif_receive_skb_list_ptype (\root\codes\kernel-dev\linux\net\core\dev.c:5570)
__netif_receive_skb_list_core (\root\codes\kernel-dev\linux\net\core\dev.c:5618)
__netif_receive_skb_list (\root\codes\kernel-dev\linux\net\core\dev.c:5670)
netif_receive_skb_list_internal (\root\codes\kernel-dev\linux\net\core\dev.c:5761)
gro_normal_list (\root\codes\kernel-dev\linux\include\net\gro.h:439)
napi_complete_done (\root\codes\kernel-dev\linux\net\core\dev.c:6101)
e1000_clean (\root\codes\kernel-dev\linux\drivers\net\ethernet\intel\e1000\e1000_main.c:3811)
__napi_poll (\root\codes\kernel-dev\linux\net\core\dev.c:6531)
napi_poll (\root\codes\kernel-dev\linux\net\core\dev.c:6598)
net_rx_action (\root\codes\kernel-dev\linux\net\core\dev.c:6731)
__do_softirq (\root\codes\kernel-dev\linux\kernel\softirq.c:553)
invoke_softirq (\root\codes\kernel-dev\linux\kernel\softirq.c:427)
__irq_exit_rcu (\root\codes\kernel-dev\linux\kernel\softirq.c:632)
irq_exit_rcu (\root\codes\kernel-dev\linux\kernel\softirq.c:644)
sysvec_apic_timer_interrupt (\root\codes\kernel-dev\linux\arch\x86\kernel\apic\apic.c:1074)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static inline struct sock *__inet_lookup(struct net *net,
struct inet_hashinfo *hashinfo,
struct sk_buff *skb, int doff,
const __be32 saddr, const __be16 sport,
const __be32 daddr, const __be16 dport,
const int dif, const int sdif,
bool *refcounted)
{
u16 hnum = ntohs(dport);
struct sock *sk;

sk = __inet_lookup_established(net, hashinfo, saddr, sport,
daddr, hnum, dif, sdif);
*refcounted = true;
if (sk)
return sk;
*refcounted = false;
return __inet_lookup_listener(net, hashinfo, skb, doff, saddr,
sport, daddr, hnum, dif, sdif);
}

到TCP层后,skb需要转换成 struct sock,从 kernel 协议栈查找skb -> struct sk 映射

阅读全文 »

目标:

分析 kernel 驱动收到后skb

1. 如何唤醒epoll callback?
2. accpet syscall 如何从listener socket spawn 子 socket?

一个极简epoll server程序(无法编译,只说明流程)

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
// 创建listener socket
int listener_fd = socket();
bind(listener_fd);
listen(listener_fd);
set_socket_non_blocking(listener_fd);

// 创建socket
ep = epoll_create1(0);
struct epoll_event event;
event.data.fd = listener_fd;
event.events = EPOLLIN | EPOLLET;
// 将listener socket注册进epoll
s = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);
for ( ;; ) {
// eventloop
int n = epoll_wait(ep, &events, 1024, -1);
for (itn i = 0; i < n ; i ++) {
if(events[i].data.fd == listener_fd) {
// new incoming connection
int child = accept(listener_fd, &addr, &addr_len);
// 将子连接socket加入epoll eventloop
make_socket_non_blocking(infd);
struct epoll_event;
event.data.fd = infd;
event.events = EPOLLIN | EPOLLET;
s = epoll_ctl(ep, EPOLL_CTL_ADD, child, &event);
}
}
}

epoll 注册 wake callback

阅读全文 »

以联发科 mt7921 无线网卡驱动为例

kernel mt7921/ 下有pci,usb接口的驱动,usb接口驱动入口是usb.c。手里有mt7921芯片的USB无线网卡,对应的驱动为mt7921u

pci驱动为 mt7921e
USB接口 MT7921 外观

PCI接口的MT7921网卡外观

阅读全文 »

WI-FI 如何传输数据

无线通讯通过电磁波完成,发送端通过电流转换成电磁场,电磁场产生无线电波在空间中传递。利用这现象,将信息调制到无线电波上发送。计算机领域中,信息就是 0/1,通过电磁波的带宽表示(也可以用电磁波其他的概念组合表示,不一定是是带宽)

接收端利用电磁感应原理,通过解调还原成最初的数据,所以 WI-FI 设备都带天线,天线

数据在传输中,发送器会在天线上施加电流,施加的时变电压或时变电流而产生辐射的电磁场,使得电流的能量转变成无线电波。在接收时,天线会由于电场的感应,而在天线内部产生时变电流,并在其终端产生时变电压,产生电讯号经过处理之后,可以在接收器中观察或收听。天线被广泛应用于广播、点对点无线电通讯、雷达和太空探索等通讯系统。天线是无线电通讯系统中的必需组件。

维基百科对无线电通讯有个大体介绍

阅读全文 »

驱动层

驱动向内核注册 softirq,里面包含回调函数。驱动收到数据触发中断,kernel读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__netif_receive_skb_list_ptype(struct net_device * orig_dev, struct packet_type * pt_prev, struct list_head * head) (/data00/codes/linux/net/core/dev.c:5533)
__netif_receive_skb_list_core(struct list_head * head, bool pfmemalloc) (/data00/codes/linux/net/core/dev.c:5582)
__netif_receive_skb_list(struct list_head * head) (/data00/codes/linux/net/core/dev.c:5634)
netif_receive_skb_list_internal(struct list_head * head) (/data00/codes/linux/net/core/dev.c:5725)
gro_normal_list(struct napi_struct * napi) (/data00/codes/linux/include/net/gro.h:433)
gro_normal_list(struct napi_struct * napi) (/data00/codes/linux/include/net/gro.h:429)
napi_complete_done(struct napi_struct * n, int work_done) (/data00/codes/linux/net/core/dev.c:6065)
e1000_clean(struct napi_struct * napi, int budget) (/data00/codes/linux/drivers/net/ethernet/intel/e1000/e1000_main.c:3811)
__napi_poll(struct napi_struct * n, bool * repoll) (/data00/codes/linux/net/core/dev.c:6496)
napi_poll(struct list_head * repoll, struct napi_struct * n) (/data00/codes/linux/net/core/dev.c:6563)
net_rx_action(struct softirq_action * h) (/data00/codes/linux/net/core/dev.c:6696)
__do_softirq() (/data00/codes/linux/kernel/softirq.c:571)
do_softirq() (/data00/codes/linux/kernel/softirq.c:472)
do_softirq() (/data00/codes/linux/kernel/softirq.c:459)

NAPI

基于已有thread_struct封装的新的任务调度库

阅读全文 »

工作中涉及Nginx module开发,分析Nginx/tengine代码

nginx modules初始化

每个 module 必须有 ngx_module_t,且在module不同生命周期都支持 hook 函数

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
struct ngx_module_s {
ngx_uint_t ctx_index;
ngx_uint_t index;

char *name;

ngx_uint_t spare0;
ngx_uint_t spare1;

ngx_uint_t version;
const char *signature;

void *ctx;
ngx_command_t *commands;
ngx_uint_t type;

ngx_int_t (*init_master)(ngx_log_t *log);

ngx_int_t (*init_module)(ngx_cycle_t *cycle);

ngx_int_t (*init_process)(ngx_cycle_t *cycle);
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t *cycle);
void (*exit_process)(ngx_cycle_t *cycle);

void (*exit_master)(ngx_cycle_t *cycle);

uintptr_t spare_hook0;
uintptr_t spare_hook1;
uintptr_t spare_hook2;
uintptr_t spare_hook3;
uintptr_t spare_hook4;
uintptr_t spare_hook5;
uintptr_t spare_hook6;
uintptr_t spare_hook7;
};

由于 ngx_module_t 为基础类型,对于http和stream,通过 ngx_module_t#ctx进行了扩展,用于放置 http/stream module相关的 hook 函数

阅读全文 »

shadowsocks 协议分析

基础知识

nonce 和 salt 区别

salt 只用一次,用于生成 cipher,和 salt 类似的概念叫initial vector(初始化向量),可认为 salt 和 iv 是等价的概念。

nonce 每次 encrypt,decrypt都需要,而且 decrypt 时使用的nonce必须和那次encrypt的nonce一致。由于加解密的nonce必须一一对应,所以nonce往往采用双方提前约定的生成规则,一般都是每次使用完自加1,为下次使用做准备

阅读全文 »

从tun读出来后再写入tun,下次读还会将自己刚写入的packet读出来,如果设置默认路由是tun网卡,会导致死循环。下文会介绍解决routing loop的多种方法

https://www.kernel.org/doc/Documentation/networking/tuntap.txt

How does Virtual network device actually work ?
Virtual network device can be viewed as a simple Point-to-Point or
Ethernet device, which instead of receiving packets from a physical
media, receives them from user space program and instead of sending
packets via physical media sends them to the user space program.

Let’s say that you configured IPv6 on the tap0, then whenever
the kernel sends an IPv6 packet to tap0, it is passed to the application
(VTun for example). The application encrypts, compresses and sends it to
the other side over TCP or UDP. The application on the other side decompresses
and decrypts the data received and writes the packet to the TAP device,
the kernel handles the packet like it came from real physical device.

一共两个方法, read 和 write

read from tun读取数据包

阅读全文 »