diff --git a/exp/ipam/internal/webhooks/ipaddress.go b/exp/ipam/internal/webhooks/ipaddress.go index 45c4f5cf05e9..b667c9f64d57 100644 --- a/exp/ipam/internal/webhooks/ipaddress.go +++ b/exp/ipam/internal/webhooks/ipaddress.go @@ -122,17 +122,35 @@ func (webhook *IPAddress) validate(ctx context.Context, ip *ipamv1.IPAddress) er "prefix is too large for an IPv6 address", )) } - - _, err = netip.ParseAddr(ip.Spec.Gateway) - if err != nil { + if addr.Is4() && ip.Spec.Gateway == "" { allErrs = append(allErrs, field.Invalid( specPath.Child("gateway"), ip.Spec.Gateway, - "not a valid IP address", + "gateway is required for an IPv4 address", )) } - + if ip.Spec.Gateway != "" { + gateway, err := netip.ParseAddr(ip.Spec.Gateway) + if err != nil { + allErrs = append(allErrs, + field.Invalid( + specPath.Child("gateway"), + ip.Spec.Gateway, + "not a valid IP address", + )) + } + + if addr.Is4() && gateway.Is6() || + addr.Is6() && gateway.Is4() { + allErrs = append(allErrs, + field.Invalid( + specPath.Child("gateway"), + ip.Spec.Gateway, + "gateway and address IP family must match", + )) + } + } if ip.Spec.PoolRef.APIGroup == nil { allErrs = append(allErrs, field.Invalid( diff --git a/exp/ipam/internal/webhooks/ipaddress_test.go b/exp/ipam/internal/webhooks/ipaddress_test.go index 5c8809fa253c..ed0e9ddd3256 100644 --- a/exp/ipam/internal/webhooks/ipaddress_test.go +++ b/exp/ipam/internal/webhooks/ipaddress_test.go @@ -90,6 +90,38 @@ func TestIPAddressValidateCreate(t *testing.T) { extraObjs: []client.Object{claim}, expectErr: false, }, + { + name: "a valid IPv6 Address with no gateway should be accepted", + ip: getAddress(true, func(addr *ipamv1.IPAddress) { + addr.Spec.Gateway = "" + }), + extraObjs: []client.Object{claim}, + expectErr: false, + }, + { + name: "an IPv4 Address with no gateway should be rejected", + ip: getAddress(false, func(addr *ipamv1.IPAddress) { + addr.Spec.Gateway = "" + }), + extraObjs: []client.Object{claim}, + expectErr: true, + }, + { + name: "a valid IPv4 Address with valid IPv6 gateway should be rejected", + ip: getAddress(false, func(addr *ipamv1.IPAddress) { + addr.Spec.Gateway = "42::ffff" + }), + extraObjs: []client.Object{claim}, + expectErr: true, + }, + { + name: "a valid IPv6 Address with valid IPv4 gateway should be rejected", + ip: getAddress(true, func(addr *ipamv1.IPAddress) { + addr.Spec.Gateway = "10.0.0.254" + }), + extraObjs: []client.Object{claim}, + expectErr: true, + }, { name: "a prefix that is negative should be rejected", ip: getAddress(false, func(addr *ipamv1.IPAddress) {