--- /dev/null
+rlm_sql_sqlite.db
--- /dev/null
+#
+# Test the sqlippool module using sqlite backend
+#
--- /dev/null
+#
+# Input packet
+#
+Packet-Type = Access-Request
+User-Name = 'john'
+User-Password = 'testing123'
+NAS-IP-Address = 127.0.0.1
+Calling-Station-Id = 00:11:22:33:44:55
+
+#
+# Expected answer
+#
+Packet-Type == Access-Accept
--- /dev/null
+#
+# Allocate an address from an SQL IP Pool
+#
+uint32 expiry
+&control.IP-Pool.Name := 'test_alloc'
+
+#
+# Add IP addresses
+#
+%sql("DELETE FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}'")
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time) VALUES ('%{control.IP-Pool.Name}', '192.168.0.1', datetime('now', '-00:10'))")
+
+#
+# Check allocation
+#
+sqlippool.allocate
+if (!updated) {
+ test_fail
+}
+
+if !(&reply.Framed-IP-Address == 192.168.0.1) {
+ test_fail
+}
+
+#
+# Check Expiry
+#
+&expiry := %sql("SELECT strftime('%%s', expiry_time) FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'")
+if ((&expiry - %l) < 20) {
+ test_fail
+}
+
+# +2 - Some slop for macOS
+if ((&expiry - %l) > 42) {
+ test_fail
+}
+
+#
+# Verify the address details have been updated
+#
+if !(%sql("SELECT owner FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'") == '00:11:22:33:44:55') {
+ test_fail
+}
+
+if !(%sql("SELECT gateway FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'") == '127.0.0.1') {
+ test_fail
+}
+
+&Framed-IP-Address := &reply.Framed-IP-Address
+&reply := {}
+
+#
+# Add IP addresses
+#
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time) values ('%{control.IP-Pool.Name}', '192.168.1.1', datetime('now', '-00:10'))")
+
+#
+# Check we get the same lease, with the same lease time
+#
+sqlippool.allocate
+if (!updated) {
+ test_fail
+}
+
+#
+# Check the IP addresses are the same
+#
+if !(&Framed-IP-Address == &reply.Framed-IP-Address) {
+ test_fail
+}
+
+&reply := {}
+
+#
+# Now change the Calling-Station-ID and check we get a different lease
+#
+&Calling-Station-ID := 'another_mac'
+
+sqlippool.allocate
+if (!updated) {
+ test_fail
+}
+
+#
+# Check we got the right lease
+#
+if !(&reply.Framed-IP-Address == 192.168.1.1) {
+ test_fail
+}
+
+&reply := {}
+
+test_pass
--- /dev/null
+#
+# Input packet
+#
+Packet-Type = Access-Request
+User-Name = 'john'
+User-Password = 'testing123'
+NAS-IP-Address = 127.0.0.1
+Calling-Station-Id = 00:11:22:33:44:55
+
+#
+# Expected answer
+#
+Packet-Type == Access-Accept
--- /dev/null
+#
+# Allocate an address from an SQL IP Pool
+#
+uint32 expiry
+&control.IP-Pool.Name := 'test_alloc_fail'
+
+#
+# Ensure no matching records
+#
+%sql("DELETE FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}'")
+
+#
+# Check allocation failed
+#
+sqlippool.allocate
+if (!noop) {
+ test_fail
+}
+
+if (&reply.Framed-IP-Address) {
+ test_fail
+}
+
+#
+# Insert a record
+#
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time) VALUES ('%{control.IP-Pool.Name}', '192.168.0.1', datetime('now', '-00:10'))")
+
+#
+# Check allocation
+#
+sqlippool.allocate
+if (!updated) {
+ test_fail
+}
+
+if !(&reply.Framed-IP-Address == 192.168.0.1) {
+ test_fail
+}
+
+&reply := {}
+
+#
+# Now change the Calling-Station-ID and check we don't get an address (pool full)
+#
+&Calling-Station-ID := 'another_mac'
+
+sqlippool.allocate
+if (!notfound) {
+ test_fail
+}
+
+#
+# Check we got no address
+#
+if (&reply.Framed-IP-Address) {
+ test_fail
+}
+
+test_pass
--- /dev/null
+#
+# Input packet
+#
+Packet-Type = Access-Request
+User-Name = 'john'
+User-Password = 'testing123'
+NAS-IP-Address = 127.0.0.1
+Calling-Station-Id = 00:11:22:33:44:55
+Framed-IP-Address = 192.168.0.2
+
+#
+# Expected answer
+#
+Packet-Type == Access-Accept
--- /dev/null
+#
+# Allocate an address from an SQL IP Pool
+#
+uint32 expiry
+&control.IP-Pool.Name := 'test_alloc_requested'
+
+#
+# Add IP addresses
+# Based on expiry_time, the last of these would be allocated, but the second will be requested
+#
+%sql("DELETE FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}'")
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time) VALUES ('%{control.IP-Pool.Name}', '192.168.0.1', datetime('now', '-00:10'))")
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time) VALUES ('%{control.IP-Pool.Name}', '192.168.0.2', datetime('now', '-00:10'))")
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time) VALUES ('%{control.IP-Pool.Name}', '192.168.0.3', datetime('now', '-00:20'))")
+
+#
+# Check allocation
+#
+sqlippool.allocate
+if (!updated) {
+ test_fail
+}
+
+if !(&reply.Framed-IP-Address == 192.168.0.2) {
+ test_fail
+}
+
+#
+# Check Expiry
+#
+&expiry := %sql("SELECT strftime('%%s', expiry_time) FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'")
+if ((&expiry - %l) < 20) {
+ test_fail
+}
+
+# +2 - Some slop for macOS
+if ((&expiry - %l) > 42) {
+ test_fail
+}
+
+#
+# Verify the address details have been updated
+#
+if !(%sql("SELECT owner FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'") == '00:11:22:33:44:55') {
+ test_fail
+}
+
+if !(%sql("SELECT gateway FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'") == '127.0.0.1') {
+ test_fail
+}
+
+&reply := {}
+&request -= &Framed-IP-Address
+
+#
+# Now change the Calling-Station-ID and check we get a different lease
+#
+&Calling-Station-ID := 'another_mac'
+
+sqlippool.allocate
+if (!updated) {
+ test_fail
+}
+
+#
+# Check we got the right lease - the oldest one by expiry_time
+#
+if !(&reply.Framed-IP-Address == 192.168.0.3) {
+ test_fail
+}
+
+&reply := {}
+
+test_pass
--- /dev/null
+sql {
+ driver = "sqlite"
+ dialect = "sqlite"
+ sqlite {
+ # Path to the sqlite database
+ filename = "$ENV{MODULE_TEST_DIR}/sql_sqlite/$ENV{TEST}/rlm_sql_sqlite.db"
+
+ # If the file above does not exist and bootstrap is set
+ # a new database file will be created, and the SQL statements
+ # contained within the file will be executed.
+ bootstrap = "${modconfdir}/${..:name}/main/${..dialect}/schema.sql"
+ bootstrap = "${modconfdir}/${..:name}/ippool/${..dialect}/schema.sql"
+ }
+ radius_db = "radius"
+
+ acct_table1 = "radacct"
+ acct_table2 = "radacct"
+ postauth_table = "radpostauth"
+ authcheck_table = "radcheck"
+ groupcheck_table = "radgroupcheck"
+ authreply_table = "radreply"
+ groupreply_table = "radgroupreply"
+ usergroup_table = "radusergroup"
+ read_groups = yes
+
+ # Remove stale session if checkrad does not see a double login
+ delete_stale_sessions = yes
+
+ pool {
+ start = 1
+ min = 0
+ max = 1
+ spare = 1
+ lifetime = 1
+ idle_timeout = 60
+ retry_delay = 1
+ }
+
+ # The group attribute specific to this instance of rlm_sql
+ group_attribute = "SQL-Group"
+
+ # Read database-specific queries
+ $INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf
+}
+
+sqlippool {
+ sql_module_instance = "sql"
+ dialect = "sqlite"
+ ippool_table = "fr_ippool"
+ lease_duration = 60
+ offer_duration = 30
+ pool_name = &control.IP-Pool.Name
+ allocated_address_attr = &reply.Framed-IP-Address
+ owner = "%{Calling-Station-Id}"
+ requested_address = "%{Framed-IP-Address}"
+ gateway = "%{NAS-IP-Address}"
+
+ $INCLUDE ${modconfdir}/sql/ippool/${dialect}/queries.conf
+}
+
--- /dev/null
+#
+# Input packet
+#
+Packet-Type = Access-Request
+User-Name = 'john'
+User-Password = 'testing123'
+NAS-IP-Address = 127.0.0.1
+Calling-Station-Id = 00:11:22:33:44:55
+
+#
+# Expected answer
+#
+Packet-Type == Access-Accept
--- /dev/null
+#
+# Test releasing IP addresses in the sqlippool module
+#
+uint32 expiry
+&control.IP-Pool.Name := 'test_release'
+
+#
+# Add IP addresses
+#
+%sql("DELETE FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}'")
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time) VALUES ('%{control.IP-Pool.Name}', '192.168.0.1', datetime('now', '-00:10'))")
+
+#
+# Check allocation
+#
+sqlippool.allocate
+if (!updated) {
+ test_fail
+}
+
+if !(&reply.Framed-IP-Address == 192.168.0.1) {
+ test_fail
+}
+
+#
+# Release the IP address
+#
+&Framed-IP-Address := &reply.Framed-IP-Address
+
+sqlippool.release
+if !(updated) {
+ test_fail
+}
+
+#
+# Verify the association with the device has been removed
+#
+if (%sql("SELECT address FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND owner = '%{Calling-Station-Id}")) {
+ test_fail
+}
+
+
+# Check the expiry - releasing an address sets the exipry to now
+# Allow for some time passing between packet processing and "now"
+&expiry := %sql("SELECT strftime('%%s', expiry_time) FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'")
+if (&expiry > (%l + 5)) {
+ test_fail
+}
+
+if ((%l - &expiry) > 5) {
+ test_fail
+}
+
+#
+# Release the IP address again
+# Will return notfound as address is already released.
+#
+&Framed-IP-Address := &reply.Framed-IP-Address
+
+sqlippool.release
+
+if (!notfound) {
+ test_fail
+}
+
+&reply := {}
+
+test_pass
--- /dev/null
+#
+# Input packet
+#
+Packet-Type = Access-Request
+User-Name = 'john'
+User-Password = 'testing123'
+NAS-IP-Address = 127.0.0.1
+Calling-Station-Id = aa:bb:cc:dd:ee:ff
+
+#
+# Expected answer
+#
+Packet-Type == Access-Accept
--- /dev/null
+#
+# Test static IP allocation using sqlippool
+#
+&control.IP-Pool.Name := 'test_static'
+
+#
+# Add a dynamic IP addresses
+#
+%sql("DELETE FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}'")
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time) VALUES ('%{control.IP-Pool.Name}', '192.168.0.1', datetime('now', '-00:20'))")
+
+#
+# Add a static IP assigned to the calling station in the request
+# with a shorter expiry time than the dynamic address - so the dynamic
+# would be chosen if simply selected on expiry time.
+#
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time, owner, status_id) VALUES ('%{control.IP-Pool.Name}', '192.168.0.10', datetime('now', '-00:10'), 'aa:bb:cc:dd:ee:ff', 2)")
+
+#
+# Check allocation
+#
+sqlippool.allocate
+if (!updated) {
+ test_fail
+}
+
+if !(&reply.Framed-IP-Address == 192.168.0.10) {
+ test_fail
+}
+
+#
+# Check that renewal does not mess with static IP
+#
+&Framed-IP-Address := &reply.Framed-IP-Address
+
+sqlippool.renew
+if !(updated) {
+ test_fail
+}
+
+#
+# Check that releasing does not mess with static IP
+#
+sqlippool.release
+if !(notfound) {
+ test_fail
+}
+
+#
+# Verify the association with the device remains
+#
+if !(%sql("SELECT address FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND owner = '%{Calling-Station-Id}'") == '192.168.0.10') {
+ test_fail
+}
+
+# Check the expiry - this will have been put in the future by the allocation / renewal
+# and not reset by the release.
+if (%sql("SELECT strftime('%%s', expiry_time) FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'") <= %l) {
+ test_fail
+}
+
+&reply := {}
+
+test_pass
--- /dev/null
+#
+# Input packet
+#
+Packet-Type = Access-Request
+User-Name = 'john'
+User-Password = 'testing123'
+NAS-IP-Address = 127.0.0.1
+Calling-Station-Id = 00:11:22:33:44:55
+
+#
+# Expected answer
+#
+Packet-Type == Access-Accept
--- /dev/null
+#
+# Test updates on sqlippool allocated addresses.
+#
+uint32 expiry
+&control.IP-Pool.Name := 'test_update'
+
+#
+# Add IP addresses
+#
+%sql("DELETE FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}'")
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time) VALUES ('%{control.IP-Pool.Name}', '192.168.0.1', datetime('now', '-00:10'))")
+
+# 1. Check allocation
+sqlippool.allocate
+if (!updated) {
+ test_fail
+}
+
+# 2.
+if !(&reply.Framed-IP-Address == 192.168.0.1) {
+ test_fail
+}
+
+# 3. Verify expiry time is based on offer duration
+&expiry := %sql("SELECT strftime('%%s', expiry_time) FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'")
+if ((&expiry - %l) < 20) {
+ test_fail
+}
+
+# 4.
+if ((&expiry - %l) > 40) {
+ test_fail
+}
+
+
+# 5. Verify the gateway was set
+if !(%sql("SELECT gateway FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'") == '127.0.0.1') {
+ test_fail
+}
+
+# 6. Add another IP addresses
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time) values ('%{control.IP-Pool.Name}', '192.168.1.1', datetime('now', '-00:10'))")
+
+# 7. Verify that the lease time is extended
+&Framed-IP-Address := &reply.Framed-IP-Address
+&NAS-IP-Address := 127.0.0.2
+
+sqlippool.renew
+if (!updated) {
+ test_fail
+}
+
+# 8. Check expiry reflects that
+&expiry := %sql("SELECT strftime('%%s', expiry_time) FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'")
+if ((&expiry - %l) < 50) {
+ test_fail
+}
+
+# 9.
+if ((&expiry - %l) > 70) {
+ test_fail
+}
+
+# 10. Verify the gateway was updated
+if !(%sql("SELECT gateway FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'") == '127.0.0.2') {
+ test_fail
+}
+
+# Change the ip address to one that doesn't exist in the pool and check we *can't* update it
+&Framed-IP-Address := 192.168.3.1
+
+sqlippool.renew
+# 11.
+if (!notfound) {
+ test_fail
+}
+
+# 12. This will have caused the update_free query to have run - which will have released the original address.
+if (%sql("SELECT address FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND owner = '00:11:22:33:44:55'")) {
+ test_fail
+}
+
+# re-issue the original allocation
+&reply := {}
+&Framed-IP-Address := 192.168.0.1
+sqlippool.allocate
+
+# 13. Now change the calling station ID and check that we *can't* update the lease
+&Calling-Station-ID := 'naughty'
+
+sqlippool.renew
+if (!notfound) {
+ test_fail
+}
+
+# 14. Verify the lease is still associated with the previous device
+if !(&reply.Framed-IP-Address == %sql("SELECT address FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND owner = '00:11:22:33:44:55'")) {
+ test_fail
+}
+
+&reply := {}
+
+test_pass
--- /dev/null
+#
+# Input packet
+#
+Packet-Type = Access-Request
+User-Name = 'john'
+User-Password = 'testing123'
+NAS-IP-Address = 127.0.0.1
+Calling-Station-Id = 00:11:22:33:44:55
+
+#
+# Expected answer
+#
+Packet-Type == Access-Accept
--- /dev/null
+#
+# Allocate an address from an SQL IP Pool and update
+#
+&control.IP-Pool.Name := 'test_update_alloc'
+
+#
+# Add IP addresses
+#
+%sql("DELETE FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}'")
+%sql("INSERT INTO fr_ippool (pool_name, address, expiry_time) VALUES ('%{control.IP-Pool.Name}', '192.168.0.1', datetime('now', '-00:10'))")
+
+# 1. Check allocation
+sqlippool.allocate
+if (!updated) {
+ test_fail
+}
+
+#
+# Attempt to update an IP address by performing a renew
+#
+&Framed-IP-Address := 192.168.0.1
+&NAS-IP-Address := 127.0.0.1
+
+sqlippool.renew
+
+# 2. Verify the gateway was set
+if !(%sql("SELECT gateway FROM fr_ippool WHERE pool_name = '%{control.IP-Pool.Name}' AND address = '%{reply.Framed-IP-Address}'") == '127.0.0.1') {
+ test_fail
+}
+
+# 4. Verify we got an IP
+if !(&reply.Framed-IP-Address == 192.168.0.1) {
+ test_fail
+}
+
+&reply := {}
+
+test_pass