blob: 0d43c5454e8525fe98387b89d5a99548a0230d3c [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"flag"
"fmt"
"os"
"app/context"
"netstack/fidlconv"
"fidl/fuchsia/net"
"fidl/fuchsia/netstack"
)
type netstatApp struct {
ctx *context.Context
netstack *netstack.NetstackInterface
}
type icmpHistogram struct {
// see third_party/netstack/tcpip/header/icmpv{4,6}.go for an exhaustive list
echoRequests uint64
echoReplies uint64
}
type icmpOutput struct {
received uint64
inputFailed uint64
sent uint64
sentFailed uint64
inputHistogram icmpHistogram
outputHistogram icmpHistogram
}
type statsOutput struct {
icmp icmpOutput
ip netstack.IpStats
tcp netstack.TcpStats
udp netstack.UdpStats
}
func (h icmpHistogram) String() string {
return fmt.Sprintf("\t\techo request: %d\n\t\techo replies: %d", h.echoRequests, h.echoReplies)
}
func (o *statsOutput) String() string {
return fmt.Sprintf(
`IP:
%d total packets received
%d with invalid addresses
%d incoming packets delivered
%d requests sent out
%d outgoing packets with errors
TCP:
%d ActiveConnectionOpenings
%d PassiveConnectionOpenings
%d FailedConnectionAttempts
%d ValidSegmentsReceived
%d InvalidSegmentsReceived
%d SegmentsSent
%d ResetsSent
UDP:
%d packets received
%d packet receive errors
%d packets to unknown ports received
%d receive buffer errors
%d malformed packets received
%d packets sent
ICMP:
%d ICMP messages received
%d input ICMP message failed.
ICMP input histogram:
%v
%d ICMP messages sent
%d ICMP messages failed
ICMP output histogram:
%v`,
o.ip.PacketsReceived,
o.ip.InvalidAddressesReceived,
o.ip.PacketsDelivered,
o.ip.PacketsSent,
o.ip.OutgoingPacketErrors,
o.tcp.ActiveConnectionOpenings,
o.tcp.PassiveConnectionOpenings,
o.tcp.FailedConnectionAttempts,
o.tcp.ValidSegmentsReceived,
o.tcp.InvalidSegmentsReceived,
o.tcp.SegmentsSent,
o.tcp.ResetsSent,
o.udp.PacketsReceived,
o.udp.UnknownPortErrors+o.udp.ReceiveBufferErrors+o.udp.MalformedPacketsReceived,
o.udp.UnknownPortErrors,
o.udp.ReceiveBufferErrors,
o.udp.MalformedPacketsReceived,
o.udp.PacketsSent,
o.icmp.received,
o.icmp.inputFailed,
o.icmp.inputHistogram,
o.icmp.sent,
o.icmp.sentFailed,
o.icmp.outputHistogram)
}
func (o *statsOutput) add(stats netstack.NetInterfaceStats) {
tx := stats.Tx
rx := stats.Rx
o.icmp.sent += tx.PktsEchoReq + tx.PktsEchoReqV6 + tx.PktsEchoRep + tx.PktsEchoRepV6
o.icmp.received += rx.PktsEchoReq + rx.PktsEchoReqV6 + rx.PktsEchoRep + rx.PktsEchoRepV6
o.icmp.outputHistogram.echoRequests += tx.PktsEchoReq + tx.PktsEchoReqV6
o.icmp.outputHistogram.echoReplies += tx.PktsEchoRep + tx.PktsEchoRepV6
o.icmp.inputHistogram.echoRequests += rx.PktsEchoReq + rx.PktsEchoReqV6
o.icmp.inputHistogram.echoReplies += rx.PktsEchoRep + rx.PktsEchoRepV6
}
func dumpStats(a *netstatApp) {
nics, err := a.netstack.GetInterfaces()
if err != nil {
errorf("Failed to get interfaces: %v\n.", err)
return
}
stats := &statsOutput{}
for _, nic := range nics {
nicStats, err := a.netstack.GetStats(nic.Id)
if err != nil {
errorf("Failed to get statistics for nic: %v\n", err)
} else {
stats.add(nicStats)
}
}
as, _ := a.netstack.GetAggregateStats()
stats.ip = as.IpStats
stats.tcp = as.TcpStats
stats.udp = as.UdpStats
fmt.Printf("%v\n", stats)
}
func dumpRouteTables(a *netstatApp) {
entries, err := a.netstack.GetRouteTable()
if err != nil {
errorf("Failed to get route table: %v\n", err)
return
}
for _, entry := range entries {
if netAddressZero(entry.Destination) {
fmt.Printf("default via %s, ", netAddressToString(entry.Gateway))
} else {
fmt.Printf("Destination: %s, ", netAddressToString(entry.Destination))
fmt.Printf("Mask: %s, ", netAddressToString(entry.Netmask))
if !netAddressZero(entry.Gateway) {
fmt.Printf("Gateway: %s, ", netAddressToString(entry.Gateway))
}
}
fmt.Printf("NICID: %d\n", entry.Nicid)
}
}
func netAddressZero(addr net.IpAddress) bool {
switch addr.Which() {
case net.IpAddressIpv4:
for _, b := range addr.Ipv4.Addr {
if b != 0 {
return false
}
}
return true
case net.IpAddressIpv6:
for _, b := range addr.Ipv6.Addr {
if b != 0 {
return false
}
}
return true
}
return true
}
// TODO(tamird): this is the same as netAddrToString in ifconfig.
func netAddressToString(addr net.IpAddress) string {
return fidlconv.ToTCPIPAddress(addr).String()
}
func usage() {
fmt.Printf("Usage: %s [OPTIONS]\n", os.Args[0])
flag.PrintDefaults()
}
var hasErrors bool = false
func errorf(format string, args ...interface{}) {
hasErrors = true
fmt.Printf("netstat: "+format, args...)
}
func main() {
a := &netstatApp{ctx: context.CreateFromStartupInfo()}
flag.Usage = usage
var showRouteTables bool
var showStats bool
flag.BoolVar(&showRouteTables, "r", false, "Dump the Route Tables")
flag.BoolVar(&showStats, "s", false, "Show network statistics")
flag.Parse()
req, pxy, err := netstack.NewNetstackInterfaceRequest()
if err != nil {
panic(err.Error())
}
a.netstack = pxy
defer a.netstack.Close()
a.ctx.ConnectToEnvService(req)
if showRouteTables {
dumpRouteTables(a)
}
if showStats {
dumpStats(a)
}
if hasErrors {
os.Exit(1)
}
}