| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package hostdhcp
- import (
- "fmt"
- "net"
- "time"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/netutils"
- "yunion.io/x/onecloud/pkg/hostman/options"
- "yunion.io/x/onecloud/pkg/util/icmp6"
- "yunion.io/x/onecloud/pkg/util/netutils2"
- )
- type sRARequest struct {
- vmMac net.HardwareAddr
- attempts int
- succAttempts int
- nextAttempt time.Time
- }
- func (s *SGuestDHCP6Server) requestRA(vmMac net.HardwareAddr) {
- s.raReqCh <- vmMac
- }
- func (s *SGuestDHCP6Server) handleRouterSolicitation(msg *icmp6.SRouterSolicitation) error {
- // solicitation request from guest
- s.requestRA(msg.SrcMac)
- return nil
- }
- func (s *SGuestDHCP6Server) handleRouterAdvertisement(msg *icmp6.SRouterAdvertisement) error {
- // periodic router advertisement from local router
- return nil
- }
- func (s *SGuestDHCP6Server) handleNeighborSolicitation(msg *icmp6.SNeighborSolicitation) error {
- return nil
- }
- func (s *SGuestDHCP6Server) handleNeighborAdvertisement(msg *icmp6.SNeighborAdvertisement) error {
- if msg.IsSolicited {
- //log.Infof("Save mac %s for gw IP %s", msg.SrcMac, msg.TargetAddr.String())
- s.gwMacCache.Set(msg.TargetAddr.String(), msg.SrcMac)
- }
- return nil
- }
- func (s *SGuestDHCP6Server) requestGatewayMac(gwIP net.IP, vlanId uint16) {
- // Solicited-Node Multicast Address, FF02::1:FF00:0/104, 33:33:ff:00:00:00
- destIP := netutils2.IP2SolicitMcastIP(gwIP)
- destMac := netutils2.IP2SolicitMcastMac(gwIP)
- ns := &icmp6.SNeighborSolicitation{
- SBaseICMP6Message: icmp6.SBaseICMP6Message{
- SrcMac: s.ifaceDev.GetHardwareAddr(),
- SrcIP: net.ParseIP(s.ifaceDev.Addr6LinkLocal),
- DstMac: destMac,
- DstIP: destIP,
- Vlan: vlanId,
- },
- TargetAddr: gwIP,
- }
- bytes, err := icmp6.EncodePacket(ns)
- if err != nil {
- log.Errorf("Encode NeighborSolicitation error: %v", err)
- return
- }
- err = s.conn.SendRaw(bytes, destMac)
- if err != nil {
- log.Errorf("Send RouterAdvertisement error: %v", err)
- return
- }
- }
- func (s *SGuestDHCP6Server) sendRouterAdvertisement(vmMac net.HardwareAddr) (bool, error) {
- var conf = s.getConfig(vmMac)
- if conf == nil {
- return false, errors.Wrapf(errors.ErrNotFound, "getConfig %s", vmMac.String())
- }
- if conf != nil && conf.ClientIP6 != nil && conf.Gateway6 != nil {
- gwMacObj := s.gwMacCache.AtomicGet(conf.Gateway6.String())
- if gwMacObj == nil {
- log.Debugf("No mac for gw IP %s, request it", conf.Gateway6.String())
- s.requestGatewayMac(conf.Gateway6, conf.VlanId)
- return false, nil
- }
- gwMac := gwMacObj.(net.HardwareAddr)
- pref := icmp6.PreferenceMedium
- if conf.IsDefaultGW {
- pref = icmp6.PreferenceHigh
- }
- _, ipnet, err := net.ParseCIDR(fmt.Sprintf("%s/%d", conf.ClientIP6, conf.PrefixLen6))
- if err != nil {
- return false, errors.Wrapf(err, "ParseCIDR %s/%d", conf.ClientIP6, conf.PrefixLen6)
- }
- srcIpAddr, _ := netutils.Mac2LinkLocal(gwMac.String())
- gwLinkLocalIP := srcIpAddr.ToIP()
- ra := &icmp6.SRouterAdvertisement{
- SBaseICMP6Message: icmp6.SBaseICMP6Message{
- SrcMac: gwMac,
- // https://www.rfc-editor.org/rfc/rfc4861.html#page-18
- // !!! MUST be the link-local address assigned to the interface from which this message is sent.
- SrcIP: gwLinkLocalIP,
- DstMac: vmMac,
- DstIP: net.ParseIP("ff02::1"),
- },
- CurHopLimit: 64,
- IsManaged: true,
- IsOther: true,
- IsHomeAgent: false,
- Preference: pref,
- RouterLifetime: uint16(options.HostOptions.Dhcp6RouterLifetimeSeconds),
- ReachableTime: 0,
- RetransTimer: 0,
- MTU: uint32(conf.MTU),
- PrefixInfo: []icmp6.SPrefixInfoOption{
- {
- IsOnlink: true,
- IsAutoconf: false,
- Prefix: ipnet.IP,
- PrefixLen: conf.PrefixLen6,
- ValidLifetime: uint32(options.HostOptions.Dhcp6RouterLifetimeSeconds / 2),
- PreferredLifetime: uint32(options.HostOptions.Dhcp6RouterLifetimeSeconds / 4),
- },
- },
- }
- for i := range conf.Routes6 {
- route := conf.Routes6[i]
- if route.Gateway.String() == "::" {
- // on-link routes
- ra.PrefixInfo = append(ra.PrefixInfo, icmp6.SPrefixInfoOption{
- IsOnlink: true,
- IsAutoconf: false,
- Prefix: route.Prefix,
- PrefixLen: route.PrefixLen,
- ValidLifetime: uint32(options.HostOptions.Dhcp6RouterLifetimeSeconds / 2),
- PreferredLifetime: uint32(options.HostOptions.Dhcp6RouterLifetimeSeconds / 4),
- })
- } else if route.Gateway.String() != conf.Gateway6.String() {
- // routes forwarded by router
- ra.RouteInfo = append(ra.RouteInfo, icmp6.SRouteInfoOption{
- RouteLifetime: uint32(options.HostOptions.Dhcp6RouterLifetimeSeconds),
- Prefix: route.Prefix,
- PrefixLen: route.PrefixLen,
- Preference: pref,
- })
- }
- }
- bytes, err := icmp6.EncodePacket(ra)
- if err != nil {
- log.Errorf("Encode RouterAdvertisement error: %v", err)
- return false, errors.Wrapf(err, "EncodePacket")
- }
- err = s.conn.SendRaw(bytes, vmMac)
- if err != nil {
- log.Errorf("Send RouterAdvertisement error: %v", err)
- }
- }
- return true, nil
- }
- func (s *SGuestDHCP6Server) stopRAServer() {
- close(s.raExitCh)
- close(s.raReqCh)
- }
- func (s *SGuestDHCP6Server) handleRARequest(vmMac net.HardwareAddr) {
- vmMacStr := vmMac.String()
- raReq, ok := s.raReqQueue[vmMacStr]
- if !ok {
- raReq = &sRARequest{
- vmMac: vmMac,
- }
- s.raReqQueue[vmMacStr] = raReq
- } else {
- raReq.nextAttempt = time.Time{}
- }
- // send RA immediately
- raReq.attempts++
- succ, err := s.sendRouterAdvertisement(vmMac)
- if err != nil {
- if errors.Cause(err) == errors.ErrNotFound {
- // no such vm, giveup
- delete(s.raReqQueue, vmMacStr)
- return
- }
- log.Errorf("sendRouterAdvertisement error: %v", err)
- }
- if succ {
- raReq.succAttempts++
- }
- if raReq.succAttempts < options.HostOptions.Dhcp6RouterAdvertisementAttempts {
- if raReq.attempts > 2*options.HostOptions.Dhcp6RouterAdvertisementAttempts {
- // giveup
- delete(s.raReqQueue, vmMacStr)
- } else {
- // retry next round
- }
- } else {
- // schedule RA when prefix router lifetime expires
- raReq.attempts = 0
- raReq.succAttempts = 0
- raReq.nextAttempt = time.Now().Add(time.Second * time.Duration(options.HostOptions.Dhcp6RouterLifetimeSeconds/4))
- }
- }
- func (s *SGuestDHCP6Server) startRAServer() {
- // a tiny RA server
- stop := false
- for !stop {
- select {
- case <-s.raExitCh:
- stop = true
- case raReq := <-s.raReqCh:
- // handle RA request
- s.handleRARequest(raReq)
- case <-time.After(time.Second * time.Duration(options.HostOptions.Dhcp6RouterAdvertisementIntervalSecs)):
- // send RA
- for _, raReq := range s.raReqQueue {
- if !raReq.nextAttempt.IsZero() && raReq.nextAttempt.After(time.Now()) {
- continue
- }
- s.handleRARequest(raReq.vmMac)
- }
- }
- }
- }
|