ginuerzh/gost · error

not a packet connection

Error message

not a packet connection

What it means

When routing UDP-like traffic through the TUN stack, the code takes the established connection cc and asserts it to net.PacketConn. If the underlying connection doesn't implement PacketConn (it's a stream conn), it returns "not a packet connection" and logs it with the tun prefix. This guards the packet-forwarding path which requires WriteTo/ReadFrom semantics.

Source

Thrown at tuntap.go:178

			return
		}
	}

	var tempDelay time.Duration
	for {
		err := func() error {
			var err error
			var pc net.PacketConn
			// fake tcp mode will be ignored when the client specifies a chain.
			if raddr != nil && !h.options.Chain.IsEmpty() {
				cc, err := h.options.Chain.DialContext(context.Background(), "udp", raddr.String())
				if err != nil {
					return err
				}
				var ok bool
				pc, ok = cc.(net.PacketConn)
				if !ok {
					err = errors.New("not a packet connection")
					log.Logf("[tun] %s - %s: %s", conn.LocalAddr(), raddr, err)
					return err
				}
			} else {
				if h.options.TCPMode {
					if raddr != nil {
						pc, err = tcpraw.Dial("tcp", raddr.String())
					} else {
						pc, err = tcpraw.Listen("tcp", h.options.Node.Addr)
					}
				} else {
					laddr, _ := net.ResolveUDPAddr("udp", h.options.Node.Addr)
					pc, err = net.ListenUDP("udp", laddr)
				}
			}
			if err != nil {
				return err
			}

View on GitHub (pinned to a33fdbf4c9)

Solutions

  1. Use a UDP-capable transport/relay (one that returns net.PacketConn) for tun UDP routes
  2. Check the connection type before forwarding and fall back to a packet-capable relay chain
  3. If TCPMode is intended, ensure UDP flows aren't routed through this code path (route/filter them at the tun option level)

Example fix

// before
pc, ok := cc.(net.PacketConn)
if !ok { return errors.New("not a packet connection") }
// after
pc, ok := cc.(net.PacketConn)
if !ok {
    pc, err = dialPacketConn(raddr) // use a UDP-capable dialer
    if err != nil { return err }
}
Defensive patterns

Strategy: type-guard

Validate before calling

pc, ok := cc.(net.PacketConn)
if !ok {
    // route via a UDP-capable relay before entering the packet path
    return errors.New("transport does not support packet connections")
}

Type guard

func toPacketConn(c net.Conn) (net.PacketConn, bool) {
    pc, ok := c.(net.PacketConn)
    return pc, ok
}

Try / catch

if err := forward(conn, raddr); err != nil {
    if err.Error() == "not a packet connection" {
        // fall back to a UDP-capable relay chain
        return forwardViaPacketRelay(conn, raddr)
    }
    return err
}

Prevention

When it happens

Trigger: TUN in non-TCP mode forwarding a UDP flow whose relayed connection cc is a stream-oriented net.Conn (e.g. a TCP/mux relay) rather than a net.PacketConn; config routes UDP traffic over a TCP-only transport.

Common situations: Routing tun UDP traffic through a TCP-mode relay/chain; version or config change where the UDP relay dialer started returning stream conns; mixing TCPMode with UDP routes.

Related errors


AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02). Data as JSON: /api/errors/44f13bc69119634e. Report an issue: GitHub.