+
+ /*
+ * Calculate the length to quote from original packet and prevent
+ * the ICMP mbuf from overflowing.
+ * Unfortunatly this is non-trivial since ip_forward()
+ * sends us truncated packets.
+ */
+ nlen = m_length(n);
+ if (oip->ip_p == IPPROTO_TCP) {
+ struct tcphdr *th = NULL;
+ u_int16_t tcphlen = 0;
+
+ /*
+ * If the packet got truncated and TCP header
+ * is not contained in the packet, send out
+ * standard reply with only IP header as payload
+ */
+ if (oiphlen + sizeof(struct tcphdr) > n->m_len &&
+ n->m_next == NULL) {
+ goto stdreply;
+ }
+
+ /*
+ * Otherwise, pull up to get IP and TCP headers
+ * together
+ */
+ if (n->m_len < (oiphlen + sizeof(struct tcphdr)) &&
+ (n = m_pullup(n, (oiphlen + sizeof(struct tcphdr)))) == NULL) {
+ goto freeit;
+ }
+
+ /*
+ * Reinit pointers derived from mbuf data pointer
+ * as things might have moved around with m_pullup
+ */
+ oip = mtod(n, struct ip *);
+ th = (struct tcphdr *)(void *)((caddr_t)oip + oiphlen);
+
+ if (th != ((struct tcphdr *)P2ROUNDDOWN(th,
+ sizeof(u_int32_t))) ||
+ ((th->th_off << 2) > UINT16_MAX)) {
+ goto freeit;
+ }
+ tcphlen = (uint16_t)(th->th_off << 2);
+
+ /* Sanity checks */
+ if (tcphlen < sizeof(struct tcphdr)) {
+ goto freeit;
+ }
+ if (oip->ip_len < (oiphlen + tcphlen)) {
+ goto freeit;
+ }
+ if ((oiphlen + tcphlen) > n->m_len && n->m_next == NULL) {
+ goto stdreply;
+ }
+ if (n->m_len < (oiphlen + tcphlen) &&
+ (n = m_pullup(n, (oiphlen + tcphlen))) == NULL) {
+ goto freeit;
+ }
+
+ /*
+ * Reinit pointers derived from mbuf data pointer
+ * as things might have moved around with m_pullup
+ */
+ oip = mtod(n, struct ip *);
+ th = (struct tcphdr *)(void *)((caddr_t)oip + oiphlen);
+
+ icmpelen = max(tcphlen, min(icmp_datalen,
+ (oip->ip_len - oiphlen)));
+ } else {
+stdreply: icmpelen = max(ICMP_MINLEN, min(icmp_datalen,
+ (oip->ip_len - oiphlen)));
+ }
+
+ icmplen = min(oiphlen + icmpelen, nlen);
+ if (icmplen < sizeof(struct ip)) {