mirror of
https://github.com/jsimonetti/rtnetlink.git
synced 2026-03-31 09:51:08 +02:00
In order to update to v2.0.0, we need to update the module path per the Go module documentation. * https://go.dev/doc/modules/major-version Fixes: https://github.com/jsimonetti/rtnetlink/issues/225 This can be released as v2.0.1 Signed-off-by: SuperQ <superq@gmail.com>
196 lines
4.7 KiB
Go
196 lines
4.7 KiB
Go
package rtnl
|
|
|
|
import (
|
|
"net"
|
|
|
|
"github.com/jsimonetti/rtnetlink/v2/internal/unix"
|
|
|
|
"github.com/jsimonetti/rtnetlink/v2"
|
|
)
|
|
|
|
// AddrAdd associates an IP-address with an interface.
|
|
//
|
|
// iface, _ := net.InterfaceByName("lo")
|
|
// conn.AddrAdd(iface, rtnl.MustParseAddr("127.0.0.1/8"))
|
|
func (c *Conn) AddrAdd(ifc *net.Interface, addr *net.IPNet) error {
|
|
af, err := addrFamily(addr.IP)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
scope := addrScope(addr.IP)
|
|
prefixlen, _ := addr.Mask.Size()
|
|
tx := &rtnetlink.AddressMessage{
|
|
Family: uint8(af),
|
|
PrefixLength: uint8(prefixlen),
|
|
Scope: uint8(scope),
|
|
Index: uint32(ifc.Index),
|
|
Attributes: &rtnetlink.AddressAttributes{
|
|
Address: addr.IP,
|
|
Local: addr.IP,
|
|
},
|
|
}
|
|
if ip4 := addr.IP.To4(); ip4 != nil {
|
|
tx.Attributes.Broadcast = broadcastAddr(addr)
|
|
tx.Attributes.Address = ip4
|
|
tx.Attributes.Local = ip4
|
|
}
|
|
return c.Conn.Address.New(tx)
|
|
}
|
|
|
|
// AddrDel revokes an IP-address from an interface.
|
|
//
|
|
// iface, _ := net.InterfaceByName("lo")
|
|
// conn.AddrDel(iface, rtnl.MustParseAddr("127.0.0.1/8"))
|
|
func (c *Conn) AddrDel(ifc *net.Interface, addr *net.IPNet) error {
|
|
af, err := addrFamily(addr.IP)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
prefixlen, _ := addr.Mask.Size()
|
|
rx, err := c.Addrs(ifc, af)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, v := range rx {
|
|
plen, _ := v.Mask.Size()
|
|
if plen == prefixlen && v.IP.Equal(addr.IP) {
|
|
tx := &rtnetlink.AddressMessage{
|
|
Family: uint8(af),
|
|
PrefixLength: uint8(prefixlen),
|
|
Index: uint32(ifc.Index),
|
|
Attributes: &rtnetlink.AddressAttributes{
|
|
Address: addr.IP,
|
|
},
|
|
}
|
|
if ip4 := addr.IP.To4(); ip4 != nil {
|
|
tx.Attributes.Broadcast = broadcastAddr(addr)
|
|
tx.Attributes.Address = ip4
|
|
}
|
|
return c.Conn.Address.Delete(tx)
|
|
}
|
|
}
|
|
return &net.AddrError{Err: "address not found", Addr: addr.IP.String()}
|
|
}
|
|
|
|
// Addrs returns IP addresses matching the interface and address family.
|
|
// To retrieve all addresses configured for the system, run:
|
|
//
|
|
// conn.Addrs(nil, 0)
|
|
func (c *Conn) Addrs(ifc *net.Interface, family int) (out []*net.IPNet, err error) {
|
|
rx, err := c.Conn.Address.List()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
match := func(v *rtnetlink.AddressMessage, ifc *net.Interface, family int) bool {
|
|
if ifc != nil && v.Index != uint32(ifc.Index) {
|
|
return false
|
|
}
|
|
if family != 0 && v.Family != uint8(family) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
for _, m := range rx {
|
|
if match(&m, ifc, family) {
|
|
bitlen := 8 * len(m.Attributes.Address)
|
|
a := &net.IPNet{
|
|
IP: m.Attributes.Address,
|
|
Mask: net.CIDRMask(int(m.PrefixLength), bitlen),
|
|
}
|
|
out = append(out, a)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// ParseAddr parses a CIDR string into a host address and network mask.
|
|
// This is a convenience wrapper around net.ParseCIDR(), which surprisingly
|
|
// returns the network address and mask instead of the host address and mask.
|
|
func ParseAddr(s string) (*net.IPNet, error) {
|
|
addr, cidr, err := net.ParseCIDR(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Overwrite cidr's network address with the host address
|
|
// parsed from the string representation.
|
|
cidr.IP = addr
|
|
|
|
if isSubnetAddr(cidr) {
|
|
return nil, &net.AddrError{
|
|
Err: "attempted to parse a subnet address into a host address",
|
|
Addr: cidr.IP.String(),
|
|
}
|
|
}
|
|
|
|
return cidr, nil
|
|
}
|
|
|
|
// MustParseAddr wraps ParseAddr, but panics on error.
|
|
// Use to conveniently parse a known-valid or hardcoded
|
|
// address into a function argument.
|
|
//
|
|
// iface, _ := net.InterfaceByName("enp2s0")
|
|
// conn.AddrDel(iface, rtnl.MustParseAddr("10.1.1.1/24"))
|
|
func MustParseAddr(s string) *net.IPNet {
|
|
n, err := ParseAddr(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return n
|
|
}
|
|
|
|
func addrFamily(ip net.IP) (int, error) {
|
|
if ip.To4() != nil {
|
|
return unix.AF_INET, nil
|
|
}
|
|
if len(ip) == net.IPv6len {
|
|
return unix.AF_INET6, nil
|
|
}
|
|
return 0, &net.AddrError{Err: "invalid IP address", Addr: ip.String()}
|
|
}
|
|
|
|
func addrScope(ip net.IP) int {
|
|
if ip.IsGlobalUnicast() {
|
|
return unix.RT_SCOPE_UNIVERSE
|
|
}
|
|
if ip.IsLoopback() {
|
|
return unix.RT_SCOPE_HOST
|
|
}
|
|
return unix.RT_SCOPE_LINK
|
|
}
|
|
|
|
func broadcastAddr(ipnet *net.IPNet) net.IP {
|
|
ip := ipnet.IP.To4()
|
|
if ip == nil {
|
|
return nil
|
|
}
|
|
mask := net.IP(ipnet.Mask).To4()
|
|
n := len(ip)
|
|
if n != len(mask) {
|
|
return nil
|
|
}
|
|
out := make(net.IP, n)
|
|
for i := 0; i < n; i++ {
|
|
out[i] = ip[i] | ^mask[i]
|
|
}
|
|
return out
|
|
}
|
|
|
|
// isSubnetAddr returns true if ipnet is a network (subnet) address.
|
|
// It applies ipnet's subnet mask onto itself and compares the result to the
|
|
// value of ipnet's IP field.
|
|
func isSubnetAddr(ipnet *net.IPNet) bool {
|
|
// Addresses with /32 and /128 are always hosts.
|
|
if ones, bits := ipnet.Mask.Size(); ones == bits {
|
|
return false
|
|
}
|
|
|
|
if ipnet.IP.Mask(ipnet.Mask).Equal(ipnet.IP) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|