Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -4356,7 +4356,7 @@ ipsec_support inet | ipsec_support inet6 netipsec/subr_ipsec.c optional ipsec inet | ipsec inet6 | \ ipsec_support inet | ipsec_support inet6 -netipsec/udpencap.c optional ipsec inet +netipsec/udpencap.c optional ipsec inet | ipsec inet6 netipsec/xform_ah.c optional ipsec inet | ipsec inet6 netipsec/xform_esp.c optional ipsec inet | ipsec inet6 netipsec/xform_ipcomp.c optional ipsec inet | ipsec inet6 Index: sys/netinet/udp_usrreq.c =================================================================== --- sys/netinet/udp_usrreq.c +++ sys/netinet/udp_usrreq.c @@ -332,7 +332,7 @@ } if (up->u_flags & UF_ESPINUDP) {/* IPSec UDP encaps. */ if (IPSEC_ENABLED(ipv4) && - UDPENCAP_INPUT(n, off, AF_INET) != 0) + UDPENCAP_INPUT(ipv4, n, off, AF_INET) != 0) return (0); /* Consumed. */ } #endif /* IPSEC */ @@ -1005,7 +1005,7 @@ INP_WUNLOCK(inp); return (ENOPROTOOPT); } - error = UDPENCAP_PCBCTL(inp, sopt); + error = UDPENCAP_PCBCTL(ipv4, inp, sopt); break; #endif /* INET */ #endif /* IPSEC */ @@ -1052,7 +1052,7 @@ INP_WUNLOCK(inp); return (ENOPROTOOPT); } - error = UDPENCAP_PCBCTL(inp, sopt); + error = UDPENCAP_PCBCTL(ipv4, inp, sopt); break; #endif /* INET */ #endif /* IPSEC */ Index: sys/netinet6/in6_proto.c =================================================================== --- sys/netinet6/in6_proto.c +++ sys/netinet6/in6_proto.c @@ -158,7 +158,7 @@ .pr_flags = PR_ATOMIC|PR_ADDR, .pr_input = udp6_input, .pr_ctlinput = udp6_ctlinput, - .pr_ctloutput = ip6_ctloutput, + .pr_ctloutput = udp6_ctloutput, #ifndef INET /* Do not call initialization twice. */ .pr_init = udp_init, #endif Index: sys/netinet6/udp6_usrreq.c =================================================================== --- sys/netinet6/udp6_usrreq.c +++ sys/netinet6/udp6_usrreq.c @@ -78,6 +78,7 @@ #include "opt_rss.h" #include +#include #include #include #include @@ -154,6 +155,9 @@ INP_RLOCK(inp); return (in_pcbrele_rlocked(inp)); } + + off += sizeof(struct udphdr); + #if defined(IPSEC) || defined(IPSEC_SUPPORT) /* Check AH/ESP integrity. */ if (IPSEC_ENABLED(ipv6)) { @@ -162,6 +166,11 @@ return (0); } } + if (up->u_flags & UF_ESPINUDP) {/* IPSec UDP encaps. */ + if (IPSEC_ENABLED(ipv6) && + UDPENCAP_INPUT(ipv6, n, off, AF_INET6) != 0) + return (0); /* Consumed. */ + } #endif /* IPSEC */ #ifdef MAC if (mac_inpcb_check_deliver(inp, n) != 0) { @@ -185,7 +194,7 @@ } } - m_adj(n, off + sizeof(struct udphdr)); + m_adj(n, off); so = inp->inp_socket; SOCKBUF_LOCK(&so->so_rcv); @@ -687,6 +696,78 @@ 0, 0, udp6_getcred, "S,xucred", "Get the xucred of a UDP6 connection"); +int +udp6_ctloutput(struct socket *so, struct sockopt *sopt) +{ + struct inpcb *inp; + int error; + + error = 0; + inp = sotoinpcb(so); + KASSERT(inp != NULL, ("%s: inp == NULL", __func__)); + INP_WLOCK(inp); + if (sopt->sopt_level != so->so_proto->pr_protocol) { +#ifdef INET6 + if (INP_CHECK_SOCKAF(so, AF_INET6)) { + INP_WUNLOCK(inp); + error = ip6_ctloutput(so, sopt); + } +#endif +#if defined(INET) && defined(INET6) + else +#endif +#ifdef INET + { + INP_WUNLOCK(inp); + error = ip_ctloutput(so, sopt); + } +#endif + return (error); + } + + switch (sopt->sopt_dir) { + case SOPT_SET: + switch (sopt->sopt_name) { +#if defined(IPSEC) || defined(IPSEC_SUPPORT) +#ifdef INET6 + case UDP_ENCAP: + if (!IPSEC_ENABLED(ipv6)) { + INP_WUNLOCK(inp); + return (ENOPROTOOPT); + } + error = UDPENCAP_PCBCTL(ipv6, inp, sopt); + break; +#endif /* INET */ +#endif /* IPSEC */ + default: + INP_WUNLOCK(inp); + error = ENOPROTOOPT; + break; + } + break; + case SOPT_GET: + switch (sopt->sopt_name) { +#if defined(IPSEC) || defined(IPSEC_SUPPORT) +#ifdef INET6 + case UDP_ENCAP: + if (!IPSEC_ENABLED(ipv6)) { + INP_WUNLOCK(inp); + return (ENOPROTOOPT); + } + error = UDPENCAP_PCBCTL(ipv6, inp, sopt); + break; +#endif /* INET6 */ +#endif /* IPSEC */ + default: + INP_WUNLOCK(inp); + error = ENOPROTOOPT; + break; + } + break; + } + return (error); +} + static int udp6_output(struct socket *so, int flags_arg, struct mbuf *m, struct sockaddr *addr6, struct mbuf *control, struct thread *td) Index: sys/netinet6/udp6_var.h =================================================================== --- sys/netinet6/udp6_var.h +++ sys/netinet6/udp6_var.h @@ -72,6 +72,7 @@ void udp6_ctlinput(int, struct sockaddr *, void *); void udplite6_ctlinput(int, struct sockaddr *, void *); +int udp6_ctloutput(struct socket *, struct sockopt *); int udp6_input(struct mbuf **, int *, int); #endif Index: sys/netipsec/ipsec_input.c =================================================================== --- sys/netipsec/ipsec_input.c +++ sys/netipsec/ipsec_input.c @@ -533,6 +533,15 @@ m_copydata(m, protoff, 1, &nxt8); prot = nxt8; + /* + * Check that we have NAT-T enabled and apply transport mode + * decapsulation NAT procedure (RFC3948). + * Do this before invoking into the PFIL. + */ + if (sav->natt != NULL && + (prot == IPPROTO_UDP || prot == IPPROTO_TCP)) + udp_ipsec_adjust_cksum(m, sav, prot, skip); + /* IPv6-in-IP encapsulation */ if (prot == IPPROTO_IPV6 && saidx->mode != IPSEC_MODE_TRANSPORT) { Index: sys/netipsec/ipsec_mod.c =================================================================== --- sys/netipsec/ipsec_mod.c +++ sys/netipsec/ipsec_mod.c @@ -85,6 +85,8 @@ .capability = ipsec6_capability, .check_policy = ipsec6_in_reject, .hdrsize = ipsec_hdrsiz_inpcb, + .udp_input = udp_ipsec_input, + .udp_pcbctl = udp_ipsec_pcbctl, }; #ifndef KLD_MODULE static const struct ipsec_support ipv6_ipsec = { Index: sys/netipsec/ipsec_output.c =================================================================== --- sys/netipsec/ipsec_output.c +++ sys/netipsec/ipsec_output.c @@ -775,7 +775,6 @@ } key_freesp(&sp), sp = NULL; /* Release reference to SP */ -#ifdef INET /* * Do UDP encapsulation if SA requires it. */ @@ -784,7 +783,6 @@ if (error != 0) goto bad; } -#endif /* INET */ /* * We're done with IPsec processing, transmit the packet using the * appropriate network protocol (IP or IPv6). Index: sys/netipsec/ipsec_support.h =================================================================== --- sys/netipsec/ipsec_support.h +++ sys/netipsec/ipsec_support.h @@ -135,10 +135,10 @@ #define IPSEC_HDRSIZE(proto, inp) \ (*(proto ## _ipsec_support)->methods->hdrsize)(inp) -#define UDPENCAP_INPUT(m, ...) \ - (*ipv4_ipsec_support->methods->udp_input)(m, __VA_ARGS__) -#define UDPENCAP_PCBCTL(inp, sopt) \ - (*ipv4_ipsec_support->methods->udp_pcbctl)(inp, sopt) +#define UDPENCAP_INPUT(proto, m, ...) \ + (*(proto ## _ipsec_support)->methods->udp_input)(m, __VA_ARGS__) +#define UDPENCAP_PCBCTL(proto, inp, sopt) \ + (*(proto ## _ipsec_support)->methods->udp_pcbctl)(inp, sopt) #elif defined(IPSEC_SUPPORT) struct ipsec_support { @@ -166,10 +166,10 @@ int ipsec_kmod_udp_pcbctl(struct ipsec_support * const, struct inpcb *, struct sockopt *); -#define UDPENCAP_INPUT(m, ...) \ - ipsec_kmod_udp_input(ipv4_ipsec_support, m, __VA_ARGS__) -#define UDPENCAP_PCBCTL(inp, sopt) \ - ipsec_kmod_udp_pcbctl(ipv4_ipsec_support, inp, sopt) +#define UDPENCAP_INPUT(proto, m, ...) \ + ipsec_kmod_udp_input(proto ## _ipsec_support, m, __VA_ARGS__) +#define UDPENCAP_PCBCTL(proto, inp, sopt) \ + ipsec_kmod_udp_pcbctl(proto ## _ipsec_support, inp, sopt) #define IPSEC_INPUT(proto, ...) \ ipsec_kmod_input(proto ## _ipsec_support, __VA_ARGS__) Index: sys/netipsec/key.c =================================================================== --- sys/netipsec/key.c +++ sys/netipsec/key.c @@ -5705,6 +5705,7 @@ struct sockaddr *sa; uint32_t addr; uint16_t cksum; + int i; IPSEC_ASSERT(sav->natt == NULL, ("natt is already initialized")); /* @@ -5791,51 +5792,113 @@ if (sav->sah->saidx.mode != IPSEC_MODE_TUNNEL) { cksum = 0; if (oai != NULL) { - /* Currently we support only AF_INET */ sa = (struct sockaddr *)(oai + 1); - if (sa->sa_family != AF_INET || - sa->sa_len != sizeof(struct sockaddr_in)) { - ipseclog((LOG_DEBUG, - "%s: wrong NAT-OAi header.\n", - __func__)); - return (EINVAL); - } - /* Ignore address if it the same */ - if (((struct sockaddr_in *)sa)->sin_addr.s_addr != - sav->sah->saidx.src.sin.sin_addr.s_addr) { - bcopy(sa, &sav->natt->oai.sa, sa->sa_len); - sav->natt->flags |= IPSEC_NATT_F_OAI; - /* Calculate checksum delta */ - addr = sav->sah->saidx.src.sin.sin_addr.s_addr; - cksum = in_addword(cksum, ~addr >> 16); - cksum = in_addword(cksum, ~addr & 0xffff); - addr = sav->natt->oai.sin.sin_addr.s_addr; - cksum = in_addword(cksum, addr >> 16); - cksum = in_addword(cksum, addr & 0xffff); + switch (sa->sa_family) { +#ifdef AF_INET + case AF_INET: + if (sa->sa_len != sizeof(struct sockaddr_in)) { + ipseclog((LOG_DEBUG, + "%s: wrong NAT-OAi header.\n", + __func__)); + return (EINVAL); + } + /* Ignore address if it the same */ + if (((struct sockaddr_in *)sa)->sin_addr.s_addr != + sav->sah->saidx.src.sin.sin_addr.s_addr) { + bcopy(sa, &sav->natt->oai.sa, sa->sa_len); + sav->natt->flags |= IPSEC_NATT_F_OAI; + /* Calculate checksum delta */ + addr = sav->sah->saidx.src.sin.sin_addr.s_addr; + cksum = in_addword(cksum, ~addr >> 16); + cksum = in_addword(cksum, ~addr & 0xffff); + addr = sav->natt->oai.sin.sin_addr.s_addr; + cksum = in_addword(cksum, addr >> 16); + cksum = in_addword(cksum, addr & 0xffff); + } + break; +#endif +#ifdef AF_INET6 + case AF_INET6: + if (sa->sa_len != sizeof(struct sockaddr_in6)) { + ipseclog((LOG_DEBUG, + "%s: wrong NAT-OAi header.\n", + __func__)); + return (EINVAL); + } + /* Ignore address if it the same */ + if (memcmp(&((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, + &sav->sah->saidx.src.sin6.sin6_addr.s6_addr, 16) != 0) { + bcopy(sa, &sav->natt->oai.sa, sa->sa_len); + sav->natt->flags |= IPSEC_NATT_F_OAI; + /* Calculate checksum delta */ + for (i = 0; i < 8; i++) + { + cksum = in_addword(cksum, ~sav->sah->saidx.src.sin6.sin6_addr.s6_addr16[i]); + cksum = in_addword(cksum, sav->natt->oai.sin6.sin6_addr.s6_addr16[i]); + } + } + break; +#endif + default: + ipseclog((LOG_DEBUG, + "%s: wrong NAT-OAi header.\n", + __func__)); + return (EINVAL); } } if (oar != NULL) { - /* Currently we support only AF_INET */ sa = (struct sockaddr *)(oar + 1); - if (sa->sa_family != AF_INET || - sa->sa_len != sizeof(struct sockaddr_in)) { - ipseclog((LOG_DEBUG, - "%s: wrong NAT-OAr header.\n", - __func__)); - return (EINVAL); - } - /* Ignore address if it the same */ - if (((struct sockaddr_in *)sa)->sin_addr.s_addr != - sav->sah->saidx.dst.sin.sin_addr.s_addr) { - bcopy(sa, &sav->natt->oar.sa, sa->sa_len); - sav->natt->flags |= IPSEC_NATT_F_OAR; - /* Calculate checksum delta */ - addr = sav->sah->saidx.dst.sin.sin_addr.s_addr; - cksum = in_addword(cksum, ~addr >> 16); - cksum = in_addword(cksum, ~addr & 0xffff); - addr = sav->natt->oar.sin.sin_addr.s_addr; - cksum = in_addword(cksum, addr >> 16); - cksum = in_addword(cksum, addr & 0xffff); + switch (sa->sa_family) { +#ifdef AF_INET + case AF_INET: + if (sa->sa_len != sizeof(struct sockaddr_in)) { + ipseclog((LOG_DEBUG, + "%s: wrong NAT-OAr header.\n", + __func__)); + return (EINVAL); + } + /* Ignore address if it the same */ + if (((struct sockaddr_in *)sa)->sin_addr.s_addr != + sav->sah->saidx.dst.sin.sin_addr.s_addr) { + bcopy(sa, &sav->natt->oar.sa, sa->sa_len); + sav->natt->flags |= IPSEC_NATT_F_OAR; + /* Calculate checksum delta */ + addr = sav->sah->saidx.dst.sin.sin_addr.s_addr; + cksum = in_addword(cksum, ~addr >> 16); + cksum = in_addword(cksum, ~addr & 0xffff); + addr = sav->natt->oar.sin.sin_addr.s_addr; + cksum = in_addword(cksum, addr >> 16); + cksum = in_addword(cksum, addr & 0xffff); + } + break; +#endif +#ifdef AF_INET6 + case AF_INET6: + if (sa->sa_len != sizeof(struct sockaddr_in6)) { + ipseclog((LOG_DEBUG, + "%s: wrong NAT-OAr header.\n", + __func__)); + return (EINVAL); + } + /* Ignore address if it the same */ + if (memcmp(&((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, + &sav->sah->saidx.dst.sin6.sin6_addr.s6_addr, 16) != 0) { + bcopy(sa, &sav->natt->oar.sa, sa->sa_len); + sav->natt->flags |= IPSEC_NATT_F_OAR; + /* Calculate checksum delta */ + for (i = 0; i < 8; i++) + { + cksum = in_addword(cksum, ~sav->sah->saidx.dst.sin6.sin6_addr.s6_addr16[i]); + cksum = in_addword(cksum, sav->natt->oar.sin6.sin6_addr.s6_addr16[i]); + } + } + break; +#endif + default: + ipseclog((LOG_DEBUG, + "%s: wrong NAT-OAr header.\n", + __func__)); + return (EINVAL); } } sav->natt->cksum = cksum; Index: sys/netipsec/udpencap.c =================================================================== --- sys/netipsec/udpencap.c +++ sys/netipsec/udpencap.c @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); #include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include @@ -44,11 +45,14 @@ #include #include #include +#include #include #include #include #include +#include + #include #include @@ -118,7 +122,6 @@ union sockaddr_union dst; struct secasvar *sav; struct udphdr *udp; - struct ip *ip; uint32_t spi; int hlen; @@ -143,7 +146,8 @@ dst.sa.sa_family = af; switch (af) { #ifdef INET - case AF_INET: + case AF_INET: { + struct ip *ip; dst.sin.sin_len = sizeof(struct sockaddr_in); ip = mtod(m, struct ip *); ip->ip_p = IPPROTO_ESP; @@ -151,11 +155,19 @@ hlen = ip->ip_hl << 2; dst.sin.sin_addr = ip->ip_dst; break; + } #endif #ifdef INET6 - case AF_INET6: - /* Not yet */ - /* FALLTHROUGH */ + case AF_INET6: { + struct ip6_hdr *ip6; + dst.sin6.sin6_len = sizeof(struct sockaddr_in6); + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_nxt = IPPROTO_ESP; + off = offsetof(struct ip6_hdr, ip6_nxt); + hlen = sizeof(struct ip6_hdr); + dst.sin6.sin6_addr = ip6->ip6_dst; + break; + } #endif default: ESPSTAT_INC(esps_nopf); @@ -198,8 +210,24 @@ * We cannot yet update the cksums so clear any h/w cksum flags * as they are no longer valid. */ - if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) - m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + switch (af) { +#ifdef INET + case AF_INET: + if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) + m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID_IPV6) + m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID_IPV6 | CSUM_PSEUDO_HDR); + break; +#endif /* INET6 */ + default: + ESPSTAT_INC(esps_nopf); + m_freem(m); + return (EPFNOSUPPORT); + } /* * We can update ip_len and ip_sum here, but ipsec4_input_cb() * will do this anyway, so don't touch them here. @@ -214,16 +242,30 @@ { struct udphdr *udp; struct mbuf *n; - struct ip *ip; int hlen, off; IPSEC_ASSERT(sav->natt != NULL, ("UDP encapsulation isn't required.")); - if (sav->sah->saidx.dst.sa.sa_family == AF_INET6) - return (EAFNOSUPPORT); + switch (sav->sah->saidx.dst.sa.sa_family) + { +#ifdef INET + case AF_INET: { + struct ip *ip; + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; + break; + } +#endif +#ifdef INET6 + case AF_INET6: + hlen = sizeof(struct ip6_hdr); + break; +#endif + default: + ESPSTAT_INC(esps_nopf); + return (EAFNOSUPPORT); + } - ip = mtod(m, struct ip *); - hlen = ip->ip_hl << 2; n = m_makespace(m, hlen, sizeof(*udp), &off); if (n == NULL) { DPRINTF(("%s: m_makespace for udphdr failed\n", __func__)); @@ -236,9 +278,34 @@ udp->uh_sum = 0; udp->uh_ulen = htons(m->m_pkthdr.len - hlen); - ip = mtod(m, struct ip *); - ip->ip_len = htons(m->m_pkthdr.len); - ip->ip_p = IPPROTO_UDP; + switch (sav->sah->saidx.dst.sa.sa_family) + { +#ifdef INET + case AF_INET: { + struct ip *ip; + ip = mtod(m, struct ip *); + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_p = IPPROTO_UDP; + break; + } +#endif +#ifdef INET6 + case AF_INET6: { + struct ip6_hdr *ip6; + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(m->m_pkthdr.len); + ip6->ip6_nxt = IPPROTO_UDP; + udp->uh_sum = in6_cksum_pseudo(ip6, m->m_pkthdr.len - hlen, ip6->ip6_nxt, 0); + m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; + m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); + break; + } +#endif + default: + ESPSTAT_INC(esps_nopf); + return (EAFNOSUPPORT); + } + return (0); } @@ -246,7 +313,6 @@ udp_ipsec_adjust_cksum(struct mbuf *m, struct secasvar *sav, int proto, int skip) { - struct ip *ip; uint16_t cksum, off; IPSEC_ASSERT(sav->natt != NULL, ("NAT-T isn't required")); @@ -272,23 +338,63 @@ if (proto == IPPROTO_TCP) { /* Ignore for TCP. */ m->m_pkthdr.csum_data = 0xffff; - m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | - CSUM_PSEUDO_HDR); + switch (sav->sah->saidx.dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | + CSUM_PSEUDO_HDR); + break; +#endif +#ifdef INET6 + case AF_INET6: + m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID_IPV6 | + CSUM_PSEUDO_HDR); + break; +#endif + default: + break; + } return; } cksum = 0; /* Reset for UDP. */ } m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); } else { /* Fully recompute */ - ip = mtod(m, struct ip *); - cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, - htons(m->m_pkthdr.len - skip + proto)); - m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); - m->m_pkthdr.csum_flags = - (proto == IPPROTO_UDP) ? CSUM_UDP: CSUM_TCP; - m->m_pkthdr.csum_data = off; - in_delayed_cksum(m); - m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; + switch (sav->sah->saidx.dst.sa.sa_family) + { +#ifdef INET + case AF_INET: { + struct ip *ip; + ip = mtod(m, struct ip *); + cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, + htons(m->m_pkthdr.len - skip + proto)); + m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); + m->m_pkthdr.csum_flags = + (proto == IPPROTO_UDP) ? CSUM_UDP: CSUM_TCP; + m->m_pkthdr.csum_data = off; + in_delayed_cksum(m); + m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; + break; + } +#endif +#ifdef INET6 + case AF_INET6: { + struct ip6_hdr *ip6; + ip6 = mtod(m, struct ip6_hdr *); + cksum = in6_cksum_pseudo(ip6, m->m_pkthdr.len - skip, proto, 0); + m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum); + m->m_pkthdr.csum_flags = + (proto == IPPROTO_UDP) ? CSUM_UDP_IPV6: CSUM_TCP_IPV6; + m->m_pkthdr.csum_data = off; + in6_delayed_cksum(m, m->m_pkthdr.len - sizeof(struct ip6_hdr), sizeof(struct ip6_hdr)); + m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6; + break; + } +#endif + default: + break; + } } }