github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/ipset_linux.go (about) 1 package netlink 2 3 import ( 4 "encoding/binary" 5 "log" 6 "net" 7 "syscall" 8 9 "github.com/sagernet/netlink/nl" 10 "golang.org/x/sys/unix" 11 ) 12 13 // IPSetEntry is used for adding, updating, retreiving and deleting entries 14 type IPSetEntry struct { 15 Comment string 16 MAC net.HardwareAddr 17 IP net.IP 18 CIDR uint8 19 Timeout *uint32 20 Packets *uint64 21 Bytes *uint64 22 Protocol *uint8 23 Port *uint16 24 IP2 net.IP 25 CIDR2 uint8 26 IFace string 27 Mark *uint32 28 29 Replace bool // replace existing entry 30 } 31 32 // IPSetResult is the result of a dump request for a set 33 type IPSetResult struct { 34 Nfgenmsg *nl.Nfgenmsg 35 Protocol uint8 36 ProtocolMinVersion uint8 37 Revision uint8 38 Family uint8 39 Flags uint8 40 SetName string 41 TypeName string 42 Comment string 43 MarkMask uint32 44 45 IPFrom net.IP 46 IPTo net.IP 47 PortFrom uint16 48 PortTo uint16 49 50 HashSize uint32 51 NumEntries uint32 52 MaxElements uint32 53 References uint32 54 SizeInMemory uint32 55 CadtFlags uint32 56 Timeout *uint32 57 LineNo uint32 58 59 Entries []IPSetEntry 60 } 61 62 // IpsetCreateOptions is the options struct for creating a new ipset 63 type IpsetCreateOptions struct { 64 Replace bool // replace existing ipset 65 Timeout *uint32 66 Counters bool 67 Comments bool 68 Skbinfo bool 69 70 Revision uint8 71 IPFrom net.IP 72 IPTo net.IP 73 PortFrom uint16 74 PortTo uint16 75 } 76 77 // IpsetProtocol returns the ipset protocol version from the kernel 78 func IpsetProtocol() (uint8, uint8, error) { 79 return pkgHandle.IpsetProtocol() 80 } 81 82 // IpsetCreate creates a new ipset 83 func IpsetCreate(setname, typename string, options IpsetCreateOptions) error { 84 return pkgHandle.IpsetCreate(setname, typename, options) 85 } 86 87 // IpsetDestroy destroys an existing ipset 88 func IpsetDestroy(setname string) error { 89 return pkgHandle.IpsetDestroy(setname) 90 } 91 92 // IpsetFlush flushes an existing ipset 93 func IpsetFlush(setname string) error { 94 return pkgHandle.IpsetFlush(setname) 95 } 96 97 // IpsetSwap swaps two ipsets. 98 func IpsetSwap(setname, othersetname string) error { 99 return pkgHandle.IpsetSwap(setname, othersetname) 100 } 101 102 // IpsetList dumps an specific ipset. 103 func IpsetList(setname string) (*IPSetResult, error) { 104 return pkgHandle.IpsetList(setname) 105 } 106 107 // IpsetListAll dumps all ipsets. 108 func IpsetListAll() ([]IPSetResult, error) { 109 return pkgHandle.IpsetListAll() 110 } 111 112 // IpsetAdd adds an entry to an existing ipset. 113 func IpsetAdd(setname string, entry *IPSetEntry) error { 114 return pkgHandle.IpsetAdd(setname, entry) 115 } 116 117 // IpsetDel deletes an entry from an existing ipset. 118 func IpsetDel(setname string, entry *IPSetEntry) error { 119 return pkgHandle.IpsetDel(setname, entry) 120 } 121 122 func (h *Handle) IpsetProtocol() (protocol uint8, minVersion uint8, err error) { 123 req := h.newIpsetRequest(nl.IPSET_CMD_PROTOCOL) 124 msgs, err := req.Execute(unix.NETLINK_NETFILTER, 0) 125 126 if err != nil { 127 return 0, 0, err 128 } 129 response := ipsetUnserialize(msgs) 130 return response.Protocol, response.ProtocolMinVersion, nil 131 } 132 133 func (h *Handle) IpsetCreate(setname, typename string, options IpsetCreateOptions) error { 134 req := h.newIpsetRequest(nl.IPSET_CMD_CREATE) 135 136 if !options.Replace { 137 req.Flags |= unix.NLM_F_EXCL 138 } 139 140 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname))) 141 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(typename))) 142 143 revision := options.Revision 144 if revision == 0 { 145 revision = getIpsetDefaultWithTypeName(typename) 146 } 147 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_REVISION, nl.Uint8Attr(revision))) 148 149 data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil) 150 151 var family uint8 152 switch typename { 153 case "hash:mac": 154 case "bitmap:port": 155 buf := make([]byte, 4) 156 binary.BigEndian.PutUint16(buf, options.PortFrom) 157 binary.BigEndian.PutUint16(buf[2:], options.PortTo) 158 data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_FROM|int(nl.NLA_F_NET_BYTEORDER), buf[:2])) 159 data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_TO|int(nl.NLA_F_NET_BYTEORDER), buf[2:])) 160 default: 161 family = unix.AF_INET 162 } 163 164 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_FAMILY, nl.Uint8Attr(family))) 165 166 if timeout := options.Timeout; timeout != nil { 167 data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *timeout}) 168 } 169 170 var cadtFlags uint32 171 172 if options.Comments { 173 cadtFlags |= nl.IPSET_FLAG_WITH_COMMENT 174 } 175 if options.Counters { 176 cadtFlags |= nl.IPSET_FLAG_WITH_COUNTERS 177 } 178 if options.Skbinfo { 179 cadtFlags |= nl.IPSET_FLAG_WITH_SKBINFO 180 } 181 182 if cadtFlags != 0 { 183 data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER, Value: cadtFlags}) 184 } 185 186 req.AddData(data) 187 _, err := ipsetExecute(req) 188 return err 189 } 190 191 func (h *Handle) IpsetDestroy(setname string) error { 192 req := h.newIpsetRequest(nl.IPSET_CMD_DESTROY) 193 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname))) 194 _, err := ipsetExecute(req) 195 return err 196 } 197 198 func (h *Handle) IpsetFlush(setname string) error { 199 req := h.newIpsetRequest(nl.IPSET_CMD_FLUSH) 200 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname))) 201 _, err := ipsetExecute(req) 202 return err 203 } 204 205 func (h *Handle) IpsetSwap(setname, othersetname string) error { 206 req := h.newIpsetRequest(nl.IPSET_CMD_SWAP) 207 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname))) 208 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(othersetname))) 209 _, err := ipsetExecute(req) 210 return err 211 } 212 213 func (h *Handle) IpsetList(name string) (*IPSetResult, error) { 214 req := h.newIpsetRequest(nl.IPSET_CMD_LIST) 215 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(name))) 216 217 msgs, err := ipsetExecute(req) 218 if err != nil { 219 return nil, err 220 } 221 222 result := ipsetUnserialize(msgs) 223 return &result, nil 224 } 225 226 func (h *Handle) IpsetListAll() ([]IPSetResult, error) { 227 req := h.newIpsetRequest(nl.IPSET_CMD_LIST) 228 229 msgs, err := ipsetExecute(req) 230 if err != nil { 231 return nil, err 232 } 233 234 result := make([]IPSetResult, len(msgs)) 235 for i, msg := range msgs { 236 result[i].unserialize(msg) 237 } 238 239 return result, nil 240 } 241 242 // IpsetAdd adds an entry to an existing ipset. 243 func (h *Handle) IpsetAdd(setname string, entry *IPSetEntry) error { 244 return h.ipsetAddDel(nl.IPSET_CMD_ADD, setname, entry) 245 } 246 247 // IpsetDel deletes an entry from an existing ipset. 248 func (h *Handle) IpsetDel(setname string, entry *IPSetEntry) error { 249 return h.ipsetAddDel(nl.IPSET_CMD_DEL, setname, entry) 250 } 251 252 func (h *Handle) ipsetAddDel(nlCmd int, setname string, entry *IPSetEntry) error { 253 req := h.newIpsetRequest(nlCmd) 254 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname))) 255 256 if entry.Comment != "" { 257 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_COMMENT, nl.ZeroTerminated(entry.Comment))) 258 } 259 260 data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil) 261 262 if !entry.Replace { 263 req.Flags |= unix.NLM_F_EXCL 264 } 265 266 if entry.Timeout != nil { 267 data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *entry.Timeout}) 268 } 269 270 if entry.IP != nil { 271 nestedData := nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NET_BYTEORDER), entry.IP) 272 data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NESTED), nestedData.Serialize())) 273 } 274 275 if entry.MAC != nil { 276 data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_ETHER, entry.MAC)) 277 } 278 279 if entry.CIDR != 0 { 280 data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR, nl.Uint8Attr(entry.CIDR))) 281 } 282 283 if entry.IP2 != nil { 284 nestedData := nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NET_BYTEORDER), entry.IP2) 285 data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP2|int(nl.NLA_F_NESTED), nestedData.Serialize())) 286 } 287 288 if entry.CIDR2 != 0 { 289 data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR2, nl.Uint8Attr(entry.CIDR2))) 290 } 291 292 if entry.Port != nil { 293 if entry.Protocol == nil { 294 // use tcp protocol as default 295 val := uint8(unix.IPPROTO_TCP) 296 entry.Protocol = &val 297 } 298 data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PROTO, nl.Uint8Attr(*entry.Protocol))) 299 buf := make([]byte, 2) 300 binary.BigEndian.PutUint16(buf, *entry.Port) 301 data.AddChild(nl.NewRtAttr(int(nl.IPSET_ATTR_PORT|nl.NLA_F_NET_BYTEORDER), buf)) 302 } 303 304 if entry.IFace != "" { 305 data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IFACE, nl.ZeroTerminated(entry.IFace))) 306 } 307 308 if entry.Mark != nil { 309 data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER, Value: *entry.Mark}) 310 } 311 312 data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_LINENO | nl.NLA_F_NET_BYTEORDER, Value: 0}) 313 req.AddData(data) 314 315 _, err := ipsetExecute(req) 316 return err 317 } 318 319 func (h *Handle) newIpsetRequest(cmd int) *nl.NetlinkRequest { 320 req := h.newNetlinkRequest(cmd|(unix.NFNL_SUBSYS_IPSET<<8), nl.GetIpsetFlags(cmd)) 321 322 // Add the netfilter header 323 msg := &nl.Nfgenmsg{ 324 NfgenFamily: uint8(unix.AF_NETLINK), 325 Version: nl.NFNETLINK_V0, 326 ResId: 0, 327 } 328 req.AddData(msg) 329 req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_PROTOCOL, nl.Uint8Attr(nl.IPSET_PROTOCOL))) 330 331 return req 332 } 333 334 func getIpsetDefaultWithTypeName(typename string) uint8 { 335 switch typename { 336 case "hash:ip,port", 337 "hash:ip,port,ip", 338 "hash:ip,port,net", 339 "hash:net,port": 340 return 1 341 } 342 return 0 343 } 344 345 func ipsetExecute(req *nl.NetlinkRequest) (msgs [][]byte, err error) { 346 msgs, err = req.Execute(unix.NETLINK_NETFILTER, 0) 347 348 if err != nil { 349 if errno := int(err.(syscall.Errno)); errno >= nl.IPSET_ERR_PRIVATE { 350 err = nl.IPSetError(uintptr(errno)) 351 } 352 } 353 return 354 } 355 356 func ipsetUnserialize(msgs [][]byte) (result IPSetResult) { 357 for _, msg := range msgs { 358 result.unserialize(msg) 359 } 360 return result 361 } 362 363 func (result *IPSetResult) unserialize(msg []byte) { 364 result.Nfgenmsg = nl.DeserializeNfgenmsg(msg) 365 366 for attr := range nl.ParseAttributes(msg[4:]) { 367 switch attr.Type { 368 case nl.IPSET_ATTR_PROTOCOL: 369 result.Protocol = attr.Value[0] 370 case nl.IPSET_ATTR_SETNAME: 371 result.SetName = nl.BytesToString(attr.Value) 372 case nl.IPSET_ATTR_COMMENT: 373 result.Comment = nl.BytesToString(attr.Value) 374 case nl.IPSET_ATTR_TYPENAME: 375 result.TypeName = nl.BytesToString(attr.Value) 376 case nl.IPSET_ATTR_REVISION: 377 result.Revision = attr.Value[0] 378 case nl.IPSET_ATTR_FAMILY: 379 result.Family = attr.Value[0] 380 case nl.IPSET_ATTR_FLAGS: 381 result.Flags = attr.Value[0] 382 case nl.IPSET_ATTR_DATA | nl.NLA_F_NESTED: 383 result.parseAttrData(attr.Value) 384 case nl.IPSET_ATTR_ADT | nl.NLA_F_NESTED: 385 result.parseAttrADT(attr.Value) 386 case nl.IPSET_ATTR_PROTOCOL_MIN: 387 result.ProtocolMinVersion = attr.Value[0] 388 case nl.IPSET_ATTR_MARKMASK: 389 result.MarkMask = attr.Uint32() 390 default: 391 log.Printf("unknown ipset attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK) 392 } 393 } 394 } 395 396 func (result *IPSetResult) parseAttrData(data []byte) { 397 for attr := range nl.ParseAttributes(data) { 398 switch attr.Type { 399 case nl.IPSET_ATTR_HASHSIZE | nl.NLA_F_NET_BYTEORDER: 400 result.HashSize = attr.Uint32() 401 case nl.IPSET_ATTR_MAXELEM | nl.NLA_F_NET_BYTEORDER: 402 result.MaxElements = attr.Uint32() 403 case nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER: 404 val := attr.Uint32() 405 result.Timeout = &val 406 case nl.IPSET_ATTR_ELEMENTS | nl.NLA_F_NET_BYTEORDER: 407 result.NumEntries = attr.Uint32() 408 case nl.IPSET_ATTR_REFERENCES | nl.NLA_F_NET_BYTEORDER: 409 result.References = attr.Uint32() 410 case nl.IPSET_ATTR_MEMSIZE | nl.NLA_F_NET_BYTEORDER: 411 result.SizeInMemory = attr.Uint32() 412 case nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER: 413 result.CadtFlags = attr.Uint32() 414 case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED: 415 for nested := range nl.ParseAttributes(attr.Value) { 416 switch nested.Type { 417 case nl.IPSET_ATTR_IP | nl.NLA_F_NET_BYTEORDER: 418 result.Entries = append(result.Entries, IPSetEntry{IP: nested.Value}) 419 case nl.IPSET_ATTR_IP: 420 result.IPFrom = nested.Value 421 default: 422 log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK) 423 } 424 } 425 case nl.IPSET_ATTR_IP_TO | nl.NLA_F_NESTED: 426 for nested := range nl.ParseAttributes(attr.Value) { 427 switch nested.Type { 428 case nl.IPSET_ATTR_IP: 429 result.IPTo = nested.Value 430 default: 431 log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK) 432 } 433 } 434 case nl.IPSET_ATTR_PORT_FROM | nl.NLA_F_NET_BYTEORDER: 435 result.PortFrom = networkOrder.Uint16(attr.Value) 436 case nl.IPSET_ATTR_PORT_TO | nl.NLA_F_NET_BYTEORDER: 437 result.PortTo = networkOrder.Uint16(attr.Value) 438 case nl.IPSET_ATTR_CADT_LINENO | nl.NLA_F_NET_BYTEORDER: 439 result.LineNo = attr.Uint32() 440 case nl.IPSET_ATTR_COMMENT: 441 result.Comment = nl.BytesToString(attr.Value) 442 case nl.IPSET_ATTR_MARKMASK: 443 result.MarkMask = attr.Uint32() 444 default: 445 log.Printf("unknown ipset data attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK) 446 } 447 } 448 } 449 450 func (result *IPSetResult) parseAttrADT(data []byte) { 451 for attr := range nl.ParseAttributes(data) { 452 switch attr.Type { 453 case nl.IPSET_ATTR_DATA | nl.NLA_F_NESTED: 454 result.Entries = append(result.Entries, parseIPSetEntry(attr.Value)) 455 default: 456 log.Printf("unknown ADT attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK) 457 } 458 } 459 } 460 461 func parseIPSetEntry(data []byte) (entry IPSetEntry) { 462 for attr := range nl.ParseAttributes(data) { 463 switch attr.Type { 464 case nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER: 465 val := attr.Uint32() 466 entry.Timeout = &val 467 case nl.IPSET_ATTR_BYTES | nl.NLA_F_NET_BYTEORDER: 468 val := attr.Uint64() 469 entry.Bytes = &val 470 case nl.IPSET_ATTR_PACKETS | nl.NLA_F_NET_BYTEORDER: 471 val := attr.Uint64() 472 entry.Packets = &val 473 case nl.IPSET_ATTR_ETHER: 474 entry.MAC = net.HardwareAddr(attr.Value) 475 case nl.IPSET_ATTR_IP: 476 entry.IP = net.IP(attr.Value) 477 case nl.IPSET_ATTR_COMMENT: 478 entry.Comment = nl.BytesToString(attr.Value) 479 case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED: 480 for attr := range nl.ParseAttributes(attr.Value) { 481 switch attr.Type { 482 case nl.IPSET_ATTR_IP: 483 entry.IP = net.IP(attr.Value) 484 default: 485 log.Printf("unknown nested ADT attribute from kernel: %+v", attr) 486 } 487 } 488 case nl.IPSET_ATTR_IP2 | nl.NLA_F_NESTED: 489 for attr := range nl.ParseAttributes(attr.Value) { 490 switch attr.Type { 491 case nl.IPSET_ATTR_IP: 492 entry.IP2 = net.IP(attr.Value) 493 default: 494 log.Printf("unknown nested ADT attribute from kernel: %+v", attr) 495 } 496 } 497 case nl.IPSET_ATTR_CIDR: 498 entry.CIDR = attr.Value[0] 499 case nl.IPSET_ATTR_CIDR2: 500 entry.CIDR2 = attr.Value[0] 501 case nl.IPSET_ATTR_PORT | nl.NLA_F_NET_BYTEORDER: 502 val := networkOrder.Uint16(attr.Value) 503 entry.Port = &val 504 case nl.IPSET_ATTR_PROTO: 505 val := attr.Value[0] 506 entry.Protocol = &val 507 case nl.IPSET_ATTR_IFACE: 508 entry.IFace = nl.BytesToString(attr.Value) 509 case nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER: 510 val := attr.Uint32() 511 entry.Mark = &val 512 default: 513 log.Printf("unknown ADT attribute from kernel: %+v", attr) 514 } 515 } 516 return 517 }