icmp6handlers.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. // Copyright 2019 Yunion
  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 hostdhcp
  15. import (
  16. "fmt"
  17. "net"
  18. "time"
  19. "yunion.io/x/log"
  20. "yunion.io/x/pkg/errors"
  21. "yunion.io/x/pkg/util/netutils"
  22. "yunion.io/x/onecloud/pkg/hostman/options"
  23. "yunion.io/x/onecloud/pkg/util/icmp6"
  24. "yunion.io/x/onecloud/pkg/util/netutils2"
  25. )
  26. type sRARequest struct {
  27. vmMac net.HardwareAddr
  28. attempts int
  29. succAttempts int
  30. nextAttempt time.Time
  31. }
  32. func (s *SGuestDHCP6Server) requestRA(vmMac net.HardwareAddr) {
  33. s.raReqCh <- vmMac
  34. }
  35. func (s *SGuestDHCP6Server) handleRouterSolicitation(msg *icmp6.SRouterSolicitation) error {
  36. // solicitation request from guest
  37. s.requestRA(msg.SrcMac)
  38. return nil
  39. }
  40. func (s *SGuestDHCP6Server) handleRouterAdvertisement(msg *icmp6.SRouterAdvertisement) error {
  41. // periodic router advertisement from local router
  42. return nil
  43. }
  44. func (s *SGuestDHCP6Server) handleNeighborSolicitation(msg *icmp6.SNeighborSolicitation) error {
  45. return nil
  46. }
  47. func (s *SGuestDHCP6Server) handleNeighborAdvertisement(msg *icmp6.SNeighborAdvertisement) error {
  48. if msg.IsSolicited {
  49. //log.Infof("Save mac %s for gw IP %s", msg.SrcMac, msg.TargetAddr.String())
  50. s.gwMacCache.Set(msg.TargetAddr.String(), msg.SrcMac)
  51. }
  52. return nil
  53. }
  54. func (s *SGuestDHCP6Server) requestGatewayMac(gwIP net.IP, vlanId uint16) {
  55. // Solicited-Node Multicast Address, FF02::1:FF00:0/104, 33:33:ff:00:00:00
  56. destIP := netutils2.IP2SolicitMcastIP(gwIP)
  57. destMac := netutils2.IP2SolicitMcastMac(gwIP)
  58. ns := &icmp6.SNeighborSolicitation{
  59. SBaseICMP6Message: icmp6.SBaseICMP6Message{
  60. SrcMac: s.ifaceDev.GetHardwareAddr(),
  61. SrcIP: net.ParseIP(s.ifaceDev.Addr6LinkLocal),
  62. DstMac: destMac,
  63. DstIP: destIP,
  64. Vlan: vlanId,
  65. },
  66. TargetAddr: gwIP,
  67. }
  68. bytes, err := icmp6.EncodePacket(ns)
  69. if err != nil {
  70. log.Errorf("Encode NeighborSolicitation error: %v", err)
  71. return
  72. }
  73. err = s.conn.SendRaw(bytes, destMac)
  74. if err != nil {
  75. log.Errorf("Send RouterAdvertisement error: %v", err)
  76. return
  77. }
  78. }
  79. func (s *SGuestDHCP6Server) sendRouterAdvertisement(vmMac net.HardwareAddr) (bool, error) {
  80. var conf = s.getConfig(vmMac)
  81. if conf == nil {
  82. return false, errors.Wrapf(errors.ErrNotFound, "getConfig %s", vmMac.String())
  83. }
  84. if conf != nil && conf.ClientIP6 != nil && conf.Gateway6 != nil {
  85. gwMacObj := s.gwMacCache.AtomicGet(conf.Gateway6.String())
  86. if gwMacObj == nil {
  87. log.Debugf("No mac for gw IP %s, request it", conf.Gateway6.String())
  88. s.requestGatewayMac(conf.Gateway6, conf.VlanId)
  89. return false, nil
  90. }
  91. gwMac := gwMacObj.(net.HardwareAddr)
  92. pref := icmp6.PreferenceMedium
  93. if conf.IsDefaultGW {
  94. pref = icmp6.PreferenceHigh
  95. }
  96. _, ipnet, err := net.ParseCIDR(fmt.Sprintf("%s/%d", conf.ClientIP6, conf.PrefixLen6))
  97. if err != nil {
  98. return false, errors.Wrapf(err, "ParseCIDR %s/%d", conf.ClientIP6, conf.PrefixLen6)
  99. }
  100. srcIpAddr, _ := netutils.Mac2LinkLocal(gwMac.String())
  101. gwLinkLocalIP := srcIpAddr.ToIP()
  102. ra := &icmp6.SRouterAdvertisement{
  103. SBaseICMP6Message: icmp6.SBaseICMP6Message{
  104. SrcMac: gwMac,
  105. // https://www.rfc-editor.org/rfc/rfc4861.html#page-18
  106. // !!! MUST be the link-local address assigned to the interface from which this message is sent.
  107. SrcIP: gwLinkLocalIP,
  108. DstMac: vmMac,
  109. DstIP: net.ParseIP("ff02::1"),
  110. },
  111. CurHopLimit: 64,
  112. IsManaged: true,
  113. IsOther: true,
  114. IsHomeAgent: false,
  115. Preference: pref,
  116. RouterLifetime: uint16(options.HostOptions.Dhcp6RouterLifetimeSeconds),
  117. ReachableTime: 0,
  118. RetransTimer: 0,
  119. MTU: uint32(conf.MTU),
  120. PrefixInfo: []icmp6.SPrefixInfoOption{
  121. {
  122. IsOnlink: true,
  123. IsAutoconf: false,
  124. Prefix: ipnet.IP,
  125. PrefixLen: conf.PrefixLen6,
  126. ValidLifetime: uint32(options.HostOptions.Dhcp6RouterLifetimeSeconds / 2),
  127. PreferredLifetime: uint32(options.HostOptions.Dhcp6RouterLifetimeSeconds / 4),
  128. },
  129. },
  130. }
  131. for i := range conf.Routes6 {
  132. route := conf.Routes6[i]
  133. if route.Gateway.String() == "::" {
  134. // on-link routes
  135. ra.PrefixInfo = append(ra.PrefixInfo, icmp6.SPrefixInfoOption{
  136. IsOnlink: true,
  137. IsAutoconf: false,
  138. Prefix: route.Prefix,
  139. PrefixLen: route.PrefixLen,
  140. ValidLifetime: uint32(options.HostOptions.Dhcp6RouterLifetimeSeconds / 2),
  141. PreferredLifetime: uint32(options.HostOptions.Dhcp6RouterLifetimeSeconds / 4),
  142. })
  143. } else if route.Gateway.String() != conf.Gateway6.String() {
  144. // routes forwarded by router
  145. ra.RouteInfo = append(ra.RouteInfo, icmp6.SRouteInfoOption{
  146. RouteLifetime: uint32(options.HostOptions.Dhcp6RouterLifetimeSeconds),
  147. Prefix: route.Prefix,
  148. PrefixLen: route.PrefixLen,
  149. Preference: pref,
  150. })
  151. }
  152. }
  153. bytes, err := icmp6.EncodePacket(ra)
  154. if err != nil {
  155. log.Errorf("Encode RouterAdvertisement error: %v", err)
  156. return false, errors.Wrapf(err, "EncodePacket")
  157. }
  158. err = s.conn.SendRaw(bytes, vmMac)
  159. if err != nil {
  160. log.Errorf("Send RouterAdvertisement error: %v", err)
  161. }
  162. }
  163. return true, nil
  164. }
  165. func (s *SGuestDHCP6Server) stopRAServer() {
  166. close(s.raExitCh)
  167. close(s.raReqCh)
  168. }
  169. func (s *SGuestDHCP6Server) handleRARequest(vmMac net.HardwareAddr) {
  170. vmMacStr := vmMac.String()
  171. raReq, ok := s.raReqQueue[vmMacStr]
  172. if !ok {
  173. raReq = &sRARequest{
  174. vmMac: vmMac,
  175. }
  176. s.raReqQueue[vmMacStr] = raReq
  177. } else {
  178. raReq.nextAttempt = time.Time{}
  179. }
  180. // send RA immediately
  181. raReq.attempts++
  182. succ, err := s.sendRouterAdvertisement(vmMac)
  183. if err != nil {
  184. if errors.Cause(err) == errors.ErrNotFound {
  185. // no such vm, giveup
  186. delete(s.raReqQueue, vmMacStr)
  187. return
  188. }
  189. log.Errorf("sendRouterAdvertisement error: %v", err)
  190. }
  191. if succ {
  192. raReq.succAttempts++
  193. }
  194. if raReq.succAttempts < options.HostOptions.Dhcp6RouterAdvertisementAttempts {
  195. if raReq.attempts > 2*options.HostOptions.Dhcp6RouterAdvertisementAttempts {
  196. // giveup
  197. delete(s.raReqQueue, vmMacStr)
  198. } else {
  199. // retry next round
  200. }
  201. } else {
  202. // schedule RA when prefix router lifetime expires
  203. raReq.attempts = 0
  204. raReq.succAttempts = 0
  205. raReq.nextAttempt = time.Now().Add(time.Second * time.Duration(options.HostOptions.Dhcp6RouterLifetimeSeconds/4))
  206. }
  207. }
  208. func (s *SGuestDHCP6Server) startRAServer() {
  209. // a tiny RA server
  210. stop := false
  211. for !stop {
  212. select {
  213. case <-s.raExitCh:
  214. stop = true
  215. case raReq := <-s.raReqCh:
  216. // handle RA request
  217. s.handleRARequest(raReq)
  218. case <-time.After(time.Second * time.Duration(options.HostOptions.Dhcp6RouterAdvertisementIntervalSecs)):
  219. // send RA
  220. for _, raReq := range s.raReqQueue {
  221. if !raReq.nextAttempt.IsZero() && raReq.nextAttempt.After(time.Now()) {
  222. continue
  223. }
  224. s.handleRARequest(raReq.vmMac)
  225. }
  226. }
  227. }
  228. }