diff --git a/README.md b/README.md index fea0ace..a107822 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,6 @@ sudo setcap cap_net_raw+ep ~/go/bin/mshark ```shell mshark -h - ______ __ __ / \ | \ | \ ______ ____ | $$$$$$\| $$____ ______ ______ | $$ __ @@ -49,22 +48,23 @@ GitHub: https://github.com/shadowy-pycoder/mshark Usage: mshark [OPTIONS] Options: -h Show this help message and exit. - -D Display list of interfaces and exit. + -D Display list of interfaces and exit. + -V Show version and build information -b int - The maximum size of packet queue. (default 8192) + The maximum size of packet queue. (default 8192) -c int - The maximum number of packets to capture. + The maximum number of packets to capture. -e string - BPF filter expression. Example: "ip proto tcp" + BPF filter expression. Example: "ip proto tcp". -f value - File extension(s) to write captured data. Supported formats: stdout, txt, pcap, pcapng + File extension(s) to write captured data. Supported formats: stdout, txt, pcap, pcapng -i string - The name of the network interface. Example: eth0 (default "any") - -p Promiscuous mode. This setting is ignored for "any" interface. Defaults to false. + The name of the network interface. Example: eth0 (default "any") + -p Promiscuous mode. This setting is ignored for "any" interface. Defaults to false. -s int - The maximum length of each packet snapshot. Defaults to 65535. + The maximum length of each packet snapshot. Defaults to 65535. -t duration - The maximum duration of the packet capture process. Example: 5s + The maximum duration of the packet capture process. Example: 5s -v Display full packet info when capturing to stdout or txt. ``` diff --git a/arpspoof/arpspoof.go b/arpspoof/arpspoof.go index c3da526..7f53b8e 100644 --- a/arpspoof/arpspoof.go +++ b/arpspoof/arpspoof.go @@ -87,7 +87,7 @@ func (at *ARPTable) Delete(ip netip.Addr) { func (at *ARPTable) Refresh() error { at.Lock() defer at.Unlock() - cmd := exec.Command("sh", "-c", "ip -br neigh") + cmd := exec.Command("sh", "-c", "ip -4 -br neigh") out, err := cmd.Output() if err != nil { return err @@ -175,7 +175,7 @@ func NewARPSpoofer(conf *ARPSpoofConfig) (*ARPSpoofer, error) { arpspoofer.gwIP = gwIP } if gwMAC, ok := arpspoofer.arpTable.Get(arpspoofer.gwIP); !ok { - probeIP(arpspoofer.gwIP) + doPing(arpspoofer.gwIP) time.Sleep(probeThrottling) err = arpspoofer.arpTable.Refresh() if err != nil { @@ -294,8 +294,7 @@ func (ar *ARPSpoofer) Stop() error { return err } -func doProbe(ip netip.Addr) error { - // TODO: add manual packet crafting +func doPing(ip netip.Addr) error { ping := exec.Command("sh", "-c", fmt.Sprintf("ping -c1 -t1 -w1 %s", ip)) if err := ping.Start(); err != nil { return err @@ -306,8 +305,14 @@ func doProbe(ip netip.Addr) error { return nil } -func probeIP(ip netip.Addr) error { - return doProbe(ip) +func (ar *ARPSpoofer) doProbe(ip netip.Addr) error { + // TODO: add parsing ARP replies + ap, err := ar.newARPRequest(ar.hostMAC, ar.hostIP, ip) + if err != nil { + return err + } + ar.packets <- ap + return nil } func (ar *ARPSpoofer) probeTargetsOnce() { @@ -316,7 +321,7 @@ func (ar *ARPSpoofer) probeTargetsOnce() { wg.Add(1) go func(ip netip.Addr) { defer wg.Done() - doProbe(ip) + doPing(ip) }(ip) time.Sleep(probeThrottling) } @@ -338,7 +343,7 @@ func (ar *ARPSpoofer) probeTargets() { wg.Add(1) go func(ip netip.Addr) { defer wg.Done() - doProbe(ip) + doPing(ip) }(ip) time.Sleep(probeThrottling) } @@ -390,6 +395,20 @@ func (ar *ARPSpoofer) newARPReply(srcMAC, dstMAC net.HardwareAddr, srcIP, dstIP return &Packet{addr: dstMAC, data: eth.ToBytes()}, nil } +func (ar *ARPSpoofer) newARPRequest(srcMAC net.HardwareAddr, srcIP, dstIP netip.Addr) (*Packet, error) { + arp, err := layers.NewARPPacket(layers.OperationRequest, srcMAC, srcIP, network.LoopbackMAC, dstIP) + if err != nil { + ar.logger.Debug().Msg(err.Error()) + return nil, err + } + eth, err := layers.NewEthernetFrame(network.BroadcastMAC, srcMAC, layers.EtherTypeARP, arp.ToBytes()) + if err != nil { + ar.logger.Debug().Msg(err.Error()) + return nil, err + } + return &Packet{addr: network.BroadcastMAC, data: eth.ToBytes()}, nil +} + func (ar *ARPSpoofer) spoofTargets() { for _, targetIP := range ar.targets { if targetMAC, ok := ar.arpTable.Get(targetIP); !ok { diff --git a/cmd/mshark/cli.go b/cmd/mshark/cli.go index 6c96c21..ad989d0 100644 --- a/cmd/mshark/cli.go +++ b/cmd/mshark/cli.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "runtime" "slices" "strings" "time" @@ -17,8 +18,7 @@ import ( const app string = "mshark" -const usagePrefix string = ` - ______ __ __ +const usagePrefix string = ` ______ __ __ / \ | \ | \ ______ ____ | $$$$$$\| $$____ ______ ______ | $$ __ | \ \ | $$___\$$| $$ \ | \ / \ | $$ / \ @@ -108,6 +108,11 @@ func root(args []string) error { fmt.Print(usagePrefix) flags.PrintDefaults() } + flags.BoolFunc("V", "Show version and build information", func(flagValue string) error { + fmt.Printf("%s (built for %s %s with %s)\n", ms.Version, runtime.GOOS, runtime.GOARCH, runtime.Version()) + os.Exit(0) + return nil + }) if err := flags.Parse(args); err != nil { return err diff --git a/layers/ipv4.go b/layers/ipv4.go index 7eb51f5..5aba8c4 100644 --- a/layers/ipv4.go +++ b/layers/ipv4.go @@ -6,28 +6,50 @@ import ( "net/netip" ) -const headerSizeIPv4 = 20 +const ( + headerSizeIPv4 = 20 + headerChecksumOffsetIPv4 = 10 +) + +type IPProto uint8 + +const ( + ProtoICMP IPProto = 1 + ProtoTCP IPProto = 6 + ProtoUDP IPProto = 17 +) + +type IPv4Proto struct { + Val IPProto // 8 bits defines the protocol used in the data portion of the IP datagram. + Desc string // Protocol description. +} + +func (p *IPv4Proto) String() string { + return fmt.Sprintf("%s (%d)", p.Desc, p.Val) +} type IPv4Flags struct { + Raw uint8 Reserved uint8 - MF uint8 DF uint8 + MF uint8 } func (i *IPv4Flags) String() string { - return fmt.Sprintf("Reserved %d DF %d MF %d", i.Reserved, i.MF, i.DF) + return fmt.Sprintf("Reserved %d DF %d MF %d", i.Reserved, i.DF, i.MF) } -func newIPv4Flags(flags uint8) *IPv4Flags { +func NewIPv4Flags(flags uint8) *IPv4Flags { return &IPv4Flags{ + Raw: flags, // 3 bits Reserved: (flags >> 2) & 1, DF: (flags >> 1) & 1, MF: flags & 1, } } -// Internet Protocol version 4 is described in IETF publication RFC 791. type IPv4Packet struct { + // Internet Protocol version 4 is described in IETF publication RFC 791. Version uint8 // 4 bits version (for IPv4, this is always equal to 4). IHL uint8 // 4 bits size of header (number of 32-bit words). DSCP uint8 // 6 bits specifies differentiated services. @@ -38,13 +60,31 @@ type IPv4Packet struct { Flags *IPv4Flags // 3 bits used to control or identify fragments. FragmentOffset uint16 // 13 bits offset of a particular fragment. TTL uint8 // 8 bits limits a datagram's lifetime to prevent network failure. - Protocol uint8 // 8 bits defines the protocol used in the data portion of the IP datagram. - ProtocolDesc string // Protocol description. + Protocol *IPv4Proto HeaderChecksum uint16 // 16 bits used for error checking of the header. SrcIP netip.Addr // IPv4 address of the sender of the packet. DstIP netip.Addr // IPv4 address of the receiver of the packet. Options []byte // if ihl > 5 - payload []byte + Payload []byte +} + +func NewIPv4Packet(srcIP, dstIP netip.Addr, proto IPProto, payload []byte) (*IPv4Packet, error) { + if !srcIP.IsValid() || !srcIP.Is4() || !dstIP.IsValid() || !dstIP.Is4() { + return nil, fmt.Errorf("malformed IPv4 address") + } + ipPacket := &IPv4Packet{ + Version: 4, + IHL: 5, + TotalLength: uint16(headerSizeIPv4 + len(payload)), + Flags: NewIPv4Flags(0), + TTL: 64, + Protocol: &IPv4Proto{Val: proto, Desc: protodesc(proto)}, + SrcIP: srcIP, + DstIP: dstIP, + Payload: payload, + } + ipPacket.HeaderChecksum, _ = CalculateIPv4Checksum(ipPacket.ToBytes()) + return ipPacket, nil } func (p *IPv4Packet) String() string { @@ -58,7 +98,7 @@ func (p *IPv4Packet) String() string { - Flags: %s - Fragment Offset: %d - TTL: %d -- Protocol: %s (%d) +- Protocol: %s - Header Checksum: %#04x - SrcIP: %s - DstIP: %s @@ -76,13 +116,12 @@ func (p *IPv4Packet) String() string { p.Flags, p.FragmentOffset, p.TTL, - p.ProtocolDesc, p.Protocol, p.HeaderChecksum, p.SrcIP, p.DstIP, p.Options, - len(p.payload), + len(p.Payload), ) } @@ -90,8 +129,29 @@ func (p *IPv4Packet) Summary() string { return fmt.Sprintf("IPv4 Packet: Src IP: %s → Dst IP: %s", p.SrcIP, p.DstIP) } -// Parse parses the given byte data into an IPv4 packet struct. -func (p *IPv4Packet) Parse(data []byte) error { +func (p *IPv4Packet) MarshalBinary() ([]byte, error) { + b := make([]byte, 1+1+2+2+2+1+1+2+4+4 /* headerSizeIPv4 */ +len(p.Options)+len(p.Payload)) + b[0] = p.Version<<4 | p.IHL + b[1] = p.DSCP<<2 | p.ECN + binary.BigEndian.PutUint16(b[2:4], p.TotalLength) + binary.BigEndian.PutUint16(b[4:6], p.Identification) + binary.BigEndian.PutUint16(b[6:8], uint16(p.Flags.Raw)<<13|p.FragmentOffset) + b[8] = p.TTL + b[9] = uint8(p.Protocol.Val) + binary.BigEndian.PutUint16(b[headerChecksumOffsetIPv4:12], p.HeaderChecksum) + copy(b[12:16], p.SrcIP.AsSlice()) + copy(b[16:headerSizeIPv4], p.DstIP.AsSlice()) + copy(b[headerSizeIPv4:headerSizeIPv4+len(p.Options)], p.Options) + copy(b[headerSizeIPv4+len(p.Options):], p.Payload) + return b, nil +} + +func (p *IPv4Packet) ToBytes() []byte { + b, _ := p.MarshalBinary() + return b +} + +func (p *IPv4Packet) UnmarshalBinary(data []byte) error { if len(data) < headerSizeIPv4 { return fmt.Errorf("minimum header size for IPv4 is %d bytes, got %d bytes", headerSizeIPv4, len(data)) } @@ -106,38 +166,58 @@ func (p *IPv4Packet) Parse(data []byte) error { p.Identification = binary.BigEndian.Uint16(data[4:6]) flagsOffset := binary.BigEndian.Uint16(data[6:8]) flags := uint8(flagsOffset >> 13) - p.Flags = newIPv4Flags(flags) + p.Flags = NewIPv4Flags(flags) p.FragmentOffset = flagsOffset & (1<<13 - 1) p.TTL = data[8] - p.Protocol = data[9] - p.HeaderChecksum = binary.BigEndian.Uint16(data[10:12]) - p.SrcIP, _ = netip.AddrFromSlice(data[12:16]) - p.DstIP, _ = netip.AddrFromSlice(data[16:headerSizeIPv4]) + proto := IPProto(data[9]) + p.Protocol = &IPv4Proto{Val: proto, Desc: protodesc(proto)} + p.HeaderChecksum = binary.BigEndian.Uint16(data[headerChecksumOffsetIPv4:12]) + var ok bool + p.SrcIP, ok = netip.AddrFromSlice(data[12:16]) + if !ok { + return fmt.Errorf("malformed IPv4 address") + } + p.DstIP, ok = netip.AddrFromSlice(data[16:headerSizeIPv4]) + if !ok { + return fmt.Errorf("malformed IPv4 address") + } if p.IHL > 5 { offset := headerSizeIPv4 + ((p.IHL - 5) << 2) p.Options = data[headerSizeIPv4:offset] - p.payload = data[offset:] + p.Payload = data[offset:] } else { - p.payload = data[headerSizeIPv4:] + p.Payload = data[headerSizeIPv4:] } - p.ProtocolDesc, _ = p.NextLayer() return nil } -func (p *IPv4Packet) NextLayer() (string, []byte) { +// Parse parses the given byte data into an IPv4 packet struct. +func (p *IPv4Packet) Parse(data []byte) error { + return p.UnmarshalBinary(data) +} + +func protodesc(proto IPProto) string { // https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers - var layer string - switch p.Protocol { + var protodesc string + switch proto { case 1: - layer = "ICMP" + protodesc = "ICMP" case 6: - layer = "TCP" + protodesc = "TCP" case 17: - layer = "UDP" + protodesc = "UDP" default: + protodesc = "Unknown" + } + return protodesc +} + +func (p *IPv4Packet) NextLayer() (string, []byte) { + layer := p.Protocol.Desc + if layer == "Unknown" { layer = "" } - return layer, p.payload + return layer, p.Payload } func dscpdesc(dscp uint8) string { @@ -173,3 +253,41 @@ func dscpdesc(dscp uint8) string { } return dscpdesc } + +func CalculateIPv4Checksum(data []byte) (uint16, error) { + if len(data) < headerSizeIPv4 { + return 0, fmt.Errorf("minimum header size for IPv4 is %d bytes, got %d bytes", headerSizeIPv4, len(data)) + } + var sum uint16 + for i := 0; i < headerSizeIPv4; i += 2 { + if i != headerChecksumOffsetIPv4 { + sum = add16WithCarryWrapAround(sum, binary.BigEndian.Uint16(data[i:i+2])) + } + } + return ^sum, nil +} + +func (p *IPv4Packet) PseudoHeader() *IPv4PseudoHeader { + return &IPv4PseudoHeader{SrcIP: p.SrcIP, DstIP: p.DstIP, Protocol: p.Protocol, TotalLength: uint16(len(p.Payload))} +} + +type IPv4PseudoHeader struct { + SrcIP netip.Addr + DstIP netip.Addr + Protocol *IPv4Proto + TotalLength uint16 +} + +func (ph *IPv4PseudoHeader) MarshalBinary() ([]byte, error) { + b := make([]byte, 4+4+1+1+2) + copy(b[0:4], ph.SrcIP.AsSlice()) + copy(b[4:8], ph.DstIP.AsSlice()) + binary.BigEndian.PutUint16(b[8:10], uint16(ph.Protocol.Val)) + binary.BigEndian.PutUint16(b[10:], ph.TotalLength) + return b, nil +} + +func (ph *IPv4PseudoHeader) ToBytes() []byte { + b, _ := ph.MarshalBinary() + return b +} diff --git a/layers/ipv4_test.go b/layers/ipv4_test.go index 62ce854..4fa3a54 100644 --- a/layers/ipv4_test.go +++ b/layers/ipv4_test.go @@ -29,15 +29,14 @@ func TestParseIPv4(t *testing.T) { ECN: 0, TotalLength: 52, Identification: 47117, - Flags: &IPv4Flags{Reserved: 0, MF: 0, DF: 1}, + Flags: &IPv4Flags{Raw: 2, Reserved: 0, DF: 1, MF: 0}, FragmentOffset: 0, TTL: 64, - Protocol: 6, - ProtocolDesc: "TCP", + Protocol: &IPv4Proto{Val: 6, Desc: "TCP"}, HeaderChecksum: 33972, SrcIP: netip.AddrFrom4([4]byte{0x7F, 0x00, 0x00, 0x01}), DstIP: netip.AddrFrom4([4]byte{0x7F, 0x00, 0x00, 0x02}), - payload: []byte{}, + Payload: []byte{}, } ip := &IPv4Packet{} packet, close := testPacket(t, "ipv4") diff --git a/layers/layers.go b/layers/layers.go index e41e134..46c526a 100644 --- a/layers/layers.go +++ b/layers/layers.go @@ -59,3 +59,9 @@ func joinBytes(bs ...[]byte) []byte { } return b } + +func add16WithCarryWrapAround(x, y uint16) uint16 { + sum32 := uint32(x) + uint32(y) + sum32 = (sum32 & 0xFFFF) + (sum32 >> 16) + return uint16(sum32) +} diff --git a/layers/udp.go b/layers/udp.go index 64feba9..96eff98 100644 --- a/layers/udp.go +++ b/layers/udp.go @@ -5,15 +5,30 @@ import ( "fmt" ) -const headerSizeUDP = 8 +const ( + headerSizeUDP = 8 + headerChecksumOffsetUDP = 18 +) -// UDP protocol is defined in RFC 768. type UDPSegment struct { + // UDP protocol is defined in RFC 768. SrcPort uint16 // Identifies the sending port. DstPort uint16 // Identifies the receiving port. UDPLength uint16 // Specifies the length in bytes of the UDP header and UDP data. Checksum uint16 // The checksum field may be used for error-checking of the header and data. - payload []byte + Payload []byte +} + +func NewUDPSegment(srcPort, dstPort uint16, payload []byte, pseudo *[]byte) (*UDPSegment, error) { + udp := &UDPSegment{SrcPort: srcPort, DstPort: dstPort, UDPLength: uint16(headerSizeUDP + len(payload)), Payload: payload} + if pseudo != nil { + var err error + udp.Checksum, err = CalculateUDPChecksum(append(*pseudo, udp.ToBytes()...)) + if err != nil { + return nil, err + } + } + return udp, nil } func (u *UDPSegment) String() string { @@ -29,16 +44,30 @@ func (u *UDPSegment) String() string { u.DstPort, u.UDPLength, u.Checksum, - len(u.payload), + len(u.Payload), ) } func (u *UDPSegment) Summary() string { - return fmt.Sprintf("UDP Segment: Src Port: %d → Dst Port: %d Len: %d", u.SrcPort, u.DstPort, len(u.payload)) + return fmt.Sprintf("UDP Segment: Src Port: %d → Dst Port: %d Len: %d", u.SrcPort, u.DstPort, len(u.Payload)) } -// Parse parses the given byte data into a UDPSegment struct. -func (u *UDPSegment) Parse(data []byte) error { +func (u *UDPSegment) MarshalBinary() ([]byte, error) { + b := make([]byte, 2+2+2+2+len(u.Payload)) + binary.BigEndian.PutUint16(b[0:2], u.SrcPort) + binary.BigEndian.PutUint16(b[2:4], u.DstPort) + binary.BigEndian.PutUint16(b[4:6], u.UDPLength) + binary.BigEndian.PutUint16(b[6:headerSizeUDP], u.Checksum) + copy(b[headerSizeUDP:], u.Payload) + return b, nil +} + +func (u *UDPSegment) ToBytes() []byte { + b, _ := u.MarshalBinary() + return b +} + +func (u *UDPSegment) UnmarshalBinary(data []byte) error { if len(data) < headerSizeUDP { return fmt.Errorf("minimum header size for UDP is %d bytes, got %d bytes", headerSizeUDP, len(data)) } @@ -46,10 +75,33 @@ func (u *UDPSegment) Parse(data []byte) error { u.DstPort = binary.BigEndian.Uint16(data[2:4]) u.UDPLength = binary.BigEndian.Uint16(data[4:6]) u.Checksum = binary.BigEndian.Uint16(data[6:headerSizeUDP]) - u.payload = data[headerSizeUDP:] + u.Payload = data[headerSizeUDP:] return nil } +// Parse parses the given byte data into a UDPSegment struct. +func (u *UDPSegment) Parse(data []byte) error { + return u.UnmarshalBinary(data) +} + func (u *UDPSegment) NextLayer() (string, []byte) { - return nextAppLayer(u.SrcPort, u.DstPort), u.payload + return nextAppLayer(u.SrcPort, u.DstPort), u.Payload +} + +func CalculateUDPChecksum(data []byte) (uint16, error) { + var sum uint16 + udpLength := len(data) + for i := 0; i+1 < udpLength; i += 2 { + if i != headerChecksumOffsetUDP { + sum = add16WithCarryWrapAround(sum, binary.BigEndian.Uint16(data[i:i+2])) + } + } + if udpLength&1 == 1 { + sum = add16WithCarryWrapAround(sum, uint16(data[udpLength-1])<<8) + } + sum = ^sum + if sum == 0 { + sum = 0xFFFF + } + return sum, nil } diff --git a/layers/udp_test.go b/layers/udp_test.go index 9e2970f..a8d4887 100644 --- a/layers/udp_test.go +++ b/layers/udp_test.go @@ -25,11 +25,12 @@ func TestParseUDP(t *testing.T) { DstPort: 53, UDPLength: 52, Checksum: 3985, - payload: []byte{ + Payload: []byte{ 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77, 0x77, 0x77, 0x07, 0x67, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x41, 0x00, 0x01, - 0x00, 0x00, 0x29, 0x05, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + 0x00, 0x00, 0x29, 0x05, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, } udp := &UDPSegment{} packet, close := testPacket(t, "udp") diff --git a/mshark.go b/mshark.go index 650e7bc..67b9eee 100644 --- a/mshark.go +++ b/mshark.go @@ -252,8 +252,7 @@ func OpenLive(conf *Config, pw ...PacketWriter) error { } return fmt.Errorf("failed to read Ethernet frame: %v", err) } - p := append([]byte(nil), b[:n]...) - packetQueue <- p + packetQueue <- append([]byte(nil), b[:n]...) } } return nil diff --git a/network/network.go b/network/network.go index 7522e4c..a9a1bf7 100644 --- a/network/network.go +++ b/network/network.go @@ -12,6 +12,11 @@ import ( "text/tabwriter" ) +var ( + BroadcastMAC = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + LoopbackMAC = net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +) + // InterfaceByName returns the interface specified by name. func InterfaceByName(name string) (*net.Interface, error) { var ( @@ -65,6 +70,9 @@ func GetDefaultInterface() (*net.Interface, error) { line := scanner.Text() fields := strings.Fields(line) if len(fields) >= 2 && fields[1] == "00000000" { + if strings.Contains(fields[0], "tun") { + continue + } defaultInterface = fields[0] break } @@ -73,23 +81,35 @@ func GetDefaultInterface() (*net.Interface, error) { } func GetDefaultGatewayIPv4() (netip.Addr, error) { - cmd := exec.Command("sh", "-c", "ip route show 0.0.0.0/0 | awk '{print $3}'") - ipRaw, err := cmd.Output() - if err != nil { - return netip.Addr{}, err - } - ip, err := netip.ParseAddr(strings.TrimRight(string(ipRaw), "\n")) + cmd := exec.Command("sh", "-c", `ip -4 route show 0.0.0.0/0 | awk '{print $3 " " $5}'`) + ipdevRaw, err := cmd.Output() if err != nil { return netip.Addr{}, err } - if !ip.Is4() { - return netip.Addr{}, fmt.Errorf("only IPv4 is supported") + for line := range strings.SplitSeq(strings.TrimRight(string(ipdevRaw), "\n"), "\n") { + ipdev := strings.Fields(line) + if len(ipdev) < 2 { + continue + } + ipstr := ipdev[0] + dev := ipdev[1] + if strings.Contains(dev, "tun") { + continue + } + ip, err := netip.ParseAddr(ipstr) + if err != nil { + continue + } + if !ip.IsValid() || !ip.Is4() { + continue + } + return ip, nil } - return ip, nil + return netip.Addr{}, fmt.Errorf("gateway IPv4 not found ") } func GetGatewayIPv4FromInterface(iface string) (netip.Addr, error) { - cmd := exec.Command("sh", "-c", fmt.Sprintf("ip route show dev %s", iface)) + cmd := exec.Command("sh", "-c", fmt.Sprintf("ip -4 route show dev %s", iface)) routes, err := cmd.Output() if err != nil { return netip.Addr{}, err diff --git a/oui/oui.go b/oui/oui.go index 58af659..bc0bdfc 100644 --- a/oui/oui.go +++ b/oui/oui.go @@ -6,11 +6,8 @@ import ( "net" "strconv" "strings" -) -var ( - BroadcastMAC = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} - LoopbackMAC = net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + "github.com/shadowy-pycoder/mshark/network" ) func outMulticastRange(hw string) bool { @@ -58,10 +55,10 @@ func VendorFromMAC(hw net.HardwareAddr) string { // // If vendor is not found returns MAC address func VendorWithMAC(hw net.HardwareAddr) string { - if bytes.Equal(BroadcastMAC, hw) { + if bytes.Equal(network.BroadcastMAC, hw) { return "Broadcast_" + hw.String()[9:] } - if bytes.Equal(LoopbackMAC, hw) { + if bytes.Equal(network.LoopbackMAC, hw) { return "Loopback_" + hw.String()[9:] } vendor := Vendor(hw.String(), false) diff --git a/version.go b/version.go new file mode 100644 index 0000000..1757501 --- /dev/null +++ b/version.go @@ -0,0 +1,3 @@ +package mshark + +const Version string = "mshark v0.0.9"