module OUI
Organizationally Unique Identifier
Constants
- BLANK_LINE
- COUNTRY_OVERRIDES
- DEBUGGING_DEFAULT
- ERASE_LINE
- EXPECTED_DUPLICATES
- FIRST_LINE_INDEX
- HEX_BEGINNING_REGEX
- IMPORT_LOCAL_TXT_FILE_DEFAULT
import data/oui.txt instead of fetching remotely
- IN_MEMORY_ONLY_DEFAULT
use in-memory instead of persistent file
- LINE_LENGTH
- LOCAL_DB_DEFAULT
- LOCAL_MANUAL_FILE
- LOCAL_TXT_FILE
- MISSING_COUNTRIES
- REMOTE_TXT_URI
- ROOT
- TABLE
Public Instance Methods
# File lib/oui.rb, line 138 def clear_table debug 'clear_table' table.delete_sql end
Release backend resources
# File lib/oui.rb, line 128 def close_db semaphore.synchronize do debug 'Closing database' if @@db @@db.disconnect @@db = nil end end end
# File lib/oui.rb, line 59 def debugging @@debugging end
# File lib/oui.rb, line 63 def debugging=(v) @@debugging = (v.nil?) ? DEBUGGING_DEFAULT : v end
@param oui [String,Integer] hex or numeric OUI
to find @return [Hash,nil]
# File lib/oui.rb, line 94 def find(oui) semaphore.synchronize do update_db unless table? && table.count > 0 r = table.where(id: self.to_i(oui)).first r.delete :create_table if r # not sure why this is here, but nuking it r end end
# File lib/oui.rb, line 70 def import_local_txt_file @@import_local_txt_file end
# File lib/oui.rb, line 74 def import_local_txt_file=(v) @@import_local_txt_file = (v.nil?) ? IMPORT_LOCAL_TXT_FILE_DEFAULT : v end
# File lib/oui.rb, line 81 def in_memory_only @@in_memory_only end
# File lib/oui.rb, line 85 def in_memory_only=(v) if v != @@in_memory_only @@in_memory_only = (v.nil?) ? IN_MEMORY_ONLY_DEFAULT : v close_db end end
# File lib/oui.rb, line 48 def local_db @@local_db end
# File lib/oui.rb, line 52 def local_db=(v) @@local_db = v || LOCAL_DB_DEFAULT end
Converts an OUI
string to an integer of equal value @param oui [String,Integer] MAC OUI
in hexadecimal formats
hhhh.hh, hh:hh:hh, hh-hh-hh or hhhhhh
@return [Integer] numeric representation of oui
# File lib/oui.rb, line 107 def to_i(oui) return oui if oui.is_a? Integer oui = oui.strip.gsub(/[:\- .]/, '') if oui =~ /([[:xdigit:]]{6})/ $1.to_i(16) end end
Convert an id to OUI
@param oui [String,nil] string to place between pairs of hex digits, nil for none @return [String] hexadecimal format of id
# File lib/oui.rb, line 118 def to_s(id, sep = '-') return id if id.is_a? String unless id >= 0x000000 && id <= 0xFFFFFF raise ArgumentError, "#{id} is not a valid 24-bit OUI" end format('%06x', id).scan(/../).join(sep) end
Update database from fetched URL @param [Boolean] whether to connect to the network or not @return [Integer] number of unique records loaded
# File lib/oui.rb, line 146 def update_db(local = nil, db_file = nil) semaphore.synchronize do debug "update_db(local = #{local}, db_file = #{db_file})" close_db old_import_local_txt_file = self.import_local_txt_file self.import_local_txt_file = local old_local_db = self.local_db self.local_db = db_file ## Sequel debug '--- close db ---' debug '--- close db ---' debug '--- drop table ---' drop_table # debug '--- drop table ---' # debug '--- create table ---' create_table # debug '--- create table ---' db.transaction do debug '--- clear table ---' clear_table debug '--- clear table ---' debug '--- install manual ---' install_manual debug '--- install manual ---' debug '--- install updates ---' install_updates debug '--- install updates ---' end debug '--- close db ---' close_db debug '--- close db ---' self.local_db = old_local_db self.import_local_txt_file = old_import_local_txt_file ## AR # self.transaction do # self.delete_all # self.install_manual # self.install_updates # end debug "update_db(local = #{local}, db_file = #{db_file}) finish" end end
Private Instance Methods
Has a particular id been added yet?
# File lib/oui.rb, line 346 def added @@added ||= {} end
# File lib/oui.rb, line 203 def connect_db if in_memory_only debug 'Connecting to in-memory database' if RUBY_PLATFORM == 'java' Sequel.connect('jdbc:sqlite::memory:') else Sequel.sqlite # in-memory sqlite database end else connect_file_db local_db end end
# File lib/oui.rb, line 192 def connect_file_db(f) FileUtils.mkdir_p(File.dirname(f)) if RUBY_PLATFORM == 'java' u = 'jdbc:sqlite:'+f else u = 'sqlite:'+f end debug "Connecting to db file #{u}" Sequel.connect(u) end
@param g [Array<String>]
# File lib/oui.rb, line 300 def create_from_line_group(g) n = g.length raise ArgumentError, "Parse error lines: #{n} '#{g}'" unless (2..6).include? n id = parse_id(g) create_unless_present(id: id, organization: parse_org(g), address1: parse_address1(g), address2: parse_address2(g, id), address3: parse_address3(g), country: parse_country(g, id)) end
# File lib/oui.rb, line 233 def create_table # debug 'create_table' db.create_table TABLE do primary_key :id String :organization, null: false String :address1 String :address2 String :address3 String :country index :id end end
# File lib/oui.rb, line 366 def create_unless_present(opts) id = opts[:id] if added[id] unless expected_duplicate? id debug "OUI unexpected duplicate #{opts}" end else table.insert(opts) # self.create! opts added[id] = true end end
# File lib/oui.rb, line 216 def db @@db ||= connect_db end
# File lib/oui.rb, line 354 def debug(*args) $stderr.puts(*args) if debug? end
# File lib/oui.rb, line 350 def debug? $DEBUG || debugging || ENV['DEBUG_OUI'] end
# File lib/oui.rb, line 358 def debug_print(*args) $stderr.print(*args) if debug? end
# File lib/oui.rb, line 228 def drop_table debug 'drop_table' db.drop_table(TABLE) if table? end
Expected duplicates are 00-01-C8 (2x) and 08-00-30 (3x)
# File lib/oui.rb, line 341 def expected_duplicate?(id) EXPECTED_DUPLICATES.include? id end
# File lib/oui.rb, line 311 def fetch uri = oui_uri $stderr.puts "Fetching #{uri}" open(uri).read end
# File lib/oui.rb, line 317 def install_manual debug 'install_manual' JSON.load(File.read(LOCAL_MANUAL_FILE)).each do |g| # convert keys to symbols g = g.map { |k, v| [k.to_sym, v] } g = Hash[g] # convert OUI octets to integers g[:id] = self.to_i(g[:id]) create_unless_present(g) end rescue Errno::ENOENT end
# File lib/oui.rb, line 330 def install_updates debug 'install_updates' lines = fetch.split("\n").map { |x| x.sub(/\r$/, '') } parse_lines_into_groups(lines).each_with_index do |group, idx| create_from_line_group(group) debug "#{ERASE_LINE}Created records #{idx}" if idx % 1000 == 0 end.count debug "#{ERASE_LINE}#{BLANK_LINE}" end
# File lib/oui.rb, line 379 def oui_uri if import_local_txt_file debug 'oui_uri = local' LOCAL_TXT_FILE else debug 'oui_uri = remote' REMOTE_TXT_URI end end
# File lib/oui.rb, line 280 def parse_address1(g) g[2] if g.length >= 4 end
# File lib/oui.rb, line 284 def parse_address2(g, id) g[3] if g.length >= 5 || MISSING_COUNTRIES.include?(id) end
# File lib/oui.rb, line 288 def parse_address3(g) g[4] if g.length == 6 end
@param g [Array<String>] @param id [Integer]
# File lib/oui.rb, line 294 def parse_country(g, id) c = COUNTRY_OVERRIDES[id] || g[-1] c if c !~ /\A\h/ end
@param g [Array<String>]
# File lib/oui.rb, line 276 def parse_id(g) g[1].split(' ')[0].to_i(16) end
@param lines [Array<String>] @return [Array<Array<String>>]
# File lib/oui.rb, line 248 def parse_lines_into_groups(lines) grps, curgrp = [], [] header = true lines.each do |line| if header if line =~ HEX_BEGINNING_REGEX header = false else next end end if !curgrp.empty? && line =~ HEX_BEGINNING_REGEX grps << curgrp curgrp = [] end line.strip! next if line.empty? curgrp << line end grps << curgrp # add last group and return end
@param g [Array<String>]
# File lib/oui.rb, line 271 def parse_org(g) g[0].split("\t").last end
# File lib/oui.rb, line 362 def semaphore @@semaphore ||= Monitor.new end
# File lib/oui.rb, line 224 def table db[TABLE] end
# File lib/oui.rb, line 220 def table? db.tables.include? TABLE end