+int
+sendfragsfromto(s, buf, local, remote, count_persend, frag_flags)
+ int s;
+ vchar_t *buf;
+ struct sockaddr *local;
+ struct sockaddr *remote;
+ int count_persend;
+ u_int32_t frag_flags;
+{
+ struct isakmp *main_hdr;
+ struct isakmp *hdr;
+ struct isakmp_frag *fraghdr;
+ caddr_t data;
+ caddr_t sdata;
+ size_t datalen;
+ size_t max_datalen;
+ size_t fraglen;
+ vchar_t *frag;
+ unsigned int trailer;
+ unsigned int fragnum = 0;
+ size_t len;
+#ifdef ENABLE_NATT
+ size_t extralen = (frag_flags & FRAG_PUT_NON_ESP_MARKER)? NON_ESP_MARKER_LEN : 0;
+#else
+ size_t extralen = 0;
+#endif
+
+ /*
+ * fragmented packet must have the same exchange type (amongst other fields in the header).
+ */
+ main_hdr = (struct isakmp *)buf->v;
+
+ /*
+ * We want to send a a packet smaller than ISAKMP_FRAG_MAXLEN
+ * First compute the maximum data length that will fit in it
+ */
+ max_datalen = ISAKMP_FRAG_MAXLEN -
+ (sizeof(*main_hdr) + sizeof(*fraghdr));
+
+ sdata = buf->v;
+ len = buf->l;
+
+ while (len > 0) {
+ fragnum++;
+
+ if (len > max_datalen)
+ datalen = max_datalen;
+ else
+ datalen = len;
+
+ fraglen = sizeof(*hdr) + sizeof(*fraghdr) + datalen;
+
+ if ((frag = vmalloc(fraglen)) == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Cannot allocate memory\n");
+ return -1;
+ }
+
+ hdr = (struct isakmp *)frag->v;
+ bcopy(main_hdr, hdr, sizeof(*hdr));
+ hdr->len = htonl(frag->l);
+ hdr->np = ISAKMP_NPTYPE_FRAG;
+
+ fraghdr = (struct isakmp_frag *)(hdr + 1);
+ fraghdr->unknown0 = 0;
+ fraghdr->len = htons(fraglen - sizeof(*hdr));
+ fraghdr->unknown1 = htons(1);
+ fraghdr->index = fragnum;
+ if (len == datalen)
+ fraghdr->flags = ISAKMP_FRAG_LAST;
+ else
+ fraghdr->flags = 0;
+
+ data = (caddr_t)(fraghdr + 1);
+ memcpy(data, sdata, datalen);
+
+#ifdef ENABLE_NATT
+ /* If NAT-T port floating is in use, 4 zero bytes (non-ESP marker)
+ must added just before the packet itself. For this we must
+ allocate a new buffer and release it at the end. */
+ if (extralen) {
+ vchar_t *vbuf;
+
+ if ((vbuf = vmalloc(frag->l + extralen)) == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "%s: vbuf allocation failed\n", __FUNCTION__);
+ vfree(frag);
+ return -1;
+ }
+ *(u_int32_t *)vbuf->v = 0; // non-esp marker
+ memcpy(vbuf->v + extralen, frag->v, frag->l);
+ vfree(frag);
+ frag = vbuf;
+ }
+#endif
+
+ if (sendfromto(s, frag->v, frag->l, local, remote, count_persend) == -1) {
+ plog(LLV_ERROR, LOCATION, NULL, "sendfromto failed\n");
+ vfree(frag);
+ return -1;
+ }
+
+ vfree(frag);
+
+ len -= datalen;
+ sdata += datalen;
+ }
+
+ plog(LLV_DEBUG2, LOCATION, NULL,
+ "%s: processed %d fragments\n", __FUNCTION__, fragnum);
+
+ return fragnum;
+}