Skip to content

Commit 847b63b

Browse files
committed
network add: enable adding DHCP hosts
This makes it possible to add DHCP hosts entries directly to a new network. At least in an Ubuntu Focal server environment, this performs much better than adding a network and then adding hosts entries.
1 parent d0a5d36 commit 847b63b

File tree

2 files changed

+79
-15
lines changed

2 files changed

+79
-15
lines changed

cmd/network_add.go

+43-9
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
package cmd
22

33
import (
4+
"fmt"
5+
"net"
6+
47
libvirtxml "github.com/libvirt/libvirt-go-xml"
58
log "github.com/sirupsen/logrus"
69
"github.com/spf13/cobra"
7-
"net"
10+
11+
"github.com/LINBIT/virter/internal/virter"
812
)
913

1014
func networkAddCommand() *cobra.Command {
1115
var forward string
1216
var dhcp bool
17+
var dhcpMAC string
18+
var dhcpID uint
19+
var dhcpCount uint
1320
var network string
1421
var domain string
1522

1623
addCmd := &cobra.Command{
1724
Use: "add <name>",
1825
Short: "Add a new network",
19-
Long: `Add a new network. VMs can be attached to such a network in addition to the default network used by virter.`,
26+
Long: `Add a new network. VMs can be attached to such a network in addition to the default network used by virter. DHCP entries can be added directly to the new network.`,
2027
Args: cobra.ExactArgs(1),
2128
Run: func(cmd *cobra.Command, args []string) {
2229
v, err := InitVirter()
@@ -41,13 +48,7 @@ func networkAddCommand() *cobra.Command {
4148

4249
var dhcpDesc *libvirtxml.NetworkDHCP
4350
if dhcp {
44-
start := nextIP(ip)
45-
end := previousIP(broadcastAddress(n))
46-
47-
n.Network()
48-
dhcpDesc = &libvirtxml.NetworkDHCP{
49-
Ranges: []libvirtxml.NetworkDHCPRange{{Start: start.String(), End: end.String()}},
50-
}
51+
dhcpDesc = buildNetworkDHCP(ip, n, dhcpMAC, dhcpID, dhcpCount)
5152
}
5253

5354
addressesDesc = append(addressesDesc, libvirtxml.NetworkIP{
@@ -82,10 +83,43 @@ func networkAddCommand() *cobra.Command {
8283
addCmd.Flags().StringVarP(&forward, "forward-mode", "m", "", "Set the forward mode, for example 'nat'")
8384
addCmd.Flags().StringVarP(&network, "network-cidr", "n", "", "Configure the network range (IPv4) in CIDR notation. The IP will be assigned to the host device.")
8485
addCmd.Flags().BoolVarP(&dhcp, "dhcp", "p", false, "Configure DHCP. Use together with '--network-cidr'. DHCP range is configured starting from --network-cidr+1 until the broadcast address")
86+
addCmd.Flags().StringVarP(&dhcpMAC, "dhcp-mac", "", virter.QemuBaseMAC().String(), "Base MAC address to which ID is added. The default can be used to populate a virter access network")
87+
addCmd.Flags().UintVarP(&dhcpID, "dhcp-id", "", 0, "ID which determines the MAC and IP addresses to associate")
88+
addCmd.Flags().UintVar(&dhcpCount, "dhcp-count", 0, "Number of host entries to add")
8589
addCmd.Flags().StringVarP(&domain, "domain", "d", "", "Configure DNS names for the network")
8690
return addCmd
8791
}
8892

93+
func buildNetworkDHCP(ip net.IP, n *net.IPNet, dhcpMAC string, dhcpID uint, dhcpCount uint) *libvirtxml.NetworkDHCP {
94+
start := nextIP(ip)
95+
end := previousIP(broadcastAddress(n))
96+
97+
baseMAC, err := net.ParseMAC(dhcpMAC)
98+
if err != nil {
99+
log.Fatal(err)
100+
}
101+
102+
networkBaseIP := ip.Mask(n.Mask)
103+
hosts := make([]libvirtxml.NetworkDHCPHost, dhcpCount)
104+
for i := uint(0); i < dhcpCount; i++ {
105+
id := dhcpID + i
106+
mac, err := virter.AddToMAC(baseMAC, id)
107+
if err != nil {
108+
log.Fatal(err)
109+
}
110+
111+
hosts[i] = libvirtxml.NetworkDHCPHost{
112+
MAC: mac.String(),
113+
IP: fmt.Sprint(virter.AddToIP(networkBaseIP, id)),
114+
}
115+
}
116+
117+
return &libvirtxml.NetworkDHCP{
118+
Ranges: []libvirtxml.NetworkDHCPRange{{Start: start.String(), End: end.String()}},
119+
Hosts: hosts,
120+
}
121+
}
122+
89123
func nextIP(ip net.IP) net.IP {
90124
dup := make(net.IP, len(ip))
91125
copy(dup, ip)

internal/virter/dhcp.go

+36-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package virter
22

33
import (
44
"fmt"
5+
"math/big"
56
"net"
67
"os/exec"
78

@@ -27,7 +28,7 @@ func (v *Virter) AddDHCPHost(mac string, id uint) error {
2728
}
2829

2930
networkBaseIP := ipNet.IP.Mask(ipNet.Mask)
30-
ip := addToIP(networkBaseIP, id)
31+
ip := AddToIP(networkBaseIP, id)
3132

3233
if !ipNet.Contains(ip) {
3334
return fmt.Errorf("computed IP %v is not in network", ip)
@@ -85,7 +86,7 @@ func (v *Virter) getDomainSuffix() (string, error) {
8586
return net.Domain.Name, nil
8687
}
8788

88-
func addToIP(ip net.IP, addend uint) net.IP {
89+
func AddToIP(ip net.IP, addend uint) net.IP {
8990
i := ip.To4()
9091
v := uint(i[0])<<24 + uint(i[1])<<16 + uint(i[2])<<8 + uint(i[3])
9192
v += addend
@@ -113,10 +114,39 @@ func ipToID(ipnet net.IPNet, ip net.IP) (uint, error) {
113114

114115
// QemuMAC calculates a MAC address for a given id
115116
func QemuMAC(id uint) string {
116-
id0 := byte((id >> 16) & 0xFF)
117-
id1 := byte((id >> 8) & 0xFF)
118-
id2 := byte(id & 0xFF)
119-
return fmt.Sprintf("52:54:00:%02x:%02x:%02x", id0, id1, id2)
117+
mac, err := AddToMAC(QemuBaseMAC(), id)
118+
if err != nil {
119+
// Should never happen because "id" should never exceed 32 bits
120+
// and any 32 bit value can be added to the QEMU base MAC.
121+
panic(fmt.Sprintf("failed to construct QEMU MAC: %v", err))
122+
}
123+
124+
return mac.String()
125+
}
126+
127+
func QemuBaseMAC() net.HardwareAddr {
128+
mac, err := net.ParseMAC("52:54:00:00:00:00")
129+
if err != nil {
130+
panic("failed to parse hardcoded MAC address")
131+
}
132+
133+
return mac
134+
}
135+
136+
func AddToMAC(mac net.HardwareAddr, addend uint) (net.HardwareAddr, error) {
137+
var value big.Int
138+
value.SetBytes(mac)
139+
value.Add(&value, big.NewInt(int64(addend)))
140+
141+
valueBytes := value.Bytes()
142+
if len(valueBytes) > len(mac) {
143+
return net.HardwareAddr{}, fmt.Errorf("overflow adding %d to %v", addend, mac)
144+
}
145+
146+
// zero-pad bytes
147+
out := make([]byte, len(mac))
148+
copy(out[len(out)-len(valueBytes):], valueBytes)
149+
return out, nil
120150
}
121151

122152
func (v *Virter) getIPNet(network libvirt.Network) (net.IPNet, error) {

0 commit comments

Comments
 (0)