tstest/natlab/vnet: add IPv6 all-nodes support

This adds support for sending packets to 33:33:00:00:01 at IPv6
multicast address ff02::1 to send to all nodes.

Nothing in Tailscale depends on this (yet?), but it makes debugging in
VMs behind natlab easier (e.g. you can ping all nodes), and other
things might depend on this in the future.

Mostly I'm trying to flesh out the IPv6 support in natlab now that we
can write vnet tests.

Updates #13038

Change-Id: If590031fcf075690ca35c7b230a38c3e72e621eb
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2024-08-28 08:06:17 -07:00
committed by Brad Fitzpatrick
parent 961ee321e8
commit 73b3c8fc8c
3 changed files with 77 additions and 20 deletions

View File

@@ -820,7 +820,20 @@ func (c vmClient) proto() Protocol {
return ProtocolUnixDGRAM
}
const ethernetHeaderLen = 14
func parseEthernet(pkt []byte) (dst, src MAC, ethType layers.EthernetType, payload []byte, ok bool) {
// headerLen is the length of an Ethernet header:
// 6 bytes of destination MAC, 6 bytes of source MAC, 2 bytes of EtherType.
const headerLen = 14
if len(pkt) < headerLen {
return
}
dst = MAC(pkt[0:6])
src = MAC(pkt[6:12])
ethType = layers.EthernetType(binary.BigEndian.Uint16(pkt[12:14]))
payload = pkt[headerLen:]
ok = true
return
}
// Handles a single connection from a QEMU-style client or muxd connections for dgram mode
func (s *Server) ServeUnixConn(uc *net.UnixConn, proto Protocol) {
@@ -878,10 +891,10 @@ func (s *Server) ServeUnixConn(uc *net.UnixConn, proto Protocol) {
c := vmClient{uc, raddr}
// For the first packet from a MAC, register a writerFunc to write to the VM.
if len(packetRaw) < ethernetHeaderLen {
_, srcMAC, _, _, ok := parseEthernet(packetRaw)
if !ok {
continue
}
srcMAC := MAC(packetRaw[6:12])
srcNode, ok := s.nodeByMAC[srcMAC]
if !ok {
s.logf("[conn %p] got frame from unknown MAC %v", c.uc, srcMAC)
@@ -961,12 +974,12 @@ func (s *Server) routeUDPPacket(up UDPPacket) {
//
// It reports whether a packet was written to any clients.
func (n *network) writeEth(res []byte) bool {
if len(res) < 12 {
dstMAC, srcMAC, etherType, _, ok := parseEthernet(res)
if !ok {
return false
}
dstMAC := MAC(res[0:6])
srcMAC := MAC(res[6:12])
if dstMAC.IsBroadcast() {
if dstMAC.IsBroadcast() || (n.v6 && etherType == layers.EthernetTypeIPv6 && dstMAC == macAllNodes) {
num := 0
n.writers.Range(func(mac MAC, nw networkWriter) bool {
if mac != srcMAC {
@@ -996,6 +1009,7 @@ func (n *network) writeEth(res []byte) bool {
}
var (
macAllNodes = MAC{0: 0x33, 1: 0x33, 5: 0x01}
macAllRouters = MAC{0: 0x33, 1: 0x33, 5: 0x02}
macBroadcast = MAC{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
)
@@ -1007,7 +1021,7 @@ const (
func (n *network) HandleEthernetPacket(ep EthernetPacket) {
packet := ep.gp
dstMAC := ep.DstMAC()
isBroadcast := dstMAC.IsBroadcast()
isBroadcast := dstMAC.IsBroadcast() || (n.v6 && ep.le.EthernetType == layers.EthernetTypeIPv6 && dstMAC == macAllNodes)
isV6SpecialMAC := dstMAC[0] == 0x33 && dstMAC[1] == 0x33
// forRouter is whether the packet is destined for the router itself
@@ -1016,7 +1030,7 @@ func (n *network) HandleEthernetPacket(ep EthernetPacket) {
const debug = false
if debug {
n.logf("HandleEthernetPacket: %v => %v; type %v, forRouter=%v", ep.SrcMAC(), ep.DstMAC(), ep.le.EthernetType, forRouter)
n.logf("HandleEthernetPacket: %v => %v; type %v, bcast=%v, forRouter=%v", ep.SrcMAC(), ep.DstMAC(), ep.le.EthernetType, isBroadcast, forRouter)
}
switch ep.le.EthernetType {
@@ -1058,7 +1072,7 @@ func (n *network) HandleEthernetPacket(ep EthernetPacket) {
// log spam when verbose logging is enabled.
return
}
if isMcast {
if isMcast && !isBroadcast {
return
}
}