Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Fix applied for loading recent wireshark oui files
karagenc/oui#2
  • Loading branch information
thomasf committed Oct 31, 2023
1 parent 1bc8808 commit fb02a3f
Show file tree
Hide file tree
Showing 5 changed files with 306 additions and 4 deletions.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ require (
github.com/mxmCherry/movavg v1.1.0
github.com/peterbourgon/ff/v3 v3.4.0
github.com/rs/zerolog v1.31.0
github.com/tomruk/oui v1.0.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)

Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,6 @@ github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tomruk/oui v1.0.0 h1:wZPiV1IIxoWkBFaleAkNzq7et6SxOqZJzyR8c24brd0=
github.com/tomruk/oui v1.0.0/go.mod h1:0V8BLBhlaaunndElRMZMqYFxVNwaxCPIcLIqjfz+QLg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand Down
2 changes: 2 additions & 0 deletions internal/oui/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This is vendored version of https://github.com/tomruk/oui with fix applied
for loading newer wireshark oui files https://github.com/tomruk/oui/pull/2
303 changes: 303 additions & 0 deletions internal/oui/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
// Package oui provides functions to work with MAC and OUI's
package oui

import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"os"
"regexp"
"sort"
"strconv"
"strings"
)

const ouiReStr = `^(\S+)\s*\t+(\S+)(\s+#\s+(\S.*))?`

var ErrInvalidMACAddress = errors.New("invalid MAC address")

// Helper functions

func macToUint64(address [6]byte) uint64 {
var a uint64
for _, x := range address {
a <<= 8
a |= uint64(x)
}
return a
}

func maskToUint64(mask uint8) uint64 {
return ^(uint64(1)<<(48-mask) - 1)
}

func parseMAC(s string) ([6]byte, error) {
var hw [6]byte

var oct []string
if strings.IndexByte(s, ':') < 0 {
oct = strings.Split(s, "-")
} else {
oct = strings.Split(s, ":")
}

for i, x := range oct {
h, err := strconv.ParseUint(x, 16, 8)
if err != nil {
return hw, err
}
hw[i] = uint8(h)
}

return hw, nil
}

type addressBlock interface {
Uint64OUI() uint64
Uint64Mask() uint64
Organization() string
}

// oui, mask, organization
type addressBlock24 struct {
oui [3]byte
mask byte
organization [8]byte
}

func (a *addressBlock24) Uint64OUI() uint64 {
return uint64(a.oui[0])<<40 | uint64(a.oui[1])<<32 | uint64(a.oui[2])<<24
}

func (a *addressBlock24) Uint64Mask() uint64 {
return ^(uint64(1)<<24 - 1)
}

func (a *addressBlock24) Organization() string {
return strings.TrimSpace(string(a.organization[:]))
}

type addressBlocks24 []addressBlock24

func (bs addressBlocks24) Len() int {
return len(bs)
}

func (bs addressBlocks24) Less(i, j int) bool {
return bs[i].Uint64OUI() < bs[j].Uint64OUI()
}

func (bs addressBlocks24) Swap(i, j int) {
bs[i], bs[j] = bs[j], bs[i]
}

func (bs addressBlocks24) Search(addr uint64, i, j int) addressBlock {

k := (i + j) / 2
o := bs[k].Uint64OUI()
m := bs[k].Uint64Mask()

if addr&m == o {
return addressBlock(&bs[k])
}

if i == j {
return nil
}

if addr&m < o {
return bs.Search(addr, i, k)
} else {
return bs.Search(addr, k+1, j)
}
}

type addressBlock48 struct {
oui [6]byte
mask byte
organization [8]byte
}

func (a *addressBlock48) Uint64OUI() uint64 {
return macToUint64(a.oui)
}

func (a *addressBlock48) Uint64Mask() uint64 {
return maskToUint64(a.mask)
}

func (a *addressBlock48) Organization() string {
return strings.TrimSpace(string(a.organization[:]))
}

type addressBlocks48 []addressBlock48

func (bs addressBlocks48) Len() int {
return len(bs)
}

func (bs addressBlocks48) Less(i, j int) bool {
return bs[i].Uint64OUI() < bs[j].Uint64OUI()
}

func (bs addressBlocks48) Swap(i, j int) {
bs[i], bs[j] = bs[j], bs[i]
}

func (bs addressBlocks48) Search(addr uint64, i, j int) addressBlock {

k := (i + j) / 2
o := bs[k].Uint64OUI()
m := bs[k].Uint64Mask()

if addr&m == o {
return addressBlock(&bs[k])
}

if i == j {
return nil
}

if addr&m < o {
return bs.Search(addr, i, k)
} else {
return bs.Search(addr, k+1, j)
}
}

type DB struct {
blocks24 addressBlocks24
blocks48 addressBlocks48
}

func (db *DB) load(file io.Reader) (err error) {
fieldsRe := regexp.MustCompile(ouiReStr)

scanner := bufio.NewScanner(file)
for scanner.Scan() {
text := scanner.Text()
if text == "" || text[0] == '#' || text[0] == '\t' {
continue
}

// Skip token ring entries
if strings.Contains(text, "[TR?]") {
continue
}

// Split input text into address and organization name
fields := fieldsRe.FindAllStringSubmatch(text, -1)
// incorrectly formated database might not create fields, which would prevent the line from being parsed
if fields == nil {
continue
}
addr := fields[0][1]
org := fields[0][2] + " "

switch org[:8] {
case "IeeeRegi", "Spanning":
continue
}

var oui [6]byte
var mask int

if i := strings.IndexByte(addr, '/'); i < 0 {
if oui, err = parseMAC(addr); err != nil {
continue
}
mask = (len(addr) + 1) / 3 * 8
} else {
if oui, err = parseMAC(addr[:i]); err != nil {
continue
}
if mask, err = strconv.Atoi(addr[i+1:]); err != nil {
continue
}
}

var orgbytes [8]byte
copy(orgbytes[:], org)

if mask > 24 {
block := addressBlock48{oui, uint8(mask), orgbytes}
db.blocks48 = append(db.blocks48, block)
} else {
var o [3]byte
o[0] = oui[0]
o[1] = oui[1]
o[2] = oui[2]
block := addressBlock24{o, uint8(mask), orgbytes}
db.blocks24 = append(db.blocks24, block)
}
}

if err := scanner.Err(); err != nil {
return err
}

if len(db.blocks48) == 0 && len(db.blocks24) == 0 {
return fmt.Errorf("database is empty")
}

return nil
}

// New returns a new OUI database loaded from the data.
func NewDB(data []byte) (*DB, error) {
buf := bytes.NewBuffer(data)
return newDBFromReader(buf)
}

// NewFromReader returns a new OUI database loaded from the reader.
func NewDBFromReader(r io.Reader) (*DB, error) {
return newDBFromReader(r)
}

func newDBFromReader(r io.Reader) (*DB, error) {
db := new(DB)
if err := db.load(r); err != nil {
return nil, err
}

sort.Sort(db.blocks48)
sort.Sort(db.blocks24)

return db, nil
}

// NewFromFile returns a new OUI database loaded from the specified file.
func NewDBFromFile(path string) (*DB, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()

return newDBFromReader(file)
}

func (db *DB) blockLookup(address [6]byte) addressBlock {
a := macToUint64(address)

if b := db.blocks48.Search(a, 0, len(db.blocks48)-1); b != nil {
return b
}

return db.blocks24.Search(a, 0, len(db.blocks24)-1)
}

// Lookup obtains the vendor organization name from the MAC address s.
func (db *DB) Lookup(s string) (string, error) {
addr, err := parseMAC(s)
if err != nil {
return "", err
}
block := db.blockLookup(addr)
if block == nil {
return "", nil
}
return block.Organization(), nil
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import (
"github.com/some-programs/natbwmon/internal/arp"
"github.com/some-programs/natbwmon/internal/log"
"github.com/some-programs/natbwmon/internal/mon"
"github.com/some-programs/natbwmon/internal/oui"
"github.com/some-programs/natbwmon/internal/server"
"github.com/tomruk/oui"
)

// Flags contains the top level program configuration.
Expand Down

0 comments on commit fb02a3f

Please sign in to comment.