route.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // Copyright 2019 the Kilo authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package route
  15. import (
  16. "errors"
  17. "fmt"
  18. "sync"
  19. "github.com/vishvananda/netlink"
  20. "golang.org/x/sys/unix"
  21. )
  22. // Table represents a routing table.
  23. // Table can safely be used concurrently.
  24. type Table struct {
  25. errors chan error
  26. mu sync.Mutex
  27. routes map[string]*netlink.Route
  28. subscribed bool
  29. // Make these functions fields to allow
  30. // for testing.
  31. add func(*netlink.Route) error
  32. del func(*netlink.Route) error
  33. }
  34. // NewTable generates a new table.
  35. func NewTable() *Table {
  36. return &Table{
  37. errors: make(chan error),
  38. routes: make(map[string]*netlink.Route),
  39. add: netlink.RouteReplace,
  40. del: func(r *netlink.Route) error {
  41. name := routeToString(r)
  42. if name == "" {
  43. return errors.New("attempting to delete invalid route")
  44. }
  45. routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL)
  46. if err != nil {
  47. return fmt.Errorf("failed to list routes before deletion: %v", err)
  48. }
  49. for _, route := range routes {
  50. if routeToString(&route) == name {
  51. return netlink.RouteDel(r)
  52. }
  53. }
  54. return nil
  55. },
  56. }
  57. }
  58. // Run watches for changes to routes in the table and reconciles
  59. // the table against the desired state.
  60. func (t *Table) Run(stop <-chan struct{}) (<-chan error, error) {
  61. t.mu.Lock()
  62. if t.subscribed {
  63. t.mu.Unlock()
  64. return t.errors, nil
  65. }
  66. // Ensure a given instance only subscribes once.
  67. t.subscribed = true
  68. t.mu.Unlock()
  69. events := make(chan netlink.RouteUpdate)
  70. if err := netlink.RouteSubscribe(events, stop); err != nil {
  71. return t.errors, fmt.Errorf("failed to subscribe to route events: %v", err)
  72. }
  73. go func() {
  74. defer close(t.errors)
  75. for {
  76. var e netlink.RouteUpdate
  77. select {
  78. case e = <-events:
  79. case <-stop:
  80. return
  81. }
  82. switch e.Type {
  83. // Watch for deleted routes to reconcile this table's routes.
  84. case unix.RTM_DELROUTE:
  85. t.mu.Lock()
  86. for _, r := range t.routes {
  87. // If any deleted route's destination matches a destination
  88. // in the table, reset the corresponding route just in case.
  89. if r.Dst.IP.Equal(e.Route.Dst.IP) && r.Dst.Mask.String() == e.Route.Dst.Mask.String() {
  90. if err := t.add(r); err != nil {
  91. nonBlockingSend(t.errors, fmt.Errorf("failed add route: %v", err))
  92. }
  93. }
  94. }
  95. t.mu.Unlock()
  96. }
  97. }
  98. }()
  99. return t.errors, nil
  100. }
  101. // CleanUp will clean up any routes created by the instance.
  102. func (t *Table) CleanUp() error {
  103. t.mu.Lock()
  104. defer t.mu.Unlock()
  105. for k, route := range t.routes {
  106. if err := t.del(route); err != nil {
  107. return fmt.Errorf("failed to delete route: %v", err)
  108. }
  109. delete(t.routes, k)
  110. }
  111. return nil
  112. }
  113. // Set idempotently overwrites any routes previously defined
  114. // for the table with the given set of routes.
  115. func (t *Table) Set(routes []*netlink.Route) error {
  116. r := make(map[string]*netlink.Route)
  117. for _, route := range routes {
  118. if route == nil {
  119. continue
  120. }
  121. r[routeToString(route)] = route
  122. }
  123. t.mu.Lock()
  124. defer t.mu.Unlock()
  125. for k := range t.routes {
  126. if _, ok := r[k]; !ok {
  127. if err := t.del(t.routes[k]); err != nil {
  128. return fmt.Errorf("failed to delete route: %v", err)
  129. }
  130. delete(t.routes, k)
  131. }
  132. }
  133. for k := range r {
  134. if _, ok := t.routes[k]; !ok {
  135. if err := t.add(r[k]); err != nil {
  136. return fmt.Errorf("failed to add route %q: %v", routeToString(r[k]), err)
  137. }
  138. t.routes[k] = r[k]
  139. }
  140. }
  141. return nil
  142. }
  143. func nonBlockingSend(errors chan<- error, err error) {
  144. select {
  145. case errors <- err:
  146. default:
  147. }
  148. }
  149. func routeToString(route *netlink.Route) string {
  150. if route == nil || route.Dst == nil {
  151. return ""
  152. }
  153. src := "-"
  154. if route.Src != nil {
  155. src = route.Src.String()
  156. }
  157. gw := "-"
  158. if route.Gw != nil {
  159. gw = route.Gw.String()
  160. }
  161. return fmt.Sprintf("dst: %s, via: %s, src: %s, dev: %d", route.Dst.String(), gw, src, route.LinkIndex)
  162. }