/* $OpenBSD: npppd_subr.c,v 1.21 2021/03/29 03:54:39 yasuoka Exp $ */ /*- * Copyright (c) 2009 Internet Initiative Japan Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /**@file * This file provides helper functions for npppd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debugutil.h" #include "addr_range.h" #include "npppd_defs.h" #include "npppd_subr.h" #include "privsep.h" #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) static u_int16_t route_seq = 0; static int in_route0(int, struct in_addr *, struct in_addr *, struct in_addr *, int, const char *, uint32_t); #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) static const char * skip_space(const char *s) { const char *r; for (r = s; *r != '\0' && isspace((unsigned char)*r); r++) ;; /* skip */ return r; } /** * Read and store IPv4 address of name server from resolv.conf. * The path of resolv.conf is taken from _PATH_RESCONF in resolv.h. */ int load_resolv_conf(struct in_addr *pri, struct in_addr *sec) { FILE *filep; int i; struct in_addr *addr; char *ap, *line, buf[BUFSIZ]; pri->s_addr = INADDR_NONE; sec->s_addr = INADDR_NONE; filep = NULL; if ((filep = priv_fopen(_PATH_RESCONF)) == NULL) return 1; i = 0; while (fgets(buf, sizeof(buf), filep) != NULL) { line = (char *)skip_space(buf); if (strncmp(line, "nameserver", 10) != 0) continue; line += 10; if (!isspace((unsigned char)*line)) continue; while ((ap = strsep(&line, " \t\r\n")) != NULL) { if (*ap == '\0') continue; if (i == 0) addr = pri; else addr = sec; if (inet_aton(ap, addr) != 1) { /* * FIXME: If configured IPv6, it may have IPv6 * FIXME: address. For the present, continue. */ continue; } addr->s_addr = addr->s_addr; if (++i >= 2) goto end_loop; } } end_loop: if (filep != NULL) fclose(filep); return 0; } /* Add and delete routing entry. */ 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) { struct rt_msghdr *rtm; struct sockaddr_in sdest, smask, sgate; struct sockaddr_dl *sdl; char dl_buf[512]; /* enough size */ char *cp, buf[sizeof(*rtm) + sizeof(struct sockaddr_in) * 3 + sizeof(dl_buf) + 128]; const char *strtype; int rval, flags, sock; sock = -1; ASSERT(type == RTM_ADD || type == RTM_DELETE); if(type == RTM_ADD) strtype = "RTM_ADD"; else strtype = "RTM_DELETE"; memset(buf, 0, sizeof(buf)); memset(&sdest, 0, sizeof(sdest)); memset(&smask, 0, sizeof(smask)); memset(&sgate, 0, sizeof(sgate)); memset(&dl_buf, 0, sizeof(dl_buf)); sdl = (struct sockaddr_dl *)dl_buf; sdest.sin_addr = *dest; if (mask != NULL) smask.sin_addr = *mask; if (gate != NULL) sgate.sin_addr = *gate; sdest.sin_family = smask.sin_family = sgate.sin_family = AF_INET; sdest.sin_len = smask.sin_len = sgate.sin_len = sizeof(sgate); rtm = (struct rt_msghdr *)buf; rtm->rtm_version = RTM_VERSION; rtm->rtm_type = type; rtm->rtm_flags = rtm_flags; if (gate != NULL) rtm->rtm_flags |= RTF_GATEWAY; if (mask == NULL) rtm->rtm_flags |= RTF_HOST; if (type == RTM_ADD && mtu > 0) { rtm->rtm_inits = RTV_MTU; rtm->rtm_rmx.rmx_mtu = mtu; } if (type == RTM_ADD) rtm->rtm_flags |= RTF_UP; rtm->rtm_addrs = RTA_DST; if (gate != NULL) rtm->rtm_addrs |= RTA_GATEWAY; if (mask != NULL) rtm->rtm_addrs |= RTA_NETMASK; #ifdef RTA_IFP if (ifname != NULL) rtm->rtm_addrs |= RTA_IFP; #endif rtm->rtm_pid = getpid(); route_seq = ((route_seq + 1)&0x0000ffff); rtm->rtm_seq = route_seq; cp = (char *)rtm; cp += ROUNDUP(sizeof(*rtm)); memcpy(cp, &sdest, sdest.sin_len); cp += ROUNDUP(sdest.sin_len); if (gate != NULL) { memcpy(cp, &sgate, sgate.sin_len); cp += ROUNDUP(sgate.sin_len); } if (mask != NULL) { memcpy(cp, &smask, smask.sin_len); cp += ROUNDUP(smask.sin_len); } #ifdef RTA_IFP if (ifname != NULL) { strlcpy(sdl->sdl_data, ifname, IFNAMSIZ); sdl->sdl_family = AF_LINK; sdl->sdl_len = offsetof(struct sockaddr_dl, sdl_data) +IFNAMSIZ; sdl->sdl_index = if_nametoindex(ifname); memcpy(cp, sdl, sdl->sdl_len); cp += ROUNDUP(sdl->sdl_len); } #endif rtm->rtm_msglen = cp - buf; if ((sock = priv_socket(AF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) { log_printf(LOG_ERR, "socket() failed in %s() on %s : %m", __func__, strtype); goto fail; } if ((flags = fcntl(sock, F_GETFL)) < 0) { log_printf(LOG_ERR, "fcntl(,F_GETFL) failed on %s : %m", __func__); goto fail; } if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { log_printf(LOG_ERR, "fcntl(,F_SETFL) failed on %s : %m", __func__); goto fail; } if ((rval = priv_send(sock, buf, rtm->rtm_msglen, 0)) <= 0) { if ((type == RTM_DELETE && errno == ESRCH) || (type == RTM_ADD && errno == EEXIST)) { log_printf(LOG_DEBUG, "write() failed in %s on %s : %m", __func__, strtype); } else { log_printf(LOG_WARNING, "write() failed in %s on %s : %m", __func__, strtype); } goto fail; } close(sock); return 0; fail: if (sock >= 0) close(sock); return 1; } /** Add host routing entry. */ int in_host_route_add(struct in_addr *dest, struct in_addr *gate, const char *ifname, int mtu) { return in_route0(RTM_ADD, dest, NULL, gate, mtu, ifname, 0); } /** Delete host routing entry. */ int in_host_route_delete(struct in_addr *dest, struct in_addr *gate) { return in_route0(RTM_DELETE, dest, NULL, gate, 0, NULL, 0); } /** Add network routing entry. */ 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) { return in_route0(RTM_ADD, dest, mask, gate, mtu, ifname, rtm_flags); } /** Delete network routing entry. */ int in_route_delete(struct in_addr *dest, struct in_addr *mask, struct in_addr *gate, uint32_t rtm_flags) { return in_route0(RTM_DELETE, dest, mask, gate, 0, NULL, rtm_flags); } /** * Check whether a packet should reset idle timer * Returns 1 to don't reset timer (i.e. the packet is "idle" packet) */ int ip_is_idle_packet(const struct ip * pip, int len) { u_int16_t ip_off; const struct udphdr *uh; /* * Fragmented packet is not idle packet. * (Long packet which needs to fragment is not idle packet.) */ ip_off = ntohs(pip->ip_off); if ((ip_off & IP_MF) || ((ip_off & IP_OFFMASK) != 0)) return 0; switch (pip->ip_p) { case IPPROTO_IGMP: return 1; case IPPROTO_ICMP: /* Is length enough? */ if (pip->ip_hl * 4 + 8 > len) return 1; switch (((unsigned char *) pip)[pip->ip_hl * 4]) { case 0: /* Echo Reply */ case 8: /* Echo Request */ return 0; default: return 1; } case IPPROTO_UDP: case IPPROTO_TCP: /* * The place of port number of UDP and TCP is the same, * so can be shared. */ uh = (const struct udphdr *) (((const char *) pip) + (pip->ip_hl * 4)); /* Is length enough? */ if (pip->ip_hl * 4 + sizeof(struct udphdr) > len) return 1; switch (ntohs(uh->uh_sport)) { case 53: /* DOMAIN */ case 67: /* BOOTPS */ case 68: /* BOOTPC */ case 123: /* NTP */ case 137: /* NETBIOS-NS */ case 520: /* RIP */ return 1; } switch (ntohs(uh->uh_dport)) { case 53: /* DOMAIN */ case 67: /* BOOTPS */ case 68: /* BOOTPC */ case 123: /* NTP */ case 137: /* NETBIOS-NS */ case 520: /* RIP */ return 1; } return 0; default: return 0; } } /*********************************************************************** * Add and delete routing entry for the pool address. ***********************************************************************/ void in_addr_range_add_route(struct in_addr_range *range) { struct in_addr_range *range0; struct in_addr dest, mask, loop; for (range0 = range; range0 != NULL; range0 = range0->next){ dest.s_addr = htonl(range0->addr); mask.s_addr = htonl(range0->mask); loop.s_addr = htonl(INADDR_LOOPBACK); in_route_add(&dest, &mask, &loop, LOOPBACK_IFNAME, RTF_BLACKHOLE, 0); } log_printf(LOG_INFO, "Added routes for pooled addresses"); } void in_addr_range_delete_route(struct in_addr_range *range) { struct in_addr_range *range0; struct in_addr dest, mask, loop; for (range0 = range; range0 != NULL; range0 = range0->next){ dest.s_addr = htonl(range0->addr); mask.s_addr = htonl(range0->mask); loop.s_addr = htonl(INADDR_LOOPBACK); in_route_delete(&dest, &mask, &loop, RTF_BLACKHOLE); } log_printf(LOG_NOTICE, "Deleted routes for pooled addresses"); } /* GETSHORT is also defined in #include . */ #undef GETCHAR #undef GETSHORT #undef PUTSHORT #define GETCHAR(c, cp) { (c) = *(cp)++; } #define GETSHORT(s, cp) { \ (s) = *(cp)++ << 8; \ (s) |= *(cp)++; \ } #define PUTSHORT(s, cp) { \ *(cp)++ = (u_char) ((s) >> 8); \ *(cp)++ = (u_char) (s); \ } #define TCP_OPTLEN_IN_SEGMENT 12 /* timestamp option and padding */ #define MAXMSS(mtu) (mtu - sizeof(struct ip) - sizeof(struct tcphdr) - \ TCP_OPTLEN_IN_SEGMENT) /* adapted from FreeBSD:src/usr.sbin/ppp/tcpmss.c */ /* * Copyright (c) 2000 Ruslan Ermilov and Brian Somers * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/usr.sbin/ppp/tcpmss.c,v 1.1.4.3 2001/07/19 11:39:54 brian Exp $ */ /* * The following macro is used to update an internet checksum. "acc" is a * 32-bit accumulation of all the changes to the checksum (adding in old * 16-bit words and subtracting out new words), and "cksum" is the checksum * value to be updated. */ #define ADJUST_CHECKSUM(acc, cksum) { \ acc += cksum; \ if (acc < 0) { \ acc = -acc; \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) ~acc; \ } else { \ acc = (acc >> 16) + (acc & 0xffff); \ acc += acc >> 16; \ cksum = (u_short) acc; \ } \ } /** * Adjust mss to make IP packet be shorter than or equal MTU. * * @param pktp pointer that indicates IP packet * @param lpktp length * @param mtu MTU */ int adjust_tcp_mss(u_char *pktp, int lpktp, int mtu) { int opt, optlen, acc, ip_off, mss, maxmss; struct ip *pip; struct tcphdr *th; if (lpktp < sizeof(struct ip) + sizeof(struct tcphdr)) return 1; pip = (struct ip *)pktp; ip_off = ntohs(pip->ip_off); /* exclude non-TCP packet or fragmented packet. */ if (pip->ip_p != IPPROTO_TCP || (ip_off & IP_MF) != 0 || (ip_off & IP_OFFMASK) != 0) return 0; pktp += pip->ip_hl << 2; lpktp -= pip->ip_hl << 2; /* broken packet */ if (sizeof(struct tcphdr) > lpktp) return 1; th = (struct tcphdr *)pktp; /* MSS is selected only from SYN segment. (See RFC 793) */ if ((th->th_flags & TH_SYN) == 0) return 0; lpktp = MINIMUM(th->th_off << 4, lpktp); pktp += sizeof(struct tcphdr); lpktp -= sizeof(struct tcphdr); while (lpktp >= TCPOLEN_MAXSEG) { GETCHAR(opt, pktp); switch (opt) { case TCPOPT_MAXSEG: GETCHAR(optlen, pktp); GETSHORT(mss, pktp); maxmss = MAXMSS(mtu); if (mss > maxmss) { pktp-=2; PUTSHORT(maxmss, pktp); acc = htons(mss); acc -= htons(maxmss); ADJUST_CHECKSUM(acc, th->th_sum); } return 0; /* NOTREACHED */ break; case TCPOPT_EOL: return 0; /* NOTREACHED */ break; case TCPOPT_NOP: lpktp--; break; default: GETCHAR(optlen, pktp); if (optlen < 2) /* packet is broken */ return 1; pktp += optlen - 2; lpktp -= optlen; break; } } return 0; }