| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- package aerotun
- import (
- "encoding/json"
- "fmt"
- "net"
- "os/exec"
- )
- func runIP(args ...string) ([]byte, error) {
- out, err := exec.Command("ip", args...).CombinedOutput()
- if err != nil {
- return out, fmt.Errorf("ip %v: %s: %w", args, out, err)
- }
- return out, nil
- }
- type routeManager struct {
- run func(...string) ([]byte, error)
- owned [][]string
- }
- func (r *routeManager) add(args ...string) error {
- if _, err := r.run(append([]string{"-4", "route", "add"}, args...)...); err != nil {
- return err
- }
- r.owned = append(r.owned, append([]string(nil), args...))
- return nil
- }
- func (r *routeManager) cleanup() {
- for i := len(r.owned) - 1; i >= 0; i-- {
- _, _ = r.run(append([]string{"-4", "route", "del"}, r.owned[i]...)...)
- }
- r.owned = nil
- }
- func (r *routeManager) install(name string, endpoint net.IP) (err error) {
- if endpoint.To4() == nil {
- return fmt.Errorf("default route currently requires an IPv4 remote endpoint")
- }
- defer func() {
- if err != nil {
- r.cleanup()
- }
- }()
- out, err := r.run("-j", "-4", "route", "get", endpoint.String())
- if err != nil {
- return err
- }
- var paths []struct {
- Dev string `json:"dev"`
- Gateway string `json:"gateway"`
- }
- if err = json.Unmarshal(out, &paths); err != nil {
- return err
- }
- if len(paths) != 1 || paths[0].Dev == "" || paths[0].Dev == name {
- return fmt.Errorf("cannot find independent underlay route to remote endpoint")
- }
- host := endpoint.String() + "/32"
- // Preserve a pre-existing host route. Never replace routes owned by others.
- out, err = r.run("-j", "-4", "route", "show", "exact", host)
- if err != nil {
- return err
- }
- var existing []json.RawMessage
- if err = json.Unmarshal(out, &existing); err != nil {
- return err
- }
- if len(existing) == 0 {
- args := []string{host}
- if paths[0].Gateway != "" {
- args = append(args, "via", paths[0].Gateway)
- }
- args = append(args, "dev", paths[0].Dev)
- if err = r.add(args...); err != nil {
- return err
- }
- }
- // Split defaults leave the original default intact, so stopping/rolling back
- // removes only our routes. A pinned endpoint stays outside the tunnel.
- if err = r.add("0.0.0.0/1", "dev", name); err != nil {
- return err
- }
- if err = r.add("128.0.0.0/1", "dev", name); err != nil {
- return err
- }
- return nil
- }
|