static const size_t ns_size = sizeof(struct nd_neighbor_solicit);
static const size_t opt_size = sizeof(struct nd_opt_hdr);
+static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet);
+
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
/* 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;
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;
}
}
}
}
+static void broadcast_packet_helper(node_t *source, vpn_packet_t *packet) {
+ if(decrement_ttl && source != myself)
+ if(!do_decrement_ttl(source, packet))
+ return;
+
+ broadcast_packet(source, packet);
+}
+
/* RFC 792 */
static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_t ether_size, uint8_t type, uint8_t code) {
struct in_addr ip_src;
struct in_addr ip_dst;
uint32_t oldlen;
- int sockfd;
if(ratelimit(3))
return;
/* 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;
if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
return route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_NET_ANO);
+ if(decrement_ttl && source != myself && subnet->owner != myself)
+ if(!do_decrement_ttl(source, packet))
+ return;
+
if(priorityinheritance)
packet->priority = packet->data[15];
packet->data[31] == 255 &&
packet->data[32] == 255 &&
packet->data[33] == 255)))
- broadcast_packet(source, packet);
+ broadcast_packet_helper(source, packet);
else
route_ipv4_unicast(source, packet);
}
struct ip6_hdr ip6;
struct icmp6_hdr icmp6 = {0};
uint16_t checksum;
- int sockfd;
struct {
struct in6_addr ip6_src; /* source address */
/* 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;
if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
return route_ipv6_unreachable(source, packet, ether_size, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
+ if(decrement_ttl && source != myself && subnet->owner != myself)
+ if(!do_decrement_ttl(source, packet))
+ return;
+
via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
if(via == source) {
if(subnet->owner == myself)
return; /* silently ignore */
+ if(decrement_ttl)
+ if(!do_decrement_ttl(source, packet))
+ return;
+
/* Create neighbor advertation reply */
memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */
}
if(broadcast_mode && packet->data[38] == 255)
- broadcast_packet(source, packet);
+ broadcast_packet_helper(source, packet);
else
route_ipv6_unicast(source, packet);
}
if(subnet->owner == myself)
return; /* silently ignore */
+ if(decrement_ttl)
+ if(!do_decrement_ttl(source, packet))
+ return;
+
memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */
packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
subnet = lookup_subnet_mac(NULL, &dest);
if(!subnet) {
- broadcast_packet(source, packet);
+ broadcast_packet_helper(source, packet);
return;
}
if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
return;
+ if(decrement_ttl && source != myself && subnet->owner != myself)
+ if(!do_decrement_ttl(source, packet))
+ return;
+
uint16_t type = packet->data[12] << 8 | packet->data[13];
if(priorityinheritance && type == ETH_P_IP && packet->len >= ether_size + ip_size)
if(!checklength(source, packet, ether_size))
return;
- if(decrement_ttl && source != myself)
- if(!do_decrement_ttl(source, packet))
- return;
-
switch (routing_mode) {
case RMODE_ROUTER:
{
break;
case RMODE_HUB:
- broadcast_packet(source, packet);
+ broadcast_packet_helper(source, packet);
break;
}
}