1/*
2 * Copyright 2013 Google Inc.
3 * Author: Willem de Bruijn <willemb@google.com>
4 *         Daniel Borkmann <dborkman@redhat.com>
5 *
6 * License (GPLv2):
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#ifndef PSOCK_LIB_H
23#define PSOCK_LIB_H
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <string.h>
28#include <arpa/inet.h>
29#include <unistd.h>
30
31#define DATA_LEN			100
32#define DATA_CHAR			'a'
33#define DATA_CHAR_1			'b'
34
35#define PORT_BASE			8000
36
37#ifndef __maybe_unused
38# define __maybe_unused		__attribute__ ((__unused__))
39#endif
40
41static __maybe_unused void pair_udp_setfilter(int fd)
42{
43	/* the filter below checks for all of the following conditions that
44	 * are based on the contents of create_payload()
45	 *  ether type 0x800 and
46	 *  ip proto udp     and
47	 *  skb->len == DATA_LEN and
48	 *  udp[38] == 'a' or udp[38] == 'b'
49	 * It can be generated from the following bpf_asm input:
50	 *	ldh [12]
51	 *	jne #0x800, drop	; ETH_P_IP
52	 *	ldb [23]
53	 *	jneq #17, drop		; IPPROTO_UDP
54	 *	ld len			; ld skb->len
55	 *	jlt #100, drop		; DATA_LEN
56	 *	ldb [80]
57	 *	jeq #97, pass		; DATA_CHAR
58	 *	jne #98, drop		; DATA_CHAR_1
59	 *	pass:
60	 *	  ret #-1
61	 *	drop:
62	 *	  ret #0
63	 */
64	struct sock_filter bpf_filter[] = {
65		{ 0x28,  0,  0, 0x0000000c },
66		{ 0x15,  0,  8, 0x00000800 },
67		{ 0x30,  0,  0, 0x00000017 },
68		{ 0x15,  0,  6, 0x00000011 },
69		{ 0x80,  0,  0, 0000000000 },
70		{ 0x35,  0,  4, 0x00000064 },
71		{ 0x30,  0,  0, 0x00000050 },
72		{ 0x15,  1,  0, 0x00000061 },
73		{ 0x15,  0,  1, 0x00000062 },
74		{ 0x06,  0,  0, 0xffffffff },
75		{ 0x06,  0,  0, 0000000000 },
76	};
77	struct sock_fprog bpf_prog;
78
79	bpf_prog.filter = bpf_filter;
80	bpf_prog.len = sizeof(bpf_filter) / sizeof(struct sock_filter);
81
82	if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf_prog,
83		       sizeof(bpf_prog))) {
84		perror("setsockopt SO_ATTACH_FILTER");
85		exit(1);
86	}
87}
88
89static __maybe_unused void pair_udp_open(int fds[], uint16_t port)
90{
91	struct sockaddr_in saddr, daddr;
92
93	fds[0] = socket(PF_INET, SOCK_DGRAM, 0);
94	fds[1] = socket(PF_INET, SOCK_DGRAM, 0);
95	if (fds[0] == -1 || fds[1] == -1) {
96		fprintf(stderr, "ERROR: socket dgram\n");
97		exit(1);
98	}
99
100	memset(&saddr, 0, sizeof(saddr));
101	saddr.sin_family = AF_INET;
102	saddr.sin_port = htons(port);
103	saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
104
105	memset(&daddr, 0, sizeof(daddr));
106	daddr.sin_family = AF_INET;
107	daddr.sin_port = htons(port + 1);
108	daddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
109
110	/* must bind both to get consistent hash result */
111	if (bind(fds[1], (void *) &daddr, sizeof(daddr))) {
112		perror("bind");
113		exit(1);
114	}
115	if (bind(fds[0], (void *) &saddr, sizeof(saddr))) {
116		perror("bind");
117		exit(1);
118	}
119	if (connect(fds[0], (void *) &daddr, sizeof(daddr))) {
120		perror("connect");
121		exit(1);
122	}
123}
124
125static __maybe_unused void pair_udp_send_char(int fds[], int num, char payload)
126{
127	char buf[DATA_LEN], rbuf[DATA_LEN];
128
129	memset(buf, payload, sizeof(buf));
130	while (num--) {
131		/* Should really handle EINTR and EAGAIN */
132		if (write(fds[0], buf, sizeof(buf)) != sizeof(buf)) {
133			fprintf(stderr, "ERROR: send failed left=%d\n", num);
134			exit(1);
135		}
136		if (read(fds[1], rbuf, sizeof(rbuf)) != sizeof(rbuf)) {
137			fprintf(stderr, "ERROR: recv failed left=%d\n", num);
138			exit(1);
139		}
140		if (memcmp(buf, rbuf, sizeof(buf))) {
141			fprintf(stderr, "ERROR: data failed left=%d\n", num);
142			exit(1);
143		}
144	}
145}
146
147static __maybe_unused void pair_udp_send(int fds[], int num)
148{
149	return pair_udp_send_char(fds, num, DATA_CHAR);
150}
151
152static __maybe_unused void pair_udp_close(int fds[])
153{
154	close(fds[0]);
155	close(fds[1]);
156}
157
158#endif /* PSOCK_LIB_H */
159