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