routes.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. package aerotun
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net"
  6. "os/exec"
  7. )
  8. func runIP(args ...string) ([]byte, error) {
  9. out, err := exec.Command("ip", args...).CombinedOutput()
  10. if err != nil {
  11. return out, fmt.Errorf("ip %v: %s: %w", args, out, err)
  12. }
  13. return out, nil
  14. }
  15. type routeManager struct {
  16. run func(...string) ([]byte, error)
  17. owned [][]string
  18. }
  19. func (r *routeManager) add(args ...string) error {
  20. if _, err := r.run(append([]string{"-4", "route", "add"}, args...)...); err != nil {
  21. return err
  22. }
  23. r.owned = append(r.owned, append([]string(nil), args...))
  24. return nil
  25. }
  26. func (r *routeManager) cleanup() {
  27. for i := len(r.owned) - 1; i >= 0; i-- {
  28. _, _ = r.run(append([]string{"-4", "route", "del"}, r.owned[i]...)...)
  29. }
  30. r.owned = nil
  31. }
  32. func (r *routeManager) install(name string, endpoint net.IP) (err error) {
  33. if endpoint.To4() == nil {
  34. return fmt.Errorf("default route currently requires an IPv4 remote endpoint")
  35. }
  36. defer func() {
  37. if err != nil {
  38. r.cleanup()
  39. }
  40. }()
  41. out, err := r.run("-j", "-4", "route", "get", endpoint.String())
  42. if err != nil {
  43. return err
  44. }
  45. var paths []struct {
  46. Dev string `json:"dev"`
  47. Gateway string `json:"gateway"`
  48. }
  49. if err = json.Unmarshal(out, &paths); err != nil {
  50. return err
  51. }
  52. if len(paths) != 1 || paths[0].Dev == "" || paths[0].Dev == name {
  53. return fmt.Errorf("cannot find independent underlay route to remote endpoint")
  54. }
  55. host := endpoint.String() + "/32"
  56. // Preserve a pre-existing host route. Never replace routes owned by others.
  57. out, err = r.run("-j", "-4", "route", "show", "exact", host)
  58. if err != nil {
  59. return err
  60. }
  61. var existing []json.RawMessage
  62. if err = json.Unmarshal(out, &existing); err != nil {
  63. return err
  64. }
  65. if len(existing) == 0 {
  66. args := []string{host}
  67. if paths[0].Gateway != "" {
  68. args = append(args, "via", paths[0].Gateway)
  69. }
  70. args = append(args, "dev", paths[0].Dev)
  71. if err = r.add(args...); err != nil {
  72. return err
  73. }
  74. }
  75. // Split defaults leave the original default intact, so stopping/rolling back
  76. // removes only our routes. A pinned endpoint stays outside the tunnel.
  77. if err = r.add("0.0.0.0/1", "dev", name); err != nil {
  78. return err
  79. }
  80. if err = r.add("128.0.0.0/1", "dev", name); err != nil {
  81. return err
  82. }
  83. return nil
  84. }