diff --git a/src/core/api.go b/src/core/api.go index c886edf6..536b1a87 100644 --- a/src/core/api.go +++ b/src/core/api.go @@ -231,6 +231,18 @@ func (c *Core) MaxMTU() uint64 { return c.store.maxSessionMTU() } +// SetMTU can only safely be called after Init and before Start. +func (c *Core) SetMTU(mtu uint64) { + if mtu < 1280 { + mtu = 1280 + } + c.store.mtu = mtu +} + +func (c *Core) MTU() uint64 { + return c.store.mtu +} + // Implement io.ReadWriteCloser func (c *Core) Read(p []byte) (n int, err error) { diff --git a/src/core/keystore.go b/src/core/keystore.go index d8a808a2..d2c6c24a 100644 --- a/src/core/keystore.go +++ b/src/core/keystore.go @@ -7,6 +7,9 @@ import ( "sync" "time" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv6" + iwt "github.com/Arceliar/ironwood/types" "github.com/yggdrasil-network/yggdrasil-go/src/address" @@ -26,6 +29,7 @@ type keyStore struct { addrBuffer map[address.Address]*buffer subnetToInfo map[address.Subnet]*keyInfo subnetBuffer map[address.Subnet]*buffer + mtu uint64 } type keyInfo struct { @@ -53,6 +57,7 @@ func (k *keyStore) init(core *Core) { k.addrBuffer = make(map[address.Address]*buffer) k.subnetToInfo = make(map[address.Subnet]*keyInfo) k.subnetBuffer = make(map[address.Subnet]*buffer) + k.mtu = 1280 // Default to something safe, expect user to set this } func (k *keyStore) sendToAddress(addr address.Address, bs []byte) { @@ -238,18 +243,19 @@ func (k *keyStore) readPC(p []byte) (int, error) { if len(bs) < 40 { continue } - /* TODO? ICMP packet too big, for now tuntap sends this when needed - if len(bs) > int(tun.MTU()) { - ptb := &icmp.PacketTooBig{ - MTU: int(tun.mtu), - Data: bs[:40], - } - if packet, err := CreateICMPv6(bs[8:24], bs[24:40], ipv6.ICMPTypePacketTooBig, 0, ptb); err == nil { - _, _ = tun.core.WriteTo(packet, from) - } - continue - } - */ + if len(bs) > int(k.mtu) { + // Using bs would make it leak off the stack, so copy to buf + buf := make([]byte, 40) + copy(buf, bs) + ptb := &icmp.PacketTooBig{ + MTU: int(k.mtu), + Data: buf[:40], + } + if packet, err := CreateICMPv6(buf[8:24], buf[24:40], ipv6.ICMPTypePacketTooBig, 0, ptb); err == nil { + _, _ = k.writePC(packet) + } + continue + } var srcAddr, dstAddr address.Address var srcSubnet, dstSubnet address.Subnet copy(srcAddr[:], bs[8:]) diff --git a/src/tuntap/icmpv6.go b/src/tuntap/icmpv6.go deleted file mode 100644 index b7461a2d..00000000 --- a/src/tuntap/icmpv6.go +++ /dev/null @@ -1,80 +0,0 @@ -package tuntap - -// The ICMPv6 module implements functions to easily create ICMPv6 -// packets. These functions, when mixed with the built-in Go IPv6 -// and ICMP libraries, can be used to send control messages back -// to the host. Examples include: -// - NDP messages, when running in TAP mode -// - Packet Too Big messages, when packets exceed the session MTU -// - Destination Unreachable messages, when a session prohibits -// incoming traffic - -import ( - "encoding/binary" - "net" - - "golang.org/x/net/icmp" - "golang.org/x/net/ipv6" -) - -type ICMPv6 struct{} - -// Marshal returns the binary encoding of h. -func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) { - b := make([]byte, 40) - b[0] |= byte(h.Version) << 4 - b[0] |= byte(h.TrafficClass) >> 4 - b[1] |= byte(h.TrafficClass) << 4 - b[1] |= byte(h.FlowLabel >> 16) - b[2] = byte(h.FlowLabel >> 8) - b[3] = byte(h.FlowLabel) - binary.BigEndian.PutUint16(b[4:6], uint16(h.PayloadLen)) - b[6] = byte(h.NextHeader) - b[7] = byte(h.HopLimit) - copy(b[8:24], h.Src) - copy(b[24:40], h.Dst) - return b, nil -} - -// Creates an ICMPv6 packet based on the given icmp.MessageBody and other -// parameters, complete with IP headers only, which can be written directly to -// a TUN adapter, or called directly by the CreateICMPv6L2 function when -// generating a message for TAP adapters. -func CreateICMPv6(dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody icmp.MessageBody) ([]byte, error) { - // Create the ICMPv6 message - icmpMessage := icmp.Message{ - Type: mtype, - Code: mcode, - Body: mbody, - } - - // Convert the ICMPv6 message into []byte - icmpMessageBuf, err := icmpMessage.Marshal(icmp.IPv6PseudoHeader(src, dst)) - if err != nil { - return nil, err - } - - // Create the IPv6 header - ipv6Header := ipv6.Header{ - Version: ipv6.Version, - NextHeader: 58, - PayloadLen: len(icmpMessageBuf), - HopLimit: 255, - Src: src, - Dst: dst, - } - - // Convert the IPv6 header into []byte - ipv6HeaderBuf, err := ipv6Header_Marshal(&ipv6Header) - if err != nil { - return nil, err - } - - // Construct the packet - responsePacket := make([]byte, ipv6.HeaderLen+ipv6Header.PayloadLen) - copy(responsePacket[:ipv6.HeaderLen], ipv6HeaderBuf) - copy(responsePacket[ipv6.HeaderLen:], icmpMessageBuf) - - // Send it back - return responsePacket, nil -} diff --git a/src/tuntap/iface.go b/src/tuntap/iface.go index 8642a005..587c925e 100644 --- a/src/tuntap/iface.go +++ b/src/tuntap/iface.go @@ -1,10 +1,5 @@ package tuntap -import ( - "golang.org/x/net/icmp" - "golang.org/x/net/ipv6" -) - const TUN_OFFSET_BYTES = 4 func (tun *TunAdapter) read() { @@ -37,15 +32,8 @@ func (tun *TunAdapter) write() { tun.log.Errorln("Exiting tun writer due to core read error:", err) return } - if n > int(tun.MTU()) { - ptb := &icmp.PacketTooBig{ - MTU: int(tun.mtu), - Data: bs[:40], - } - if packet, err := CreateICMPv6(bs[8:24], bs[24:40], ipv6.ICMPTypePacketTooBig, 0, ptb); err == nil { - _, _ = tun.core.Write(packet) - } - continue + if !tun.isEnabled { + continue // Nothing to do, the tun isn't enabled } bs = buf[:TUN_OFFSET_BYTES+n] if _, err = tun.iface.Write(bs, TUN_OFFSET_BYTES); err != nil { diff --git a/src/tuntap/tun.go b/src/tuntap/tun.go index 41da0070..dbba018b 100644 --- a/src/tuntap/tun.go +++ b/src/tuntap/tun.go @@ -139,6 +139,7 @@ func (tun *TunAdapter) _start() error { if tun.MTU() != mtu { tun.log.Warnf("Warning: Interface MTU %d automatically adjusted to %d (supported range is 1280-%d)", tun.config.IfMTU, tun.MTU(), MaximumMTU()) } + tun.core.SetMTU(tun.MTU()) tun.isOpen = true tun.isEnabled = true go tun.read()