diff options
Diffstat (limited to 'usr.sbin/ppp/ip.c')
-rw-r--r-- | usr.sbin/ppp/ip.c | 993 |
1 files changed, 993 insertions, 0 deletions
diff --git a/usr.sbin/ppp/ip.c b/usr.sbin/ppp/ip.c new file mode 100644 index 0000000..5cd2272 --- /dev/null +++ b/usr.sbin/ppp/ip.c @@ -0,0 +1,993 @@ +/*- + * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org> + * based on work by Toshiharu OHNO <tony-o@iij.ad.jp> + * Internet Initiative Japan, Inc (IIJ) + * 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$ + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#ifndef NOINET6 +#include <netinet/icmp6.h> +#include <netinet/ip6.h> +#endif +#include <netinet/ip_icmp.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> +#include <sys/un.h> + +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "layer.h" +#include "proto.h" +#include "mbuf.h" +#include "log.h" +#include "defs.h" +#include "timer.h" +#include "fsm.h" +#include "lqr.h" +#include "hdlc.h" +#include "throughput.h" +#include "iplist.h" +#include "slcompress.h" +#include "ncpaddr.h" +#include "ip.h" +#include "ipcp.h" +#include "filter.h" +#include "descriptor.h" +#include "lcp.h" +#include "ccp.h" +#include "link.h" +#include "mp.h" +#ifndef NORADIUS +#include "radius.h" +#endif +#include "ipv6cp.h" +#include "ncp.h" +#include "bundle.h" +#include "tun.h" + + +#define OPCODE_QUERY 0 +#define OPCODE_IQUERY 1 +#define OPCODE_STATUS 2 + +struct dns_header { + u_short id; + unsigned qr : 1; + unsigned opcode : 4; + unsigned aa : 1; + unsigned tc : 1; + unsigned rd : 1; + unsigned ra : 1; + unsigned z : 3; + unsigned rcode : 4; + u_short qdcount; + u_short ancount; + u_short nscount; + u_short arcount; +}; + +static const char * +dns_Qclass2Txt(u_short qclass) +{ + static char failure[6]; + struct { + u_short id; + const char *txt; + } qtxt[] = { + /* rfc1035 */ + { 1, "IN" }, { 2, "CS" }, { 3, "CH" }, { 4, "HS" }, { 255, "*" } + }; + unsigned f; + + for (f = 0; f < sizeof qtxt / sizeof *qtxt; f++) + if (qtxt[f].id == qclass) + return qtxt[f].txt; + + return HexStr(qclass, failure, sizeof failure); +} + +static const char * +dns_Qtype2Txt(u_short qtype) +{ + static char failure[6]; + struct { + u_short id; + const char *txt; + } qtxt[] = { + /* rfc1035/rfc1700 */ + { 1, "A" }, { 2, "NS" }, { 3, "MD" }, { 4, "MF" }, { 5, "CNAME" }, + { 6, "SOA" }, { 7, "MB" }, { 8, "MG" }, { 9, "MR" }, { 10, "NULL" }, + { 11, "WKS" }, { 12, "PTR" }, { 13, "HINFO" }, { 14, "MINFO" }, + { 15, "MX" }, { 16, "TXT" }, { 17, "RP" }, { 18, "AFSDB" }, + { 19, "X25" }, { 20, "ISDN" }, { 21, "RT" }, { 22, "NSAP" }, + { 23, "NSAP-PTR" }, { 24, "SIG" }, { 25, "KEY" }, { 26, "PX" }, + { 27, "GPOS" }, { 28, "AAAA" }, { 252, "AXFR" }, { 253, "MAILB" }, + { 254, "MAILA" }, { 255, "*" } + }; + unsigned f; + + for (f = 0; f < sizeof qtxt / sizeof *qtxt; f++) + if (qtxt[f].id == qtype) + return qtxt[f].txt; + + return HexStr(qtype, failure, sizeof failure); +} + +static __inline int +PortMatch(int op, u_short pport, u_short rport) +{ + switch (op) { + case OP_EQ: + return pport == rport; + case OP_GT: + return pport > rport; + case OP_LT: + return pport < rport; + default: + return 0; + } +} + +/* + * Return a text string representing the cproto protocol number. + * + * The purpose of this routine is calculate this result, for + * the many times it is needed in FilterCheck, only on demand + * (i.e. when the corresponding logging functions are invoked). + * + * This optimization saves, over the previous implementation, which + * calculated prototxt at the beginning of FilterCheck, an + * open/read/close system call sequence per packet, approximately + * halving the ppp system overhead and reducing the overall (u + s) + * time by 38%. + * + * The caching performed here is just a side effect. + */ +static const char * +prototxt(int cproto) +{ + static int oproto = -1; + static char protobuff[16] = "-1"; + struct protoent *pe; + + if (cproto == oproto) + return protobuff; + if ((pe = getprotobynumber(cproto)) == NULL) + snprintf(protobuff, sizeof protobuff, "%d", cproto); + else + snprintf(protobuff, sizeof protobuff, "%s", pe->p_name); + oproto = cproto; + return (protobuff); +} + +/* + * Check a packet against the given filter + * Returns 0 to accept the packet, non-zero to drop the packet. + * If psecs is not NULL, populate it with the timeout associated + * with the filter rule matched. + * + * If filtering is enabled, the initial fragment of a datagram must + * contain the complete protocol header, and subsequent fragments + * must not attempt to over-write it. + * + * One (and only one) of pip or pip6 must be set. + */ +int +FilterCheck(const unsigned char *packet, +#ifdef NOINET6 + u_int32_t family __unused, +#else + u_int32_t family, +#endif + const struct filter *filter, unsigned *psecs) +{ + int gotinfo; /* true if IP payload decoded */ + int cproto; /* IPPROTO_* protocol number if (gotinfo) */ + int estab, syn, finrst; /* TCP state flags if (gotinfo) */ + u_short sport, dport; /* src, dest port from packet if (gotinfo) */ + int n; /* filter rule to process */ + int len; /* bytes used in dbuff */ + int didname; /* true if filter header printed */ + int match; /* true if condition matched */ + int mindata; /* minimum data size or zero */ + const struct filterent *fp = filter->rule; + char dbuff[100], dstip[16]; + struct ncpaddr srcaddr, dstaddr; + const char *payload; /* IP payload */ + int datalen; /* IP datagram length */ + + if (fp->f_action == A_NONE) + return 0; /* No rule is given. Permit this packet */ + +#ifndef NOINET6 + if (family == AF_INET6) { + const struct ip6_hdr *pip6 = (const struct ip6_hdr *)packet; + + ncpaddr_setip6(&srcaddr, &pip6->ip6_src); + ncpaddr_setip6(&dstaddr, &pip6->ip6_dst); + datalen = ntohs(pip6->ip6_plen); + payload = packet + sizeof *pip6; + cproto = pip6->ip6_nxt; + } else +#endif + { + /* + * Deny any packet fragment that tries to over-write the header. + * Since we no longer have the real header available, punt on the + * largest normal header - 20 bytes for TCP without options, rounded + * up to the next possible fragment boundary. Since the smallest + * `legal' MTU is 576, and the smallest recommended MTU is 296, any + * fragmentation within this range is dubious at best + */ + const struct ip *pip = (const struct ip *)packet; + + len = ntohs(pip->ip_off) & IP_OFFMASK; /* fragment offset */ + if (len > 0) { /* Not first fragment within datagram */ + if (len < (24 >> 3)) { /* don't allow fragment to over-write header */ + log_Printf(LogFILTER, " error: illegal header\n"); + return 1; + } + /* permit fragments on in and out filter */ + if (!filter->fragok) { + log_Printf(LogFILTER, " error: illegal fragmentation\n"); + return 1; + } else + return 0; + } + + ncpaddr_setip4(&srcaddr, pip->ip_src); + ncpaddr_setip4(&dstaddr, pip->ip_dst); + datalen = ntohs(pip->ip_len) - (pip->ip_hl << 2); + payload = packet + (pip->ip_hl << 2); + cproto = pip->ip_p; + } + + + gotinfo = estab = syn = finrst = didname = 0; + sport = dport = 0; + + for (n = 0; n < MAXFILTERS; ) { + if (fp->f_action == A_NONE) { + n++; + fp++; + continue; + } + + if (!didname) { + log_Printf(LogDEBUG, "%s filter:\n", filter->name); + didname = 1; + } + + match = 0; + + if ((ncprange_family(&fp->f_src) == AF_UNSPEC || + ncprange_contains(&fp->f_src, &srcaddr)) && + (ncprange_family(&fp->f_dst) == AF_UNSPEC || + ncprange_contains(&fp->f_dst, &dstaddr))) { + if (fp->f_proto != 0) { + if (!gotinfo) { + const struct tcphdr *th; + const struct udphdr *uh; + const struct icmp *ih; +#ifndef NOINET6 + const struct icmp6_hdr *ih6; +#endif + mindata = 0; + sport = dport = 0; + estab = syn = finrst = -1; + + switch (cproto) { + case IPPROTO_ICMP: + mindata = 8; /* ICMP must be at least 8 octets */ + ih = (const struct icmp *)payload; + sport = ih->icmp_type; + if (log_IsKept(LogDEBUG)) + snprintf(dbuff, sizeof dbuff, "sport = %d", sport); + break; + +#ifndef NOINET6 + case IPPROTO_ICMPV6: + mindata = 8; /* ICMP must be at least 8 octets */ + ih6 = (const struct icmp6_hdr *)payload; + sport = ih6->icmp6_type; + if (log_IsKept(LogDEBUG)) + snprintf(dbuff, sizeof dbuff, "sport = %d", sport); + break; +#endif + + case IPPROTO_IGMP: + mindata = 8; /* IGMP uses 8-octet messages */ + break; + +#ifdef IPPROTO_GRE + case IPPROTO_GRE: + mindata = 2; /* GRE uses 2-octet+ messages */ + break; +#endif +#ifdef IPPROTO_OSPFIGP + case IPPROTO_OSPFIGP: + mindata = 8; /* IGMP uses 8-octet messages */ + break; +#endif +#ifndef NOINET6 + case IPPROTO_IPV6: + mindata = 20; /* RFC2893 Section 3.5: 5 * 32bit words */ + break; +#endif + + case IPPROTO_UDP: + mindata = 8; /* UDP header is 8 octets */ + uh = (const struct udphdr *)payload; + sport = ntohs(uh->uh_sport); + dport = ntohs(uh->uh_dport); + if (log_IsKept(LogDEBUG)) + snprintf(dbuff, sizeof dbuff, "sport = %d, dport = %d", + sport, dport); + break; + + case IPPROTO_TCP: + th = (const struct tcphdr *)payload; + /* + * TCP headers are variable length. The following code + * ensures that the TCP header length isn't de-referenced if + * the datagram is too short + */ + if (datalen < 20 || datalen < (th->th_off << 2)) { + log_Printf(LogFILTER, " error: TCP header incorrect\n"); + return 1; + } + sport = ntohs(th->th_sport); + dport = ntohs(th->th_dport); + estab = (th->th_flags & TH_ACK); + syn = (th->th_flags & TH_SYN); + finrst = (th->th_flags & (TH_FIN|TH_RST)); + if (log_IsKept(LogDEBUG)) { + if (!estab) + snprintf(dbuff, sizeof dbuff, + "flags = %02x, sport = %d, dport = %d", + th->th_flags, sport, dport); + else + *dbuff = '\0'; + } + break; + default: + break; + } + + if (datalen < mindata) { + log_Printf(LogFILTER, " error: proto %s must be at least" + " %d octets\n", prototxt(cproto), mindata); + return 1; + } + + if (log_IsKept(LogDEBUG)) { + if (estab != -1) { + len = strlen(dbuff); + snprintf(dbuff + len, sizeof dbuff - len, + ", estab = %d, syn = %d, finrst = %d", + estab, syn, finrst); + } + log_Printf(LogDEBUG, " Filter: proto = %s, %s\n", + prototxt(cproto), dbuff); + } + gotinfo = 1; + } + + if (log_IsKept(LogDEBUG)) { + if (fp->f_srcop != OP_NONE) { + snprintf(dbuff, sizeof dbuff, ", src %s %d", + filter_Op2Nam(fp->f_srcop), fp->f_srcport); + len = strlen(dbuff); + } else + len = 0; + if (fp->f_dstop != OP_NONE) { + snprintf(dbuff + len, sizeof dbuff - len, + ", dst %s %d", filter_Op2Nam(fp->f_dstop), + fp->f_dstport); + } else if (!len) + *dbuff = '\0'; + + log_Printf(LogDEBUG, " rule = %d: Address match, " + "check against proto %d%s, action = %s\n", + n, fp->f_proto, dbuff, filter_Action2Nam(fp->f_action)); + } + + if (cproto == fp->f_proto) { + if ((fp->f_srcop == OP_NONE || + PortMatch(fp->f_srcop, sport, fp->f_srcport)) && + (fp->f_dstop == OP_NONE || + PortMatch(fp->f_dstop, dport, fp->f_dstport)) && + (fp->f_estab == 0 || estab) && + (fp->f_syn == 0 || syn) && + (fp->f_finrst == 0 || finrst)) { + match = 1; + } + } + } else { + /* Address is matched and no protocol specified. Make a decision. */ + log_Printf(LogDEBUG, " rule = %d: Address match, action = %s\n", n, + filter_Action2Nam(fp->f_action)); + match = 1; + } + } else + log_Printf(LogDEBUG, " rule = %d: Address mismatch\n", n); + + if (match != fp->f_invert) { + /* Take specified action */ + if (fp->f_action < A_NONE) + fp = &filter->rule[n = fp->f_action]; + else { + if (fp->f_action == A_PERMIT) { + if (psecs != NULL) + *psecs = fp->timeout; + if (strcmp(filter->name, "DIAL") == 0) { + /* If dial filter then even print out accept packets */ + if (log_IsKept(LogFILTER)) { + snprintf(dstip, sizeof dstip, "%s", ncpaddr_ntoa(&dstaddr)); + log_Printf(LogFILTER, "%sbound rule = %d accept %s " + "src = %s:%d dst = %s:%d\n", filter->name, n, + prototxt(cproto), ncpaddr_ntoa(&srcaddr), sport, + dstip, dport); + } + } + return 0; + } else { + if (log_IsKept(LogFILTER)) { + snprintf(dstip, sizeof dstip, "%s", ncpaddr_ntoa(&dstaddr)); + log_Printf(LogFILTER, + "%sbound rule = %d deny %s src = %s/%d dst = %s/%d\n", + filter->name, n, prototxt(cproto), + ncpaddr_ntoa(&srcaddr), sport, dstip, dport); + } + return 1; + } /* Explict match. Deny this packet */ + } + } else { + n++; + fp++; + } + } + + if (log_IsKept(LogFILTER)) { + snprintf(dstip, sizeof dstip, "%s", ncpaddr_ntoa(&dstaddr)); + log_Printf(LogFILTER, + "%sbound rule = implicit deny %s src = %s/%d dst = %s/%d\n", + filter->name, prototxt(cproto), ncpaddr_ntoa(&srcaddr), + sport, dstip, dport); + } + + return 1; /* No rule matched, deny this packet */ +} + +static void +ip_LogDNS(const struct udphdr *uh, const char *direction) +{ + struct dns_header header; + const u_short *pktptr; + const u_char *ptr; + u_short *hptr, tmp; + unsigned len; + + ptr = (const char *)uh + sizeof *uh; + len = ntohs(uh->uh_ulen) - sizeof *uh; + if (len < sizeof header + 5) /* rfc1024 */ + return; + + pktptr = (const u_short *)ptr; + hptr = (u_short *)&header; + ptr += sizeof header; + len -= sizeof header; + + while (pktptr < (const u_short *)ptr) { + *hptr++ = ntohs(*pktptr); /* Careful of macro side-effects ! */ + pktptr++; + } + + if (header.opcode == OPCODE_QUERY && header.qr == 0) { + /* rfc1035 */ + char namewithdot[MAXHOSTNAMELEN + 1], *n; + const char *qtype, *qclass; + const u_char *end; + + n = namewithdot; + end = ptr + len - 4; + if (end - ptr >= (int)sizeof namewithdot) + end = ptr + sizeof namewithdot - 1; + while (ptr < end) { + len = *ptr++; + if ((int)len > end - ptr) + len = end - ptr; + if (n != namewithdot) + *n++ = '.'; + memcpy(n, ptr, len); + ptr += len; + n += len; + } + *n = '\0'; + + if (log_IsKept(LogDNS)) { + memcpy(&tmp, end, sizeof tmp); + qtype = dns_Qtype2Txt(ntohs(tmp)); + memcpy(&tmp, end + 2, sizeof tmp); + qclass = dns_Qclass2Txt(ntohs(tmp)); + + log_Printf(LogDNS, "%sbound query %s %s %s\n", + direction, qclass, qtype, namewithdot); + } + } +} + +/* + * Check if the given packet matches the given filter. + * One of pip or pip6 must be set. + */ +int +PacketCheck(struct bundle *bundle, u_int32_t family, + const unsigned char *packet, int nb, struct filter *filter, + const char *prefix, unsigned *psecs) +{ + char logbuf[200]; + static const char *const TcpFlags[] = { + "FIN", "SYN", "RST", "PSH", "ACK", "URG" + }; + const struct tcphdr *th; + const struct udphdr *uh; + const struct icmp *icmph; +#ifndef NOINET6 + const struct icmp6_hdr *icmp6h; +#endif + const unsigned char *payload; + struct ncpaddr srcaddr, dstaddr; + int cproto, mask, len, n, pri, logit, result, datalen, frag; + unsigned loglen; + u_char tos; + + logit = (log_IsKept(LogTCPIP) || log_IsKept(LogDNS)) && + (!filter || filter->logok); + loglen = 0; + pri = 0; + +#ifndef NOINET6 + if (family == AF_INET6) { + const struct ip6_hdr *pip6 = (const struct ip6_hdr *)packet; + + ncpaddr_setip6(&srcaddr, &pip6->ip6_src); + ncpaddr_setip6(&dstaddr, &pip6->ip6_dst); + datalen = ntohs(pip6->ip6_plen); + payload = packet + sizeof *pip6; + cproto = pip6->ip6_nxt; + tos = 0; /* XXX: pip6->ip6_vfc >> 4 ? */ + frag = 0; /* XXX: ??? */ + } else +#endif + { + const struct ip *pip = (const struct ip *)packet; + + ncpaddr_setip4(&srcaddr, pip->ip_src); + ncpaddr_setip4(&dstaddr, pip->ip_dst); + datalen = ntohs(pip->ip_len) - (pip->ip_hl << 2); + payload = packet + (pip->ip_hl << 2); + cproto = pip->ip_p; + tos = pip->ip_tos; + frag = ntohs(pip->ip_off) & IP_OFFMASK; + } + + uh = NULL; + + if (logit && loglen < sizeof logbuf) { + if (prefix) + snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s", prefix); + else if (filter) + snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s ", filter->name); + else + snprintf(logbuf + loglen, sizeof logbuf - loglen, " "); + loglen += strlen(logbuf + loglen); + } + + switch (cproto) { + case IPPROTO_ICMP: + if (logit && loglen < sizeof logbuf) { + len = datalen - sizeof *icmph; + icmph = (const struct icmp *)payload; + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "ICMP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), icmph->icmp_type); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), len, nb); + loglen += strlen(logbuf + loglen); + } + break; + +#ifndef NOINET6 + case IPPROTO_ICMPV6: + if (logit && loglen < sizeof logbuf) { + len = datalen - sizeof *icmp6h; + icmp6h = (const struct icmp6_hdr *)payload; + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "ICMP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), icmp6h->icmp6_type); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), len, nb); + loglen += strlen(logbuf + loglen); + } + break; +#endif + + case IPPROTO_UDP: + uh = (const struct udphdr *)payload; + if (tos == IPTOS_LOWDELAY && bundle->ncp.cfg.urgent.tos) + pri++; + + if (!frag && ncp_IsUrgentUdpPort(&bundle->ncp, ntohs(uh->uh_sport), + ntohs(uh->uh_dport))) + pri++; + + if (logit && loglen < sizeof logbuf) { + len = datalen - sizeof *uh; + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "UDP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), ntohs(uh->uh_sport)); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "%s:%d (%d/%d)", ncpaddr_ntoa(&dstaddr), ntohs(uh->uh_dport), + len, nb); + loglen += strlen(logbuf + loglen); + } + + if (Enabled(bundle, OPT_FILTERDECAP) && + payload[sizeof *uh] == HDLC_ADDR && + payload[sizeof *uh + 1] == HDLC_UI) { + u_short proto; + const char *type; + + memcpy(&proto, payload + sizeof *uh + 2, sizeof proto); + type = NULL; + + switch (ntohs(proto)) { + case PROTO_IP: + snprintf(logbuf + loglen, sizeof logbuf - loglen, " contains "); + result = PacketCheck(bundle, AF_INET, payload + sizeof *uh + 4, + nb - (payload - packet) - sizeof *uh - 4, filter, + logbuf, psecs); + if (result != -2) + return result; + type = "IP"; + break; + + case PROTO_VJUNCOMP: type = "compressed VJ"; break; + case PROTO_VJCOMP: type = "uncompressed VJ"; break; + case PROTO_MP: type = "Multi-link"; break; + case PROTO_ICOMPD: type = "Individual link CCP"; break; + case PROTO_COMPD: type = "CCP"; break; + case PROTO_IPCP: type = "IPCP"; break; + case PROTO_LCP: type = "LCP"; break; + case PROTO_PAP: type = "PAP"; break; + case PROTO_CBCP: type = "CBCP"; break; + case PROTO_LQR: type = "LQR"; break; + case PROTO_CHAP: type = "CHAP"; break; + } + if (type) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, + " - %s data", type); + loglen += strlen(logbuf + loglen); + } + } + + break; + +#ifdef IPPROTO_GRE + case IPPROTO_GRE: + if (logit && loglen < sizeof logbuf) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "GRE: %s ---> ", ncpaddr_ntoa(&srcaddr)); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), datalen, nb); + loglen += strlen(logbuf + loglen); + } + break; +#endif + +#ifdef IPPROTO_OSPFIGP + case IPPROTO_OSPFIGP: + if (logit && loglen < sizeof logbuf) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "OSPF: %s ---> ", ncpaddr_ntoa(&srcaddr)); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), datalen, nb); + loglen += strlen(logbuf + loglen); + } + break; +#endif + +#ifndef NOINET6 + case IPPROTO_IPV6: + if (logit && loglen < sizeof logbuf) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "IPv6: %s ---> ", ncpaddr_ntoa(&srcaddr)); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), datalen, nb); + loglen += strlen(logbuf + loglen); + } + + if (Enabled(bundle, OPT_FILTERDECAP)) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, " contains "); + result = PacketCheck(bundle, AF_INET6, payload, nb - (payload - packet), + filter, logbuf, psecs); + if (result != -2) + return result; + } + break; +#endif + + case IPPROTO_IPIP: + if (logit && loglen < sizeof logbuf) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "IPIP: %s ---> ", ncpaddr_ntoa(&srcaddr)); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "%s", ncpaddr_ntoa(&dstaddr)); + loglen += strlen(logbuf + loglen); + } + + if (Enabled(bundle, OPT_FILTERDECAP) && + ((const struct ip *)payload)->ip_v == 4) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, " contains "); + result = PacketCheck(bundle, AF_INET, payload, nb - (payload - packet), + filter, logbuf, psecs); + loglen += strlen(logbuf + loglen); + if (result != -2) + return result; + } + break; + + case IPPROTO_ESP: + if (logit && loglen < sizeof logbuf) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "ESP: %s ---> ", ncpaddr_ntoa(&srcaddr)); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s, spi %p", + ncpaddr_ntoa(&dstaddr), payload); + loglen += strlen(logbuf + loglen); + } + break; + + case IPPROTO_AH: + if (logit && loglen < sizeof logbuf) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "AH: %s ---> ", ncpaddr_ntoa(&srcaddr)); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s, spi %p", + ncpaddr_ntoa(&dstaddr), payload + sizeof(u_int32_t)); + loglen += strlen(logbuf + loglen); + } + break; + + case IPPROTO_IGMP: + if (logit && loglen < sizeof logbuf) { + uh = (const struct udphdr *)payload; + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "IGMP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), + ntohs(uh->uh_sport)); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "%s:%d", ncpaddr_ntoa(&dstaddr), ntohs(uh->uh_dport)); + loglen += strlen(logbuf + loglen); + } + break; + + case IPPROTO_TCP: + th = (const struct tcphdr *)payload; + if (tos == IPTOS_LOWDELAY && bundle->ncp.cfg.urgent.tos) + pri++; + + if (!frag && ncp_IsUrgentTcpPort(&bundle->ncp, ntohs(th->th_sport), + ntohs(th->th_dport))) + pri++; + + if (logit && loglen < sizeof logbuf) { + len = datalen - (th->th_off << 2); + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "TCP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), ntohs(th->th_sport)); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "%s:%d", ncpaddr_ntoa(&dstaddr), ntohs(th->th_dport)); + loglen += strlen(logbuf + loglen); + n = 0; + for (mask = TH_FIN; mask != 0x40; mask <<= 1) { + if (th->th_flags & mask) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, " %s", TcpFlags[n]); + loglen += strlen(logbuf + loglen); + } + n++; + } + snprintf(logbuf + loglen, sizeof logbuf - loglen, + " seq:%lx ack:%lx (%d/%d)", + (u_long)ntohl(th->th_seq), (u_long)ntohl(th->th_ack), len, nb); + loglen += strlen(logbuf + loglen); + if ((th->th_flags & TH_SYN) && nb > 40) { + const u_short *sp; + + sp = (const u_short *)(payload + 20); + if (ntohs(sp[0]) == 0x0204) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, + " MSS = %d", ntohs(sp[1])); + loglen += strlen(logbuf + loglen); + } + } + } + break; + + default: + if (prefix) + return -2; + + if (logit && loglen < sizeof logbuf) { + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "<%d>: %s ---> ", cproto, ncpaddr_ntoa(&srcaddr)); + loglen += strlen(logbuf + loglen); + snprintf(logbuf + loglen, sizeof logbuf - loglen, + "%s (%d)", ncpaddr_ntoa(&dstaddr), nb); + loglen += strlen(logbuf + loglen); + } + break; + } + + if (filter && FilterCheck(packet, family, filter, psecs)) { + if (logit) + log_Printf(LogTCPIP, "%s - BLOCKED\n", logbuf); + result = -1; + } else { + /* Check Keep Alive filter */ + if (logit && log_IsKept(LogTCPIP)) { + unsigned alivesecs; + + alivesecs = 0; + if (filter && + FilterCheck(packet, family, &bundle->filter.alive, &alivesecs)) + log_Printf(LogTCPIP, "%s - NO KEEPALIVE\n", logbuf); + else if (psecs != NULL) { + if(*psecs == 0) + *psecs = alivesecs; + if (*psecs) { + if (*psecs != alivesecs) + log_Printf(LogTCPIP, "%s - (timeout = %d / ALIVE = %d secs)\n", + logbuf, *psecs, alivesecs); + else + log_Printf(LogTCPIP, "%s - (timeout = %d secs)\n", logbuf, *psecs); + } else + log_Printf(LogTCPIP, "%s\n", logbuf); + } + } + result = pri; + } + + if (filter && uh && ntohs(uh->uh_dport) == 53 && log_IsKept(LogDNS)) + ip_LogDNS(uh, filter->name); + + return result; +} + +static size_t +ip_Input(struct bundle *bundle, struct link *l, struct mbuf *bp, u_int32_t af) +{ + ssize_t nw; + size_t nb; + struct tun_data tun; + char *data; + unsigned secs, alivesecs; + + nb = m_length(bp); + if (nb > sizeof tun.data) { + log_Printf(LogWARN, "ip_Input: %s: Packet too large (got %zd, max %d)\n", + l->name, nb, (int)(sizeof tun.data)); + m_freem(bp); + return 0; + } + mbuf_Read(bp, tun.data, nb); + + secs = 0; + if (PacketCheck(bundle, af, tun.data, nb, &bundle->filter.in, + NULL, &secs) < 0) + return 0; + + alivesecs = 0; + if (!FilterCheck(tun.data, af, &bundle->filter.alive, &alivesecs)) { + if (secs == 0) + secs = alivesecs; + bundle_StartIdleTimer(bundle, secs); + } + + if (bundle->dev.header) { + tun.header.family = htonl(af); + nb += sizeof tun - sizeof tun.data; + data = (char *)&tun; + } else + data = tun.data; + + nw = write(bundle->dev.fd, data, nb); + if (nw != (ssize_t)nb) { + if (nw == -1) + log_Printf(LogERROR, "ip_Input: %s: wrote %zd, got %s\n", + l->name, nb, strerror(errno)); + else + log_Printf(LogERROR, "ip_Input: %s: wrote %zd, got %zd\n", l->name, nb, + nw); + } + + return nb; +} + +struct mbuf * +ipv4_Input(struct bundle *bundle, struct link *l, struct mbuf *bp) +{ + int nb; + + if (bundle->ncp.ipcp.fsm.state != ST_OPENED) { + log_Printf(LogWARN, "ipv4_Input: IPCP not open - packet dropped\n"); + m_freem(bp); + return NULL; + } + + m_settype(bp, MB_IPIN); + + nb = ip_Input(bundle, l, bp, AF_INET); + ipcp_AddInOctets(&bundle->ncp.ipcp, nb); + + return NULL; +} + +#ifndef NOINET6 +struct mbuf * +ipv6_Input(struct bundle *bundle, struct link *l, struct mbuf *bp) +{ + int nb; + + if (bundle->ncp.ipv6cp.fsm.state != ST_OPENED) { + log_Printf(LogWARN, "ipv6_Input: IPV6CP not open - packet dropped\n"); + m_freem(bp); + return NULL; + } + + m_settype(bp, MB_IPV6IN); + + nb = ip_Input(bundle, l, bp, AF_INET6); + ipv6cp_AddInOctets(&bundle->ncp.ipv6cp, nb); + + return NULL; +} +#endif |