123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- package netlink
- import (
- "fmt"
- "net"
- "strings"
- "syscall"
- "github.com/vishvananda/netlink/nl"
- "github.com/vishvananda/netns"
- "golang.org/x/sys/unix"
- )
- // IFA_FLAGS is a u32 attribute.
- const IFA_FLAGS = 0x8
- // AddrAdd will add an IP address to a link device.
- // Equivalent to: `ip addr add $addr dev $link`
- func AddrAdd(link Link, addr *Addr) error {
- return pkgHandle.AddrAdd(link, addr)
- }
- // AddrAdd will add an IP address to a link device.
- // Equivalent to: `ip addr add $addr dev $link`
- func (h *Handle) AddrAdd(link Link, addr *Addr) error {
- req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
- return h.addrHandle(link, addr, req)
- }
- // AddrReplace will replace (or, if not present, add) an IP address on a link device.
- // Equivalent to: `ip addr replace $addr dev $link`
- func AddrReplace(link Link, addr *Addr) error {
- return pkgHandle.AddrReplace(link, addr)
- }
- // AddrReplace will replace (or, if not present, add) an IP address on a link device.
- // Equivalent to: `ip addr replace $addr dev $link`
- func (h *Handle) AddrReplace(link Link, addr *Addr) error {
- req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_REPLACE|unix.NLM_F_ACK)
- return h.addrHandle(link, addr, req)
- }
- // AddrDel will delete an IP address from a link device.
- // Equivalent to: `ip addr del $addr dev $link`
- func AddrDel(link Link, addr *Addr) error {
- return pkgHandle.AddrDel(link, addr)
- }
- // AddrDel will delete an IP address from a link device.
- // Equivalent to: `ip addr del $addr dev $link`
- func (h *Handle) AddrDel(link Link, addr *Addr) error {
- req := h.newNetlinkRequest(unix.RTM_DELADDR, unix.NLM_F_ACK)
- return h.addrHandle(link, addr, req)
- }
- func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
- base := link.Attrs()
- if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) {
- return fmt.Errorf("label must begin with interface name")
- }
- h.ensureIndex(base)
- family := nl.GetIPFamily(addr.IP)
- msg := nl.NewIfAddrmsg(family)
- msg.Index = uint32(base.Index)
- msg.Scope = uint8(addr.Scope)
- mask := addr.Mask
- if addr.Peer != nil {
- mask = addr.Peer.Mask
- }
- prefixlen, masklen := mask.Size()
- msg.Prefixlen = uint8(prefixlen)
- req.AddData(msg)
- var localAddrData []byte
- if family == FAMILY_V4 {
- localAddrData = addr.IP.To4()
- } else {
- localAddrData = addr.IP.To16()
- }
- localData := nl.NewRtAttr(unix.IFA_LOCAL, localAddrData)
- req.AddData(localData)
- var peerAddrData []byte
- if addr.Peer != nil {
- if family == FAMILY_V4 {
- peerAddrData = addr.Peer.IP.To4()
- } else {
- peerAddrData = addr.Peer.IP.To16()
- }
- } else {
- peerAddrData = localAddrData
- }
- addressData := nl.NewRtAttr(unix.IFA_ADDRESS, peerAddrData)
- req.AddData(addressData)
- if addr.Flags != 0 {
- if addr.Flags <= 0xff {
- msg.IfAddrmsg.Flags = uint8(addr.Flags)
- } else {
- b := make([]byte, 4)
- native.PutUint32(b, uint32(addr.Flags))
- flagsData := nl.NewRtAttr(IFA_FLAGS, b)
- req.AddData(flagsData)
- }
- }
- if family == FAMILY_V4 {
- if addr.Broadcast == nil {
- calcBroadcast := make(net.IP, masklen/8)
- for i := range localAddrData {
- calcBroadcast[i] = localAddrData[i] | ^mask[i]
- }
- addr.Broadcast = calcBroadcast
- }
- req.AddData(nl.NewRtAttr(unix.IFA_BROADCAST, addr.Broadcast))
- if addr.Label != "" {
- labelData := nl.NewRtAttr(unix.IFA_LABEL, nl.ZeroTerminated(addr.Label))
- req.AddData(labelData)
- }
- }
- // 0 is the default value for these attributes. However, 0 means "expired", while the least-surprising default
- // value should be "forever". To compensate for that, only add the attributes if at least one of the values is
- // non-zero, which means the caller has explicitly set them
- if addr.ValidLft > 0 || addr.PreferedLft > 0 {
- cachedata := nl.IfaCacheInfo{
- IfaValid: uint32(addr.ValidLft),
- IfaPrefered: uint32(addr.PreferedLft),
- }
- req.AddData(nl.NewRtAttr(unix.IFA_CACHEINFO, cachedata.Serialize()))
- }
- _, err := req.Execute(unix.NETLINK_ROUTE, 0)
- return err
- }
- // AddrList gets a list of IP addresses in the system.
- // Equivalent to: `ip addr show`.
- // The list can be filtered by link and ip family.
- func AddrList(link Link, family int) ([]Addr, error) {
- return pkgHandle.AddrList(link, family)
- }
- // AddrList gets a list of IP addresses in the system.
- // Equivalent to: `ip addr show`.
- // The list can be filtered by link and ip family.
- func (h *Handle) AddrList(link Link, family int) ([]Addr, error) {
- req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP)
- msg := nl.NewIfInfomsg(family)
- req.AddData(msg)
- msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR)
- if err != nil {
- return nil, err
- }
- indexFilter := 0
- if link != nil {
- base := link.Attrs()
- h.ensureIndex(base)
- indexFilter = base.Index
- }
- var res []Addr
- for _, m := range msgs {
- addr, msgFamily, ifindex, err := parseAddr(m)
- if err != nil {
- return res, err
- }
- if link != nil && ifindex != indexFilter {
- // Ignore messages from other interfaces
- continue
- }
- if family != FAMILY_ALL && msgFamily != family {
- continue
- }
- res = append(res, addr)
- }
- return res, nil
- }
- func parseAddr(m []byte) (addr Addr, family, index int, err error) {
- msg := nl.DeserializeIfAddrmsg(m)
- family = -1
- index = -1
- attrs, err1 := nl.ParseRouteAttr(m[msg.Len():])
- if err1 != nil {
- err = err1
- return
- }
- family = int(msg.Family)
- index = int(msg.Index)
- var local, dst *net.IPNet
- for _, attr := range attrs {
- switch attr.Attr.Type {
- case unix.IFA_ADDRESS:
- dst = &net.IPNet{
- IP: attr.Value,
- Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
- }
- case unix.IFA_LOCAL:
- // iproute2 manual:
- // If a peer address is specified, the local address
- // cannot have a prefix length. The network prefix is
- // associated with the peer rather than with the local
- // address.
- n := 8 * len(attr.Value)
- local = &net.IPNet{
- IP: attr.Value,
- Mask: net.CIDRMask(n, n),
- }
- case unix.IFA_BROADCAST:
- addr.Broadcast = attr.Value
- case unix.IFA_LABEL:
- addr.Label = string(attr.Value[:len(attr.Value)-1])
- case IFA_FLAGS:
- addr.Flags = int(native.Uint32(attr.Value[0:4]))
- case nl.IFA_CACHEINFO:
- ci := nl.DeserializeIfaCacheInfo(attr.Value)
- addr.PreferedLft = int(ci.IfaPrefered)
- addr.ValidLft = int(ci.IfaValid)
- }
- }
- // libnl addr.c comment:
- // IPv6 sends the local address as IFA_ADDRESS with no
- // IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS
- // with IFA_ADDRESS being the peer address if they differ
- //
- // But obviously, as there are IPv6 PtP addresses, too,
- // IFA_LOCAL should also be handled for IPv6.
- if local != nil {
- if family == FAMILY_V4 && local.IP.Equal(dst.IP) {
- addr.IPNet = dst
- } else {
- addr.IPNet = local
- addr.Peer = dst
- }
- } else {
- addr.IPNet = dst
- }
- addr.Scope = int(msg.Scope)
- return
- }
- type AddrUpdate struct {
- LinkAddress net.IPNet
- LinkIndex int
- Flags int
- Scope int
- PreferedLft int
- ValidLft int
- NewAddr bool // true=added false=deleted
- }
- // AddrSubscribe takes a chan down which notifications will be sent
- // when addresses change. Close the 'done' chan to stop subscription.
- func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error {
- return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
- }
- // AddrSubscribeAt works like AddrSubscribe plus it allows the caller
- // to choose the network namespace in which to subscribe (ns).
- func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error {
- return addrSubscribeAt(ns, netns.None(), ch, done, nil, false)
- }
- // AddrSubscribeOptions contains a set of options to use with
- // AddrSubscribeWithOptions.
- type AddrSubscribeOptions struct {
- Namespace *netns.NsHandle
- ErrorCallback func(error)
- ListExisting bool
- }
- // AddrSubscribeWithOptions work like AddrSubscribe but enable to
- // provide additional options to modify the behavior. Currently, the
- // namespace can be provided as well as an error callback.
- func AddrSubscribeWithOptions(ch chan<- AddrUpdate, done <-chan struct{}, options AddrSubscribeOptions) error {
- if options.Namespace == nil {
- none := netns.None()
- options.Namespace = &none
- }
- return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
- }
- func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
- s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_IFADDR, unix.RTNLGRP_IPV6_IFADDR)
- if err != nil {
- return err
- }
- if done != nil {
- go func() {
- <-done
- s.Close()
- }()
- }
- if listExisting {
- req := pkgHandle.newNetlinkRequest(unix.RTM_GETADDR,
- unix.NLM_F_DUMP)
- infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
- req.AddData(infmsg)
- if err := s.Send(req); err != nil {
- return err
- }
- }
- go func() {
- defer close(ch)
- for {
- msgs, err := s.Receive()
- if err != nil {
- if cberr != nil {
- cberr(err)
- }
- return
- }
- for _, m := range msgs {
- if m.Header.Type == unix.NLMSG_DONE {
- continue
- }
- if m.Header.Type == unix.NLMSG_ERROR {
- native := nl.NativeEndian()
- error := int32(native.Uint32(m.Data[0:4]))
- if error == 0 {
- continue
- }
- if cberr != nil {
- cberr(syscall.Errno(-error))
- }
- return
- }
- msgType := m.Header.Type
- if msgType != unix.RTM_NEWADDR && msgType != unix.RTM_DELADDR {
- if cberr != nil {
- cberr(fmt.Errorf("bad message type: %d", msgType))
- }
- return
- }
- addr, _, ifindex, err := parseAddr(m.Data)
- if err != nil {
- if cberr != nil {
- cberr(fmt.Errorf("could not parse address: %v", err))
- }
- return
- }
- ch <- AddrUpdate{LinkAddress: *addr.IPNet,
- LinkIndex: ifindex,
- NewAddr: msgType == unix.RTM_NEWADDR,
- Flags: addr.Flags,
- Scope: addr.Scope,
- PreferedLft: addr.PreferedLft,
- ValidLft: addr.ValidLft}
- }
- }
- }()
- return nil
- }
|