This adds a new option, BroadcastSubnet, that allows the user to
declare broadcast subnets, i.e. subnets which are considered broadcast
addresses by the tinc routing layer. Previously only the global IPv4
and IPv6 broadcast addresses were supported by virtue of being
hardcoded.
This is useful when using tinc in router mode with Ethernet virtual
devices, as it can be used to provide broadcast support for a local
broadcast address (e.g. 10.42.255.255) instead of just the global
address (255.255.255.255).
This is implemented by removing hardcoded broadcast addresses and
introducing "broadcast subnets", which are subnets with a NULL owner.
By default, behavior is unchanged; this is accomplished by adding
the global broadcast addresses for Ethernet, IPv4 and IPv6 at start
time.
Broadcast packets received from other nodes are never forwarded.
If the IndirectData option is also set, broadcast packets will only be sent to nodes which we have a meta connection to.
.El
Broadcast packets received from other nodes are never forwarded.
If the IndirectData option is also set, broadcast packets will only be sent to nodes which we have a meta connection to.
.El
+.It Va BroadcastSubnet Li = Ar address Ns Op Li / Ns Ar prefixlength
+Declares a broadcast subnet. Any packet with a destination address falling into such a subnet will be routed as a broadcast (provided all nodes have it declared).
+This is most useful to declare subnet broadcast addresses (e.g. 10.42.255.255), otherwise
+.Nm tinc
+won't know what to do with them.
+.Pp
+Note that global broadcast addresses (MAC ff:ff:ff:ff:ff:ff, IPv4 255.255.255.255), as well as IPv6 multicast space (ff00::/8) are always considered broadcast addresses and don't need to be declared.
.It Va ConnectTo Li = Ar name
Specifies which other tinc daemon to connect to on startup.
Multiple
.It Va ConnectTo Li = Ar name
Specifies which other tinc daemon to connect to on startup.
Multiple
if(strictsubnets) {
for splay_each(subnet_t, subnet, subnet_tree)
if(strictsubnets) {
for splay_each(subnet_t, subnet, subnet_tree)
+ if (subnet->owner)
+ subnet->expires = 1;
load_all_subnets();
for splay_each(subnet_t, subnet, subnet_tree) {
load_all_subnets();
for splay_each(subnet_t, subnet, subnet_tree) {
+ if (!subnet->owner)
+ continue;
if(subnet->expires == 1) {
send_del_subnet(everyone, subnet);
if(subnet->owner->status.reachable)
if(subnet->expires == 1) {
send_del_subnet(everyone, subnet);
if(subnet->owner->status.reachable)
+ const char* const DEFAULT_BROADCAST_SUBNETS[] = { "ff:ff:ff:ff:ff:ff", "255.255.255.255", "ff00::/8" };
+ for (size_t i = 0; i < sizeof(DEFAULT_BROADCAST_SUBNETS) / sizeof(*DEFAULT_BROADCAST_SUBNETS); i++) {
+ subnet_t *s = new_subnet();
+ if (!str2net(s, DEFAULT_BROADCAST_SUBNETS[i]))
+ abort();
+ subnet_add(NULL, s);
+ }
+ for (config_t* cfg = lookup_config(config_tree, "BroadcastSubnet"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
+ subnet_t *s;
+ if (!get_config_subnet(cfg, &s))
+ continue;
+ subnet_add(NULL, s);
+ }
+
#if !defined(SOL_IP) || !defined(IP_TOS)
if(priorityinheritance)
logger(DEBUG_ALWAYS, LOG_WARNING, "%s not supported on this platform", "PriorityInheritance");
#if !defined(SOL_IP) || !defined(IP_TOS)
if(priorityinheritance)
logger(DEBUG_ALWAYS, LOG_WARNING, "%s not supported on this platform", "PriorityInheritance");
-static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
+static void route_ipv4(node_t *source, vpn_packet_t *packet) {
+ if(!checklength(source, packet, ether_size + ip_size))
+ return;
+
subnet_t *subnet;
node_t *via;
ipv4_t dest;
subnet_t *subnet;
node_t *via;
ipv4_t dest;
+ if (!subnet->owner) {
+ broadcast_packet(source, packet);
+ return;
+ }
+
if(subnet->owner == source) {
logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
return;
if(subnet->owner == source) {
logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
return;
send_packet(subnet->owner, packet);
}
send_packet(subnet->owner, packet);
}
-static void route_ipv4(node_t *source, vpn_packet_t *packet) {
- if(!checklength(source, packet, ether_size + ip_size))
- return;
-
- if(broadcast_mode && (((packet->data[30] & 0xf0) == 0xe0) || (
- packet->data[30] == 255 &&
- packet->data[31] == 255 &&
- packet->data[32] == 255 &&
- packet->data[33] == 255)))
- broadcast_packet(source, packet);
- else
- route_ipv4_unicast(source, packet);
-}
-
/* RFC 2463 */
static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_t ether_size, uint8_t type, uint8_t code) {
/* RFC 2463 */
static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_t ether_size, uint8_t type, uint8_t code) {
send_packet(source, packet);
}
send_packet(source, packet);
}
-static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
+static void route_neighborsol(node_t *source, vpn_packet_t *packet);
+
+static void route_ipv6(node_t *source, vpn_packet_t *packet) {
+ if(!checklength(source, packet, ether_size + ip6_size))
+ return;
+
+ if(packet->data[20] == IPPROTO_ICMPV6 && checklength(source, packet, ether_size + ip6_size + icmp6_size) && packet->data[54] == ND_NEIGHBOR_SOLICIT) {
+ route_neighborsol(source, packet);
+ return;
+ }
+
subnet_t *subnet;
node_t *via;
ipv6_t dest;
subnet_t *subnet;
node_t *via;
ipv6_t dest;
+ if (!subnet->owner) {
+ broadcast_packet(source, packet);
+ return;
+ }
+
if(subnet->owner == source) {
logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
return;
if(subnet->owner == source) {
logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
return;
send_packet(source, packet);
}
send_packet(source, packet);
}
-static void route_ipv6(node_t *source, vpn_packet_t *packet) {
- if(!checklength(source, packet, ether_size + ip6_size))
- return;
-
- if(packet->data[20] == IPPROTO_ICMPV6 && checklength(source, packet, ether_size + ip6_size + icmp6_size) && packet->data[54] == ND_NEIGHBOR_SOLICIT) {
- route_neighborsol(source, packet);
- return;
- }
-
- if(broadcast_mode && packet->data[38] == 255)
- broadcast_packet(source, packet);
- else
- route_ipv6_unicast(source, packet);
-}
-
/* RFC 826 */
static void route_arp(node_t *source, vpn_packet_t *packet) {
/* RFC 826 */
static void route_arp(node_t *source, vpn_packet_t *packet) {
memcpy(&dest, &packet->data[0], sizeof dest);
subnet = lookup_subnet_mac(NULL, &dest);
memcpy(&dest, &packet->data[0], sizeof dest);
subnet = lookup_subnet_mac(NULL, &dest);
+ if(!subnet || !subnet->owner) {
broadcast_packet(source, packet);
return;
}
broadcast_packet(source, packet);
return;
}
subnet->owner = n;
splay_insert(subnet_tree, subnet);
subnet->owner = n;
splay_insert(subnet_tree, subnet);
- splay_insert(n->subnet_tree, subnet);
+ if (n)
+ splay_insert(n->subnet_tree, subnet);
subnet_cache_flush();
}
void subnet_del(node_t *n, subnet_t *subnet) {
subnet_cache_flush();
}
void subnet_del(node_t *n, subnet_t *subnet) {
- splay_delete(n->subnet_tree, subnet);
+ if (n)
+ splay_delete(n->subnet_tree, subnet);
splay_delete(subnet_tree, subnet);
subnet_cache_flush();
splay_delete(subnet_tree, subnet);
subnet_cache_flush();
if(!memcmp(address, &p->net.mac.address, sizeof *address)) {
r = p;
if(!memcmp(address, &p->net.mac.address, sizeof *address)) {
r = p;
- if(p->owner->status.reachable)
+ if(!p->owner || p->owner->status.reachable)
if(!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength)) {
r = p;
if(!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength)) {
r = p;
- if(p->owner->status.reachable)
+ if(!p->owner || p->owner->status.reachable)
if(!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength)) {
r = p;
if(!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength)) {
r = p;
- if(p->owner->status.reachable)
+ if(!p->owner || p->owner->status.reachable)