| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- // 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"
- "sync"
- "time"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/onecloud/pkg/util/dhcp"
- )
- type SRelayCache6 struct {
- peerMac net.HardwareAddr
- peerUdp *net.UDPAddr
- linkAddr net.IP
- peerAddr net.IP
- timer time.Time
- }
- type SDHCP6Relay struct {
- server *dhcp.DHCP6Server
- OnRecv recvFunc
- guestDHCPConn *dhcp.Conn
- ipv6srcAddr net.IP
- destaddr net.IP
- destport int
- cache sync.Map
- }
- func NewDHCP6Relay(guestDHCPConn *dhcp.Conn, config *SDHCPRelayUpstream) (*SDHCP6Relay, error) {
- relay := new(SDHCP6Relay)
- relay.guestDHCPConn = guestDHCPConn
- log.Infof("Set Relay To Address: %s, %d", config.IP, config.Port)
- relay.destaddr = net.ParseIP(config.IP)
- relay.destport = config.Port
- relay.cache = sync.Map{}
- return relay, nil
- }
- func (r *SDHCP6Relay) Setup(addr string) error {
- r.ipv6srcAddr = net.ParseIP(addr)
- if len(r.ipv6srcAddr) == 0 {
- return fmt.Errorf("wrong ip address %s", addr)
- }
- log.Infof("DHCP6 Relay Setup on %s %d", addr, DEFAULT_DHCP6_RELAY_PORT)
- var err error
- r.server, err = dhcp.NewDHCP6Server3(addr, DEFAULT_DHCP6_RELAY_PORT)
- if err != nil {
- return errors.Wrapf(err, "NewDHCP6Server3")
- }
- go r.server.ListenAndServe(r)
- return nil
- }
- func (r *SDHCP6Relay) ServeDHCP(pkt dhcp.Packet, cliMac net.HardwareAddr, addr *net.UDPAddr) (dhcp.Packet, []string, error) {
- pkg, err := r.serveDHCPInternal(pkt, addr)
- return pkg, nil, err
- }
- func (r *SDHCP6Relay) OnRecvICMP6(pkt dhcp.Packet) error {
- // null operation
- return nil
- }
- func getSessionKey(tid uint32, clientID []byte) string {
- return fmt.Sprintf("%x-%x", tid, clientID)
- }
- func (r *SDHCP6Relay) serveDHCPInternal(pkt dhcp.Packet, _ *net.UDPAddr) (dhcp.Packet, error) {
- if pkt.Type6() != dhcp.DHCPV6_RELAY_REPL {
- return nil, errors.Wrapf(errors.ErrInvalidFormat, "not a valid relay reply message")
- }
- hopCount := pkt.HopCount()
- decapPkt, err := dhcp.DecapDHCP6RelayMsg(pkt)
- if err != nil {
- return nil, errors.Wrapf(err, "DecapDHCP6RelayMsg")
- }
- tid, err := decapPkt.TID6()
- if err != nil {
- return nil, errors.Wrapf(err, "TID6")
- }
- cliID, err := decapPkt.ClientID()
- if err != nil {
- return nil, errors.Wrapf(err, "ClientID")
- }
- key := getSessionKey(tid, cliID)
- v, ok := r.cache.Load(key)
- if ok {
- r.cache.Delete(key)
- val := v.(*SRelayCache6)
- if hopCount > 1 {
- pkt.SetHopCount(hopCount - 1)
- pkt.SetLinkAddr(val.linkAddr)
- pkt.SetPeerAddr(val.peerAddr)
- if err := r.server.GetConn().SendDHCP(pkt, val.peerUdp, val.peerMac); err != nil {
- log.Errorf("send relay packet to client %s %s failed: %s", val.peerUdp, val.peerMac, err)
- }
- } else {
- pkt = decapPkt
- if err := r.guestDHCPConn.SendDHCP(pkt, val.peerUdp, val.peerMac); err != nil {
- log.Errorf("last hop send dhcp packet to client %s %s failed: %s", val.peerUdp, val.peerMac, err)
- }
- }
- }
- return nil, nil
- }
- func (r *SDHCP6Relay) Relay(pkt dhcp.Packet, cliMac net.HardwareAddr, cliAddr *net.UDPAddr) (dhcp.Packet, error) {
- if cliAddr.IP.Equal(r.ipv6srcAddr) {
- // come from local? ignore it
- return nil, nil
- }
- log.Infof("Receive IPv6 DHCPRequest FROM %s, relay to upstream %s:%d", cliAddr.IP, r.destaddr, r.destport)
- if pkt.Type6() == dhcp.DHCPV6_RELAY_REPL {
- return nil, errors.Wrapf(errors.ErrInvalidFormat, "cannot relay a reply message")
- }
- session := &SRelayCache6{
- peerMac: cliMac,
- peerUdp: cliAddr,
- timer: time.Now(),
- }
- hopCount := uint8(0)
- if pkt.Type6() == dhcp.DHCPV6_RELAY_FORW {
- hopCount = pkt.HopCount()
- session.linkAddr = pkt.LinkAddr()
- session.peerAddr = pkt.PeerAddr()
- } else {
- pkt = dhcp.EncapDHCP6RelayMsg(pkt)
- }
- pkt.SetHopCount(hopCount + 1)
- pkt.SetLinkAddr(r.ipv6srcAddr)
- pkt.SetPeerAddr(cliAddr.IP)
- tid, err := pkt.TID6()
- if err != nil {
- return nil, errors.Wrapf(err, "TID6")
- }
- cliID, err := pkt.ClientID()
- if err != nil {
- return nil, errors.Wrapf(err, "ClientID")
- }
- sessionKey := getSessionKey(tid, cliID)
- r.cache.Store(sessionKey, session)
- err = r.server.GetConn().SendDHCP(pkt, &net.UDPAddr{IP: r.destaddr, Port: r.destport}, nil)
- if err != nil {
- return nil, errors.Wrapf(err, "SendDHCP to upstream %s:%d", r.destaddr, r.destport)
- }
- return nil, nil
- }
|