+ return ip_csum(iph,ihl*4);
+}
+#endif
+
+struct iphdr {
+#if defined (WORDS_BIGENDIAN)
+ uint8_t version:4,
+ ihl:4;
+#else
+ uint8_t ihl:4,
+ version:4;
+#endif
+ uint8_t tos;
+ uint16_t tot_len;
+ uint16_t id;
+ uint16_t frag_off;
+ uint8_t ttl;
+ uint8_t protocol;
+ uint16_t check;
+ uint32_t saddr;
+ uint32_t daddr;
+ /* The options start here. */
+};
+
+struct icmphdr {
+ struct iphdr iph;
+ uint8_t type;
+ uint8_t code;
+ uint16_t check;
+ union {
+ uint32_t unused;
+ struct {
+ uint8_t pointer;
+ uint8_t unused1;
+ uint16_t unused2;
+ } pprob;
+ uint32_t gwaddr;
+ struct {
+ uint16_t id;
+ uint16_t seq;
+ } echo;
+ } d;
+};
+
+static void netlink_packet_deliver(struct netlink *st, struct buffer_if *buf);
+
+static struct icmphdr *netlink_icmp_tmpl(struct netlink *st,
+ uint32_t dest,uint16_t len)
+{
+ struct icmphdr *h;
+
+ BUF_ALLOC(&st->icmp,"netlink_icmp_tmpl");
+ buffer_init(&st->icmp,st->max_start_pad);
+ h=buf_append(&st->icmp,sizeof(*h));
+
+ h->iph.version=4;
+ h->iph.ihl=5;
+ h->iph.tos=0;
+ h->iph.tot_len=htons(len+(h->iph.ihl*4)+8);
+ h->iph.id=0;
+ h->iph.frag_off=0;
+ h->iph.ttl=255;
+ h->iph.protocol=1;
+ h->iph.saddr=htonl(st->secnet_address);
+ h->iph.daddr=htonl(dest);
+ h->iph.check=0;
+ h->iph.check=ip_fast_csum((uint8_t *)&h->iph,h->iph.ihl);
+ h->check=0;
+ h->d.unused=0;
+
+ return h;
+}
+
+/* Fill in the ICMP checksum field correctly */
+static void netlink_icmp_csum(struct icmphdr *h)
+{
+ uint32_t len;
+
+ len=ntohs(h->iph.tot_len)-(4*h->iph.ihl);
+ h->check=0;
+ h->check=ip_csum(&h->type,len);
+}
+
+/* RFC1122:
+ * An ICMP error message MUST NOT be sent as the result of
+ * receiving:
+ *
+ * * an ICMP error message, or
+ *
+ * * a datagram destined to an IP broadcast or IP multicast
+ * address, or
+ *
+ * * a datagram sent as a link-layer broadcast, or
+ *
+ * * a non-initial fragment, or
+ *
+ * * a datagram whose source address does not define a single
+ * host -- e.g., a zero address, a loopback address, a
+ * broadcast address, a multicast address, or a Class E
+ * address.
+ */
+static bool_t netlink_icmp_may_reply(struct buffer_if *buf)
+{
+ struct iphdr *iph;
+ uint32_t source;
+
+ iph=(struct iphdr *)buf->start;
+ if (iph->protocol==1) return False; /* Overly-broad; we may reply to
+ eg. icmp echo-request */
+ /* How do we spot broadcast destination addresses? */
+ if (ntohs(iph->frag_off)&0x1fff) return False; /* Non-initial fragment */
+ source=ntohl(iph->saddr);
+ if (source==0) return False;
+ if ((source&0xff000000)==0x7f000000) return False;
+ /* How do we spot broadcast source addresses? */
+ if ((source&0xf0000000)==0xe0000000) return False; /* Multicast */
+ if ((source&0xf0000000)==0xf0000000) return False; /* Class E */
+ return True;
+}
+
+/* How much of the original IP packet do we include in its ICMP
+ response? The header plus up to 64 bits. */
+static uint16_t netlink_icmp_reply_len(struct buffer_if *buf)
+{
+ struct iphdr *iph=(struct iphdr *)buf->start;
+ uint16_t hlen,plen;
+
+ hlen=iph->ihl*4;
+ /* We include the first 8 bytes of the packet data, provided they exist */
+ hlen+=8;
+ plen=ntohs(iph->tot_len);
+ return (hlen>plen?plen:hlen);
+}
+
+static void netlink_icmp_simple(struct netlink *st, struct buffer_if *buf,
+ uint8_t type, uint8_t code)
+{
+ struct iphdr *iph=(struct iphdr *)buf->start;
+ struct icmphdr *h;
+ uint16_t len;
+
+ if (netlink_icmp_may_reply(buf)) {
+ len=netlink_icmp_reply_len(buf);
+ h=netlink_icmp_tmpl(st,ntohl(iph->saddr),len);
+ h->type=type; h->code=code;
+ memcpy(buf_append(&st->icmp,len),buf->start,len);
+ netlink_icmp_csum(h);
+ netlink_packet_deliver(st,&st->icmp);
+ BUF_ASSERT_FREE(&st->icmp);
+ }
+}
+
+/*
+ * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the
+ * checksum.
+ *
+ * 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)
+{
+ struct iphdr *iph=(struct iphdr *)buf->start;
+ uint32_t len;
+
+ if (iph->ihl < 5 || iph->version != 4) {
+ printf("ihl/version check failed\n");
+ return False;
+ }
+ if (buf->size < iph->ihl*4) {
+ printf("buffer size check failed\n");
+ return False;
+ }
+ if (ip_fast_csum((uint8_t *)iph, iph->ihl)!=0) {
+ printf("checksum failed\n");
+ return False;
+ }
+ len=ntohs(iph->tot_len);
+ /* There should be no padding */
+ if (buf->size!=len || len<(iph->ihl<<2)) {
+ printf("length check failed buf->size=%d len=%d\n",buf->size,len);
+ return False;
+ }
+
+ /* XXX check that there's no source route specified */
+ return True;
+}
+
+static void netlink_packet_deliver(struct netlink *st, struct buffer_if *buf)
+{
+ struct iphdr *iph=(struct iphdr *)buf->start;
+ uint32_t dest=ntohl(iph->daddr);