github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/addr_linux.go (about)

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