kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp
sar -n DEV 1

解析pcap数据包格式(code)

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <linux/types.h>

typedef unsigned int  bpf_u_int32;
typedef unsigned short  u_short;
typedef int bpf_int32;
typedef struct pcap_file_header {
	bpf_u_int32 magic;
	u_short version_major;
	u_short version_minor;
	bpf_int32 thiszone;
	bpf_u_int32 sigfigs;   
	bpf_u_int32 snaplen;   
	bpf_u_int32 linktype;  
}pcap_file_header;
 
typedef struct  timestamp{
	bpf_u_int32 timestamp_s;
	bpf_u_int32 timestamp_ms;
}timestamp;
 
typedef struct pcap_header{
	timestamp ts;
	bpf_u_int32 capture_len;
	bpf_u_int32 len;
 
}pcap_header;


#define ETH_ALEN 6
#define __LITTLE_ENDIAN_BITFIELD 1
#define NIPQUAD(addr) \
		((unsigned char *)&addr)[0], \
		((unsigned char *)&addr)[1], \
		((unsigned char *)&addr)[2], \
		((unsigned char *)&addr)[3]


struct ethhdr {
	unsigned char h_dest[ETH_ALEN];       /* destination eth addr */
	unsigned char h_source[ETH_ALEN];     /* source ether addr    */
	unsigned short    h_proto;                /* packet type ID field */
};

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u8  ihl:4,
			version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
	__u8  version:4,
			ihl:4;
#endif
	__u8    tos;
	__be16  tot_len;
	__be16  id;
	__be16  frag_off;
	__u8    ttl;
	__u8    protocol;
	__u16   check;
	__be32  saddr;
	__be32  daddr;
	/*The options start here. */
};

struct tcphdr {
	__u16   source;
	__u16   dest;
	__u32   seq;
	__u32   ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u16   res1:4,
			doff:4,
			fin:1,
			syn:1,
			rst:1,
			psh:1,
			ack:1,
			urg:1,
			ece:1,
			cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
	__u16   doff:4,
			res1:4,
			cwr:1,
			ece:1,
			urg:1,
			ack:1,
			psh:1,
			rst:1,
			syn:1,
			fin:1;
#endif  
	__u16   window;
	__u16   check;
	__u16   urg_ptr;
};

struct udphdr {
	__u16   source;
	__u16   dest;
	__u16   len;
	__u16   check;
};

struct icmphdr {
	__u8  type;
	__u8  code;
	__u16 checksum;
	union {
		struct {
			__u16   id;
			__u16   sequence;
		} echo;
		__u32   gateway;
		struct {
			__u16   __unused;
			__u16   mtu;
		} frag;
	} un;
};

FILE *fp1, *fp2;
__u32 flag1, flag2, seq1, seq2, ip1, ip2;
long long stime;

void printPcap(int count, void * data, struct pcap_header *ph)
{
	size_t size = ph->capture_len;
	unsigned  short iPos = 0;
	struct ethhdr *eth;
	struct iphdr *iph;
	struct tcphdr *tcph;
	struct udphdr *udph;
	struct icmphdr *icmph;
	__u8 op1, op2, type, len;
	void * data1;
	int i;
	long long dt;

	if (data==NULL) {
		return;
	}
	eth = (struct ethhdr*)(data);
	eth->h_proto = ntohs(eth->h_proto);
	//printf("Ether:\tproto = 0x%x\n", eth->h_proto);
	if (eth->h_proto == 0x0800) { // IP
		iph = (struct iphdr*)(data+sizeof(struct ethhdr));
		if (iph->protocol == IPPROTO_TCP) { // tcp
			tcph = (struct tcphdr*)(data+sizeof(struct ethhdr)+sizeof(struct iphdr));
			if (tcph->ack == 0 && tcph->syn == 1) {
				seq1 = ntohl(tcph->seq);
				ip1 = iph->saddr;
				flag1 = 1;
				stime = 1000000LL*ph->ts.timestamp_s + ph->ts.timestamp_ms;
			} else if (tcph->ack == 1 && tcph->syn == 1) {
				seq2 = ntohl(tcph->seq);
				ip2 = iph->saddr;
				flag2 = 1;
			}

			if (flag1 == 0) {
				flag1 = 1;
				seq1 = ntohl(tcph->seq)-1;
				ip1 = iph->saddr;
				stime = 1000000LL*ph->ts.timestamp_s + ph->ts.timestamp_ms;
			}
			if (flag2 == 0 && iph->saddr != ip1) {
				flag2 = 1;
				seq2 = ntohl(tcph->seq)-1;
				ip2 = iph->saddr;
			}

			dt = (1000000LL*ph->ts.timestamp_s+ph->ts.timestamp_ms) - stime;
			fprintf(fp1, "%d\t%llu\t", count, dt/1000);
			fprintf(fp1, "%d.%d.%d.%d\t%d.%d.%d.%d\t%u\t%u\t%d\t",
						NIPQUAD(iph->saddr), NIPQUAD(iph->daddr),
						(iph->saddr==ip1?ntohl(tcph->seq)-seq1:ntohl(tcph->seq)-seq2),
						(iph->saddr==ip1?ntohl(tcph->ack_seq)-seq2:ntohl(tcph->ack_seq)-seq1),
						ntohs(iph->tot_len)-iph->ihl*4-tcph->doff*4 + tcph->syn + tcph->fin);
			fprintf(fp1, "%d\t%d\t%d\t%d\t", ntohs(iph->tot_len), iph->ihl*4, tcph->doff*4, iph->ttl);
			fprintf(fp1, "%u\t%u\t%d\t%d\t%d\t",
						ntohl(tcph->seq), ntohl(tcph->ack_seq),
						tcph->ack, tcph->syn, ntohs(tcph->window));

			if (tcph->doff > 5) {
				data1 = data + sizeof(struct ethhdr)+sizeof(struct iphdr)+sizeof(struct tcphdr);
				op1 = *(__u8*)(data1);
				op2 = *(__u8*)(data1+1);
				type = *(__u8*)(data1+2);
				len = *(__u8*)(data1+3);
				//fprintf(fp1, "%u\t%u\t%u\t%u\n", op1, op2, type, len);
				if (op1 == 1 && op2 == 1 && type == 5) { // sack
					i = 4;
					while (i < len+2) {
						if (i > 4) fprintf(fp1, " ");
						fprintf(fp1, "%u-%u", ntohl(*(__u32*)(data1+i))-seq2, ntohl(*(__u32*)(data1+i+4))-seq2);
						i += 8;
					}
				}
			}
			fprintf(fp1, "\n");

		} else if (iph->protocol == IPPROTO_UDP) { // udp
			udph = (struct udphdr*)(data+sizeof(struct ethhdr)+sizeof(struct iphdr));
			//printf("UDP:\tsource=%u\tdest=%u\tlen=%d\n", ntohs(udph->source), ntohs(udph->dest), ntohs(udph->len));
		} else if (iph->protocol == IPPROTO_ICMP) { // ICMP
			icmph = (struct icmphdr*)(data+sizeof(struct ethhdr)+sizeof(struct iphdr));
			//printf("ICMP:\ttype=%u\tcode=%u\n", icmph->type, icmph->code);
		}
	}
}

#define MAX_ETH_FRAME 1514000
int main (int argc, const char * argv[])
{
	pcap_file_header  pfh;
	pcap_header  ph;
	int count=0;
	void * buff = NULL;
	int readSize=0;
	int ret = 0;
 
	 if (argc != 2) {
		 printf("uage: ./a.out pcap_filename\n");
		 return -1;
	 }
	FILE *fp = fopen(argv[1], "r");
	if (fp==NULL) {
		fprintf(stderr, "Open file %s error.", argv[1]);
		return -1;
	}
	fread(&pfh, sizeof(pcap_file_header), 1, fp);
 
	fp1 = fopen("out", "w");
	fprintf(fp1, "#ID\tTIME\tsaddr\tdaddr\tseq\tack_seq\tpayload\ttot_len\tihl\tdoff\tttl\tseq\tack_seq\tack\tsyn\twin\tSACK\n");
	buff = (void *)malloc(MAX_ETH_FRAME);
	flag1 = flag2 = 0;

	for (count=1; ; count++) {
		memset(buff,0,MAX_ETH_FRAME);
		readSize=fread(&ph, sizeof(pcap_header), 1, fp);
		if (readSize<=0) {
			break;
		}
		if (buff==NULL) {
			fprintf(stderr, "malloc memory failed.\n");
			return -1;
		}
 
		readSize=fread(buff,1,ph.capture_len, fp);
		if (readSize != ph.capture_len) {
			free(buff);
			fprintf(stderr, "pcap file parse error.\n");
			return -1;
		}
		printPcap(count, buff, &ph);

		if (feof(fp) || readSize <=0 ) {
			break;
		}
	}
	fclose(fp);
	fclose(fp1);
	return ret;
}

解析pcap数据包格式

协议是一个比较复杂的协议集,有很多专业书籍介绍。在此,我仅介绍其与编程密切相关的部分:以太网上TCP/IP协议的分层结构及其报文格式。
我们知道TCP/IP协议采用分层结构,其分层模型及协议如下表:
应 用 层 (Application) HTTP、Telnet、FTP、SMTP、SNMP
传 输 层 (Transport) TCP、UDP
网 间 网层 (Internet) IP【ARP、RARP、ICMP】
网络接口层 (Network) Ethernet、X.25、SLIP、PPP

协议采用分层结构,因此,数据报文也采用分层封装的方法。下面以应用最广泛的以太网为例说明其数据报文分层封装,如下图所示:

任何通讯协议都有独特的报文格式,TCP/IP协议也不例外。对于通讯协议编程,我们首先要清楚其报文格式。由于TCP/IP协议采用分层模型,各层都有专用的报头,以下就简单介绍以太网下TCP/IP各层报文格式。

8字节的前导用于帧同步,CRC域用于帧校验。这些用户不必关心其由网卡芯片自动添加。目的地址和源地址是指网卡的物理地址,即MAC地址,具有唯一性。帧类型或协议类型是指数据包的高级协议,如 0x0806表示ARP协议,0x0800表示IP协议等。

  ARP/RARP(地址解析/反向地址解析)报文格式如下图:

“硬件类型”域指发送者本机网络接口类型(值“1”代表以太网)。“协议类型”域指发送者所提供/请求的高级协议地址类型(“0x0800”代表 IP协议)。“操作”域指出本报文的类型(“1”为ARP请求,“2”为ARP响应,“3”为RARP请求,“4”为RARP响应)。

  IP数据报头格式如下图:

  我们用单片机实现TCP/IP协议要作一些简化,不考虑数据分片和优先权。因此,在此我们不讨论服务类型和标志偏移域,只需填“0” 即可。协议“版本”为4,“头长度”单位为32Bit,“总长度”以字节为单位,表示整个IP数据报长度。“标识”是数据包的ID号,用于识别不同的IP 数据包。“生存时间” TTL是个数量及的概念,防止无用数据包一直存在网络中。一般每经过路由器时减一,因此通过TTL 可以算出数据包到达目的地所经过的路由器个数。“协议”域表示创建该数据包的高级协议类型。如 1表示ICMP协议,6表示TCP协议,17表示 UDP协议等。IP数据包为简化数据转发时间,仅采用头校验的方法,数据正确性由高层协议保证。

  ICMP(网间网控制报文协议)协议 应用广泛。在此仅给出最常见的回应请求与应答报文格式,用户命令ping便是利用此报文来测试信宿机的可到达性。报文格式如下图所示:

  类型0 为回应应答报文,8 为回应请求报文。整个数据包均参与检验。注意ICMP封装在IP数据包里传送。

  UDP报文格式如下图:

  TCP报文格式如下图:


WireShark捕获的数据

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
		以下为物理层的数据帧概况

Frame 1 (62 bytes on wire, 62 bytes captured)           1号帧,线路62字节,实际捕获62字节
Arrival Time: Jan 21, 2008 15:17:33.910261000           捕获日期和时间
[Time delta from previous packet:0.00000 seconds]       此包与前一包的时间间隔
[Time since reference or first frame: 0.00 seconds]     此包与第1帧的间隔时间
Frame Number: 1                                         帧序号
Packet Length: 62 bytes                                 帧长度
Capture Length: 62 bytes                                捕获长度
[Frame is marked: False]                                此帧是否做了标记:否
[Protocols in frame: eth:ip:tcp]                        帧内封装的协议层次结构
[Coloring Rule Name: HTTP]                              用不同颜色染色标记的协议名称:HTTP
[Coloring Rule String: http || tcp.port == 80]          染色显示规则的字符串:


		以下为数据链路层以太网帧头部信息
Ethernet II, Src: AcerTech_5b:d4:61 (00:00:e2:5b:d4:61), Dst: Jetcell_e5:1d:0a (00:d0:2b:e5:1d:0a)
以太网协议版本II,源地址:厂名_序号(网卡地址),目的:厂名_序号(网卡地址)
 Destination: Jetcell_e5:1d:0a (00:d0:2b:e5:1d:0a)       目的:厂名_序号(网卡地址)
 Source: AcerTech_5b:d4:61 (00:00:e2:5b:d4:61)           源:厂名_序号(网卡地址)
 Type: IP (0x0800)                                       帧内封装的上层协议类型为IP(十六进制码0800)看教材70页图3.2

		以下为互联网层IP包头部信息
Internet Protocol, Src: 202.203.44.225 (202.203.44.225), Dst: 202.203.208.32 (202.203.208.32)
互联网协议,源IP地址,目的IP地址
Version: 4                                                       互联网协议IPv4
Header length: 20 bytes                                          IP包头部长度
Differentiated Services Field:0x00(DSCP 0x00:Default;ECN:0x00)   差分服务字段
Total Length: 48                                                 IP包的总长度
Identification:0x8360 (33632)                                    标志字段
Flags:                                                           标记字段(在路由传输时,是否允许将此IP包分段)
Fragment offset: 0                                               分段偏移量(将一个IP包分段后传输时,本段的标识)
Time to live: 128                                                生存期TTL
Protocol: TCP (0x06)                                             此包内封装的上层协议为TCP
Header checksum: 0xe4ce [correct]                                头部数据的校验和
Source: 202.203.44.225 (202.203.44.225)                          源IP地址
Destination: 202.203.208.32 (202.203.208.32)                     目的IP地址

		以下为传输层TCP数据段头部信息
Transmission Control Protocol, Src Port: 2764 (2764), Dst Port: http (80), Seq: 0, Len: 0   传输控制协议TCP的内容
Source port: 2764 (2764)                              源端口名称(端口号)
Destination port: http (80)                            目的端口名http(端口号80)
Sequence number: 0    (relative sequence number)       序列号(相对序列号)
Header length: 28 bytes                                头部长度
Flags: 0x02 (SYN)                                      TCP标记字段(本字段是SYN,是请求建立TCP连接)
Window size: 65535                                     流量控制的窗口大小
Checksum: 0xf73b [correct]                             TCP数据段的校验和
Options: (8 bytes)                                     可选项

linux内核调试转储工具kdump crash

http://www.ibm.com/developerworks/cn/linux/l-cn-kdump4/index.html

1
2
3
4
5
$ crash vmlinux vmcore 
crash> bt
crash> dis -l ffffffff80081000
crash> gdb x/8ub ffffffff90091000
......

如果是未完成文件可以尝试以最小方式调试

1
2
$ crash --minimal vmlinux vmcore
crash> log
1
2
3
4
5
6
7
8
9
10
11
crash_H_args_xbt> mod -S
 MODULE   NAME         SIZE  OBJECT FILE
c8019000  soundcore   2788  /lib/modules/2.2.5-15/misc/soundcore.o
。。。
crash_H_args_xbt> mod -s soundcore
 MODULE   NAME         SIZE  OBJECT FILE
c8019000  soundcore   2788  /lib/modules/2.2.5-15/misc/soundcore.o
crash_H_args_xbt> mod -d soundcore
crash_H_args_xbt> mod -s soundcore /tmp/soundcore.o
 MODULE   NAME         SIZE  OBJECT FILE
c8019000  soundcore   2788  /tmp/soundcore.o

1、kdump介绍与设置

1)介绍:

Kdump 是一种基于 kexec 的内存转储工具,目前它已经被内核主线接收,成为了内核的一部分,它也由此获得了绝大多数 Linux 发行版的支持。与传统的内存转储机制不同不同,基于 Kdump 的系统工作的时候需要两个内核,一个称为系统内核,即系统正常工作时运行的内核;另外一个称为捕获内核,即正常内核崩溃时,用来进行内存转储的内核。

安装crash,kexec-tools

2)设置

查看/boot/grub/grub.conf文件中kernel一行最后是否有crashkernel=128M@64M,如果没有,添加上去,重启
如何设定 crashkernel 参数
在 kdump 的配置中,往往困惑于 crashkernel 的设置。“crashkernel=X@Y”,X 应该多大? Y 又应该设在哪里呢?实际我们 可以完全省略“@Y”这一部分,这样,kernel 会为我们自动选择一个起始地址。而对于 X 的大小,般对 i386/x86_64 的系统, 设为 128M 即可;对于 powerpc 的系统,则要设为 256M。rhel6 引入的“auto”已经要被放弃了,代之以原来就有的如下语法:

1
2
3
4
5
6
crashkernel=<range1>:<size1>[,<range2>:<size2>,...][@offset] 
		  range=start-[end] 
		  'start' is inclusive and 'end' is exclusive. 

		  For example: 
		  crashkernel=512M-2G:64M,2G-:128M

如何判断捕获内核是否加载
可通过查看 /sys/kernel/kexec_crash_loaded 的值。“1”为已经加载,“0”为还未加载。
缩小 crashkernel
可以通过向 /sys/kernel/kexec_crash_size 中输入一个比其原值小的数来缩小甚至完全释放 crashkernel。

3)测试kdump是否可用

执行

1
2
echo 1 > /proc/sys/kernel/sysrq
echo c > /proc/sysrq-trigger

经过两次自动重启后,查看/var/crash/目录下是否有vmcore文件生成,如果有表示kdump可用

2、生成带调试信息的vmlinux文件

1)

centos: debuginfo.centos.org

2)按顺序安装

kernel-debuginfo-common-2.6.18-194.3.1.el5.i686.rpm和kernel-debuginfo-2.6.18-194.3.1.el5.i686.rpm, 之后会在/usr/lib/debug/lib/modules/2.6.18-194.3.1.el5/下生产vmlinux文件
或在源码里make binrpm-pkg -j8,然后该目录下会生成一个vmlinux
在编译内核之前,需要确认.config中,以下编译选项是否打开:

(1)CONFIG_DEBUG_INFO ,必须打开该选项,否则crash会出现以下错误:
crash no debugging data available
(2)CONFIG_STRICT_DEVMEM,必须打开该选项,否则crash会出现以下错误:
crash: read error: kernel virtual address: c0670680 type: “kernel_config_data”
WARNING: cannot read kernel_config_data
crash: read error: kernel virtual address: c066bb68 type: “cpu_possible_mask”

3、进入vmlinux所在目录,

执行crash /var/crash/2012-03-13-21:05/vmcore vmlinux
mod -S XXX –导入XXX目录下所有符号
log –查看日志文件,找到最后一条,如EIP: [] bshtej_interrupt+0x103f/0x11cb [tej21] SS:ESP 0068:c0768f38
l* bshtej_interrupt+0x103f 出现如下内容

1
2
3
4
5
6
7
8
9
10
11
0xf8ee53f5 is in bshtej_interrupt (/opt/dahdi-linux-complete-2.2.1+2.2.1/linux/drivers/dahdi/tej21/tej21.c:2910).
2904          int c, x;
2905
2906
2907          for(c = 0; c < MAX_CARDS; c++)
2908          {
2909              if (!cards[c]) break;
2910              for (x=0;x<cards[c]->numspans;x++) {
2911                  if (cards[c]->tspans[x]->sync)
2912                  {
2913

到此可确定死机问题出现在2910行。

4、设置过滤等级:

vmcore文件一般会收集内核崩溃时的各种信息,所以生成时间会较长,文件比较大,如果不需要某些信息的话,可对kdump.conf文件进行配置

1
vim  /etc/kdump.conf

将core_collector makedumpfile -c 这行打开,并加上-d 31,参数说明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-c: Compress dump data by each page.
-d: Specify the type of unnecessary page for analysis.
  Dump  | zero    cache   cache   user    free
  Level | page    page    private data    page
  -------+---------------------------------------
  0  |
  1  |    X
  2  |        X
  4  |        X   X
  8  |                X
  16  |                   X
  31  |   X   X   X   X   X

##### 5、根据Oops值大致判断错误:
Oops的错误代码根据错误的原因会有不同的定义如果发现自己遇到的Oops和下面无法对应的话,最好去内核代码里查找:
  • error_code:
  • bit 0 == 0 means no page found, 1 means protection fault
  • bit 1 == 0 means read, 1 means write
  • bit 2 == 0 means kernel, 1 means user-mode
  • bit 3 == 0 means data, 1 means instruction ```