1 /* $OpenBSD: npppd_subr.c,v 1.23 2024/09/20 02:00:46 jsg 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 * 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 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_pton(AF_INET, 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 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 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 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 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 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 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 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 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 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