1 /*
2  * scamper_ip4.c
3  *
4  * $Id: scamper_ip4.c,v 1.18 2020/03/17 07:32:16 mjl Exp $
5  *
6  * Copyright (C) 2009-2011 The University of Waikato
7  * Author: Matthew Luckie
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, version 2.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 #include "internal.h"
28 
29 #include "scamper_addr.h"
30 #include "scamper_dl.h"
31 #include "scamper_probe.h"
32 #include "scamper_ip4.h"
33 #include "scamper_tcp4.h"
34 #include "scamper_privsep.h"
35 #include "scamper_debug.h"
36 #include "utils.h"
37 
scamper_ip4_close(int fd)38 void scamper_ip4_close(int fd)
39 {
40 #ifndef _WIN32
41   close(fd);
42 #else
43   closesocket(fd);
44 #endif
45   return;
46 }
47 
scamper_ip4_openraw_fd(void)48 int scamper_ip4_openraw_fd(void)
49 {
50   int fd, hdr;
51   if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1)
52     {
53       printerror(__func__, "could not open socket");
54       goto err;
55     }
56   hdr = 1;
57   if(setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (void *)&hdr, sizeof(hdr)) == -1)
58     {
59       printerror(__func__, "could not IP_HDRINCL");
60       goto err;
61     }
62   return fd;
63 
64  err:
65   if(fd != -1) scamper_ip4_close(fd);
66   return -1;
67 }
68 
scamper_ip4_openraw(void)69 int scamper_ip4_openraw(void)
70 {
71 #if defined(WITHOUT_PRIVSEP)
72   return scamper_ip4_openraw_fd();
73 #else
74   return scamper_privsep_open_rawip();
75 #endif
76 }
77 
scamper_ip4_hlen(scamper_probe_t * pr,size_t * hlen)78 int scamper_ip4_hlen(scamper_probe_t *pr, size_t *hlen)
79 {
80   size_t ip4hlen = sizeof(struct ip);
81   scamper_probe_ipopt_t *opt;
82   int i;
83 
84   for(i=0; i<pr->pr_ipoptc; i++)
85     {
86       opt = &pr->pr_ipopts[i];
87       if(opt->type == SCAMPER_PROBE_IPOPTS_V4RR)
88 	{
89 	  /*
90 	   * want the ability to record at least one IP address otherwise
91 	   * the option is useless.
92 	   */
93 	  if(ip4hlen + 8 > 60)
94 	    goto err;
95 
96 	  /* for now assume this option fills the rest of the option space */
97 	  ip4hlen = 60;
98 	}
99       else if(opt->type == SCAMPER_PROBE_IPOPTS_V4TSPS)
100 	{
101 	  if(opt->opt_v4tsps_ipc < 1 || opt->opt_v4tsps_ipc > 4)
102 	    goto err;
103 
104 	  ip4hlen += (opt->opt_v4tsps_ipc * 4 * 2) + 4;
105 	  if(ip4hlen > 60)
106 	    goto err;
107 	}
108       else if(opt->type == SCAMPER_PROBE_IPOPTS_V4TSO)
109 	{
110 	  ip4hlen += 40;
111 	  if(ip4hlen > 60)
112 	    goto err;
113 	}
114       else if(opt->type == SCAMPER_PROBE_IPOPTS_V4TSAA)
115 	{
116 	  ip4hlen += 36;
117 	  if(ip4hlen > 60)
118 	    goto err;
119 	}
120       else if(opt->type == SCAMPER_PROBE_IPOPTS_QUICKSTART)
121 	{
122 	  ip4hlen += 8;
123 	  if(ip4hlen > 60)
124 	    goto err;
125 	}
126       else goto err;
127     }
128 
129   *hlen = ip4hlen;
130   return 0;
131 
132  err:
133   scamper_debug(__func__, "invalid IPv4 header specification");
134   return -1;
135 }
136 
scamper_ip4_build(scamper_probe_t * pr,uint8_t * buf,size_t * len)137 int scamper_ip4_build(scamper_probe_t *pr, uint8_t *buf, size_t *len)
138 {
139   scamper_probe_ipopt_t *opt;
140   struct ip *ip;
141   size_t off, ip4hlen;
142   int i, j;
143 
144   if(scamper_ip4_hlen(pr, &ip4hlen) != 0)
145     return -1;
146 
147   if(ip4hlen > *len)
148     {
149       *len = ip4hlen;
150       return -1;
151     }
152 
153   ip  = (struct ip *)buf;
154   off = sizeof(struct ip);
155 
156 #ifndef _WIN32
157   ip->ip_v   = 4;
158   ip->ip_hl  = (ip4hlen / 4);
159 #else
160   ip->ip_vhl = 0x40 | (ip4hlen / 4);
161 #endif
162 
163   if((pr->pr_ip_off & IP_OFFMASK) != 0)
164     ip->ip_len = htons(ip4hlen + pr->pr_len);
165   else if(pr->pr_ip_proto == IPPROTO_ICMP || pr->pr_ip_proto == IPPROTO_UDP)
166     ip->ip_len = htons(ip4hlen + 8 + pr->pr_len);
167   else if(pr->pr_ip_proto == IPPROTO_TCP)
168     ip->ip_len = htons(ip4hlen + scamper_tcp4_hlen(pr) + pr->pr_len);
169   else
170     {
171       scamper_debug(__func__, "unimplemented pr %d", pr->pr_ip_proto);
172       return -1;
173     }
174 
175   ip->ip_tos = pr->pr_ip_tos;
176   ip->ip_id  = htons(pr->pr_ip_id);
177   ip->ip_off = htons(pr->pr_ip_off);
178   ip->ip_ttl = pr->pr_ip_ttl;
179   ip->ip_p   = pr->pr_ip_proto;
180   ip->ip_sum = 0;
181   memcpy(&ip->ip_src, pr->pr_ip_src->addr, sizeof(ip->ip_src));
182   memcpy(&ip->ip_dst, pr->pr_ip_dst->addr, sizeof(ip->ip_dst));
183 
184   for(i=0; i<pr->pr_ipoptc; i++)
185     {
186       opt = &pr->pr_ipopts[i];
187       if(opt->type == SCAMPER_PROBE_IPOPTS_V4RR)
188 	{
189 	  memset(buf+off+3, 0, 37);
190 	  buf[off+0] = 7;
191 	  buf[off+1] = 39;
192 	  buf[off+2] = 4;
193 	  off = 60;
194 	}
195       else if(opt->type == SCAMPER_PROBE_IPOPTS_V4TSPS ||
196 	      opt->type == SCAMPER_PROBE_IPOPTS_V4TSO  ||
197 	      opt->type == SCAMPER_PROBE_IPOPTS_V4TSAA)
198 	{
199 	  buf[off+0] = 68;
200 	  buf[off+2] = 5;
201 
202 	  if(opt->type == SCAMPER_PROBE_IPOPTS_V4TSPS)
203 	    {
204 	      buf[off+1] = (opt->opt_v4tsps_ipc * 4 * 2) + 4;
205 	      buf[off+3] = 3;
206 	      off += 4;
207 	      for(j=0; j<opt->opt_v4tsps_ipc; j++)
208 		{
209 		  memcpy(buf+off, &opt->opt_v4tsps_ips[j], 4); off += 4;
210 		  memset(buf+off, 0, 4); off += 4;
211 		}
212 	    }
213 	  else if(opt->type == SCAMPER_PROBE_IPOPTS_V4TSO)
214 	    {
215 	      buf[off+1] = 40;
216 	      memset(buf+off+3, 0, 41);
217 	      off += 40;
218 	    }
219 	  else if(opt->type == SCAMPER_PROBE_IPOPTS_V4TSAA)
220 	    {
221 	      buf[off+1] = 36;
222 	      buf[off+3] = 1;
223 	      memset(buf+off+4, 0, 36);
224 	      off += 36;
225 	    }
226 	}
227       else if(opt->type == SCAMPER_PROBE_IPOPTS_QUICKSTART)
228 	{
229 	  assert(opt->opt_qs_func <= 0xf);
230 	  assert(opt->opt_qs_rate <= 0xf);
231 	  buf[off+0] = 25;
232 	  buf[off+1] = 8;
233 	  buf[off+2] = (opt->opt_qs_func << 4) | opt->opt_qs_rate;
234 	  buf[off+3] = opt->opt_qs_ttl;
235 	  bytes_htonl(&buf[off+4], opt->opt_qs_nonce << 2);
236 	  off += 8;
237 	}
238       else return -1;
239     }
240 
241   assert(off == ip4hlen);
242   ip->ip_sum = in_cksum(ip, ip4hlen);
243 
244   *len = off;
245   return 0;
246 }
247 
248 /*
249  * scamper_ip4_frag_build
250  *
251  * given an IPv4 fragment, build it.
252  */
scamper_ip4_frag_build(scamper_probe_t * probe,uint8_t * buf,size_t * len)253 int scamper_ip4_frag_build(scamper_probe_t *probe, uint8_t *buf, size_t *len)
254 {
255   size_t ip4hlen, req;
256   int rc = 0;
257 
258   /* build the IPv4 header */
259   ip4hlen = *len;
260   scamper_ip4_build(probe, buf, &ip4hlen);
261 
262   /* calculate the total number of bytes required for this packet */
263   req = ip4hlen + probe->pr_len;
264 
265   if(req > *len)
266     rc = -1;
267   else if(probe->pr_len != 0)
268     memcpy(buf + ip4hlen, probe->pr_data, probe->pr_len);
269 
270   *len = req;
271   return rc;
272 }
273