Fix invalid checksum generation.
[tinc] / src / route.c
index f046e6e..c1be6cb 100644 (file)
@@ -160,7 +160,7 @@ static void clamp_mss(const node_t *source, const node_t *via, vpn_packet_t *pac
                /* Found it */
                uint16_t oldmss = packet->data[start + 22 + i] << 8 | packet->data[start + 23 + i];
                uint16_t newmss = mtu - start - 20;
-               uint16_t csum = packet->data[start + 16] << 8 | packet->data[start + 17];
+               uint32_t csum = packet->data[start + 16] << 8 | packet->data[start + 17];
 
                if(oldmss <= newmss)
                        break;
@@ -171,11 +171,13 @@ static void clamp_mss(const node_t *source, const node_t *via, vpn_packet_t *pac
                packet->data[start + 22 + i] = newmss >> 8;
                packet->data[start + 23 + i] = newmss & 0xff;
                csum ^= 0xffff;
-               csum -= oldmss;
+               csum += oldmss ^ 0xffff;
                csum += newmss;
+               csum = (csum & 0xffff) + (csum >> 16);
+               csum += csum >> 16;
                csum ^= 0xffff;
                packet->data[start + 16] = csum >> 8;
-               packet->data[start + 17] = csum & 0xff;
+               packet->data[start + 17] = csum;
                break;
        }
 }
@@ -258,7 +260,6 @@ 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;
@@ -278,21 +279,23 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_
 
        /* 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))) {
+       if (type == ICMP_TIME_EXCEEDED && code == ICMP_EXC_TTL) {
+               int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+               if (sockfd != -1) {
+                       struct sockaddr_in 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;
+                       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);
                }
-               close(sockfd);
        }
 
        oldlen = packet->len - ether_size;
@@ -473,7 +476,6 @@ 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 */
@@ -500,21 +502,23 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_
 
        /* 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))) {
+       if (type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT) {
+               int sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
+               if (sockfd != -1) {
+                       struct sockaddr_in6 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;
+                       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);
                }
-               close(sockfd);
        }
 
        pseudo.length = packet->len - ether_size;