diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in index 560ae479..b49e3de6 100644 --- a/doc/tinc.conf.5.in +++ b/doc/tinc.conf.5.in @@ -159,8 +159,25 @@ It is possible to bind only to a single interface with this variable. .Pp This option may not work on all platforms. -.It Va Broadcast Li = yes | no Po yes Pc Bq experimental -When disabled, tinc will drop all broadcast and multicast packets, in both router and switch mode. +.It Va Broadcast Li = no | mst | direct Po mst Pc Bq experimental +This option selects the way broadcast packets are sent to other daemons. +NOTE: all nodes in a VPN must use the same +.Va Broadcast +mode, otherwise routing loops can form. + +.Bl -tag -width indent +.It no +Broadcast packets are never sent to other nodes. + +.It mst +Broadcast packets are sent and forwarded via the VPN's Minimum Spanning Tree. +This ensures broadcast packets reach all nodes. + +.It direct +Broadcast packets are sent directly to all nodes that can be reached directly. +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 ConnectTo Li = Ar name Specifies which other tinc daemon to connect to on startup. diff --git a/doc/tinc.texi b/doc/tinc.texi index 84e3495c..d7776826 100644 --- a/doc/tinc.texi +++ b/doc/tinc.texi @@ -778,8 +778,23 @@ variable. This option may not work on all platforms. @cindex Broadcast -@item Broadcast = (yes) [experimental] -When disabled, tinc will drop all broadcast and multicast packets, in both router and switch mode. +@item Broadcast = (mst) [experimental] +This option selects the way broadcast packets are sent to other daemons. +@emph{NOTE: all nodes in a VPN must use the same Broadcast mode, otherwise routing loops can form.} + +@table @asis +@item no +Broadcast packets are never sent to other nodes. + +@item mst +Broadcast packets are sent and forwarded via the VPN's Minimum Spanning Tree. +This ensures broadcast packets reach all nodes. + +@item direct +Broadcast packets are sent directly to all nodes that can be reached directly. +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. +@end table @cindex ConnectTo @item ConnectTo = <@var{name}> diff --git a/src/net_packet.c b/src/net_packet.c index b11949a2..43c8ce20 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -584,24 +584,50 @@ void send_packet(const node_t *n, vpn_packet_t *packet) { void broadcast_packet(const node_t *from, vpn_packet_t *packet) { avl_node_t *node; connection_t *c; + node_t *n; + + // Always give ourself a copy of the packet. + if(from != myself) + send_packet(myself, packet); + + // In TunnelServer mode, do not forward broadcast packets. + // The MST might not be valid and create loops. + if(tunnelserver || broadcast_mode == BMODE_NONE) + return; ifdebug(TRAFFIC) logger(LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)", packet->len, from->name, from->hostname); - if(from != myself) { - send_packet(myself, packet); + switch(broadcast_mode) { + // In MST mode, broadcast packets travel via the Minimum Spanning Tree. + // This guarantees all nodes receive the broadcast packet, and + // usually distributes the sending of broadcast packets over all nodes. + case BMODE_MST: + for(node = connection_tree->head; node; node = node->next) { + c = node->data; - // In TunnelServer mode, do not forward broadcast packets. - // The MST might not be valid and create loops. - if(tunnelserver) - return; - } + if(c->status.active && c->status.mst && c != from->nexthop->connection) + send_packet(c->node, packet); + } + break; - for(node = connection_tree->head; node; node = node->next) { - c = node->data; + // In direct mode, we send copies to each node we know of. + // However, this only reaches nodes that can be reached in a single hop. + // We don't have enough information to forward broadcast packets in this case. + case BMODE_DIRECT: + if(from != myself) + break; - if(c->status.active && c->status.mst && c != from->nexthop->connection) - send_packet(c->node, packet); + for(node = node_udp_tree->head; node; node = node->next) { + n = node->data; + + if(n->status.reachable && ((n->via == myself && n->nexthop == n) || n->via == n)) + send_packet(c->node, packet); + } + break; + + default: + break; } } diff --git a/src/net_setup.c b/src/net_setup.c index a179228b..b8e17da9 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -429,7 +429,19 @@ static bool setup_myself(void) { get_config_bool(lookup_config(config_tree, "PriorityInheritance"), &priorityinheritance); get_config_bool(lookup_config(config_tree, "DecrementTTL"), &decrement_ttl); - get_config_bool(lookup_config(config_tree, "Broadcast"), &broadcast); + if(get_config_string(lookup_config(config_tree, "Broadcast"), &mode)) { + if(!strcasecmp(mode, "no")) + broadcast_mode = BMODE_NONE; + else if(!strcasecmp(mode, "yes") || !strcasecmp(mode, "mst")) + broadcast_mode = BMODE_MST; + else if(!strcasecmp(mode, "direct")) + broadcast_mode = BMODE_DIRECT; + else { + logger(LOG_ERR, "Invalid broadcast mode!"); + return false; + } + free(mode); + } #if !defined(SOL_IP) || !defined(IP_TOS) if(priorityinheritance) diff --git a/src/route.c b/src/route.c index 6eadb888..74ad9a34 100644 --- a/src/route.c +++ b/src/route.c @@ -34,12 +34,12 @@ rmode_t routing_mode = RMODE_ROUTER; fmode_t forwarding_mode = FMODE_INTERNAL; +bmode_t broadcast_mode = BMODE_MST; bool decrement_ttl = false; bool directonly = false; bool priorityinheritance = false; int macexpire = 600; bool overwrite_mac = false; -bool broadcast = true; mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}}; /* Sizes of various headers */ @@ -430,7 +430,7 @@ static void route_ipv4(node_t *source, vpn_packet_t *packet) { if(!checklength(source, packet, ether_size + ip_size)) return; - if(broadcast && (((packet->data[30] & 0xf0) == 0xe0) || ( + if(broadcast_mode && (((packet->data[30] & 0xf0) == 0xe0) || ( packet->data[30] == 255 && packet->data[31] == 255 && packet->data[32] == 255 && @@ -727,7 +727,7 @@ static void route_ipv6(node_t *source, vpn_packet_t *packet) { return; } - if(broadcast && packet->data[38] == 255) + if(broadcast_mode && packet->data[38] == 255) broadcast_packet(source, packet); else route_ipv6_unicast(source, packet); @@ -817,8 +817,7 @@ static void route_mac(node_t *source, vpn_packet_t *packet) { subnet = lookup_subnet_mac(NULL, &dest); if(!subnet) { - if(broadcast) - broadcast_packet(source, packet); + broadcast_packet(source, packet); return; } diff --git a/src/route.h b/src/route.h index 5622feb2..7b45e76a 100644 --- a/src/route.h +++ b/src/route.h @@ -36,12 +36,18 @@ typedef enum fmode_t { FMODE_KERNEL, } fmode_t; +typedef enum bmode_t { + BMODE_NONE = 0, + BMODE_MST, + BMODE_DIRECT, +} bmode_t; + extern rmode_t routing_mode; extern fmode_t forwarding_mode; +extern bmode_t broadcast_mode; extern bool decrement_ttl; extern bool directonly; extern bool overwrite_mac; -extern bool broadcast; extern bool priorityinheritance; extern int macexpire;