|
| 1 | +/* |
| 2 | +Copyright 2024 The Kubernetes Authors. |
| 3 | +
|
| 4 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +you may not use this file except in compliance with the License. |
| 6 | +You may obtain a copy of the License at |
| 7 | +
|
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +
|
| 10 | +Unless required by applicable law or agreed to in writing, software |
| 11 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +See the License for the specific language governing permissions and |
| 14 | +limitations under the License. |
| 15 | +*/ |
| 16 | + |
| 17 | +package net |
| 18 | + |
| 19 | +import ( |
| 20 | + "net" |
| 21 | + "net/netip" |
| 22 | +) |
| 23 | + |
| 24 | +// AddrFromIP converts a net.IP to a netip.Addr. Given valid input this will always |
| 25 | +// succeed; it will return the invalid netip.Addr on nil or garbage input. |
| 26 | +// |
| 27 | +// Use this rather than netip.AddrFromSlice(), which (despite the claims of its |
| 28 | +// documentation) does not always do what you would expect if you pass it a net.IP. |
| 29 | +func AddrFromIP(ip net.IP) netip.Addr { |
| 30 | + // Naively using netip.AddrFromSlice() gives unexpected results: |
| 31 | + // |
| 32 | + // ip := net.ParseIP("1.2.3.4") |
| 33 | + // addr, _ := netip.AddrFromSlice(ip) |
| 34 | + // addr.String() => "::ffff:1.2.3.4" |
| 35 | + // addr.Is4() => false |
| 36 | + // addr.Is6() => true |
| 37 | + // |
| 38 | + // This is because net.IP and netip.Addr have different ideas about how to handle |
| 39 | + // "IPv4-mapped IPv6" addresses, but netip.AddrFromSlice ignores that fact. |
| 40 | + // |
| 41 | + // In net.IP, parsing either "1.2.3.4" or "::ffff:1.2.3.4", will give you the |
| 42 | + // same result: |
| 43 | + // |
| 44 | + // ip1 := net.ParseIP("1.2.3.4") |
| 45 | + // []byte(ip1) => []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 1, 2, 3, 4} |
| 46 | + // ip1.String() => "1.2.3.4" |
| 47 | + // ip2 := net.ParseIP("::ffff:1.2.3.4") |
| 48 | + // []byte(ip2) => []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 1, 2, 3, 4} |
| 49 | + // ip2.String() => "1.2.3.4" |
| 50 | + // |
| 51 | + // net.IP normally stores IPv4 addresses as 16-byte IPv4-mapped IPv6 addresses, |
| 52 | + // but it hides that from the user, and it never stringifies an IPv4 IP to an |
| 53 | + // IPv4-mapped IPv6 form, even if that was the format you started with. |
| 54 | + // |
| 55 | + // net.IP *can* represent IPv4 addresses in a 4-byte format, but this is treated |
| 56 | + // as completly equivalent to the 16-byte representation: |
| 57 | + // |
| 58 | + // ip4 := ip1.To4() |
| 59 | + // []byte(ip4) => []byte{1, 2, 3, 4} |
| 60 | + // ip4.String() => "1.2.3.4" |
| 61 | + // ip1.Equal(ip4) => true |
| 62 | + // |
| 63 | + // netip.Addr, on the other hand, treats "plain" IPv4 and IPv4-mapped IPv6 as two |
| 64 | + // completely separate things: |
| 65 | + // |
| 66 | + // a1 := netip.MustParseAddr("1.2.3.4") |
| 67 | + // a2 := netip.MustParseAddr("::ffff:1.2.3.4") |
| 68 | + // a1.String() => "1.2.3.4" |
| 69 | + // a2.String() => "::ffff:1.2.3.4" |
| 70 | + // a1 == a2 => false |
| 71 | + // |
| 72 | + // which would be fine, except that netip.AddrFromSlice breaks net.IP's normal |
| 73 | + // semantics by converting the 4-byte and 16-byte net.IP forms to different |
| 74 | + // netip.Addr values, giving the confusing results above. |
| 75 | + // |
| 76 | + // In order to correctly convert an IPv4 address from net.IP to netip.Addr, you |
| 77 | + // need to either call .To4() on it before converting, or call .Unmap() on it |
| 78 | + // after converting. (The latter option is slightly simpler for us here because we |
| 79 | + // can just do it unconditionally, since it's a no-op in the IPv6 and invalid |
| 80 | + // cases). |
| 81 | + |
| 82 | + addr, _ := netip.AddrFromSlice(ip) |
| 83 | + return addr.Unmap() |
| 84 | +} |
| 85 | + |
| 86 | +// IPFromAddr converts a netip.Addr to a net.IP. Given valid input this will always |
| 87 | +// succeed; it will return nil if addr is the invalid netip.Addr. |
| 88 | +func IPFromAddr(addr netip.Addr) net.IP { |
| 89 | + // addr.AsSlice() returns: |
| 90 | + // - a []byte of length 4 if addr is a normal IPv4 address |
| 91 | + // - a []byte of length 16 if addr is an IPv6 address (including IPv4-mapped IPv6) |
| 92 | + // - nil if addr is the zero Addr (which is the only other possibility) |
| 93 | + // |
| 94 | + // Any of those values can be correctly cast directly to a net.IP. |
| 95 | + // |
| 96 | + // Note that we don't bother to do any "cleanup" here like in the AddrFromIP case, |
| 97 | + // so converting a plain IPv4 netip.Addr to net.IP gives a different result than |
| 98 | + // converting an IPv4-mapped IPv6 netip.Addr: |
| 99 | + // |
| 100 | + // ip1 := netutils.IPFromAddr(netip.MustParseAddr("1.2.3.4")) |
| 101 | + // []byte(ip1) => []byte{1, 2, 3, 4} |
| 102 | + // |
| 103 | + // ip2 := netutils.IPFromAddr(netip.MustParseAddr("::ffff:1.2.3.4")) |
| 104 | + // []byte(ip2) => []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 1, 2, 3, 4} |
| 105 | + // |
| 106 | + // However, the net.IP API treats the two values as the same anyway, so it doesn't |
| 107 | + // matter. |
| 108 | + // |
| 109 | + // ip1.String() => "1.2.3.4" |
| 110 | + // ip2.String() => "1.2.3.4" |
| 111 | + // ip2.Equal(ip1) => true |
| 112 | + |
| 113 | + return net.IP(addr.AsSlice()) |
| 114 | +} |
| 115 | + |
| 116 | +// PrefixFromIPNet converts a *net.IPNet to a netip.Prefix. Given valid input this will |
| 117 | +// always succeed; it will return the invalid netip.Prefix on nil or garbage input. |
| 118 | +func PrefixFromIPNet(ipnet *net.IPNet) netip.Prefix { |
| 119 | + if ipnet == nil { |
| 120 | + return netip.Prefix{} |
| 121 | + } |
| 122 | + |
| 123 | + addr := AddrFromIP(ipnet.IP) |
| 124 | + if !addr.IsValid() { |
| 125 | + return netip.Prefix{} |
| 126 | + } |
| 127 | + |
| 128 | + prefixLen, bits := ipnet.Mask.Size() |
| 129 | + if prefixLen == 0 && bits == 0 { |
| 130 | + // non-CIDR Mask representation; not representible as a netip.Prefix |
| 131 | + return netip.Prefix{} |
| 132 | + } |
| 133 | + if bits == 128 && addr.Is4() && (bits-prefixLen <= 32) { |
| 134 | + // In the same way that net.IP allows an IPv4 IP to be either 4 or 16 |
| 135 | + // bytes (32 or 128 bits), *net.IPNet allows an IPv4 CIDR to have either a |
| 136 | + // 32-bit or a 128-bit mask. If the mask is 128 bits, we discard the |
| 137 | + // leftmost 96 bits. |
| 138 | + prefixLen -= 128 - 32 |
| 139 | + } else if bits != addr.BitLen() { |
| 140 | + // invalid IPv4/IPv6 mix |
| 141 | + return netip.Prefix{} |
| 142 | + } |
| 143 | + |
| 144 | + return netip.PrefixFrom(addr, prefixLen) |
| 145 | +} |
| 146 | + |
| 147 | +// IPNetFromPrefix converts a netip.Prefix to a *net.IPNet. Given valid input this will |
| 148 | +// always succeed; it will return nil if prefix is the invalid netip.Prefix or is |
| 149 | +// otherwise invalid. |
| 150 | +func IPNetFromPrefix(prefix netip.Prefix) *net.IPNet { |
| 151 | + addr := prefix.Addr() |
| 152 | + bits := prefix.Bits() |
| 153 | + if bits == -1 || !addr.IsValid() { |
| 154 | + return nil |
| 155 | + } |
| 156 | + addrLen := addr.BitLen() |
| 157 | + |
| 158 | + // (As with IPFromAddr, a plain IPv4 netip.Prefix and an equivalent IPv4-mapped |
| 159 | + // IPv6 netip.Prefix will get converted to distinct *net.IPNet values, but |
| 160 | + // *net.IPNet will treat them equivalently.) |
| 161 | + |
| 162 | + return &net.IPNet{ |
| 163 | + IP: IPFromAddr(addr), |
| 164 | + Mask: net.CIDRMask(bits, addrLen), |
| 165 | + } |
| 166 | +} |
0 commit comments