Repository: moby/moby
File: daemon/libnetwork/drivers/bridge/bridge_linux.go (Go)

Code before:
	}

	return nil
}

// trimPortBindings compares pbmReq with the current port bindings, and removes
// port bindings that are no longer required.
//
// ep.portMapping is updated when bindings are removed.
func (ep *bridgeEndpoint) trimPortBindings(ctx context.Context, n *bridgeNetwork, pbmReq portBindingMode) (func() []portmapperapi.PortBinding, error) {
	// If the endpoint is the gateway for IPv4 and IPv6, there's nothing to drop.
	if pbmReq.ipv4 && pbmReq.ipv6 {
		return nil, nil
	}

	toDrop := make([]portmapperapi.PortBinding, 0, len(ep.portMapping))
	toKeep := slices.DeleteFunc(ep.portMapping, func(pb portmapperapi.PortBinding) bool {
		is4 := pb.HostIP.To4() != nil
		if (is4 && !pbmReq.ipv4) || (!is4 && !pbmReq.ipv6) {
			toDrop = append(toDrop, pb)
			return true
		}
		return false
	})
	if len(toDrop) == 0 {
		return nil, nil
	}

	if err := n.unmapPBs(ctx, toDrop); err != nil {
		log.G(ctx).WithFields(log.Fields{
			"error": err,
			"gw4":   pbmReq.ipv4,
			"gw6":   pbmReq.ipv6,
			"nid":   stringid.TruncateID(n.id),
			"eid":   stringid.TruncateID(ep.id),
		}).Error("Failed to release port bindings")
		return nil, err
	}
	ep.portMapping = toKeep

	undo := func() []portmapperapi.PortBinding {
		pbReq := make([]portmapperapi.PortBindingReq, 0, len(toDrop))
		for _, pb := range toDrop {
			pbReq = append(pbReq, portmapperapi.PortBindingReq{PortBinding: pb.GetCopy()})
		}
		pbs, err := n.addPortMappings(ctx, ep, pbReq, n.config.DefaultBindingIP, ep.portBindingState)
		if err != nil {
			log.G(ctx).WithFields(log.Fields{
				"error": err,
				"nid":   stringid.TruncateID(n.id),
				"eid":   stringid.TruncateID(ep.id),

Review comment (suggestion):
😅 

```suggestion
	// from host IPv6 to container IPv4 because there is no IPv6 gateway - unless the
```

Code after:

	// Remember the new port binding state.
	endpoint.portBindingState = pbmReq

	// Clean the connection tracker state of the host for the specific endpoint. This is needed because some flows may
	// be bound to the local proxy, or to the host (for UDP packets), and won't be redirected to the new endpoints.
	clearConntrackEntries(d.nlh, endpoint)

	if err = d.storeUpdate(ctx, endpoint); err != nil {
		return fmt.Errorf("failed to update bridge endpoint %.7s to store: %v", endpoint.id, err)
	}

	return nil
}

// trimPortBindings compares pbmReq with the current port bindings, and removes
// port bindings that are no longer required.
//
// ep.portMapping is updated when bindings are removed.
func (ep *bridgeEndpoint) trimPortBindings(ctx context.Context, n *bridgeNetwork, pbmReq portBindingMode) (func() []portmapperapi.PortBinding, error) {
	// Drop IPv4 bindings if this endpoint is not the IPv4 gateway, unless the
	// network is "routed" (routed bindings get dropped unconditionally by Leave).
	drop4 := !pbmReq.ipv4 && !n.gwMode(firewaller.IPv4).routed()

	// Drop IPv6 bindings if this endpoint is not the IPv6 gateway, and not proxying
	// from host IPv6 to container IPv6 because there is no IPv6 gateway - unless the
	// IPv6 network is "routed" (routed bindings get dropped unconditionally by Leave).
	drop6 := !pbmReq.ipv6 && !n.gwMode(firewaller.IPv6).routed()

	if !drop4 && !drop6 {
		return nil, nil
	}

	toDrop := make([]portmapperapi.PortBinding, 0, len(ep.portMapping))
	toKeep := slices.DeleteFunc(ep.portMapping, func(pb portmapperapi.PortBinding) bool {
		is4 := pb.HostIP.To4() != nil
		if (is4 && drop4) || (!is4 && drop6) {
			toDrop = append(toDrop, pb)
			return true
		}
		return false
	})
	if len(toDrop) == 0 {
		return nil, nil
	}

	if err := n.unmapPBs(ctx, toDrop); err != nil {
		log.G(ctx).WithFields(log.Fields{
			"error": err,
			"gw4":   pbmReq.ipv4,
			"gw6":   pbmReq.ipv6,