aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ip6_icmp.c10
-rw-r--r--src/ip6_icmp.h1
-rw-r--r--src/ip_icmp.c12
-rw-r--r--src/ip_icmp.h2
-rw-r--r--src/socket.c60
-rw-r--r--src/udp.c17
6 files changed, 96 insertions, 6 deletions
diff --git a/src/ip6_icmp.c b/src/ip6_icmp.c
index d9c872b..119c8be 100644
--- a/src/ip6_icmp.c
+++ b/src/ip6_icmp.c
@@ -69,7 +69,7 @@ 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)
+void icmp6_forward_error(struct mbuf *m, uint8_t type, uint8_t code, struct in6_addr *src)
{
Slirp *slirp = m->slirp;
struct mbuf *t;
@@ -88,7 +88,7 @@ void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
/* IPv6 packet */
struct ip6 *rip = mtod(t, struct ip6 *);
- rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR;
+ rip->ip_src = *src;
rip->ip_dst = ip->ip_src;
inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN);
DEBUG_ARG("target = %s", addrstr);
@@ -131,6 +131,12 @@ void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
ip6_output(NULL, t, 0);
}
+void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code)
+{
+ struct in6_addr src = LINKLOCAL_ADDR;
+ icmp6_forward_error(m, type, code, &src);
+}
+
/*
* Send NDP Router Advertisement
*/
diff --git a/src/ip6_icmp.h b/src/ip6_icmp.h
index c37e60f..9070999 100644
--- a/src/ip6_icmp.h
+++ b/src/ip6_icmp.h
@@ -212,6 +212,7 @@ struct ndpopt {
void icmp6_init(Slirp *slirp);
void icmp6_cleanup(Slirp *slirp);
void icmp6_input(struct mbuf *);
+void icmp6_forward_error(struct mbuf *m, uint8_t type, uint8_t code, struct in6_addr *src);
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);
diff --git a/src/ip_icmp.c b/src/ip_icmp.c
index 45694cd..f4d686b 100644
--- a/src/ip_icmp.c
+++ b/src/ip_icmp.c
@@ -271,8 +271,8 @@ end_error:
*/
#define ICMP_MAXDATALEN (IP_MSS - 28)
-void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
- const char *message)
+void icmp_forward_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
+ const char *message, struct in_addr *src)
{
unsigned hlen, shlen, s_ip_len;
register struct ip *ip;
@@ -387,7 +387,7 @@ void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
ip->ip_ttl = MAXTTL;
ip->ip_p = IPPROTO_ICMP;
ip->ip_dst = ip->ip_src; /* ip addresses */
- ip->ip_src = m->slirp->vhost_addr;
+ ip->ip_src = *src;
(void)ip_output((struct socket *)NULL, m);
@@ -396,6 +396,12 @@ end_error:
}
#undef ICMP_MAXDATALEN
+void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
+ const char *message)
+{
+ icmp_forward_error(msrc, type, code, minsize, message, &msrc->slirp->vhost_addr);
+}
+
/*
* Reflect the ip packet back to the source
*/
diff --git a/src/ip_icmp.h b/src/ip_icmp.h
index 84707db..569a083 100644
--- a/src/ip_icmp.h
+++ b/src/ip_icmp.h
@@ -157,6 +157,8 @@ struct icmp {
void icmp_init(Slirp *slirp);
void icmp_cleanup(Slirp *slirp);
void icmp_input(struct mbuf *, int);
+void icmp_forward_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
+ const char *message, struct in_addr *src);
void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize,
const char *message);
void icmp_reflect(struct mbuf *);
diff --git a/src/socket.c b/src/socket.c
index 3fbb873..0d9b8e3 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -8,6 +8,9 @@
#ifdef __sun__
#include <sys/filio.h>
#endif
+#ifdef __linux__
+#include <linux/errqueue.h>
+#endif
static void sofcantrcvmore(struct socket *so);
static void sofcantsendmore(struct socket *so);
@@ -494,12 +497,67 @@ void sorecvfrom(struct socket *so)
struct sockaddr_storage addr;
struct sockaddr_storage saddr, daddr;
socklen_t addrlen = sizeof(struct sockaddr_storage);
+ char buff[256];
+
+#ifdef __linux__
+ ssize_t size;
+ struct msghdr msg;
+ struct iovec iov;
+ char control[1024];
+
+ /* First look for errors */
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = &saddr;
+ msg.msg_namelen = sizeof(saddr);
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+ iov.iov_base = buff;
+ iov.iov_len = sizeof(buff);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ size = recvmsg(so->s, &msg, MSG_ERRQUEUE);
+ if (size >= 0) {
+ struct cmsghdr *cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_RECVERR) {
+ struct sock_extended_err *ee =
+ (struct sock_extended_err *) CMSG_DATA(cmsg);
+
+ if (ee->ee_origin == SO_EE_ORIGIN_ICMP) {
+ /* Got an ICMP error, forward it */
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *) SO_EE_OFFENDER(ee);
+ icmp_forward_error(so->so_m, ee->ee_type, ee->ee_code,
+ 0, NULL, &sin->sin_addr);
+ }
+ }
+ else if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == IPV6_RECVERR) {
+ struct sock_extended_err *ee =
+ (struct sock_extended_err *) CMSG_DATA(cmsg);
+
+ if (ee->ee_origin == SO_EE_ORIGIN_ICMP6) {
+ /* Got an ICMPv6 error, forward it */
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *) SO_EE_OFFENDER(ee);
+ icmp6_forward_error(so->so_m, ee->ee_type, ee->ee_code,
+ &sin6->sin6_addr);
+ }
+ }
+ }
+ return;
+ }
+#endif
DEBUG_CALL("sorecvfrom");
DEBUG_ARG("so = %p", so);
if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */
- char buff[256];
int len;
len = recvfrom(so->s, buff, 256, 0, (struct sockaddr *)&addr, &addrlen);
diff --git a/src/udp.c b/src/udp.c
index b5b4210..050cee4 100644
--- a/src/udp.c
+++ b/src/udp.c
@@ -303,6 +303,23 @@ int udp_attach(struct socket *so, unsigned short af)
so->s = -1;
return -1;
}
+
+#ifdef __linux__
+ {
+ int opt = 1;
+ switch (af) {
+ case AF_INET:
+ setsockopt(so->s, IPPROTO_IP, IP_RECVERR, &opt, sizeof(opt));
+ break;
+ case AF_INET6:
+ setsockopt(so->s, IPPROTO_IPV6, IPV6_RECVERR, &opt, sizeof(opt));
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+#endif
+
so->so_expire = curtime + SO_EXPIRE;
insque(so, &so->slirp->udb);
}