mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-15 10:17:20 +00:00
f80b39d182
This allows preferring small (e.g. ACK) packets, in upload heavy environments. It was already possible to mark packets urgent based on destination port. This option piggy backs on that feature.
1000 lines
30 KiB
C
1000 lines
30 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* 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[NCP_ASCIIBUFFERSIZE];
|
|
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;
|
|
} /* Explicit 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++;
|
|
else if (!frag && ncp_IsUrgentTcpLen(&bundle->ncp, datalen))
|
|
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);
|
|
}
|
|
}
|
|
snprintf(logbuf + loglen, sizeof logbuf - loglen, " pri:%d", pri);
|
|
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
|