aboutsummaryrefslogtreecommitdiffstats
path: root/test/packetdrill/packet.h
blob: aa41104c2ac1a13a09127588d25c90d0cb5c34bf (plain)
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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
/*
 * Copyright 2013 Google Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */
/*
 * Author: ncardwell@google.com (Neal Cardwell)
 *
 * Interface and type declarations for a representation of TCP/IP packets.
 * Packets are represented in their wire format.
 */

#ifndef __PACKET_H__
#define __PACKET_H__

#include "types.h"

#include <sys/time.h>
#include "assert.h"
#include "gre.h"
#include "header.h"
#include "icmp.h"
#include "icmpv6.h"
#include "ip.h"
#include "ipv6.h"
#include "tcp.h"
#include "udp.h"
#include "unaligned.h"

/* The data offset field is 4 bits, and specifies the length of the TCP header,
 * including options, in 32-bit words.
 */
#define MAX_TCP_HEADER_BYTES (15*4)

#define MAX_TCP_DATAGRAM_BYTES (64*1024)	/* for sanity-checking */
#define MAX_UDP_DATAGRAM_BYTES (64*1024)	/* for sanity-checking */

/* We allow reading pretty big packets, since some interface MTUs can
 * be pretty big (the Linux loopback MTU, for example, is typically
 * around 16KB).
 */
static const int PACKET_READ_BYTES = 64 * 1024;

/* Maximum number of headers. */
#define PACKET_MAX_HEADERS	6

/* Maximum number of bytes of headers. */
#define PACKET_MAX_HEADER_BYTES	256

/* TCP/UDP/IPv4 packet, including IPv4 header, TCP/UDP header, and data. There
 * may also be a link layer header between the 'buffer' and 'ip'
 * pointers, but we typically ignore that. The 'buffer_bytes' field
 * gives the total space in the buffer, which may be bigger than the
 * actual amount occupied by the packet data.
 */
struct packet {
	u8 *buffer;		/* data buffer: full contents of packet */
	u32 buffer_bytes;	/* bytes of space in data buffer */
	u32 l2_header_bytes;	/* bytes in outer hardware/layer-2 header */
	u32 ip_bytes;		/* bytes in outermost IP hdrs/payload */
	enum direction_t direction;	/* direction packet is traveling */

	/* Metadata about all the headers in the packet, including all
	 * layers of encapsulation, from outer to inner, starting from
	 * the outermost IP header at headers[0].
	 */
	struct header headers[PACKET_MAX_HEADERS];

	/* The following pointers point into the 'buffer' area. Each
	 * pointer may be NULL if there is no header of that type
	 * present in the packet. In each case these are pointers to
	 * the innermost header of that kind, since that is where most
	 * of the interesting TCP/UDP/IP action is.
	 */

	/* Layer 3 */
	struct ipv4 *ipv4;	/* start of IPv4 header, if present */
	struct ipv6 *ipv6;	/* start of IPv6 header, if present */

	/* Layer 4 */
	struct tcp *tcp;	/* start of TCP header, if present */
	struct udp *udp;	/* start of UDP header, if present */
	struct icmpv4 *icmpv4;	/* start of ICMPv4 header, if present */
	struct icmpv6 *icmpv6;	/* start of ICMPv6 header, if present */
	bool echoed_header;     /* icmp payload is an echoed header?
				   This is for TCP/UDP */


	s64 time_usecs;		/* wall time of receive/send if non-zero */

	u32 flags;		/* various meta-flags */
#define FLAG_WIN_NOCHECK	0x1  /* don't check TCP receive window */
#define FLAG_OPTIONS_NOCHECK	0x2  /* don't check TCP options */

	enum tos_chk_t tos_chk;	/* how to treat the TOS byte of a packet */

	__be32 *tcp_ts_val;	/* location of TCP timestamp val, or NULL */
	__be32 *tcp_ts_ecr;	/* location of TCP timestamp ecr, or NULL */
	int	mss;
};

/* A simple list of packets. */
struct packet_list {
	struct packet *packet;    /* the packet content */
	struct packet_list *next; /* link to next element, or NULL if last */
};

/* Allocate a packet_list and initialize its fields to NULL. */
extern struct packet_list *packet_list_new(void);

/* Free an entire packet list. */
extern void packet_list_free(struct packet_list *list);

/* Allocate and initialize a packet. */
extern struct packet *packet_new(u32 buffer_length);

/* Free all the memory used by the packet. */
extern void packet_free(struct packet *packet);

/* Create a packet that is a copy of the contents of the given packet. */
extern struct packet *packet_copy(struct packet *old_packet);

/* Return the number of headers in the given packet. */
extern int packet_header_count(const struct packet *packet);

/* Attempt to append a new header to the given packet. Return a
 * pointer to the new header metadata, or NULL if we can't add the
 * header.
 */
extern struct header *packet_append_header(struct packet *packet,
					   enum header_t header_type,
					   int header_bytes);

/* Return a newly-allocated packet that is a copy of the given inner packet
 * but with the given outer packet prepended.
 */
extern struct packet *packet_encapsulate(struct packet *outer,
					 struct packet *inner);

/* Aggregate a list of packets into a new packet carrying the combined
 * payload and return the newly allocated packet. The head and tail parameters
 * point to the first and the last packet, respectively, in the input list.
 * payload_size is the payload size for the aggregated packet, equal to the
 * summed payload across all the packets in the list.
 * The source packets were previously checked to have compatible headers. Copy
 * the headers from the last source packet, and update the length fields in all
 * the headers to match the combined payload.
 */
extern struct packet *aggregate_packets(const struct packet_list *head,
					const struct packet_list *tail,
					int payload_size);

/* Encapsulate a packet and free the original outer and inner packets. */
static inline struct packet *packet_encapsulate_and_free(struct packet *outer,
							 struct packet *inner)
{
	struct packet *packet = packet_encapsulate(outer, inner);
	packet_free(outer);
	packet_free(inner);
	return packet;
}

/* Return the direction in which the given packet is traveling. */
static inline enum direction_t packet_direction(const struct packet *packet)
{
	return packet->direction;
}

/* Convenience accessors for peeking around in the packet... */

/* Return the address family corresponding to the packet protocol. */
static inline int packet_address_family(const struct packet *packet)
{
	if (packet->ipv4 != NULL)
		return AF_INET;
	if (packet->ipv6 != NULL)
		return AF_INET6;
	return AF_UNSPEC;
}

/* Return a pointer to the first byte of the outermost IP header. */
static inline u8 *packet_start(const struct packet *packet)
{
	u8 *start = packet->headers[0].h.ptr;
	assert(start != NULL);
	return start;
}

/* Return a pointer to the first byte of the innermost IP header. */
static inline u8 *ip_start(struct packet *packet)
{
	if (packet->ipv4 != NULL)
		return (u8 *)packet->ipv4;
	if (packet->ipv6 != NULL)
		return (u8 *)packet->ipv6;
	assert(!"bad address family");
	return 0;
}


/* Return the length in bytes of the IP header for packets of the
 * given address family, assuming no IP options.
 */
static inline int ip_header_min_len(int address_family)
{
	if (address_family == AF_INET)
		return sizeof(struct ipv4);
	else if (address_family == AF_INET6)
		return sizeof(struct ipv6);
	else
		assert(!"bad ip_version in config");
}

/* Return the layer4 protocol of the packet. */
static inline int packet_ip_protocol(const struct packet *packet)
{
	if (packet->ipv4 != NULL)
		return packet->ipv4->protocol;
	if (packet->ipv6 != NULL)
		return packet->ipv6->next_header;
	assert(!"no valid IP header");
	return 0;
}

/* Return the length of an optionless TCP or UDP header. */
static inline int layer4_header_len(int protocol)
{
	if (protocol == IPPROTO_TCP)
		return sizeof(struct tcp);
	if (protocol == IPPROTO_UDP)
		return sizeof(struct udp);
	assert(!"bad protocol");
	return 0;
}

/* Return the length of the TCP header, including options. */
static inline int packet_tcp_header_len(const struct packet *packet)
{
	assert(packet->tcp);
	return packet->tcp->doff * sizeof(u32);
}

/* Return the length of the UDP header. */
static inline int packet_udp_header_len(const struct packet *packet)
{
	assert(packet->udp);
	return sizeof(struct udp);
}

/* Return the length of the ICMPv4 header. */
static inline int packet_icmpv4_header_len(const struct packet *packet)
{
	assert(packet->icmpv4);
	return sizeof(struct icmpv4);
}

/* Return the length of the ICMPv6 header. */
static inline int packet_icmpv6_header_len(const struct packet *packet)
{
	assert(packet->icmpv6);
	return sizeof(struct icmpv6);
}

/* Return the length of the TCP options. */
static inline int packet_tcp_options_len(const struct packet *packet)
{
	assert(packet->tcp);
	return packet_tcp_header_len(packet) - sizeof(*(packet->tcp));
}

/* Return a pointer to the TCP options. */
static inline u8 *packet_tcp_options(struct packet *packet)
{
	assert(packet->tcp);
	return (u8 *) (packet->tcp + 1);
}

static inline u32 packet_tcp_ts_val(const struct packet *packet)
{
	return get_unaligned_be32(packet->tcp_ts_val);
}

static inline u32 packet_tcp_ts_ecr(const struct packet *packet)
{
	return get_unaligned_be32(packet->tcp_ts_ecr);
}

static inline void packet_set_tcp_ts_val(struct packet *packet, u32 ts_val)
{
	put_unaligned_be32(ts_val, packet->tcp_ts_val);
}

static inline void packet_set_tcp_ts_ecr(struct packet *packet, u32 ts_ecr)
{
	put_unaligned_be32(ts_ecr, packet->tcp_ts_ecr);
}

/* Return a pointer to the TCP/UDP data payload. */
static inline u8 *packet_payload(const struct packet *packet)
{
	if (packet->tcp)
		return ((u8 *) packet->tcp) + packet_tcp_header_len(packet);
	if (packet->udp)
		return ((u8 *) packet->udp) + packet_udp_header_len(packet);
	if (packet->icmpv4)
		return ((u8 *) packet->icmpv4) + packet_icmpv4_header_len(packet);
	if (packet->icmpv6)
		return ((u8 *) packet->icmpv6) + packet_icmpv6_header_len(packet);

	assert(!"no valid payload; not TCP or UDP or ICMP!?");
	return NULL;
}

/* Return a pointer to the byte beyond the end of the packet. */
static inline u8 *packet_end(const struct packet *packet)
{
	return packet_start(packet) + packet->ip_bytes;
}

/* Return the length of the TCP/UDP payload. */
static inline int packet_payload_len(const struct packet *packet)
{
	return packet_end(packet) - packet_payload(packet);
}

/* Return the location of the IP header echoed by an ICMP message. */
static inline u8 *packet_echoed_ip_header(struct packet *packet)
{
	if (packet->icmpv4 != NULL)
		return (u8 *)(packet->icmpv4 + 1);
	if (packet->icmpv6 != NULL)
		return (u8 *)(packet->icmpv6 + 1);
	assert(!"no valid icmp header");
	return NULL;
}

/* Return the location of the IPv4 header echoed by an ICMP message, or NULL. */
static inline struct ipv4 *packet_echoed_ipv4_header(struct packet *packet)
{
	return (struct ipv4 *)((packet->icmpv4 != NULL) ?
			       (packet->icmpv4 + 1) : NULL);
}

/* Return the location of the IPv6 header echoed by an ICMP message, or NULL. */
static inline struct ipv6 *packet_echoed_ipv6_header(struct packet *packet)
{
	return (struct ipv6 *)((packet->icmpv6 != NULL) ?
			       (packet->icmpv6 + 1) : NULL);
}

/* Return the length in bytes of the IP header echoed by an ICMP message.
 * For now we do not generate any IP options for echoed IP headers.
 */
static inline int packet_echoed_ip_header_len(struct packet *packet)
{
	if (packet->icmpv4 != NULL)
		return sizeof(struct ipv4);
	if (packet->icmpv6 != NULL)
		return sizeof(struct ipv6);
	assert(!"no valid icmp header");
	return 0;
}

/* Return the layer4 protocol of the packet echoed inside an ICMP packet. */
static inline int packet_echoed_ip_protocol(struct packet *packet)
{
	if (packet->icmpv4 != NULL)
		return packet_echoed_ipv4_header(packet)->protocol;
	if (packet->icmpv6 != NULL)
		return packet_echoed_ipv6_header(packet)->next_header;
	assert(!"no valid icmp header");
	return 0;
}

/* Return the location of the TCP or UDP header echoed by an ICMP message. */
static inline u8 *packet_echoed_layer4_header(struct packet *packet)
{
	u8 *echoed_ip = packet_echoed_ip_header(packet);
	int ip_header_len = packet_echoed_ip_header_len(packet);
	return echoed_ip + ip_header_len;
}

/* Return the location of the TCP header echoed by an ICMP message. */
static inline struct tcp *packet_echoed_tcp_header(struct packet *packet)
{
	if (packet_echoed_ip_protocol(packet) == IPPROTO_TCP)
		return (struct tcp *)(packet_echoed_layer4_header(packet));
	return NULL;
}

/* Return the location of the UDP header echoed by an ICMP message. */
static inline struct udp *packet_echoed_udp_header(struct packet *packet)
{
	if (packet_echoed_ip_protocol(packet) == IPPROTO_UDP)
		return (struct udp *)(packet_echoed_layer4_header(packet));
	return NULL;
}

/* Return the location of the TCP sequence number echoed by an ICMP message. */
static inline u32 *packet_echoed_tcp_seq(struct packet *packet)
{
	struct tcp *echoed_tcp = packet_echoed_tcp_header(packet);
	assert(echoed_tcp);
	u32 *seq = &(echoed_tcp->seq);
	/* Check that the seq field is actually in the space we
	 * reserved for the echoed prefix of the TCP header.
	 */
	assert((char *) (seq + 1) <= (char *) echoed_tcp + ICMP_ECHO_BYTES);
	return seq;
}

#endif /* __PACKET_H__ */