Fix source IP address for ICMP unreachable packets generated by tinc.
[tinc] / src / route.c
index a1254d8..f046e6e 100644 (file)
@@ -258,6 +258,7 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_
        struct in_addr ip_src;
        struct in_addr ip_dst;
        uint32_t oldlen;
+       int sockfd;
 
        if(ratelimit(3))
                return;
@@ -275,6 +276,25 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_
        ip_src = ip.ip_src;
        ip_dst = ip.ip_dst;
 
+       /* Try to reply with an IP address assigned to the local machine */
+
+       sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sockfd != -1) {
+               struct sockaddr_in addr;
+               memset(&addr, 0, sizeof(addr));
+               addr.sin_family = AF_INET;
+               addr.sin_addr = ip.ip_src;
+               if (!connect(sockfd, (const struct sockaddr*) &addr, sizeof(addr))) {
+                       memset(&addr, 0, sizeof(addr));
+                       addr.sin_family = AF_INET;
+                       socklen_t addrlen = sizeof(addr);
+                       if (!getsockname(sockfd, (struct sockaddr*) &addr, &addrlen) && addrlen <= sizeof(addr)) {
+                               ip_dst = addr.sin_addr;
+                       }
+               }
+               close(sockfd);
+       }
+
        oldlen = packet->len - ether_size;
 
        if(type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
@@ -453,6 +473,7 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_
        struct ip6_hdr ip6;
        struct icmp6_hdr icmp6 = {0};
        uint16_t checksum;      
+       int sockfd;
 
        struct {
                struct in6_addr ip6_src;        /* source address */
@@ -477,6 +498,25 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_
        pseudo.ip6_src = ip6.ip6_dst;
        pseudo.ip6_dst = ip6.ip6_src;
 
+       /* Try to reply with an IP address assigned to the local machine */
+
+       sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
+       if (sockfd != -1) {
+               struct sockaddr_in6 addr;
+               memset(&addr, 0, sizeof(addr));
+               addr.sin6_family = AF_INET6;
+               addr.sin6_addr = ip6.ip6_src;
+               if (!connect(sockfd, (const struct sockaddr*) &addr, sizeof(addr))) {
+                       memset(&addr, 0, sizeof(addr));
+                       addr.sin6_family = AF_INET6;
+                       socklen_t addrlen = sizeof(addr);
+                       if (!getsockname(sockfd, (struct sockaddr*) &addr, &addrlen) && addrlen <= sizeof(addr)) {
+                               pseudo.ip6_src = addr.sin6_addr;
+                       }
+               }
+               close(sockfd);
+       }
+
        pseudo.length = packet->len - ether_size;
 
        if(type == ICMP6_PACKET_TOO_BIG)