github.com/vishvananda/netlink@v1.3.0/addr_linux.go (about)

     1  package netlink
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strings"
     7  	"syscall"
     8  
     9  	"github.com/vishvananda/netlink/nl"
    10  	"github.com/vishvananda/netns"
    11  	"golang.org/x/sys/unix"
    12  )
    13  
    14  // AddrAdd will add an IP address to a link device.
    15  //
    16  // Equivalent to: `ip addr add $addr dev $link`
    17  //
    18  // If `addr` is an IPv4 address and the broadcast address is not given, it
    19  // will be automatically computed based on the IP mask if /30 or larger.
    20  func AddrAdd(link Link, addr *Addr) error {
    21  	return pkgHandle.AddrAdd(link, addr)
    22  }
    23  
    24  // AddrAdd will add an IP address to a link device.
    25  //
    26  // Equivalent to: `ip addr add $addr dev $link`
    27  //
    28  // If `addr` is an IPv4 address and the broadcast address is not given, it
    29  // will be automatically computed based on the IP mask if /30 or larger.
    30  func (h *Handle) AddrAdd(link Link, addr *Addr) error {
    31  	req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
    32  	return h.addrHandle(link, addr, req)
    33  }
    34  
    35  // AddrReplace will replace (or, if not present, add) an IP address on a link device.
    36  //
    37  // Equivalent to: `ip addr replace $addr dev $link`
    38  //
    39  // If `addr` is an IPv4 address and the broadcast address is not given, it
    40  // will be automatically computed based on the IP mask if /30 or larger.
    41  func AddrReplace(link Link, addr *Addr) error {
    42  	return pkgHandle.AddrReplace(link, addr)
    43  }
    44  
    45  // AddrReplace will replace (or, if not present, add) an IP address on a link device.
    46  //
    47  // Equivalent to: `ip addr replace $addr dev $link`
    48  //
    49  // If `addr` is an IPv4 address and the broadcast address is not given, it
    50  // will be automatically computed based on the IP mask if /30 or larger.
    51  func (h *Handle) AddrReplace(link Link, addr *Addr) error {
    52  	req := h.newNetlinkRequest(unix.RTM_NEWADDR, unix.NLM_F_CREATE|unix.NLM_F_REPLACE|unix.NLM_F_ACK)
    53  	return h.addrHandle(link, addr, req)
    54  }
    55  
    56  // AddrDel will delete an IP address from a link device.
    57  //
    58  // Equivalent to: `ip addr del $addr dev $link`
    59  //
    60  // If `addr` is an IPv4 address and the broadcast address is not given, it
    61  // will be automatically computed based on the IP mask if /30 or larger.
    62  func AddrDel(link Link, addr *Addr) error {
    63  	return pkgHandle.AddrDel(link, addr)
    64  }
    65  
    66  // AddrDel will delete an IP address from a link device.
    67  // Equivalent to: `ip addr del $addr dev $link`
    68  //
    69  // If `addr` is an IPv4 address and the broadcast address is not given, it
    70  // will be automatically computed based on the IP mask if /30 or larger.
    71  func (h *Handle) AddrDel(link Link, addr *Addr) error {
    72  	req := h.newNetlinkRequest(unix.RTM_DELADDR, unix.NLM_F_ACK)
    73  	return h.addrHandle(link, addr, req)
    74  }
    75  
    76  func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
    77  	family := nl.GetIPFamily(addr.IP)
    78  	msg := nl.NewIfAddrmsg(family)
    79  	msg.Scope = uint8(addr.Scope)
    80  	if link == nil {
    81  		msg.Index = uint32(addr.LinkIndex)
    82  	} else {
    83  		base := link.Attrs()
    84  		if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) {
    85  			return fmt.Errorf("label must begin with interface name")
    86  		}
    87  		h.ensureIndex(base)
    88  		msg.Index = uint32(base.Index)
    89  	}
    90  	mask := addr.Mask
    91  	if addr.Peer != nil {
    92  		mask = addr.Peer.Mask
    93  	}
    94  	prefixlen, masklen := mask.Size()
    95  	msg.Prefixlen = uint8(prefixlen)
    96  	req.AddData(msg)
    97  
    98  	var localAddrData []byte
    99  	if family == FAMILY_V4 {
   100  		localAddrData = addr.IP.To4()
   101  	} else {
   102  		localAddrData = addr.IP.To16()
   103  	}
   104  
   105  	localData := nl.NewRtAttr(unix.IFA_LOCAL, localAddrData)
   106  	req.AddData(localData)
   107  	var peerAddrData []byte
   108  	if addr.Peer != nil {
   109  		if family == FAMILY_V4 {
   110  			peerAddrData = addr.Peer.IP.To4()
   111  		} else {
   112  			peerAddrData = addr.Peer.IP.To16()
   113  		}
   114  	} else {
   115  		peerAddrData = localAddrData
   116  	}
   117  
   118  	addressData := nl.NewRtAttr(unix.IFA_ADDRESS, peerAddrData)
   119  	req.AddData(addressData)
   120  
   121  	if addr.Flags != 0 {
   122  		if addr.Flags <= 0xff {
   123  			msg.IfAddrmsg.Flags = uint8(addr.Flags)
   124  		} else {
   125  			b := make([]byte, 4)
   126  			native.PutUint32(b, uint32(addr.Flags))
   127  			flagsData := nl.NewRtAttr(unix.IFA_FLAGS, b)
   128  			req.AddData(flagsData)
   129  		}
   130  	}
   131  
   132  	if family == FAMILY_V4 {
   133  		// Automatically set the broadcast address if it is unset and the
   134  		// subnet is large enough to sensibly have one (/30 or larger).
   135  		// See: RFC 3021
   136  		if addr.Broadcast == nil && prefixlen < 31 {
   137  			calcBroadcast := make(net.IP, masklen/8)
   138  			for i := range localAddrData {
   139  				calcBroadcast[i] = localAddrData[i] | ^mask[i]
   140  			}
   141  			addr.Broadcast = calcBroadcast
   142  		}
   143  
   144  		if addr.Broadcast != nil {
   145  			req.AddData(nl.NewRtAttr(unix.IFA_BROADCAST, addr.Broadcast))
   146  		}
   147  
   148  		if addr.Label != "" {
   149  			labelData := nl.NewRtAttr(unix.IFA_LABEL, nl.ZeroTerminated(addr.Label))
   150  			req.AddData(labelData)
   151  		}
   152  	}
   153  
   154  	// 0 is the default value for these attributes. However, 0 means "expired", while the least-surprising default
   155  	// value should be "forever". To compensate for that, only add the attributes if at least one of the values is
   156  	// non-zero, which means the caller has explicitly set them
   157  	if addr.ValidLft > 0 || addr.PreferedLft > 0 {
   158  		cachedata := nl.IfaCacheInfo{unix.IfaCacheinfo{
   159  			Valid:    uint32(addr.ValidLft),
   160  			Prefered: uint32(addr.PreferedLft),
   161  		}}
   162  		req.AddData(nl.NewRtAttr(unix.IFA_CACHEINFO, cachedata.Serialize()))
   163  	}
   164  
   165  	_, err := req.Execute(unix.NETLINK_ROUTE, 0)
   166  	return err
   167  }
   168  
   169  // AddrList gets a list of IP addresses in the system.
   170  // Equivalent to: `ip addr show`.
   171  // The list can be filtered by link and ip family.
   172  func AddrList(link Link, family int) ([]Addr, error) {
   173  	return pkgHandle.AddrList(link, family)
   174  }
   175  
   176  // AddrList gets a list of IP addresses in the system.
   177  // Equivalent to: `ip addr show`.
   178  // The list can be filtered by link and ip family.
   179  func (h *Handle) AddrList(link Link, family int) ([]Addr, error) {
   180  	req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP)
   181  	msg := nl.NewIfAddrmsg(family)
   182  	req.AddData(msg)
   183  
   184  	msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	indexFilter := 0
   190  	if link != nil {
   191  		base := link.Attrs()
   192  		h.ensureIndex(base)
   193  		indexFilter = base.Index
   194  	}
   195  
   196  	var res []Addr
   197  	for _, m := range msgs {
   198  		addr, msgFamily, err := parseAddr(m)
   199  		if err != nil {
   200  			return res, err
   201  		}
   202  
   203  		if link != nil && addr.LinkIndex != indexFilter {
   204  			// Ignore messages from other interfaces
   205  			continue
   206  		}
   207  
   208  		if family != FAMILY_ALL && msgFamily != family {
   209  			continue
   210  		}
   211  
   212  		res = append(res, addr)
   213  	}
   214  
   215  	return res, nil
   216  }
   217  
   218  func parseAddr(m []byte) (addr Addr, family int, err error) {
   219  	msg := nl.DeserializeIfAddrmsg(m)
   220  
   221  	family = -1
   222  	addr.LinkIndex = -1
   223  
   224  	attrs, err1 := nl.ParseRouteAttr(m[msg.Len():])
   225  	if err1 != nil {
   226  		err = err1
   227  		return
   228  	}
   229  
   230  	family = int(msg.Family)
   231  	addr.LinkIndex = int(msg.Index)
   232  
   233  	var local, dst *net.IPNet
   234  	for _, attr := range attrs {
   235  		switch attr.Attr.Type {
   236  		case unix.IFA_ADDRESS:
   237  			dst = &net.IPNet{
   238  				IP:   attr.Value,
   239  				Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
   240  			}
   241  		case unix.IFA_LOCAL:
   242  			// iproute2 manual:
   243  			// If a peer address is specified, the local address
   244  			// cannot have a prefix length. The network prefix is
   245  			// associated with the peer rather than with the local
   246  			// address.
   247  			n := 8 * len(attr.Value)
   248  			local = &net.IPNet{
   249  				IP:   attr.Value,
   250  				Mask: net.CIDRMask(n, n),
   251  			}
   252  		case unix.IFA_BROADCAST:
   253  			addr.Broadcast = attr.Value
   254  		case unix.IFA_LABEL:
   255  			addr.Label = string(attr.Value[:len(attr.Value)-1])
   256  		case unix.IFA_FLAGS:
   257  			addr.Flags = int(native.Uint32(attr.Value[0:4]))
   258  		case unix.IFA_CACHEINFO:
   259  			ci := nl.DeserializeIfaCacheInfo(attr.Value)
   260  			addr.PreferedLft = int(ci.Prefered)
   261  			addr.ValidLft = int(ci.Valid)
   262  		}
   263  	}
   264  
   265  	// libnl addr.c comment:
   266  	// IPv6 sends the local address as IFA_ADDRESS with no
   267  	// IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS
   268  	// with IFA_ADDRESS being the peer address if they differ
   269  	//
   270  	// But obviously, as there are IPv6 PtP addresses, too,
   271  	// IFA_LOCAL should also be handled for IPv6.
   272  	if local != nil {
   273  		if family == FAMILY_V4 && dst != nil && local.IP.Equal(dst.IP) {
   274  			addr.IPNet = dst
   275  		} else {
   276  			addr.IPNet = local
   277  			addr.Peer = dst
   278  		}
   279  	} else {
   280  		addr.IPNet = dst
   281  	}
   282  
   283  	addr.Scope = int(msg.Scope)
   284  
   285  	return
   286  }
   287  
   288  type AddrUpdate struct {
   289  	LinkAddress net.IPNet
   290  	LinkIndex   int
   291  	Flags       int
   292  	Scope       int
   293  	PreferedLft int
   294  	ValidLft    int
   295  	NewAddr     bool // true=added false=deleted
   296  }
   297  
   298  // AddrSubscribe takes a chan down which notifications will be sent
   299  // when addresses change.  Close the 'done' chan to stop subscription.
   300  func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error {
   301  	return addrSubscribeAt(netns.None(), netns.None(), ch, done, nil, false, 0, nil, false)
   302  }
   303  
   304  // AddrSubscribeAt works like AddrSubscribe plus it allows the caller
   305  // to choose the network namespace in which to subscribe (ns).
   306  func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error {
   307  	return addrSubscribeAt(ns, netns.None(), ch, done, nil, false, 0, nil, false)
   308  }
   309  
   310  // AddrSubscribeOptions contains a set of options to use with
   311  // AddrSubscribeWithOptions.
   312  type AddrSubscribeOptions struct {
   313  	Namespace              *netns.NsHandle
   314  	ErrorCallback          func(error)
   315  	ListExisting           bool
   316  	ReceiveBufferSize      int
   317  	ReceiveBufferForceSize bool
   318  	ReceiveTimeout         *unix.Timeval
   319  }
   320  
   321  // AddrSubscribeWithOptions work like AddrSubscribe but enable to
   322  // provide additional options to modify the behavior. Currently, the
   323  // namespace can be provided as well as an error callback.
   324  func AddrSubscribeWithOptions(ch chan<- AddrUpdate, done <-chan struct{}, options AddrSubscribeOptions) error {
   325  	if options.Namespace == nil {
   326  		none := netns.None()
   327  		options.Namespace = &none
   328  	}
   329  	return addrSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting,
   330  		options.ReceiveBufferSize, options.ReceiveTimeout, options.ReceiveBufferForceSize)
   331  }
   332  
   333  func addrSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}, cberr func(error), listExisting bool,
   334  	rcvbuf int, rcvTimeout *unix.Timeval, rcvBufForce bool) error {
   335  	s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_IPV4_IFADDR, unix.RTNLGRP_IPV6_IFADDR)
   336  	if err != nil {
   337  		return err
   338  	}
   339  	if rcvTimeout != nil {
   340  		if err := s.SetReceiveTimeout(rcvTimeout); err != nil {
   341  			return err
   342  		}
   343  	}
   344  	if rcvbuf != 0 {
   345  		err = s.SetReceiveBufferSize(rcvbuf, rcvBufForce)
   346  		if err != nil {
   347  			return err
   348  		}
   349  	}
   350  	if done != nil {
   351  		go func() {
   352  			<-done
   353  			s.Close()
   354  		}()
   355  	}
   356  	if listExisting {
   357  		req := pkgHandle.newNetlinkRequest(unix.RTM_GETADDR,
   358  			unix.NLM_F_DUMP)
   359  		infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
   360  		req.AddData(infmsg)
   361  		if err := s.Send(req); err != nil {
   362  			return err
   363  		}
   364  	}
   365  	go func() {
   366  		defer close(ch)
   367  		for {
   368  			msgs, from, err := s.Receive()
   369  			if err != nil {
   370  				if cberr != nil {
   371  					cberr(fmt.Errorf("Receive failed: %v",
   372  						err))
   373  				}
   374  				return
   375  			}
   376  			if from.Pid != nl.PidKernel {
   377  				if cberr != nil {
   378  					cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel))
   379  				}
   380  				continue
   381  			}
   382  			for _, m := range msgs {
   383  				if m.Header.Type == unix.NLMSG_DONE {
   384  					continue
   385  				}
   386  				if m.Header.Type == unix.NLMSG_ERROR {
   387  					error := int32(native.Uint32(m.Data[0:4]))
   388  					if error == 0 {
   389  						continue
   390  					}
   391  					if cberr != nil {
   392  						cberr(fmt.Errorf("error message: %v",
   393  							syscall.Errno(-error)))
   394  					}
   395  					continue
   396  				}
   397  				msgType := m.Header.Type
   398  				if msgType != unix.RTM_NEWADDR && msgType != unix.RTM_DELADDR {
   399  					if cberr != nil {
   400  						cberr(fmt.Errorf("bad message type: %d", msgType))
   401  					}
   402  					continue
   403  				}
   404  
   405  				addr, _, err := parseAddr(m.Data)
   406  				if err != nil {
   407  					if cberr != nil {
   408  						cberr(fmt.Errorf("could not parse address: %v", err))
   409  					}
   410  					continue
   411  				}
   412  
   413  				ch <- AddrUpdate{LinkAddress: *addr.IPNet,
   414  					LinkIndex:   addr.LinkIndex,
   415  					NewAddr:     msgType == unix.RTM_NEWADDR,
   416  					Flags:       addr.Flags,
   417  					Scope:       addr.Scope,
   418  					PreferedLft: addr.PreferedLft,
   419  					ValidLft:    addr.ValidLft}
   420  			}
   421  		}
   422  	}()
   423  
   424  	return nil
   425  }