diff mbox

[PATCHv7,7/9] slirp: Handle IPv6 in TCP functions

Message ID 06d461a76323aec31c62db291e292da1c78107a5.1454927009.git.samuel.thibault@ens-lyon.org
State New
Headers show

Commit Message

Samuel Thibault Feb. 8, 2016, 10:28 a.m. UTC
From: Guillaume Subiron <maethor@subiron.org>

This patch adds IPv6 case in TCP functions refactored by the last
patches.
This also adds IPv6 pseudo-header in tcpiphdr structure.
Finally, tcp_input() is called by ip6_input().

Signed-off-by: Guillaume Subiron <maethor@subiron.org>
Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
---
 slirp/ip6_input.c  |  3 ++-
 slirp/tcp.h        |  2 ++
 slirp/tcp_input.c  | 60 ++++++++++++++++++++++++++++++++++++++++++------------
 slirp/tcp_output.c | 16 +++++++++++++++
 slirp/tcp_subr.c   | 33 +++++++++++++++++++++++++++++-
 slirp/tcpip.h      |  9 ++++++++
 6 files changed, 108 insertions(+), 15 deletions(-)

Comments

Thomas Huth Feb. 10, 2016, 10:47 a.m. UTC | #1
On 08.02.2016 11:28, Samuel Thibault wrote:
> From: Guillaume Subiron <maethor@subiron.org>
> 
> This patch adds IPv6 case in TCP functions refactored by the last
> patches.
> This also adds IPv6 pseudo-header in tcpiphdr structure.
> Finally, tcp_input() is called by ip6_input().
> 
> Signed-off-by: Guillaume Subiron <maethor@subiron.org>
> Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
> ---
...
> diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c
> index eb0df81..5840471 100644
> --- a/slirp/tcp_input.c
> +++ b/slirp/tcp_input.c
> @@ -216,7 +216,8 @@ present:
>  void
>  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;
> @@ -230,6 +231,7 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af)
>  	int ret;
>  	struct sockaddr_storage lhost, fhost;
>  	struct sockaddr_in *lhost4, *fhost4;
> +	struct sockaddr_in6 *lhost6, *fhost6;
>      struct ex_list *ex_ptr;
>      Slirp *slirp;
>  
> @@ -256,6 +258,11 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af)
>  	}
>  	slirp = m->slirp;
>  
> +	ip = mtod(m, struct ip *);
> +	ip6 = mtod(m, struct ip6 *);
> +	save_ip = *ip;
> +	save_ip6 = *ip6;

Could you do the "save_ip = *ip" within the "case AF_INET" below, and
the "save_ip6 = *ip6" within the case AF_INET6 ? That would avoid to
copy bytes that are not required.

>  	switch (af) {
>  	case AF_INET:
>  	    if (iphlen > sizeof(struct ip)) {
> @@ -264,13 +271,6 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af)
>  	    }
>  	    /* 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;
>  
>  	    /*
> @@ -295,16 +295,35 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af)
>  	    ti->ti_dst = save_ip.ip_dst;
>  	    ti->ti_pr = save_ip.ip_p;
>  	    ti->ti_len = htons((uint16_t)tlen);
> -	    len = ((sizeof(struct tcpiphdr) - sizeof(struct tcphdr)) + tlen);
> -	    if (cksum(m, len)) {
> -	        goto drop;
> -	    }
> +	    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));
> +	    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:
>  	    goto drop;
>  	}
>  
> +	len = ((sizeof(struct tcpiphdr) - sizeof(struct tcphdr)) + tlen);
> +	if (cksum(m, len)) {
> +	    goto drop;
> +	}
> +
>  	/*
>  	 * Check that TCP offset makes sense,
>  	 * pull out TCP options and adjust length.		XXX
> @@ -350,6 +369,14 @@ findso:
>  	    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:
>  	    goto drop;
>  	}
> @@ -409,7 +436,6 @@ findso:
>  	          so->so_iptos = ((struct ip *)ti)->ip_tos;
>  	          break;
>  	      default:
> -	          goto drop;
>  	          break;
>  	      }
>  	  }
> @@ -643,6 +669,9 @@ findso:
>  	      case AF_INET:
>  	          *ip = save_ip;
>  	          break;
> +	      case AF_INET6:
> +	          *ip6 = save_ip6;
> +	          break;
>  	      default:
>  	          goto drop;
>  	      }
> @@ -1518,7 +1547,12 @@ tcp_mss(struct tcpcb *tp, u_int offer)
>  	    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();
>  	    break;
>  	}
...

 Thomas
Samuel Thibault Feb. 10, 2016, 12:30 p.m. UTC | #2
Thomas Huth, on Wed 10 Feb 2016 11:47:05 +0100, wrote:
> > +	ip = mtod(m, struct ip *);
> > +	ip6 = mtod(m, struct ip6 *);
> > +	save_ip = *ip;
> > +	save_ip6 = *ip6;
> 
> Could you do the "save_ip = *ip" within the "case AF_INET" below, and
> the "save_ip6 = *ip6" within the case AF_INET6 ? That would avoid to
> copy bytes that are not required.

The issue is that when save_ip is used later on in another switch/case,
the compiler will warn that save_ip may be used uninitialized, because
the compiler is not smart enough to realize that the two codes are under
the same conditions. It seems to happen that my current version of gcc
doesn't warn about save_ip, but it does warn about ip if I moved that
too for instance. So we can move the assignment indeed, but there will
probably be some compilers which will emit a warning here, I don't know
what we prefer.

Samuel
Thomas Huth Feb. 10, 2016, 12:41 p.m. UTC | #3
On 10.02.2016 13:30, Samuel Thibault wrote:
> Thomas Huth, on Wed 10 Feb 2016 11:47:05 +0100, wrote:
>>> +	ip = mtod(m, struct ip *);
>>> +	ip6 = mtod(m, struct ip6 *);
>>> +	save_ip = *ip;
>>> +	save_ip6 = *ip6;
>>
>> Could you do the "save_ip = *ip" within the "case AF_INET" below, and
>> the "save_ip6 = *ip6" within the case AF_INET6 ? That would avoid to
>> copy bytes that are not required.
> 
> The issue is that when save_ip is used later on in another switch/case,
> the compiler will warn that save_ip may be used uninitialized, because
> the compiler is not smart enough to realize that the two codes are under
> the same conditions. It seems to happen that my current version of gcc
> doesn't warn about save_ip, but it does warn about ip if I moved that
> too for instance. So we can move the assignment indeed, but there will
> probably be some compilers which will emit a warning here, I don't know
> what we prefer.

If current compilers only complain about the "ip = ..." statement, then
I'd suggest to give it a try to only move the "save_ip = ..." statements
into the switch cases (I think it's worth a try since this is the more
expensive operation). If that causes trouble later, we can still move
the statements back (or maybe fix the warnings by other means).

 Thomas
diff mbox

Patch

diff --git a/slirp/ip6_input.c b/slirp/ip6_input.c
index d7c612e..b03b795 100644
--- a/slirp/ip6_input.c
+++ b/slirp/ip6_input.c
@@ -58,7 +58,8 @@  void ip6_input(struct mbuf *m)
      */
     switch (ip6->ip_nh) {
     case IPPROTO_TCP:
-        icmp6_send_error(m, ICMP6_UNREACH, ICMP6_UNREACH_NO_ROUTE);
+        NTOHS(ip6->ip_pl);
+        tcp_input(m, sizeof(struct ip6), (struct socket *)NULL, AF_INET6);
         break;
     case IPPROTO_UDP:
         udp6_input(m);
diff --git a/slirp/tcp.h b/slirp/tcp.h
index 2e2b403..61befcd 100644
--- a/slirp/tcp.h
+++ b/slirp/tcp.h
@@ -106,6 +106,8 @@  struct tcphdr {
  */
 #undef TCP_MSS
 #define	TCP_MSS	1460
+#undef TCP6_MSS
+#define TCP6_MSS 1440
 
 #undef TCP_MAXWIN
 #define	TCP_MAXWIN	65535	/* largest value for (unscaled) window */
diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c
index eb0df81..5840471 100644
--- a/slirp/tcp_input.c
+++ b/slirp/tcp_input.c
@@ -216,7 +216,8 @@  present:
 void
 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;
@@ -230,6 +231,7 @@  tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af)
 	int ret;
 	struct sockaddr_storage lhost, fhost;
 	struct sockaddr_in *lhost4, *fhost4;
+	struct sockaddr_in6 *lhost6, *fhost6;
     struct ex_list *ex_ptr;
     Slirp *slirp;
 
@@ -256,6 +258,11 @@  tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af)
 	}
 	slirp = m->slirp;
 
+	ip = mtod(m, struct ip *);
+	ip6 = mtod(m, struct ip6 *);
+	save_ip = *ip;
+	save_ip6 = *ip6;
+
 	switch (af) {
 	case AF_INET:
 	    if (iphlen > sizeof(struct ip)) {
@@ -264,13 +271,6 @@  tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af)
 	    }
 	    /* 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;
 
 	    /*
@@ -295,16 +295,35 @@  tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af)
 	    ti->ti_dst = save_ip.ip_dst;
 	    ti->ti_pr = save_ip.ip_p;
 	    ti->ti_len = htons((uint16_t)tlen);
-	    len = ((sizeof(struct tcpiphdr) - sizeof(struct tcphdr)) + tlen);
-	    if (cksum(m, len)) {
-	        goto drop;
-	    }
+	    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));
+	    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:
 	    goto drop;
 	}
 
+	len = ((sizeof(struct tcpiphdr) - sizeof(struct tcphdr)) + tlen);
+	if (cksum(m, len)) {
+	    goto drop;
+	}
+
 	/*
 	 * Check that TCP offset makes sense,
 	 * pull out TCP options and adjust length.		XXX
@@ -350,6 +369,14 @@  findso:
 	    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:
 	    goto drop;
 	}
@@ -409,7 +436,6 @@  findso:
 	          so->so_iptos = ((struct ip *)ti)->ip_tos;
 	          break;
 	      default:
-	          goto drop;
 	          break;
 	      }
 	  }
@@ -643,6 +669,9 @@  findso:
 	      case AF_INET:
 	          *ip = save_ip;
 	          break;
+	      case AF_INET6:
+	          *ip6 = save_ip6;
+	          break;
 	      default:
 	          goto drop;
 	      }
@@ -1518,7 +1547,12 @@  tcp_mss(struct tcpcb *tp, u_int offer)
 	    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();
 	    break;
 	}
 
diff --git a/slirp/tcp_output.c b/slirp/tcp_output.c
index 429018a..b016a56 100644
--- a/slirp/tcp_output.c
+++ b/slirp/tcp_output.c
@@ -63,6 +63,7 @@  tcp_output(struct tcpcb *tp)
 	register struct mbuf *m;
 	register struct tcpiphdr *ti, tcpiph_save;
 	struct ip *ip;
+	struct ip6 *ip6;
 	u_char opt[MAX_TCPOPTLEN];
 	unsigned optlen, hdrlen;
 	int idle, sendalot;
@@ -468,6 +469,21 @@  send:
 	    error = ip_output(so, m);
 	    break;
 
+	case AF_INET6:
+	    m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+	                                         - sizeof(struct ip6);
+	    m->m_len  -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+	                                         - sizeof(struct ip6);
+	    ip6 = mtod(m, struct ip6 *);
+
+	    ip6->ip_pl = tcpiph_save.ti_len;
+	    ip6->ip_dst = tcpiph_save.ti_dst6;
+	    ip6->ip_src = tcpiph_save.ti_src6;
+	    ip6->ip_nh = tcpiph_save.ti_nh6;
+
+	    error = ip6_output(so, m, 0);
+	    break;
+
 	default:
 	    goto out;
 	}
diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c
index f2ef8f3..b18e0da 100644
--- a/slirp/tcp_subr.c
+++ b/slirp/tcp_subr.c
@@ -88,7 +88,17 @@  tcp_template(struct tcpcb *tp)
 	    n->ti_dport = so->so_lport;
 	    break;
 
+	case AF_INET6:
+	    n->ti_nh6 = IPPROTO_TCP;
+	    n->ti_len = htons(sizeof(struct tcphdr));
+	    n->ti_src6 = so->so_faddr6;
+	    n->ti_dst6 = so->so_laddr6;
+	    n->ti_sport = so->so_fport6;
+	    n->ti_dport = so->so_lport6;
+	    break;
+
 	default:
+	    g_assert_not_reached();
 	    break;
 	}
 
@@ -156,7 +166,12 @@  tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
 		    xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t);
 		    xchg(ti->ti_dport, ti->ti_sport, uint16_t);
 		    break;
+		case AF_INET6:
+		    xchg(ti->ti_dst6, ti->ti_src6, struct in6_addr);
+		    xchg(ti->ti_dport, ti->ti_sport, uint16_t);
+		    break;
 		default:
+		    g_assert_not_reached();
 		    break;
 		}
 #undef xchg
@@ -182,6 +197,7 @@  tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
 
 	struct tcpiphdr tcpiph_save = *(mtod(m, struct tcpiphdr *));
 	struct ip *ip;
+	struct ip6 *ip6;
 
 	switch (af) {
 	case AF_INET:
@@ -204,7 +220,22 @@  tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
 	    (void) ip_output((struct socket *)0, m);
 	    break;
 
+	case AF_INET6:
+	    m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+	                                         - sizeof(struct ip6);
+	    m->m_len  -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr)
+	                                         - sizeof(struct ip6);
+	    ip6 = mtod(m, struct ip6 *);
+	    ip6->ip_pl = tlen;
+	    ip6->ip_dst = tcpiph_save.ti_dst6;
+	    ip6->ip_src = tcpiph_save.ti_src6;
+	    ip6->ip_nh = tcpiph_save.ti_nh6;
+
+	    (void) ip6_output((struct socket *)0, m, 0);
+	    break;
+
 	default:
+	    g_assert_not_reached();
 	    break;
 	}
 }
@@ -225,7 +256,7 @@  tcp_newtcpcb(struct socket *so)
 
 	memset((char *) tp, 0, sizeof(struct tcpcb));
 	tp->seg_next = tp->seg_prev = (struct tcpiphdr*)tp;
-	tp->t_maxseg = TCP_MSS;
+	tp->t_maxseg = (so->so_ffamily == AF_INET) ? TCP_MSS : TCP6_MSS;
 
 	tp->t_flags = TCP_DO_RFC1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
 	tp->t_socket = so;
diff --git a/slirp/tcpip.h b/slirp/tcpip.h
index d9b5d70..4a2987f 100644
--- a/slirp/tcpip.h
+++ b/slirp/tcpip.h
@@ -45,6 +45,12 @@  struct tcpiphdr {
             uint8_t ih_x1;          /* (unused) */
             uint8_t ih_pr;          /* protocol */
         } ti_i4;
+        struct {
+            struct  in6_addr ih_src;
+            struct  in6_addr ih_dst;
+            uint8_t ih_x1;
+            uint8_t ih_nh;
+        } ti_i6;
     } ti;
     uint16_t    ti_x0;
     uint16_t    ti_len;             /* protocol length */
@@ -54,6 +60,9 @@  struct tcpiphdr {
 #define	ti_pr		ti.ti_i4.ih_pr
 #define	ti_src		ti.ti_i4.ih_src
 #define	ti_dst		ti.ti_i4.ih_dst
+#define	ti_src6		ti.ti_i6.ih_src
+#define	ti_dst6		ti.ti_i6.ih_dst
+#define	ti_nh6		ti.ti_i6.ih_nh
 #define	ti_sport	ti_t.th_sport
 #define	ti_dport	ti_t.th_dport
 #define	ti_seq		ti_t.th_seq