-
Notifications
You must be signed in to change notification settings - Fork 6
networkmanager: repair pooled TAP drift instead of failing #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
fee39db
f7eae7f
f8f789f
f1c3f7c
6a1ef4e
def1947
daa6f1f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ import ( | |
| "net" | ||
|
|
||
| "github.com/inclusionAI/sandboxd/internal/util" | ||
| "github.com/sirupsen/logrus" | ||
| "github.com/vishvananda/netlink" | ||
| "golang.org/x/sys/unix" | ||
| ) | ||
|
|
@@ -190,16 +191,36 @@ func (m *InterfaceManager) setTapState(resource *NetResource, up bool) error { | |
| if link.Type() != "tuntap" { | ||
| return fmt.Errorf("pooled endpoint %s has type %q, want tuntap", expectedName, link.Type()) | ||
| } | ||
| if m.bridgeLink == nil || link.Attrs().MasterIndex != m.bridgeLink.Attrs().Index { | ||
| return fmt.Errorf("pooled TAP %s is not attached to %s", expectedName, BridgeName) | ||
| // Host-side drift (stale bridge attach, host MAC randomised by an | ||
| // unclean predecessor or foreign tooling) is repaired in place instead of | ||
| // failing the allocation or daemon recovery: the endpoint identity is | ||
| // fully determined by its name, so re-stamping the deterministic | ||
| // attributes can only converge it back to the expected state. Identity | ||
| // violations (wrong type above, wrong name earlier) stay hard errors. | ||
| if m.bridgeLink == nil { | ||
| return fmt.Errorf("pooled TAP %s has no %s to attach to", expectedName, BridgeName) | ||
| } | ||
| if link.Attrs().MasterIndex != m.bridgeLink.Attrs().Index { | ||
| if err := netlink.LinkSetMaster(link, m.bridgeLink); err != nil { | ||
| return fmt.Errorf( | ||
| "reattach pooled TAP %s to %s: %w", expectedName, BridgeName, err, | ||
| ) | ||
| } | ||
| logrus.Warnf( | ||
| "networkmanager: repaired pooled TAP %s bridge attach (was not on %s)", | ||
| expectedName, BridgeName, | ||
| ) | ||
| } | ||
| expectedHostMAC, _ := tapHostMAC(ip4) | ||
| if !bytes.Equal(link.Attrs().HardwareAddr, expectedHostMAC) { | ||
| return fmt.Errorf( | ||
| "pooled TAP %s host MAC is %s, want %s", | ||
| expectedName, | ||
| link.Attrs().HardwareAddr, | ||
| expectedHostMAC, | ||
| if err := netlink.LinkSetHardwareAddr(link, expectedHostMAC); err != nil { | ||
| return fmt.Errorf( | ||
| "restore pooled TAP %s host MAC to %s: %w", expectedName, expectedHostMAC, err, | ||
| ) | ||
| } | ||
| logrus.Warnf( | ||
| "networkmanager: repaired pooled TAP %s host MAC %s -> %s", | ||
| expectedName, link.Attrs().HardwareAddr, expectedHostMAC, | ||
| ) | ||
| } | ||
| expectedGuestMAC, _ := tapGuestMAC(ip4) | ||
|
|
@@ -212,12 +233,14 @@ func (m *InterfaceManager) setTapState(resource *NetResource, up bool) error { | |
| ) | ||
| } | ||
| if resource.Interface.Index != 0 && resource.Interface.Index != link.Attrs().Index { | ||
| return fmt.Errorf( | ||
| "pooled TAP %s index is %d, lease records %d", | ||
| expectedName, | ||
| link.Attrs().Index, | ||
| resource.Interface.Index, | ||
| // The kernel ifindex is bookkeeping, not identity (it changes whenever | ||
| // the device is recreated); refresh the lease record instead of | ||
| // failing over a value nobody consumes. | ||
| logrus.Warnf( | ||
| "networkmanager: pooled TAP %s index drifted (%d, lease records %d); refreshing lease", | ||
| expectedName, link.Attrs().Index, resource.Interface.Index, | ||
| ) | ||
| resource.Interface.Index = link.Attrs().Index | ||
|
Comment on lines
+243
to
+247
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in f7eae7f (durable key swap on recovery)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Superseded by f1c3f7c: active-lease ifindex drift is now rejected outright at recovery (see tianyuzhou95's thread), so recovery never rewrites the durable key anymore. The remaining refresh paths ( |
||
| } | ||
| if up { | ||
| if err := netlink.LinkSetUp(link); err != nil { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -170,6 +170,15 @@ func (m *InterfaceManager) load(ips sets.Set[string]) error { | |
| if stateErr := m.setTapState(stored, true); stateErr != nil { | ||
| return fmt.Errorf("recover active pooled TAP %s: %w", dev.Name, stateErr) | ||
| } | ||
| // setTapState may repair lease bookkeeping in memory (e.g. an | ||
| // ifindex refresh after the device was recreated). Swap the | ||
| // durable key so the repaired lease is what gets stored and | ||
| // handed back on recycle, instead of re-warning every restart. | ||
| if refreshed := stored.ToString(); refreshed != activeID { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not repair active TAP replacement by rewriting the lease key An active TAP should not be recreated by sandboxd: Rewriting the key here is also not sufficient to migrate ownership. The original serialized The MAC and bridge self-repair can remain independent of this check. A regression test should verify that active ifindex replacement is rejected while idle orphan MAC and bridge drift are still repaired.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in f1c3f7c — agreed on all three points.
|
||
| m.usingInterfaces.Pop(activeID) | ||
| m.usingInterfaces.Set(refreshed, struct{}{}) | ||
| m.storeMark.Store(true) | ||
| } | ||
| } else { | ||
| if stateErr := m.setTapState(current, false); stateErr != nil { | ||
| return fmt.Errorf("recover idle pooled TAP %s: %w", dev.Name, stateErr) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
False positive: net.HardwareAddr implements Stringer; fmt %s invokes it (verified output is colon-separated), and the pre-PR code used the same pattern.