xref: /openbsd-src/sys/lib/libsa/net.c (revision e5157e49389faebcb42b7237d55fbf096d9c2523)
1 /*	$OpenBSD: net.c,v 1.14 2014/07/13 15:31:20 mpi Exp $	*/
2 /*	$NetBSD: net.c,v 1.14 1996/10/13 02:29:02 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 1992 Regents of the University of California.
6  * All rights reserved.
7  *
8  * This software was developed by the Computer Systems Engineering group
9  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
10  * contributed to Berkeley.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Lawrence Berkeley Laboratory and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  * @(#) Header: net.c,v 1.9 93/08/06 19:32:15 leres Exp  (LBL)
41  */
42 
43 #include <sys/param.h>
44 #include <sys/socket.h>
45 
46 #include <net/if.h>
47 #include <netinet/in.h>
48 
49 #include <netinet/in.h>
50 #include <netinet/if_ether.h>
51 #include <netinet/ip.h>
52 #include <netinet/ip_var.h>
53 #include <netinet/udp.h>
54 #include <netinet/udp_var.h>
55 
56 #include "stand.h"
57 #include "net.h"
58 
59 /* Caller must leave room for ethernet, ip and udp headers in front!! */
60 ssize_t
61 sendudp(struct iodesc *d, void *pkt, size_t len)
62 {
63 	ssize_t cc;
64 	struct ip *ip;
65 	struct udpiphdr *ui;
66 	struct udphdr *uh;
67 	u_char *ea;
68 	struct ip tip;
69 
70 #ifdef NET_DEBUG
71 	if (debug) {
72 		printf("sendudp: d=%x called.\n", (u_int)d);
73 		if (d) {
74 			printf("saddr: %s:%d",
75 			    inet_ntoa(d->myip), ntohs(d->myport));
76 			printf(" daddr: %s:%d\n",
77 			    inet_ntoa(d->destip), ntohs(d->destport));
78 		}
79 	}
80 #endif
81 
82 	uh = (struct udphdr *)pkt - 1;
83 	ip = (struct ip *)uh - 1;
84 	len += sizeof(*ip) + sizeof(*uh);
85 
86 	bzero(ip, sizeof(*ip) + sizeof(*uh));
87 
88 	ip->ip_v = IPVERSION;			/* half-char */
89 	ip->ip_hl = sizeof(*ip) >> 2;		/* half-char */
90 	ip->ip_len = htons(len);
91 	ip->ip_p = IPPROTO_UDP;			/* char */
92 	ip->ip_ttl = IP_TTL;			/* char */
93 	ip->ip_src = d->myip;
94 	ip->ip_dst = d->destip;
95 	ip->ip_sum = in_cksum(ip, sizeof(*ip));	 /* short, but special */
96 
97 	uh->uh_sport = d->myport;
98 	uh->uh_dport = d->destport;
99 	uh->uh_ulen = htons(len - sizeof(*ip));
100 
101 	/* Calculate checksum (must save and restore ip header) */
102 	tip = *ip;
103 	ui = (struct udpiphdr *)ip;
104 	bzero(ui->ui_x1, sizeof(ui->ui_x1));
105 	ui->ui_len = uh->uh_ulen;
106 	uh->uh_sum = in_cksum(ui, len);
107 	*ip = tip;
108 
109 	if (ip->ip_dst.s_addr == INADDR_BROADCAST || ip->ip_src.s_addr == 0 ||
110 	    netmask == 0 || SAMENET(ip->ip_src, ip->ip_dst, netmask))
111 		ea = arpwhohas(d, ip->ip_dst);
112 	else
113 		ea = arpwhohas(d, gateip);
114 
115 	cc = sendether(d, ip, len, ea, ETHERTYPE_IP);
116 	if (cc < 0)
117 		return (-1);
118 	if ((size_t)cc != len)
119 		panic("sendudp: bad write (%d != %d)", cc, len);
120 	return (cc - (sizeof(*ip) + sizeof(*uh)));
121 }
122 
123 /*
124  * Receive a UDP packet and validate it is for us.
125  * Caller leaves room for the headers (Ether, IP, UDP)
126  */
127 ssize_t
128 readudp(struct iodesc *d, void *pkt, size_t len, time_t tleft)
129 {
130 	ssize_t n;
131 	size_t hlen;
132 	struct ip *ip;
133 	struct udphdr *uh;
134 	struct udpiphdr *ui;
135 	struct ip tip;
136 	u_int16_t etype;	/* host order */
137 
138 #ifdef NET_DEBUG
139 	if (debug)
140 		printf("readudp: called\n");
141 #endif
142 
143 	uh = (struct udphdr *)pkt - 1;
144 	ip = (struct ip *)uh - 1;
145 
146 	n = readether(d, ip, len + sizeof(*ip) + sizeof(*uh), tleft, &etype);
147 	if (n < 0 || (size_t)n < sizeof(*ip) + sizeof(*uh))
148 		return -1;
149 
150 	/* Ethernet address checks now in readether() */
151 
152 	/* Need to respond to ARP requests. */
153 	if (etype == ETHERTYPE_ARP) {
154 		struct arphdr *ah = (void *)ip;
155 		if (ah->ar_op == htons(ARPOP_REQUEST)) {
156 			/* Send ARP reply */
157 			arp_reply(d, ah);
158 		}
159 		return -1;
160 	}
161 
162 	if (etype != ETHERTYPE_IP) {
163 #ifdef NET_DEBUG
164 		if (debug)
165 			printf("readudp: not IP. ether_type=%x\n", etype);
166 #endif
167 		return -1;
168 	}
169 
170 	/* Check ip header */
171 	if (ip->ip_v != IPVERSION ||
172 	    ip->ip_p != IPPROTO_UDP) {	/* half char */
173 #ifdef NET_DEBUG
174 		if (debug)
175 			printf("readudp: IP version or not UDP. ip_v=%d ip_p=%d\n", ip->ip_v, ip->ip_p);
176 #endif
177 		return -1;
178 	}
179 
180 	hlen = ip->ip_hl << 2;
181 	if (hlen < sizeof(*ip) ||
182 	    in_cksum(ip, hlen) != 0) {
183 #ifdef NET_DEBUG
184 		if (debug)
185 			printf("readudp: short hdr or bad cksum.\n");
186 #endif
187 		return -1;
188 	}
189 	NTOHS(ip->ip_len);
190 	if (n < ip->ip_len) {
191 #ifdef NET_DEBUG
192 		if (debug)
193 			printf("readudp: bad length %d < %d.\n", n, ip->ip_len);
194 #endif
195 		return -1;
196 	}
197 	if (d->myip.s_addr && ip->ip_dst.s_addr != d->myip.s_addr) {
198 #ifdef NET_DEBUG
199 		if (debug) {
200 			printf("readudp: bad saddr %s != ", inet_ntoa(d->myip));
201 			printf("%s\n", inet_ntoa(ip->ip_dst));
202 		}
203 #endif
204 		return -1;
205 	}
206 
207 	/* If there were ip options, make them go away */
208 	if (hlen != sizeof(*ip)) {
209 		bcopy(((u_char *)ip) + hlen, uh, len - hlen);
210 		ip->ip_len = sizeof(*ip);
211 		n -= hlen - sizeof(*ip);
212 	}
213 	if (uh->uh_dport != d->myport) {
214 #ifdef NET_DEBUG
215 		if (debug)
216 			printf("readudp: bad dport %d != %d\n",
217 				d->myport, ntohs(uh->uh_dport));
218 #endif
219 		return -1;
220 	}
221 
222 	if (uh->uh_sum) {
223 		n = ntohs(uh->uh_ulen) + sizeof(*ip);
224 		if (n > RECV_SIZE - ETHER_SIZE) {
225 			printf("readudp: huge packet, udp len %ld\n", (long)n);
226 			return -1;
227 		}
228 
229 		/* Check checksum (must save and restore ip header) */
230 		tip = *ip;
231 		ui = (struct udpiphdr *)ip;
232 		bzero(ui->ui_x1, sizeof(ui->ui_x1));
233 		ui->ui_len = uh->uh_ulen;
234 		if (in_cksum(ui, n) != 0) {
235 #ifdef NET_DEBUG
236 			if (debug)
237 				printf("readudp: bad cksum\n");
238 #endif
239 			*ip = tip;
240 			return -1;
241 		}
242 		*ip = tip;
243 	}
244 	NTOHS(uh->uh_dport);
245 	NTOHS(uh->uh_sport);
246 	NTOHS(uh->uh_ulen);
247 	if (uh->uh_ulen < sizeof(*uh)) {
248 #ifdef NET_DEBUG
249 		if (debug)
250 			printf("readudp: bad udp len %d < %d\n",
251 				uh->uh_ulen, sizeof(*uh));
252 #endif
253 		return -1;
254 	}
255 
256 	n -= sizeof(*ip) + sizeof(*uh);
257 	return (n);
258 }
259 
260 /*
261  * Send a packet and wait for a reply, with exponential backoff.
262  *
263  * The send routine must return the actual number of bytes written.
264  *
265  * The receive routine can indicate success by returning the number of
266  * bytes read; it can return 0 to indicate EOF; it can return -1 with a
267  * non-zero errno to indicate failure; finally, it can return -1 with a
268  * zero errno to indicate it isn't done yet.
269  */
270 ssize_t
271 sendrecv(struct iodesc *d, ssize_t (*sproc)(struct iodesc *, void *, size_t),
272     void *sbuf, size_t ssize,
273     ssize_t (*rproc)(struct iodesc *, void *, size_t, time_t),
274     void *rbuf, size_t rsize)
275 {
276 	ssize_t cc;
277 	time_t t, tmo, tlast;
278 	long tleft;
279 
280 #ifdef NET_DEBUG
281 	if (debug)
282 		printf("sendrecv: called\n");
283 #endif
284 
285 	tmo = MINTMO;
286 	tlast = tleft = 0;
287 	t = getsecs();
288 	for (;;) {
289 		if (tleft <= 0) {
290 			if (tmo >= MAXTMO) {
291 				errno = ETIMEDOUT;
292 				return -1;
293 			}
294 			cc = (*sproc)(d, sbuf, ssize);
295 			if (cc < 0 || (size_t)cc < ssize)
296 				panic("sendrecv: short write! (%d < %d)",
297 				    cc, ssize);
298 
299 			tleft = tmo;
300 			tmo <<= 1;
301 			if (tmo > MAXTMO)
302 				tmo = MAXTMO;
303 			tlast = t;
304 		}
305 
306 		/* Try to get a packet and process it. */
307 		cc = (*rproc)(d, rbuf, rsize, tleft);
308 		/* Return on data, EOF or real error. */
309 		if (cc != -1 || errno != 0)
310 			return (cc);
311 
312 		/* Timed out or didn't get the packet we're waiting for */
313 		t = getsecs();
314 		tleft -= t - tlast;
315 		tlast = t;
316 	}
317 }
318 
319 /*
320  * Like inet_addr() in the C library, but we only accept base-10.
321  * Return values are in network order.
322  */
323 u_int32_t
324 inet_addr(char *cp)
325 {
326 	u_long val;
327 	int n;
328 	char c;
329 	u_int parts[4];
330 	u_int *pp = parts;
331 
332 	for (;;) {
333 		/*
334 		 * Collect number up to ``.''.
335 		 * Values are specified as for C:
336 		 * 0x=hex, 0=octal, other=decimal.
337 		 */
338 		val = 0;
339 		while ((c = *cp) != '\0') {
340 			if (c >= '0' && c <= '9') {
341 				val = (val * 10) + (c - '0');
342 				cp++;
343 				continue;
344 			}
345 			break;
346 		}
347 		if (*cp == '.') {
348 			/*
349 			 * Internet format:
350 			 *	a.b.c.d
351 			 *	a.b.c	(with c treated as 16-bits)
352 			 *	a.b	(with b treated as 24 bits)
353 			 */
354 			if (pp >= parts + 3 || val > 0xff)
355 				goto bad;
356 			*pp++ = val, cp++;
357 		} else
358 			break;
359 	}
360 	/*
361 	 * Check for trailing characters.
362 	 */
363 	if (*cp != '\0')
364 		goto bad;
365 
366 	/*
367 	 * Concoct the address according to
368 	 * the number of parts specified.
369 	 */
370 	n = pp - parts + 1;
371 	switch (n) {
372 
373 	case 1:				/* a -- 32 bits */
374 		break;
375 
376 	case 2:				/* a.b -- 8.24 bits */
377 		if (val > 0xffffff)
378 			goto bad;
379 		val |= parts[0] << 24;
380 		break;
381 
382 	case 3:				/* a.b.c -- 8.8.16 bits */
383 		if (val > 0xffff)
384 			goto bad;
385 		val |= (parts[0] << 24) | (parts[1] << 16);
386 		break;
387 
388 	case 4:				/* a.b.c.d -- 8.8.8.8 bits */
389 		if (val > 0xff)
390 			goto bad;
391 		val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
392 		break;
393 	}
394 
395 	return (htonl(val));
396  bad:
397 	return (htonl(INADDR_NONE));
398 }
399 
400 char *
401 inet_ntoa(struct in_addr ia)
402 {
403 	return (intoa(ia.s_addr));
404 }
405 
406 /* Similar to inet_ntoa() */
407 char *
408 intoa(u_int32_t addr)
409 {
410 	char *cp;
411 	u_int byte;
412 	int n;
413 	static char buf[sizeof(".255.255.255.255")];
414 
415 	NTOHL(addr);
416 	cp = &buf[sizeof buf];
417 	*--cp = '\0';
418 
419 	n = 4;
420 	do {
421 		byte = addr & 0xff;
422 		*--cp = byte % 10 + '0';
423 		byte /= 10;
424 		if (byte > 0) {
425 			*--cp = byte % 10 + '0';
426 			byte /= 10;
427 			if (byte > 0)
428 				*--cp = byte + '0';
429 		}
430 		*--cp = '.';
431 		addr >>= 8;
432 	} while (--n > 0);
433 
434 	return (cp+1);
435 }
436 
437 static char *
438 number(char *s, int *n)
439 {
440 	for (*n = 0; isdigit(*s); s++)
441 		*n = (*n * 10) + *s - '0';
442 	return s;
443 }
444 
445 u_int32_t
446 ip_convertaddr(char *p)
447 {
448 #define IP_ANYADDR	0
449 	u_int32_t addr = 0, n;
450 
451 	if (p == (char *)0 || *p == '\0')
452 		return IP_ANYADDR;
453 	p = number(p, &n);
454 	addr |= (n << 24) & 0xff000000;
455 	if (*p == '\0' || *p++ != '.')
456 		return IP_ANYADDR;
457 	p = number(p, &n);
458 	addr |= (n << 16) & 0xff0000;
459 	if (*p == '\0' || *p++ != '.')
460 		return IP_ANYADDR;
461 	p = number(p, &n);
462 	addr |= (n << 8) & 0xff00;
463 	if (*p == '\0' || *p++ != '.')
464 		return IP_ANYADDR;
465 	p = number(p, &n);
466 	addr |= n & 0xff;
467 	if (*p != '\0')
468 		return IP_ANYADDR;
469 
470 	return htonl(addr);
471 }
472