Author: attilio
Date: Mon May  9 18:29:48 2011
New Revision: 221705
URL: http://svn.freebsd.org/changeset/base/221705

Log:
  MFC r221023:
  Add the possibility to verify MD5 hash of incoming TCP packets.
  
  Sponsored by: Sandvine Incorporated

Modified:
  stable/8/sys/netinet/tcp_input.c
  stable/8/sys/netinet/tcp_subr.c
  stable/8/sys/netinet/tcp_syncache.c
  stable/8/sys/netinet/tcp_var.h
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)

Modified: stable/8/sys/netinet/tcp_input.c
==============================================================================
--- stable/8/sys/netinet/tcp_input.c    Mon May  9 18:05:13 2011        
(r221704)
+++ stable/8/sys/netinet/tcp_input.c    Mon May  9 18:29:48 2011        
(r221705)
@@ -205,6 +205,12 @@ static void         tcp_xmit_timer(struct tcpcb
 static void     tcp_newreno_partial_ack(struct tcpcb *, struct tcphdr *);
 static void inline
                 tcp_congestion_exp(struct tcpcb *);
+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
 
 /*
  * Kernel module interface for updating tcpstat.  The argument is an index
@@ -236,6 +242,40 @@ tcp_congestion_exp(struct tcpcb *tp)
                tp->t_flags |= TF_ECN_SND_CWR;
 }
 
+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) \
@@ -315,6 +355,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
@@ -472,10 +515,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.
@@ -657,8 +697,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.
                 */
@@ -817,6 +873,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
@@ -1021,6 +1097,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

Modified: stable/8/sys/netinet/tcp_subr.c
==============================================================================
--- stable/8/sys/netinet/tcp_subr.c     Mon May  9 18:05:13 2011        
(r221704)
+++ stable/8/sys/netinet/tcp_subr.c     Mon May  9 18:29:48 2011        
(r221705)
@@ -257,6 +257,12 @@ SYSCTL_VNET_INT(_net_inet_tcp_inflight, 
     &VNET_NAME(tcp_inflight_stab), 0,
     "Inflight Algorithm Stabilization 20 = 2 packets");
 
+#ifdef TCP_SIGNATURE
+static int     tcp_sig_checksigs = 1;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, signature_verify_input, CTLFLAG_RW,
+    &tcp_sig_checksigs, 0, "Verify RFC2385 digests on inbound traffic");
+#endif
+
 VNET_DEFINE(uma_zone_t, sack_hole_zone);
 #define        V_sack_hole_zone                VNET(sack_hole_zone)
 
@@ -2084,6 +2090,66 @@ tcp_signature_compute(struct mbuf *m, in
        KEY_FREESAV(&sav);
        return (0);
 }
+
+/*
+ * Verify the TCP-MD5 hash of a TCP segment. (RFC2385)
+ *
+ * Parameters:
+ * m           pointer to head of mbuf chain
+ * len         length of TCP segment data, excluding options
+ * optlen      length of TCP segment options
+ * buf         pointer to storage for computed MD5 digest
+ * direction   direction of flow (IPSEC_DIR_INBOUND or OUTBOUND)
+ *
+ * Return 1 if successful, otherwise return 0.
+ */
+int
+tcp_signature_verify(struct mbuf *m, int off0, int tlen, int optlen,
+    struct tcpopt *to, struct tcphdr *th, u_int tcpbflag)
+{
+       char tmpdigest[TCP_SIGLEN];
+
+       if (tcp_sig_checksigs == 0)
+               return (1);
+       if ((tcpbflag & TF_SIGNATURE) == 0) {
+               if ((to->to_flags & TOF_SIGNATURE) != 0) {
+
+                       /*
+                        * If this socket is not expecting signature but
+                        * the segment contains signature just fail.
+                        */
+                       TCPSTAT_INC(tcps_sig_err_sigopt);
+                       TCPSTAT_INC(tcps_sig_rcvbadsig);
+                       return (0);
+               }
+
+               /* Signature is not expected, and not present in segment. */
+               return (1);
+       }
+
+       /*
+        * If this socket is expecting signature but the segment does not
+        * contain any just fail.
+        */
+       if ((to->to_flags & TOF_SIGNATURE) == 0) {
+               TCPSTAT_INC(tcps_sig_err_nosigopt);
+               TCPSTAT_INC(tcps_sig_rcvbadsig);
+               return (0);
+       }
+       if (tcp_signature_compute(m, off0, tlen, optlen, &tmpdigest[0],
+           IPSEC_DIR_INBOUND) == -1) {
+               TCPSTAT_INC(tcps_sig_err_buildsig);
+               TCPSTAT_INC(tcps_sig_rcvbadsig);
+               return (0);
+       }
+       
+       if (bcmp(to->to_signature, &tmpdigest[0], TCP_SIGLEN) != 0) {
+               TCPSTAT_INC(tcps_sig_rcvbadsig);
+               return (0);
+       }
+       TCPSTAT_INC(tcps_sig_rcvgoodsig);
+       return (1);
+}
 #endif /* TCP_SIGNATURE */
 
 static int

Modified: stable/8/sys/netinet/tcp_syncache.c
==============================================================================
--- stable/8/sys/netinet/tcp_syncache.c Mon May  9 18:05:13 2011        
(r221704)
+++ stable/8/sys/netinet/tcp_syncache.c Mon May  9 18:29:48 2011        
(r221705)
@@ -1010,7 +1010,8 @@ _syncache_add(struct in_conninfo *inc, s
        struct syncache_head *sch;
        struct mbuf *ipopts = NULL;
        u_int32_t flowtmp;
-       int win, sb_hiwat, ip_ttl, ip_tos, noopt;
+       u_int ltflags;
+       int win, sb_hiwat, ip_ttl, ip_tos;
        char *s;
 #ifdef INET6
        int autoflowlabel = 0;
@@ -1043,7 +1044,7 @@ _syncache_add(struct in_conninfo *inc, s
        ip_tos = inp->inp_ip_tos;
        win = sbspace(&so->so_rcv);
        sb_hiwat = so->so_rcv.sb_hiwat;
-       noopt = (tp->t_flags & TF_NOOPT);
+       ltflags = (tp->t_flags & (TF_NOOPT | TF_SIGNATURE));
 
        /* By the time we drop the lock these should no longer be used. */
        so = NULL;
@@ -1238,14 +1239,14 @@ _syncache_add(struct in_conninfo *inc, s
         * XXX: Currently we always record the option by default and will
         * attempt to use it in syncache_respond().
         */
-       if (to->to_flags & TOF_SIGNATURE)
+       if (to->to_flags & TOF_SIGNATURE || ltflags & TF_SIGNATURE)
                sc->sc_flags |= SCF_SIGNATURE;
 #endif
        if (to->to_flags & TOF_SACKPERM)
                sc->sc_flags |= SCF_SACK;
        if (to->to_flags & TOF_MSS)
                sc->sc_peer_mss = to->to_mss;   /* peer mss may be zero */
-       if (noopt)
+       if (ltflags & TF_NOOPT)
                sc->sc_flags |= SCF_NOOPT;
        if ((th->th_flags & (TH_ECE|TH_CWR)) && V_tcp_do_ecn)
                sc->sc_flags |= SCF_ECN;

Modified: stable/8/sys/netinet/tcp_var.h
==============================================================================
--- stable/8/sys/netinet/tcp_var.h      Mon May  9 18:05:13 2011        
(r221704)
+++ stable/8/sys/netinet/tcp_var.h      Mon May  9 18:29:48 2011        
(r221705)
@@ -470,7 +470,14 @@ struct     tcpstat {
        u_long  tcps_ecn_shs;           /* ECN successful handshakes */
        u_long  tcps_ecn_rcwnd;         /* # times ECN reduced the cwnd */
 
-       u_long  _pad[12];               /* 6 UTO, 6 TBD */
+       /* TCP_SIGNATURE related stats */
+       u_long  tcps_sig_rcvgoodsig;    /* Total matching signature received */
+       u_long  tcps_sig_rcvbadsig;     /* Total bad signature received */
+       u_long  tcps_sig_err_buildsig;  /* Mismatching signature received */
+       u_long  tcps_sig_err_sigopt;    /* No signature expected by socket */
+       u_long  tcps_sig_err_nosigopt;  /* No signature provided by segment */
+
+       u_long  _pad[7];                /* 6 UTO, 1 TBD */
 };
 
 #ifdef _KERNEL
@@ -638,6 +645,8 @@ int  tcp_twrespond(struct tcptw *, int);
 void    tcp_setpersist(struct tcpcb *);
 #ifdef TCP_SIGNATURE
 int     tcp_signature_compute(struct mbuf *, int, int, int, u_char *, u_int);
+int     tcp_signature_verify(struct mbuf *, int, int, int, struct tcpopt *,
+           struct tcphdr *, u_int);
 #endif
 void    tcp_slowtimo(void);
 struct tcptemp *
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to