+ error = ip6_fragment_packet(&m, opt,
+ &exthdrs, ifp, mtu, alwaysfrag, unfragpartlen, ro_pmtu, nxt0,
+ optlen);
+
+ if (error)
+ goto bad;
+
+/*
+ * The evaluateloop label is where we decide whether to continue looping over
+ * packets or call into nd code to send.
+ */
+evaluateloop:
+
+ /*
+ * m may be NULL when we jump to the evaluateloop label from PF or
+ * other code that can drop packets.
+ */
+ if (m != NULL) {
+ /*
+ * If we already have a chain to send, tack m onto the end.
+ * Otherwise make m the start and end of the to-be-sent chain.
+ */
+ if (sendchain != NULL) {
+ sendchain_last->m_nextpkt = m;
+ } else {
+ sendchain = m;
+ }
+
+ /* Fragmentation may mean m is a chain. Find the last packet. */
+ while (m->m_nextpkt)
+ m = m->m_nextpkt;
+ sendchain_last = m;
+ pktcnt++;
+ }
+
+ /* Fill in next m from inputchain as appropriate. */
+ m = inputchain;
+ if (m != NULL) {
+ /* Isolate m from rest of input chain. */
+ inputchain = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+
+ /*
+ * Clear exthdrs and ipsec_state so stale contents are not
+ * reused. Note this also clears the exthdrs.merged flag.
+ */
+ bzero(&exthdrs, sizeof(exthdrs));
+ bzero(&ipsec_state, sizeof(ipsec_state));
+
+ /* Continue looping. */
+ goto loopit;
+ }
+
+ /*
+ * If we get here, there's no more mbufs in inputchain, so send the
+ * sendchain if there is one.
+ */
+ if (pktcnt > 0) {
+ error = nd6_output_list(ifp, origifp, sendchain, dst,
+ ro->ro_rt, adv);
+ /*
+ * Fall through to done label even in error case because
+ * nd6_output_list frees packetchain in both success and
+ * failure cases.
+ */
+ }
+
+done:
+ if (ifpp_save != NULL && *ifpp_save != NULL) {
+ ifnet_release(*ifpp_save);
+ *ifpp_save = NULL;
+ }
+ ROUTE_RELEASE(&ip6route);
+#if IPSEC
+ ROUTE_RELEASE(&ipsec_state.ro);
+ if (sp != NULL)
+ key_freesp(sp, KEY_SADB_UNLOCKED);
+#endif /* IPSEC */
+#if NECP
+ ROUTE_RELEASE(&necp_route);
+#endif /* NECP */
+#if DUMMYNET
+ ROUTE_RELEASE(&saved_route);
+ ROUTE_RELEASE(&saved_ro_pmtu);
+#endif /* DUMMYNET */
+
+ if (ia != NULL)
+ IFA_REMREF(&ia->ia_ifa);
+ if (src_ia != NULL)
+ IFA_REMREF(&src_ia->ia_ifa);
+ if (ifp != NULL)
+ ifnet_release(ifp);
+ if (origifp != NULL)
+ ifnet_release(origifp);
+ if (ip6_output_measure) {
+ net_perf_measure_time(&net_perf, &start_tv, packets_processed);
+ net_perf_histogram(&net_perf, packets_processed);
+ }
+ return (error);
+
+freehdrs:
+ if (exthdrs.ip6e_hbh != NULL) {
+ if (exthdrs.merged)
+ panic("Double free of ip6e_hbh");
+ m_freem(exthdrs.ip6e_hbh);
+ }
+ if (exthdrs.ip6e_dest1 != NULL) {
+ if (exthdrs.merged)
+ panic("Double free of ip6e_dest1");
+ m_freem(exthdrs.ip6e_dest1);
+ }
+ if (exthdrs.ip6e_rthdr != NULL) {
+ if (exthdrs.merged)
+ panic("Double free of ip6e_rthdr");
+ m_freem(exthdrs.ip6e_rthdr);
+ }
+ if (exthdrs.ip6e_dest2 != NULL) {
+ if (exthdrs.merged)
+ panic("Double free of ip6e_dest2");
+ m_freem(exthdrs.ip6e_dest2);
+ }
+ /* FALLTHRU */
+bad:
+ if (inputchain != NULL)
+ m_freem_list(inputchain);
+ if (sendchain != NULL)
+ m_freem_list(sendchain);
+ if (m != NULL)
+ m_freem(m);
+
+ goto done;
+
+#undef ipf_pktopts
+#undef exthdrs
+#undef ip6route
+#undef ipsec_state
+#undef saved_route
+#undef saved_ro_pmtu
+#undef args
+}
+
+/* ip6_fragment_packet
+ *
+ * The fragmentation logic is rather complex:
+ * 1: normal case (dontfrag == 0, alwaysfrag == 0)
+ * 1-a: send as is if tlen <= path mtu
+ * 1-b: fragment if tlen > path mtu
+ *
+ * 2: if user asks us not to fragment (dontfrag == 1)
+ * 2-a: send as is if tlen <= interface mtu
+ * 2-b: error if tlen > interface mtu
+ *
+ * 3: if we always need to attach fragment header (alwaysfrag == 1)
+ * always fragment
+ *
+ * 4: if dontfrag == 1 && alwaysfrag == 1
+ * error, as we cannot handle this conflicting request
+ */
+
+static int
+ip6_fragment_packet(struct mbuf **mptr, struct ip6_pktopts *opt,
+ struct ip6_exthdrs *exthdrsp, struct ifnet *ifp, uint32_t mtu,
+ boolean_t alwaysfrag, uint32_t unfragpartlen, struct route_in6 *ro_pmtu,
+ int nxt0, uint32_t optlen)
+{
+ VERIFY(NULL != mptr);
+ struct mbuf *m = *mptr;
+ int error = 0;
+ size_t tlen = m->m_pkthdr.len;
+ boolean_t dontfrag = (opt != NULL && (opt->ip6po_flags & IP6PO_DONTFRAG));
+
+ if (m->m_pkthdr.pkt_flags & PKTF_FORWARDED) {
+ dontfrag = TRUE;
+ /*
+ * Discard partial sum information if this packet originated
+ * from another interface; the packet would already have the
+ * final checksum and we shouldn't recompute it.
+ */
+ if ((m->m_pkthdr.csum_flags & (CSUM_DATA_VALID|CSUM_PARTIAL)) ==
+ (CSUM_DATA_VALID|CSUM_PARTIAL)) {
+ m->m_pkthdr.csum_flags &= ~CSUM_TX_FLAGS;
+ m->m_pkthdr.csum_data = 0;
+ }
+ }