diff --git a/luci-app-ap-modem/Makefile b/luci-app-ap-modem/Makefile new file mode 100644 index 00000000..6f7cefbc --- /dev/null +++ b/luci-app-ap-modem/Makefile @@ -0,0 +1,17 @@ + +include $(TOPDIR)/rules.mk + +PKG_VERSION:=1.0.5-20241208 +PKG_RELEASE:= +PKG_MAINTAINER:=jjm2473 + +LUCI_TITLE:=Easy Access AP / Modem +LUCI_PKGARCH:=all + +define Package/luci-app-ap-modem/conffiles +/etc/config/ap_modem +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-ap-modem/htdocs/luci-static/resources/view/ap_modem.js b/luci-app-ap-modem/htdocs/luci-static/resources/view/ap_modem.js new file mode 100644 index 00000000..a5438670 --- /dev/null +++ b/luci-app-ap-modem/htdocs/luci-static/resources/view/ap_modem.js @@ -0,0 +1,54 @@ +'use strict'; +'require view'; +'require uci'; +'require form'; + +return view.extend({ + load: function() { + return Promise.all([ + uci.load('ap_modem') + ]); + }, + + render: function() { + + var m, s, o, ss; + + m = new form.Map('ap_modem', _('Access AP / Modem'), + _('Allows clients in the local network to access AP or modem on different subnet.')); + + s = m.section(form.NamedSection, 'config', 'ap_modem', _('Global Settings')); + s.anonymous = true; + s.addremove = false; + + o = s.option(form.Flag, 'enabled', _('Enable')); + o.rmempty = false; + + s = m.section(form.NamedSection, 'config', 'ap_modem', _('Interface Settings')); + s.anonymous = true; + s.addremove = false; + + [ + {id:"lan", title:_("LAN"), subtitle:_("AP on LAN side"), placeholder:"192.168.31.254", + example:_("
For example, you want to access the AP, its IP address is 192.168.31.1, but the client and the router are not in its subnet, so it cannot be connected. " + + "Then you can add 192.168.31.254 here, the client will be able to access 192.168.31.1 after saving and applying.")}, + {id:"wan", title:_("WAN"), subtitle:_("AP / Modem on WAN side"), placeholder:"192.168.1.254", + example:_("
For example, you want to access the modem, its IP address is 192.168.1.1, but because it uses PPPoE bridge mode, so it cannot be connected. " + + "Then you can add 192.168.1.254 here, the client will be able to access 192.168.1.1 after saving and applying.")}, + ].forEach(function(vif) { + s.tab(vif.id, vif.title); + + o = s.taboption(vif.id, form.SectionValue, '__'+vif.id+'__', form.NamedSection, vif.id, null, + vif.subtitle, _('Here add the IP address of the same subnet as the target device, but not the same as the target device.
Do not add IPs already used by other devices.') + vif.example); + + ss = o.subsection; + ss.anonymous = true; + + o = ss.option(form.DynamicList, 'ipaddr', _('Virtual IP'), _('Supports "IP/MASK", "IP/PREFIX", and pure "IP" format, pure "IP" assumes a prefix of 24 bits')); + o.datatype = 'ipmask4'; + o.placeholder = vif.placeholder; + }); + + return m.render(); + } +}); diff --git a/luci-app-ap-modem/po/zh-cn/ap-modem.po b/luci-app-ap-modem/po/zh-cn/ap-modem.po new file mode 100644 index 00000000..a8b05942 --- /dev/null +++ b/luci-app-ap-modem/po/zh-cn/ap-modem.po @@ -0,0 +1,43 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8" + +msgid "Access AP / Modem" +msgstr "访问AP/光猫" + +msgid "Allows clients in the local network to access AP or modem on different subnet." +msgstr "让局域网客户端访问不同子网的AP或光猫" + +msgid "Global Settings" +msgstr "全局设置" + +msgid "Interface Settings" +msgstr "网络接口配置" + +msgid "AP on LAN side" +msgstr "位于LAN口的AP" + +msgid "AP / Modem on WAN side" +msgstr "位于WAN口的AP或光猫" + +msgid "Virtual IP" +msgstr "虚拟IP地址" + +msgid "" +"Here add the IP address of the same subnet as the target device, but not the same as the target device. " +"
Do not add IPs already used by other devices." +msgstr "" +"这里添加与目标设备同子网的IP地址,但不能与目标设备IP完全相同。
不要添加其他设备正在使用的IP。" + +msgid "" +"
For example, you want to access the AP, its IP address is 192.168.31.1, but the client and the router are not in its subnet, so it cannot be connected. " +"Then you can add 192.168.31.254 here, the client will be able to access 192.168.31.1 after saving and applying." +msgstr "
例如,你想要访问AP后台,它的IP地址是192.168.31.1,但是由于现在客户端和路由器都不在它的子网,无法连接。那么可以在这里添加192.168.31.254,保存并应用之后客户端就能访问192.168.31.1了。" + +msgid "" +"
For example, you want to access the modem, its IP address is 192.168.1.1, but because it uses PPPoE bridge mode, so it cannot be connected. " +"Then you can add 192.168.1.254 here, the client will be able to access 192.168.1.1 after saving and applying." +msgstr "
例如,你想要访问光猫后台,它的IP地址是192.168.1.1,但是由于它使用PPPoE桥接模式,无法连接。那么可以在这里添加192.168.1.254,保存并应用之后客户端就能访问192.168.1.1了。" + +msgid "Supports \"IP/MASK\", \"IP/PREFIX\", and pure \"IP\" format, pure \"IP\" assumes a prefix of 24 bits" +msgstr "支持“IP/掩码”、“IP/前缀长度”、以及纯“IP”格式,纯“IP”则假设前缀24位" diff --git a/luci-app-ap-modem/po/zh_Hans b/luci-app-ap-modem/po/zh_Hans new file mode 120000 index 00000000..41451e4a --- /dev/null +++ b/luci-app-ap-modem/po/zh_Hans @@ -0,0 +1 @@ +zh-cn \ No newline at end of file diff --git a/luci-app-ap-modem/root/etc/config/ap_modem b/luci-app-ap-modem/root/etc/config/ap_modem new file mode 100644 index 00000000..7f1957de --- /dev/null +++ b/luci-app-ap-modem/root/etc/config/ap_modem @@ -0,0 +1,8 @@ +config ap_modem 'config' + option enabled '0' + +config interface 'lan' + # list ipaddr '192.168.31.254' + +config interface 'wan' + # list ipaddr '192.168.1.254' diff --git a/luci-app-ap-modem/root/etc/init.d/ap_modem b/luci-app-ap-modem/root/etc/init.d/ap_modem new file mode 100755 index 00000000..78ff7a8b --- /dev/null +++ b/luci-app-ap-modem/root/etc/init.d/ap_modem @@ -0,0 +1,208 @@ +#!/bin/sh /etc/rc.common + +START=20 +USE_PROCD=1 + +boot() { + # procd will call service_triggers + rc_procd true +} + +service_triggers() { + procd_add_reload_trigger "network" "ap_modem" +} + +find_uci_section_i() { + local key="$2" + local value="$3" + local testv + config_get testv "$1" "$key" + [[ "$value" = "$testv" ]] && echo "$1" +} + +find_uci_section() { + local config="$1" + local type="$2" + local key="$3" + local value="$4" + ( + config_load "$config" + config_foreach find_uci_section_i "$type" "$key" "$value" + ) +} + +generate_config() { + [[ "`uci -q get network.lan.device`" = "br-lan" ]] || { + echo "network.lan.device != br-lan in uci" >&2 + return 1 + } + local wan_dev="`uci -q get network.wan.device`" + [[ -z "$wan_dev" ]] && { + echo "get network.wan.device in uci failed" >&2 + return 1 + } + local lan_dev=`find_uci_section network device name 'br-lan' | head -1` + [[ -z "$lan_dev" ]] && { + echo "network.device.name=br-lan not found in uci" >&2 + return 1 + } + + local wan_zone=`find_uci_section firewall zone name 'wan' | head -1` + [[ -z "$wan_zone" ]] && { + echo "firewall.zone.name=wan not found in uci" >&2 + return 1 + } + + # virutal lan device + uci -q get network.veth_lan >/dev/null || uci -q batch <<-EOF >/dev/null + set network.veth_lan=device + set network.veth_lan.type=veth + set network.veth_lan.name=vap-lan + set network.veth_lan.sendredirects=0 + set network.veth_lan.ipv6=0 + set network.veth_lan.multicast=0 + set network.veth_lan.peer_name=vap-lan-peer +EOF + uci -q get network.veth_lan_peer >/dev/null || uci -q batch <<-EOF >/dev/null + set network.veth_lan_peer=device + set network.veth_lan_peer.name=vap-lan-peer + set network.veth_lan_peer.sendredirects=0 + set network.veth_lan_peer.ipv6=0 + set network.veth_lan_peer.multicast=0 +EOF + uci -q get "network.$lan_dev.ports" | grep -Fwq 'vap-lan' || uci add_list "network.$lan_dev.ports=vap-lan" + + # interface + if uci -q get network.vap_lan >/dev/null; then + uci -q delete network.vap_lan.auto + else + uci -q batch <<-EOF >/dev/null + set network.vap_lan=interface + set network.vap_lan.proto=static + set network.vap_lan.device=vap-lan-peer + set network.vap_lan.defaultroute=0 + set network.vap_lan.delegate=0 +EOF + fi + if uci -q get network.vap_wan >/dev/null; then + uci -q delete network.vap_wan.auto + else + uci -q batch <<-EOF >/dev/null + set network.vap_wan=interface + set network.vap_wan.proto=static + set network.vap_wan.device=$wan_dev + set network.vap_wan.defaultroute=0 + set network.vap_wan.delegate=0 +EOF + fi + # firewall + local fw_wan_net="`uci -q get firewall.$wan_zone.network`" + echo "$fw_wan_net" | grep -Fwq 'vap_lan' || uci -q batch <<-EOF >/dev/null + add_list firewall.$wan_zone.network=vap_lan +EOF + echo "$fw_wan_net" | grep -Fwq 'vap_wan' || uci -q batch <<-EOF >/dev/null + add_list firewall.$wan_zone.network=vap_wan +EOF + uci commit firewall + + # ip + local bip + local black_ip="127.0.0.1/8" + [[ "`uci -q get network.lan.proto`" = "static" ]] && { + local lan_ip="`uci -q get network.lan.ipaddr`" + if [[ -n "$lan_ip" ]]; then + local netmask="`uci -q get network.lan.netmask`" + [[ -n "$netmask" ]] || netmask=255.255.255.255 + for bip in $lan_ip; do + if [[ "$bip" = "*/*" ]]; then + eval "$(ipcalc.sh $bip )";black_ip="$black_ip $IP/$PREFIX" + else + eval "$(ipcalc.sh $bip $netmask )";black_ip="$black_ip $IP/$PREFIX" + fi + done + fi + } + + logger -t 'ap_modem' -p INFO "black: $black_ip" + + local ipaddr + local vip + local vip_p + local vnet + local bnet + local ok + local vif + for vif in lan wan; do + config_get ipaddr $vif ipaddr + [[ -n "$ipaddr" ]] || continue + for vip in $ipaddr; do + ok=1 + [[ "$vip" = "*/*" ]] || vip="$vip/24" + eval "$(ipcalc.sh $vip )";vip="$IP";vip_p="$PREFIX" + [[ "$vip_p" = 32 ]] && { + logger -t 'ap_modem' -p DEBUG "vap_$vif skip $vip/$vip_p" + break + } + for bip in $black_ip; do + eval "$(ipcalc.sh "$bip" )";bip="$IP"; + [[ "$PREFIX" -le "$vip_p" ]] || PREFIX=$vip_p + eval "$(ipcalc.sh "$vip/$PREFIX" )";vnet="$NETWORK"; + eval "$(ipcalc.sh "$bip/$PREFIX" )";bnet="$NETWORK"; + if [[ "$vnet" = "$bnet" ]]; then + logger -t 'ap_modem' -p DEBUG "vap_$vif skip $vip/$vip_p" + ok=0 + break + fi + done + [[ "$ok" = 1 ]] && { + uci add_list "network.vap_$vif.ipaddr=$vip/$vip_p" + black_ip="$black_ip $vip/$vip_p" + } + done + done + + uci commit network +} + +clean_config() { + local wan_zone=`find_uci_section firewall zone name 'wan' | head -1` + [[ -z "$wan_zone" ]] || uci -q batch <<-EOF >/dev/null + del_list firewall.$wan_zone.network=vap_lan + del_list firewall.$wan_zone.network=vap_wan + commit firewall +EOF + local lan_dev=`find_uci_section network device name 'br-lan' | head -1` + [[ -z "$lan_dev" ]] || { + uci del_list "network.$lan_dev.ports=vap-lan" + } + + uci -q batch <<-EOF >/dev/null + set network.vap_lan.auto=0 + set network.vap_wan.auto=0 + delete network.veth_lan_peer + delete network.veth_lan + commit network +EOF +} + +start_service() { + config_load ap_modem + config_get_bool enabled "config" enabled 0 + uci -q batch <<-EOF >/dev/null + delete network.vap_lan.ipaddr + delete network.vap_wan.ipaddr + commit network +EOF + if [[ "$enabled" = "1" ]]; then + generate_config + else + clean_config + fi + /etc/init.d/network reload + return 0 +} + +stop_service() { + clean_config + /etc/init.d/network reload +} diff --git a/luci-app-ap-modem/root/usr/share/luci/menu.d/luci-app-ap-modem.json b/luci-app-ap-modem/root/usr/share/luci/menu.d/luci-app-ap-modem.json new file mode 100644 index 00000000..e10e1d0c --- /dev/null +++ b/luci-app-ap-modem/root/usr/share/luci/menu.d/luci-app-ap-modem.json @@ -0,0 +1,14 @@ +{ + "admin/network/ap_modem": { + "title": "Access AP / Modem", + "order": 99, + "action": { + "type": "view", + "path": "ap_modem" + }, + "depends": { + "acl": [ "luci-app-ap-modem" ], + "uci": { "ap_modem": true } + } + } +} diff --git a/luci-app-ap-modem/root/usr/share/rpcd/acl.d/luci-app-ap-modem.json b/luci-app-ap-modem/root/usr/share/rpcd/acl.d/luci-app-ap-modem.json new file mode 100644 index 00000000..3fbdd084 --- /dev/null +++ b/luci-app-ap-modem/root/usr/share/rpcd/acl.d/luci-app-ap-modem.json @@ -0,0 +1,11 @@ +{ + "luci-app-ap-modem": { + "description": "Grant access to 'Access AP / Modem'", + "read": { + "uci": [ "ap_modem" ] + }, + "write": { + "uci": [ "ap_modem" ] + } + } +}