book: Understanding Linux Network Internals
 socket读写错误返回值:errno
 TCP: Robert Elliot Kahn
 IP: Robert Elliot Kahn, Vint Cerf
 1 RFC规范
 RFC793:TCP
 RFC768:UDP
 RFC791:IP
 RFC826:ARP
 RFC792:ICMP
 RFC5681:TCP拥塞
 RFC6298:TCP重传
 RFC2131:DHCP
 RFC1112和RFC2365:IP组播
 RFC4862:IPv6无状态地址自动配置
 RFC2460:IPv6规范
 2 TCP/IP二层协议
 2.1 ARP超时时间设置
 /proc/sys/net/ipv4/neigh/ethX/base_reachable_time_ms
 [timeout/2, 3*timeout/2] (default from 15s to 45s)
 LwIP etharp_tmr(void)
 Linux static ARP
 arp -s 10.0.0.2 00:0c:29:c0:94:bf
 The above commands tells local ARP table that the host with IP address 10.0.0.2 has MAC address 00:0c:29:c0:94:bf. Once you have configured a static ARP entry, you can verify that.
 arp -a -n
 2.2 TUN and TAP
 TUN/TAP:TUNnel / Network Terminal Access Point
 TAP运行在链路层,主要用于虚拟机虚拟ethernet网络。
 TUN运行在L3 IP层,TUN与IPSec配合,实现IP加密。对/dev/net/tun节点ioctl创建tun0网络,需要给tun0配置IP地址,如下所示。
 ifconfig tun0 192.168.1.2/24 up
 应用程序通过/dev/net/tun读取tun0发送的报文,经过处理后,通过物理网卡发送出去;应用程序从物理网卡接收到数据,经过处理后,再写入/dev/net/tun,使用tun0接口的应用程序就接收到了数据。
 2.3 Bridge
 struct net_device {
     [...]
     // 属于哪个网桥端口
     struct net_bridge_port *br_port;
     [...]
 };
 网桥设备端口(eth0、eth1,etc)的MAC被静态配置到FDB(Forwarding Data Base)中,is_local和is_static同时置1。
 - QNX hypervisor showcase
 1) 连接USB转以太网的RJ45网线到Windows,并且配置其静态IP地址,需要与Android虚拟eth0保持在同一个网段
 2) Android一端建立网桥
 3) attach Android虚拟eth0到网桥
 4) attach Android eth1 (USB转以太网) 到网桥
 5) 将Android eth0的IP地址配置给网桥br0,并配置br0的路由
 6) 在Windows上ping QNX端虚拟vp0
 1) Create network bridge
 # create bridge
 ip link add name br0 type bridge
 ip link set dev eth0 master br0
 ip link set dev eth1 master br0
 # ip link show master br0
 # ip link set eth1 promisc on
 # release virtual eth0 IP address
 ip link set dev eth0 down
 ip addr del 192.168.5.100/24 dev eth0
 ip link set dev eth0 up
 # bring up USBEthernet
 ip link set dev eth1 down
 ip addr add 0.0.0.0 dev eth1
 ip link set dev eth1 up
 # configure br0 ip address and route
 ip addr add 192.168.5.100 dev br0
 ip link set dev br0 up
 ip route add 192.168.5.0/24 via 192.168.5.100
 # Windows: ping 192.168.5.X -l 1024 -w 20000
 2) Delete network bridge
 # tear down bridge
 ip route del 192.168.5.0/24 via 192.168.5.100 dev br0
 ip link set dev eth0 down
 ip link set dev eth1 down
 ip link set dev br0 down
 # delete bridge
 ip link set dev eth0 nomaster
 ip link set dev eth1 nomaster
 ip link del br0
 # bring up virtual eth0 with IP address
 ip addr add 192.168.5.100/24 dev eth0
 ip link set dev eth0 up
 2.4 Switch VLAN Mode
 Tag-based VLANs are the industry standard 802.1Q VLANs (dot1q), while the port-based VLANs are more akin to private VLANs.
 trunk:VLAN ID多于一个的port口,基于Tag-based,使用IEEE 802.1Q VID
 access:untagged port,基于Tag-based,使用IEEE 802.1Q PVID,一个port最多有一个untagged的VLAN
 2.5 raw socket CONFIG_PACKET_MMAP
 https://www.kernel.org/doc/html/latest/networking/packet_mmap.html
 3 TCP协议细节
 3.1 TCP的6种标识
 SYN:SYNchronous,建立联机
 ACK:ACKnowledgement,确认
 PSH:PuSH,传送
 FIN:FINish,结束
 RST:ReSeT,重置
 URG:URGent,紧急
 SEQ:SEQuence number,顺序号码
 3.2 TCP接收数据流程
 net/ipv4/tcp_ipv4.c
 tcp_v4_do_rcv()
 net/ipv4/tcp_input.c
 tcp_rcv_established()
 tcp_validate_incoming()
 TCP fast path只能处理ACK和PSH;如果TCP flag中有其它标志的必须走slow path,譬如有RST标志的一定走slow path
 3.3 TCP ACK机制
 TCP延时确认时间通常为40毫秒(#define TCP_DELACK_MIN ((unsigned)(HZ/25)))
 icsk->icsk_ack.pingpong == 0,表示使用快速确认。
 icsk->icsk_ack.pingpong == 1,表示使用延迟确认。
 3.4 tcp_mmap
 TCP: A subsequent read from the socket will block until SO_RCVLOWAT (Receive Low Water Mark) bytes are available, the default value is 1 byte.
 https://lwn.net/Articles/752207/
 UDP: recvfrom() receives one datagram at a time. If you want to receive more than one at a time, use recvmsg().
 UDP isn't a stream protocol, once you do the initial recvfrom, the remainder of the packet is discarded. The second recvfrom is awaiting the next packet.
 4 TCP 3次握手
 4.1 Wireshark抓包分析
 Wireshark抓包时,3次握手显示的是相对序列号/确认号。如果想要关闭相对序列号/确认号,可以选择Wireshark菜单栏中的 Edit -> Preferences ->protocols ->TCP,去掉Relative sequence number后面勾选框中的√即可。
 原始的Seq和ACK的值都很大,为便于理解,使用相对值;有点类似于真值表。
 Seq, ACK
 0, 0
 0, 1
 1, 1
 3次握手Wireshark过滤规则:tcp.flags.syn==1 or tcp.flags.ack==0
 4.2 Linux内核3次握手和接受数据状态机
 Figure 4-1 SYN Queue and Accept Queue in Server side
 net/ipv4/tcp_input.c
 服务端 - tcp_rcv_state_process()
 客户端 - tcp_rcv_synsent_state_process()
 TCP server端完成最终的3次握手 - 一直处于TCP_LISTEN状态监听连接
 in net/ipv4/tcp_minisocks.c
 三次握手成功后,tcp_check_req()调用syn_recv_sock() -> tcp_v4_syn_recv_sock(),创建一个新的socket给accept()函数返回。
 int tcp_child_process(struct sock *parent, struct sock *child, struct sk_buff *skb)
 4.3 tcp_timestamps and tcp_tw_recycle
 man tcp
 TCP时间戳位于TCP选项中,总共10个字节,kind=8,lenth=10,info字段由timestamp和timestamp echo两个值组成,各4个字节的长度。
 tcp_tw_recycle只用在服务端,而tcp_timestamps同时用在服务和客户端,当打开了tcp_timestamps,那么对应机器的TCP报文中就包含时间戳。
 服务器tcp_v4_conn_request()
 tm->tcpm_ts_stamp记录的是之前收到FIN包时服务器的时间戳
 tm->tcpm_ts记录的是FIN包中的时间戳
 req->ts_recent表示当前SYN包中的时间戳
 因此 (u32)get_seconds() - tm->tcpm_ts_stamp < TCP_PAWS_MSL (60s) 表示缓存还未过期,(s32)(tm->tcpm_ts - req->ts_recent) > TCP_PAWS_WINDOW (1s) 表示当前SYN包中的时间戳比之前FIN包中的时间戳还小1s(换句话说,时光倒流了),因此认为当前SYN包是之前重传的,直接丢弃。
 例子:Carplay server端打开tcp_tw_recycle(socket方式可以设置SO_LINGER = 0),有可能会导致server端因为时间戳异常直接丢弃client发过来的SYN报文 - tcp_peer_is_proven(req, dst, true),导致client连接超时,可以使用命令netstat -s确认。特别是有多个client位于路由器NAT之后,虽然有多个client连接服务器,因为路由器要做SNAT转换,所以server只看到一个源IP地址,由于不同client机器的时间不同步,一个client的断开,可能会导致其它client无法连接,实际是不同client发送过来的SYN报文,因此server端一般不要使能tcp_tw_recycle选项。
 Linux系统的默认设置
 tcp_timestamps默认开启。tcp_timestamps是RFC1323定义的优化选项,主要用于TCP连接中 RTT(Round Trip Time)的计算,开启tcp_timestamps有利于系统计算更加准确的RTT,也就有利于TCP性能的提升。tcp_tw_recycle默认关闭。
 如果客户端的tcp_timestamps没有开启,那么TCP报文中(当然也包括SYN报文)不会包含时间戳,根据上述分析,服务端即使打开tcp_tw_recycle也不起作用。
 4.4 TCP 3次握手SYN重试次数设置
 2分7秒:
 第1次发送SYN报文后等待1s(2的0次幂),如果超时,则重试;
 第2次发送后等待2s(2的1次幂),如果超时,则重试;
 第3次发送后等待4s(2的2次幂),如果超时,则重试;
 第4次发送后等待8s(2的3次幂),如果超时,则重试;
 第5次发送后等待16s(2的4次幂),如果超时,则重试;
 第6次发送后等待32s(2的5次幂),如果超时,则重试;
 第7次发送后等待64s(2的6次幂),如果超时,则超时失败;
 上面的结果刚好是127秒。也就是说Linux内核在尝试建立TCP连接时,最多会尝试7次,超时则返回错误Connection timed out。
 echo 3 > /proc/sys/net/ipv4/tcp_syn_retries
 4.5 TCP滑动窗口
 由于TCP的头部窗口字段只有16 bit,最多表示64k,为了表示更大的窗口,使用了可选的放大倍数。
 1)在TCP三次握手的时候在SYN或SYN-ACK包中,通知options可选信息,告知对方将使用放大倍数
 2)SYN本身不放大
 3)Window size value表示报文的值,Calculated window size表示放大后的值,也就是实际可用的值;Window size scaling factor表示放大倍数
 cat /proc/sys/net/ipv4/tcp_window_scaling
 4)在途字节数:on the line,是站在发送者的角度,表示的概念是,我已经发了多少,减去对方最近的一次确认,确认了多少,也就是Seq + Len - Ack (最近的一次Ack)。
 如果在途字节数等于对方的接收窗口,这个时候Wireshark打上TCP window Full标记,表示我不能再发送数据了。
 5)showcase
 #include <linux/tcp.h>
 struct tcp_info ti;
 socklen_t tisize = sizeof(ti);
 getsockopt(fd, IPPROTO_TCP, TCP_INFO, &ti, &tisize);
 // ti.tcpi_rcv_space contains the advertised tcp receive
 // Since Linux 4.8, read or set the send and receive window directly.
 struct tcp_repair_window trw;
 socklen_t trwsize = sizeof(trw);
 getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &trwsize);
 4.6 TCP SYN fail
 [9th-Apr-2022]
 4.6.1 tcp_timestamps and tcp_tw_recycle
 tcp_tw_reuse is only for tcp client.
 tcp_timestamps is for both client and server, enabled by default in Linux.
 tcp_tw_recycle is only for tcp server, never enable it, otherwise CarPlay fails three-way tcp handshake in latest iOS.
 Refer to 4.3 for more info.
 4.6.2 iptables
 iptables -A OUTPUT -o <NODE> -p tcp -m tcp --syn -j ACCEPT
 This configuration is for HiCar.
 5 TCP 4次挥手
 5.1 工作原理
 @ net/ipv4/tcp.c
 tcp_close() - 如果接收缓冲区中还有数据,协议栈就会发送RST而不是FIN
 4次握手Wireshark过滤规则:tcp.flags.fin == 1
 5.2 MSL
 MSL就是maximum segment lifetime(最大分段生存时间),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间,IP数据包将在网络中消失(IP报文头中的TTL减到0),MSL在RFC 1122上建议是2分钟,而源自berkeley的TCP实现传统上使用30秒。
 遇到特殊问题时,也可以直接修改内核的宏TCP_TIMEWAIT_LEN。
 Figure 5-1 4-way handshake
 5.3 showcase
 # FIN_WAIT1
 # if this value is equal to 0, kernel chooses 8
 echo 2 > /proc/sys/net/ipv4/tcp_orphan_retries
 # TCP_FIN_TIMEOUT
 echo 3 > /proc/sys/net/ipv4/tcp_fin_timeout
 # TCP_TIMEWAIT_LEN,
 # this may cause TCP SYN issue
 echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
 6 FIB
 6.1 路由表
 1)Android支持255个路由表
 Linux内核支持策略表的数目是2的15次方,范围是[0, 32767],而内核支持的路由表范围是[0, 255]。
 ip route list table [ID]
 0 - 显示全部表
 253 - default
 254 - main
 255 - local
 2)Android默认gateway
 Android Framework激活一个网络后,会用网络的名字(wlan0或者eth0)创建一个ip rule(kernel函数fib_nl_newrule)作为default gateway,优先级等于22000(RULE_PRIORITY_DEFAULT_NETWORK,参考netd),当目的IP不是局域网地址,并且对下面的三个表做LPM后,仍旧不匹配,会使用这个表(kernel函数fib_select_default)。
 ip route list table default
 ip route list table main
 ip route list table local
 可以用如下的方式查看优先级是22000的那个ip rule。
 ip route list table eth0
 ip route list table wlan0
 3)Route Flags
 route -n:
 U: Route is up
 H: Target is a host
 G: Use gateway
 R: Reinstate route for dynamic routing
 D: Dynamically installed by daemon or redirect
 M: Modified from routing daemon or redirect
 A: Installed by addrconf
 !: Reject route
 6.2 fib_lookup
 1)路由查找使用的算法是LPM(最长匹配,Longest Prefix Match)。匹配的网络掩码越长,说明路由越准确。
 比如,192.168.0.0/16和192.168.0.0/24这两个网络掩码,根据LPM算法,192.168.0.0/24将首先进行匹配,匹配不符,才匹配192.168.0.0/16。
 2)fib_select_default,多网卡负载均衡文件节点/proc/net/bonding/bond0。
 route:基于ioctl
 ip route:基于netlink
 6.3 ip rule解读
 CONFIG_IP_MULTIPLE_TABLES
 busybox route -n
 ip route list table main或者ip route
 ip route list table 1
 ip rule
 6.4 默认网关的作用
 syntax:
 ip route add {NETWORK/MASK} via {GATEWAYIP}
 ip route add {NETWORK/MASK} dev {DEVICE}
 ip route add default {NETWORK/MASK} dev {DEVICE}
 ip route add default {NETWORK/MASK} via {GATEWAYIP}
 ip route add default via 192.168.0.1 dev eth0 table 1
 # ip rule add from all lookup main pref 9999
 # 上一条命令等价于在netd中添加modifyIpRule(RTM_NEWRULE, 9999, 254, 0, 0);
 ip rule add from 192.168.0.0/24 table 1
 ip rule add to 192.168.0.0/24 table 1
 table 1中包含了eth0默认网关,而table main中包含了正常的eth0路由。
 6.5 获取网络接口的IP地址
 ret = ioctl(fd, SIOCGIFADDR, &ifr);
 1) /sys/class/net/<ifname> not exists, errno = ENODEV
 2) /sys/class/net/<ifname> exists and ip address not configured, errno = EADDRNOTAVAIL
 3) <ifname> ip address configured, ioctl return 0
 7 Linux sk_buff重要字段
 h:指向TCP/UDP头
 nh:指向IP头
 mac:指向MAC头,neigh_hh_output(),hh means hardware header
 head:指向缓冲区的头,head <= data
 data:指向缓冲区MAC头
 tail:指向payload尾部
 end:指向缓冲区的尾部,end >= tail
 TX: 邻居子系统,添加12字节MAC地址头
 RX: __netif_receive_skb()
 8 Security
 TLS
 IPsec: strongSwan
 MACsec: 2021 28nm 88Q5151, 88Q5152
 9 常用调试命令
 9.1 dropwatch
 CONFIG_NET_DROP_MONITOR=m
 echo 0 > /proc/sys/kernel/kptr_restrict
 libncurses.a (new curses) for Android
 https://github.com/CyanogenMod/android_external_libncurses
 libhistory.a and libreadline.a for Android
 https://github.com/LineageOS/android_external_bash
 libnl.a
 external/libnl
 libpcap.a
 external/libpcap
 dropwatch
 https://github.com/nhorman/dropwatch
 9.2 ethtool
 rxwen/ethtool
 https://github.com/rxwen/ethtool
 ethtool eth0
 netstat -apnt
 netstat -apnu
 9.3 nmap
 Network Mapper,Linux下的网络扫描和嗅探工具。
 9.4 ss
 ss -4tu0
 ss -tan state time-wait | wc -l
 ss:Socket Statistics,代替net-tools中的netstat。ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信息,而且比netstat更快速更高效。ss快的秘诀在于,它利用到了TCP协议栈中tcp_diag。tcp_diag是一个用于分析统计的模块,可以获得Linux内核中第一手的信息,这就确保了ss的快捷高效。
 9.5 tcpdump
 tcpdump -X -i eth0 -s 0 -C 20 -W 3 -w /data/eth_sniff.pcap
 9.6 traceroute
 Windows 等效命令是tracert
 9.7 TCP/UDP测试工具
 Linux TCP Server: busybox nc -lvz -s 192.168.0.10 -p 8001
 10 Abbreviations
 3Com: Computer, Communication, Compatibility, by Robert Metcalfe
 ndos: network device operations
 PFE: S32G Packet Forward Engine, route table offload
 trie: pronounced tree or try

















![[附源码]计算机毕业设计JAVAjsp闲置物品线上交易系统](https://img-blog.csdnimg.cn/3bb6a04cc9b1433cb514adb16dcd99b1.png)

