slirp: Forward ICMP echo requests via unprivileged sockets
Linux 3.0 gained support for unprivileged ICMP ping sockets. Use this
feature to forward guest pings to the outer world. The host admin has to
set the ping_group_range in order to grant access to those sockets. To
allow ping for the users group (GID 100):
echo 100 100 > /proc/sys/net/ipv4/ping_group_range
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c
index 4f10826..14a5312 100644
--- a/slirp/ip_icmp.c
+++ b/slirp/ip_icmp.c
@@ -60,6 +60,52 @@
/* 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;
+}
+
+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_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.
*/
@@ -97,7 +143,6 @@
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);
@@ -107,6 +152,9 @@
struct socket *so;
struct sockaddr_in addr;
if ((so = socreate(slirp)) == NULL) goto freeit;
+ if (icmp_send(so, m, hlen) == 0) {
+ return;
+ }
if(udp_attach(so) == -1) {
DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n",
errno,strerror(errno)));
@@ -321,6 +369,7 @@
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);
@@ -351,3 +400,39 @@
(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 = 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_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);
+}