Skip to content

Commit 2c8d36e

Browse files
authored
Always randomize MAC address (#251)
* Always randomize MAC address * Worker: check DHCP lease time and print a warning if it's unconfigured * Further improve the explanation * Add two leases example to the explanation * Add an example of the resulting /var/db/dhcpd_leases
1 parent 2aae818 commit 2c8d36e

File tree

3 files changed

+121
-8
lines changed

3 files changed

+121
-8
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package dhcpleasetime
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"howett.net/plist"
7+
"os"
8+
)
9+
10+
const (
11+
internetSharingPath = "/Library/Preferences/SystemConfiguration/com.apple.InternetSharing.default.plist"
12+
)
13+
14+
type InternetSharing struct {
15+
Bootpd Bootpd `plist:"bootpd"`
16+
}
17+
18+
type Bootpd struct {
19+
DHCPLeaseTimeSecs int `plist:"DHCPLeaseTimeSecs"`
20+
}
21+
22+
func Check() error {
23+
errDefault := fmt.Errorf("it seems that you're using a default DHCP lease time of 1 day which may result in " +
24+
"failures communicating with the VMs, please read https://tart.run/faq/#changing-the-default-dhcp-lease-time " +
25+
"for more details and intstructions on how to fix that")
26+
27+
plistBytes, err := os.ReadFile(internetSharingPath)
28+
if err != nil {
29+
if errors.Is(err, os.ErrNotExist) {
30+
return errDefault
31+
}
32+
33+
return fmt.Errorf("failed to check the default DHCP lease time: %w, please read "+
34+
"https://tart.run/faq/#changing-the-default-dhcp-lease-time for more details", err)
35+
}
36+
37+
var internetSharing InternetSharing
38+
39+
_, err = plist.Unmarshal(plistBytes, &internetSharing)
40+
if err != nil {
41+
return fmt.Errorf("failed to check the default DHCP lease time: %w, please read "+
42+
"https://tart.run/faq/#changing-the-default-dhcp-lease-time for more details", err)
43+
}
44+
45+
if internetSharing.Bootpd.DHCPLeaseTimeSecs == 0 {
46+
return errDefault
47+
}
48+
49+
if internetSharing.Bootpd.DHCPLeaseTimeSecs > 3600 {
50+
return fmt.Errorf("it seems that you're using a DHCP lease time larger than 1 hour which may result in " +
51+
"failures communicating with the VMs, please read https://tart.run/faq/#changing-the-default-dhcp-lease-time " +
52+
"for more details and intstructions on how to fix that")
53+
}
54+
55+
return nil
56+
}

internal/worker/vmmanager/vm.go

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -225,15 +225,67 @@ func (vm *VM) cloneAndConfigure(ctx context.Context) error {
225225
}
226226
}
227227

228-
// Randomize the VM's MAC-address when using bridged networking
229-
// to avoid collisions when cloning from an OCI image on multiple hosts
228+
// Randomize VM's MAC-address, this is important when using shared (NAT) networking
229+
// with full /var/db/dhcpd_leases file (e.g. 256 entries) having an expired entry
230+
// for a MAC address used by some OCI image, for example:
230231
//
231-
// See https://github.com/cirruslabs/orchard/issues/181 for more details.
232-
if vm.Resource.NetBridged != "" {
233-
_, _, err = tart.Tart(ctx, vm.logger, "set", "--random-mac", vm.id())
234-
if err != nil {
235-
return err
236-
}
232+
// {
233+
// name=adminsVlMachine
234+
// ip_address=192.168.64.2
235+
// hw_address=1,11:11:11:11:11:11
236+
// identifier=1,11:11:11:11:11:11
237+
// lease=0x1234
238+
//}
239+
//
240+
// The next VM to start with a MAC address 22:22:22:22:22:22 will assume that
241+
// 192.168.64.2 is free (because its lease expired a long time ago) and will
242+
// add a new entry using its MAC address and 192.168.64.2 to the
243+
// /var/db/dhcpd_leases and won't delete the old entry:
244+
//
245+
// {
246+
// name=adminsVlMachine
247+
// ip_address=192.168.64.2
248+
// hw_address=1,11:11:11:11:11:11
249+
// identifier=1,11:11:11:11:11:11
250+
// lease=0x1234
251+
// }
252+
// {
253+
// name=adminsVlMachine
254+
// ip_address=192.168.64.2
255+
// hw_address=1,22:22:22:22:22:22
256+
// identifier=1,22:22:22:22:22:22
257+
// lease=0x67ade532
258+
// }
259+
//
260+
// Afterward, when an OCI VM with MAC address 11:11:11:11:11:11 is cloned and run,
261+
// it will re-use the 192.168.64.2 entry instead of creating a new one, even through
262+
// its lease had already expired. The resulting /var/db/dhcpd_leases will look like this:
263+
//
264+
// {
265+
// name=adminsVlMachine
266+
// ip_address=192.168.64.2
267+
// hw_address=1,11:11:11:11:11:11
268+
// identifier=1,11:11:11:11:11:11
269+
// lease=0x67ade5c6
270+
// }
271+
// {
272+
// name=adminsVlMachine
273+
// ip_address=192.168.64.2
274+
// hw_address=1,22:22:22:22:22:22
275+
// identifier=1,22:22:22:22:22:22
276+
// lease=0x67ade532
277+
// }
278+
//
279+
// As a result, you will see two VMs with different MAC address using an identical
280+
// IP address 192.168.64.2.
281+
//
282+
// Another scenarion when this is important is when using bridged networking
283+
// to avoid collisions when cloning from an OCI image on multiple hosts[1].
284+
//
285+
// [1]: https://github.com/cirruslabs/orchard/issues/181
286+
_, _, err = tart.Tart(ctx, vm.logger, "set", "--random-mac", vm.id())
287+
if err != nil {
288+
return err
237289
}
238290

239291
if vm.Resource.RandomSerial {

internal/worker/worker.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"github.com/avast/retry-go/v4"
88
"github.com/cirruslabs/orchard/internal/opentelemetry"
9+
"github.com/cirruslabs/orchard/internal/worker/dhcpleasetime"
910
"github.com/cirruslabs/orchard/internal/worker/iokitregistry"
1011
"github.com/cirruslabs/orchard/internal/worker/ondiskname"
1112
"github.com/cirruslabs/orchard/internal/worker/tart"
@@ -88,6 +89,10 @@ func New(client *client.Client, opts ...Option) (*Worker, error) {
8889
}
8990

9091
func (worker *Worker) Run(ctx context.Context) error {
92+
if err := dhcpleasetime.Check(); err != nil {
93+
worker.logger.Warnf("%v", err)
94+
}
95+
9196
for {
9297
if err := worker.runNewSession(ctx); err != nil {
9398
return err

0 commit comments

Comments
 (0)