diff --git a/pkg/baremetal/pxe/dhcp.go b/pkg/baremetal/pxe/dhcp.go index 852add8b28..3c80772651 100644 --- a/pkg/baremetal/pxe/dhcp.go +++ b/pkg/baremetal/pxe/dhcp.go @@ -68,25 +68,29 @@ type dhcpRequest struct { netConfig *types.SNetworkConfig } -func (h *DHCPHandler) ServeDHCP(pkt dhcp.Packet, _ *net.UDPAddr, _ *net.Interface) (dhcp.Packet, error) { +func (h *DHCPHandler) ServeDHCP(pkt dhcp.Packet, _ *net.UDPAddr, _ *net.Interface) (dhcp.Packet, []string, error) { req, err := h.newRequest(pkt, h.baremetalManager) if err != nil { log.Errorf("[DHCP] new request by packet error: %v", err) - return nil, err + return nil, nil, err } log.V(4).Debugf("[DHCP] request packet: %#v", req) if req.RelayAddr.String() == "0.0.0.0" { - return nil, fmt.Errorf("Request not from a DHCP relay, ignore mac: %s", req.ClientMac) + return nil, nil, fmt.Errorf("Request not from a DHCP relay, ignore mac: %s", req.ClientMac) } - conf, err := req.fetchConfig(h.baremetalManager.GetClientSession()) + conf, targets, err := req.fetchConfig(h.baremetalManager.GetClientSession()) if err != nil { - return nil, err + return nil, nil, err } if conf == nil { - return nil, fmt.Errorf("Empty packet config") + return nil, nil, fmt.Errorf("Empty packet config") } - return dhcp.MakeReplyPacket(pkt, conf) + pkg, err := dhcp.MakeReplyPacket(pkt, conf) + if err != nil { + return nil, nil, errors.Wrap(err, "dhcp.MakeReplyPacket") + } + return pkg, targets, nil } func (h *DHCPHandler) newRequest(pkt dhcp.Packet, man IBaremetalManager) (*dhcpRequest, error) { @@ -173,33 +177,43 @@ func formatUuidString(uuidBytes []byte) string { }, "-") } -func (req *dhcpRequest) fetchConfig(session *mcclient.ClientSession) (*dhcp.ResponseConfig, error) { +func (req *dhcpRequest) fetchConfig(session *mcclient.ClientSession) (*dhcp.ResponseConfig, []string, error) { // 1. find_network_conf netConf, err := req.findNetworkConf(session, false) if err != nil { - return nil, err + return nil, nil, err } req.netConfig = netConf + var dhcpAddrList []string + if len(netConf.GuestDhcp) > 0 { + dhcpAddrList = strings.Split(netConf.GuestDhcp, ",") + } + log.Debugf("dhcpAddrList %s", dhcpAddrList) // TODO: set cache for netConf if req.isPXERequest() { if !o.Options.EnablePxeBoot { - return nil, errors.Error("PXE Boot disabled") + return nil, nil, errors.Error("PXE Boot disabled") } // handle PXE DHCP request log.Infof("DHCP relay from %s(%s) for %s, find matched networks: %#v", req.RelayAddr, req.ClientAddr, req.ClientMac, netConf) bmDesc, err := req.createOrUpdateBaremetal(session) if err != nil { - return nil, err + return nil, nil, err } err = req.doInitBaremetalAdminNetif(bmDesc) if err != nil { - return nil, err + return nil, nil, err } // always response PXE request // let bootloader decide boot local or remote // if req.baremetalInstance.NeedPXEBoot() { - return req.baremetalInstance.GetPXEDHCPConfig(req.ClientArch) + conf, err := req.baremetalInstance.GetPXEDHCPConfig(req.ClientArch) + if err != nil { + return nil, nil, errors.Wrap(err, "req.baremetalInstance.GetPXEDHCPConfig") + } + return conf, dhcpAddrList, nil + // } // ignore // log.Warningf("No need to pxeboot, ignore the request ...(mac:%s guid:%s)", req.ClientMac, req.ClientGuid) @@ -213,7 +227,7 @@ func (req *dhcpRequest) fetchConfig(session *mcclient.ClientSession) (*dhcp.Resp // from guestdhcp import GuestDHCPHelperTask // task = GuestDHCPHelperTask(self) // task.start() - return nil, nil + return nil, nil, nil } req.baremetalInstance = bmInstance ipmiNic := req.baremetalInstance.GetIPMINic(req.ClientMac) @@ -221,16 +235,20 @@ func (req *dhcpRequest) fetchConfig(session *mcclient.ClientSession) (*dhcp.Resp err = req.baremetalInstance.InitAdminNetif( req.ClientMac, req.netConfig.WireId, api.NIC_TYPE_IPMI, api.NETWORK_TYPE_IPMI, false, "") if err != nil { - return nil, err + return nil, nil, err } } else { err = req.baremetalInstance.RegisterNetif(req.ClientMac, req.netConfig.WireId) if err != nil { log.Errorf("RegisterNetif error: %v", err) - return nil, err + return nil, nil, err } } - return req.baremetalInstance.GetDHCPConfig(req.ClientMac) + conf, err := req.baremetalInstance.GetDHCPConfig(req.ClientMac) + if err != nil { + return nil, nil, errors.Wrap(err, "req.baremetalInstance.GetDHCPConfig") + } + return conf, dhcpAddrList, nil } } @@ -243,8 +261,14 @@ func (req *dhcpRequest) findNetworkConf(session *mcclient.ClientSession, filterU fmt.Sprintf("guest_gateway.equals(%s)", req.RelayAddr)), "filter.0") params.Add(jsonutils.NewString( - fmt.Sprintf("guest_dhcp.equals(%s)", req.RelayAddr)), + fmt.Sprintf("guest_dhcp.startswith('%s,')", req.RelayAddr)), "filter.1") + params.Add(jsonutils.NewString( + fmt.Sprintf("guest_dhcp.contains(',%s,')", req.RelayAddr)), + "filter.2") + params.Add(jsonutils.NewString( + fmt.Sprintf("guest_dhcp.endswith(',%s')", req.RelayAddr)), + "filter.3") params.Add(jsonutils.JSONTrue, "filter_any") } params.Add(jsonutils.JSONTrue, "is_on_premise") @@ -259,9 +283,16 @@ func (req *dhcpRequest) findNetworkConf(session *mcclient.ClientSession, filterU } return nil, fmt.Errorf("DHCP relay from %s(%s) for %s, find no match network", req.RelayAddr, req.ClientAddr, req.ClientMac) } - + idx := 0 + for i := range ret.Data { + netType, _ := ret.Data[i].GetString("server_type") + if netType == api.NETWORK_TYPE_PXE { + idx = i + break + } + } network := types.SNetworkConfig{} - err = ret.Data[0].Unmarshal(&network) + err = ret.Data[idx].Unmarshal(&network) return &network, err } diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 1b0edfe075..fb0ad507f0 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -82,7 +82,8 @@ type SNetwork struct { GuestIpMask int8 `nullable:"false" list:"user" update:"user" create:"required"` // Column(TINYINT, nullable=False) GuestGateway string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True) GuestDns string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True) - GuestDhcp string `width:"16" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True) + // allow multiple dhcp, seperated by "," + GuestDhcp string `width:"64" charset:"ascii" nullable:"true" list:"user" update:"user" create:"optional"` // Column(VARCHAR(16, charset='ascii'), nullable=True) GuestDomain string `width:"128" charset:"ascii" nullable:"true" get:"user" update:"user"` // Column(VARCHAR(128, charset='ascii'), nullable=True) @@ -1212,8 +1213,17 @@ func (manager *SNetworkManager) ValidateCreateData(ctx context.Context, userCred for _, key := range []string{"guest_gateway", "guest_dns", "guest_dhcp"} { ipStr, _ := data.GetString(key) - if len(ipStr) > 0 && !regutils.MatchIPAddr(ipStr) { - return nil, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr) + if len(ipStr) > 0 { + if key == "guest_dhcp" { + ipList := strings.Split(ipStr, ",") + for _, ipstr := range ipList { + if !regutils.MatchIPAddr(ipstr) { + return nil, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipstr) + } + } + } else if !regutils.MatchIPAddr(ipStr) { + return nil, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr) + } } } @@ -1430,9 +1440,18 @@ func (self *SNetwork) ValidateUpdateData(ctx context.Context, userCred mcclient. if len(ipStr) > 0 { if self.isManaged() { return nil, httperrors.NewForbiddenError("Cannot update a managed network") + } + if key == "guest_dhcp" { + ipList := strings.Split(ipStr, ",") + for _, ipstr := range ipList { + if !regutils.MatchIPAddr(ipstr) { + return nil, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipstr) + } + } } else if !regutils.MatchIPAddr(ipStr) { return nil, httperrors.NewInputParameterError("%s: Invalid IP address %s", key, ipStr) } + } } diff --git a/pkg/hostman/hostinfo/hostdhcp/dhcprelay.go b/pkg/hostman/hostinfo/hostdhcp/dhcprelay.go index 53aa96b517..0a5f77b455 100644 --- a/pkg/hostman/hostinfo/hostdhcp/dhcprelay.go +++ b/pkg/hostman/hostinfo/hostdhcp/dhcprelay.go @@ -83,7 +83,12 @@ func (r *SDHCPRelay) Setup(addr string) error { return nil } -func (r *SDHCPRelay) ServeDHCP(pkt dhcp.Packet, _ *net.UDPAddr, intf *net.Interface) (dhcp.Packet, error) { +func (r *SDHCPRelay) ServeDHCP(pkt dhcp.Packet, addr *net.UDPAddr, intf *net.Interface) (dhcp.Packet, []string, error) { + pkg, err := r.serveDHCPInternal(pkt, addr, intf) + return pkg, nil, err +} + +func (r *SDHCPRelay) serveDHCPInternal(pkt dhcp.Packet, _ *net.UDPAddr, intf *net.Interface) (dhcp.Packet, error) { log.Infof("DHCP Relay Reply TO %s", pkt.CHAddr()) v, ok := r.cache.Load(pkt.TransactionID()) if ok { diff --git a/pkg/hostman/hostinfo/hostdhcp/dhcpserver.go b/pkg/hostman/hostinfo/hostdhcp/dhcpserver.go index e945ae3e11..5b0530a66c 100644 --- a/pkg/hostman/hostinfo/hostdhcp/dhcpserver.go +++ b/pkg/hostman/hostinfo/hostdhcp/dhcpserver.go @@ -159,7 +159,12 @@ func (s *SGuestDHCPServer) IsDhcpPacket(pkt dhcp.Packet) bool { return pkt != nil && (pkt.Type() == dhcp.Request || pkt.Type() == dhcp.Discover) } -func (s *SGuestDHCPServer) ServeDHCP(pkt dhcp.Packet, addr *net.UDPAddr, intf *net.Interface) (dhcp.Packet, error) { +func (s *SGuestDHCPServer) ServeDHCP(pkt dhcp.Packet, addr *net.UDPAddr, intf *net.Interface) (dhcp.Packet, []string, error) { + pkg, err := s.serveDHCPInternal(pkt, addr, intf) + return pkg, nil, err +} + +func (s *SGuestDHCPServer) serveDHCPInternal(pkt dhcp.Packet, addr *net.UDPAddr, intf *net.Interface) (dhcp.Packet, error) { if !s.IsDhcpPacket(pkt) { return nil, nil } diff --git a/pkg/util/dhcp/server.go b/pkg/util/dhcp/server.go index 1d56d577e7..ae77c2868a 100644 --- a/pkg/util/dhcp/server.go +++ b/pkg/util/dhcp/server.go @@ -87,7 +87,7 @@ func NewDHCPServer2(iface string, dhcpServerPort, dhcpRelayPort uint16) (*DHCPSe } type DHCPHandler interface { - ServeDHCP(pkt Packet, addr *net.UDPAddr, intf *net.Interface) (Packet, error) + ServeDHCP(pkt Packet, addr *net.UDPAddr, intf *net.Interface) (Packet, []string, error) } func (s *DHCPServer) ListenAndServe(handler DHCPHandler) error { @@ -117,7 +117,7 @@ func (s *DHCPServer) serveDHCP(handler DHCPHandler) error { } }() - resp, err := handler.ServeDHCP(pkt, addr, intf) + resp, targets, err := handler.ServeDHCP(pkt, addr, intf) if err != nil { log.Warningf("[DHCP] handler serve error: %v", err) return @@ -126,6 +126,18 @@ func (s *DHCPServer) serveDHCP(handler DHCPHandler) error { // log.Warningf("[DHCP] hander response null packet") return } + if len(targets) > 0 { + targetUdpAddr := *addr + for _, target := range targets { + log.Debugf("[DHCP] Send packet back to %s", target) + targetUdpAddr.IP = net.ParseIP(target) + resp.SetGIAddr(targetUdpAddr.IP) + if err = s.conn.SendDHCP(resp, &targetUdpAddr, mac, intf); err != nil { + log.Errorf("[DHCP] failed to response packet for %s: %v", pkt.CHAddr(), err) + } + } + return + } //log.Debugf("[DHCP] send response packet: %s to interface: %#v", resp.DebugString(), intf) if err = s.conn.SendDHCP(resp, addr, mac, intf); err != nil { log.Errorf("[DHCP] failed to response packet for %s: %v", pkt.CHAddr(), err)