go-routeros

repository·master·Indexed 42 days ago

https://github.com/go-routeros/routeros

A Go library providing programmatic access to MikroTik devices via the RouterOS API. It supports synchronous and asynchronous command execution, TLS connections, and event listening. Key features include the v3 module, Dial and DialTLS connection methods, and a Listen API for receiving !re sentences from devices.

Tokens
4.2K
Snippets
16
Records
21
Agent score
55%

What's inside go-routeros

  1. Explore go-routeros usage examples

    master

    The repository provides several example implementations to demonstrate different ways to interact with Mikrotik devices via the RouterOS API:

    • Run: Basic execution patterns (examples/run/main.go).
    • Listen: Implementing a listener pattern (examples/listen/main.go).
    • Tab: Tabular or interactive usage (examples/tab/main.go).
  2. Install the go-routeros library

    master

    To install the RouterOS Client for Go, use the following command to fetch the v3 module:

    go get github.com/go-routeros/routeros/v3

    Note: The project has moved away from gopkg.in. Ensure your import paths use the Go module format: github.com/go-routeros/routeros/v3.

    go get github.com/go-routeros/routeros/v3
  3. Understand RouterOS listener termination sentences

    master

    When a listening session ends, the ListenReply.Done field is populated based on the sentence received from RouterOS. The following special sentences determine the state of the listener:

    SentenceMeaning / Behavior
    !doneThe command completed successfully.
    !trapAn error occurred. If category is "2", it indicates the command was interrupted. Otherwise, it is treated as a DeviceError.
    !fatalA fatal error occurred; results in a DeviceError.
    !reA standard re-entry sentence; these are sent to the Chan() channel.
    !empty or ""Empty sentences are ignored.
  4. Execute RouterOS commands synchronously

    master

    The Client provides several methods to send commands (sentences) to a RouterOS device and wait for the response. You can use variadic methods for simple commands or slice-based methods for pre-constructed argument lists.

    Synchronous Methods

    • Run(sentences ...string) (*Reply, error): A convenience method that accepts a variadic list of strings representing the command parts.
    • RunArgs(sentences []string) (*Reply, error): Sends a slice of strings and waits for the reply using context.Background().
    • RunContext(ctx context.Context, sentences ...string) (*Reply, error): Sends a variadic list of strings while respecting the provided context.Context for cancellation or timeouts.
    • RunArgsContext(ctx context.Context, sentences []string) (*Reply, error): Sends a slice of strings while respecting the provided context.Context.

    If the client is not in asynchronous mode, these methods will block until the RouterOS device returns a Done signal or an error occurs.

    // Using variadic Run
    reply, err := client.Run("/ip", "address", "print")
    
    // Using RunArgsContext with a timeout
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    sentences := []string{"/ip", "address", "print"}
    reply, err := client.RunArgsContext(ctx, sentences)
  5. Close the Client connection

    master

    Call Close() to gracefully shut down the connection to the RouterOS device. This closes the underlying io.ReadWriteCloser and the internal protocol reader/writer. It is safe to call multiple times.

    defer client.Close()
  6. Connect to RouterOS using Dial

    master

    Use the Dial family of functions to establish a TCP connection and authenticate with a RouterOS device in a single step. These functions handle the connection, the /login command, and any necessary MD5 challenge-response cycles automatically.

    Available connection methods:

    • Dial(address, username, password): Standard TCP connection using context.Background().
    • DialTimeout(address, username, password, timeout): TCP connection with a specified timeout.
    • DialContext(ctx, address, username, password): TCP connection using a provided context.Context for cancellation or deadlines.
    import "github.com/go-routeros/routeros/v3"
    
    // Connect with a timeout
    client, err := routeros.DialTimeout("192.168.88.1:8728", "admin", "password", 5 * time.Second)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()
  7. Initialize a Client from an existing connection

    master

    If you already have an established io.ReadWriteCloser (such as a custom net connection), you can wrap it in a routeros.Client using NewClient.

    Note: When using NewClient, you must manually call Login or LoginContext to authenticate before executing commands.

    // Assuming 'conn' is an io.ReadWriteCloser
    client, err := routeros.NewClient(conn)
    if err != nil {
        log.Fatal(err)
    }
    
    // You MUST login manually after using NewClient
    err = client.Login("admin", "password")
    if err != nil {
        log.Fatal(err)
    }
  8. Authenticate with Login and LoginContext

    master

    The Login and LoginContext methods execute the /login command on the RouterOS device. This method is compatible with both modern RouterOS versions (post-6.43) which use a single-stage cleartext login, and older versions that require a two-stage MD5 challenge-response.

    • Login(username, password): Uses context.Background().
    • LoginContext(ctx, username, password): Uses the provided context for lifecycle management.
    err := client.LoginContext(ctx, "admin", "password")
    if err != nil {
        log.Fatal(err)
    }
  9. Use the Reply type to handle RouterOS API responses

    master

    The Reply struct represents the complete response received from a RouterOS API call. It aggregates multiple proto.Sentence objects into two categories:

    1. Re: A slice of *proto.Sentence containing the actual data/sentences returned by the command.
    2. Done: A single *proto.Sentence indicating the end of the response stream.

    You can use the String() method on a Reply instance to get a newline-delimited string representation of all sentences contained within the reply.

    // Example of what a Reply structure looks like conceptually
    reply := &routeros.Reply{
        Re:   []*proto.Sentence{/* ... data sentences ... */},
        Done: &proto.Sentence{/* ... done sentence ... */},
    }
    
    // Get a string representation of the entire response
    fmt.Println(reply.String())
  10. Enable asynchronous mode with Async()

    master

    Call Async() on a *Client instance to start asynchronous mode. This allows the client to process multiple requests concurrently by listening for tagged sentences from the RouterOS device. Async() returns immediately and provides a receive-only error channel (<-chan error) that will receive an error if the asynchronous loop terminates unexpectedly. If the client is already in asynchronous mode, the channel will receive errAlreadyAsync.

    // Assuming 'client' is an initialized *routeros.Client
    errChan := client.Async()
    
    // Monitor the error channel in a separate goroutine or after your operations
    go func() {
        if err := <-errChan;
            err != nil {
            log.Printf("Async mode error: %v", err)
        }
    }()
  11. Enable asynchronous mode with AsyncContext(ctx)

    master

    Use AsyncContext(ctx context.Context) to start asynchronous mode with a specific lifecycle controlled by the provided context.Context. If the context is canceled or reaches its deadline, the asynchronous loop will be terminated and the underlying connection will be canceled. Like Async(), it returns a <-chan error to signal if the background loop fails.

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    
    errChan := client.AsyncContext(ctx)
    
    // The loop will stop if ctx is canceled