+ iph=(struct iphdr *)buf->start;
+ icmph=(struct icmphdr *)buf->start;
+ if (iph->protocol==1) {
+ switch(icmph->type) {
+ case 3: /* Destination unreachable */
+ case 11: /* Time Exceeded */
+ case 12: /* Parameter Problem */
+ return False;
+ }
+ }
+ /* 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. */
+
+/* XXX TODO RFC1812:
+4.3.2.3 Original Message Header
+
+ Historically, every ICMP error message has included the Internet
+ header and at least the first 8 data bytes of the datagram that
+ triggered the error. This is no longer adequate, due to the use of
+ IP-in-IP tunneling and other technologies. Therefore, the ICMP
+ datagram SHOULD contain as much of the original datagram as possible
+ without the length of the ICMP datagram exceeding 576 bytes. The
+ returned IP header (and user data) MUST be identical to that which
+ was received, except that the router is not required to undo any
+ modifications to the IP header that are normally performed in
+ forwarding that were performed before the error was detected (e.g.,
+ decrementing the TTL, or updating options). Note that the
+ requirements of Section [4.3.3.5] supersede this requirement in some
+ cases (i.e., for a Parameter Problem message, if the problem is in a
+ modified field, the router must undo the modification). See Section
+ [4.3.3.5]).
+ */
+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);
+}
+
+/* client indicates where the packet we're constructing a response to
+ comes from. NULL indicates the host. */
+static void netlink_icmp_simple(struct netlink *st, struct buffer_if *buf,
+ struct netlink_client *client,
+ 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,NULL,&st->icmp);
+ BUF_ASSERT_FREE(&st->icmp);
+ }
+}
+
+/*
+ * 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)
+{
+ struct iphdr *iph=(struct iphdr *)buf->start;
+ uint32_t len;
+
+ if (iph->ihl < 5 || iph->version != 4) return False;
+ if (buf->size < iph->ihl*4) return False;
+ if (ip_fast_csum((uint8_t *)iph, iph->ihl)!=0) return False;
+ len=ntohs(iph->tot_len);
+ /* There should be no padding */
+ if (buf->size!=len || len<(iph->ihl<<2)) return False;
+ /* XXX check that there's no source route specified */
+ return True;
+}
+
+/* Deliver a packet. "client" is the _origin_ of the packet, not its
+ destination, and is NULL for packets from the host and packets
+ generated internally in secnet. */
+static void netlink_packet_deliver(struct netlink *st,
+ struct netlink_client *client,
+ struct buffer_if *buf)
+{
+ struct iphdr *iph=(struct iphdr *)buf->start;
+ uint32_t dest=ntohl(iph->daddr);
+ uint32_t source=ntohl(iph->saddr);
+ uint32_t best_quality;
+ bool_t allow_route=False;
+ bool_t found_allowed=False;
+ int best_match;
+ int i;
+
+ BUF_ASSERT_USED(buf);
+
+ if (dest==st->secnet_address) {
+ Message(M_ERR,"%s: trying to deliver a packet to myself!\n",st->name);
+ BUF_FREE(buf);