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