Ai
5 Star 6 Fork 4

zstackio/zstack-vyos

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
dhcp.go 17.15 KB
一键复制 编辑 原始数据 按行查看 历史
shixin.ruan 提交于 2024-07-08 14:31 +08:00 . [root]: use openeuler image for vpc
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
package plugin
import (
"bytes"
"fmt"
"html/template"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"zstack-vyos/server"
"zstack-vyos/utils"
log "github.com/sirupsen/logrus"
)
const (
ADD_DHCP_PATH = "/adddhcp"
REFRESH_DHCP_SERVER_PATH = "/refreshDhcpServer"
START_DHCP_SERVER_PATH = "/startDhcpServer"
STOP_DHCP_SERVER_PATH = "/stopDhcpServer"
REMOVE_DHCP_PATH = "/removedhcp"
DHCPD_BIN_PATH_VYOS_1_1_7 = "/usr/sbin/dhcpd3"
DHCPD_BIN_PATH_VYOS_1_2 = "/usr/sbin/dhcpd"
HOST_HOST_FILE_TEMP = "/tmp/.dhcphosts"
HOST_HOST_FILE = "/etc/hosts"
OMAPI_PORT = 7911
GROUP_FULL = "full"
GROUP_PARTIAL = "partial"
MAX_LEASE_TIME = 691200 /* 8 days */
)
func getDhcpdPath() string {
return filepath.Join(utils.GetZvrRootPath(), "dhcp3")
}
func getDhcpScriptPath() string {
return filepath.Join(utils.GetZvrRootPath(), "keepalived/script/dhcpd.sh")
}
type dhcpInfo struct {
Ip string `json:"ip"`
Mac string `json:"mac"`
Netmask string `json:"netmask"`
Gateway string `json:"gateway"`
Dns []string `json:"dns"`
Hostname string `json:"hostname"`
VrNicMac string `json:"vrNicMac"`
DnsDomain string `json:"dnsDomain"`
IsDefaultL3Network bool `json:"isDefaultL3Network"`
Mtu int `json:"mtu"`
}
type dhcpServer struct {
NicMac string `json:"nicMac"`
Subnet string `json:"subnet"`
Netmask string `json:"netmask"`
Gateway string `json:"gateway"`
DnsServer string `json:"dnsServer"`
DnsDomain string `json:"dnsDomain"`
Mtu int `json:"mtu"`
DhcpInfos []dhcpInfo `json:"dhcpInfos"`
}
type addDhcpCmd struct {
DhcpEntries []dhcpInfo `json:"dhcpEntries"`
Rebuild bool `json:"rebuild"`
}
type removeDhcpCmd struct {
DhcpEntries []dhcpInfo `json:"dhcpEntries"`
}
type dhcpServerCmd struct {
DhcpServers []dhcpServer `json:"dhcpServers"`
}
type DhcpServerStruct struct {
NicMac string
Subnet string
Netmask string
Gateway string
DnsServer string
DnsDomain string
Mtu int
/* dhcp entry info, key is nic.mac */
DhcpInfos map[string]dhcpInfo
}
/* all dhcp server, key is vrNicMac */
var (
DhcpServerEntries = make(map[string]*DhcpServerStruct)
DEFAULT_HOSTS = []string{
"127.0.0.1 localhost",
"::1 ip6-localhost ip6-loopback",
"fe00::0 ip6-localnet",
"ff00::0 ip6-mcastprefix",
"ff02::1 ip6-allnodes",
"ff02::2 ip6-allrouters",
"ff02::3 ip6-allhosts",
"127.0.1.1 vyos #vyatta entry",
}
)
func getDhcpServerPath(nicName string) (pid, conf, lease, tempConf string) {
pid = fmt.Sprintf("%s/%s/%s.pid", getDhcpdPath(), nicName, nicName)
conf = fmt.Sprintf("%s/%s/%s.conf", getDhcpdPath(), nicName, nicName)
lease = fmt.Sprintf("%s/%s/%s.lease", getDhcpdPath(), nicName, nicName)
tempConf = fmt.Sprintf("%s/%s/%s.tempConf", getDhcpdPath(), nicName, nicName)
os.Mkdir(fmt.Sprintf("%s/%s", getDhcpdPath(), nicName), os.ModePerm)
return pid, conf, lease, tempConf
}
func getNicIndex(nicName string) (int, error) {
return strconv.Atoi(nicName[len(nicName)-1:])
}
func getNicOmApiPort(nicName string) int {
idx, err := getNicIndex(nicName)
utils.PanicOnError(err)
return OMAPI_PORT + idx
}
func makeLanName(nicName string) string {
return nicName + "-subnet"
}
const dhcpServerHaTemplate_VYOS_1_1_7 = `# generated by ZStack, don't modify it'
sudo pkill -9 dhcpd3
{{ range .DhcpServers }}
sudo touch {{.LeaseFile}}
sudo chmod 666 {{.LeaseFile}}
sudo truncate -s 0 {{.LeaseFile}}
sudo rm -f {{.PidFile}}
sudo cp {{.TempConf}} {{.ConfFile}}
sudo /usr/sbin/dhcpd3 -pf {{.PidFile}} -cf {{.ConfFile}} -lf {{.LeaseFile}}
{{ end }}
`
const dhcpServerHaTemplate_VYOS_1_2 = `# generated by ZStack, don't modify it'
sudo pkill -9 dhcpd
{{ range .DhcpServers }}
sudo touch {{.LeaseFile}}
sudo chmod 666 {{.LeaseFile}}
sudo truncate -s 0 {{.LeaseFile}}
sudo rm -f {{.PidFile}}
sudo cp {{.TempConf}} {{.ConfFile}}
sudo /usr/sbin/dhcpd -pf {{.PidFile}} -cf {{.ConfFile}} -lf {{.LeaseFile}}
{{ end }}
`
type dhcpServerFiles struct {
ConfFile string
PidFile string
LeaseFile string
TempConf string
}
func getHostNameFromIp(ip string) string {
return strings.Replace(ip, ".", "-", -1)
}
func getHostNameFromIpMac(ip, mac string) string {
return fmt.Sprintf("%s-%s", strings.Replace(ip, ".", "-", -1), strings.Replace(mac, ":", "", -1))
}
func writeDhcpScriptFile() {
var fileList []dhcpServerFiles
var dhcpServerTemplate string
if utils.IsHaEnabled() {
/* generate a temp configure file for ha */
for _, dhcp := range DhcpServerEntries {
nicname, err := utils.GetNicNameByMac(dhcp.NicMac)
utils.PanicOnError(err)
pid, conf, lease, tempConf := getDhcpServerPath(nicname)
getDhcpConfigFile(*dhcp, tempConf, nicname)
fileList = append(fileList, dhcpServerFiles{conf, pid, lease, tempConf})
}
var buf bytes.Buffer
m := map[string]interface{}{}
m["DhcpServers"] = fileList
if utils.Vyos_version == utils.VYOS_1_1_7 {
dhcpServerTemplate = dhcpServerHaTemplate_VYOS_1_1_7
} else {
dhcpServerTemplate = dhcpServerHaTemplate_VYOS_1_2
}
tmpl, err := template.New("haConf").Parse(dhcpServerTemplate)
utils.PanicOnError(err)
err = tmpl.Execute(&buf, m)
utils.PanicOnError(err)
err = ioutil.WriteFile(getDhcpScriptPath(), buf.Bytes(), 0755)
utils.PanicOnError(err)
}
}
/* each interface will have a dhcp server */
func addDhcpHandler(ctx *server.CommandContext) interface{} {
cmd := &addDhcpCmd{}
ctx.GetCommand(cmd)
return addDhcp(cmd)
}
func addDhcp(cmd *addDhcpCmd) interface{} {
for _, entry := range cmd.DhcpEntries {
nicName, err := utils.GetNicNameByMac(entry.VrNicMac)
utils.PanicOnError(err)
omApiPort := getNicOmApiPort(nicName)
group := GROUP_FULL
if !entry.IsDefaultL3Network {
group = GROUP_PARTIAL
}
/* add a entry by OMAPI */
hostName := entry.Hostname
if hostName == "" {
hostName = strings.Replace(entry.Ip, ".", "-", -1)
entry.Hostname = hostName
}
if _, ok := DhcpServerEntries[entry.VrNicMac]; ok {
/* delete the entry which has same ip but different mac */
for _, e := range DhcpServerEntries[entry.VrNicMac].DhcpInfos {
/* for some reason, MN node may send 2 entries with same ip but different macs
so delete old entry if existed */
if e.Ip == entry.Ip {
log.Errorf("[vyos dhcp] found 2 entries with same ip, old: %+v, new: %+v", e, entry)
b := &utils.Bash{
Command: fmt.Sprintf(`omshell << EOF
server localhost
port %d
connect
new host
set hardware-address = %s
open
remove
EOF`, omApiPort, e.Mac),
NoLog: true}
if err = b.Run(); err != nil {
log.Errorf("[vyos dhcp] delete old entry [mac: %s] failed, %s", e.Mac, err)
}
delete(DhcpServerEntries[entry.VrNicMac].DhcpInfos, e.Mac)
}
/* duplicated hostname */
if e.Hostname == hostName {
hostName = getHostNameFromIpMac(entry.Ip, entry.Mac)
entry.Hostname = hostName
}
}
DhcpServerEntries[entry.VrNicMac].DhcpInfos[entry.Mac] = entry
} else {
log.Errorf("[vyos dhcp] can not save dhcp entry[%+v] to buffer", entry)
continue
}
/* add a entry by OMAPI */
if entry.IsDefaultL3Network {
b := &utils.Bash{
Command: fmt.Sprintf(`omshell << EOF
server localhost
port %d
connect
new host
set name = "%s"
set hardware-address = %s
set hardware-type = 1
set ip-address = %s
set group = "%s"
create
EOF`, omApiPort, hostName, entry.Mac, entry.Ip, group),
NoLog: true}
if err = b.Run(); err != nil {
log.Errorf("[vyos dhcp] add new entry [mac: %+v] failed, %s", entry, err)
}
} else {
b := &utils.Bash{
Command: fmt.Sprintf(`omshell << EOF
server localhost
port %d
connect
new host
set name = "%s"
set hardware-address = %s
set hardware-type = 1
set ip-address = %s
set group = "%s"
create
EOF`, omApiPort, hostName, entry.Mac, entry.Ip, group),
NoLog: true}
if err = b.Run(); err != nil {
log.Errorf("[vyos dhcp] add new entry [mac: %+v] failed, %s", entry, err)
}
}
}
writeDhcpScriptFile()
return nil
}
func stopAllDhcpServers() {
var progname string
if utils.Vyos_version == utils.VYOS_1_1_7 {
progname = "dhcpd3"
} else {
progname = "dhcpd"
}
bash := &utils.Bash{
Command: fmt.Sprintf("pkill -9 %s; rm -rf %s/*", progname, getDhcpdPath()),
Sudo: true,
}
bash.Run()
}
func stopDhcpServer(pidFile string) {
b := &utils.Bash{
Command: fmt.Sprintf("kill -9 $(cat %s)", pidFile),
Sudo: true,
}
b.Run()
}
/* each interface will have a dhcp server */
func refreshDhcpServer(ctx *server.CommandContext) interface{} {
cmd := &dhcpServerCmd{}
ctx.GetCommand(cmd)
stopAllDhcpServers()
/* no dhcp servers now */
if len(cmd.DhcpServers) == 0 {
return nil
}
/* empty the dhcp entries */
DhcpServerEntries = make(map[string]*DhcpServerStruct)
/* start dhcp servers */
for _, server := range cmd.DhcpServers {
startDhcpServer(server)
}
writeDhcpScriptFile()
return nil
}
/* each interface will have a dhcp server */
func startDhcpServerCmd(ctx *server.CommandContext) interface{} {
cmd := &dhcpServerCmd{}
ctx.GetCommand(cmd)
server := cmd.DhcpServers[0]
nicname, err := utils.GetNicNameByMac(server.NicMac)
utils.PanicOnError(err)
pidFile, _, _, _ := getDhcpServerPath(nicname)
stopDhcpServer(pidFile)
startDhcpServer(server)
writeDhcpScriptFile()
return nil
}
/* each interface will have a dhcp server */
func stopDhcpServerCmd(ctx *server.CommandContext) interface{} {
cmd := &dhcpServerCmd{}
ctx.GetCommand(cmd)
server := cmd.DhcpServers[0]
nicname, err := utils.GetNicNameByMac(server.NicMac)
utils.PanicOnError(err)
pidFile, _, _, _ := getDhcpServerPath(nicname)
stopDhcpServer(pidFile)
delete(DhcpServerEntries, server.NicMac)
writeDhcpScriptFile()
return nil
}
func removeDhcpHandler(ctx *server.CommandContext) interface{} {
cmd := &removeDhcpCmd{}
ctx.GetCommand(cmd)
for _, entry := range cmd.DhcpEntries {
nicname, err := utils.GetNicNameByMac(entry.VrNicMac)
utils.PanicOnError(err)
omApiPort := getNicOmApiPort(nicname)
/* add a entry by OMAPI */
b := &utils.Bash{
Command: fmt.Sprintf(`omshell << EOF
server localhost
port %d
connect
new host
set hardware-address = %s
open
remove
EOF`, omApiPort, entry.Mac),
NoLog: true}
err = b.Run()
/* remove info from buffered dhcp server info */
if _, ok := DhcpServerEntries[entry.VrNicMac]; ok {
delete(DhcpServerEntries[entry.VrNicMac].DhcpInfos, entry.Mac)
}
}
writeDhcpScriptFile()
return nil
}
const dhcpServerTemplate = `# generated by ZStack, don't modify it'
ddns-update-style none;
omapi-port {{.OMAPIPort}};
log-facility local3;
shared-network {{.SubnetName}} {
authoritative;
subnet {{.Subnet}} netmask {{.NetMask}} {
default-lease-time {{.MaxLeaseTime}};
max-lease-time {{.MaxLeaseTime}};
server-identifier {{.DnsServer}};
option subnet-mask {{.NetMask}};
option interface-mtu {{.Mtu}};
option broadcast-address {{.BroadCastAddress}};
use-host-decl-names on;
group full {
option domain-name-servers {{.DnsServer}};
option routers {{.Gateway}};
{{ if ne .DnsDomain "" }}option domain-name "{{.DnsDomain}}";{{ end }}
{{ range .FullEntries }}
host {{.HostName}} {
option host-name "{{.HostName}}";
fixed-address {{.Ip}};
hardware ethernet {{.Mac}};
}
{{ end }}
}
group partial {
{{ range .PartEntries }}
host {{.HostName}} {
fixed-address {{.Ip}};
hardware ethernet {{.Mac}};
}
{{ end }}
}
}
}
`
func setDhcpFirewallRules(nicName string) error {
table := utils.NewIpTables(utils.FirewallTable)
var rules []*utils.IpTableRule
rule := utils.NewIpTableRule(utils.GetRuleSetName(nicName, utils.RULESET_LOCAL))
rule.SetAction(utils.IPTABLES_ACTION_RETURN).SetComment(utils.SystemTopRule)
rule.SetProto(utils.IPTABLES_PROTO_UDP).SetDstPort("67")
rules = append(rules, rule)
rule = utils.NewIpTableRule(utils.GetRuleSetName(nicName, utils.RULESET_LOCAL))
rule.SetAction(utils.IPTABLES_ACTION_RETURN).SetComment(utils.SystemTopRule)
rule.SetProto(utils.IPTABLES_PROTO_UDP).SetDstPort("68")
rules = append(rules, rule)
rule = utils.NewIpTableRule(utils.GetRuleSetName(nicName, utils.RULESET_LOCAL))
rule.SetAction(utils.IPTABLES_ACTION_RETURN).SetComment(utils.SystemTopRule)
rule.SetProto(utils.IPTABLES_PROTO_UDP).SetDstPort("53")
rules = append(rules, rule)
rule = utils.NewIpTableRule(utils.GetRuleSetName(nicName, utils.RULESET_LOCAL))
rule.SetAction(utils.IPTABLES_ACTION_RETURN).SetComment(utils.SystemTopRule)
rule.SetProto(utils.IPTABLES_PROTO_TCP).SetDstPort("68")
rules = append(rules, rule)
table.AddIpTableRules(rules)
return table.Apply()
}
func makeDhcpFirewallRuleDescription(nicname string) string {
return fmt.Sprintf("DHCP-for-%s", nicname)
}
func getDhcpConfigFile(dhcp DhcpServerStruct, confFile string, nicname string) {
subnet := strings.Split(dhcp.Subnet, "/")
dhcpServer := map[string]interface{}{}
dhcpServer["OMAPIPort"] = getNicOmApiPort(nicname)
dhcpServer["SubnetName"] = makeLanName(nicname)
dhcpServer["Subnet"] = subnet[0]
dhcpServer["NetMask"] = dhcp.Netmask
dhcpServer["BroadCastAddress"] = utils.GetBroadcastIpFromNetwork(dhcp.Gateway, dhcp.Netmask)
dhcpServer["Mtu"] = dhcp.Mtu
dhcpServer["Gateway"] = dhcp.Gateway
dhcpServer["DnsServer"] = dhcp.DnsServer
dhcpServer["MaxLeaseTime"] = MAX_LEASE_TIME
dhcpServer["DnsDomain"] = dhcp.DnsDomain
/* nics which are default nic of the vm, will get ip/gateway/dns domain */
var fullEntries []map[string]interface{}
/* nics which are not default nic of the vm, will get ip */
var partEntries []map[string]interface{}
for _, info := range dhcp.DhcpInfos {
entry := map[string]interface{}{}
entry["HostName"] = info.Hostname
entry["Ip"] = info.Ip
entry["Mac"] = info.Mac
if info.IsDefaultL3Network {
fullEntries = append(fullEntries, entry)
} else {
partEntries = append(partEntries, entry)
}
}
dhcpServer["FullEntries"] = fullEntries
dhcpServer["PartEntries"] = partEntries
var buf bytes.Buffer
tmpl, err := template.New("conf").Parse(dhcpServerTemplate)
utils.PanicOnError(err)
err = tmpl.Execute(&buf, dhcpServer)
utils.PanicOnError(err)
err = ioutil.WriteFile(confFile, buf.Bytes(), 0755)
utils.PanicOnError(err)
}
func startDhcpServer(dhcp dhcpServer) {
nicname, err := utils.GetNicNameByMac(dhcp.NicMac)
utils.PanicOnError(err)
pidFile, conFile, leaseFile, _ := getDhcpServerPath(nicname)
/* lease file must be created first */
utils.CreateFileIfNotExists(leaseFile, os.O_WRONLY|os.O_APPEND, os.ModePerm)
os.Truncate(leaseFile, 0)
utils.CreateFileIfNotExists(conFile, os.O_WRONLY|os.O_APPEND, os.ModePerm)
os.Truncate(conFile, 0)
os.Remove(pidFile)
hostNameMap := make(map[string]string)
dhcpStruct := DhcpServerStruct{dhcp.NicMac, dhcp.Subnet, dhcp.Netmask, dhcp.Gateway,
dhcp.DnsServer, dhcp.DnsDomain, dhcp.Mtu, map[string]dhcpInfo{}}
for _, info := range dhcp.DhcpInfos {
/* if there is duplicated hostname */
hostName := info.Hostname
if hostName == "" {
hostName = getHostNameFromIp(info.Ip)
info.Hostname = hostName
}
if _, ok := hostNameMap[hostName]; ok {
info.Hostname = getHostNameFromIpMac(info.Ip, info.Mac)
hostNameMap[info.Hostname] = info.Hostname
} else {
hostNameMap[hostName] = hostName
}
dhcpStruct.DhcpInfos[info.Mac] = info
}
getDhcpConfigFile(dhcpStruct, conFile, nicname)
/* start dhcp server for nic */
var dhcpdBinPath string
if utils.Vyos_version == utils.VYOS_1_1_7 {
dhcpdBinPath = DHCPD_BIN_PATH_VYOS_1_1_7
} else {
dhcpdBinPath = DHCPD_BIN_PATH_VYOS_1_2
}
b := &utils.Bash{
Command: fmt.Sprintf("sudo %s -pf %s -cf %s -lf %s", dhcpdBinPath, pidFile, conFile, leaseFile),
}
err = b.Run()
if utils.IsSkipVyosIptables() {
err = setDhcpFirewallRules(nicname)
utils.PanicOnError(err)
} else {
tree := server.NewParserFromShowConfiguration().Tree
des := makeDhcpFirewallRuleDescription(nicname)
if r := tree.FindFirewallRuleByDescription(nicname, "local", des); r == nil {
tree.SetFirewallOnInterface(nicname, "local",
fmt.Sprintf("description %v", des),
"destination port 67-68",
"protocol udp",
"action accept",
)
}
des = makeDnsFirewallRuleDescription(nicname)
if r := tree.FindFirewallRuleByDescription(nicname, "local", des); r == nil {
/* dhcp will set vpc as dns forwarder */
tree.SetFirewallOnInterface(nicname, "local",
fmt.Sprintf("description %v", des),
"destination port 53",
"protocol tcp_udp",
"action accept",
)
}
tree.AttachFirewallToInterface(nicname, "local")
tree.Apply(false)
}
delete(DhcpServerEntries, dhcp.NicMac)
DhcpServerEntries[dhcp.NicMac] = &dhcpStruct
addDnsNic(nicname)
}
func DhcpEntryPoint() {
os.Mkdir(getDhcpdPath(), os.ModePerm)
server.RegisterAsyncCommandHandler(ADD_DHCP_PATH, server.VyosLock(addDhcpHandler))
server.RegisterAsyncCommandHandler(REMOVE_DHCP_PATH, server.VyosLock(removeDhcpHandler))
server.RegisterAsyncCommandHandler(REFRESH_DHCP_SERVER_PATH, server.VyosLock(refreshDhcpServer))
server.RegisterAsyncCommandHandler(START_DHCP_SERVER_PATH, server.VyosLock(startDhcpServerCmd))
server.RegisterAsyncCommandHandler(STOP_DHCP_SERVER_PATH, server.VyosLock(stopDhcpServerCmd))
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/zstackio/zstack-vyos.git
git@gitee.com:zstackio/zstack-vyos.git
zstackio
zstack-vyos
zstack-vyos
master

搜索帮助