Add tcpoptions to ipfw. This works much in the same way as ipoptions do.

It also squashes 99% of packet kiddie synflood orgies.  For example, to
rate syn packets without MSS,

ipfw pipe 10 config 56Kbit/s queue 10Packets
ipfw add pipe 10 tcp from any to any in setup tcpoptions !mss

Submitted by:  Richard A. Steenbergen <ras@e-gerbil.net>
This commit is contained in:
dan 2000-06-08 15:34:51 +00:00
parent c5a16b5d15
commit 9214e704a3
4 changed files with 168 additions and 5 deletions

View File

@ -650,6 +650,25 @@ The supported IP options are:
The absence of a particular option may be denoted
with a
.Ql ! .
.It Cm tcpoptions Ar spec
Match if the TCP header contains the comma separated list of
options specified in
.Ar spec .
The supported TCP options are:
.Pp
.Cm mss
(maximum segment size),
.Cm window
(tcp window advertisement),
.Cm sack
(selective ack),
.Cm ts
(rfc1323 timestamp) and
.Cm cc
(rfc1644 t/tcp connection count).
The absence of a particular option may be denoted
with a
.Ql ! .
.It Cm established
TCP packets only.
Match packets that have the RST or ACK bits set.

View File

@ -437,7 +437,7 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
#define PRINTFLG(x) {if (_flg_printed) printf(",");\
printf(x); _flg_printed = 1;}
printf(" tcpflg ");
printf(" tcpflags ");
if (chain->fw_tcpf & IP_FW_TCPF_FIN) PRINTFLG("fin");
if (chain->fw_tcpnf & IP_FW_TCPF_FIN) PRINTFLG("!fin");
if (chain->fw_tcpf & IP_FW_TCPF_SYN) PRINTFLG("syn");
@ -451,6 +451,24 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
if (chain->fw_tcpf & IP_FW_TCPF_URG) PRINTFLG("urg");
if (chain->fw_tcpnf & IP_FW_TCPF_URG) PRINTFLG("!urg");
}
if (chain->fw_tcpopt || chain->fw_tcpnopt) {
int _opt_printed = 0;
#define PRINTTOPT(x) {if (_opt_printed) printf(",");\
printf(x); _opt_printed = 1;}
printf(" tcpoptions ");
if (chain->fw_tcpopt & IP_FW_TCPOPT_MSS) PRINTTOPT("mss");
if (chain->fw_tcpnopt & IP_FW_TCPOPT_MSS) PRINTTOPT("!mss");
if (chain->fw_tcpopt & IP_FW_TCPOPT_WINDOW) PRINTTOPT("window");
if (chain->fw_tcpnopt & IP_FW_TCPOPT_WINDOW) PRINTTOPT("!window");
if (chain->fw_tcpopt & IP_FW_TCPOPT_SACK) PRINTTOPT("sack");
if (chain->fw_tcpnopt & IP_FW_TCPOPT_SACK) PRINTTOPT("!sack");
if (chain->fw_tcpopt & IP_FW_TCPOPT_TS) PRINTTOPT("ts");
if (chain->fw_tcpnopt & IP_FW_TCPOPT_TS) PRINTTOPT("!ts");
if (chain->fw_tcpopt & IP_FW_TCPOPT_CC) PRINTTOPT("cc");
if (chain->fw_tcpnopt & IP_FW_TCPOPT_CC) PRINTTOPT("!cc");
}
if (chain->fw_flg & IP_FW_F_ICMPBIT) {
int type_index;
int first = 1;
@ -818,6 +836,7 @@ show_usage(const char *fmt, ...)
" {established|setup}\n"
" tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n"
" ipoptions [!]{ssrr|lsrr|rr|ts},...\n"
" tcpoptions [!]{mss|window|sack|ts|cc},...\n"
" icmptypes {type[,type]}...\n"
" pipeconfig:\n"
" {bw|bandwidth} <number>{bit/s|Kbit/s|Mbit/s|Bytes/s|KBytes/s|MBytes/s}\n"
@ -1025,9 +1044,7 @@ fill_port(cnt, ptr, off, arg)
}
static void
fill_tcpflag(set, reset, vp)
u_char *set, *reset;
char **vp;
fill_tcpflag(u_char *set, u_char *reset, char **vp)
{
char *p = *vp,*q;
u_char *d;
@ -1066,6 +1083,45 @@ fill_tcpflag(set, reset, vp)
}
}
static void
fill_tcpopts(u_char *set, u_char *reset, char **vp)
{
char *p = *vp,*q;
u_char *d;
while (p && *p) {
struct tpcopts {
char * name;
u_char value;
} opts[] = {
{ "mss", IP_FW_TCPOPT_MSS },
{ "window", IP_FW_TCPOPT_WINDOW },
{ "sack", IP_FW_TCPOPT_SACK },
{ "ts", IP_FW_TCPOPT_TS },
{ "cc", IP_FW_TCPOPT_CC },
};
int i;
if (*p == '!') {
p++;
d = reset;
} else {
d = set;
}
q = strchr(p, ',');
if (q)
*q++ = '\0';
for (i = 0; i < sizeof(opts) / sizeof(opts[0]); ++i)
if (!strncmp(p, opts[i].name, strlen(p))) {
*d |= opts[i].value;
break;
}
if (i == sizeof(opts) / sizeof(opts[0]))
show_usage("invalid tcp option ``%s''", p);
p = q;
}
}
static void
fill_ipopt(u_char *set, u_char *reset, char **vp)
{
@ -1839,7 +1895,7 @@ add(ac,av)
rule.fw_tcpnf |= IP_FW_TCPF_ACK;
av++; ac--; continue;
}
if (!strncmp(*av,"tcpflags",strlen(*av))) {
if (!strncmp(*av,"tcpflags",strlen(*av)) || !strncmp(*av,"tcpflgs",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
@ -1847,6 +1903,14 @@ add(ac,av)
fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av);
av++; ac--; continue;
}
if (!strncmp(*av,"tcpoptions",strlen(*av)) || !strncmp(*av, "tcpopts",strlen(*av))) {
av++; ac--;
if (!ac)
show_usage("missing argument"
" for ``tcpflags''");
fill_tcpopts(&rule.fw_tcpopt, &rule.fw_tcpnopt, av);
av++; ac--; continue;
}
}
if (rule.fw_prot == IPPROTO_ICMP) {
if (!strncmp(*av,"icmptypes",strlen(*av))) {

View File

@ -353,6 +353,73 @@ ipopts_match(struct ip *ip, struct ip_fw *f)
return 0;
}
static int
tcpopts_match(struct tcphdr *tcp, struct ip_fw *f)
{
register u_char *cp;
int opt, optlen, cnt;
u_char opts, nopts, nopts_sve;
cp = (u_char *)(tcp + 1);
cnt = (tcp->th_off << 2) - sizeof (struct tcphdr);
opts = f->fw_tcpopt;
nopts = nopts_sve = f->fw_tcpnopt;
for (; cnt > 0; cnt -= optlen, cp += optlen) {
opt = cp[0];
if (opt == TCPOPT_EOL)
break;
if (opt == TCPOPT_NOP)
optlen = 1;
else {
optlen = cp[1];
if (optlen <= 0)
break;
}
switch (opt) {
default:
break;
case TCPOPT_MAXSEG:
opts &= ~IP_FW_TCPOPT_MSS;
nopts &= ~IP_FW_TCPOPT_MSS;
break;
case TCPOPT_WINDOW:
opts &= ~IP_FW_TCPOPT_WINDOW;
nopts &= ~IP_FW_TCPOPT_WINDOW;
break;
case TCPOPT_SACK_PERMITTED:
case TCPOPT_SACK:
opts &= ~IP_FW_TCPOPT_SACK;
nopts &= ~IP_FW_TCPOPT_SACK;
break;
case TCPOPT_TIMESTAMP:
opts &= ~IP_FW_TCPOPT_TS;
nopts &= ~IP_FW_TCPOPT_TS;
break;
case TCPOPT_CC:
case TCPOPT_CCNEW:
case TCPOPT_CCECHO:
opts &= ~IP_FW_TCPOPT_CC;
nopts &= ~IP_FW_TCPOPT_CC;
break;
}
if (opts == nopts)
break;
}
if (opts == 0 && nopts == nopts_sve)
return 1;
else
return 0;
}
static __inline int
iface_match(struct ifnet *ifp, union ip_fw_if *ifu, int byname)
{
@ -1143,6 +1210,9 @@ ip_fw_chk(struct ip **pip, int hlen,
break;
}
tcp = (struct tcphdr *) ((u_int32_t *)ip + ip->ip_hl);
if (f->fw_tcpopt != f->fw_tcpnopt && !tcpopts_match(tcp, f))
continue;
if (f->fw_tcpf != f->fw_tcpnf && !tcpflg_match(tcp, f))
continue;
goto check_ports;

View File

@ -64,6 +64,7 @@ struct ip_fw {
unsigned fw_icmptypes[IP_FW_ICMPTYPES_DIM]; /* ICMP types bitmap */
} fw_uar;
u_char fw_ipopt,fw_ipnopt; /* IP options set/unset */
u_char fw_tcpopt,fw_tcpnopt; /* TCP options set/unset */
u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */
long timestamp; /* timestamp (tv_sec) of last match */
union ip_fw_if fw_in_if, fw_out_if; /* Incoming and outgoing interfaces */
@ -229,6 +230,15 @@ struct ipfw_dyn_rule {
#define IP_FW_IPOPT_RR 0x04
#define IP_FW_IPOPT_TS 0x08
/*
* Definitions for TCP option names.
*/
#define IP_FW_TCPOPT_MSS 0x01
#define IP_FW_TCPOPT_WINDOW 0x02
#define IP_FW_TCPOPT_SACK 0x04
#define IP_FW_TCPOPT_TS 0x08
#define IP_FW_TCPOPT_CC 0x10
/*
* Definitions for TCP flags.
*/