ipn/ipnlocal: add delegated interface information to /interfaces PeerAPI handler

Exposes the delegated interface data added by #7248 in the debug
endpoint. I would have found it useful when working on that PR, and
it may be handy in the future as well.

Also makes the interfaces table slightly easier to parse by adding
borders to it. To make then nicer-looking, the CSP was relaxed to allow
inline styles.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
This commit is contained in:
Mihai Parparita 2023-02-23 11:48:03 -08:00 committed by Mihai Parparita
parent e484e1c0fc
commit 780c56e119
3 changed files with 42 additions and 3 deletions

View File

@ -670,7 +670,7 @@ func (h *peerAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
if peerAPIRequestShouldGetSecurityHeaders(r) {
w.Header().Set("Content-Security-Policy", `default-src 'none'; frame-ancestors 'none'; script-src 'none'; script-src-elem 'none'; script-src-attr 'none'`)
w.Header().Set("Content-Security-Policy", `default-src 'none'; frame-ancestors 'none'; script-src 'none'; script-src-elem 'none'; script-src-attr 'none'; style-src 'unsafe-inline'`)
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-Content-Type-Options", "nosniff")
}
@ -799,15 +799,21 @@ func (h *peerAPIHandler) handleServeInterfaces(w http.ResponseWriter, r *http.Re
fmt.Fprintf(w, "<h3>Could not get the default route: %s</h3>\n", html.EscapeString(err.Error()))
}
if hasCGNATInterface, err := interfaces.HasCGNATInterface(); hasCGNATInterface {
fmt.Fprintln(w, "<p>There is another interface using the CGNAT range.</p>")
} else if err != nil {
fmt.Fprintf(w, "<p>Could not check for CGNAT interfaces: %s</p>\n", html.EscapeString(err.Error()))
}
i, err := interfaces.GetList()
if err != nil {
fmt.Fprintf(w, "Could not get interfaces: %s\n", html.EscapeString(err.Error()))
return
}
fmt.Fprintln(w, "<table>")
fmt.Fprintln(w, "<table style='border-collapse: collapse' border=1 cellspacing=0 cellpadding=2>")
fmt.Fprint(w, "<tr>")
for _, v := range []any{"Index", "Name", "MTU", "Flags", "Addrs"} {
for _, v := range []any{"Index", "Name", "MTU", "Flags", "Addrs", "Extra"} {
fmt.Fprintf(w, "<th>%v</th> ", v)
}
fmt.Fprint(w, "</tr>\n")
@ -816,6 +822,11 @@ func (h *peerAPIHandler) handleServeInterfaces(w http.ResponseWriter, r *http.Re
for _, v := range []any{iface.Index, iface.Name, iface.MTU, iface.Flags, ipps} {
fmt.Fprintf(w, "<td>%s</td> ", html.EscapeString(fmt.Sprintf("%v", v)))
}
if extras, err := interfaces.InterfaceDebugExtras(iface.Index); err == nil && extras != "" {
fmt.Fprintf(w, "<td>%s</td> ", html.EscapeString(extras))
} else if err != nil {
fmt.Fprintf(w, "<td>%s</td> ", html.EscapeString(err.Error()))
}
fmt.Fprint(w, "</tr>\n")
})
fmt.Fprintln(w, "</table>")

View File

@ -756,3 +756,15 @@ func HasCGNATInterface() (bool, error) {
}
return hasCGNATInterface, nil
}
var interfaceDebugExtras func(ifIndex int) (string, error)
// InterfaceDebugExtras returns extra debugging information about an interface
// if any (an empty string will be returned if there are no additional details).
// Formatting is platform-dependent and should not be parsed.
func InterfaceDebugExtras(ifIndex int) (string, error) {
if interfaceDebugExtras != nil {
return interfaceDebugExtras(ifIndex)
}
return "", nil
}

View File

@ -4,6 +4,7 @@
package interfaces
import (
"fmt"
"net"
"strings"
"sync"
@ -29,6 +30,10 @@ func parseRoutingTable(rib []byte) ([]route.Message, error) {
m map[int]string // ifindex => name
}
func init() {
interfaceDebugExtras = interfaceDebugExtrasDarwin
}
// getDelegatedInterface returns the interface index of the underlying interface
// for the given interface index. 0 is returned if the interface does not
// delegate.
@ -93,3 +98,14 @@ func getDelegatedInterface(ifIndex int) (int, error) {
}
return int(ifr.ifr_delegated), nil
}
func interfaceDebugExtrasDarwin(ifIndex int) (string, error) {
delegated, err := getDelegatedInterface(ifIndex)
if err != nil {
return "", err
}
if delegated == 0 {
return "", nil
}
return fmt.Sprintf("delegated=%d", delegated), nil
}