package rtnl import ( "net" "github.com/jsimonetti/rtnetlink/internal/unix" "github.com/jsimonetti/rtnetlink" ) // 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 }