summaryrefslogtreecommitdiffstats
path: root/sys/netinet/tcp_input.c
diff options
context:
space:
mode:
authorattilio <attilio@FreeBSD.org>2011-04-25 17:13:40 +0000
committerattilio <attilio@FreeBSD.org>2011-04-25 17:13:40 +0000
commit2e19c21f223f834300d9e8b7fc46636902205453 (patch)
treea9b2378f15a6b24078acade80e7136f7779e5dbb /sys/netinet/tcp_input.c
parent99f9647714747def6d98acca6bfe0f865c0d2f1c (diff)
downloadFreeBSD-src-2e19c21f223f834300d9e8b7fc46636902205453.zip
FreeBSD-src-2e19c21f223f834300d9e8b7fc46636902205453.tar.gz
Add the possibility to verify MD5 hash of incoming TCP packets.
As long as this is a costy function, even when compiled in (along with the option TCP_SIGNATURE), it can be disabled via the net.inet.tcp.signature_verify_input sysctl. Sponsored by: Sandvine Incorporated Reviewed by: emaste, bz MFC after: 2 weeks
Diffstat (limited to 'sys/netinet/tcp_input.c')
-rw-r--r--sys/netinet/tcp_input.c103
1 files changed, 99 insertions, 4 deletions
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index cb9453f..cf1daa8 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -215,6 +215,12 @@ static void tcp_pulloutofband(struct socket *,
struct tcphdr *, struct mbuf *, int);
static void tcp_xmit_timer(struct tcpcb *, int);
static void tcp_newreno_partial_ack(struct tcpcb *, struct tcphdr *);
+static void inline tcp_fields_to_host(struct tcphdr *);
+#ifdef TCP_SIGNATURE
+static void inline tcp_fields_to_net(struct tcphdr *);
+static int inline tcp_signature_verify_input(struct mbuf *, int, int,
+ int, struct tcpopt *, struct tcphdr *, u_int);
+#endif
static void inline cc_ack_received(struct tcpcb *tp, struct tcphdr *th,
uint16_t type);
static void inline cc_conn_init(struct tcpcb *tp);
@@ -440,6 +446,40 @@ cc_post_recovery(struct tcpcb *tp, struct tcphdr *th)
tp->t_bytes_acked = 0;
}
+static inline void
+tcp_fields_to_host(struct tcphdr *th)
+{
+
+ th->th_seq = ntohl(th->th_seq);
+ th->th_ack = ntohl(th->th_ack);
+ th->th_win = ntohs(th->th_win);
+ th->th_urp = ntohs(th->th_urp);
+}
+
+#ifdef TCP_SIGNATURE
+static inline void
+tcp_fields_to_net(struct tcphdr *th)
+{
+
+ th->th_seq = htonl(th->th_seq);
+ th->th_ack = htonl(th->th_ack);
+ th->th_win = htons(th->th_win);
+ th->th_urp = htons(th->th_urp);
+}
+
+static inline int
+tcp_signature_verify_input(struct mbuf *m, int off0, int tlen, int optlen,
+ struct tcpopt *to, struct tcphdr *th, u_int tcpbflag)
+{
+ int ret;
+
+ tcp_fields_to_net(th);
+ ret = tcp_signature_verify(m, off0, tlen, optlen, to, th, tcpbflag);
+ tcp_fields_to_host(th);
+ return (ret);
+}
+#endif
+
/* Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. */
#ifdef INET6
#define ND6_HINT(tp) \
@@ -519,6 +559,9 @@ tcp_input(struct mbuf *m, int off0)
int thflags;
int rstreason = 0; /* For badport_bandlim accounting purposes */
uint8_t iptos;
+#ifdef TCP_SIGNATURE
+ uint8_t sig_checked = 0;
+#endif
#ifdef IPFIREWALL_FORWARD
struct m_tag *fwd_tag;
#endif
@@ -676,10 +719,7 @@ tcp_input(struct mbuf *m, int off0)
/*
* Convert TCP protocol specific fields to host format.
*/
- th->th_seq = ntohl(th->th_seq);
- th->th_ack = ntohl(th->th_ack);
- th->th_win = ntohs(th->th_win);
- th->th_urp = ntohs(th->th_urp);
+ tcp_fields_to_host(th);
/*
* Delay dropping TCP, IP headers, IPv6 ext headers, and TCP options.
@@ -861,8 +901,24 @@ relocked:
}
INP_INFO_WLOCK_ASSERT(&V_tcbinfo);
+#ifdef TCP_SIGNATURE
+ tcp_dooptions(&to, optp, optlen,
+ (thflags & TH_SYN) ? TO_SYN : 0);
+ if (sig_checked == 0) {
+ tp = intotcpcb(inp);
+ if (tp == NULL || tp->t_state == TCPS_CLOSED) {
+ rstreason = BANDLIM_RST_CLOSEDPORT;
+ goto dropwithreset;
+ }
+ if (!tcp_signature_verify_input(m, off0, tlen, optlen,
+ &to, th, tp->t_flags))
+ goto dropunlock;
+ sig_checked = 1;
+ }
+#else
if (thflags & TH_SYN)
tcp_dooptions(&to, optp, optlen, TO_SYN);
+#endif
/*
* NB: tcp_twcheck unlocks the INP and frees the mbuf.
*/
@@ -1021,6 +1077,26 @@ relocked:
tp = intotcpcb(inp);
KASSERT(tp->t_state == TCPS_SYN_RECEIVED,
("%s: ", __func__));
+#ifdef TCP_SIGNATURE
+ if (sig_checked == 0) {
+ tcp_dooptions(&to, optp, optlen,
+ (thflags & TH_SYN) ? TO_SYN : 0);
+ if (!tcp_signature_verify_input(m, off0, tlen,
+ optlen, &to, th, tp->t_flags)) {
+
+ /*
+ * In SYN_SENT state if it receives an
+ * RST, it is allowed for further
+ * processing.
+ */
+ if ((thflags & TH_RST) == 0 ||
+ (tp->t_state == TCPS_SYN_SENT) == 0)
+ goto dropunlock;
+ }
+ sig_checked = 1;
+ }
+#endif
+
/*
* Process the segment and the data it
* contains. tcp_do_segment() consumes
@@ -1225,6 +1301,25 @@ relocked:
return;
}
+#ifdef TCP_SIGNATURE
+ if (sig_checked == 0) {
+ tcp_dooptions(&to, optp, optlen,
+ (thflags & TH_SYN) ? TO_SYN : 0);
+ if (!tcp_signature_verify_input(m, off0, tlen, optlen, &to,
+ th, tp->t_flags)) {
+
+ /*
+ * In SYN_SENT state if it receives an RST, it is
+ * allowed for further processing.
+ */
+ if ((thflags & TH_RST) == 0 ||
+ (tp->t_state == TCPS_SYN_SENT) == 0)
+ goto dropunlock;
+ }
+ sig_checked = 1;
+ }
+#endif
+
/*
* Segment belongs to a connection in SYN_SENT, ESTABLISHED or later
* state. tcp_do_segment() always consumes the mbuf chain, unlocks
OpenPOWER on IntegriCloud