+ /* we (ab)use the icmp buffer to stash the original packet */
+ struct buffer_if *orig = &st->icmp;
+ BUF_ALLOC(orig,"netlink_client_deliver fragment orig");
+ buffer_copy(orig,buf);
+ BUF_FREE(buf);
+
+ const uint8_t *startindata = orig->start + hl;
+ const uint8_t *indata = startindata;
+ const uint8_t *endindata = orig->start + orig->size;
+ _Bool filtered = 0;
+
+ for (;;) {
+ /* compute our fragment offset */
+ long dataoffset = indata - startindata
+ + (orig_frag & IPHDR_FRAG_OFF)*8;
+ assert(!(dataoffset & 7));
+ if (dataoffset > IPHDR_FRAG_OFF*8) {
+ BADFRAG("ultimate fragment offset out of range");
+ break;
+ }
+
+ BUF_ALLOC(buf,"netlink_client_deliver fragment frag");
+ buffer_init(buf,calculate_max_start_pad());
+
+ /* copy header (possibly filtered); will adjust in a bit */
+ struct iphdr *fragh = buf_append(buf, hl);
+ memcpy(fragh, orig->start, hl);
+
+ /* decide how much payload to copy and copy it */
+ long avail = mtu - hl;
+ long remain = endindata - indata;
+ long use = avail < remain ? (avail & ~(long)7) : remain;
+ BUF_ADD_BYTES(append, buf, indata, use);
+ indata += use;
+
+ _Bool last_frag = indata >= endindata;
+
+ /* adjust the header */
+ fragh->tot_len = htons(buf->size);
+ fragh->frag =
+ htons((orig_frag & ~IPHDR_FRAG_OFF) |
+ (last_frag ? 0 : IPHDR_FRAG_MORE) |
+ (dataoffset >> 3));
+ fragh->check = 0;
+ fragh->check = ip_fast_csum((const void*)fragh, fragh->ihl);
+
+ /* actually send it */
+ deliver(deliver_dst, buf);
+ if (last_frag)
+ break;
+
+ /* after copying the header for the first frag,
+ * we filter the header for the remaining frags */
+ if (!filtered++) {
+ const char *bad = fragment_filter_header(orig->start, &hl);
+ if (bad) { BADFRAG("%s", bad); break; }
+ }
+ }
+
+ BUF_FREE(orig);
+
+#undef BADFRAG
+}
+
+/* Deliver a packet _to_ client; used after we have decided
+ * what to do with it (and just to check that the client has
+ * actually registered a delivery function with us). */
+static void netlink_client_deliver(struct netlink *st,
+ struct netlink_client *client,
+ uint32_t source, uint32_t dest,
+ struct buffer_if *buf)
+{
+ if (!client->deliver) {
+ string_t s,d;
+ s=ipaddr_to_string(source);
+ d=ipaddr_to_string(dest);
+ Message(M_ERR,"%s: dropping %s->%s, client not registered\n",
+ st->name,s,d);
+ BUF_FREE(buf);
+ return;
+ }
+ netlink_maybe_fragment(st,NULL, client->deliver,client->dst,client->name,
+ client->mtu, source,dest,buf);
+ client->outcount++;
+}
+
+/* Deliver a packet to the host; used after we have decided that that
+ * is what to do with it. */
+static void netlink_host_deliver(struct netlink *st,
+ struct netlink_client *sender,
+ uint32_t source, uint32_t dest,
+ struct buffer_if *buf)
+{
+ netlink_maybe_fragment(st,sender, st->deliver_to_host,st->dst,"(host)",
+ st->mtu, source,dest,buf);
+ st->outcount++;