summaryrefslogtreecommitdiff
path: root/ring_tx.c
blob: f91f7f7892a467dc04d1cc5ee5e6082e69e1445d (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
/*
 * netsniff-ng - the packet sniffing beast
 * Copyright 2009, 2010 Daniel Borkmann.
 * Copyright 2009, 2010 Emmanuel Roullit.
 * Subject to the GPL, version 2.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>

#include "die.h"
#include "xmalloc.h"
#include "ring_tx.h"
#include "built_in.h"

void set_packet_loss_discard(int sock)
{
	int ret, discard = 1;
	ret = setsockopt(sock, SOL_PACKET, PACKET_LOSS, (void *) &discard,
			 sizeof(discard));
	if (ret < 0)
		panic("setsockopt: cannot set packet loss");
}

void destroy_tx_ring(int sock, struct ring *ring)
{
	int ret;

	munmap(ring->mm_space, ring->mm_len);
	ring->mm_len = 0;

	fmemset(&ring->layout, 0, sizeof(ring->layout));
	ret = setsockopt(sock, SOL_PACKET, PACKET_TX_RING, &ring->layout,
			 sizeof(ring->layout));
	if (unlikely(ret))
		panic("Cannot destroy the TX_RING: %s!\n", strerror(errno));

	xfree(ring->frames);
}

void setup_tx_ring_layout(int sock, struct ring *ring, unsigned int size,
			  int jumbo_support)
{
	fmemset(&ring->layout, 0, sizeof(ring->layout));

	ring->layout.tp_block_size = (jumbo_support ?
				      getpagesize() << 4 :
				      getpagesize() << 2);
	ring->layout.tp_frame_size = (jumbo_support ?
				      TPACKET_ALIGNMENT << 12 :
				      TPACKET_ALIGNMENT << 7);
	ring->layout.tp_block_nr = size / ring->layout.tp_block_size;
	ring->layout.tp_frame_nr = ring->layout.tp_block_size /
				   ring->layout.tp_frame_size *
				   ring->layout.tp_block_nr;

	bug_on(ring->layout.tp_block_size < ring->layout.tp_frame_size);
	bug_on((ring->layout.tp_block_size % ring->layout.tp_frame_size) != 0);
	bug_on((ring->layout.tp_block_size % getpagesize()) != 0);
}

void create_tx_ring(int sock, struct ring *ring, int verbose)
{
	int ret;

	set_sockopt_tpacket_v2(sock);
retry:
	ret = setsockopt(sock, SOL_PACKET, PACKET_TX_RING, &ring->layout,
			 sizeof(ring->layout));
	if (errno == ENOMEM && ring->layout.tp_block_nr > 1) {
		ring->layout.tp_block_nr >>= 1;
		ring->layout.tp_frame_nr = ring->layout.tp_block_size / 
					   ring->layout.tp_frame_size * 
					   ring->layout.tp_block_nr;
		goto retry;
	}

	if (ret < 0)
		panic("Cannot allocate TX_RING!\n");

	ring->mm_len = ring->layout.tp_block_size * ring->layout.tp_block_nr;

	if (verbose) {
		printf("TX: %.2Lf MiB, %u Frames, each %u Byte allocated\n",
		       (long double) ring->mm_len / (1 << 20),
		       ring->layout.tp_frame_nr, ring->layout.tp_frame_size);
	}
}

void mmap_tx_ring(int sock, struct ring *ring)
{
	ring->mm_space = mmap(0, ring->mm_len, PROT_READ | PROT_WRITE,
			      MAP_SHARED | MAP_LOCKED | MAP_POPULATE, sock, 0);
	if (ring->mm_space == MAP_FAILED) {
		destroy_tx_ring(sock, ring);
		panic("Cannot mmap TX_RING!\n");
	}
}

void alloc_tx_ring_frames(struct ring *ring)
{
	int i;
	size_t len = ring->layout.tp_frame_nr * sizeof(*ring->frames);

	ring->frames = xmalloc_aligned(len, CO_CACHE_LINE_SIZE);
	fmemset(ring->frames, 0, len);

	for (i = 0; i < ring->layout.tp_frame_nr; ++i) {
		ring->frames[i].iov_len = ring->layout.tp_frame_size;
		ring->frames[i].iov_base = ring->mm_space +
					   (i * ring->layout.tp_frame_size);
	}
}

void bind_tx_ring(int sock, struct ring *ring, int ifindex)
{
	int ret;

	fmemset(&ring->s_ll, 0, sizeof(ring->s_ll));

	ring->s_ll.sll_family = AF_PACKET;
	ring->s_ll.sll_protocol = htons(ETH_P_ALL);
	ring->s_ll.sll_ifindex = ifindex;
	ring->s_ll.sll_hatype = 0;
	ring->s_ll.sll_halen = 0;
	ring->s_ll.sll_pkttype = 0;

	ret = bind(sock, (struct sockaddr *) &ring->s_ll, sizeof(ring->s_ll));
	if (ret < 0) {
		destroy_tx_ring(sock, ring);
		panic("Cannot bind TX_RING!\n");
	}
}