1 /*
2    Raw socket (un) marshalling tests
3 
4    Copyright (C) Martin Schwenke  2018
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "replace.h"
21 
22 #include <assert.h>
23 
24 /* For ether_aton() */
25 #ifdef _AIX
26 #include <arpa/inet.h>
27 #endif
28 #ifdef __FreeBSD__
29 #include <net/ethernet.h>
30 #endif
31 #ifdef linux
32 #include <netinet/ether.h>
33 #endif
34 
35 #include "common/system_socket.c"
36 
37 #include "protocol/protocol_util.h"
38 
test_types(void)39 static void test_types(void)
40 {
41 	/*
42 	 * We use this struct in the code but don't pack it due to
43 	 * portability concerns.  It should have no padding.
44 	 */
45 	struct {
46 		struct ip ip;
47 		struct tcphdr tcp;
48 	} ip4pkt;
49 
50 	assert(sizeof(ip4pkt) == sizeof(struct ip) + sizeof(struct tcphdr));
51 }
52 
53 #ifdef HAVE_PACKETSOCKET
54 
test_arp(const char * addr_str,const char * hwaddr_str,bool reply)55 static void test_arp(const char *addr_str, const char *hwaddr_str, bool reply)
56 {
57 	ctdb_sock_addr addr;
58 	struct ether_addr *hw, *dhw;
59 	uint8_t buf[512];
60 	size_t buflen = sizeof(buf);
61 	size_t len;
62 	ssize_t num_written;
63 	int ret;
64 
65 	ret = ctdb_sock_addr_from_string(addr_str, &addr, false);
66 	assert(ret == 0);
67 
68 	hw = ether_aton(hwaddr_str);
69 	assert(hw != NULL);
70 
71 	switch (addr.ip.sin_family) {
72 	case AF_INET:
73 		ret = arp_build(buf, buflen, &addr.ip, hw, reply, &dhw, &len);
74 		break;
75 	case AF_INET6:
76 		ret = ip6_na_build(buf, buflen, &addr.ip6, hw, &dhw, &len);
77 		break;
78 	default:
79 		abort();
80 	}
81 
82 	assert(ret == 0);
83 
84 	num_written = write(STDOUT_FILENO, buf, len);
85 	assert(num_written != -1 && (size_t)num_written == len);
86 }
87 
88 #else /* HAVE_PACKETSOCKET  */
89 
test_arp(const char * addr_str,const char * hwaddr_str,bool reply)90 static void test_arp(const char *addr_str, const char *hwaddr_str, bool reply)
91 {
92 	fprintf(stderr, "PACKETSOCKET not supported\n");
93 }
94 
95 #endif /* HAVE_PACKETSOCKET */
96 
test_tcp(const char * src_str,const char * dst_str,const char * seq_str,const char * ack_str,const char * rst_str)97 static void test_tcp(const char *src_str,
98 		     const char *dst_str,
99 		     const char *seq_str,
100 		     const char *ack_str,
101 		     const char *rst_str)
102 {
103 	ctdb_sock_addr src, dst;
104 	uint32_t seq, ack;
105 	int rst;
106 	uint8_t buf[512];
107 	struct ether_header *eth;
108 	size_t expected_len, len;
109 	ssize_t num_written;
110 	char src_str_out[64], dst_str_out[64];
111 	uint32_t seq_out, ack_out;
112 	int rst_out = 0;
113 	uint16_t window;
114 	int ret;
115 
116 	ret = ctdb_sock_addr_from_string(src_str, &src, true);
117 	assert(ret == 0);
118 
119 	ret = ctdb_sock_addr_from_string(dst_str, &dst, true);
120 	assert(ret == 0);
121 
122 	seq = atoi(seq_str);
123 	ack = atoi(ack_str);
124 	rst = atoi(rst_str);
125 
126 	/* Need to fake this up */
127 	eth = (struct ether_header *) buf;
128 	memset(eth, 0, sizeof(*eth));
129 
130 	switch (src.ip.sin_family) {
131 	case AF_INET:
132 		eth->ether_type = htons(ETHERTYPE_IP);
133 		expected_len = 40;
134 		ret = tcp4_build(buf + sizeof(struct ether_header),
135 				 sizeof(buf) - sizeof(struct ether_header),
136 				 &src.ip,
137 				 &dst.ip,
138 				 seq,
139 				 ack,
140 				 rst,
141 				 &len);
142 		break;
143 	case AF_INET6:
144 		eth->ether_type = htons(ETHERTYPE_IP6);
145 		expected_len = 60;
146 		ret = tcp6_build(buf + sizeof(struct ether_header),
147 				 sizeof(buf) - sizeof(struct ether_header),
148 				 &src.ip6,
149 				 &dst.ip6,
150 				 seq,
151 				 ack,
152 				 rst,
153 				 &len);
154 		break;
155 	default:
156 		abort();
157 	}
158 
159 	assert(ret == 0);
160 	assert(len == expected_len);
161 
162 	num_written = write(STDOUT_FILENO,
163 			    buf + sizeof(struct ether_header),
164 			    len);
165 	assert(num_written != -1 && (size_t)num_written == len);
166 
167 	switch (ntohs(eth->ether_type)) {
168 	case ETHERTYPE_IP:
169 		ret = tcp4_extract(buf + sizeof(struct ether_header),
170 				   len,
171 				   &src.ip,
172 				   &dst.ip,
173 				   &ack_out,
174 				   &seq_out,
175 				   &rst_out,
176 				   &window);
177 		break;
178 	case ETHERTYPE_IP6:
179 		ret = tcp6_extract(buf + sizeof(struct ether_header),
180 				   len,
181 				   &src.ip6,
182 				   &dst.ip6,
183 				   &ack_out,
184 				   &seq_out,
185 				   &rst_out,
186 				   &window);
187 		break;
188 	default:
189 		abort();
190 	}
191 
192 	assert(ret == 0);
193 
194 	assert(seq == seq_out);
195 	assert(ack == ack_out);
196 	assert((rst != 0) == (rst_out != 0));
197 	assert(window == htons(1234));
198 
199 	ret = ctdb_sock_addr_to_buf(src_str_out, sizeof(src_str_out),
200 				    &src, true);
201 	assert(ret == 0);
202 	ret = strcmp(src_str, src_str_out);
203 	assert(ret == 0);
204 
205 	ret = ctdb_sock_addr_to_buf(dst_str_out, sizeof(dst_str_out),
206 				    &dst, true);
207 	assert(ret == 0);
208 	ret = strcmp(dst_str, dst_str_out);
209 	assert(ret == 0);
210 }
211 
usage(const char * prog)212 static void usage(const char *prog)
213 {
214 	fprintf(stderr, "usage: %s <cmd> [<arg> ...]\n", prog);
215 	fprintf(stderr, "  commands:\n");
216 	fprintf(stderr, "    types\n");
217 	fprintf(stderr, "    arp <ipaddr> <hwaddr> [reply]\n");
218 	fprintf(stderr, "    tcp <src> <dst> <seq> <ack> <rst>\n");
219 
220 	exit(1);
221 }
222 
main(int argc,char ** argv)223 int main(int argc, char **argv)
224 {
225 
226 	if (argc < 2) {
227 		usage(argv[0]);
228 	}
229 
230 	if (strcmp(argv[1], "types") == 0) {
231 		test_types();
232 	} else if (strcmp(argv[1], "arp") == 0) {
233 		/*
234 		 * Extra arg indicates that a reply should be
235 		 * constructed for IPv4 - value is ignored
236 		 */
237 		if (argc != 4 && argc != 5) {
238 			usage(argv[0]);
239 		}
240 		test_arp(argv[2], argv[3], (argc == 5));
241 	} else if (strcmp(argv[1], "tcp") == 0) {
242 		if (argc != 7) {
243 			usage(argv[0]);
244 		}
245 		test_tcp(argv[2], argv[3], argv[4], argv[5], argv[6]);
246 	} else {
247 		usage(argv[0]);
248 	}
249 
250 	return 0;
251 }
252