|
|
|
@ -16,6 +16,7 @@ import (
|
|
|
|
"tailscale.com/envknob"
|
|
|
|
"tailscale.com/envknob"
|
|
|
|
"tailscale.com/net/tsaddr"
|
|
|
|
"tailscale.com/net/tsaddr"
|
|
|
|
"tailscale.com/types/logger"
|
|
|
|
"tailscale.com/types/logger"
|
|
|
|
|
|
|
|
"tailscale.com/util/eventbus"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
var debugNetlinkMessages = envknob.RegisterBool("TS_DEBUG_NETLINK")
|
|
|
|
var debugNetlinkMessages = envknob.RegisterBool("TS_DEBUG_NETLINK")
|
|
|
|
@ -27,15 +28,26 @@ type unspecifiedMessage struct{}
|
|
|
|
|
|
|
|
|
|
|
|
func (unspecifiedMessage) ignore() bool { return false }
|
|
|
|
func (unspecifiedMessage) ignore() bool { return false }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// RuleDeleted reports that one of Tailscale's policy routing rules
|
|
|
|
|
|
|
|
// was deleted.
|
|
|
|
|
|
|
|
type RuleDeleted struct {
|
|
|
|
|
|
|
|
// Table is the table number that the deleted rule referenced.
|
|
|
|
|
|
|
|
Table uint8
|
|
|
|
|
|
|
|
// Priority is the lookup priority of the deleted rule.
|
|
|
|
|
|
|
|
Priority uint32
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// nlConn wraps a *netlink.Conn and returns a monitor.Message
|
|
|
|
// nlConn wraps a *netlink.Conn and returns a monitor.Message
|
|
|
|
// instead of a netlink.Message. Currently, messages are discarded,
|
|
|
|
// instead of a netlink.Message. Currently, messages are discarded,
|
|
|
|
// but down the line, when messages trigger different logic depending
|
|
|
|
// but down the line, when messages trigger different logic depending
|
|
|
|
// on the type of event, this provides the capability of handling
|
|
|
|
// on the type of event, this provides the capability of handling
|
|
|
|
// each architecture-specific message in a generic fashion.
|
|
|
|
// each architecture-specific message in a generic fashion.
|
|
|
|
type nlConn struct {
|
|
|
|
type nlConn struct {
|
|
|
|
logf logger.Logf
|
|
|
|
busClient *eventbus.Client
|
|
|
|
conn *netlink.Conn
|
|
|
|
rulesDeleted *eventbus.Publisher[RuleDeleted]
|
|
|
|
buffered []netlink.Message
|
|
|
|
logf logger.Logf
|
|
|
|
|
|
|
|
conn *netlink.Conn
|
|
|
|
|
|
|
|
buffered []netlink.Message
|
|
|
|
|
|
|
|
|
|
|
|
// addrCache maps interface indices to a set of addresses, and is
|
|
|
|
// addrCache maps interface indices to a set of addresses, and is
|
|
|
|
// used to suppress duplicate RTM_NEWADDR messages. It is populated
|
|
|
|
// used to suppress duplicate RTM_NEWADDR messages. It is populated
|
|
|
|
@ -44,7 +56,7 @@ type nlConn struct {
|
|
|
|
addrCache map[uint32]map[netip.Addr]bool
|
|
|
|
addrCache map[uint32]map[netip.Addr]bool
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func newOSMon(logf logger.Logf, m *Monitor) (osMon, error) {
|
|
|
|
func newOSMon(bus *eventbus.Bus, logf logger.Logf, m *Monitor) (osMon, error) {
|
|
|
|
conn, err := netlink.Dial(unix.NETLINK_ROUTE, &netlink.Config{
|
|
|
|
conn, err := netlink.Dial(unix.NETLINK_ROUTE, &netlink.Config{
|
|
|
|
// Routes get us most of the events of interest, but we need
|
|
|
|
// Routes get us most of the events of interest, but we need
|
|
|
|
// address as well to cover things like DHCP deciding to give
|
|
|
|
// address as well to cover things like DHCP deciding to give
|
|
|
|
@ -59,12 +71,22 @@ func newOSMon(logf logger.Logf, m *Monitor) (osMon, error) {
|
|
|
|
logf("monitor_linux: AF_NETLINK RTMGRP failed, falling back to polling")
|
|
|
|
logf("monitor_linux: AF_NETLINK RTMGRP failed, falling back to polling")
|
|
|
|
return newPollingMon(logf, m)
|
|
|
|
return newPollingMon(logf, m)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &nlConn{logf: logf, conn: conn, addrCache: make(map[uint32]map[netip.Addr]bool)}, nil
|
|
|
|
client := bus.Client("netmon-iprules")
|
|
|
|
|
|
|
|
return &nlConn{
|
|
|
|
|
|
|
|
busClient: client,
|
|
|
|
|
|
|
|
rulesDeleted: eventbus.Publish[RuleDeleted](client),
|
|
|
|
|
|
|
|
logf: logf,
|
|
|
|
|
|
|
|
conn: conn,
|
|
|
|
|
|
|
|
addrCache: make(map[uint32]map[netip.Addr]bool),
|
|
|
|
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c *nlConn) IsInterestingInterface(iface string) bool { return true }
|
|
|
|
func (c *nlConn) IsInterestingInterface(iface string) bool { return true }
|
|
|
|
|
|
|
|
|
|
|
|
func (c *nlConn) Close() error { return c.conn.Close() }
|
|
|
|
func (c *nlConn) Close() error {
|
|
|
|
|
|
|
|
c.busClient.Close()
|
|
|
|
|
|
|
|
return c.conn.Close()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (c *nlConn) Receive() (message, error) {
|
|
|
|
func (c *nlConn) Receive() (message, error) {
|
|
|
|
if len(c.buffered) == 0 {
|
|
|
|
if len(c.buffered) == 0 {
|
|
|
|
@ -219,6 +241,10 @@ func (c *nlConn) Receive() (message, error) {
|
|
|
|
// On `ip -4 rule del pref 5210 table main`, logs:
|
|
|
|
// On `ip -4 rule del pref 5210 table main`, logs:
|
|
|
|
// monitor: ip rule deleted: {Family:2 DstLength:0 SrcLength:0 Tos:0 Table:254 Protocol:0 Scope:0 Type:1 Flags:0 Attributes:{Dst:<nil> Src:<nil> Gateway:<nil> OutIface:0 Priority:5210 Table:254 Mark:4294967295 Expires:<nil> Metrics:<nil> Multipath:[]}}
|
|
|
|
// monitor: ip rule deleted: {Family:2 DstLength:0 SrcLength:0 Tos:0 Table:254 Protocol:0 Scope:0 Type:1 Flags:0 Attributes:{Dst:<nil> Src:<nil> Gateway:<nil> OutIface:0 Priority:5210 Table:254 Mark:4294967295 Expires:<nil> Metrics:<nil> Multipath:[]}}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.rulesDeleted.Publish(RuleDeleted{
|
|
|
|
|
|
|
|
Table: rmsg.Table,
|
|
|
|
|
|
|
|
Priority: rmsg.Attributes.Priority,
|
|
|
|
|
|
|
|
})
|
|
|
|
rdm := ipRuleDeletedMessage{
|
|
|
|
rdm := ipRuleDeletedMessage{
|
|
|
|
table: rmsg.Table,
|
|
|
|
table: rmsg.Table,
|
|
|
|
priority: rmsg.Attributes.Priority,
|
|
|
|
priority: rmsg.Attributes.Priority,
|
|
|
|
|