Android: badvpn中使用的LwIP的版本及tun2socks工作原理

badvpn中tun2socks工具如何读取tun设备中的packet并将其通过socks5转发的呢? 我们知道在badvpn中使用到了LwIP, tun设备中的packet首先由LwIP处理,将TCP packet解析出来再进行转发?

  • 关于badvpn的相关应用

请看这里:

Android: VpnService与badvpn – tun2socks & udpgw

  • 关于LwIP

项目主页请看这里 :https://savannah.nongnu.org/projects/lwip/

这是它的相关介绍:

lwIP is a small independent implementation of the TCP/IP protocol suite that has been initially developed by Adam Dunkels and is now continued here.

The focus of the lwIP TCP/IP implementation is to reduce resource usage while still having a full scale TCP. This makes lwIP suitable for use in embedded systems with tens of kilobytes of free RAM and room for around 40 kilobytes of code ROM.

Main features include:

– Protocols: IP, ICMP, UDP, TCP, IGMP, ARP, PPPoS, PPPoE

– DHCP client, DNS client, AutoIP/APIPA (Zeroconf), SNMP agent (private MIB support)

– APIs: specialized APIs for enhanced performance, optional Berkeley-alike socket API

– Extended features: IP forwarding over multiple network interfaces, TCP congestion control, RTT estimation and fast recovery/fast retransmit

– Addon applications: HTTP server, SNTP client, SMTP client, ping, NetBIOS nameserver

源代码可以从这里下载:https://savannah.nongnu.org/git/?group=lwip

  • 使用的LwIP与upstream的差异

这里我们先确定一下badvpn的版本:

commit ffd16e27d0bd58fec068fa9271b33fe559efb5a5
Author: Ambroz Bizjak <ambrop7@gmail.com>
Date:   Sun Mar 12 12:02:02 2017 +0100

    Fix bug UDP checksum calculation.
    
    Check for zero result should be done after not before inverting.

而最后一次修改LwIP的时间是:

commit cedb690976a130b6721a4956c652bacfffaaeaae
Author: Ambroz Bizjak <ambrop7@gmail.com>
Date:   Sun Nov 16 17:13:25 2014 +0100

    lwip: Fix undefined behavior.

可以看到lwip好长时间没有更新了。

从LwIP相关的log及lwip/CHANGELOG文件可以找到与upstream最接近的版本为:

commit 9809f1ff6600466da3f96fb304791201c54b34fc
Author: Simon Goldschmidt <goldsimon@gmx.de>
Date:   Wed Apr 24 21:38:01 2013 +0200

    Fixed bug #38586

通过diff命令我们可以看到badvpn修改了哪些代码:

diff -E -b -B -w -r badvpn/lwip/src/core/ipv4/ip4.c lwip/src/core/ipv4/ip4.c
476,481d475
<   /* if we're pretending we are everyone for TCP, assume the packet is for source interface if it
<      isn't for a local address */
<   if (netif == NULL && (inp->flags & NETIF_FLAG_PRETEND_TCP) && IPH_PROTO(iphdr) == IP_PROTO_TCP) {
<       netif = inp;
<   }
< 
diff -E -b -B -w -r badvpn/lwip/src/core/ipv6/ip6.c lwip/src/core/ipv6/ip6.c
481,486d480
<   /* if we're pretending we are everyone for TCP, assume the packet is for source interface if it
<      isn't for a local address */
<   if (netif == NULL && (inp->flags & NETIF_FLAG_PRETEND_TCP) && IP6H_NEXTH(ip6hdr) == IP6_NEXTH_TCP) {
<       netif = inp;
<   }
< 
diff -E -b -B -w -r badvpn/lwip/src/core/ipv6/nd6.c lwip/src/core/ipv6/nd6.c
624,627c624
<   s8_t i;
< #if LWIP_IPV6_AUTOCONFIG
<   s8_t j;
< #endif
---
>   s8_t i, j;
1403d1399
< #if LWIP_IPV6_AUTOCONFIG
1405d1400
< #endif
diff -E -b -B -w -r badvpn/lwip/src/core/netif.c lwip/src/core/netif.c
352,358d351
< int netif_is_named (struct netif *netif, const char name[3])
< {
<     u8_t num = name[2] - '0';
<     
<     return (!memcmp(netif->name, name, 2) && netif->num == num);
< }
< 
445,453d437
< void netif_set_pretend_tcp (struct netif *netif, u8_t pretend)
< {
<     if (pretend) {
<         netif->flags |= NETIF_FLAG_PRETEND_TCP;
<     } else {
<         netif->flags &= ~NETIF_FLAG_PRETEND_TCP;
<     }
< }
< 
754c738
<   tcpip_callback((tcpip_callback_fn)netif_poll, netif);
---
>   tcpip_callback_with_block((tcpip_callback_fn)netif_poll, netif, 0);
861c845
<   u8_t i, addr_index, min_len;
---
>   u8_t i, addr_index;
884d867
<     min_len = netif->hwaddr_len < 8 ? netif->hwaddr_len : 8;
886c869
<     for (i = 0; i < min_len; i++) {
---
>     for (i = 0; i < 8; i++) {
diff -E -b -B -w -r badvpn/lwip/src/core/tcp.c lwip/src/core/tcp.c
212c212
<     if (pcb->local_port != 0 || pcb->bound_to_netif) {
---
>     if (pcb->local_port != 0) {
487d486
<   pcb->bound_to_netif = 0;
496,521d494
< 
< err_t
< tcp_bind_to_netif(struct tcp_pcb *pcb, const char ifname[3])
< {
<   LWIP_ERROR("tcp_bind_if: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
<   
<   /* Check if the interface is already in use */
<   for (int i = 0; i < NUM_TCP_PCB_LISTS; i++) {
<     for(struct tcp_pcb *cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
<       if (IP_PCB_IPVER_EQ(pcb, cpcb) &&
<           cpcb->bound_to_netif &&
<           !memcmp(cpcb->local_netif, ifname, sizeof(cpcb->local_netif))) {
<         return ERR_USE;
<       }
<     }
<   }
< 
<   pcb->bound_to_netif = 1;
<   ipX_addr_set_any(PCB_ISIPV6(pcb), &pcb->local_ip);
<   pcb->local_port = 0;
<   memcpy(pcb->local_netif, ifname, sizeof(pcb->local_netif));
<   TCP_REG(&tcp_bound_pcbs, pcb);
<   LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind_to_netif: bind to interface %c%c%c\n", ifname[0], ifname[1], ifname[2]));
<   return ERR_OK;
< }
< 
564c537
<   if (ip_get_option(pcb, SOF_REUSEADDR) && !pcb->have_local_netif) {
---
>   if (ip_get_option(pcb, SOF_REUSEADDR)) {
584d556
<   lpcb->bound_to_netif = pcb->bound_to_netif;
586d557
<   memcpy(lpcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif));
598c569
<   if (pcb->local_port != 0 || pcb->bound_to_netif) {
---
>   if (pcb->local_port != 0) {
754d724
<   LWIP_ERROR("tcp_connect: cannot connect pcb bound to netif", !pcb->bound_to_netif, return ERR_VAL);
1569,1570c1539
<         if ((!lpcb->bound_to_netif && !pcb->bound_to_netif &&
<              (lpcb->local_port == pcb->local_port) &&
---
>         if ((lpcb->local_port == pcb->local_port) &&
1573,1575c1542
<              ipX_addr_cmp(PCB_ISIPV6(lpcb), &pcb->local_ip, &lpcb->local_ip))) ||
<             (lpcb->bound_to_netif && pcb->bound_to_netif &&
<              !memcmp(lpcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif)))) {
---
>              ipX_addr_cmp(PCB_ISIPV6(lpcb), &pcb->local_ip, &lpcb->local_ip))) {
diff -E -b -B -w -r badvpn/lwip/src/core/tcp_in.c lwip/src/core/tcp_in.c
222,223d221
<     struct tcp_pcb_listen *netif_pcb = NULL;
<     struct tcp_pcb *netif_pcb_prev = NULL;
225,231c223
<       if (lpcb->bound_to_netif) {
<         if (IP_PCB_IPVER_INPUT_MATCH(lpcb) && netif_is_named(inp, lpcb->local_netif)) {
<           netif_pcb = lpcb;
<           netif_pcb_prev = prev;
<         }
<       }
<       else if (lpcb->local_port == tcphdr->dest) {
---
>       if (lpcb->local_port == tcphdr->dest) {
268,271d259
<     if (lpcb == NULL && netif_pcb) {
<         lpcb = netif_pcb;
<         prev = netif_pcb_prev;
<     }
510,512c498
<     npcb->bound_to_netif = pcb->bound_to_netif;
<     npcb->local_port = tcphdr->dest;
<     memcpy(npcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif));
---
>     npcb->local_port = pcb->local_port;
diff -E -b -B -w -r badvpn/lwip/src/include/lwip/netif.h lwip/src/include/lwip/netif.h
100,102d99
< /** Whether to pretend that we are every host for TCP packets.
<  * Set by netif_set_pretend_tcp. */
< #define NETIF_FLAG_PRETEND_TCP  0x100U
241c238
<   u16_t flags;
---
>   u8_t flags;
327,328d323
< int netif_is_named (struct netif *netif, const char name[3]);
< 
334d328
< void netif_set_pretend_tcp(struct netif *netif, u8_t pretend);
diff -E -b -B -w -r badvpn/lwip/src/include/lwip/tcp.h lwip/src/include/lwip/tcp.h
166,168c166
<   int bound_to_netif; \
<   u16_t local_port; \
<   char local_netif[3]
---
>   u16_t local_port
238c236
<   u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */
---
>   u16_t snd_queuelen; /* Available buffer space for sending (in pbufs). */
351d348
< err_t            tcp_bind_to_netif (struct tcp_pcb *pcb, const char ifname[3]);

从这里我们可以看到badvpn修改了这些文件:

badvpn/lwip/src/core/ipv4/ip4.c
badvpn/lwip/src/core/ipv6/ip6.c
badvpn/lwip/src/core/ipv6/nd6.c
badvpn/lwip/src/core/netif.c
badvpn/lwip/src/core/tcp.c
badvpn/lwip/src/core/tcp_in.c
badvpn/lwip/src/core/tcp_out.c
badvpn/lwip/src/include/lwip/netif.h
badvpn/lwip/src/include/lwip/tcp.h

总的来说是为了获取TCP packet并对其进行处理。

  • 关于TUN设备

文档:https://en.wikipedia.org/wiki/TUN/TAP

TUN (namely network TUNnel) simulates a network layer device and it operates with layer 3 packets like IP packets. TAP (namely network tap) simulates a link layer device and it operates with layer 2 packets like Ethernet frames. TUN is used with routing, while TAP is used for creating a network bridge.

Packets sent by an operating system via a TUN/TAP device are delivered to a user-space program which attaches itself to the device. A user-space program may also pass packets into a TUN/TAP device. In this case the TUN/TAP device delivers (or “injects”) these packets to the operating-system network stack thus emulating their reception from an external source.

  • 关于TCP协议

看图,看原文

2017_03_21_Tcp_state_diagram_fixed_new.svg

(图片来自https://en.wikipedia.org/wiki/Transmission_Control_Protocol)

  • 工作原理

执行如下命令

badvpn-tun2socks --netif-ipaddr 10.0.0.1 --netif-netmask 255.255.255.0 \
    --socks-server-addr 127.0.0.1:1080 \
    --tunfd 66 --tunmtu 1500 --loglevel 5 --udpgw-remote-server-addr 192.168.1.6:8700

1. 通过lwip创建虚拟设备ho0

INFO(lwip): netif_set_ipaddr: netif address being changed
INFO(lwip): netif: IP address of interface
INFO(lwip): netif: netmask of interface
INFO(lwip): netif: GW address of interface
DEBUG(tun2socks): netif func init
INFO(lwip): netif: added interface ho IP addr
INFO(lwip): 10.0.0.1
INFO(lwip):  netmask
INFO(lwip): 255.255.255.0
INFO(lwip):  gw
INFO(lwip): 0.0.0.0
INFO(lwip):
INFO(lwip): netif: setting default interface ho
INFO(lwip): tcp_bind_to_netif: bind to interface ho0

2. 读取packet

00000000: 45 00 00 3c 5c c7 40 00 40 06 6e 6c 0a 00 00 02  E..<\.@.@.nl....
00000010: 5d 2e 08 59 8a 60 01 bb fd de 46 95 00 00 00 00  ]..Y.`....F.....
00000020: a0 02 ff ff ed 96 00 00 02 04 05 b4 04 02 08 0a  ................
00000030: 00 00 1a 52 00 00 00 00 01 03 03 06              ...R........

3. 解析packet

INFO(lwip): ip_input: iphdr->dest 0x59082e5d netif->ip_addr 0x100000a (0x82e5d, 0xa, 0x59000000)
INFO(lwip): ip_input: iphdr->dest 93.46.8.89 netif->ip_addr 10.0.0.1 mask 255.255.255.0 (93.46.8.0, 10.0.0.0, 0.0.0.89)
INFO(lwip): ip_input:
INFO(lwip): IP header:
INFO(lwip): +-------------------------------+
INFO(lwip): | 4 | 5 |  0x00 |        60     | (v, hl, tos, len)
INFO(lwip): +-------------------------------+
INFO(lwip): |    23751      |010|       0   | (id, flags, offset)
INFO(lwip): +-------------------------------+
INFO(lwip): |   64  |    6  |    0x6e6c     | (ttl, proto, chksum)
INFO(lwip): +-------------------------------+
INFO(lwip): |   10  |    0  |    0  |    2  | (src)
INFO(lwip): +-------------------------------+
INFO(lwip): |   93  |   46  |    8  |   89  | (dest)
INFO(lwip): +-------------------------------+
INFO(lwip): ip_input: p->len 60 p->tot_len 60
INFO(lwip): TCP header:
INFO(lwip): +-------------------------------+
INFO(lwip): |    35424      |      443      | (src port, dest port)
INFO(lwip): +-------------------------------+
INFO(lwip): |           4259202709          | (seq no)
INFO(lwip): +-------------------------------+
INFO(lwip): |           0000000000          | (ack no)
INFO(lwip): +-------------------------------+
INFO(lwip): | 10 |   |000010|     65535     | (hdrlen, flags (
INFO(lwip): SYN
INFO(lwip):
INFO(lwip): ), win)
INFO(lwip): +-------------------------------+
INFO(lwip): |    0xed96     |         0     | (chksum, urgp)
INFO(lwip): +-------------------------------+
INFO(lwip): inet_chksum_pseudo(): checksumming pbuf 0xb6c98800 (has next 0x0)
INFO(lwip): inet_chksum_pseudo(): pbuf chain lwip_chksum()=ffff
INFO(lwip): tcp_input: packed for LISTENing connection.
INFO(lwip): TCP connection request 35424 -> 443.
INFO(lwip): tcp_parseopt: MSS
INFO(lwip): tcp_parseopt: other
INFO(lwip): tcp_parseopt: other
INFO(lwip): tcp_parseopt: NOP
INFO(lwip): tcp_parseopt: other
INFO(lwip): tcp_enqueue_flags: queuelen: 0
INFO(lwip): tcp_enqueue_flags: queueing 6510:6511 (0x12)
INFO(lwip): tcp_enqueue_flags: 1 (after enqueued)
INFO(lwip): tcp_output: snd_wnd 65535, cwnd 1, wnd 1, effwnd 0, seq 6510, ack 6510
INFO(lwip): tcp_output: snd_wnd 65535, cwnd 1, wnd 1, effwnd 0, seq 6510, ack 6510, i 0
INFO(lwip): tcp_output_segment: rtseq 6510
INFO(lwip): tcp_output_segment: 6510:6510
INFO(lwip): inet_chksum_pseudo(): checksumming pbuf 0xb6ca2000 (has next 0x0)
INFO(lwip): inet_chksum_pseudo(): pbuf chain lwip_chksum()=40d8
INFO(lwip): ip_output_if: ho0
INFO(lwip): IP header:
INFO(lwip): +-------------------------------+
INFO(lwip): | 4 | 5 |  0x00 |        44     | (v, hl, tos, len)
INFO(lwip): +-------------------------------+
INFO(lwip): |        0      |000|       0   | (id, flags, offset)
INFO(lwip): +-------------------------------+
INFO(lwip): |  255  |    6  |    0x4c43     | (ttl, proto, chksum)
INFO(lwip): +-------------------------------+
INFO(lwip): |   93  |   46  |    8  |   89  | (src)
INFO(lwip): +-------------------------------+
INFO(lwip): |   10  |    0  |    0  |    2  | (dest)
INFO(lwip): +-------------------------------+

4. 写入数据

INFO(lwip): netif->output()
00000000: 45 00 00 2c 00 00 00 00 ff 06 4c 43 5d 2e 08 59  E..,......LC]..Y
00000010: 0a 00 00 02 01 bb 8a 60 00 00 19 6e fd de 46 96  .......`...n..F.
00000020: 60 12 16 d0 27 bf 00 00 02 04 05 b4              `...'.......

还没看懂,待续。。。

  • 实现一个简单的tun2socks

待续。。。

  • 相关的参考文档
  1. https://savannah.nongnu.org/projects/lwip/
  2. https://github.com/ambrop72/badvpn
  3. https://en.wikipedia.org/wiki/Transmission_Control_Protocol
  4. https://en.wikipedia.org/wiki/TUN/TAP

《Android: badvpn中使用的LwIP的版本及tun2socks工作原理》有7个想法

  1. 你好,我正在开发一个android上的https 代理(http tunnel)看到你的blog 有这方面的文章,想从你这砍点经验值。 求线上联络方式=)

  2. 这种东西,用普通SOCKS5,还可以实现局部代理,不用路由表。
    参考8u18.com有例程。

发表评论

电子邮件地址不会被公开。 必填项已用*标注