* ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp
*/
+#include "qemu/osdep.h"
#include "slirp.h"
#include "ip_icmp.h"
/* Be nice and tell them it's just a pseudo-ping packet */
static const char icmp_ping_msg[] = "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
-/* list of actions for icmp_error() on RX of an icmp message */
+/* list of actions for icmp_send_error() on RX of an icmp message */
static const int icmp_flush[19] = {
/* ECHO REPLY (0) */ 0,
1,
/* ADDR MASK REPLY (18) */ 0
};
+void icmp_init(Slirp *slirp)
+{
+ slirp->icmp.so_next = slirp->icmp.so_prev = &slirp->icmp;
+ slirp->icmp_last_so = &slirp->icmp;
+}
+
+void icmp_cleanup(Slirp *slirp)
+{
+ while (slirp->icmp.so_next != &slirp->icmp) {
+ icmp_detach(slirp->icmp.so_next);
+ }
+}
+
+static int icmp_send(struct socket *so, struct mbuf *m, int hlen)
+{
+ struct ip *ip = mtod(m, struct ip *);
+ struct sockaddr_in addr;
+
+ so->s = qemu_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+ if (so->s == -1) {
+ return -1;
+ }
+
+ so->so_m = m;
+ so->so_faddr = ip->ip_dst;
+ so->so_laddr = ip->ip_src;
+ so->so_iptos = ip->ip_tos;
+ so->so_type = IPPROTO_ICMP;
+ so->so_state = SS_ISFCONNECTED;
+ so->so_expire = curtime + SO_EXPIRE;
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr = so->so_faddr;
+
+ insque(so, &so->slirp->icmp);
+
+ if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0,
+ (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ DEBUG_MISC((dfd, "icmp_input icmp sendto tx errno = %d-%s\n",
+ errno, strerror(errno)));
+ icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
+ icmp_detach(so);
+ }
+
+ return 0;
+}
+
+void icmp_detach(struct socket *so)
+{
+ closesocket(so->s);
+ sofree(so);
+}
+
/*
* Process a received ICMP message.
*/
Slirp *slirp = m->slirp;
DEBUG_CALL("icmp_input");
- DEBUG_ARG("m = %lx", (long )m);
+ DEBUG_ARG("m = %p", m);
DEBUG_ARG("m_len = %d", m->m_len);
/*
*/
if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */
freeit:
- m_freem(m);
+ m_free(m);
goto end_error;
}
DEBUG_ARG("icmp_type = %d", icp->icmp_type);
switch (icp->icmp_type) {
case ICMP_ECHO:
- icp->icmp_type = ICMP_ECHOREPLY;
ip->ip_len += hlen; /* since ip_input subtracts this */
if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) {
icmp_reflect(m);
+ } else if (slirp->restricted) {
+ goto freeit;
} else {
struct socket *so;
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
if ((so = socreate(slirp)) == NULL) goto freeit;
- if(udp_attach(so) == -1) {
+ if (icmp_send(so, m, hlen) == 0) {
+ return;
+ }
+ if (udp_attach(so, AF_INET) == -1) {
DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n",
errno,strerror(errno)));
sofree(so);
goto end_error;
}
so->so_m = m;
+ so->so_ffamily = AF_INET;
so->so_faddr = ip->ip_dst;
so->so_fport = htons(7);
+ so->so_lfamily = AF_INET;
so->so_laddr = ip->ip_src;
so->so_lport = htons(9);
so->so_iptos = ip->ip_tos;
so->so_state = SS_ISFCONNECTED;
/* Send the packet */
- addr.sin_family = AF_INET;
- if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
- slirp->vnetwork_addr.s_addr) {
- /* It's an alias */
- if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
- if (get_dns_addr(&addr.sin_addr) < 0)
- addr.sin_addr = loopback_addr;
- } else {
- addr.sin_addr = loopback_addr;
- }
- } else {
- addr.sin_addr = so->so_faddr;
- }
- addr.sin_port = so->so_fport;
+ addr = so->fhost.ss;
+ sotranslate_out(so, &addr);
+
if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0,
- (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ (struct sockaddr *)&addr, sockaddr_size(&addr)) == -1) {
DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n",
errno,strerror(errno)));
- icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
+ icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
udp_detach(so);
}
} /* if ip->ip_dst.s_addr == alias_addr.s_addr */
case ICMP_TSTAMP:
case ICMP_MASKREQ:
case ICMP_REDIRECT:
- m_freem(m);
+ m_free(m);
break;
default:
- m_freem(m);
+ m_free(m);
} /* swith */
end_error:
#define ICMP_MAXDATALEN (IP_MSS-28)
void
-icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
+icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
const char *message)
{
unsigned hlen, shlen, s_ip_len;
register struct icmp *icp;
register struct mbuf *m;
- DEBUG_CALL("icmp_error");
- DEBUG_ARG("msrc = %lx", (long )msrc);
+ DEBUG_CALL("icmp_send_error");
+ DEBUG_ARG("msrc = %p", msrc);
DEBUG_ARG("msrc_len = %d", msrc->m_len);
if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error;
#endif
if(ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */
+ /* Do not reply to source-only IPs */
+ if ((ip->ip_src.s_addr & htonl(~(0xf << 28))) == 0) {
+ goto end_error;
+ }
+
shlen=ip->ip_hl << 2;
s_ip_len=ip->ip_len;
if(ip->ip_p == IPPROTO_ICMP) {
ip->ip_ttl = MAXTTL;
ip->ip_p = IPPROTO_ICMP;
- ip->ip_dst = ip->ip_src; /* ip adresses */
+ ip->ip_dst = ip->ip_src; /* ip addresses */
ip->ip_src = m->slirp->vhost_addr;
(void ) ip_output((struct socket *)NULL, m);
m->m_len -= hlen;
icp = mtod(m, struct icmp *);
+ icp->icmp_type = ICMP_ECHOREPLY;
icp->icmp_cksum = 0;
icp->icmp_cksum = cksum(m, ip->ip_len - hlen);
(void ) ip_output((struct socket *)NULL, m);
}
+
+void icmp_receive(struct socket *so)
+{
+ struct mbuf *m = so->so_m;
+ struct ip *ip = mtod(m, struct ip *);
+ int hlen = ip->ip_hl << 2;
+ u_char error_code;
+ struct icmp *icp;
+ int id, len;
+
+ m->m_data += hlen;
+ m->m_len -= hlen;
+ icp = mtod(m, struct icmp *);
+
+ id = icp->icmp_id;
+ len = qemu_recv(so->s, icp, m->m_len, 0);
+ icp->icmp_id = id;
+
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ if (len == -1 || len == 0) {
+ if (errno == ENETUNREACH) {
+ error_code = ICMP_UNREACH_NET;
+ } else {
+ error_code = ICMP_UNREACH_HOST;
+ }
+ DEBUG_MISC((dfd, " udp icmp rx errno = %d-%s\n", errno,
+ strerror(errno)));
+ icmp_send_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno));
+ } else {
+ icmp_reflect(so->so_m);
+ so->so_m = NULL; /* Don't m_free() it again! */
+ }
+ icmp_detach(so);
+}