summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordan <dan@FreeBSD.org>2000-06-08 15:34:51 +0000
committerdan <dan@FreeBSD.org>2000-06-08 15:34:51 +0000
commitc3897dad80cc844b7f75b8cfa53825c031d02605 (patch)
treea156e91e6ecec2e7ac6b722fecb49d451eb5515b
parent31f827d91f8e4147518f8d6192a98d5196ef935a (diff)
downloadFreeBSD-src-c3897dad80cc844b7f75b8cfa53825c031d02605.zip
FreeBSD-src-c3897dad80cc844b7f75b8cfa53825c031d02605.tar.gz
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>
-rw-r--r--sbin/ipfw/ipfw.819
-rw-r--r--sbin/ipfw/ipfw.c74
-rw-r--r--sys/netinet/ip_fw.c70
-rw-r--r--sys/netinet/ip_fw.h10
4 files changed, 168 insertions, 5 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index b7104a8..13dde53 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -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.
diff --git a/sbin/ipfw/ipfw.c b/sbin/ipfw/ipfw.c
index 0c43203..75cb893 100644
--- a/sbin/ipfw/ipfw.c
+++ b/sbin/ipfw/ipfw.c
@@ -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;
@@ -1067,6 +1084,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)
{
char *p = *vp,*q;
@@ -1839,7 +1895,7 @@ badviacombo:
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 @@ badviacombo:
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))) {
diff --git a/sys/netinet/ip_fw.c b/sys/netinet/ip_fw.c
index b9245bd..4ed2b0b 100644
--- a/sys/netinet/ip_fw.c
+++ b/sys/netinet/ip_fw.c
@@ -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 @@ again:
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;
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index 9467624..78fd18d 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -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 */
@@ -230,6 +231,15 @@ struct ipfw_dyn_rule {
#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.
*/
#define IP_FW_TCPF_FIN TH_FIN
OpenPOWER on IntegriCloud