0%

本文是对 validate tag not working in ShouldBindWith 的回答。

有人提了这么一个问题:

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
package main

import (
"log"
"net/http"

"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)

// MyStruct ..
type MyStruct struct {
Order string `form:"order,default=DESC" validate:"omitempty,oneof=ASC DESC"`
}

func startPage(c *gin.Context) {
var person MyStruct
if c.ShouldBindWith(&person, binding.Query) == nil {
log.Println("====== Only Bind By Query String ======")
log.Println(person.Order)
c.String(http.StatusOK, "Success")
return
// log.Println(person.Address)
}
c.String(http.StatusBadRequest, "Failed")
return
}

func main() {

route := gin.Default()
route.Any("/testing", startPage)
route.Run(":8085")
}

然后表单验证的时候,明明 Order 传递了一个不符合 oneof=ASC DESC 的值,但却验证通过了。

原因

虽然 gin 使用了 go-playground/validator 来作为表单验证的组件,但是在 gin 里面却修改了 struct 验证的 tagname,我们可以看看 default_validator.go#L95

1
2
3
4
5
6
func (v *defaultValidator) lazyinit() {
v.once.Do(func() {
v.validate = validator.New()
v.validate.SetTagName("binding")
})
}

然后,我们就不能通过给 struct 加上 vaidate 的 tag 来写上我们的验证规则,取而代之的是,需要将我们的验证规则写在 binding 这个 tag 里面:

validate:"omitempty,oneof=ASC DESC" 替换成 binding:"omitempty,oneof=ASC DESC",我们即可实现在 gin 中使用 validator 验证我们的 struct。

1
2
3
type MyStruct struct {
Order string `form:"order,default=DESC" binding:"omitempty,oneof=ASC DESC"`
}

参考官方文档: 模型绑定和验证

tcpdump 是一个命令行的网络流量分析工具,功能非常强大,一般我们用来抓 TCP 的包。

非常重要:如果 tcpdump 没有输出,尝试加上 -i any 选项。

tcpdump 核心参数图解

tcpdump 参数的组成部分: 1. option 可选参数 2. proto 类过滤器:根据协议进行过滤,可识别的关键词有:tcp, udp 等 3. type 类过滤器:可识别的关键词有:host, net, port, portrange 4. direction 类过滤器:根据数据流向进行过滤,可识别的关键词有:src, dst,你可以使用逻辑运算符进行组合,如 src or dst

理解 tcpdump 的输出

输出内容结构

1
21:26:49.013621 IP 172.20.20.1.15605 > 172.20.20.2.5920: Flags [P.], seq 49:97, ack 106048, win 4723, length 48
  • 第一列:时分秒毫秒 21:26:49.013621
  • 第二列:网络协议 IP
  • 第三列:发送方的 ip地址+端口号,其中 172.20.20.1 是 ip,而 15605 是端口号
  • 第四列:箭头 >,表示数据流向
  • 第五列:接收方的 ip地址+端口号,其中 172.20.20.2 是 ip,而 5920 是端口号
  • 第六列:冒号
  • 第七列: 数据包内容,包括 Flags 标识符,seq 号,ack 号,win 窗口,数据长度,其中 [P.] 表示 PUSH 标志位为 1

Flags 标识符

  • [S]:SYN(开始连接)
  • [P]:PSH(推送数据)
  • [F]:FIN(结束连接)
  • [R]:RST(重置连接)
  • [.]:没有 Flag(意思是除上面四种类型外的其他情况,有可能是 ACK 也有可能是 URG)

常规过滤规则

基于 IP 地址过滤:host

使用 host 就可以指定 host ip 进行过滤:

1
tcpdump host 192.168.10.100

数据包的 ip 可以再细分为源 ip 和目标 ip 两种:

1
2
3
4
# 根据源 ip 进行过滤
tcpdump -i eth2 src 192.168.10.100
# 根据目标 ip 进行过滤
tdpcump -i eth2 dst 192.168.10.200

基于网段进行过滤:net

若你的 ip 范围是一个网段,可以直接这样指定:

1
tcpdump net 192.168.10.0/24

网段统一可以再细分为源网段和目标网段:

1
2
3
4
# 根据源网段进行过滤
tcpdump src net 192.168
# 根据目标网段进行过滤
tcpdump dst net 192.168

基于端口进行过滤:port

使用 port 就可以指定特定端口进行过滤:

1
tcpdump port 8888

端口同样可以再细分为源端口,目标端口:

1
2
3
4
# 根据源端口进行过滤
tcpdump src port 8888
# 根据目标端口进行过滤
tcpdump dst port 8888

如果你想同时指定两个端口你可以这样写:

1
tcpdump port 80 or port 8888

但也可以简写成这样:

1
tcpdump port 80 or 8888

如果你想抓取的不再是一两个端口,而是一个范围,一个一个指定就非常麻烦了,此时你可以这样指定一个端口段:

1
2
3
tcpdump portrange 8000-8080
tcpdump src portrange 8000-8080
tcpdump dst portrange 8000-8080

对于一些常见协议的默认端口,我们还可以直接使用协议名,而不用具体的端口号:

比如 http == 80, https == 443 等。

1
tcpdump tcp port http

基于协议进行过滤:proto

常见的网络协议有 tcp,udp,icmp,http,ip,ipv6 等。

若你只想查看 icmp 的包,可以直接这样写:

1
tcpdump icmp

protocol 可选值:ip,ip6,arp,rarp,atalk,aarp,decnet,sca,lat,mopdl,moprc,iso,stp,ipx,or netbeui

基于IP协议版本进行过滤

当你想查看 tcp 的包,你也许会这样子写:

1
tcpdump tcp

这样子写也没问题,就是不够精准,为什么这么说呢?

ip 根据版本的不同,可以再细分为 IPv4 和 IPv6 两种,如果你只指定了 tcp,这两种其实都会包含在内。

那有什么办法,能够将 IPv4 和 IPv6 区分开来呢?

很简单,如果是 IPv4 的 tcp 包,就这样写(数字 6 表示的是 tcp 在 ip 报文中的编号。)

1
2
3
4
tcpdump 'ip proto tcp'
tcpdump ip proto 6
tcpdump 'ip protochain tcp'
tcpdump ip protochain 6

而如果是 IPv6 的 tcp 包,就这样写:

1
2
3
4
tcpdump 'ip6 proto tcp'
tcpdump ip6 proto 6
tcpdump 'ip6 protochain tcp'
tcpdump ip6 prorochain 6

关于上面这几个命令示例,有两点需要注意:

  1. 跟在 proto 和 protochain 后面的如果是 tcp, udp, icmp ,那么过滤器需要用引号包含,这是因为 tcp,udp, icmp 是 tcpdump 的关键字。
  2. 跟在ip 和 ip6 关键字后面的 proto 和 protochain 是两个新面孔,看起来用法类似,它们是否等价,又有什么区别呢?

proto 后面跟的 的关键词是固定的,只能是 ip, ip6, arp, rarp, atalk, aarp, decnet, sca, lat, mopdl, moprc, iso, stp, ipx, or netbeui 这里面的其中一个。

而 protochain 后面跟的 protocol 要求就没有那么严格,它可以是任意词,只要 tcpdump 的 IP 报文头部里的 protocol 字段为 就能匹配上。

理论上来讲,下面两种写法效果是一样的

1
2
tcpdump 'ip && tcp'
tcpdump 'ip proto tcp'

同样的,这两种写法也是一样的

1
2
tcpdump 'ip6 && tcp'
tcpdump 'ip6 proto tcp'

可选参数解析

设置不解析域名提升速度

  • -n 不把 ip 转化成域名,直接显示 ip,避免执行 DNS lookups 的过程,速度会快很多。
  • -nn 不把协议和端口号转化成名字,速度也会快很多
  • -N 不打印出 host 的域名部分。比如,如果设置了此选项,tcpdump 将会打印 'nic' 而不是 'nic.ddn.mil'

过滤结果输出到文件

使用 tcpdump 工具抓到包后,往往需要再借助其他的工具进行分析,比如常见的 wireshark。

而要使用 wireshark,我们得将 tcpdump 抓到的包数据生成到文件中,最后再使用 wireshark 打开它即可。

使用 -w 参数后接一个以 .pcap 后缀命名的文件名,就可以将 tcpdump 抓到的数据保存到文件中。(可以使用 wireshark 分析这个文件,也可以使用 tcpdump 读取)

1
tcpdump icmp -w icmp.pcap

从文件中读取包数据

使用 -w 是写入数据到文件,而使用 -r 是从文件中读取数据。

读取后,我们照样可以使用上述的过滤器语法进行过滤分析。

1
tcpdump -r all.pcap

读取之后过滤:

1
tcpdump -r all.pcap src host 192.168.2.11

控制详细内容的输出

  • -v 产生详细的输出。比如包的 TTL,id 标识,数据包长度,以及 IP 包的一些选项。同时它还会打开一些附加的包完整性检测,比如对 IP 和 ICMP 包头部的校验和。
  • -vv 产生比-v更详细的输出. 比如NFS回应包中的附加域将会被打印, SMB数据包也会被完全解码。
  • -vvv 产生比-vv更详细的输出。比如 telent 时所使用的SB, SE 选项将会被打印, 如果telnet同时使用的是图形界面,其相应的图形选项将会以16进制的方式打印出来

控制时间的显示

  • -t 在每行的输出中不输出时间
  • -tt 在每行的输出中会输出时间戳
  • -ttt 输出每两行打印的时间间隔(以毫秒为单位)
  • -tttt 在每行打印的时间戳之前添加日期的打印(此种选项,输出的时间最为直观)

显示数据包的头部

  • -x 以 16 进制的形式打印每个包的头部数据(但不包括数据链路层的头部)
  • -xx 以 16 进制的形式打印每个包的头部数据(包括数据链路层的头部)
  • -X 以 16 进制和 ASCII 码形式打印出每个包的数据(但不包括连接层的头部),这在分析一些新协议的数据包很方便。
  • -XX 以 16 进制和 ASCII 码形式打印出每个包的数据(包括连接层的头部),这在分析一些新协议的数据包很方便

过滤指定网卡的数据包

  • -i 指定要过滤的网卡接口,如果要查看所有网卡,可以 -i any

过滤特定流向的数据包

  • -Q 选择是入方向还是出方向的数据包,可选项有:in,out,inout。也可以使用 --direction=[direction] 这种写法

其他常用的一些参数

  • -A 以 ASCII 码方式显示每一个数据包(不显示链路层头部信息)。在抓取包含网页数据的数据包时,可方便查看数据。
  • -l 基于行的输出,便于你保存查看,或者交给其他工具分析
  • -q 简洁地打印输出。即打印很少的协议相关的信息,从而输出行都比较简短
  • -c 捕获 count 个包 tcpdump 就退出
  • -s tcpdump 默认只会截取前 96 字节的内容,要想截取所有的报文内容,可以使用 -s numbernumber 就是你要截取的报文子节数,如果是 0 的话,表示截取报文全部内容。
  • -S 使用绝对序列号,而不是相对序列号
  • -C file-size,tcpdump 在把原始数据包直接保存到文件中之前,检查此文件大小是否超过 file-size。如果超过了,将关闭文件,另创一个文件继续用于原始数据包的记录。新创建的文件名与 -w 选项指定的文件名一致,但文件名后多了一个* 数字。该数字会从 1 开始随着新创建文件的增多而增加。file-size 的单位是百万子节(nt:这里指1,000,000个字节,并非1,048,576个字节, 后者是以1024字节为1k, 1024k字节为1M计算所得, 即1M=1024 * 1024 = 1,048,576)
  • -F 使用 file 文件作为过滤条件表达式的输入,此时命令行上的输入将被忽略。

对输出内容进行控制的参数

  • -D 显示所有可用的网络接口的列表(跟 netstat -i 类似)
  • -e 每行的打印输出中将包括数据包的数据链路层头部信息
  • -E 揭秘 IPSEC 数据
  • -L 列出指定网络接口所支持的数据链路层的类型后退出
  • -Z 后接用户名,在抓包时会受到权限的控制。如果以 root 用户启动 tcpdump,tcpdump 将会有超级用户权限
  • -d 打印出易读的包匹配码
  • -dd 以 C 语言的形式打印出包匹配码
  • -ddd 以十进制的形式打印出包匹配码

过滤规则组合

有编程基础的同学,对于下面三个逻辑运算符应该不陌生了吧。

  • and 所有的条件都要满足,也可以表示为 &&
  • or 只要有一个条件满足就可以,也可以表示为 ||
  • not 取反,也可以使用 |

举个例子,我想需要抓取一个来自 10.5.2.3,发往任意主机的 3389 端口的包。

1
tcpdump src 10.5.2.3 and dst port 3389

当你在使用多个过滤器进行组合时,有可能需要用到括号,而括号在 shell 中是特殊符号,因此你需要使用引号将其包含。例子如下:

1
tcpdump 'src 10.0.2.4 and (dst port 3389 or 22)'

而在单个过滤器里,常常会判断一条件是否成立,这时候,就要使用下面两个符号:

  • = 判断二者相等
  • == 判断二者相等
  • != 判断二者不相等

当你使用这两个符号时,tcpdump 还提供了一些关键字的接口来方便我们进行判断,比如:

  • if:表示网卡接口名
  • proc:表示进程名
  • pid:表示进程 id
  • svc:表示 service class
  • dir:表示方向,in 和 out
  • eproc:表示 effictive processes name
  • epid:表示 effictive process ID

比如我现在要过滤来自进程名为 nc 发出的流经 en0 网卡的数据包,或者不流经 en0 的入方向数据包,可以这样子写。

1
tcpdump "(if=en0 and proc=nc) || (if != en0 and dir = in)"

特殊过滤规则

根据 tcpflags 进行过滤

tcpdump 支持我们根据数据包的标志位进行过滤:

1
proto [ expr:size ]
  • proto 可以是熟知的协议之一(如ip,arp,udp,icmp,ipv6)
  • expr 可以是数值,也可以是一个表达式,表示与指定的协议头开始处的子节偏移量。
  • size 是可选的,表示从子节偏移量开始取的子节数量
  1. tcpflags 可以理解为一个别名常量,相当于 13,它代表着与指定的协议头开头相关的子节偏移量,也就是标志位,所以 tcp[tcpflags] 等价于 tcp[13]就是 tcp 报文的第 13 个字节。
  2. tcp-fin, tcp-syn, tcp-rst, tcp-push, tcp-ack, tcp-urg 这些同样可以理解为别名常量,分别代表 1,2,4,8,16,32,64。(就是 000001,000010 ... 这些二进制数)

由于数字不好记忆,所以一般使用这样的 “别名常量” 表示。

因此当下面这个表达式成立时,就代表这个包是一个 syn 包。

1
tcp[tcpflags] == tcp-syn

要抓取特定数据包,方法有很多种。

下面以最常见的 syn 包为例,演示一下如何用 tcpdump 抓取到 syn 包,而其他的类型的包也是同样的道理。

据我总结,主要有三种写法:

  1. 使用数字表示偏移量:
1
tcpdump -i eth0 "tcp[13] & 2 != 0"
  1. 使用别名常量表示偏移量:
1
tcpdump -i eth0 "tcp[tcpflags] & tcp-syn != 0"
  1. 使用混合写法
1
2
tcpdump -i eth0 "tcp[tcpflags] & 2 != 0"
tcpdump -i eth0 "tcp[13] & tcp-syn != 0"

如果我想同时捕获多种类型的包呢,比如 syn 或 ack 包

  1. 第一种写法
1
tcpdump -i eth0 'tcp[13] == 2 or tcp[13] == 16'
  1. 第二种写法
1
tcpdump -i eth0 'tcp[tcpflags] == tcp-syn or tcp[tcpflags] == tcp-ack'
  1. 第三种写法

捕获的是 tcp and ack

1
tcpdump -i eth0 "tcp[tcpflags] & (tcp-syn|tcp-ack) != 0"
  1. 第四种写法:注意这里是单个等号,而不是像上面一样两个等号,18 (syn+ack) = 2 (syn) + 16(ack)
1
2
tcpdump -i eth0 'tcp[13] = 18'
tcpdump -i eth0 'tcp[tcpflags] = 18'

tcp 中有类似 tcp-syn 的别名常量,其他协议也是有的,比如 icmp 协议,可以使用的别名常量有:

1
2
3
4
5
icmp-echoreply, icmp-unreach, icmp-sourcequench, 
icmp-redirect, icmp-echo, icmp-routeradvert,
icmp-routersolicit, icmp-timx-ceed, icmp-paramprob,
icmp-tstamp, icmp-tstampreply,icmp-ireq,
icmp-ireqreply, icmp-maskreq, icmp-maskreply

基于包大小进行过滤

若你想查看指定大小的数据包,也是可以的:

1
2
3
tcpdump less 32
tcpdump greater 64
tcpdump <= 128

根据 mac 地址进行过滤

例子如下,其中 ehost 是记录在 /etc/ethers 里的 name

1
2
3
tcpdump ether host [ehost]
tcpdump ether dst [ehost]
tcpdump ether src [ehost]

过滤通过指定网关的数据包

1
tcpdump gateway [host]

过滤广播/多播数据包

1
2
3
4
5
6
7
tcpdump ether broadcast
tcpdump ether multicast

tcpdump ip broadcast
tcpdump ip multicast

tcpdump ip6 multicast

抓包实战应用例子

提取 HTTP 的 UserAgent

从 HTTP 请求头提取 HTTP 的User-Agent:

1
tcpdump -nn -A -s1500 -l | grep "User-Agent:"

通过 egrep 可以同时提取 User-Agent 和 主机名(或其他 HTTP 头):

1
tcpdump -nn -A -s1500 -l | egrep "User-Agent:|Host:"

抓取 HTTP GET 和 POST 请求

抓取 HTTP GET 请求包:

1
2
3
$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'
# or
$ tcpdump -vvAls0 | grep 'GET'

抓取 HTTP POST 请求包:

1
2
3
$ tcpdump -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354'
# or
$ tcpdump -vvAls0 | grep 'POST'

注意:该方法不能保证抓取到 HTTP POST 有效数据流量,因为一个 POST 请求会被分割为多个 TCP 数据包。

找出发包数量最多的 IP

找出一段时间内发包最多的 IP,或者从一堆报文中找出发包最多的 IP,可以使用下面的命令:

1
tcpdump -nnn -t -c 200 | cut -f 1,2,3,4 -d '.' | sort | uniq -c | sort -nr | head -n 20
  • cut -f 1,2,3,4 -d '.': 以 . 为分隔符,打印出每行的前四列。即 IP 地址。
  • sort | uniq -c: 排序并计数
  • sort -nr: 按照数值大小逆向排序

抓取 DNS 请求和响应

DNS 的默认端口是 53,因此可以通过端口进行过滤:

1
tcpdump -i any -s0 port 53

切割 pcap 文件

当抓取大量数据并写入文件时,可以自动切割为多个大小相同的文件。例如,下面的命令表示每 3600 秒创建一个新文件 capture-(hour).pcap,每个文件大小不超过 200*1000000 字节:

1
tcpdump  -w /tmp/capture-%H.pcap -G 3600 -C 200

这些文件的命名为 capture-{1-24}.pcap,24 小时之后,之前的文件就会被覆盖。

提取 HTTP POST 请求中的密码

从 HTTP POST 请求中提取密码和主机名:

1
tcpdump -s 0 -A -n -l | egrep -i "POST /|pwd=|passwd=|password=|Host:"

提取 HTTP 请求的 URL

提取 HTTP 请求的主机名和路径:

1
tcpdump -s 0 -v -n -l | egrep -i "POST /|GET /|Host:"

抓取 HTTP 有效数据包

抓取 80 端口的 HTTP 有效数据包,排除 TCP 连接建立过程的数据包(SYN / FIN / ACK):

1
tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'

结合 Wireshark 进行分析

通常 Wireshark(或 tshark)比 tcpdump 更容易分析应用层协议。一般的做法是在远程服务器上先使用 tcpdump 抓取数据并写入文件,然后再将文件拷贝到本地工作站上用 Wireshark 分析。

还有一种更高效的方法,可以通过 ssh 连接将抓取到的数据实时发送给 Wireshark 进行分析。以 MacOS 系统为例,可以通过 brew cask install wireshark 来安装,然后通过下面的命令来分析:

1
ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - not port 22' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i -

例如,如果想分析 DNS 协议,可以使用下面的命令:

1
ssh root@remotesystem 'tcpdump -s0 -c 1000 -nn -w - port 53' | /Applications/Wireshark.app/Contents/MacOS/Wireshark -k -i -

-c 选项用来限制抓取数据的大小。如果不限制大小,就只能通过 ctrl-c 来停止抓取,这样一来不仅关闭了 tcpdump,也关闭了 wireshark。

netstat 命令用于显示各种网络相关信息。

常见参数

  • -a (all) 显示所有选项,默认不显示 LISTEN 相关
  • -t (tcp) 仅显示 tcp 相关选项
  • -u (udp) 仅显示 udp 相关选项
  • -n 拒绝显示别名,能显示数字的全部转化成数字
  • -l 仅列出有在 Listen(监听)的服务状态
  • -p 显示建立相关链接的程序名
  • -r 显示路由信息,路由表
  • -e 显示扩展信息,例如 uid
  • -s 按各个协议进行统计
  • -c 每隔一个固定时间,执行该 netstat 命令
  • -i --interfaces,显示所有网卡
  • -s 显示网络统计信息

示例

显示正在监听的 tcp 套接字

1
netstat -tl

输出:

1
2
3
4
5
6
7
8
9
10
11
12
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 localhost:9121 0.0.0.0:* LISTEN
tcp 0 0 localhost:websm 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:zabbix-agent 0.0.0.0:* LISTEN
tcp 0 0 localhost:9187 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:zabbix-trapper 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:xprint-server 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:commplex-main 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:empowerid 0.0.0.0:* LISTEN
tcp 0 0 localhost:cslistener 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:rsync 0.0.0.0:* LISTEN

显示路由表

1
netstat -r

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
default gateway 0.0.0.0 UG 0 0 0 enp4s0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-4caf812bdaa6
172.19.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-bf1a2715a536
172.20.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-24ea9b4941ce
172.21.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-dad742f48f75
172.22.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-c3b89e07005b
172.23.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-e80111182b15
172.24.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-52c539f436eb
172.27.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-6135592da74f
172.30.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-2b9072cf2223
192.168.2.0 0.0.0.0 255.255.255.0 U 0 0 0 enp4s0
192.168.250.0 0.0.0.0 255.255.255.0 U 0 0 0 br-ff86e2f8db31

显示相关的程序名

1
netstat -tlp

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 localhost:9121 0.0.0.0:* LISTEN 6994/redis_exporter
tcp 0 0 localhost:websm 0.0.0.0:* LISTEN 6970/prometheus
tcp 0 0 0.0.0.0:zabbix-agent 0.0.0.0:* LISTEN 1495/zabbix_agentd
tcp 0 0 localhost:9187 0.0.0.0:* LISTEN 6992/postgres_expor
tcp 0 0 0.0.0.0:zabbix-trapper 0.0.0.0:* LISTEN 3160/zabbix_server
tcp 0 0 0.0.0.0:xprint-server 0.0.0.0:* LISTEN 6979/nginx: master
tcp 0 0 0.0.0.0:commplex-main 0.0.0.0:* LISTEN 3152/python3
tcp 0 0 0.0.0.0:empowerid 0.0.0.0:* LISTEN 2271/nginx: master
tcp 0 0 localhost:cslistener 0.0.0.0:* LISTEN 1757/php-fpm: maste
tcp 0 0 0.0.0.0:rsync 0.0.0.0:* LISTEN 949/rsync
tcp 0 0 0.0.0.0:mysql 0.0.0.0:* LISTEN 2216/mysqld
tcp 0 0 localhost:jetdirect 0.0.0.0:* LISTEN 6984/node_exporter
tcp 0 0 localhost:9229 0.0.0.0:* LISTEN 6991/gitlab-workhor
tcp 0 0 localhost:distinct 0.0.0.0:* LISTEN 1493/php-fpm: maste
tcp 0 0 localhost:webcache 0.0.0.0:* LISTEN 7142/unicorn master
tcp 0 0 localhost:9168 0.0.0.0:* LISTEN 6975/ruby
tcp 0 0 0.0.0.0:9264 0.0.0.0:* LISTEN 2271/nginx: master
tcp 0 0 0.0.0.0:http 0.0.0.0:* LISTEN 2271/nginx: master
tcp 0 0 0.0.0.0:scp-config 0.0.0.0:* LISTEN 2271/nginx: master

拒绝显示别名

还是用上一个命令,上一个命令的输出中,我们可以看到某些应该是数字端口的地方,却出现了一些单词,比如:

1
tcp        0      0 localhost:websm         0.0.0.0:*               LISTEN      6970/prometheus

我们可以加上 -n 参数:

1
netstat -nltp

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:9121 0.0.0.0:* LISTEN 6994/redis_exporter
tcp 0 0 127.0.0.1:9090 0.0.0.0:* LISTEN 6970/prometheus
tcp 0 0 0.0.0.0:10050 0.0.0.0:* LISTEN 1495/zabbix_agentd
tcp 0 0 127.0.0.1:9187 0.0.0.0:* LISTEN 6992/postgres_expor
tcp 0 0 0.0.0.0:10051 0.0.0.0:* LISTEN 3160/zabbix_server
tcp 0 0 0.0.0.0:8100 0.0.0.0:* LISTEN 6979/nginx: master
tcp 0 0 0.0.0.0:5000 0.0.0.0:* LISTEN 3152/python3
tcp 0 0 0.0.0.0:7080 0.0.0.0:* LISTEN 2271/nginx: master
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 1757/php-fpm: maste
tcp 0 0 0.0.0.0:873 0.0.0.0:* LISTEN 949/rsync
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 2216/mysqld
tcp 0 0 127.0.0.1:9100 0.0.0.0:* LISTEN 6984/node_exporter
tcp 0 0 127.0.0.1:9229 0.0.0.0:* LISTEN 6991/gitlab-workhor
tcp 0 0 127.0.0.1:9999 0.0.0.0:* LISTEN 1493/php-fpm: maste
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN 7142/unicorn master
tcp 0 0 127.0.0.1:9168 0.0.0.0:* LISTEN 6975/ruby
tcp 0 0 0.0.0.0:9264 0.0.0.0:* LISTEN 2271/nginx: master
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 2271/nginx: master
tcp 0 0 0.0.0.0:10001 0.0.0.0:* LISTEN 2271/nginx: master

这样我们就可以看到具体监听的端口了。

查看建立的连接

1
netstat -ant | grep ESTABLISHED

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
tcp        0      0 192.168.2.168:51924     192.168.2.156:9094      ESTABLISHED
tcp 0 0 192.168.2.168:53442 192.168.2.156:9092 ESTABLISHED
tcp 0 0 192.168.2.168:48928 192.168.2.227:9092 ESTABLISHED
tcp 0 0 192.168.2.168:32856 120.79.186.232:9113 ESTABLISHED
tcp 0 0 192.168.2.168:59814 192.168.2.185:9092 ESTABLISHED
tcp 0 0 192.168.2.168:60048 192.168.2.168:9092 ESTABLISHED
tcp 0 0 192.168.2.168:49480 47.115.135.169:9092 ESTABLISHED
tcp 0 0 192.168.2.168:60134 120.79.15.181:14101 ESTABLISHED
tcp 0 0 192.168.2.168:34968 119.23.209.74:9092 ESTABLISHED
tcp 0 0 192.168.2.168:37062 119.23.209.74:14101 ESTABLISHED
tcp 0 0 192.168.2.168:44492 120.77.177.149:14101 ESTABLISHED
tcp 0 0 192.168.2.168:59580 120.79.186.232:9092 ESTABLISHED
tcp 0 0 192.168.2.168:33466 120.77.177.149:9092 ESTABLISHED

显示所有可用网卡

1
netstat -i

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Kernel Interface table
Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
br-24ea9b4941ce 1500 12923986 0 0 0 8918266 0 0 0 BMU
br-2b9072cf2223 1500 0 0 0 0 0 0 0 0 BMU
br-4caf812bdaa6 1500 0 0 0 0 0 0 0 0 BMU
br-52c539f436eb 1500 4420030 0 0 0 4572461 0 0 0 BMU
br-6135592da74f 1500 7591698 0 0 0 11314882 0 0 0 BMRU
br-bf1a2715a536 1500 7591698 0 0 0 11314882 0 0 0 BMU
br-c3b89e07005b 1500 0 0 0 0 0 0 0 0 BMU
br-dad742f48f75 1500 2091 0 0 0 6783 0 0 0 BMU
br-e80111182b15 1500 0 0 0 0 0 0 0 0 BMU
br-ff86e2f8db31 1500 12923986 0 0 0 8918266 0 0 0 BMU
docker0 1500 42136153 0 0 0 68259406 0 0 0 BMRU
enp4s0 1500 1904508155 0 2777565 0 2142106883 0 0 0 BMRU
lo 65536 987259699 0 0 0 987259699 0 0 0 LRU

显示网络统计信息

1
netstat -s

输出:

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
70
71
Ip:
2331469472 total packets received
100868012 forwarded
0 incoming packets discarded
2153729362 incoming packets delivered
2961922941 requests sent out
16 outgoing packets dropped
231 dropped because of missing route
285 reassemblies required
142 packets reassembled ok
Icmp:
1683106 ICMP messages received
561981 input ICMP message failed.
InCsumErrors: 44
ICMP input histogram:
destination unreachable: 1678491
echo requests: 4424
echo replies: 147
1755127 ICMP messages sent
0 ICMP messages failed
ICMP output histogram:
destination unreachable: 1750607
echo request: 239
echo replies: 4281
IcmpMsg:
InType0: 147
InType3: 1678491
InType8: 4424
OutType0: 4281
OutType3: 1750607
OutType8: 239
Tcp:
75243858 active connections openings
52446738 passive connection openings
13907686 failed connection attempts
904581 connection resets received
55 connections established
2222345241 segments received
2883961201 segments send out
33088737 segments retransmited
335714 bad segments received.
11770965 resets sent
Udp:
42916525 packets received
3132 packets to unknown port received.
0 packet receive errors
43474964 packets sent
0 receive buffer errors
0 send buffer errors
UdpLite:
TcpExt:
13429 invalid SYN cookies received
2868 resets received for embryonic SYN_RECV sockets
35 packets pruned from receive queue because of socket buffer overrun
329 ICMP packets dropped because they were out-of-window
59244840 TCP sockets finished time wait in fast timer
7185 packets rejects in established connections because of timestamp
23339679 delayed acks sent
.... // 省略一大堆输出
IpExt:
InNoRoutes: 23
InMcastPkts: 11716265
OutMcastPkts: 78163
InBcastPkts: 52491220
InOctets: 1924057038252
OutOctets: 3172007243489
InMcastOctets: 1284732984
OutMcastOctets: 13779932
InBcastOctets: 10254385628
InNoECTPkts: 2384812576
InECT0Pkts: 614417

问题:在本机运行的时候,执行了 go get github.com/stretchr/testify 之后,go.mod 里面的内容如下图,都飘红了。而且执行 go mod tidy 会直接移除这几个依赖,但是我的项目却是要依赖于 github.com/stretchr/testify 的。

解决方法:goland 的配置中勾上 Enable Go modules intergration

netstat

要列出正在侦听的所有 TCP 或 UDP 端口,包括使用端口的服务和套接字状态,请使用以下命令:

1
sudo netstat -tunlp

此命令中使用的选项具有以下含义:

  • -t - 显示 TCP 端口。
  • -u - 显示 UDP 端口。
  • -n - 显示数字地址而不是解析主机。
  • -l - 只显示监听端口。
  • -p - 显示监听进程的PID和名称。仅当您以 root 或sudo 用户身份运行命令时才会显示此信息。

输出将如下所示:

1
2
3
4
5
6
7
8
9
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 445/sshd
tcp 0 0 0.0.0.0:25 0.0.0.0:* LISTEN 929/master
tcp6 0 0 :::3306 :::* LISTEN 534/mysqld
tcp6 0 0 :::80 :::* LISTEN 515/apache2
tcp6 0 0 :::22 :::* LISTEN 445/sshd
tcp6 0 0 :::25 :::* LISTEN 929/master
tcp6 0 0 :::33060 :::* LISTEN 534/mysqld
udp 0 0 0.0.0.0:68 0.0.0.0:* 966/dhclient

列的含义:

  • Proto - 套接字使用的协议
  • Local Address - 进程侦听的 IP 地址和端口号
  • PID/Program name - PID 和进程的名称

如果要过滤结果,请使用 grep 命令。

1
sudo netstat -tnlp | grep :22

输出显示这台机器上的 22 端口被 SSH 服务器使用:

1
2
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      445/sshd
tcp6 0 0 :::22 :::* LISTEN 445/sshd

如果输出为空,则表示端口上没有任何内容正在侦听。

您还可以根据条件过滤列表,例如 PID、协议、状态等。

ss

ss 是新的 netstat。它缺少一些 netstat 特性,但暴露了更多的 TCP 状态,而且速度稍快。命令选项基本相同,因此从 netstat 到的转换 ss 并不困难。

要通过 ss 获取所有侦听端口的列表:

1
sudo ss -tunlp

输出与 netstat 几乎相同:

1
2
3
4
5
6
7
8
State    Recv-Q   Send-Q     Local Address:Port      Peer Address:Port                                                                                        
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=445,fd=3))
LISTEN 0 100 0.0.0.0:25 0.0.0.0:* users:(("master",pid=929,fd=13))
LISTEN 0 128 *:3306 *:* users:(("mysqld",pid=534,fd=30))
LISTEN 0 128 *:80 *:* users:(("apache2",pid=765,fd=4),("apache2",pid=764,fd=4),("apache2",pid=515,fd=4))
LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=445,fd=4))
LISTEN 0 100 [::]:25 [::]:* users:(("master",pid=929,fd=14))
LISTEN 0 70 *:33060 *:* users:(("mysqld",pid=534,fd=33))

lsof

lsof 是一个强大的命令行实用程序,可提供有关进程打开的文件的信息。

在 Linux 中,一切都是文件。您可以将套接字视为写入网络的文件。

要获取所有侦听 TCP 端口的列表,lsof 类型为:

1
sudo lsof -nP -iTCP -sTCP:LISTEN

使用的选项如下: * -n - 不要将端口号转换为端口名称。 * -p - 不解析主机名,显示数字地址。 * -iTCP -sTCP:LISTEN - 仅显示具有 TCP 状态 LISTEN 的网络文件。

1
2
3
4
5
6
7
8
9
10
COMMAND   PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd 445 root 3u IPv4 16434 0t0 TCP *:22 (LISTEN)
sshd 445 root 4u IPv6 16445 0t0 TCP *:22 (LISTEN)
apache2 515 root 4u IPv6 16590 0t0 TCP *:80 (LISTEN)
mysqld 534 mysql 30u IPv6 17636 0t0 TCP *:3306 (LISTEN)
mysqld 534 mysql 33u IPv6 19973 0t0 TCP *:33060 (LISTEN)
apache2 764 www-data 4u IPv6 16590 0t0 TCP *:80 (LISTEN)
apache2 765 www-data 4u IPv6 16590 0t0 TCP *:80 (LISTEN)
master 929 root 13u IPv4 19637 0t0 TCP *:25 (LISTEN)
master 929 root 14u IPv6 19638 0t0 TCP *:25 (LISTEN)

要查找正在侦听特定端口的进程,例如,3306您将使用的端口:

1
sudo lsof -nP -iTCP:3306 -sTCP:LISTEN

输出显示 MySQL 服务器使用端口3306:

1
2
COMMAND PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
mysqld 534 mysql 30u IPv6 17636 0t0 TCP *:3306 (LISTEN)