xref: /openbsd/usr.sbin/npppd/npppd/npppd_subr.c (revision f5c2ff87)
1 /*	$OpenBSD: npppd_subr.c,v 1.21 2021/03/29 03:54:39 yasuoka Exp $ */
2 
3 /*-
4  * Copyright (c) 2009 Internet Initiative Japan Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 /**@file
29  * This file provides helper functions for npppd.
30  */
31 
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <netinet/ip.h>
36 #include <netinet/udp.h>
37 #include <netinet/tcp.h>
38 #include <net/route.h>
39 #include <net/if_dl.h>
40 #include <net/if.h>
41 #include <arpa/inet.h>
42 #include <stdlib.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <syslog.h>
46 #include <stddef.h>
47 #include <unistd.h>
48 #include <errno.h>
49 #include <ctype.h>
50 #include <string.h>
51 #include <resolv.h>
52 
53 #include "debugutil.h"
54 #include "addr_range.h"
55 
56 #include "npppd_defs.h"
57 #include "npppd_subr.h"
58 #include "privsep.h"
59 
60 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
61 
62 static u_int16_t route_seq = 0;
63 static int  in_route0(int, struct in_addr *, struct in_addr *, struct in_addr *, int, const char *, uint32_t);
64 #define ROUNDUP(a) \
65 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
66 
67 static const char *
skip_space(const char * s)68 skip_space(const char *s)
69 {
70 	const char *r;
71 	for (r = s; *r != '\0' && isspace((unsigned char)*r); r++)
72 		;; /* skip */
73 
74 	return r;
75 }
76 
77 /**
78  * Read and store IPv4 address of name server from resolv.conf.
79  * The path of resolv.conf is taken from _PATH_RESCONF in resolv.h.
80  */
81 int
load_resolv_conf(struct in_addr * pri,struct in_addr * sec)82 load_resolv_conf(struct in_addr *pri, struct in_addr *sec)
83 {
84 	FILE *filep;
85 	int i;
86 	struct in_addr *addr;
87 	char *ap, *line, buf[BUFSIZ];
88 
89 	pri->s_addr = INADDR_NONE;
90 	sec->s_addr = INADDR_NONE;
91 
92 	filep = NULL;
93 	if ((filep = priv_fopen(_PATH_RESCONF)) == NULL)
94 		return 1;
95 
96 	i = 0;
97 	while (fgets(buf, sizeof(buf), filep) != NULL) {
98 		line = (char *)skip_space(buf);
99 		if (strncmp(line, "nameserver", 10) != 0)
100 			continue;
101 		line += 10;
102 		if (!isspace((unsigned char)*line))
103 			continue;
104 		while ((ap = strsep(&line, " \t\r\n")) != NULL) {
105 			if (*ap == '\0')
106 				continue;
107 			if (i == 0)
108 				addr = pri;
109 			else
110 				addr = sec;
111 			if (inet_aton(ap, addr) != 1) {
112 				/*
113 				 * FIXME: If configured IPv6, it may have IPv6
114 				 * FIXME: address.  For the present, continue.
115 				 */
116 				continue;
117 			}
118 			addr->s_addr = addr->s_addr;
119 			if (++i >= 2)
120 				goto end_loop;
121 		}
122 	}
123 end_loop:
124 	if (filep != NULL)
125 		fclose(filep);
126 
127 	return 0;
128 }
129 
130 /* Add and delete routing entry. */
131 static int
in_route0(int type,struct in_addr * dest,struct in_addr * mask,struct in_addr * gate,int mtu,const char * ifname,uint32_t rtm_flags)132 in_route0(int type, struct in_addr *dest, struct in_addr *mask,
133     struct in_addr *gate, int mtu, const char *ifname, uint32_t rtm_flags)
134 {
135 	struct rt_msghdr *rtm;
136 	struct sockaddr_in sdest, smask, sgate;
137 	struct sockaddr_dl *sdl;
138 	char dl_buf[512];	/* enough size */
139 	char *cp, buf[sizeof(*rtm) + sizeof(struct sockaddr_in) * 3 +
140 	    sizeof(dl_buf) + 128];
141 	const char *strtype;
142 	int rval, flags, sock;
143 
144 	sock = -1;
145 
146 	ASSERT(type == RTM_ADD || type == RTM_DELETE);
147 	if(type == RTM_ADD)
148 		strtype = "RTM_ADD";
149 	else
150 		strtype = "RTM_DELETE";
151 
152 	memset(buf, 0, sizeof(buf));
153 	memset(&sdest, 0, sizeof(sdest));
154 	memset(&smask, 0, sizeof(smask));
155 	memset(&sgate, 0, sizeof(sgate));
156 	memset(&dl_buf, 0, sizeof(dl_buf));
157 
158 	sdl = (struct sockaddr_dl *)dl_buf;
159 
160 	sdest.sin_addr = *dest;
161 	if (mask != NULL)
162 		smask.sin_addr = *mask;
163 	if (gate != NULL)
164 		sgate.sin_addr = *gate;
165 
166 	sdest.sin_family = smask.sin_family = sgate.sin_family = AF_INET;
167 	sdest.sin_len = smask.sin_len = sgate.sin_len = sizeof(sgate);
168 
169 	rtm = (struct rt_msghdr *)buf;
170 
171 	rtm->rtm_version = RTM_VERSION;
172 	rtm->rtm_type = type;
173 	rtm->rtm_flags = rtm_flags;
174 	if (gate != NULL)
175 		rtm->rtm_flags |= RTF_GATEWAY;
176 	if (mask == NULL)
177 		rtm->rtm_flags |= RTF_HOST;
178 
179 	if (type == RTM_ADD && mtu > 0) {
180 		rtm->rtm_inits = RTV_MTU;
181 		rtm->rtm_rmx.rmx_mtu = mtu;
182 	}
183 
184 	if (type == RTM_ADD)
185 		rtm->rtm_flags |= RTF_UP;
186 
187 	rtm->rtm_addrs = RTA_DST;
188 	if (gate != NULL)
189 		rtm->rtm_addrs |= RTA_GATEWAY;
190 	if (mask != NULL)
191 		rtm->rtm_addrs |= RTA_NETMASK;
192 #ifdef RTA_IFP
193 	if (ifname != NULL)
194 		rtm->rtm_addrs |= RTA_IFP;
195 #endif
196 
197 	rtm->rtm_pid = getpid();
198 	route_seq = ((route_seq + 1)&0x0000ffff);
199 	rtm->rtm_seq = route_seq;
200 
201 	cp = (char *)rtm;
202 	cp += ROUNDUP(sizeof(*rtm));
203 
204 	memcpy(cp, &sdest, sdest.sin_len);
205 	cp += ROUNDUP(sdest.sin_len);
206 	if (gate != NULL) {
207 		memcpy(cp, &sgate, sgate.sin_len);
208 		cp += ROUNDUP(sgate.sin_len);
209 	}
210 	if (mask != NULL) {
211 		memcpy(cp, &smask, smask.sin_len);
212 		cp += ROUNDUP(smask.sin_len);
213 	}
214 #ifdef RTA_IFP
215 	if (ifname != NULL) {
216 		strlcpy(sdl->sdl_data, ifname, IFNAMSIZ);
217 		sdl->sdl_family = AF_LINK;
218 		sdl->sdl_len = offsetof(struct sockaddr_dl, sdl_data) +IFNAMSIZ;
219 		sdl->sdl_index = if_nametoindex(ifname);
220 		memcpy(cp, sdl, sdl->sdl_len);
221 		cp += ROUNDUP(sdl->sdl_len);
222 	}
223 #endif
224 
225 	rtm->rtm_msglen = cp - buf;
226 
227 	if ((sock = priv_socket(AF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) {
228 		log_printf(LOG_ERR, "socket() failed in %s() on %s : %m",
229 		    __func__, strtype);
230 		goto fail;
231 	}
232 
233 	if ((flags = fcntl(sock, F_GETFL)) < 0) {
234 		log_printf(LOG_ERR, "fcntl(,F_GETFL) failed on %s : %m",
235 		    __func__);
236 		goto fail;
237 	}
238 
239 	if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
240 		log_printf(LOG_ERR, "fcntl(,F_SETFL) failed on %s : %m",
241 		    __func__);
242 		goto fail;
243 	}
244 
245 	if ((rval = priv_send(sock, buf, rtm->rtm_msglen, 0)) <= 0) {
246 		if ((type == RTM_DELETE && errno == ESRCH) ||
247 		    (type == RTM_ADD    && errno == EEXIST)) {
248 			log_printf(LOG_DEBUG,
249 			    "write() failed in %s on %s : %m", __func__,
250 			    strtype);
251 		} else {
252 			log_printf(LOG_WARNING,
253 			    "write() failed in %s on %s : %m", __func__,
254 			    strtype);
255 		}
256 		goto fail;
257 	}
258 
259 	close(sock);
260 
261 	return 0;
262 
263 fail:
264 	if (sock >= 0)
265 		close(sock);
266 
267 	return 1;
268 }
269 
270 /** Add host routing entry. */
271 int
in_host_route_add(struct in_addr * dest,struct in_addr * gate,const char * ifname,int mtu)272 in_host_route_add(struct in_addr *dest, struct in_addr *gate,
273     const char *ifname, int mtu)
274 {
275 	return in_route0(RTM_ADD, dest, NULL, gate, mtu, ifname, 0);
276 }
277 
278 /** Delete host routing entry. */
279 int
in_host_route_delete(struct in_addr * dest,struct in_addr * gate)280 in_host_route_delete(struct in_addr *dest, struct in_addr *gate)
281 {
282 	return in_route0(RTM_DELETE, dest, NULL, gate, 0, NULL, 0);
283 }
284 
285 /** Add network routing entry. */
286 int
in_route_add(struct in_addr * dest,struct in_addr * mask,struct in_addr * gate,const char * ifname,uint32_t rtm_flags,int mtu)287 in_route_add(struct in_addr *dest, struct in_addr *mask, struct in_addr *gate,
288     const char *ifname, uint32_t rtm_flags, int mtu)
289 {
290 	return in_route0(RTM_ADD, dest, mask, gate, mtu, ifname, rtm_flags);
291 }
292 
293 /** Delete network routing entry. */
294 int
in_route_delete(struct in_addr * dest,struct in_addr * mask,struct in_addr * gate,uint32_t rtm_flags)295 in_route_delete(struct in_addr *dest, struct in_addr *mask,
296     struct in_addr *gate, uint32_t rtm_flags)
297 {
298 	return in_route0(RTM_DELETE, dest, mask, gate, 0, NULL, rtm_flags);
299 }
300 
301 /**
302  *  Check whether a packet should reset idle timer
303  *  Returns 1 to don't reset timer (i.e. the packet is "idle" packet)
304  */
305 int
ip_is_idle_packet(const struct ip * pip,int len)306 ip_is_idle_packet(const struct ip * pip, int len)
307 {
308 	u_int16_t ip_off;
309 	const struct udphdr *uh;
310 
311 	/*
312          * Fragmented packet is not idle packet.
313          * (Long packet which needs to fragment is not idle packet.)
314          */
315 	ip_off = ntohs(pip->ip_off);
316 	if ((ip_off & IP_MF) || ((ip_off & IP_OFFMASK) != 0))
317 		return 0;
318 
319 	switch (pip->ip_p) {
320 	case IPPROTO_IGMP:
321 		return 1;
322 	case IPPROTO_ICMP:
323 		/* Is length enough? */
324 		if (pip->ip_hl * 4 + 8 > len)
325 			return 1;
326 
327 		switch (((unsigned char *) pip)[pip->ip_hl * 4]) {
328 		case 0:	/* Echo Reply */
329 		case 8:	/* Echo Request */
330 			return 0;
331 		default:
332 			return 1;
333 		}
334 	case IPPROTO_UDP:
335 	case IPPROTO_TCP:
336 		/*
337 		 * The place of port number of UDP and TCP is the same,
338 		 * so can be shared.
339 		 */
340 		uh = (const struct udphdr *) (((const char *) pip) +
341 		    (pip->ip_hl * 4));
342 
343 		/* Is length enough? */
344 		if (pip->ip_hl * 4 + sizeof(struct udphdr) > len)
345 			return 1;
346 
347 		switch (ntohs(uh->uh_sport)) {
348 		case 53:	/* DOMAIN */
349 		case 67:	/* BOOTPS */
350 		case 68:	/* BOOTPC */
351 		case 123:	/* NTP */
352 		case 137:	/* NETBIOS-NS */
353 		case 520:	/* RIP */
354 			return 1;
355 		}
356 		switch (ntohs(uh->uh_dport)) {
357 		case 53:	/* DOMAIN */
358 		case 67:	/* BOOTPS */
359 		case 68:	/* BOOTPC */
360 		case 123:	/* NTP */
361 		case 137:	/* NETBIOS-NS */
362 		case 520:	/* RIP */
363 			return 1;
364 		}
365 		return 0;
366 	default:
367 		return 0;
368 	}
369 }
370 
371 /***********************************************************************
372  * Add and delete routing entry for the pool address.
373  ***********************************************************************/
374 void
in_addr_range_add_route(struct in_addr_range * range)375 in_addr_range_add_route(struct in_addr_range *range)
376 {
377 	struct in_addr_range *range0;
378 	struct in_addr dest, mask, loop;
379 
380 	for (range0 = range; range0 != NULL; range0 = range0->next){
381 		dest.s_addr = htonl(range0->addr);
382 		mask.s_addr = htonl(range0->mask);
383 		loop.s_addr = htonl(INADDR_LOOPBACK);
384 		in_route_add(&dest, &mask, &loop, LOOPBACK_IFNAME,
385 		    RTF_BLACKHOLE, 0);
386 	}
387 	log_printf(LOG_INFO, "Added routes for pooled addresses");
388 }
389 
390 void
in_addr_range_delete_route(struct in_addr_range * range)391 in_addr_range_delete_route(struct in_addr_range *range)
392 {
393 	struct in_addr_range *range0;
394 	struct in_addr dest, mask, loop;
395 
396 	for (range0 = range; range0 != NULL; range0 = range0->next){
397 		dest.s_addr = htonl(range0->addr);
398 		mask.s_addr = htonl(range0->mask);
399 		loop.s_addr = htonl(INADDR_LOOPBACK);
400 
401 		in_route_delete(&dest, &mask, &loop, RTF_BLACKHOLE);
402 	}
403 	log_printf(LOG_NOTICE, "Deleted routes for pooled addresses");
404 }
405 
406 
407 /* GETSHORT is also defined in #include <arpa/nameser_compat.h>. */
408 #undef	GETCHAR
409 #undef	GETSHORT
410 #undef	PUTSHORT
411 
412 #define GETCHAR(c, cp) { (c) = *(cp)++; }
413 #define GETSHORT(s, cp) { \
414 	(s) = *(cp)++ << 8; \
415 	(s) |= *(cp)++; \
416 }
417 #define PUTSHORT(s, cp) { \
418 	*(cp)++ = (u_char) ((s) >> 8); \
419 	*(cp)++ = (u_char) (s); \
420 }
421 #define TCP_OPTLEN_IN_SEGMENT	12	/* timestamp option and padding */
422 #define MAXMSS(mtu) (mtu - sizeof(struct ip) - sizeof(struct tcphdr) - \
423     TCP_OPTLEN_IN_SEGMENT)
424 
425 /* adapted from FreeBSD:src/usr.sbin/ppp/tcpmss.c */
426 /*
427  * Copyright (c) 2000 Ruslan Ermilov and Brian Somers <brian@Awfulhak.org>
428  * All rights reserved.
429  *
430  * Redistribution and use in source and binary forms, with or without
431  * modification, are permitted provided that the following conditions
432  * are met:
433  * 1. Redistributions of source code must retain the above copyright
434  *    notice, this list of conditions and the following disclaimer.
435  * 2. Redistributions in binary form must reproduce the above copyright
436  *    notice, this list of conditions and the following disclaimer in the
437  *    documentation and/or other materials provided with the distribution.
438  *
439  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
440  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
441  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
442  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
443  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
444  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
445  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
446  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
447  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
448  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
449  * SUCH DAMAGE.
450  *
451  * $FreeBSD: src/usr.sbin/ppp/tcpmss.c,v 1.1.4.3 2001/07/19 11:39:54 brian Exp $
452  */
453 
454 /*
455  * The following macro is used to update an internet checksum.  "acc" is a
456  * 32-bit accumulation of all the changes to the checksum (adding in old
457  * 16-bit words and subtracting out new words), and "cksum" is the checksum
458  * value to be updated.
459  */
460 #define ADJUST_CHECKSUM(acc, cksum) {			\
461 	acc += cksum;					\
462 	if (acc < 0) {					\
463 		acc = -acc;				\
464 		acc = (acc >> 16) + (acc & 0xffff);	\
465 		acc += acc >> 16;			\
466 		cksum = (u_short) ~acc;			\
467 	} else {					\
468 		acc = (acc >> 16) + (acc & 0xffff);	\
469 		acc += acc >> 16;			\
470 		cksum = (u_short) acc;			\
471 	}						\
472 }
473 
474 /**
475  * Adjust mss to make IP packet be shorter than or equal MTU.
476  *
477  * @param	pktp	pointer that indicates IP packet
478  * @param	lpktp	length
479  * @param	mtu	MTU
480  */
481 int
adjust_tcp_mss(u_char * pktp,int lpktp,int mtu)482 adjust_tcp_mss(u_char *pktp, int lpktp, int mtu)
483 {
484 	int opt, optlen, acc, ip_off, mss, maxmss;
485 	struct ip *pip;
486 	struct tcphdr *th;
487 
488 	if (lpktp < sizeof(struct ip) + sizeof(struct tcphdr))
489 		return 1;
490 
491 	pip = (struct ip *)pktp;
492 	ip_off = ntohs(pip->ip_off);
493 
494 	/* exclude non-TCP packet or fragmented packet. */
495 	if (pip->ip_p != IPPROTO_TCP || (ip_off & IP_MF) != 0 ||
496 	    (ip_off & IP_OFFMASK) != 0)
497 		return 0;
498 
499 	pktp += pip->ip_hl << 2;
500 	lpktp -= pip->ip_hl << 2;
501 
502 	/* broken packet */
503 	if (sizeof(struct tcphdr) > lpktp)
504 		return 1;
505 
506 	th = (struct tcphdr *)pktp;
507 	/* MSS is selected only from SYN segment. (See RFC 793) */
508 	if ((th->th_flags & TH_SYN) == 0)
509 		return 0;
510 
511 	lpktp = MINIMUM(th->th_off << 4, lpktp);
512 
513 	pktp += sizeof(struct tcphdr);
514 	lpktp -= sizeof(struct tcphdr);
515 
516 	while (lpktp >= TCPOLEN_MAXSEG) {
517 		GETCHAR(opt, pktp);
518 		switch (opt) {
519 		case TCPOPT_MAXSEG:
520 			GETCHAR(optlen, pktp);
521 			GETSHORT(mss, pktp);
522 			maxmss = MAXMSS(mtu);
523 			if (mss > maxmss) {
524 				pktp-=2;
525 				PUTSHORT(maxmss, pktp);
526 				acc = htons(mss);
527 				acc -= htons(maxmss);
528 				ADJUST_CHECKSUM(acc, th->th_sum);
529 			}
530 			return 0;
531 			/* NOTREACHED */
532 			break;
533 		case TCPOPT_EOL:
534 			return 0;
535 			/* NOTREACHED */
536 			break;
537 		case TCPOPT_NOP:
538 			lpktp--;
539 			break;
540 		default:
541 			GETCHAR(optlen, pktp);
542 			if (optlen < 2)	/* packet is broken */
543 				return 1;
544 			pktp += optlen - 2;
545 			lpktp -= optlen;
546 			break;
547 		}
548 	}
549 	return 0;
550 }
551