diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -2170,6 +2170,7 @@ char ip6buf[INET6_ADDRSTRLEN]; char *lladdr; int icmp6len, is_onlink, is_router, lladdrlen; + u_int lladdr_pad; M_ASSERTPKTHDR(m); KASSERT(m->m_pkthdr.rcvif != NULL, ("%s: no rcvif", __func__)); @@ -2296,8 +2297,9 @@ lladdr = NULL; lladdrlen = 0; + lladdr_pad = nd6_lladdr_opt_pad(ifp); if (ndopts.nd_opts_tgt_lladdr) { - lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); + lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1) + lladdr_pad; lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; } @@ -2492,6 +2494,7 @@ { /* target lladdr option */ int len; + u_int pad; struct nd_opt_hdr *nd_opt; char *lladdr; @@ -2499,6 +2502,7 @@ if (ln == NULL) goto nolladdropt; + pad = nd6_lladdr_opt_pad(ifp); len = sizeof(*nd_opt) + ifp->if_addrlen; len = (len + 7) & ~7; /* round by 8 */ /* safety check */ @@ -2507,9 +2511,10 @@ if (ln->la_flags & LLE_VALID) { nd_opt = (struct nd_opt_hdr *)p; + memset(nd_opt, 0, len); nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; nd_opt->nd_opt_len = len >> 3; - lladdr = (char *)(nd_opt + 1); + lladdr = (char *)(nd_opt + 1) + pad; bcopy(ln->ll_addr, lladdr, ifp->if_addrlen); p += len; } diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -390,6 +390,7 @@ void nd6_ns_output(struct ifnet *, const struct in6_addr *, const struct in6_addr *, const struct in6_addr *, uint8_t *); caddr_t nd6_ifptomac(struct ifnet *); +u_int nd6_lladdr_opt_pad(struct ifnet *); void nd6_dad_init(void); void nd6_dad_start(struct ifaddr *, int); void nd6_dad_stop(struct ifaddr *); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -139,6 +139,7 @@ char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; char *lladdr; int anycast, lladdrlen, proxy, rflag, tentative, tlladdr; + u_int lladdr_pad; ifa = NULL; @@ -226,8 +227,9 @@ lladdr = NULL; lladdrlen = 0; + lladdr_pad = nd6_lladdr_opt_pad(ifp); if (ndopts.nd_opts_src_lladdr) { - lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); + lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1) + lladdr_pad; lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } @@ -559,6 +561,7 @@ struct nd_opt_hdr *nd_opt; char *mac; int optlen; + u_int pad; mac = NULL; if (ifp->if_carp) @@ -568,6 +571,7 @@ if (mac != NULL) { nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); + pad = nd6_lladdr_opt_pad(ifp); optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; /* 8 byte alignments... */ optlen = (optlen + 7) & ~7; @@ -577,7 +581,8 @@ bzero(nd_opt, optlen); nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; nd_opt->nd_opt_len = optlen >> 3; - bcopy(mac, nd_opt + 1, ifp->if_addrlen); + memcpy((char *)(nd_opt + 1) + pad, mac, + ifp->if_addrlen); } } /* @@ -662,6 +667,7 @@ size_t linkhdrsize; int flags, is_override, is_router, is_solicited; int lladdr_off, lladdrlen, checklink; + u_int lladdr_pad; bool flush_holdchain = false; NET_EPOCH_ASSERT(); @@ -730,8 +736,9 @@ lladdr = NULL; lladdrlen = 0; + lladdr_pad = nd6_lladdr_opt_pad(ifp); if (ndopts.nd_opts_tgt_lladdr) { - lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); + lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1) + lladdr_pad; lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; } @@ -1072,6 +1079,7 @@ } } if ((tlladdr & ND6_NA_OPT_LLA) && mac != NULL) { + u_int pad = nd6_lladdr_opt_pad(ifp); int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1); @@ -1084,7 +1092,7 @@ bzero((caddr_t)nd_opt, optlen); nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; nd_opt->nd_opt_len = optlen >> 3; - bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); + memcpy((char *)(nd_opt + 1) + pad, mac, ifp->if_addrlen); } else flags &= ~ND_NA_FLAG_OVERRIDE; @@ -1141,6 +1149,25 @@ } } +/* + * Number of reserved octets between the header of an ND link-layer + * address option and the link-layer address itself. On IPoIB links + * two zero octets are prepended to the 20-octet link-layer address + * to fill the 24-octet option (RFC 4391, section 9.3); on all other + * link types the address immediately follows the option header. + */ +u_int +nd6_lladdr_opt_pad(struct ifnet *ifp) +{ + switch (ifp->if_type) { + case IFT_INFINIBAND: + case IFT_INFINIBANDLAG: + return (2); + default: + return (0); + } +} + struct dadq { TAILQ_ENTRY(dadq) dad_list; struct ifaddr *dad_ifa; diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -169,6 +169,7 @@ char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; char *lladdr; int lladdrlen; + u_int lladdr_pad; ifp = m->m_pkthdr.rcvif; @@ -223,8 +224,9 @@ lladdr = NULL; lladdrlen = 0; + lladdr_pad = nd6_lladdr_opt_pad(ifp); if (ndopts.nd_opts_src_lladdr) { - lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); + lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1) + lladdr_pad; lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } @@ -577,9 +579,10 @@ { char *lladdr = NULL; int lladdrlen = 0; + u_int lladdr_pad = nd6_lladdr_opt_pad(ifp); if (ndopts.nd_opts_src_lladdr) { - lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); + lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1) + lladdr_pad; lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; }