Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netinet6/ip6_output.c
| Show First 20 Lines • Show All 204 Lines • ▼ Show 20 Lines | in6_delayed_cksum(struct mbuf *m, uint32_t plen, u_short offset) | ||||
| offset += m->m_pkthdr.csum_data; /* checksum offset */ | offset += m->m_pkthdr.csum_data; /* checksum offset */ | ||||
| if (offset + sizeof(csum) > m->m_len) | if (offset + sizeof(csum) > m->m_len) | ||||
| m_copyback(m, offset, sizeof(csum), (caddr_t)&csum); | m_copyback(m, offset, sizeof(csum), (caddr_t)&csum); | ||||
| else | else | ||||
| *(u_short *)mtodo(m, offset) = csum; | *(u_short *)mtodo(m, offset) = csum; | ||||
| } | } | ||||
| static int | |||||
| ip6_output_delayed_csum(struct mbuf *m, struct ifnet *ifp, int csum_flags, | |||||
| int plen, int optlen, bool frag) | |||||
| { | |||||
| KASSERT((plen >= optlen), ("%s:%d: plen %d < optlen %d, m %p, ifp %p " | |||||
| "csum_flags %#x frag %d\n", | |||||
| __func__, __LINE__, plen, optlen, m, ifp, csum_flags, frag)); | |||||
| if ((csum_flags & CSUM_DELAY_DATA_IPV6) || | |||||
| #ifdef SCTP | |||||
| (csum_flags & CSUM_SCTP_IPV6) || | |||||
| #endif | |||||
| (!frag && (ifp->if_capenable & IFCAP_NOMAP) == 0)) { | |||||
| m = mb_unmapped_to_ext(m); | |||||
| if (m == NULL) { | |||||
| if (frag) | |||||
| in6_ifstat_inc(ifp, ifs6_out_fragfail); | |||||
| else | |||||
| IP6STAT_INC(ip6s_odropped); | |||||
| return (ENOBUFS); | |||||
| } | |||||
| if (csum_flags & CSUM_DELAY_DATA_IPV6) { | |||||
| in6_delayed_cksum(m, plen - optlen, | |||||
| sizeof(struct ip6_hdr) + optlen); | |||||
| m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6; | |||||
| } | |||||
| #ifdef SCTP | |||||
| if (csum_flags & CSUM_SCTP_IPV6) { | |||||
| sctp_delayed_cksum(m, sizeof(struct ip6_hdr) + optlen); | |||||
| m->m_pkthdr.csum_flags &= ~CSUM_SCTP_IPV6; | |||||
| } | |||||
| #endif | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| int | int | ||||
| ip6_fragment(struct ifnet *ifp, struct mbuf *m0, int hlen, u_char nextproto, | ip6_fragment(struct ifnet *ifp, struct mbuf *m0, int hlen, u_char nextproto, | ||||
| int fraglen , uint32_t id) | int fraglen , uint32_t id) | ||||
| { | { | ||||
| struct mbuf *m, **mnext, *m_frgpart; | struct mbuf *m, **mnext, *m_frgpart; | ||||
| struct ip6_hdr *ip6, *mhip6; | struct ip6_hdr *ip6, *mhip6; | ||||
| struct ip6_frag *ip6f; | struct ip6_frag *ip6f; | ||||
| int off; | int off; | ||||
| ▲ Show 20 Lines • Show All 160 Lines • ▼ Show 20 Lines | |||||
| ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, | ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, | ||||
| struct route_in6 *ro, int flags, struct ip6_moptions *im6o, | struct route_in6 *ro, int flags, struct ip6_moptions *im6o, | ||||
| struct ifnet **ifpp, struct inpcb *inp) | struct ifnet **ifpp, struct inpcb *inp) | ||||
| { | { | ||||
| struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||||
| struct ifnet *ifp, *origifp; | struct ifnet *ifp, *origifp; | ||||
| struct mbuf *m = m0; | struct mbuf *m = m0; | ||||
| struct mbuf *mprev; | struct mbuf *mprev; | ||||
| int hlen, tlen, len; | int tlen, len; | ||||
| struct route_in6 ip6route; | struct route_in6 ip6route; | ||||
| struct rtentry *rt = NULL; | struct rtentry *rt = NULL; | ||||
| struct sockaddr_in6 *dst, src_sa, dst_sa; | struct sockaddr_in6 *dst, src_sa, dst_sa; | ||||
| struct in6_addr odst; | struct in6_addr odst; | ||||
| u_char *nexthdrp; | u_char *nexthdrp; | ||||
| int error = 0; | int error = 0; | ||||
| struct in6_ifaddr *ia = NULL; | struct in6_ifaddr *ia = NULL; | ||||
| u_long mtu; | u_long mtu; | ||||
| ▲ Show 20 Lines • Show All 604 Lines • ▼ Show 20 Lines | passout: | ||||
| } else | } else | ||||
| tso = 0; | tso = 0; | ||||
| /* | /* | ||||
| * If we added extension headers, we will not do TSO and calculate the | * If we added extension headers, we will not do TSO and calculate the | ||||
| * checksums ourselves for now. | * checksums ourselves for now. | ||||
| * XXX-BZ Need a framework to know when the NIC can handle it, even | * XXX-BZ Need a framework to know when the NIC can handle it, even | ||||
| * with ext. hdrs. | * with ext. hdrs. | ||||
| */ | */ | ||||
| if (sw_csum & CSUM_DELAY_DATA_IPV6) { | error = ip6_output_delayed_csum(m, ifp, sw_csum, plen, optlen, false); | ||||
| sw_csum &= ~CSUM_DELAY_DATA_IPV6; | if (error != 0) | ||||
| m = mb_unmapped_to_ext(m); | |||||
| if (m == NULL) { | |||||
| error = ENOBUFS; | |||||
| IP6STAT_INC(ip6s_odropped); | |||||
| goto bad; | goto bad; | ||||
| } | /* XXX-BZ m->m_pkthdr.csum_flags &= ~ifp->if_hwassist; */ | ||||
| in6_delayed_cksum(m, plen, sizeof(struct ip6_hdr)); | |||||
| } else if ((ifp->if_capenable & IFCAP_NOMAP) == 0) { | |||||
| m = mb_unmapped_to_ext(m); | |||||
| if (m == NULL) { | |||||
| error = ENOBUFS; | |||||
| IP6STAT_INC(ip6s_odropped); | |||||
| goto bad; | |||||
| } | |||||
| } | |||||
| #ifdef SCTP | |||||
| if (sw_csum & CSUM_SCTP_IPV6) { | |||||
| sw_csum &= ~CSUM_SCTP_IPV6; | |||||
| m = mb_unmapped_to_ext(m); | |||||
| if (m == NULL) { | |||||
| error = ENOBUFS; | |||||
| IP6STAT_INC(ip6s_odropped); | |||||
| goto bad; | |||||
| } | |||||
| sctp_delayed_cksum(m, sizeof(struct ip6_hdr)); | |||||
| } | |||||
| #endif | |||||
| m->m_pkthdr.csum_flags &= ifp->if_hwassist; | |||||
| tlen = m->m_pkthdr.len; | tlen = m->m_pkthdr.len; | ||||
| if ((opt && (opt->ip6po_flags & IP6PO_DONTFRAG)) || tso) | if ((opt && (opt->ip6po_flags & IP6PO_DONTFRAG)) || tso) | ||||
| dontfrag = 1; | dontfrag = 1; | ||||
| else | else | ||||
| dontfrag = 0; | dontfrag = 0; | ||||
| if (dontfrag && alwaysfrag) { /* Case 4. */ | if (dontfrag && alwaysfrag) { /* Case 4. */ | ||||
| /* Conflicting request - can't transmit. */ | /* Conflicting request - can't transmit. */ | ||||
| ▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | passout: | ||||
| } else { | } else { | ||||
| u_char nextproto; | u_char nextproto; | ||||
| /* | /* | ||||
| * Too large for the destination or interface; | * Too large for the destination or interface; | ||||
| * fragment if possible. | * fragment if possible. | ||||
| * Must be able to put at least 8 bytes per fragment. | * Must be able to put at least 8 bytes per fragment. | ||||
| */ | */ | ||||
| hlen = unfragpartlen; | |||||
| if (mtu > IPV6_MAXPACKET) | if (mtu > IPV6_MAXPACKET) | ||||
| mtu = IPV6_MAXPACKET; | mtu = IPV6_MAXPACKET; | ||||
| len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7; | len = (mtu - unfragpartlen - sizeof(struct ip6_frag)) & ~7; | ||||
| if (len < 8) { | if (len < 8) { | ||||
| error = EMSGSIZE; | error = EMSGSIZE; | ||||
| in6_ifstat_inc(ifp, ifs6_out_fragfail); | in6_ifstat_inc(ifp, ifs6_out_fragfail); | ||||
| goto bad; | goto bad; | ||||
| } | } | ||||
| /* | /* | ||||
| * If the interface will not calculate checksums on | * If the interface will not calculate checksums on | ||||
| * fragmented packets, then do it here. | * fragmented packets, then do it here. | ||||
| * XXX-BZ handle the hw offloading case. Need flags. | * XXX-BZ handle the hw offloading case. Need flags. | ||||
| */ | */ | ||||
| if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) { | error = ip6_output_delayed_csum(m, ifp, m->m_pkthdr.csum_flags, | ||||
| m = mb_unmapped_to_ext(m); | plen, optlen, true); | ||||
| if (m == NULL) { | if (error != 0) | ||||
| in6_ifstat_inc(ifp, ifs6_out_fragfail); | |||||
| error = ENOBUFS; | |||||
| goto bad; | goto bad; | ||||
| } | |||||
| in6_delayed_cksum(m, plen, hlen); | |||||
| m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6; | |||||
| } | |||||
| #ifdef SCTP | |||||
| if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) { | |||||
| m = mb_unmapped_to_ext(m); | |||||
| if (m == NULL) { | |||||
| in6_ifstat_inc(ifp, ifs6_out_fragfail); | |||||
| error = ENOBUFS; | |||||
| goto bad; | |||||
| } | |||||
| sctp_delayed_cksum(m, hlen); | |||||
| m->m_pkthdr.csum_flags &= ~CSUM_SCTP_IPV6; | |||||
| } | |||||
| #endif | |||||
| /* | /* | ||||
| * Change the next header field of the last header in the | * Change the next header field of the last header in the | ||||
| * unfragmentable part. | * unfragmentable part. | ||||
| */ | */ | ||||
| if (exthdrs.ip6e_rthdr) { | if (exthdrs.ip6e_rthdr) { | ||||
| nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *); | nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *); | ||||
| *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT; | *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT; | ||||
| } else if (exthdrs.ip6e_dest1) { | } else if (exthdrs.ip6e_dest1) { | ||||
| nextproto = *mtod(exthdrs.ip6e_dest1, u_char *); | nextproto = *mtod(exthdrs.ip6e_dest1, u_char *); | ||||
| *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT; | *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT; | ||||
| } else if (exthdrs.ip6e_hbh) { | } else if (exthdrs.ip6e_hbh) { | ||||
| nextproto = *mtod(exthdrs.ip6e_hbh, u_char *); | nextproto = *mtod(exthdrs.ip6e_hbh, u_char *); | ||||
| *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT; | *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT; | ||||
| } else { | } else { | ||||
| ip6 = mtod(m, struct ip6_hdr *); | |||||
| nextproto = ip6->ip6_nxt; | nextproto = ip6->ip6_nxt; | ||||
| ip6->ip6_nxt = IPPROTO_FRAGMENT; | ip6->ip6_nxt = IPPROTO_FRAGMENT; | ||||
| } | } | ||||
| /* | /* | ||||
| * Loop through length of segment after first fragment, | * Loop through length of segment after first fragment, | ||||
| * make new header and copy data of each part and link onto | * make new header and copy data of each part and link onto | ||||
| * chain. | * chain. | ||||
| */ | */ | ||||
| m0 = m; | m0 = m; | ||||
| id = htonl(ip6_randomid()); | id = htonl(ip6_randomid()); | ||||
| if ((error = ip6_fragment(ifp, m, hlen, nextproto, len, id))) | error = ip6_fragment(ifp, m, unfragpartlen, nextproto,len, id); | ||||
| if (error != 0) | |||||
| goto sendorfree; | goto sendorfree; | ||||
| in6_ifstat_inc(ifp, ifs6_out_fragok); | in6_ifstat_inc(ifp, ifs6_out_fragok); | ||||
| } | } | ||||
| /* Remove leading garbage. */ | /* Remove leading garbage. */ | ||||
| sendorfree: | sendorfree: | ||||
| m = m0->m_nextpkt; | m = m0->m_nextpkt; | ||||
| ▲ Show 20 Lines • Show All 2,074 Lines • Show Last 20 Lines | |||||