+}
+
+/*
+ * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the
+ * checksum.
+ * RFC1812: 4.2.2.5 MUST discard messages containing invalid checksums.
+ *
+ * Is the datagram acceptable?
+ *
+ * 1. Length at least the size of an ip header
+ * 2. Version of 4
+ * 3. Checksums correctly.
+ * 4. Doesn't have a bogus length
+ */
+static bool_t netlink_check(struct netlink *st, struct buffer_if *buf,
+ char *errmsgbuf, int errmsgbuflen)
+{
+#define BAD(...) do{ \
+ snprintf(errmsgbuf,errmsgbuflen,__VA_ARGS__); \
+ return False; \
+ }while(0)
+
+ if (buf->size < (int)sizeof(struct iphdr)) BAD("len %"PRIu32"",buf->size);
+ struct iphdr *iph=(struct iphdr *)buf->start;
+ int32_t len;
+
+ if (iph->version != 4) BAD("version %u",iph->version);
+ if (iph->ihl < 5) BAD("ihl %u",iph->ihl);
+ if (buf->size < iph->ihl*4) BAD("size %"PRId32"<%u*4",buf->size,iph->ihl);
+ if (ip_fast_csum((uint8_t *)iph, iph->ihl)!=0) BAD("csum");
+ len=ntohs(iph->tot_len);
+ /* There should be no padding */
+ if (buf->size!=len) BAD("len %"PRId32"!=%"PRId32,buf->size,len);
+ if (len<(iph->ihl<<2)) BAD("len %"PRId32"<(%u<<2)",len,iph->ihl);
+ /* XXX check that there's no source route specified */
+ return True;
+
+#undef BAD
+}
+
+static const char *fragment_filter_header(uint8_t *base, long *hlp)
+{
+ const int fixedhl = sizeof(struct iphdr);
+ long hl = *hlp;
+ const uint8_t *ipend = base + hl;
+ uint8_t *op = base + fixedhl;
+ const uint8_t *ip = op;
+
+ while (ip < ipend) {
+ uint8_t opt = ip[0];
+ int remain = ipend - ip;
+ if (opt == 0x00) /* End of Options List */ break;
+ if (opt == 0x01) /* No Operation */ continue;
+ if (remain < 2) return "IPv4 options truncated at length";
+ int optlen = ip[1];
+ if (remain < optlen) return "IPv4 options truncated in option";
+ if (opt & 0x80) /* copy */ {
+ memmove(op, ip, optlen);
+ op += optlen;
+ }
+ ip += optlen;
+ }
+ while ((hl = (op - base)) & 0x3)
+ *op++ = 0x00 /* End of Option List */;
+ ((struct iphdr*)base)->ihl = hl >> 2;
+ *hlp = hl;
+
+ return 0;
+}
+
+/* Fragment or send ICMP Fragmentation Needed */
+static void netlink_maybe_fragment(struct netlink *st,
+ struct netlink_client *sender,
+ netlink_deliver_fn *deliver,
+ void *deliver_dst,
+ const char *delivery_name,
+ int32_t mtu,
+ uint32_t source, uint32_t dest,
+ struct buffer_if *buf)
+{
+ struct iphdr *iph=(struct iphdr*)buf->start;
+ long hl = iph->ihl*4;
+ const char *ssource = ipaddr_to_string(source);
+
+ if (buf->size <= mtu) {
+ deliver(deliver_dst, buf);
+ return;
+ }
+
+ MDEBUG("%s: fragmenting %s->%s org.size=%"PRId32"\n",
+ st->name, ssource, delivery_name, buf->size);
+
+#define BADFRAG(m, ...) \
+ Message(M_WARNING, \
+ "%s: fragmenting packet from source %s" \
+ " for transmission via %s: " m "\n", \
+ st->name, ssource, delivery_name, \
+ ## __VA_ARGS__);
+
+ unsigned orig_frag = ntohs(iph->frag);
+
+ if (orig_frag&IPHDR_FRAG_DONT) {
+ union icmpinfofield info =
+ { .fragneeded = { .unused = 0, .mtu = htons(mtu) } };
+ netlink_icmp_simple(st,sender,buf,
+ ICMP_TYPE_UNREACHABLE,
+ ICMP_CODE_FRAGMENTATION_REQUIRED,
+ info);
+ BUF_FREE(buf);
+ return;
+ }
+ if (mtu < hl + 8) {
+ BADFRAG("mtu %"PRId32" too small", mtu);
+ BUF_FREE(buf);