1 /*
2 * scamper_udp6.c
3 *
4 * $Id: scamper_udp6.c,v 1.62 2020/06/12 23:57:02 mjl Exp $
5 *
6 * Copyright (C) 2003-2006 Matthew Luckie
7 * Copyright (C) 2006-2010 The University of Waikato
8 * Copyright (C) 2020 Matthew Luckie
9 * Author: Matthew Luckie
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, version 2.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 #include "internal.h"
30
31 #include "scamper_addr.h"
32 #include "scamper_dl.h"
33 #include "scamper_probe.h"
34 #include "scamper_ip6.h"
35 #include "scamper_udp6.h"
36 #include "scamper_icmp_resp.h"
37 #include "scamper_fds.h"
38
39 #include "scamper_debug.h"
40 #include "utils.h"
41
scamper_udp6_cksum(scamper_probe_t * probe)42 uint16_t scamper_udp6_cksum(scamper_probe_t *probe)
43 {
44 uint16_t *w, tmp;
45 int i, sum = 0;
46
47 /* compute the checksum over the psuedo header */
48 w = (uint16_t *)probe->pr_ip_src->addr;
49 sum += *w++; sum += *w++; sum += *w++; sum += *w++;
50 sum += *w++; sum += *w++; sum += *w++; sum += *w++;
51 w = (uint16_t *)probe->pr_ip_dst->addr;
52 sum += *w++; sum += *w++; sum += *w++; sum += *w++;
53 sum += *w++; sum += *w++; sum += *w++; sum += *w++;
54 sum += htons(probe->pr_len + 8);
55 sum += htons(IPPROTO_UDP);
56
57 /* main UDP header */
58 sum += htons(probe->pr_udp_sport);
59 sum += htons(probe->pr_udp_dport);
60 sum += htons(probe->pr_len + 8);
61
62 /* compute the checksum over the payload of the UDP message */
63 w = (uint16_t *)probe->pr_data;
64 for(i = probe->pr_len; i > 1; i -= 2)
65 {
66 sum += *w++;
67 }
68 if(i != 0)
69 {
70 sum += ((uint8_t *)w)[0];
71 }
72
73 /* fold the checksum */
74 sum = (sum >> 16) + (sum & 0xffff);
75 sum += (sum >> 16);
76
77 if((tmp = ~sum) == 0)
78 {
79 tmp = 0xffff;
80 }
81
82 return tmp;
83 }
84
scamper_udp6_build(scamper_probe_t * probe,uint8_t * buf,size_t * len)85 int scamper_udp6_build(scamper_probe_t *probe, uint8_t *buf, size_t *len)
86 {
87 struct ip6_hdr *ip6;
88 struct udphdr *udp;
89 size_t ip6hlen, req;
90
91 /* build the IPv6 header */
92 ip6hlen = *len;
93 scamper_ip6_build(probe, buf, &ip6hlen);
94
95 /* calculate the total number of bytes required for this packet */
96 req = ip6hlen + 8 + probe->pr_len;
97
98 if(req <= *len)
99 {
100 /* calculate and record the plen value */
101 ip6 = (struct ip6_hdr *)buf;
102 ip6->ip6_plen = htons(ip6hlen - 40 + 8 + probe->pr_len);
103
104 udp = (struct udphdr *)(buf + ip6hlen);
105 udp->uh_sport = htons(probe->pr_udp_sport);
106 udp->uh_dport = htons(probe->pr_udp_dport);
107 udp->uh_ulen = htons(sizeof(struct udphdr) + probe->pr_len);
108 udp->uh_sum = scamper_udp6_cksum(probe);
109
110 /* if there is data to include in the payload, copy it in now */
111 if(probe->pr_len != 0)
112 {
113 memcpy(buf + ip6hlen + 8, probe->pr_data, probe->pr_len);
114 }
115
116 *len = req;
117 return 0;
118 }
119
120 *len = req;
121 return -1;
122 }
123
124 /*
125 * scamper_udp6_probe:
126 *
127 * given the address, hop limit, destination UDP port number, and size, send
128 * a UDP probe packet encapsulated in an IPv6 header.
129 *
130 * the size parameter is useful when doing path MTU discovery, and represents
131 * how large the packet should be including IPv6 and UDP headers
132 *
133 * this function returns 0 on success, -1 otherwise
134 */
scamper_udp6_probe(scamper_probe_t * probe)135 int scamper_udp6_probe(scamper_probe_t *probe)
136 {
137 struct sockaddr_in6 sin6;
138 int i, j, k;
139 char addr[128];
140
141 assert(probe != NULL);
142 assert(probe->pr_ip_proto == IPPROTO_UDP);
143 assert(probe->pr_ip_dst != NULL);
144 assert(probe->pr_ip_src != NULL);
145 assert(probe->pr_len != 0 || probe->pr_data == NULL);
146
147 i = probe->pr_ip_ttl;
148 if(setsockopt(probe->pr_fd,
149 IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&i, sizeof(i)) == -1)
150 {
151 printerror(__func__, "could not set hlim to %d", i);
152 return -1;
153 }
154
155 sockaddr_compose((struct sockaddr *)&sin6, AF_INET6,
156 probe->pr_ip_dst->addr, probe->pr_udp_dport);
157
158 /* get the transmit time immediately before we send the packet */
159 gettimeofday_wrap(&probe->pr_tx);
160
161 /*
162 * if we are using RECVERR socket, then we might need to try probing
163 * multiple times to get the packet to send.
164 */
165 if((probe->pr_flags & SCAMPER_PROBE_FLAG_RXERR) == 0)
166 k = 1;
167 else
168 k = 5;
169
170 for(j=0; j<k; j++)
171 {
172 i = sendto(probe->pr_fd, probe->pr_data, probe->pr_len, 0,
173 (struct sockaddr *)&sin6, sizeof(struct sockaddr_in6));
174
175 /*
176 * if we sent the probe successfully, there is nothing more to
177 * do here
178 */
179 if(i == probe->pr_len)
180 return 0;
181 else if(i != -1)
182 break;
183 }
184
185 /* get a copy of the errno variable as it is immediately after the sendto */
186 probe->pr_errno = errno;
187
188 /* error condition, could not send the packet at all */
189 if(i == -1)
190 {
191 printerror(__func__, "could not send to %s (%d hlim, %d dport, %d len)",
192 scamper_addr_tostr(probe->pr_ip_dst, addr, sizeof(addr)),
193 probe->pr_ip_ttl, probe->pr_udp_dport, probe->pr_len);
194 }
195 /* error condition, sent a portion of the probe */
196 else
197 {
198 printerror_msg(__func__, "sent %d bytes of %d byte packet to %s",
199 i, (int)probe->pr_len,
200 scamper_addr_tostr(probe->pr_ip_dst, addr, sizeof(addr)));
201 }
202
203 return -1;
204 }
205
206 #if defined(IPV6_RECVERR)
scamper_udp6_read_err(int fd,scamper_icmp_resp_t * resp)207 static int scamper_udp6_read_err(int fd, scamper_icmp_resp_t *resp)
208 {
209 struct sock_extended_err *ee = NULL;
210 struct sockaddr_in6 from, *sin6;
211 struct cmsghdr *cm;
212 struct msghdr msg;
213 struct iovec iov;
214 ssize_t pbuflen;
215 uint8_t ctrlbuf[2048];
216 uint8_t rxbuf[65536];
217
218 memset(&iov, 0, sizeof(iov));
219 iov.iov_base = (caddr_t)rxbuf;
220 iov.iov_len = sizeof(rxbuf);
221
222 msg.msg_name = (caddr_t)&from;
223 msg.msg_namelen = sizeof(from);
224 msg.msg_iov = &iov;
225 msg.msg_iovlen = 1;
226 msg.msg_control = (caddr_t)ctrlbuf;
227 msg.msg_controllen = sizeof(ctrlbuf);
228
229 /* two calls to recvmsg, first one looking in the error queue */
230 if((pbuflen = recvmsg(fd, &msg, MSG_ERRQUEUE)) == -1)
231 {
232 recvmsg(fd, &msg, 0);
233 return -1;
234 }
235
236 if(msg.msg_controllen < sizeof(struct cmsghdr))
237 return -1;
238
239 memset(resp, 0, sizeof(scamper_icmp_resp_t));
240 resp->ir_ip_ttl = -1;
241
242 cm = (struct cmsghdr *)CMSG_FIRSTHDR(&msg);
243 while(cm != NULL)
244 {
245 if(cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_TIMESTAMP)
246 {
247 timeval_cpy(&resp->ir_rx, (struct timeval *)CMSG_DATA(cm));
248 resp->ir_flags |= SCAMPER_ICMP_RESP_FLAG_KERNRX;
249 }
250 else if(cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_RECVERR)
251 {
252 ee = (struct sock_extended_err *)CMSG_DATA(cm);
253 if(ee->ee_origin == SO_EE_ORIGIN_ICMP6)
254 {
255 resp->ir_icmp_type = ee->ee_type;
256 resp->ir_icmp_code = ee->ee_code;
257 sin6 = (struct sockaddr_in6 *)SO_EE_OFFENDER(ee);
258 memcpy(&resp->ir_ip_src.v6, &sin6->sin6_addr,
259 sizeof(struct in6_addr));
260 }
261 }
262 #if defined(IPV6_HOPLIMIT)
263 else if(cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_HOPLIMIT)
264 {
265 resp->ir_ip_ttl = *((uint8_t *)CMSG_DATA(cm));
266 }
267 #endif
268 cm = (struct cmsghdr *)CMSG_NXTHDR(&msg, cm);
269 }
270
271 if(ee == NULL ||
272 (resp->ir_icmp_type != ICMP6_TIME_EXCEEDED &&
273 resp->ir_icmp_type != ICMP6_DST_UNREACH))
274 return -1;
275
276 resp->ir_fd = fd;
277 resp->ir_af = AF_INET6;
278 memcpy(&resp->ir_inner_ip_dst.v6, &from.sin6_addr, sizeof(struct in6_addr));
279 resp->ir_flags |= SCAMPER_ICMP_RESP_FLAG_RXERR;
280 resp->ir_flags |= SCAMPER_ICMP_RESP_FLAG_INNER_IP;
281 resp->ir_inner_udp_dport = ntohs(from.sin6_port);
282 resp->ir_inner_ip_proto = IPPROTO_UDP;
283 resp->ir_inner_udp_data = rxbuf;
284 resp->ir_inner_udp_datalen = pbuflen;
285 resp->ir_inner_ip_size = 40 + 8 + pbuflen;
286
287 if((resp->ir_flags & SCAMPER_ICMP_RESP_FLAG_KERNRX) == 0)
288 gettimeofday_wrap(&resp->ir_rx);
289
290 return 0;
291 }
292
293 #endif
294
scamper_udp6_read_err_cb(int fd,void * param)295 void scamper_udp6_read_err_cb(int fd, void *param)
296 {
297 #if defined(IPV6_RECVERR)
298 scamper_icmp_resp_t ir;
299 memset(&ir, 0, sizeof(ir));
300 if(scamper_udp6_read_err(fd, &ir) == 0 &&
301 scamper_fd_sport((const scamper_fd_t *)param,&ir.ir_inner_udp_sport) == 0)
302 scamper_icmp_resp_handle(&ir);
303 scamper_icmp_resp_clean(&ir);
304 #endif
305 return;
306 }
307
scamper_udp6_close(int fd)308 void scamper_udp6_close(int fd)
309 {
310 #ifndef _WIN32
311 close(fd);
312 #else
313 closesocket(fd);
314 #endif
315 return;
316 }
317
scamper_udp6_open(const void * addr,int sport)318 int scamper_udp6_open(const void *addr, int sport)
319 {
320 struct sockaddr_in6 sin6;
321 char buf[128];
322 int opt, fd = -1;
323
324 if((fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1)
325 {
326 printerror(__func__, "could not open socket");
327 goto err;
328 }
329
330 #ifdef IPV6_V6ONLY
331 opt = 1;
332 if(setsockopt(fd,IPPROTO_IPV6,IPV6_V6ONLY, (char *)&opt,sizeof(opt)) == -1)
333 {
334 printerror(__func__, "could not set IPV6_V6ONLY");
335 goto err;
336 }
337 #endif
338
339 sockaddr_compose((struct sockaddr *)&sin6, AF_INET6, addr, sport);
340 if(bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
341 {
342 if(addr == NULL || addr_tostr(AF_INET6, addr, buf, sizeof(buf)) == NULL)
343 printerror(__func__, "could not bind port %d", sport);
344 else
345 printerror(__func__, "could not bind %s:%d", buf, sport);
346 goto err;
347 }
348
349 opt = 65535 + 128;
350 if(setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&opt, sizeof(opt)) == -1)
351 {
352 printerror(__func__, "could not set SO_SNDBUF");
353 return -1;
354 }
355
356 #if defined(IPV6_DONTFRAG)
357 opt = 1;
358 if(setsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG,
359 (char *)&opt, sizeof(opt)) == -1)
360 {
361 printerror(__func__, "could not set IPV6_DONTFRAG");
362 goto err;
363 }
364 #endif
365
366 return fd;
367
368 err:
369 if(fd != -1) scamper_udp6_close(fd);
370 return -1;
371 }
372
373 #if defined(IPV6_RECVERR)
scamper_udp6_open_err(const void * addr,int sport)374 int scamper_udp6_open_err(const void *addr, int sport)
375 {
376 int opt, fd;
377
378 if((fd = scamper_udp6_open(addr, sport)) == -1)
379 return -1;
380
381 opt = 65535 + 128;
382 if(setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&opt, sizeof(opt)) != 0)
383 {
384 printerror(__func__, "could not set SO_RCVBUF");
385 goto err;
386 }
387
388 opt = 1;
389 if(setsockopt(fd, IPPROTO_IPV6, IPV6_RECVERR, &opt, sizeof(opt)) != 0)
390 {
391 printerror(__func__, "could not set IPV6_RECVERR");
392 goto err;
393 }
394
395 #if defined(IPV6_HOPLIMIT)
396 opt = 1;
397 if(setsockopt(fd,IPPROTO_IPV6,IPV6_HOPLIMIT,(char *)&opt,sizeof(opt)) != 0)
398 {
399 printerror(__func__, "could not set IPV6_HOPLIMIT");
400 }
401 #endif
402
403 #if defined(SO_TIMESTAMP)
404 opt = 1;
405 if(setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) != 0)
406 {
407 printerror(__func__, "could not set SO_TIMESTAMP");
408 goto err;
409 }
410 #endif
411
412 return fd;
413
414 err:
415 if(fd != -1) scamper_udp6_close(fd);
416 return -1;
417 }
418 #else
scamper_udp6_open_err(const void * addr,int sport)419 int scamper_udp6_open_err(const void *addr, int sport)
420 {
421 return -1;
422 }
423 #endif
424