@@ -60,6 +60,72 @@ static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip,
ip6_output(NULL, t, 0);
}
+void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
+{
+ Slirp *slirp = m->slirp;
+ struct mbuf *t;
+ struct ip6 *ip = mtod(m, struct ip6 *);
+
+ DEBUG_CALL("icmp6_send_error");
+ DEBUG_ARGS((dfd, " type = %d, code = %d\n", type, code));
+
+ if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) ||
+ IN6_IS_ADDR_UNSPECIFIED(&ip->ip_src)) {
+ /* TODO icmp error? */
+ return;
+ }
+
+ t = m_get(slirp);
+
+ /* IPv6 packet */
+ struct ip6 *rip = mtod(t, struct ip6 *);
+ rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
+ rip->ip_dst = ip->ip_src;
+#if !defined(_WIN32) || (_WIN32_WINNT >= 0x0600)
+ char addrstr[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
+ DEBUG_ARG("target = %s", addrstr);
+#endif
+
+ rip->ip_nh = IPPROTO_ICMPV6;
+ const int error_data_len = min(m->m_len,
+ IF_MTU - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN));
+ rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len);
+ t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl);
+
+ /* ICMPv6 packet */
+ t->m_data += sizeof(struct ip6);
+ struct icmp6 *ricmp = mtod(t, struct icmp6 *);
+ ricmp->icmp6_type = type;
+ ricmp->icmp6_code = code;
+ ricmp->icmp6_cksum = 0;
+
+ switch (type) {
+ case ICMP6_UNREACH:
+ case ICMP6_TIMXCEED:
+ ricmp->icmp6_err.unused = 0;
+ break;
+ case ICMP6_TOOBIG:
+ ricmp->icmp6_err.mtu = htonl(IF_MTU);
+ break;
+ case ICMP6_PARAMPROB:
+ /* TODO: Handle this case */
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ t->m_data += ICMP6_ERROR_MINLEN;
+ memcpy(t->m_data, m->m_data, error_data_len);
+
+ /* Checksum */
+ t->m_data -= ICMP6_ERROR_MINLEN;
+ t->m_data -= sizeof(struct ip6);
+ ricmp->icmp6_cksum = ip6_cksum(t);
+
+ ip6_output(NULL, t, 0);
+}
+
/*
* Send NDP Router Advertisement
*/
@@ -19,6 +19,12 @@ struct icmp6_echo { /* Echo Messages */
uint16_t seq_num;
};
+union icmp6_error_body {
+ uint32_t unused;
+ uint32_t pointer;
+ uint32_t mtu;
+};
+
/*
* NDP Messages
*/
@@ -82,6 +88,7 @@ struct icmp6 {
uint8_t icmp6_code; /* type sub code */
uint16_t icmp6_cksum; /* ones complement cksum of struct */
union {
+ union icmp6_error_body error_body;
struct icmp6_echo echo;
struct ndp_rs ndp_rs;
struct ndp_ra ndp_ra;
@@ -89,6 +96,7 @@ struct icmp6 {
struct ndp_na ndp_na;
struct ndp_redirect ndp_redirect;
} icmp6_body;
+#define icmp6_err icmp6_body.error_body
#define icmp6_echo icmp6_body.echo
#define icmp6_nrs icmp6_body.ndp_rs
#define icmp6_nra icmp6_body.ndp_ra
@@ -98,6 +106,7 @@ struct icmp6 {
} QEMU_PACKED;
#define ICMP6_MINLEN 4
+#define ICMP6_ERROR_MINLEN 8
#define ICMP6_ECHO_MINLEN 8
#define ICMP6_NDP_RS_MINLEN 8
#define ICMP6_NDP_RA_MINLEN 16
@@ -197,6 +206,7 @@ struct ndpopt {
void icmp6_init(Slirp *slirp);
void icmp6_cleanup(Slirp *slirp);
void icmp6_input(struct mbuf *);
+void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code);
void ndp_send_ra(Slirp *slirp);
void ndp_send_ns(Slirp *slirp, struct in6_addr addr);
@@ -39,9 +39,14 @@ void ip6_input(struct mbuf *m)
goto bad;
}
+ if (ntohs(ip6->ip_pl) > IF_MTU) {
+ icmp6_send_error(m, ICMP6_TOOBIG, 0);
+ goto bad;
+ }
+
/* check ip_ttl for a correct ICMP reply */
if (ip6->ip_hl == 0) {
- /*icmp_send_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");*/
+ icmp6_send_error(m, ICMP6_TIMXCEED, ICMP6_TIMXCEED_INTRANS);
goto bad;
}
@@ -50,10 +55,10 @@ void ip6_input(struct mbuf *m)
*/
switch (ip6->ip_nh) {
case IPPROTO_TCP:
- /*tcp_input(m, hlen, (struct socket *)NULL);*/
+ icmp6_send_error(m, ICMP6_UNREACH, ICMP6_UNREACH_NO_ROUTE);
break;
case IPPROTO_UDP:
- /*udp_input(m, hlen);*/
+ icmp6_send_error(m, ICMP6_UNREACH, ICMP6_UNREACH_NO_ROUTE);
break;
case IPPROTO_ICMPV6:
icmp6_input(m);