xref: /freebsd/sbin/ipf/ipsend/ip.c (revision d746ab215cc85d8f7ab05c5f866c338782c390ec)
1 /*	$FreeBSD$	*/
2 
3 /*
4  * ip.c (C) 1995-1998 Darren Reed
5  *
6  * See the IPFILTER.LICENCE file for details on licencing.
7  */
8 #if !defined(lint)
9 static const char sccsid[] = "%W% %G% (C)1995";
10 static const char rcsid[] = "@(#)$Id$";
11 #endif
12 #include <sys/param.h>
13 #include <sys/types.h>
14 #include <netinet/in_systm.h>
15 #include <sys/socket.h>
16 #include <net/if.h>
17 #include <netinet/in.h>
18 #include <netinet/ip.h>
19 #include <sys/param.h>
20 # include <net/route.h>
21 # include <netinet/if_ether.h>
22 # include <netinet/ip_var.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include "ipsend.h"
29 
30 
31 static	char	*ipbuf = NULL, *ethbuf = NULL;
32 
33 
34 u_short
35 chksum(u_short *buf, int len)
36 {
37 	u_long	sum = 0;
38 	int	nwords = len >> 1;
39 
40 	for(; nwords > 0; nwords--)
41 		sum += *buf++;
42 	sum = (sum>>16) + (sum & 0xffff);
43 	sum += (sum >>16);
44 	return (~sum);
45 }
46 
47 
48 int
49 send_ether(int nfd, char *buf, int len, struct in_addr gwip)
50 {
51 	static	struct	in_addr	last_gw;
52 	static	char	last_arp[6] = { 0, 0, 0, 0, 0, 0};
53 	ether_header_t	*eh;
54 	char	*s;
55 	int	err;
56 
57 	if (!ethbuf)
58 		ethbuf = (char *)calloc(1, 65536+1024);
59 	s = ethbuf;
60 	eh = (ether_header_t *)s;
61 
62 	bcopy((char *)buf, s + sizeof(*eh), len);
63 	if (gwip.s_addr == last_gw.s_addr)
64 	    {
65 		bcopy(last_arp, (char *) &eh->ether_dhost, 6);
66 	    }
67 	else if (arp((char *)&gwip, (char *) &eh->ether_dhost) == -1)
68 	    {
69 		perror("arp");
70 		return (-2);
71 	    }
72 	eh->ether_type = htons(ETHERTYPE_IP);
73 	last_gw.s_addr = gwip.s_addr;
74 	err = sendip(nfd, s, sizeof(*eh) + len);
75 	return (err);
76 }
77 
78 
79 /*
80  */
81 int
82 send_ip(int nfd, int mtu, ip_t *ip, struct in_addr gwip, int frag)
83 {
84 	static	struct	in_addr	last_gw, local_ip;
85 	static	char	local_arp[6] = { 0, 0, 0, 0, 0, 0};
86 	static	char	last_arp[6] = { 0, 0, 0, 0, 0, 0};
87 	static	u_short	id = 0;
88 	ether_header_t	*eh;
89 	ip_t	ipsv;
90 	int	err, iplen;
91 
92 	if (!ipbuf)
93 	  {
94 		ipbuf = (char *)malloc(65536);
95 		if (!ipbuf)
96 		  {
97 			perror("malloc failed");
98 			return (-2);
99 		  }
100 	  }
101 
102 	eh = (ether_header_t *)ipbuf;
103 
104 	bzero((char *) &eh->ether_shost, sizeof(eh->ether_shost));
105 	if (last_gw.s_addr && (gwip.s_addr == last_gw.s_addr))
106 	    {
107 		bcopy(last_arp, (char *) &eh->ether_dhost, 6);
108 	    }
109 	else if (arp((char *)&gwip, (char *) &eh->ether_dhost) == -1)
110 	    {
111 		perror("arp");
112 		return (-2);
113 	    }
114 	bcopy((char *) &eh->ether_dhost, last_arp, sizeof(last_arp));
115 	eh->ether_type = htons(ETHERTYPE_IP);
116 
117 	bcopy((char *)ip, (char *)&ipsv, sizeof(*ip));
118 	last_gw.s_addr = gwip.s_addr;
119 	iplen = ip->ip_len;
120 	ip->ip_len = htons(iplen);
121 	if (!(frag & 2)) {
122 		if (!IP_V(ip))
123 			IP_V_A(ip, IPVERSION);
124 		if (!ip->ip_id)
125 			ip->ip_id  = htons(id++);
126 		if (!ip->ip_ttl)
127 			ip->ip_ttl = 60;
128 	}
129 
130 	if (ip->ip_src.s_addr != local_ip.s_addr) {
131 		(void) arp((char *)&ip->ip_src, (char *) &local_arp);
132 		bcopy(local_arp, (char *) &eh->ether_shost,sizeof(last_arp));
133 		local_ip = ip->ip_src;
134 	} else
135 		bcopy(local_arp, (char *) &eh->ether_shost, 6);
136 
137 	if (!frag || (sizeof(*eh) + iplen < mtu))
138 	    {
139 		ip->ip_sum = 0;
140 		ip->ip_sum = chksum((u_short *)ip, IP_HL(ip) << 2);
141 
142 		bcopy((char *)ip, ipbuf + sizeof(*eh), iplen);
143 		err =  sendip(nfd, ipbuf, sizeof(*eh) + iplen);
144 	    }
145 	else
146 	    {
147 		/*
148 		 * Actually, this is bogus because we're putting all IP
149 		 * options in every packet, which isn't always what should be
150 		 * done.  Will do for now.
151 		 */
152 		ether_header_t	eth;
153 		char	optcpy[48], ol;
154 		char	*s;
155 		int	i, sent = 0, ts, hlen, olen;
156 
157 		hlen = IP_HL(ip) << 2;
158 		if (mtu < (hlen + 8)) {
159 			fprintf(stderr, "mtu (%d) < ip header size (%d) + 8\n",
160 				mtu, hlen);
161 			fprintf(stderr, "can't fragment data\n");
162 			return (-2);
163 		}
164 		ol = (IP_HL(ip) << 2) - sizeof(*ip);
165 		for (i = 0, s = (char*)(ip + 1); ol > 0; )
166 			if (*s == IPOPT_EOL) {
167 				optcpy[i++] = *s;
168 				break;
169 			} else if (*s == IPOPT_NOP) {
170 				s++;
171 				ol--;
172 			} else
173 			    {
174 				olen = (int)(*(u_char *)(s + 1));
175 				ol -= olen;
176 				if (IPOPT_COPIED(*s))
177 				    {
178 					bcopy(s, optcpy + i, olen);
179 					i += olen;
180 					s += olen;
181 				    }
182 			    }
183 		if (i)
184 		    {
185 			/*
186 			 * pad out
187 			 */
188 			while ((i & 3) && (i & 3) != 3)
189 				optcpy[i++] = IPOPT_NOP;
190 			if ((i & 3) == 3)
191 				optcpy[i++] = IPOPT_EOL;
192 		    }
193 
194 		bcopy((char *)eh, (char *)&eth, sizeof(eth));
195 		s = (char *)ip + hlen;
196 		iplen = ntohs(ip->ip_len) - hlen;
197 		ip->ip_off |= htons(IP_MF);
198 
199 		while (1)
200 		    {
201 			if ((sent + (mtu - hlen)) >= iplen)
202 			    {
203 				ip->ip_off ^= htons(IP_MF);
204 				ts = iplen - sent;
205 			    }
206 			else
207 				ts = (mtu - hlen);
208 			ip->ip_off &= htons(0xe000);
209 			ip->ip_off |= htons(sent >> 3);
210 			ts += hlen;
211 			ip->ip_len = htons(ts);
212 			ip->ip_sum = 0;
213 			ip->ip_sum = chksum((u_short *)ip, hlen);
214 			bcopy((char *)ip, ipbuf + sizeof(*eh), hlen);
215 			bcopy(s + sent, ipbuf + sizeof(*eh) + hlen, ts - hlen);
216 			err =  sendip(nfd, ipbuf, sizeof(*eh) + ts);
217 
218 			bcopy((char *)&eth, ipbuf, sizeof(eth));
219 			sent += (ts - hlen);
220 			if (!(ntohs(ip->ip_off) & IP_MF))
221 				break;
222 			else if (!(ip->ip_off & htons(0x1fff)))
223 			    {
224 				hlen = i + sizeof(*ip);
225 				IP_HL_A(ip, (sizeof(*ip) + i) >> 2);
226 				bcopy(optcpy, (char *)(ip + 1), i);
227 			    }
228 		    }
229 	    }
230 
231 	bcopy((char *)&ipsv, (char *)ip, sizeof(*ip));
232 	return (err);
233 }
234 
235 
236 /*
237  * send a tcp packet.
238  */
239 int
240 send_tcp(int nfd, int mtu, ip_t *ip, struct in_addr gwip)
241 {
242 	static	tcp_seq	iss = 2;
243 	tcphdr_t *t, *t2;
244 	int	thlen, i, iplen, hlen;
245 	u_32_t	lbuf[20];
246 	ip_t	*ip2;
247 
248 	iplen = ip->ip_len;
249 	hlen = IP_HL(ip) << 2;
250 	t = (tcphdr_t *)((char *)ip + hlen);
251 	ip2 = (struct ip *)lbuf;
252 	t2 = (tcphdr_t *)((char *)ip2 + hlen);
253 	thlen = TCP_OFF(t) << 2;
254 	if (!thlen)
255 		thlen = sizeof(tcphdr_t);
256 	bzero((char *)ip2, sizeof(*ip2) + sizeof(*t2));
257 	ip->ip_p = IPPROTO_TCP;
258 	ip2->ip_p = ip->ip_p;
259 	ip2->ip_src = ip->ip_src;
260 	ip2->ip_dst = ip->ip_dst;
261 	bcopy((char *)ip + hlen, (char *)t2, thlen);
262 
263 	if (!t2->th_win)
264 		t2->th_win = htons(4096);
265 	iss += 63;
266 
267 	i = sizeof(struct tcpiphdr) / sizeof(long);
268 
269 	if ((t2->th_flags == TH_SYN) && !ntohs(ip->ip_off) &&
270 	    (lbuf[i] != htonl(0x020405b4))) {
271 		lbuf[i] = htonl(0x020405b4);
272 		bcopy((char *)ip + hlen + thlen, (char *)ip + hlen + thlen + 4,
273 		      iplen - thlen - hlen);
274 		thlen += 4;
275 	    }
276 	TCP_OFF_A(t2, thlen >> 2);
277 	ip2->ip_len = htons(thlen);
278 	ip->ip_len = hlen + thlen;
279 	t2->th_sum = 0;
280 	t2->th_sum = chksum((u_short *)ip2, thlen + sizeof(ip_t));
281 
282 	bcopy((char *)t2, (char *)ip + hlen, thlen);
283 	return (send_ip(nfd, mtu, ip, gwip, 1));
284 }
285 
286 
287 /*
288  * send a udp packet.
289  */
290 int
291 send_udp(int nfd, int mtu, ip_t *ip, struct in_addr gwip)
292 {
293 	struct	tcpiphdr *ti;
294 	int	thlen;
295 	u_long	lbuf[20];
296 
297 	ti = (struct tcpiphdr *)lbuf;
298 	bzero((char *)ti, sizeof(*ti));
299 	thlen = sizeof(udphdr_t);
300 	ti->ti_pr = ip->ip_p;
301 	ti->ti_src = ip->ip_src;
302 	ti->ti_dst = ip->ip_dst;
303 	bcopy((char *)ip + (IP_HL(ip) << 2),
304 	      (char *)&ti->ti_sport, sizeof(udphdr_t));
305 
306 	ti->ti_len = htons(thlen);
307 	ip->ip_len = (IP_HL(ip) << 2) + thlen;
308 	ti->ti_sum = 0;
309 	ti->ti_sum = chksum((u_short *)ti, thlen + sizeof(ip_t));
310 
311 	bcopy((char *)&ti->ti_sport,
312 	      (char *)ip + (IP_HL(ip) << 2), sizeof(udphdr_t));
313 	return (send_ip(nfd, mtu, ip, gwip, 1));
314 }
315 
316 
317 /*
318  * send an icmp packet.
319  */
320 int
321 send_icmp(int nfd, int mtu, ip_t *ip, in_addr gwip)
322 {
323 	struct	icmp	*ic;
324 
325 	ic = (struct icmp *)((char *)ip + (IP_HL(ip) << 2));
326 
327 	ic->icmp_cksum = 0;
328 	ic->icmp_cksum = chksum((u_short *)ic, sizeof(struct icmp));
329 
330 	return (send_ip(nfd, mtu, ip, gwip, 1));
331 }
332 
333 
334 int
335 send_packet(int nfd, int mtu, ip_t *ip, struct in_addr gwip)
336 	int	nfd, mtu;
337 	ip_t	*ip;
338 	struct	in_addr	gwip;
339 {
340 	switch (ip->ip_p)
341 	{
342 	case IPPROTO_TCP :
343 (                return send_tcp(nfd, mtu, ip, gwip));
344 	case IPPROTO_UDP :
345 (                return send_udp(nfd, mtu, ip, gwip));
346 	case IPPROTO_ICMP :
347 (                return send_icmp(nfd, mtu, ip, gwip));
348 	default :
349 (                return send_ip(nfd, mtu, ip, gwip, 1));
350 	}
351 }
352