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