diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ip6_icmp.c | 10 | ||||
-rw-r--r-- | src/ip6_icmp.h | 1 | ||||
-rw-r--r-- | src/ip_icmp.c | 12 | ||||
-rw-r--r-- | src/ip_icmp.h | 2 | ||||
-rw-r--r-- | src/socket.c | 60 | ||||
-rw-r--r-- | src/udp.c | 17 |
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); @@ -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); } |