gluon/package/gluon-static-ip/luasrc/lib/gluon/upgrade/540-static-ip
2022-08-11 11:13:43 +02:00

199 lines
5.5 KiB
Lua
Executable File

#!/usr/bin/lua
local uci = require('simple-uci').cursor()
local site = require 'gluon.site'
local wireless = require 'gluon.wireless'
local ip = require 'luci.ip' -- luci-lib-ip
local util = require 'gluon.util'
local IPV4_PREFIX_MAX = 32
local IPV6_PREFIX_MAX = 128
function hex2bin(str)
local map = {
['0'] = '0000',
['1'] = '0001',
['2'] = '0010',
['3'] = '0011',
['4'] = '0100',
['5'] = '0101',
['6'] = '0110',
['7'] = '0111',
['8'] = '1000',
['9'] = '1001',
['A'] = '1010',
['B'] = '1011',
['C'] = '1100',
['D'] = '1101',
['E'] = '1110',
['F'] = '1111',
}
return str:gsub('[0-9A-F]', map)
end
-- since we have a limit of 32 bit integers we.. uhm... do some hex2bin and then convert it byte by byte to a hex ipv6 block string
-- (the ip lib is in c and handles this stuff just fine)
function ipnum(macaddr, prefixOverflow, hex)
local binaryMac = hex2bin(macaddr:gsub(':', ''):upper()):sub(-prefixOverflow)
if not hex then
return tonumber(binaryMac, 2)
end
-- pad with 0s until we have block sized packets
while string.len(binaryMac) % 16 ~= 0 do
binaryMac = '0' .. binaryMac
end
local out = ''
for i=0,string.len(binaryMac)/16-1 do
if out ~= '' then
out = out .. ':'
end
out = out .. string.format("%02x", tonumber(binaryMac:sub(1+i*16,(i+1)*16), 2))
end
return out
end
local function static_ip(name, ifname, macaddr, actually_use)
-- actually_use = if ip should be applied to interface or not
-- if set and actually_use=false then it will be removed
local static4 = uci:get('gluon-static-ip', name, 'ip4')
local static6 = uci:get('gluon-static-ip', name, 'ip6')
if site.prefix4() then
if not static4 and site.node_prefix4() and name ~= 'loopback' then
local tmp4 = ip.new(site.node_prefix4())
-- magic that turns mac into random number
local ipnum = ipnum(macaddr, IPV4_PREFIX_MAX - site.node_prefix4_range())
-- the rare case where we get 0 or 1 as our mac based number
if ipnum < 2 then
ipnum = 2
end
static4 = tmp4:add(ipnum):string()
end
end
if site.prefix6() then
if not static6 and site.node_prefix6() and (site.tmpIp6Everywhere() or name == 'loopback') then
local tmp6 = ip.new(site.node_prefix6())
-- magic that turns mac into random number
local ipnum = ipnum(macaddr, IPV6_PREFIX_MAX - site.node_prefix6_range(64), true)
-- the rare case where we get 0 or 1 as our mac based number
if tonumber(ipnum:gsub(':', ''), 16) < 2 then
ipnum = 2
end
static6 = tmp6:add('::' .. ipnum):string()
end
end
uci:section('gluon-static-ip', 'interface', name, {
ip4 = static4,
ip6 = static6,
})
if actually_use then
-- we have to set proto to static as otherwise we won't have the ip assigned
-- TODO: maybe modify the protos instead to allow reading static ips and using them?
-- NOTE: wan also uses dhcp/static directly
uci:set('network', name, 'proto', 'static')
if site.prefix4 and static4 then
local ip4 = ip.new(static4)
if not ip4 or not ip4:is4() then
print('E: ' .. name .. ' has invalid ip4 ' .. static4)
return
end
uci:set('network', name, 'ipaddr', ip4:host():string())
uci:set('network', name, 'netmask', ip4:mask():string())
elseif name ~= 'loopback' then
if uci:get('network', name, 'ipaddr') then
uci:del('network', name, 'ipaddr')
end
if uci:get('network', name, 'netmask') then
uci:del('network', name, 'netmask')
end
end
if site.prefix6 and static6 then
local ip6 = ip.new(static6)
if not ip6 or not ip6:is6() then
print('E: ' .. name .. ' has invalid ip6 ' .. static6)
return
end
uci:set('network', name, 'ip6addr', ip6:string())
else
if uci:get('network', name, 'ip6addr') then
uci:del('network', name, 'ip6addr')
end
end
else
if uci:get('network', name, 'proto') == 'static' then
uci:del('network', name, 'ipaddr')
uci:del('network', name, 'netmask')
uci:del('network', name, 'ip6addr')
end
end
end
wireless.foreach_radio(uci, function(radio, index, config)
local function do_config(type)
net = type .. radio['.name']
use = uci:get('wireless', net, 'disabled') ~= '1'
static_ip(net, uci:get('wireless', net, 'ifname'), uci:get('wireless', net, 'macaddr'), use)
end
do_config('mesh_')
if uci:get('network', 'ibss_' .. radio['.name'], 'proto') then
do_config('ibss_')
end
end)
local function apply_network(name, use, mac)
if not uci:get('network', name, 'proto') then
print('warn: non-existent network ' .. name)
return
end
not_disabled = uci:get('network', name, 'disabled') ~= '1'
if use == nil then
use = not_disabled
end
macaddr = uci:get('network', name, 'macaddr')
if not macaddr then
macaddr = util.generate_mac(mac)
uci:set('network', name, 'macaddr', macaddr)
end
static_ip(name, uci:get('network', name, 'ifname'), macaddr, use)
end
if pcall(function() require 'gluon.mesh-vpn' end) then
local vpn_core = require 'gluon.mesh-vpn'
apply_network('mesh_vpn', vpn_core.enabled(), 7)
end
local uplink_mesh = not uci:get_bool('network', 'mesh_uplink', 'disabled')
apply_network('mesh_uplink', uplink_mesh, 10)
local other_mesh = not uci:get_bool('network', 'mesh_other', 'disabled')
apply_network('mesh_other', other_mesh, 11)
apply_network('loopback', true, 12)
uci:save('gluon-static-ip')
uci:save('network')