+
+/*
+ * @brief Given outgoing interface it determines what checksum needs
+ * to be computed in software and what needs to be offloaded to the
+ * interface.
+ *
+ * @param ifp Pointer to the outgoing interface
+ * @param m Pointer to the packet
+ * @param hlen IP header length
+ * @param ip_len Total packet size i.e. headers + data payload
+ * @param sw_csum Pointer to a software checksum flag set
+ *
+ * @return void
+ */
+void
+ip_output_checksum(struct ifnet *ifp, struct mbuf *m, int hlen, int ip_len,
+ uint32_t *sw_csum)
+{
+ int tso = TSO_IPV4_OK(ifp, m);
+ uint32_t hwcap = ifp->if_hwassist;
+
+ m->m_pkthdr.csum_flags |= CSUM_IP;
+
+ if (!hwcksum_tx) {
+ /* do all in software; hardware checksum offload is disabled */
+ *sw_csum = (CSUM_DELAY_DATA | CSUM_DELAY_IP) &
+ m->m_pkthdr.csum_flags;
+ } else {
+ /* do in software what the hardware cannot */
+ *sw_csum = m->m_pkthdr.csum_flags &
+ ~IF_HWASSIST_CSUM_FLAGS(hwcap);
+ }
+
+ if (hlen != sizeof (struct ip)) {
+ *sw_csum |= ((CSUM_DELAY_DATA | CSUM_DELAY_IP) &
+ m->m_pkthdr.csum_flags);
+ } else if (!(*sw_csum & CSUM_DELAY_DATA) && (hwcap & CSUM_PARTIAL)) {
+ int interface_mtu = ifp->if_mtu;
+
+ if (INTF_ADJUST_MTU_FOR_CLAT46(ifp)) {
+ interface_mtu = IN6_LINKMTU(ifp);
+ /* Further adjust the size for CLAT46 expansion */
+ interface_mtu -= CLAT46_HDR_EXPANSION_OVERHD;
+ }
+
+ /*
+ * Partial checksum offload, if non-IP fragment, and TCP only
+ * (no UDP support, as the hardware may not be able to convert
+ * +0 to -0 (0xffff) per RFC1122 4.1.3.4. unless the interface
+ * supports "invert zero" capability.)
+ */
+ if (hwcksum_tx && !tso &&
+ ((m->m_pkthdr.csum_flags & CSUM_TCP) ||
+ ((hwcap & CSUM_ZERO_INVERT) &&
+ (m->m_pkthdr.csum_flags & CSUM_ZERO_INVERT))) &&
+ ip_len <= interface_mtu) {
+ uint16_t start = sizeof (struct ip);
+ uint16_t ulpoff = m->m_pkthdr.csum_data & 0xffff;
+ m->m_pkthdr.csum_flags |=
+ (CSUM_DATA_VALID | CSUM_PARTIAL);
+ m->m_pkthdr.csum_tx_stuff = (ulpoff + start);
+ m->m_pkthdr.csum_tx_start = start;
+ /* do IP hdr chksum in software */
+ *sw_csum = CSUM_DELAY_IP;
+ } else {
+ *sw_csum |= (CSUM_DELAY_DATA & m->m_pkthdr.csum_flags);
+ }
+ }
+
+ if (*sw_csum & CSUM_DELAY_DATA) {
+ in_delayed_cksum(m);
+ *sw_csum &= ~CSUM_DELAY_DATA;
+ }
+
+ if (hwcksum_tx) {
+ /*
+ * Drop off bits that aren't supported by hardware;
+ * also make sure to preserve non-checksum related bits.
+ */
+ m->m_pkthdr.csum_flags =
+ ((m->m_pkthdr.csum_flags &
+ (IF_HWASSIST_CSUM_FLAGS(hwcap) | CSUM_DATA_VALID)) |
+ (m->m_pkthdr.csum_flags & ~IF_HWASSIST_CSUM_MASK));
+ } else {
+ /* drop all bits; hardware checksum offload is disabled */
+ m->m_pkthdr.csum_flags = 0;
+ }
+}
+
+/*
+ * GRE protocol output for PPP/PPTP
+ */
+int
+ip_gre_output(struct mbuf *m)
+{
+ struct route ro;
+ int error;
+
+ bzero(&ro, sizeof (ro));
+
+ error = ip_output(m, NULL, &ro, 0, NULL, NULL);
+
+ ROUTE_RELEASE(&ro);
+
+ return (error);
+}
+
+static int
+sysctl_reset_ip_output_stats SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2)
+ int error, i;
+
+ i = ip_output_measure;
+ error = sysctl_handle_int(oidp, &i, 0, req);
+ if (error || req->newptr == USER_ADDR_NULL)
+ goto done;
+ /* impose bounds */
+ if (i < 0 || i > 1) {
+ error = EINVAL;
+ goto done;
+ }
+ if (ip_output_measure != i && i == 1) {
+ net_perf_initialize(&net_perf, ip_output_measure_bins);
+ }
+ ip_output_measure = i;
+done:
+ return (error);
+}
+
+static int
+sysctl_ip_output_measure_bins SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2)
+ int error;
+ uint64_t i;
+
+ i = ip_output_measure_bins;
+ error = sysctl_handle_quad(oidp, &i, 0, req);
+ if (error || req->newptr == USER_ADDR_NULL)
+ goto done;
+ /* validate data */
+ if (!net_perf_validate_bins(i)) {
+ error = EINVAL;
+ goto done;
+ }
+ ip_output_measure_bins = i;
+done:
+ return (error);
+}
+
+static int
+sysctl_ip_output_getperf SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ if (req->oldptr == USER_ADDR_NULL)
+ req->oldlen = (size_t)sizeof (struct ipstat);
+
+ return (SYSCTL_OUT(req, &net_perf, MIN(sizeof (net_perf), req->oldlen)));
+}