istio.io/istio@v0.0.0-20240520182934-d79c90f27776/cni/pkg/ipset/nldeps_linux.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ipset 16 17 import ( 18 "errors" 19 "fmt" 20 "net" 21 "net/netip" 22 23 "github.com/vishvananda/netlink" 24 "github.com/vishvananda/netlink/nl" 25 "golang.org/x/sys/unix" 26 ) 27 28 func RealNlDeps() NetlinkIpsetDeps { 29 return &realDeps{} 30 } 31 32 type realDeps struct{} 33 34 func (m *realDeps) ipsetIPHashCreate(name string, v6 bool) error { 35 var family uint8 36 37 if v6 { 38 family = unix.AF_INET6 39 } else { 40 family = unix.AF_INET 41 } 42 err := netlink.IpsetCreate(name, "hash:ip", netlink.IpsetCreateOptions{Comments: true, Replace: true, Family: family}) 43 if ipsetErr, ok := err.(nl.IPSetError); ok && ipsetErr == nl.IPSET_ERR_EXIST { 44 return nil 45 } 46 47 return err 48 } 49 50 func (m *realDeps) destroySet(name string) error { 51 err := netlink.IpsetDestroy(name) 52 return err 53 } 54 55 func (m *realDeps) addIP(name string, ip netip.Addr, ipProto uint8, comment string, replace bool) error { 56 err := netlink.IpsetAdd(name, &netlink.IPSetEntry{ 57 Comment: comment, 58 IP: net.IP(ip.AsSlice()), 59 Protocol: &ipProto, 60 Replace: replace, 61 }) 62 if err != nil { 63 return fmt.Errorf("failed to add IP %s with and proto %d to ipset %s: %w", ip, ipProto, name, err) 64 } 65 return nil 66 } 67 68 func (m *realDeps) deleteIP(name string, ip netip.Addr, ipProto uint8) error { 69 err := netlink.IpsetDel(name, &netlink.IPSetEntry{ 70 IP: net.IP(ip.AsSlice()), 71 Protocol: &ipProto, 72 }) 73 if err != nil { 74 return fmt.Errorf("failed to delete IP %s with and proto %d from ipset %s: %w", ip, ipProto, name, err) 75 } 76 return nil 77 } 78 79 func (m *realDeps) flush(name string) error { 80 err := netlink.IpsetFlush(name) 81 if err != nil { 82 return fmt.Errorf("failed to flush ipset %s: %w", name, err) 83 } 84 return nil 85 } 86 87 // Alpine and some distros struggles with this - ipset CLI utilities support this, but 88 // the kernel can be out of sync with the CLI utility, leading to errors like: 89 // 90 // ipset v7.10: Argument `comment' is supported in the kernel module of the set type hash:ip 91 // starting from the revision 3 and you have installed revision 1 only. 92 // Your kernel is behind your ipset utility. 93 // 94 // This happens with kernels as recent as Fedora38, e.g: 6.4.11-200.fc38.aarch64 95 func (m *realDeps) clearEntriesWithComment(name, comment string) error { 96 res, err := netlink.IpsetList(name) 97 if err != nil { 98 return fmt.Errorf("failed to list ipset %s: %w", name, err) 99 } 100 for _, entry := range res.Entries { 101 if entry.Comment == comment { 102 err := netlink.IpsetDel(name, &entry) 103 if err != nil { 104 return fmt.Errorf("failed to delete IP %s from ipset %s: %w", entry.IP, name, err) 105 } 106 } 107 } 108 return nil 109 } 110 111 func (m *realDeps) clearEntriesWithIP(name string, ip netip.Addr) error { 112 delIP := net.IP(ip.AsSlice()) 113 res, err := netlink.IpsetList(name) 114 if err != nil { 115 return fmt.Errorf("failed to list ipset %s: %w", name, err) 116 } 117 118 var delErrs []error 119 120 for _, entry := range res.Entries { 121 if entry.IP.Equal(delIP) { 122 err := netlink.IpsetDel(name, &entry) 123 if err != nil { 124 delErrs = append(delErrs, fmt.Errorf("failed to delete IP %s from ipset %s: %w", entry.IP, name, err)) 125 } 126 } 127 } 128 129 return errors.Join(delErrs...) 130 } 131 132 func (m *realDeps) listEntriesByIP(name string) ([]netip.Addr, error) { 133 var ipList []netip.Addr 134 135 res, err := netlink.IpsetList(name) 136 if err != nil { 137 return ipList, fmt.Errorf("failed to list ipset %s: %w", name, err) 138 } 139 140 for _, entry := range res.Entries { 141 addr, _ := netip.AddrFromSlice(entry.IP) 142 ipList = append(ipList, addr) 143 } 144 145 return ipList, nil 146 }