Subject: tun and ipv6
To: None <tech-net@netbsd.org>
From: None <zul@netbsd-fr.org>
List: tech-net
Date: 02/15/2006 21:47:43
--/04w6evG8XlLl3ft
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: inline

Hi list

rpaulo@ added some preliminary code to add ipv6 support to tun. But
currently, it is only possible to have ipv6 support into "tunnel mode". 
To solve this issue, I think we can use an implementation like obsd or
fbsd : they preprend the packet with the af_family of the packet, so they
can retrieve the family of the packet.

A patch which implements this idea is attached. I don't have tested it.
Please excuse me if they are some errors, it is my first kernel patch.

Take cares.
-- 
Degroote Arnaud


--/04w6evG8XlLl3ft
Content-Type: text/plain; charset=iso-8859-1
Content-Disposition: attachment; filename=patch_tun

--- if_tun.c.orig	2006-02-13 20:02:49.000000000 +0100
+++ if_tun.c	2006-02-13 19:57:58.000000000 +0100
@@ -345,8 +345,9 @@
 			/* find internet addresses and delete routes */
 			struct ifaddr *ifa;
 			IFADDR_FOREACH(ifa, ifp) {
-#ifdef INET
-				if (ifa->ifa_addr->sa_family == AF_INET) {
+#if defined(INET) || defined(INET6)
+				if (ifa->ifa_addr->sa_family == AF_INET ||
+				    ifa->ifa_addr->sa_family == AF_INET6) {
 					rtinit(ifa, (int)RTM_DELETE,
 					       tp->tun_flags & TUN_DSTADDR
 							? RTF_HOST
@@ -396,6 +397,24 @@
 			}
 		}
 #endif
+#ifdef INET6
+        if (ifa->ifa_addr->sa_family == AF_INET6) {
+            struct sockaddr_in6 *sin;
+
+            sin = (struct sockaddr_in6 *)ifa->ifa_addr;
+            if (!IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr))
+                tp->tun_flags |= TUN_IASET;
+
+            if (ifp->if_flags & IFF_POINTOPOINT) {
+                sin = (struct sockaddr_in6 *)ifa->ifa_dstaddr;
+                if (sin &&
+                    !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr))
+                    tp->tun_flags |= TUN_DSTADDR;
+            } else
+                tp->tun_flags &= ~TUN_DSTADDR;
+        }
+#endif /* INET6 */
+
 	}
 
 	return;
@@ -474,8 +493,9 @@
 	struct tun_softc *tp = ifp->if_softc;
 	int		s;
 	int		error;
-#ifdef INET
+#if defined(INET) || defined(INET6)
 	int		mlen;
+    uint32_t * af;
 #endif
 	ALTQ_DECL(struct altq_pktattr pktattr;)
 
@@ -519,7 +539,18 @@
 				goto out;
 			}
 			bcopy(dst, mtod(m0, char *), dst->sa_len);
-		}
+		} else {
+       /* Prepend the adress family */
+            M_PREPEND(m0,sizeof(*af),M_DONTWAIT);
+            if (m0 == NULL) {
+                IF_DROP(&ifp->if_snd);
+                error = ENOBUFS;
+                goto out;
+            }
+            af = mtod(m0,u_int32_t *);
+            *af = htonl(dst->sa_family);
+       }
+
 		/* FALLTHROUGH */
 	case AF_UNSPEC:
 		IFQ_ENQUEUE(&ifp->if_snd, m0, &pktattr, error);
@@ -753,6 +784,7 @@
 	struct ifqueue	*ifq;
 	struct sockaddr	dst;
 	int		isr, error = 0, s, tlen, mlen;
+    uint32_t family;
 
 	s = splnet();
 	tp = tun_find_unit(dev);
@@ -787,9 +819,12 @@
 				}
 		}
 	} else {
-#ifdef INET
-		dst.sa_family = AF_INET;
-#endif
+            if (uio->uio_resid < sizeof(family)){
+                error = EIO;
+                goto out0;
+            }
+            error = uiomove((caddr_t)&family,sizeof(family), uio);
+            dst.sa_family = ntohl(family);
 	}
 
 	if (uio->uio_resid > TUNMTU) {

--/04w6evG8XlLl3ft--