1 /*
2  * scamper_udp4.c
3  *
4  * $Id: scamper_udp4.c,v 1.75 2020/03/17 07:32:16 mjl Exp $
5  *
6  * Copyright (C) 2003-2006 Matthew Luckie
7  * Copyright (C) 2006-2010 The University of Waikato
8  * Author: Matthew Luckie
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, version 2.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 #include "internal.h"
29 
30 #include "scamper_addr.h"
31 #include "scamper_dl.h"
32 #include "scamper_probe.h"
33 #include "scamper_ip4.h"
34 #include "scamper_udp4.h"
35 #include "scamper_privsep.h"
36 #include "scamper_debug.h"
37 #include "utils.h"
38 
39 /*
40  * these variables are used to store a packet buffer that is allocated
41  * in the scamper_udp4_probe function large enough for the largest probe
42  * the routine sends
43  */
44 static uint8_t *pktbuf = NULL;
45 static size_t   pktbuf_len = 0;
46 
scamper_udp4_cksum(scamper_probe_t * probe)47 uint16_t scamper_udp4_cksum(scamper_probe_t *probe)
48 {
49   uint16_t tmp, *w;
50   int i, sum = 0;
51 
52   /* compute the checksum over the psuedo header */
53   w = (uint16_t *)probe->pr_ip_src->addr;
54   sum += *w++; sum += *w++;
55   w = (uint16_t *)probe->pr_ip_dst->addr;
56   sum += *w++; sum += *w++;
57   sum += htons(IPPROTO_UDP);
58   sum += htons(probe->pr_len + 8);
59 
60   /* main UDP header */
61   sum += htons(probe->pr_udp_sport);
62   sum += htons(probe->pr_udp_dport);
63   sum += htons(probe->pr_len + 8);
64 
65   /* compute the checksum over the payload of the UDP message */
66   w = (uint16_t *)probe->pr_data;
67   for(i = probe->pr_len; i > 1; i -= 2)
68     {
69       sum += *w++;
70     }
71   if(i != 0)
72     {
73       sum += ((uint8_t *)w)[0];
74     }
75 
76   /* fold the checksum */
77   sum  = (sum >> 16) + (sum & 0xffff);
78   sum += (sum >> 16);
79 
80   if((tmp = ~sum) == 0)
81     {
82       tmp = 0xffff;
83     }
84 
85   return tmp;
86 }
87 
udp4_build(scamper_probe_t * probe,uint8_t * buf)88 static void udp4_build(scamper_probe_t *probe, uint8_t *buf)
89 {
90   struct udphdr *udp = (struct udphdr *)buf;
91 
92   udp->uh_sport = htons(probe->pr_udp_sport);
93   udp->uh_dport = htons(probe->pr_udp_dport);
94   udp->uh_ulen  = htons(8 + probe->pr_len);
95   udp->uh_sum = scamper_udp4_cksum(probe);
96 
97   /* if there is data to include in the payload, copy it in now */
98   if(probe->pr_len > 0)
99     {
100       memcpy(buf + 8, probe->pr_data, probe->pr_len);
101     }
102 
103   return;
104 }
105 
scamper_udp4_build(scamper_probe_t * probe,uint8_t * buf,size_t * len)106 int scamper_udp4_build(scamper_probe_t *probe, uint8_t *buf, size_t *len)
107 {
108   size_t ip4hlen, req;
109   int rc = 0;
110 
111   ip4hlen = *len;
112   scamper_ip4_build(probe, buf, &ip4hlen);
113   req = ip4hlen + 8 + probe->pr_len;
114 
115   if(req <= *len)
116     udp4_build(probe, buf + ip4hlen);
117   else
118     rc = -1;
119 
120   *len = req;
121   return rc;
122 }
123 
scamper_udp4_probe(scamper_probe_t * probe)124 int scamper_udp4_probe(scamper_probe_t *probe)
125 {
126   struct sockaddr_in  sin4;
127   int                 i;
128   char                addr[128];
129   size_t              ip4hlen, len, tmp;
130   uint8_t            *buf;
131 
132 #if !defined(IP_HDR_HTONS)
133   struct ip          *ip;
134 #endif
135 
136   assert(probe != NULL);
137   assert(probe->pr_ip_proto == IPPROTO_UDP);
138   assert(probe->pr_ip_dst != NULL);
139   assert(probe->pr_ip_src != NULL);
140   assert(probe->pr_len > 0 || probe->pr_data == NULL);
141 
142   scamper_ip4_hlen(probe, &ip4hlen);
143 
144   /* compute length, for sake of readability */
145   len = ip4hlen + sizeof(struct udphdr) + probe->pr_len;
146 
147   if(pktbuf_len < len)
148     {
149       if((buf = realloc(pktbuf, len)) == NULL)
150 	{
151 	  printerror(__func__, "could not realloc");
152 	  return -1;
153 	}
154       pktbuf     = buf;
155       pktbuf_len = len;
156     }
157 
158   tmp = len;
159   scamper_ip4_build(probe, pktbuf, &tmp);
160 
161 #if !defined(IP_HDR_HTONS)
162   ip = (struct ip *)pktbuf;
163   ip->ip_len = ntohs(ip->ip_len);
164   ip->ip_off = ntohs(ip->ip_off);
165 #endif
166 
167   udp4_build(probe, pktbuf + ip4hlen);
168 
169   sockaddr_compose((struct sockaddr *)&sin4, AF_INET,
170 		   probe->pr_ip_dst->addr, probe->pr_udp_dport);
171 
172   /* get the transmit time immediately before we send the packet */
173   gettimeofday_wrap(&probe->pr_tx);
174 
175   i = sendto(probe->pr_fd, pktbuf, len, 0, (struct sockaddr *)&sin4,
176 	     sizeof(struct sockaddr_in));
177 
178   if(i < 0)
179     {
180       /* error condition, could not send the packet at all */
181       probe->pr_errno = errno;
182       printerror(__func__, "could not send to %s (%d ttl, %d dport, %d len)",
183 		 scamper_addr_tostr(probe->pr_ip_dst, addr, sizeof(addr)),
184 		 probe->pr_ip_ttl, probe->pr_udp_dport, len);
185       return -1;
186     }
187   else if((size_t)i != len)
188     {
189       /* error condition, sent a portion of the probe */
190       printerror_msg(__func__, "sent %d bytes of %d byte packet to %s",
191 		     i, (int)len,
192 		     scamper_addr_tostr(probe->pr_ip_dst, addr, sizeof(addr)));
193       return -1;
194     }
195 
196   return 0;
197 }
198 
scamper_udp4_cleanup()199 void scamper_udp4_cleanup()
200 {
201   if(pktbuf != NULL)
202     {
203       free(pktbuf);
204       pktbuf = NULL;
205     }
206 
207   return;
208 }
209 
scamper_udp4_close(int fd)210 void scamper_udp4_close(int fd)
211 {
212 #ifndef _WIN32
213   close(fd);
214 #else
215   closesocket(fd);
216 #endif
217   return;
218 }
219 
scamper_udp4_opendgram(const void * addr,int sport)220 int scamper_udp4_opendgram(const void *addr, int sport)
221 {
222   struct sockaddr_in sin4;
223   char tmp[32];
224   int fd, opt;
225 
226   if((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
227     {
228       printerror(__func__, "could not open socket");
229       goto err;
230     }
231 
232   opt = 1;
233   if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) != 0)
234     {
235       printerror(__func__, "could not set SO_REUSEADDR");
236       goto err;
237     }
238 
239   sockaddr_compose((struct sockaddr *)&sin4, AF_INET, addr, sport);
240   if(bind(fd, (struct sockaddr *)&sin4, sizeof(sin4)) == -1)
241     {
242       printerror(__func__, "could not bind %s",
243 		 sockaddr_tostr((struct sockaddr *)&sin4, tmp, sizeof(tmp)));
244       goto err;
245     }
246 
247   return fd;
248 
249  err:
250   if(fd != -1) scamper_udp4_close(fd);
251   return -1;
252 }
253 
scamper_udp4_openraw_fd(const void * addr)254 int scamper_udp4_openraw_fd(const void *addr)
255 {
256   struct sockaddr_in sin4;
257   int hdr, fd;
258   char tmp[32];
259 
260   if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1)
261     {
262       printerror(__func__, "could not open socket");
263       goto err;
264     }
265   hdr = 1;
266   if(setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (void *)&hdr, sizeof(hdr)) == -1)
267     {
268       printerror(__func__, "could not IP_HDRINCL");
269       goto err;
270     }
271   sockaddr_compose((struct sockaddr *)&sin4, AF_INET, addr, 0);
272   if(bind(fd, (struct sockaddr *)&sin4, sizeof(sin4)) == -1)
273     {
274       printerror(__func__, "could not bind %s",
275 		 sockaddr_tostr((struct sockaddr *)&sin4, tmp, sizeof(tmp)));
276       goto err;
277     }
278 
279   return fd;
280 
281  err:
282   if(fd != -1) scamper_udp4_close(fd);
283   return -1;
284 }
285 
scamper_udp4_openraw(const void * addr)286 int scamper_udp4_openraw(const void *addr)
287 {
288   int fd, opt;
289 
290 #if defined(WITHOUT_PRIVSEP)
291   fd = scamper_udp4_openraw_fd(addr);
292 #else
293   fd = scamper_privsep_open_rawudp(addr);
294 #endif
295   if(fd == -1)
296     return -1;
297 
298   opt = 65535 + 128;
299   if(setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&opt, sizeof(opt)) == -1)
300     {
301       printerror(__func__, "could not set SO_SNDBUF");
302       goto err;
303     }
304   return fd;
305 
306  err:
307   if(fd != -1) scamper_udp4_close(fd);
308   return -1;
309 }
310