Sort dhcp ip addresses

Does anyone have a script for sorting the static leases in /etc/config/dhcp?

Likely a one-liner of your choice with sort

https://linux.die.net/man/1/sort

I think it might take quite a bit of Linux-fu to sort all of the hosts blocks according to their ip addresses.

Drop me a PM with one of your lists and how you want it sorted (many reasonably don't want "private" data like that on the open Internet). I'll take a stab at it when I get a little time. (I don't run DHCP on OpenWRT, or I'd use my own data.)

As a hint of how to approach it:

sort -n -t .

Sort numerically and use a period as the separator

Close, if you're OK with .122 showing up before .17

$ sort -n
10.0.2.133
10.0.0.133
192.168.1.122
172.17.22.33
10.2.0.11
172.16.0.254
192.168.1.17
10.0.0.13
10.0.0.12
10.0.0.122

^D
10.0.0.12
10.0.0.122
10.0.0.13
10.0.0.133
10.0.2.133
10.2.0.11
172.16.0.254
172.17.22.33
192.168.1.122
192.168.1.17

Bingo!

$ sort -n -t . -k1,1 -k2,2 -k3,3 -k4,4
10.0.2.133
10.0.0.133
192.168.1.122
172.17.22.33
10.2.0.11
172.16.0.254
192.168.1.17
10.0.0.13
10.0.0.12
10.0.0.122

^D
10.0.0.12
10.0.0.13
10.0.0.122
10.0.0.133
10.0.2.133
10.2.0.11
172.16.0.254
172.17.22.33
192.168.1.17
192.168.1.122

(IPv6 addresses aren't going to be so easy though!)

# cat sort-leases.sh 
#!/bin/sh

. /lib/functions.sh

offset=0
sections=""

append_section() {
	local cfg="$1"
	local oIFS="$IFS"
	local ipaddr type

	config_get type "$cfg" TYPE

	case "$type" in
		host)
			config_get ipaddr "$cfg" ip
			IFS="."; set -- ${ipaddr:-0.0.0.0}; IFS="$oIFS"
			sections=$(printf "1%03d%03d%03d%03d:%s\n%s" $1 $2 $3 $4 "$cfg" "$sections")
		;;
		*)
			sections=$(printf "0%012d:%s\n%s" $((offset++)) "$cfg" "$sections")
		;;
	esac
}

config_load dhcp
config_foreach append_section

i=0

for tuple in $(echo "$sections" | sort); do
	cfg="${tuple#*:}"
	uci reorder "dhcp.$cfg=$((i++))"
done

uci show dhcp
# uci commit dhcp
4 Likes

Please forgive my ignorance. Just starting with openwrt.
Can someone please guide me on how/where to include this script? Sorted static lease is what I am looking for for ages.

Not familiar with linux so hopefully using luCI is an option to add this script?

It would be most convenient if we could program LuCI to sort the IP addresses via clicking the column headings. I think one would run these commands on the file after copying it to their computer. Here is a script from my former colleague, Pete Oaks. He said I could copyleft it, granting permission for anyone to change it, use part or all of it for other purposes, and/or share. We hope it is helpful, but we don't guarantee its results. PS—it is a ruby script, so save with a dot rb extension and run with a ruby interpreter.

require 'ipaddr'

# notes:
# - all config host lines should have the 4 lines for name, dns, mac and ip
# - ip should be last line or second to last line

# TODO: check if mac line is switched with ip line and swap for easier sorting.
#   Better yet, make is so it checks each type before adding
#   Add option to specify the file name being used & default to dhcp if none given

ip_array = []; ip_sorted = []; skipped = 0

# strip the text from string and get only the IP
def strip_ip(str)  # " option ip '10.10.10.123'" you get => '10.10.10.123'
  output = nil
  if str && str.match(/'(.*?)'/)
    output = str.match(/'(.*?)'/)[0]
    output[0] = ""; output[-1] = ""
  end
  return output
end

# check that a file named dhcp exists
begin
  puts "Looking for the dhcp file..."
  File.open("dhcp", "r") { |f| puts "Found one..." }
rescue StandardError => e
  puts e.message; puts e.backtrace.inspect
  puts "You need a file named dhcp in the same folder as this script. No such file found, bailing..."
  exit
end

puts "Creating a file named dhcp_new or emptying it out if it already exists..."
File.open("dhcp_new", "w") { |f| f << "" }
File.open("dhcp_skipped_entries", "w") { |f| f << "" }

puts "Starting import..."
File.open("dhcp", "r") do |f|
  counter = 0
  f.each_line do |line|
    if line.match(/config host/)
      ip_array << [line]
      counter = 1
    elsif (counter > 0 && counter < 4)
      ip_array[-1] << line
      counter += 1
    elsif counter == 4
      ip_array[-1] << line
      ip_array[-1] << strip_ip(line)
      counter += 1
    elsif counter == 5 || line.match(/available/)
      counter = 0
    else
      File.open("dhcp_new", "a") { |f| f << line }
    end
  end
end

# Check for issues like IP in wrong place or empty IP values which mess up sorting
ip_array.each_with_index do |v,i|
  if v[4].match(/option mac/) && v[3].match(/option ip/)
    puts "found mac & ip in wrong order... flipping!"
    0.upto(4) { |n| puts v[n] }; puts ""
    v[3], v[4] = v[4], v[3]
    v[5] = strip_ip(v[4])
  end

  begin
    IPAddr.new(v[5])
  rescue StandardError => e
    skipped += 1
    puts "Doesn't look like a valid IP for: #{v[1].strip} - I can't sort it!"
    puts "Skipping... Check file 'dhcp_skipped_entries' to see entry that was skipped"
    puts ""

    File.open("dhcp_skipped_entries", "a") { |f| 0.upto(4) { |n| f << v[n] } }
    ip_array.delete_at(i)
  end
end

begin
  puts "Beginning sort..."
  ip_sorted = ip_array.sort { |a,b| IPAddr.new(a[5].to_s) <=> IPAddr.new(b[5].to_s) }
rescue StandardError => e
  puts e.message; puts e.backtrace.inspect
  puts "I can't understand this IP value which means I can't sort it. Sorry, aborting..."
  exit
end

# Add the sorted IP addresses to the new file
File.open("dhcp_new", "a") do |f|
  ip_sorted.each do |host_entry|
    0.upto(4) { |n| f << host_entry[n] }
    f << "\n"
  end
end

puts "Completed... new file - dhcp_new - created"
puts "#{skipped} entries were skipped.. please look in the file 'dhcp_skipped_entries' to see what was skipped" if skipped > 0


1 Like

hi and thanks for the script.
However I have problems using it.
I'm running it under windows and get following error:

["C:/Ruby24/lib/ruby/2.4.0/ipaddr.rb:563:in `in6_addr'", "C:/Ruby24/lib/ruby/2.4.0/ipaddr.rb:500:in `initialize'", "sort.rb:83:in `new'", "sort.rb:83:in `block in <main>'", "sort.rb:83:in `sort'", "sort.rb:83:in `<main>'"]
I can't understand this IP value which means I can't sort it. Sorry, aborting...

Any ideas how to fix it ?
I've tryed several versions of ruby.

Did you try the router?

I'm receiving same error when running it from the router:

Beginning sort...
invalid address
["/usr/lib/ruby/2.5/ipaddr.rb:649:in `in6_addr'", "/usr/lib/ruby/2.5/ipaddr.rb:586:in `initialize'", "sort.rb:83:in `new'", "sort.rb:83:in `block in <main>'", "sort.rb:83:in `sort'", "sort.rb:83:in `<main>'"]
I can't understand this IP value which means I can't sort it. Sorry, aborting...

Any news?
I'm receiving same error too...
Thank you...

Sorry, I guess I was too busy to reply back in November. Thank you for asking for an update, back in November, Pete said, "It has to be in the format we are using. Four entries in the order we use. It's either that or one of the ips in his list is not a proper ip." Here is the format we used; I think it was the default format unless one of us mixed it up along the way:

config host
	option name 'hostname'
	option dns '1'
	option mac '00:ff:88:bb:44:22'
	option ip '192.168.10.1'

I don't know if the IP network matters. As the notes alluded, we used a 10.10 network.

2 Likes

awk '{print $3 " % " $0}' /tmp/dhcp.leases | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n -V | sed 's/[^%]*% //'

type this command after ssh login to your router. It will display all devices that has dhcp lease sorted on 3rd colum with ip address in ascending order. no changes to original file just displaying on screen.

Hi,
I used the following script:

awk '{print $3 " % " $4}' /tmp/dhcp.leases | sed "s/192.168.3.//"| sort -n

It does the following:

  • Grab the IP address and system name
  • remove the standard part of the IP-address
  • sort naturally so it will show 12, 20, 155 instead of 12, 155, 20.

Actually, what I need is a way to sort by ANY of the columns on the "DHCP and DNS" page. If for example I am trying to track down a particular MAC address, I would click on that heading and then I'd need to scan down the list for the one I want. Or maybe I need to find a particular hostname, so I'd sort on that column instead.

3 Likes