Author: jtl
Date: Fri Oct  7 10:47:32 2016
New Revision: 306802
URL: https://svnweb.freebsd.org/changeset/base/306802

Log:
  MFC r296454:
    Some cleanup in tcp_respond() in preparation for another change:
    - Reorder variables by size
    - Move initializer closer to where it is used
    - Remove unneeded variable
  
  MFC r296455:
    As reported on the transport@ and current@ mailing lists, the FreeBSD TCP
    stack is not compliant with RFC 7323, which requires that TCP stacks send
    a timestamp option on all packets (except, optionally, RSTs) after the
    session is established.
  
    This patch adds that support. It also adds a TCP signature option to the
    packet, if appropriate.
  
  MFC r300764 (by jhb@):
    Don't reuse the source mbuf in tcp_respond() if it is not writable.
  
    Not all mbufs passed up from device drivers are M_WRITABLE().  In
    particular, the Chelsio T4/T5 driver uses a feature called "buffer
    packing" to receive multiple frames in a single receive buffer.  The mbufs
    to receive multiple frames in a single receive buffer.  The mbufs for
    these frames all share the same external storage so are treated as
    read-only by the rest of the stack when multiple frames are in flight.
    Previously tcp_respond() would blindly overwrite read-only mbufs when
    INVARIANTS was disabled or panic with an assertion failure if INVARIANTS
    was enabled.  Note that the new case is a bit of a mix of the two other
    cases in tcp_respond().  The TCP and IP headers must be copied explicitly
    into the new mbuf instead of being inherited (similar to the m == NULL
    case), but the addresses and ports must be swapped in the reply (similar
    to the m != NULL case).

Modified:
  stable/10/sys/netinet/tcp_subr.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/netinet/tcp_subr.c
==============================================================================
--- stable/10/sys/netinet/tcp_subr.c    Fri Oct  7 06:29:24 2016        
(r306801)
+++ stable/10/sys/netinet/tcp_subr.c    Fri Oct  7 10:47:32 2016        
(r306802)
@@ -556,16 +556,18 @@ void
 tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m,
     tcp_seq ack, tcp_seq seq, int flags)
 {
-       int tlen;
-       int win = 0;
+       struct tcpopt to;
+       struct inpcb *inp;
        struct ip *ip;
+       struct mbuf *optm;
        struct tcphdr *nth;
+       u_char *optp;
 #ifdef INET6
        struct ip6_hdr *ip6;
        int isipv6;
 #endif /* INET6 */
-       int ipflags = 0;
-       struct inpcb *inp;
+       int optlen, tlen, win;
+       bool incl_opts;
 
        KASSERT(tp != NULL || m != NULL, ("tcp_respond: tp and m both NULL"));
 
@@ -582,18 +584,21 @@ tcp_respond(struct tcpcb *tp, void *ipge
        } else
                inp = NULL;
 
+       incl_opts = false;
+       win = 0;
        if (tp != NULL) {
                if (!(flags & TH_RST)) {
                        win = sbspace(&inp->inp_socket->so_rcv);
                        if (win > (long)TCP_MAXWIN << tp->rcv_scale)
                                win = (long)TCP_MAXWIN << tp->rcv_scale;
                }
+               if ((tp->t_flags & TF_NOOPT) == 0)
+                       incl_opts = true;
        }
        if (m == NULL) {
                m = m_gethdr(M_NOWAIT, MT_DATA);
                if (m == NULL)
                        return;
-               tlen = 0;
                m->m_data += max_linkhdr;
 #ifdef INET6
                if (isipv6) {
@@ -610,17 +615,54 @@ tcp_respond(struct tcpcb *tp, void *ipge
                }
                bcopy((caddr_t)th, (caddr_t)nth, sizeof(struct tcphdr));
                flags = TH_ACK;
+       } else if (!M_WRITABLE(m)) {
+               struct mbuf *n;
+
+               /* Can't reuse 'm', allocate a new mbuf. */
+               n = m_gethdr(M_NOWAIT, MT_DATA);
+               if (n == NULL) {
+                       m_freem(m);
+                       return;
+               }
+
+               if (!m_dup_pkthdr(n, m, M_NOWAIT)) {
+                       m_freem(m);
+                       m_freem(n);
+                       return;
+               }
+
+               n->m_data += max_linkhdr;
+               /* m_len is set later */
+#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
+#ifdef INET6
+               if (isipv6) {
+                       bcopy((caddr_t)ip6, mtod(n, caddr_t),
+                             sizeof(struct ip6_hdr));
+                       ip6 = mtod(n, struct ip6_hdr *);
+                       xchg(ip6->ip6_dst, ip6->ip6_src, struct in6_addr);
+                       nth = (struct tcphdr *)(ip6 + 1);
+               } else
+#endif /* INET6 */
+               {
+                       bcopy((caddr_t)ip, mtod(n, caddr_t), sizeof(struct ip));
+                       ip = mtod(n, struct ip *);
+                       xchg(ip->ip_dst.s_addr, ip->ip_src.s_addr, uint32_t);
+                       nth = (struct tcphdr *)(ip + 1);
+               }
+               bcopy((caddr_t)th, (caddr_t)nth, sizeof(struct tcphdr));
+               xchg(nth->th_dport, nth->th_sport, uint16_t);
+               th = nth;
+               m_freem(m);
+               m = n;
        } else {
                /*
                 *  reuse the mbuf. 
-                * XXX MRT We inherrit the FIB, which is lucky.
+                * XXX MRT We inherit the FIB, which is lucky.
                 */
                m_freem(m->m_next);
                m->m_next = NULL;
                m->m_data = (caddr_t)ipgen;
                /* m_len is set later */
-               tlen = 0;
-#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
 #ifdef INET6
                if (isipv6) {
                        xchg(ip6->ip6_dst, ip6->ip6_src, struct in6_addr);
@@ -643,12 +685,64 @@ tcp_respond(struct tcpcb *tp, void *ipge
                xchg(nth->th_dport, nth->th_sport, uint16_t);
 #undef xchg
        }
+       tlen = 0;
+#ifdef INET6
+       if (isipv6)
+               tlen = sizeof (struct ip6_hdr) + sizeof (struct tcphdr);
+#endif
+#if defined(INET) && defined(INET6)
+       else
+#endif
+#ifdef INET
+               tlen = sizeof (struct tcpiphdr);
+#endif
+#ifdef INVARIANTS
+       m->m_len = 0;
+       KASSERT(M_TRAILINGSPACE(m) >= tlen,
+           ("Not enough trailing space for message (m=%p, need=%d, have=%ld)",
+           m, tlen, (long)M_TRAILINGSPACE(m)));
+#endif
+       m->m_len = tlen;
+       to.to_flags = 0;
+       if (incl_opts) {
+               /* Make sure we have room. */
+               if (M_TRAILINGSPACE(m) < TCP_MAXOLEN) {
+                       m->m_next = m_get(M_NOWAIT, MT_DATA);
+                       if (m->m_next) {
+                               optp = mtod(m->m_next, u_char *);
+                               optm = m->m_next;
+                       } else
+                               incl_opts = false;
+               } else {
+                       optp = (u_char *) (nth + 1);
+                       optm = m;
+               }
+       }
+       if (incl_opts) {
+               /* Timestamps. */
+               if (tp->t_flags & TF_RCVD_TSTMP) {
+                       to.to_tsval = tcp_ts_getticks() + tp->ts_offset;
+                       to.to_tsecr = tp->ts_recent;
+                       to.to_flags |= TOF_TS;
+               }
+#ifdef TCP_SIGNATURE
+               /* TCP-MD5 (RFC2385). */
+               if (tp->t_flags & TF_SIGNATURE)
+                       to.to_flags |= TOF_SIGNATURE;
+#endif
+
+               /* Add the options. */
+               tlen += optlen = tcp_addoptions(&to, optp);
+
+               /* Update m_len in the correct mbuf. */
+               optm->m_len += optlen;
+       } else
+               optlen = 0;
 #ifdef INET6
        if (isipv6) {
                ip6->ip6_flow = 0;
                ip6->ip6_vfc = IPV6_VERSION;
                ip6->ip6_nxt = IPPROTO_TCP;
-               tlen += sizeof (struct ip6_hdr) + sizeof (struct tcphdr);
                ip6->ip6_plen = htons(tlen - sizeof(*ip6));
        }
 #endif
@@ -657,14 +751,12 @@ tcp_respond(struct tcpcb *tp, void *ipge
 #endif
 #ifdef INET
        {
-               tlen += sizeof (struct tcpiphdr);
                ip->ip_len = htons(tlen);
                ip->ip_ttl = V_ip_defttl;
                if (V_path_mtu_discovery)
                        ip->ip_off |= htons(IP_DF);
        }
 #endif
-       m->m_len = tlen;
        m->m_pkthdr.len = tlen;
        m->m_pkthdr.rcvif = NULL;
 #ifdef MAC
@@ -686,7 +778,7 @@ tcp_respond(struct tcpcb *tp, void *ipge
        nth->th_seq = htonl(seq);
        nth->th_ack = htonl(ack);
        nth->th_x2 = 0;
-       nth->th_off = sizeof (struct tcphdr) >> 2;
+       nth->th_off = (sizeof (struct tcphdr) + optlen) >> 2;
        nth->th_flags = flags;
        if (tp != NULL)
                nth->th_win = htons((u_short) (win >> tp->rcv_scale));
@@ -694,6 +786,13 @@ tcp_respond(struct tcpcb *tp, void *ipge
                nth->th_win = htons((u_short)win);
        nth->th_urp = 0;
 
+#ifdef TCP_SIGNATURE
+       if (to.to_flags & TOF_SIGNATURE) {
+               tcp_signature_compute(m, 0, 0, optlen, to.to_signature,
+                   IPSEC_DIR_OUTBOUND);
+       }
+#endif
+
        m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
 #ifdef INET6
        if (isipv6) {
@@ -725,13 +824,13 @@ tcp_respond(struct tcpcb *tp, void *ipge
        TCP_PROBE5(send, NULL, tp, mtod(m, const char *), tp, nth);
 #ifdef INET6
        if (isipv6)
-               (void) ip6_output(m, NULL, NULL, ipflags, NULL, NULL, inp);
+               (void) ip6_output(m, NULL, NULL, 0, NULL, NULL, inp);
 #endif /* INET6 */
 #if defined(INET) && defined(INET6)
        else
 #endif
 #ifdef INET
-               (void) ip_output(m, NULL, NULL, ipflags, NULL, inp);
+               (void) ip_output(m, NULL, NULL, 0, NULL, inp);
 #endif
 }
 
_______________________________________________
svn-src-stable-10@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-stable-10
To unsubscribe, send any mail to "svn-src-stable-10-unsubscr...@freebsd.org"

Reply via email to