These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / slirp / tcp_input.c
index f946db8..e2b5d4e 100644 (file)
@@ -38,6 +38,7 @@
  * terms and conditions of the copyright.
  */
 
+#include "qemu/osdep.h"
 #include <slirp.h>
 #include "ip_icmp.h"
 
@@ -213,9 +214,10 @@ present:
  * protocol specification dated September, 1981 very closely.
  */
 void
-tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
+tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af)
 {
-       struct ip save_ip, *ip;
+       struct ip save_ip, *ip;
+       struct ip6 save_ip6, *ip6;
        register struct tcpiphdr *ti;
        caddr_t optp = NULL;
        int optlen = 0;
@@ -227,12 +229,15 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
        int iss = 0;
        u_long tiwin;
        int ret;
+       struct sockaddr_storage lhost, fhost;
+       struct sockaddr_in *lhost4, *fhost4;
+       struct sockaddr_in6 *lhost6, *fhost6;
     struct ex_list *ex_ptr;
     Slirp *slirp;
 
        DEBUG_CALL("tcp_input");
-       DEBUG_ARGS((dfd, " m = %8lx  iphlen = %2d  inso = %lx\n",
-                   (long )m, iphlen, (long )inso ));
+       DEBUG_ARGS((dfd, " m = %p  iphlen = %2d  inso = %p\n",
+                   m, iphlen, inso));
 
        /*
         * If called with m == 0, then we're continuing the connect
@@ -253,37 +258,83 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
        }
        slirp = m->slirp;
 
-       /*
-        * Get IP and TCP header together in first mbuf.
-        * Note: IP leaves IP header in first mbuf.
-        */
-       ti = mtod(m, struct tcpiphdr *);
-       if (iphlen > sizeof(struct ip )) {
-         ip_stripoptions(m, (struct mbuf *)0);
-         iphlen=sizeof(struct ip );
-       }
-       /* XXX Check if too short */
+       ip = mtod(m, struct ip *);
+       ip6 = mtod(m, struct ip6 *);
 
+       switch (af) {
+       case AF_INET:
+           if (iphlen > sizeof(struct ip)) {
+               ip_stripoptions(m, (struct mbuf *)0);
+               iphlen = sizeof(struct ip);
+           }
+           /* XXX Check if too short */
 
-       /*
-        * Save a copy of the IP header in case we want restore it
-        * for sending an ICMP error message in response.
-        */
-       ip=mtod(m, struct ip *);
-       save_ip = *ip;
-       save_ip.ip_len+= iphlen;
 
-       /*
-        * Checksum extended TCP header and data.
-        */
-       tlen = ((struct ip *)ti)->ip_len;
-        tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
-        memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
-       ti->ti_x1 = 0;
-       ti->ti_len = htons((uint16_t)tlen);
-       len = sizeof(struct ip ) + tlen;
-       if(cksum(m, len)) {
-         goto drop;
+           /*
+            * Save a copy of the IP header in case we want restore it
+            * for sending an ICMP error message in response.
+            */
+           save_ip = *ip;
+           save_ip.ip_len += iphlen;
+
+           /*
+            * Get IP and TCP header together in first mbuf.
+            * Note: IP leaves IP header in first mbuf.
+            */
+           m->m_data -= sizeof(struct tcpiphdr) - sizeof(struct ip)
+                                                - sizeof(struct tcphdr);
+           m->m_len += sizeof(struct tcpiphdr) - sizeof(struct ip)
+                                               - sizeof(struct tcphdr);
+           ti = mtod(m, struct tcpiphdr *);
+
+           /*
+            * Checksum extended TCP header and data.
+            */
+           tlen = ip->ip_len;
+           tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
+           memset(&ti->ih_mbuf, 0 , sizeof(struct mbuf_ptr));
+           memset(&ti->ti, 0, sizeof(ti->ti));
+           ti->ti_x0 = 0;
+           ti->ti_src = save_ip.ip_src;
+           ti->ti_dst = save_ip.ip_dst;
+           ti->ti_pr = save_ip.ip_p;
+           ti->ti_len = htons((uint16_t)tlen);
+           break;
+
+       case AF_INET6:
+           /*
+            * Save a copy of the IP header in case we want restore it
+            * for sending an ICMP error message in response.
+            */
+           save_ip6 = *ip6;
+           /*
+            * Get IP and TCP header together in first mbuf.
+            * Note: IP leaves IP header in first mbuf.
+            */
+           m->m_data -= sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+                                                + sizeof(struct tcphdr));
+           m->m_len  += sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+                                                + sizeof(struct tcphdr));
+           ti = mtod(m, struct tcpiphdr *);
+
+           tlen = ip6->ip_pl;
+           tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
+           memset(&ti->ih_mbuf, 0 , sizeof(struct mbuf_ptr));
+           memset(&ti->ti, 0, sizeof(ti->ti));
+           ti->ti_x0 = 0;
+           ti->ti_src6 = save_ip6.ip_src;
+           ti->ti_dst6 = save_ip6.ip_dst;
+           ti->ti_nh6 = save_ip6.ip_nh;
+           ti->ti_len = htons((uint16_t)tlen);
+           break;
+
+       default:
+           g_assert_not_reached();
+       }
+
+       len = ((sizeof(struct tcpiphdr) - sizeof(struct tcphdr)) + tlen);
+       if (cksum(m, len)) {
+           goto drop;
        }
 
        /*
@@ -320,17 +371,31 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
         * Locate pcb for segment.
         */
 findso:
-       so = slirp->tcp_last_so;
-       if (so->so_fport != ti->ti_dport ||
-           so->so_lport != ti->ti_sport ||
-           so->so_laddr.s_addr != ti->ti_src.s_addr ||
-           so->so_faddr.s_addr != ti->ti_dst.s_addr) {
-               so = solookup(&slirp->tcb, ti->ti_src, ti->ti_sport,
-                              ti->ti_dst, ti->ti_dport);
-               if (so)
-                       slirp->tcp_last_so = so;
+       lhost.ss_family = af;
+       fhost.ss_family = af;
+       switch (af) {
+       case AF_INET:
+           lhost4 = (struct sockaddr_in *) &lhost;
+           lhost4->sin_addr = ti->ti_src;
+           lhost4->sin_port = ti->ti_sport;
+           fhost4 = (struct sockaddr_in *) &fhost;
+           fhost4->sin_addr = ti->ti_dst;
+           fhost4->sin_port = ti->ti_dport;
+           break;
+       case AF_INET6:
+           lhost6 = (struct sockaddr_in6 *) &lhost;
+           lhost6->sin6_addr = ti->ti_src6;
+           lhost6->sin6_port = ti->ti_sport;
+           fhost6 = (struct sockaddr_in6 *) &fhost;
+           fhost6->sin6_addr = ti->ti_dst6;
+           fhost6->sin6_port = ti->ti_dport;
+           break;
+       default:
+           g_assert_not_reached();
        }
 
+       so = solookup(&slirp->tcp_last_so, &slirp->tcb, &lhost, &fhost);
+
        /*
         * If the state is CLOSED (i.e., TCB does not exist) then
         * all data in the incoming segment is discarded.
@@ -374,13 +439,21 @@ findso:
          sbreserve(&so->so_snd, TCP_SNDSPACE);
          sbreserve(&so->so_rcv, TCP_RCVSPACE);
 
-         so->so_laddr = ti->ti_src;
-         so->so_lport = ti->ti_sport;
-         so->so_faddr = ti->ti_dst;
-         so->so_fport = ti->ti_dport;
-
-         if ((so->so_iptos = tcp_tos(so)) == 0)
-           so->so_iptos = ((struct ip *)ti)->ip_tos;
+         so->lhost.ss = lhost;
+         so->fhost.ss = fhost;
+
+         so->so_iptos = tcp_tos(so);
+         if (so->so_iptos == 0) {
+             switch (af) {
+             case AF_INET:
+                 so->so_iptos = ((struct ip *)ti)->ip_tos;
+                 break;
+             case AF_INET6:
+                 break;
+             default:
+                 g_assert_not_reached();
+             }
+         }
 
          tp = sototcpcb(so);
          tp->t_state = TCPS_LISTEN;
@@ -559,8 +632,9 @@ findso:
           * If this is destined for the control address, then flag to
           * tcp_ctl once connected, otherwise connect
           */
-         if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
-             slirp->vnetwork_addr.s_addr) {
+         if (af == AF_INET &&
+                (so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+                slirp->vnetwork_addr.s_addr) {
            if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr &&
                so->so_faddr.s_addr != slirp->vnameserver_addr.s_addr) {
                /* May be an add exec */
@@ -584,24 +658,60 @@ findso:
            goto cont_input;
          }
 
-         if((tcp_fconnect(so) == -1) && (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) {
-           u_char code=ICMP_UNREACH_NET;
+         if ((tcp_fconnect(so, so->so_ffamily) == -1) &&
+              (errno != EAGAIN) &&
+              (errno != EINPROGRESS) && (errno != EWOULDBLOCK)
+          ) {
+           uint8_t code;
            DEBUG_MISC((dfd, " tcp fconnect errno = %d-%s\n",
                        errno,strerror(errno)));
            if(errno == ECONNREFUSED) {
              /* ACK the SYN, send RST to refuse the connection */
-             tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0,
-                         TH_RST|TH_ACK);
+             tcp_respond(tp, ti, m, ti->ti_seq + 1, (tcp_seq) 0,
+                         TH_RST | TH_ACK, af);
            } else {
-             if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+             switch (af) {
+             case AF_INET:
+               code = ICMP_UNREACH_NET;
+               if (errno == EHOSTUNREACH) {
+                 code = ICMP_UNREACH_HOST;
+               }
+               break;
+             case AF_INET6:
+               code = ICMP6_UNREACH_NO_ROUTE;
+               if (errno == EHOSTUNREACH) {
+                 code = ICMP6_UNREACH_ADDRESS;
+               }
+               break;
+             default:
+               g_assert_not_reached();
+             }
              HTONL(ti->ti_seq);             /* restore tcp header */
              HTONL(ti->ti_ack);
              HTONS(ti->ti_win);
              HTONS(ti->ti_urp);
              m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
              m->m_len  += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
-             *ip=save_ip;
-             icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno));
+             switch (af) {
+             case AF_INET:
+               m->m_data += sizeof(struct tcpiphdr) - sizeof(struct ip)
+                                                    - sizeof(struct tcphdr);
+               m->m_len  -= sizeof(struct tcpiphdr) - sizeof(struct ip)
+                                                    - sizeof(struct tcphdr);
+               *ip = save_ip;
+               icmp_send_error(m, ICMP_UNREACH, code, 0, strerror(errno));
+               break;
+             case AF_INET6:
+               m->m_data += sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+                                                    + sizeof(struct tcphdr));
+               m->m_len  -= sizeof(struct tcpiphdr) - (sizeof(struct ip6)
+                                                    + sizeof(struct tcphdr));
+               *ip6 = save_ip6;
+               icmp6_send_error(m, ICMP6_UNREACH, code);
+               break;
+             default:
+               g_assert_not_reached();
+             }
            }
             tcp_close(tp);
            m_free(m);
@@ -616,6 +726,12 @@ findso:
            so->so_ti = ti;
            tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
            tp->t_state = TCPS_SYN_RECEIVED;
+           /*
+            * Initialize receive sequence numbers now so that we can send a
+            * valid RST if the remote end rejects our connection.
+            */
+           tp->irs = ti->ti_seq;
+           tcp_rcvseqinit(tp);
            tcp_template(tp);
          }
          return;
@@ -917,8 +1033,8 @@ trimthenstep6:
 
                if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) {
                        if (ti->ti_len == 0 && tiwin == tp->snd_wnd) {
-                         DEBUG_MISC((dfd, " dup ack  m = %lx  so = %lx\n",
-                                     (long )m, (long )so));
+                         DEBUG_MISC((dfd, " dup ack  m = %p  so = %p\n",
+                                     m, so));
                                /*
                                 * If we have outstanding data (other than
                                 * a window probe), this is a completely
@@ -1273,11 +1389,11 @@ dropafterack:
 dropwithreset:
        /* reuses m if m!=NULL, m_free() unnecessary */
        if (tiflags & TH_ACK)
-               tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
+               tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST, af);
        else {
                if (tiflags & TH_SYN) ti->ti_len++;
-               tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
-                   TH_RST|TH_ACK);
+               tcp_respond(tp, ti, m, ti->ti_seq + ti->ti_len, (tcp_seq) 0,
+                   TH_RST | TH_ACK, af);
        }
 
        return;
@@ -1296,7 +1412,7 @@ tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti)
        int opt, optlen;
 
        DEBUG_CALL("tcp_dooptions");
-       DEBUG_ARGS((dfd, " tp = %lx  cnt=%i\n", (long)tp, cnt));
+       DEBUG_ARGS((dfd, " tp = %p  cnt=%i\n", tp, cnt));
 
        for (; cnt > 0; cnt -= optlen, cp += optlen) {
                opt = cp[0];
@@ -1377,7 +1493,7 @@ tcp_xmit_timer(register struct tcpcb *tp, int rtt)
        register short delta;
 
        DEBUG_CALL("tcp_xmit_timer");
-       DEBUG_ARG("tp = %lx", (long)tp);
+       DEBUG_ARG("tp = %p", tp);
        DEBUG_ARG("rtt = %d", rtt);
 
        if (tp->t_srtt != 0) {
@@ -1465,10 +1581,22 @@ tcp_mss(struct tcpcb *tp, u_int offer)
        int mss;
 
        DEBUG_CALL("tcp_mss");
-       DEBUG_ARG("tp = %lx", (long)tp);
+       DEBUG_ARG("tp = %p", tp);
        DEBUG_ARG("offer = %d", offer);
 
-       mss = min(IF_MTU, IF_MRU) - sizeof(struct tcpiphdr);
+       switch (so->so_ffamily) {
+       case AF_INET:
+           mss = min(IF_MTU, IF_MRU) - sizeof(struct tcphdr)
+                                     + sizeof(struct ip);
+           break;
+       case AF_INET6:
+           mss = min(IF_MTU, IF_MRU) - sizeof(struct tcphdr)
+                                     + sizeof(struct ip6);
+           break;
+       default:
+           g_assert_not_reached();
+       }
+
        if (offer)
                mss = min(mss, offer);
        mss = max(mss, 32);