+#ifdef IKE_NAT_T
+/*
+ * isakmp packet handler for natt port (4500)
+ */
+int
+isakmp_natt_handler(so_isakmp)
+ int so_isakmp;
+{
+ u_char temp_buffer[sizeof(struct isakmp) + 4];
+ struct isakmp *isakmp = (struct isakmp*)(temp_buffer + 4);
+ struct sockaddr_storage remote;
+ struct sockaddr_storage local;
+ int remote_len = sizeof(remote);
+ int local_len = sizeof(local);
+ int len;
+ u_short port;
+ vchar_t *buf = NULL;
+ int error = -1;
+
+ /* read message by MSG_PEEK */
+ while ((len = recvfromto(so_isakmp, temp_buffer, sizeof(temp_buffer),
+ MSG_PEEK, (struct sockaddr *)&remote, &remote_len,
+ (struct sockaddr *)&local, &local_len)) < 0) {
+ if (errno == EINTR)
+ continue;
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to receive isakmp packet\n");
+ goto end;
+ }
+
+ /* remove the four bytes of zeros on nat traversal port */
+ if (*(u_long*)temp_buffer != 0L)
+ {
+ /*
+ * This is a UDP encapsulated IPSec packet,
+ * we should drop it.
+ *
+ * TBD: Need a way to read the packet.
+ * The kernel intercepts these packets on Mac OS X
+ * but not all kernels will handle this the same way.
+ */
+ goto end;
+ }
+
+ /* check isakmp header length */
+ if (len < sizeof(temp_buffer)) {
+ plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote,
+ "packet shorter than isakmp header size.\n");
+ /* dummy receive */
+ if ((len = recvfrom(so_isakmp, (char *)temp_buffer, sizeof(temp_buffer),
+ 0, (struct sockaddr *)&remote, &remote_len)) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to receive isakmp packet\n");
+ }
+ goto end;
+ }
+
+ remote_len = sizeof(remote);
+
+ /* read real message */
+ if ((buf = vmalloc(ntohl(isakmp->len) + 4)) == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate reading buffer\n");
+ /* dummy receive */
+ if ((len = recvfrom(so_isakmp, (char *)temp_buffer, sizeof(temp_buffer),
+ 0, (struct sockaddr *)&remote, &remote_len)) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to receive isakmp packet\n");
+ }
+ goto end;
+ }
+
+ while ((len = recvfromto(so_isakmp, buf->v, buf->l,
+ 0, (struct sockaddr *)&remote, &remote_len,
+ (struct sockaddr *)&local, &local_len)) < 0) {
+ if (errno == EINTR)
+ continue;
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to read isakmp packet from socket %d, len=%d\n", so_isakmp, buf->l);
+ error = -2; /* serious problem with socket */
+ goto end;
+ }
+
+ if (len != buf->l) {
+ plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote,
+ "received invalid length, header says %d, packet is %d bytes why ?\n",
+ len, buf->l);
+ goto end;
+ }
+
+ /*
+ * Discard first 4 bytes, they're either:
+ * 0 - this is IKE traffic
+ * !0 - first four bytes are the SPI of a UDP encapsulated IPSec packet
+ * The seond type of packet should be interecepted by the kernel
+ * or dropped before we get to this point.
+ */
+ {
+ vchar_t *newbuf = vmalloc(buf->l - 4);
+ if (newbuf == NULL)
+ {
+ plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote,
+ "couldn't allocate smaller buffer.\n");
+ goto end;
+ }
+ memcpy(newbuf->v, buf->v + 4, newbuf->l);
+ vfree(buf);
+ buf = newbuf;
+ len = buf->l;
+ }
+
+ plog(LLV_DEBUG, LOCATION, NULL, "===\n");
+ plog(LLV_DEBUG, LOCATION, (struct sockaddr *)&local,
+ "%d bytes message received from %s\n",
+ len, saddr2str((struct sockaddr *)&remote));
+ plogdump(LLV_DEBUG, buf->v, buf->l);
+
+ /* avoid packets with malicious port/address */
+ switch (remote.ss_family) {
+ case AF_INET:
+ port = ((struct sockaddr_in *)&remote)->sin_port;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ port = ((struct sockaddr_in6 *)&remote)->sin6_port;
+ break;
+#endif
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid family: %d\n", remote.ss_family);
+ goto end;
+ }
+ if (port == 0) {
+ plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote,
+ "src port == 0 (valid as UDP but not with IKE)\n");
+ goto end;
+ }
+
+ {
+ struct isakmp *isakmp = (struct isakmp*)buf->v;
+ plog(LLV_DEBUG, LOCATION, (struct sockaddr*)&remote,
+ "natt receiving packet %.8X%.8X:%.8X%.8X %u\n",
+ *(u_long*)isakmp->i_ck, *(u_long*)&isakmp->i_ck[4],
+ *(u_long*)isakmp->r_ck, *(u_long*)&isakmp->r_ck[4],
+ isakmp->msgid);
+ }
+
+ /* XXX: check sender whether to be allowed or not to accept */
+
+ /* XXX: I don't know how to check isakmp half connection attack. */
+
+ /* simply reply if the packet was processed. */
+ if (check_recvdpkt((struct sockaddr *)&remote,
+ (struct sockaddr *)&local, buf)) {
+ plog(LLV_NOTIFY, LOCATION, NULL,
+ "the packet is retransmitted by %s.\n",
+ saddr2str((struct sockaddr *)&remote));
+ error = 0;
+ goto end;
+ }
+
+ /* isakmp main routine */
+ if (isakmp_main(buf, (struct sockaddr *)&remote,
+ (struct sockaddr *)&local) != 0) goto end;
+
+ error = 0;
+
+end:
+ if (buf != NULL)
+ vfree(buf);
+
+ return(error);
+}
+#endif
+
+