zebra: fix sockaddr_dl length assumptions (BZ#737)

Quagga makes bad assumptions about sockaddr_dl (on NetBSD, but possibly
on other systems as well).  Particularly, sizeof(struct sockaddr_dl)
returns a size that does not include the full sdl_data field, leading to
not enough data being copied.  This breaks IPv6 RAs in particular, as
a broken mac address from sockaddr_dl will be included in the packets.

From: Matthias-Christian Ott <ott@mirix.org>
Tested-by: Uwe Toenjes <6bone@6bone.informatik.uni-leipzig.de>
[further simplified + more comments]
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index cde36bd..73fabd4 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -477,13 +477,22 @@
 
       /*
        * XXX sockaddr_dl contents can be larger than the structure
-       * definition, so the user of the stored structure must be
-       * careful not to read off the end.
-       *
+       * definition.  There are 2 big families here:
+       *  - BSD has sdl_len + sdl_data[16] + overruns sdl_data
+       *    we MUST use sdl_len here or we'll truncate data.
+       *  - Solaris has no sdl_len, but sdl_data[244]
+       *    presumably, it's not going to run past that, so sizeof()
+       *    is fine here.
        * a nonzero ifnlen from RTA_NAME_GET() means sdl is valid
        */
       if (ifnlen)
+      {
+#ifdef HAVE_STRUCT_SOCKADDR_DL_SDL_LEN
+	memcpy (&ifp->sdl, sdl, sdl->sdl_len);
+#else
 	memcpy (&ifp->sdl, sdl, sizeof (struct sockaddr_dl));
+#endif /* HAVE_STRUCT_SOCKADDR_DL_SDL_LEN */
+      }
 
       if_add_update (ifp);
     }
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 9e47f23..cb8dbcb 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -153,7 +153,7 @@
   stream_putl (s, ifp->mtu6);
   stream_putl (s, ifp->bandwidth);
 #ifdef HAVE_STRUCT_SOCKADDR_DL
-  stream_put (s, &ifp->sdl, sizeof (ifp->sdl));
+  stream_put (s, &ifp->sdl, sizeof (ifp->sdl_storage));
 #else
   stream_putl (s, ifp->hw_addr_len);
   if (ifp->hw_addr_len)