+
+void
+esp6_ctlinput(int cmd, struct sockaddr *sa, void *d, __unused struct ifnet *ifp)
+{
+ const struct newesp *espp;
+ struct newesp esp;
+ struct ip6ctlparam *ip6cp = NULL, ip6cp1;
+ struct secasvar *sav;
+ struct ip6_hdr *ip6;
+ struct mbuf *m;
+ int off = 0;
+ struct sockaddr_in6 *sa6_src, *sa6_dst;
+
+ if (sa->sa_family != AF_INET6 ||
+ sa->sa_len != sizeof(struct sockaddr_in6))
+ return;
+ if ((unsigned)cmd >= PRC_NCMDS)
+ return;
+
+ /* if the parameter is from icmp6, decode it. */
+ if (d != NULL) {
+ ip6cp = (struct ip6ctlparam *)d;
+ m = ip6cp->ip6c_m;
+ ip6 = ip6cp->ip6c_ip6;
+ off = ip6cp->ip6c_off;
+ } else {
+ m = NULL;
+ ip6 = NULL;
+ }
+
+ if (ip6) {
+ /*
+ * Notify the error to all possible sockets via pfctlinput2.
+ * Since the upper layer information (such as protocol type,
+ * source and destination ports) is embedded in the encrypted
+ * data and might have been cut, we can't directly call
+ * an upper layer ctlinput function. However, the pcbnotify
+ * function will consider source and destination addresses
+ * as well as the flow info value, and may be able to find
+ * some PCB that should be notified.
+ * Although pfctlinput2 will call esp6_ctlinput(), there is
+ * no possibility of an infinite loop of function calls,
+ * because we don't pass the inner IPv6 header.
+ */
+ bzero(&ip6cp1, sizeof(ip6cp1));
+ ip6cp1.ip6c_src = ip6cp->ip6c_src;
+ pfctlinput2(cmd, sa, (void *)&ip6cp1);
+
+ /*
+ * Then go to special cases that need ESP header information.
+ * XXX: We assume that when ip6 is non NULL,
+ * M and OFF are valid.
+ */
+
+ /* check if we can safely examine src and dst ports */
+ if (m->m_pkthdr.len < off + sizeof(esp))
+ return;
+
+ if (m->m_len < off + sizeof(esp)) {
+ /*
+ * this should be rare case,
+ * so we compromise on this copy...
+ */
+ m_copydata(m, off, sizeof(esp), (caddr_t)&esp);
+ espp = &esp;
+ } else
+ espp = (struct newesp*)(void *)(mtod(m, caddr_t) + off);
+
+ if (cmd == PRC_MSGSIZE) {
+ int valid = 0;
+
+ /*
+ * Check to see if we have a valid SA corresponding to
+ * the address in the ICMP message payload.
+ */
+ sa6_src = ip6cp->ip6c_src;
+ sa6_dst = (struct sockaddr_in6 *)(void *)sa;
+ sav = key_allocsa(AF_INET6,
+ (caddr_t)&sa6_src->sin6_addr,
+ (caddr_t)&sa6_dst->sin6_addr,
+ IPPROTO_ESP, espp->esp_spi);
+ if (sav) {
+ if (sav->state == SADB_SASTATE_MATURE ||
+ sav->state == SADB_SASTATE_DYING)
+ valid++;
+ key_freesav(sav, KEY_SADB_UNLOCKED);
+ }
+
+ /* XXX Further validation? */
+
+ /*
+ * Depending on the value of "valid" and routing table
+ * size (mtudisc_{hi,lo}wat), we will:
+ * - recalcurate the new MTU and create the
+ * corresponding routing entry, or
+ * - ignore the MTU change notification.
+ */
+ icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
+ }
+ } else {
+ /* we normally notify any pcb here */
+ }
+}