+/*
+ * Radar 3642395, make sure all multicasts are in a standard format.
+ */
+static struct sockaddr*
+copy_and_normalize(
+ const struct sockaddr *original)
+{
+ int alen = 0;
+ const u_char *aptr = NULL;
+ struct sockaddr *copy = NULL;
+ struct sockaddr_dl *sdl_new = NULL;
+ int len = 0;
+
+ if (original->sa_family != AF_LINK &&
+ original->sa_family != AF_UNSPEC) {
+ /* Just make a copy */
+ MALLOC(copy, struct sockaddr*, original->sa_len, M_IFADDR, M_WAITOK);
+ if (copy != NULL)
+ bcopy(original, copy, original->sa_len);
+ return copy;
+ }
+
+ switch (original->sa_family) {
+ case AF_LINK: {
+ const struct sockaddr_dl *sdl_original =
+ (const struct sockaddr_dl*)original;
+
+ if (sdl_original->sdl_nlen + sdl_original->sdl_alen + sdl_original->sdl_slen +
+ offsetof(struct sockaddr_dl, sdl_data) > sdl_original->sdl_len)
+ return NULL;
+
+ alen = sdl_original->sdl_alen;
+ aptr = CONST_LLADDR(sdl_original);
+ }
+ break;
+
+ case AF_UNSPEC: {
+ if (original->sa_len < ETHER_ADDR_LEN +
+ offsetof(struct sockaddr, sa_data)) {
+ return NULL;
+ }
+
+ alen = ETHER_ADDR_LEN;
+ aptr = (const u_char*)original->sa_data;
+ }
+ break;
+ }
+
+ if (alen == 0 || aptr == NULL)
+ return NULL;
+
+ len = alen + offsetof(struct sockaddr_dl, sdl_data);
+ MALLOC(sdl_new, struct sockaddr_dl*, len, M_IFADDR, M_WAITOK);
+
+ if (sdl_new != NULL) {
+ bzero(sdl_new, len);
+ sdl_new->sdl_len = len;
+ sdl_new->sdl_family = AF_LINK;
+ sdl_new->sdl_alen = alen;
+ bcopy(aptr, LLADDR(sdl_new), alen);
+ }
+
+ return (struct sockaddr*)sdl_new;
+}
+