🦄 Sync 2026-08-26 20:42:53

This commit is contained in:
github-actions[bot] committed 2026-08-26 20:42:53 +08:00
1 parent 53bb25bf38
commit e7a277e681
487 files changed
+55081 -19590

No files matched your search

+19 -18
View File
@@ -2,18 +2,18 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=appfilter
PKG_VERSION:=6.1.8
PKG_RELEASE:=2
PKG_VERSION:=7.0.1
PKG_RELEASE:=3
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
TARGET_CFLAGS +=-Werror=implicit-function-declaration
define Package/appfilter
SECTION:=TT Apps
CATEGORY:=TT Apps
DEPENDS:=+libubox +libubus +libuci +libpthread +libjson-c +libblobmsg-json
TITLE:=OAF client and config service
SECTION:=Base system
CATEGORY:=Base system
DEPENDS:=+libubox +libubus +libuci +libpthread +libjson-c +libblobmsg-json +libuci-lua +libsqlite3 +libcurl
TITLE:=OAF service
endef
define Build/Prepare
@@ -25,7 +25,7 @@ define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR)/ \
CC="$(TARGET_CROSS)gcc" \
CFLAGS="$(TARGET_CFLAGS)" \
LIBS="$(TARGET_LDFLAGS) -lm -lpthread -lubox -luci -lubus -ljson-c -lblobmsg_json" \
LIBS="$(TARGET_LDFLAGS) -lm -lpthread -lubox -luci -lubus -ljson-c -lblobmsg_json -lsqlite3 -lcurl" \
all
endef
@@ -35,28 +35,29 @@ define Build/Compile/Default
endef
define Package/appfilter/description
openappfilter app
OAF service
endef
define Package/appfilter/conffiles
/etc/config/appfilter
/etc/config/user_info
/etc/fwxd/custom_feature.cfg
/etc/config/fwx
endef
define Package/appfilter/install
$(INSTALL_DIR) $(1)/usr/bin $(1)/etc/init.d
$(INSTALL_DIR) $(1)/etc/appfilter
$(INSTALL_DIR) $(1)/etc/fwxd
$(INSTALL_DIR) $(1)/etc/config
$(CP) ./files/*.cfg $(1)/etc/appfilter/
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d/
$(INSTALL_DATA) ./files/feature.bin $(1)/etc/fwxd/feature.bin
$(INSTALL_DATA) ./files/custom_feature.cfg $(1)/etc/fwxd/custom_feature.cfg
$(INSTALL_BIN) ./files/appfilter.init $(1)/etc/init.d/appfilter
$(INSTALL_BIN) ./files/oaf_rule $(1)/usr/bin
$(INSTALL_BIN) ./files/gen_class.sh $(1)/usr/bin
$(INSTALL_DATA) ./files/appfilter.config $(1)/etc/config/appfilter
$(INSTALL_DATA) ./files/user_info.config $(1)/etc/config/user_info
$(INSTALL_BIN) $(PKG_BUILD_DIR)/oafd $(1)/usr/bin
$(INSTALL_BIN) ./files/hnat.sh $(1)/usr/bin
$(INSTALL_BIN) ./files/rule_manager.lua $(1)/usr/bin/rule_manager
$(INSTALL_BIN) ./files/hnat.sh $(1)/usr/bin/hnat.sh
$(INSTALL_DATA) ./files/oaf_version $(1)/etc/oaf_version
$(INSTALL_DATA) ./files/luci-app-oaf.json $(1)/usr/share/rpcd/acl.d/
endef
$(eval $(call BuildPackage,appfilter))
+1 -34
View File
@@ -1,36 +1,3 @@
config global global
option enable '0'
option work_mode '0'
option record_enable '1'
option disable_hnat '0'
option tcp_rst '1'
option lan_ifname 'br-lan'
option auto_load_engine '1'
option disable_quic '0'
config appfilter appfilter
config feature feature
option update 0
option format 'v3.0'
config time 'time'
option deny_time '60'
option start_time '00:00'
option end_time '23:59'
option allow_time '20'
option time_mode '0'
option days '1 2 3 4 5 6 0'
list time '00:00-23:59'
option daily_limit_0 '0:0:0'
option daily_limit_1 '0:0:0'
option daily_limit_2 '0:0:0'
option daily_limit_3 '0:0:0'
option daily_limit_4 '0:0:0'
option daily_limit_5 '0:0:0'
option daily_limit_6 '0:0:0'
config user user
config rule 'rule'
+95 -10
View File
@@ -1,28 +1,113 @@
#!/bin/sh /etc/rc.common
. /usr/share/libubox/jshn.sh
. /lib/functions.sh
START=96
USE_PROCD=1
OAFD_BIN="/usr/bin/oafd"
FEATURE_FILE="/tmp/feature.cfg"
FWXD_BIN="/usr/bin/oafd"
CUSTOM_FEATURE_FILE="/etc/fwxd/custom_feature.cfg"
LEGACY_CUSTOM_FEATURE_FILE="/etc/custom_feature.cfg"
ensure_config_file()
{
local file="$1"
[ -e "$file" ] && return
mkdir -p /etc/config
cat > "$file"
chmod 0644 "$file"
}
ensure_default_configs()
{
ensure_config_file /etc/config/appfilter <<'EOF'
EOF
ensure_config_file /etc/config/appfilter_whitelist <<'EOF'
EOF
ensure_config_file /etc/config/fwx <<'EOF'
config global global
option lan_ifname 'br-lan'
option tcp_rst '1'
option theme_mode '1'
option feature_token ''
config appfilter appfilter
option enable 1
config macfilter macfilter
option enable 1
config record 'record'
option enable '1'
option record_time '3'
option app_valid_time '3'
option history_data_size '10'
option history_data_path '/tmp/oaf'
option base_data_path '/tmp/oaf'
config network network
option work_mode 0
config dashboard 'dashboard'
config advanced 'advanced'
option disable_hnat '0'
config status 'status'
option notice_status '0'
EOF
ensure_config_file /etc/config/fwx_record <<'EOF'
config whitelist 'whitelist'
EOF
ensure_config_file /etc/config/macfilter <<'EOF'
config global 'global'
option enable '1'
EOF
ensure_config_file /etc/config/macfilter_whitelist <<'EOF'
EOF
ensure_config_file /etc/config/mac_blacklist <<'EOF'
config settings 'base'
EOF
ensure_config_file /etc/config/user_info <<'EOF'
EOF
}
stop_service(){
killall -9 oafd
killall -9 rule_manager
}
start_service(){
test -f $FEATURE_FILE &&{
rm $FEATURE_FILE
}
ensure_default_configs
if [ ! -f /etc/appfilter/feature.cfg ]; then
cp /etc/appfilter/feature_cn.cfg /etc/appfilter/feature.cfg
lsmod |grep "oaf" >/dev/null
if [ $? -ne 0 ];then
modprobe oaf
fi
[ -x /usr/bin/hnat.sh ] && /usr/bin/hnat.sh 1
if [ -s "$LEGACY_CUSTOM_FEATURE_FILE" ] && [ ! -s "$CUSTOM_FEATURE_FILE" ]; then
mv -f "$LEGACY_CUSTOM_FEATURE_FILE" "$CUSTOM_FEATURE_FILE"
chmod 0644 "$CUSTOM_FEATURE_FILE"
fi
hnat.sh 1
ln -s /etc/appfilter/feature.cfg $FEATURE_FILE
procd_open_instance
procd_set_param respawn 60 5 5
procd_set_param stderr 1
procd_set_param command "$OAFD_BIN"
procd_set_param command "$FWXD_BIN"
procd_close_instance
procd_open_instance
procd_set_param respawn 60 5 5
procd_set_param stderr 1
procd_set_param command "/usr/bin/rule_manager"
procd_close_instance
}
Whitespace-only changes.
Whitespace-only changes.
Binary file not shown.
-256
View File
@@ -1,256 +0,0 @@
#version v22.3.24
#format v3.0
#id name:[proto;sport;dport;host url;request;dict]
#class chat 1 聊天
1003 微博:[tcp;;;weibo;;]
1004 陌陌:[tcp;;;momo;;,tcp;;;;;04:2f|05:66|06:65|07:65;;1,tcp;;;;;00:03|01:03|02:00;;1]
1005 支付宝:[tcp;;;alipay.com;;,tcp;;;alipayobjects.com;;,tcp;;;alive.alipay.com;;,udp;;1100-1200;;;00:00|01:01|02:00,tcp;;80;;;00:50|01:52|02:49]
1006 钉钉:[tcp;;;dingtalk;;,tcp;;;;d?host=;,tcp;;;;/man/api;]
1007 Soul:[tcp;;;soulapp;;]
1008 伊对:[tcp;;;520yidui;;]
1009 探探:[tcp;;;tancdn;;,tcp;;;tantanapp;;]
1010 多闪:[tcp;;;ppkankan;;]
1012 Instagram:[tcp;;;instagram;;]
1013 Facebook:[tcp;;;facebook;;]
1014 WhatsApp:[tcp;;;whatsapp;;]
#class game 2 游戏
2001 王者荣耀:[tcp;;6000-9000;;;00:33|1:66|02:00|03:0a]
2027 英雄联盟手游:[tcp;;10001;;;00:33|1:66|02:00|03:0b,tcp;;11001;;;00:33|1:66|02:00|03:0b]
2005 欢乐斗地主:[tcp;;12000;;;00:43|01:66|02:aa|03:00,tcp;;;huanle.qq.com;;]
2015 我的世界:[tcp;;443;g79mclobt.nie.netease;;,tcp;;443;x19.*.netease.com;;,tcp;;443;mc.*.netease;;]
2006 梦幻西游:[tcp;;;;;00:0e|01:00|02:fe|03:ff,tcp;;;g18.proxima.nie;;]
2007 明日之后:[tcp;;12500-14000;;;00:02|01:00|02:00|03:00|04:00|05:00,tcp;;;g66.update.netease;;]
2008 QQ飞车:[udp;;;;;00:28|01:28,tcp;;10000;;;00:33|01:66|02:00|03:08]
2009 跑跑卡丁车:[tcp;;8888;;;00:33|01:66|02:00|03:0b,tcp;;49150-49159;;wepop/;]
2010 开心消消乐:[tcp;;80;happyelements;;]
2011 狂野飙车:[tcp;;;asphalt9;;]
2012 率土之滨:[tcp;;10001;;;00:00|01:00,tcp;;8001;;;00:00|01:00]
2013 一刀传世:[tcp;;8040;;;00:47|01:45]
2014 第五人格:[tcp;;;h55.proxima;;,tcp;;;h55.update;;]
2016 皇室战争:[udp;;9339;;;]
2017 炉石传说:[tcp;;3724;;;00:73:01:00:02:00]
2023 原神:[tcp;;443;yuanshen.com;;]
2025 天涯明月刀:[tcp;;10000;;;00:43|01:66|02:aa]
2026 微信小游戏:[tcp;;443;mmgame;;,tcp;;443;game.weixin.qq;;]
2033 我叫MT4:[tcp;;21248;;;,tcp;;;dir.mt4.qq.com;;]
2034 神都夜行录:[udp;;;;;00:00|01:00|02:00|03:00|04:56|05:40]
2041 光遇:[udp;;10000-15000;;;00:8f|01:ff,tcp;;;ma75.update.netease.com;;,tcp;;;ma75.proxima.nie.netease;;]
2042 保卫萝卜4:[tcp;;;s4.luobo.cn;;]
2040 哈利波特:[tcp;;10021-12000;;;00:02|01:00|02:00|03:00|04:00|05:00,tcp;;443;g92.proxima;;]
2067 9377游戏:[tcp;;;www.9377.com;;]
2068 4399游戏:[tcp;;;4399.com;;]
2069 7k7k游戏:[tcp;;;7k7k.com;;]
2070 17173游戏:[tcp;;;17173.com;;]
2071 37网游:[tcp;;;37.com;;]
2074 hao123游戏:[tcp;;;game.hao123.com;;]
2075 51游戏:[tcp;;;www.51.com;;]
2050 uu加速器:[tcp;;;mg.uu.163.com;;]
2051 腾讯加速器:[tcp;;;m.acc.qq.com;;]
2080 乐逗游戏:[tcp;;;.uu.cc;;]
#class video 3 视频
3001 抖音:[tcp;;;-dy-;;,tcp;;;-dy.;;,tcp;;;douyin;;,tcp;;;amemv.com;;,tcp;;;pstatp.com;;,tcp;;;volcsirius.com;;,tcp;;80;;^/pull.*.douyincdn.com;,tcp;;;ecombdapi.com;;,udp;;443;;;09:51|10:30|11:34;amemv.com;0,udp;;16000;;;00:00|01:01,udp;;1000-2000;;;00:00|01:01]
3049 Youtube:[tcp;;;youtube;;]
3006 斗鱼:[tcp;;;douyu;;,tcp;;;douyu;;-2:2f|-1:00]
3004 爱奇艺:[tcp;;;iqiyi;;,tcp;;;qy.net;;,tcp;;;inter.71edge.com;;,tcp;;;;^/videos;,tcp;;80;;;00:51|01:48|02:54,tcp;;80;;;09:00|10:00|11:00]
3043 Netflix:[tcp;;;netflix.com;;]
3008 虎牙直播:[tcp;;;huya;;,udp;;;;;01:00|02:00|03:00|04:23,udp;;;;;01:00|02:00|03:00|04:24]
3010 小红书:[tcp;;;xiaohongshu;;,tcp;;;xhscdn;;]
3011 花椒直播:[tcp;;;huajiao;;]
3012 映客直播:[tcp;;;;.inke.cn;]
3016 芒果tv:[tcp;;443;mgtv;;,tcp;;80;mgtv;;,tcp;;443;hitv;;]
3017 西瓜视频:[tcp;;;ixigua;;,tcp;;443;snsdk;;,tcp;;;xg-p.ixigua;;,tcp;;;bdxigua;;]
3018 搜狐视频:[tcp;;;aty.sohu.com;;,tcp;;;tv.itc.cn;;]
3020 咪咕视频:[tcp;;;miguvideo;;,tcp;;;migu.cn;;]
3022 人人视频:[tcp;;;rr.tv;;]
3023 央视影音:[tcp;;;cntv;;]
3024 优酷&酷喵:[tcp;;;youku;;,tcp;;;ykimg;;,tcp;;;;/youku;,tcp;;;galitv.alicdn.com;;,tcp;;;cibntv.;;,tcp;;;miaozhen.com;;;;1]
3025 最右:[tcp;;;izuiyou;;]
3026 风行视频:[tcp;;;funshion;;]
3019 播聊:[tcp;;80;randlove.cn;;,tcp;;;yueliao;;,tcp;;;5glive;;]
3021 韩剧TV:[tcp;;;hanju.koudaibaobao;;]
3027 企鹅电竞:[tcp;;;egame.qq;;,tcp;;;liveplay;;,tcp;;;;pggame;]
3028 波波视频:[tcp;;;miaopai;;]
3029 酷狗短酷:[tcp;;;bssdl.kugou;;]
3030 酷狗直播:[tcp;;;rt-m.kugou;;,tcp;;;kgimg.com;;]
3023 央视影音:[tcp;;;cntv;;]
3026 风行视频:[tcp;;;funshion;;]
3089 华数TV:[tcp;;;wasu.cn;;]
3121 梨视频:[tcp;;;pearvideo.com;;]
3094 南瓜电影:[tcp;;;vcinema.cn;;]
3084 六间房:[tcp;;;v.6.cn;;]
3085 百度直播:[tcp;;;live.baidu.com;;]
3086 度小视:[tcp;;;quanmin.baidu.com;;]
#class shopping 4 购物
4001 淘宝:[tcp;;;taobao;;,tcp;;;alicdn.com;;,tcp;;;tmall.com;;,tcp;;443;;;00:d1|01:00,tcp;;443;;;00:d2|01:00,tcp;;443;;;00:d3|01:00,tcp;;443;;;00:d4|01:00,tcp;;443;;;00:d5|01:00,tcp;;443;;;00:b1|01:00,tcp;;443;;;00:b2|01:00,tcp;;443;;;00:b3|01:00,tcp;;443;;;00:b4|01:00,tcp;;443;;;00:b5|01:00,udp;;1000-1200;;;00:82|01:cc,tcp;;;;/mediaplatform;]
4002 京东:[tcp;;;360buyimg;;,tcp;;;jd.com;;,tcp;;;jdcdn.com;;,tcp;;;vod.300hu.com;;]
4003 唯品会:[tcp;;;vips-mobile;;,tcp;;;vipshop;;,tcp;;;vip.com;;,tcp;;;vipstatic.com;;,tcp;;;appsimg.com;;]
4004 拼多多:[tcp;;;pinduoduo;;,tcp;;;yangkeduo.com;;,tcp;;;s1p.cdntip.com;;]
4010 饿了么:[tcp;;;eleme;;]
4012 闲鱼:[tcp;;;xianyu;;]
4021 转转:[tcp;;;zhuanzhuan;;,tcp;;;zhuanstatic;;]
4005 蘑菇街:[tcp;;;mogujie;;,tcp;;;mogucdn;;,tcp;;;;;00:73|01:ea|02:68|03:fb|04:3f]
4006 苏宁易购:[tcp;;;.suning.;;]
4007 当当网:[tcp;;;.dangdang.com;;]
4008 1号店:[tcp;;;.yhd.com;;]
4009 朴朴超市:[tcp;;;pupumall;;,tcp;;;pupuapi;;]
4013 叮咚买菜:[tcp;;;ddxq.mobi;;]
4014 小米有品:[tcp;;;youpin;;,tcp;;;shopapi.io.mi.com;;]
4015 微店:[tcp;;;weidian;;]
4016 折800:[tcp;;;zhe800.com;;]
4018 好省:[tcp;;;hzhstb.com;;]
4019 什么值得买:[tcp;;;smzdm.com;;]
4022 网易严选:[tcp;;;yanxuan;;]
4023 识货:[tcp;;;shihuo;;]
4024 考拉海购:[tcp;;;kaola;;]
4025 宜家家居:[tcp;;;ikea.cn;;]
4026 小象优品:[tcp;;;xiaoxiangyoupin;;]
4040 国美:[tcp;;;gome.com;;]
4041 酒仙网:[tcp;;;jiuxian.com;;]
4052 1688:[tcp;;;1688.com;;]
4053 亚马逊:[tcp;;;amazon.cn;;]
4054 Lazada:[tcp;;;lazada.com;;]
#class music 5 音乐
5001 网易云音乐:[tcp;;;music.163;;,tcp;;;music.126;;]
5002 QQ音乐:[tcp;;;;^/amobile.music.tc.qq.com;,tcp;;;qqmusic;;]
5003 酷狗音乐:[tcp;;;kugou;;,tcp;;;kgimg;;,tcp;;;fanxing;;]
5004 酷我音乐:[tcp;;;.kuwo.cn;;]
5005 喜马拉雅:[tcp;;;.ximalaya.com;;]
5006 千千音乐:[tcp;;;music.taihe.com;;]
5007 虾米音乐:[tcp;;;xiami;;]
5008 音悦台:[tcp;;;yinyuetai.com;;]
5009 豆瓣FM:[tcp;;;douban.fm;;]
5010 唱吧:[tcp;;;changba.com;;]
5011 音乐随心听:[tcp;;;fm.taihe.com;;]
5012 懒人听书:[tcp;;;lrts.me;;]
#class employee 6 招聘
6001 前程无忧:[tcp;;;51job;;]
6002 智联招聘:[tcp;;;zhaopin;;]
6003 猎聘:[tcp;;;liepin;;]
6004 赶集网:[tcp;;;58.com;;,tcp;;;58cdn;;]
6005 同城急聘:[tcp;;;xiaomei;;]
6006 领英:[tcp;;;linkedin;;]
6007 斗米:[tcp;;;doumi;;]
6008 看准:[tcp;;;kanzhun.com;;]
6009 应届生求职:[tcp;;;yingjiesheng.com;;]
6010 中华英才网:[tcp;;;chinahr.com;;]
6011 拉勾网:[tcp;;;lagou.com;;]
6012 大街网:[tcp;;;dajie.com;;]
6013 boss直聘:[tcp;;;zhipin.com;;]
6014 实习僧:[tcp;;;shixiseng.com;;]
#class download 7 下载
7002 AppStore:[tcp;;;itunes.apple.com;;]
7004 ftp文件传输:[tcp;;21;;;]
7005 vivo应用商店:[tcp;;443;appstore.vivo;;,tcp;;443;apkappdefwsdl.vivo;;]
7006 王者荣耀更新:[tcp;;80;;/sgame/;]
7007 天翼云盘:[tcp;;;ctyunapi;;]
7008 腾讯微云:[tcp;;;weiyun.com;;,tcp;;;aegis.qq.com;;,tcp;;;pingtas.qq.com;;,tcp;;443;;;00:77|01:6e|02:73]
7009 坚果云:[tcp;;;jianguoyun;;]
7010 蓝奏云:[tcp;;;pan.lanzou.com;;]
7011 华为云:[tcp;;;cloud.huawei.com;;,tcp;;;hicloud.com;;,tcp;;;myhuaweicloud.cn;;]
7020 windows更新:[tcp;;80;update.microsoft.com;;,tcp;;;windowsupdate.com;;]
7030 向日葵:[tcp;;;oray.com;;,tcp;;;oray.net;;]
7031 TeamViewer:[tcp;;;teamviewer;;]
7032 阿里云盘:[tcp;;;aliyundrive;;]
#class website 8 常用网站
8079 Google:[tcp;;;google.com;;]
8001 百度:[tcp;;;www.baidu.com;;,tcp;;;m.baidu.com;;]
8112 Apple:[tcp;;;www.apple.com;;,tcp;;;m.apple.com;;]
8002 新浪:[tcp;;;www.sina.com;;,tcp;;;m.sina.com;;]
8003 搜狐:[tcp;;;www.sohu.com;;,tcp;;;m.sohu.com;;]
8004 网易:[tcp;;;www.163.com;;,tcp;;443;www.126.com;;]
8005 凤凰网:[tcp;;;ifeng.com;;]
8009 hao123:[tcp;;;www.hao123.com;;,tcp;;;m.hao123.com;;]
8010 2345:[tcp;;;www.2345.com;;,tcp;;;m.2345.com;;]
8006 人民网:[tcp;;;people.com.cn;;]
8008 中华网:[tcp;;;www.china.com;;]
8020 天涯社区:[tcp;;;tianya.cn;;]
8026 穷游网:[tcp;;;qyer.com;;]
8027 驴妈妈:[tcp;;;lvmama.com;;]
8029 太平洋汽车:[tcp;;;pcauto.com.cn;;]
8030 易车网:[tcp;;;bitauto.com;;]
8031 爱卡汽车:[tcp;;;xcar.com.cn;;]
8035 和讯:[tcp;;;hexun.com;;]
8036 第一财经:[tcp;;;yicai.com;;]
8037 全景网:[tcp;;;p5w.net;;]
8038 中彩网:[tcp;;;zhcw.com;;]
8039 体育彩票:[tcp;;;lottery.gov.cn;;]
8041 豆丁:[tcp;;;docin.com;;]
8044 缤客:[tcp;;;booking.com;;]
8046 猫扑:[tcp;;;mop.com;;]
8064 潇湘书院:[tcp;;;xxsy.net;;]
8065 cctv5:[tcp;;;sports.cctv.com;;]
8066 虎扑体育:[tcp;;;hupu.com;;]
8077 知网:[tcp;;;www.cnki.net;;]
8087 github:[tcp;;;github.com;;]
8089 gitee:[tcp;;;gitee.com;;]
8090 必应:[tcp;;;bing.com;;]
8092 中国福利彩:[tcp;;;www.cwl.gov.cn;;]
8093 新浪彩票:[tcp;;;lottery.sina.com.cn;;]
8094 竞彩网:[tcp;;;www.sporttery.cn;;]
8096 新浪体育:[tcp;;;sports.sina.com.cn;;]
8098 小米官网:[tcp;;;www.mi.com;;]
8099 BBC:[tcp;;;www.bbc.com;;]
8100 腾讯智影:[tcp;;;zenvideo.qq.com;;]
8103 IT之家:[tcp;;;www.ithome.com;;]
8104 太平洋电脑:[tcp;;;www.pconline.com.cn;;]
8105 中国移动:[tcp;;;www.10086.cn;;]
8106 中国联通:[tcp;;;www.10010.com;;]
8107 中国电信:[tcp;;;www.189.cn;;]
8108 华为商城:[tcp;;;www.vmall.com;;]
8110 vivo官网:[tcp;;;www.vivo.com.cn;;]
8111 华为官网:[tcp;;;www.huawei.com;;]
#class life 10 生活
10003 京东钱包:[tcp;;;jdpay.com;;]
10034 饿了么:[tcp;;;eleme.com;;]
10035 美团:[tcp;;;meituan;;]
10004 豆瓣:[tcp;;;douban.com;;]
10005 知乎:[tcp;;;zhihu.com;;]
10006 链家:[tcp;;;lianjia.com;;]
10007 天眼查:[tcp;;;tianyancha.com;;]
10008 有道词典:[tcp;;;dict.youdao.com;;]
10010 萤石云:[tcp;;;ys7.com;;,udp;;;;;00:e2|01:62|02:0c,tcp;;11001;;;00:24|01:0a]
10011 掌阅:[tcp;;;ireader.com;;,tcp;;;zhangyue;;]
10012 安居客:[tcp;;;anjuke.com;;]
10013 房天下:[tcp;;;fang.com;;]
10014 58同城:[tcp;;;58.com;;]
10015 动漫之家:[tcp;;;dmzj.com;;]
10016 汽车之家:[tcp;;;autohome.com.cn;;]
10017 飞猪:[tcp;;;fliggy.com;;]
10018 12306:[tcp;;;12306.cn;;]
10019 马蜂窝:[tcp;;;mafengwo.cn;;]
10020 途牛:[tcp;;;tuniu.com;;]
10021 小爱音箱:[tcp;;;ai.xiaomi.com;;,tcp;;;mina.mi.com;;]
10022 搜狗拼音:[tcp;;;pinyin.sogou.com;;]:5:1
#class finance 14 金融
14001 建设银行:[tcp;;;ccb.com;;]
14002 农业银行:[tcp;;;abchina.com;;]
14003 中国银行:[tcp;;;boc.cn;;]
14004 交通银行:[tcp;;;bankcomm.com;;]
14005 招商银行:[tcp;;;cmbchina.com;;]
14006 邮政储蓄:[tcp;;;psbc.com;;]
14007 兴业银行:[tcp;;;cib.com.cn;;]
14008 浦发银行:[tcp;;;spdb.com.cn;;]
14009 中信银行:[tcp;;;citicbank.com;;]
14010 上海银行:[tcp;;;bosc.cn;;]
14011 平安银行:[tcp;;;pingan.com.cn;;]
14012 人民银行:[tcp;;;pbc.gov.cn;;]
14013 北京银行:[tcp;;;bankofbeijing;;]
14014 银联在线:[tcp;;;95516.com;;]
#class tools 11 工具
11001 samba共享:[tcp;;445;;;]
11002 ftp文件传输:[tcp;;21;;;]
11003 SSH:[tcp;;;;;00:53|01:53|02:48]
-234
View File
@@ -1,234 +0,0 @@
#version v22.3.24
#format v2.0
#id name:[proto;sport;dport;host url;request;dict]
#class chat 1 聊天
1003 微博:[tcp;;443;weibo;;]
1004 陌陌:[tcp;;;momo;;,tcp;;;;;04:2f|05:66|06:65|07:65,tcp;;;;;00:03|01:03|02:00]
1005 支付宝:[tcp;;443;alipay.com;;]
1006 钉钉:[tcp;;;dingtalk;;,tcp;;;;d?host=;,tcp;;;;/man/api;,tcp;;;;/beacon;]
1007 Soul:[tcp;;;soulapp;;]
1008 伊对:[tcp;;;520yidui;;]
1009 探探:[tcp;;;tancdn;;,tcp;;;tantanapp;;]
1010 多闪:[tcp;;;ppkankan;;]
#class game 2 游戏
2001 王者荣耀:[tcp;;;;;00:33|1:66|02:00|03:0b]
2002 和平精英:[tcp;;17500;;;00:33|1:66|03:0a|05:0a]
2003 英雄联盟手游:[tcp;;;;;00:33|01:66|02:00|03:0b,tcp;;443;;;00:01|01:00|10:86|11:47]
2015 我的世界:[tcp;;443;g79mclobt.nie.netease;;]
2005 欢乐斗地主:[tcp;;8000;;;00:74|01:67|02:77|03:5f]
2006 梦幻西游:[tcp;;;;;00:0e|01:00|02:fe|03:ff]
2007 明日之后:[udp;;;;;00:05|01:09|02:00,tcp;;;;;00:02|01:00|02:00|03:00|04:00|05:00]
2008 QQ飞车:[udp;;;;;00:28|01:28,tcp;;10000;;;00:33|01:66|02:00|03:08]
2009 跑跑卡丁车:[tcp;;8888;;;00:33|01:66|02:00|03:08]
2010 开心消消乐:[tcp;;80;happyelements;;]
2011 狂野飙车:[tcp;;;asphalt9;;]
2012 率土之滨:[tcp;;10001;;;00:00|01:00,tcp;;8001;;;00:00|01:00]
2013 一刀传世:[tcp;;8040;;;00:47|01:45]
2014 第五人格:[tcp;;4010;;;,tcp;;4010;;;,tcp;;4020;;;,tcp;;4030;;;,tcp;;4040;;;,tcp;;4050;;;,tcp;;4060;;;,tcp;;4070;;;,tcp;;4080;;;,tcp;;4090;;;]
2016 皇室战争:[udp;;9339;;;]
2017 炉石传说:[tcp;;3724;;;00:73:01:00:02:00]
2023 原神:[tcp;;443;yuanshen.com;;]
2025 天涯明月刀:[tcp;;10000;;;00:43|01:66|02:aa]
2026 微信小游戏:[tcp;;443;mmgame;;,tcp;;443;game.weixin.qq;;]
2033 我叫MT4:[tcp;;21248;;;,tcp;;;dir.mt4.qq.com;;]
2034 神都夜行录:[udp;;;;;00:00|01:00|02:00|03:00|04:56|05:40]
2041 光遇:[udp;;10000-15000;;;00:8f|01:ff,tcp;;;ma75.update.netease.com;;,tcp;;;ma75.proxima.nie.netease;;]
2042 保卫萝卜4:[tcp;;;s4.luobo.cn;;]
2040 哈利波特:[tcp;;10021-12000;;;00:02|01:00|02:00|03:00|04:00|05:00,tcp;;443;g92.proxima;;]
2067 9377游戏:[tcp;;;www.9377.com;;]
2068 4399游戏:[tcp;;;4399.com;;]
2069 7k7k游戏:[tcp;;;7k7k.com;;]
2070 17173游戏:[tcp;;;17173.com;;]
2071 37网游:[tcp;;;37.com;;]
2072 游民星空:[tcp;;;gamersky.com;;]
2073 游侠网:[tcp;;;ali213.net;;]
2074 hao123游戏:[tcp;;;game.hao123.com;;]
2075 51游戏:[tcp;;;www.51.com;;]
2050 uu加速器:[tcp;;;mg.uu.163.com;;]
2051 腾讯加速器:[tcp;;;m.acc.qq.com;;]
2080 乐逗游戏:[tcp;;;.uu.cc;;]
#class video 3 视频
3001 抖音短视频:[tcp;;;-dy-;;,tcp;;;-dy.;;,tcp;;;douyin;;]
3002 火山小视频:[tcp;;;.huoshan.com;;,tcp;;;hs.pstatp.com;;,tcp;;;hs.ixigua.com;;]
3003 腾讯视频:[tcp;;443;v.qq.com;;,tcp;;443;video.qq.com;;,tcp;;443;btrace.qq.com;;]
3004 爱奇艺:[tcp;;;iqiyi;;,tcp;;;qy.net;;]
3005 微视:[tcp;;80;;;00:34|01:16|02:75,tcp;;80;weishi.qq.com;;]
3006 斗鱼直播:[tcp;;;douyu;;,tcp;;;douyu;;-2:2f|-1:00]
3008 虎牙直播:[tcp;;;huya;;,udp;;;;;01:00|02:00|03:00|04:23,udp;;;;;01:00|02:00|03:00|04:24]
3009 快手:[tcp;;;kuaishou;;,tcp;;;ksyuncdn.com;;,tcp;;;.gifshow.com;;,tcp;;;yximgs.com;;,tcp;;80;;/ksc;,tcp;;;kwaicdn;;,tcp;;;kwimgs;;]
3010 小红书:[tcp;;;xiaohongshu;;,tcp;;;xhscdn;;]
3011 花椒直播:[tcp;;;huajiao;;]
3012 映客直播:[tcp;;;;.inke.cn;]
3013 YY:[udp;;;;;02:00|03:00|04:08,udp;;;;;00:4f|01:00|02:00]
3014 哔哩哔哩:[tcp;;;bilivideo;;,tcp;;;bilibili.com;;,tcp;;;;;00:47|05:75|06:70|07:67,tcp;;;;/bfs/emote/;,,tcp;;;hdslb.com;;]
3016 芒果tv:[tcp;;443;mgtv;;,tcp;;80;mgtv;;,tcp;;443;hitv;;]
3017 西瓜视频:[tcp;;;ixigua;;,tcp;;443;snsdk;;,tcp;;;xg-p.ixigua;;,tcp;;;bdxigua;;]
3018 搜狐视频:[tcp;;;aty.sohu.com;;,tcp;;;tv.itc.cn;;]
3019 播聊:[tcp;;80;randlove.cn;;,tcp;;;yueliao;;,tcp;;;5glive;;]
3020 咪咕视频:[tcp;;;miguvideo;;,tcp;;;migu.cn;;]
3021 韩剧TV:[tcp;;;hanju.koudaibaobao;;]
3022 人人视频:[tcp;;;rr.tv;;]
3023 央视影音:[tcp;;;cntv;;]
3024 土豆视频:[tcp;;;youku;;,tcp;;;ykimg;;]
3025 最右:[tcp;;;izuiyou;;]
3026 风行视频:[tcp;;;funshion;;]
3027 企鹅电竞:[tcp;;;egame.qq;;,tcp;;;liveplay;;,tcp;;;;pggame;]
3028 波波视频:[tcp;;;miaopai;;]
3029 酷狗短酷:[tcp;;;bssdl.kugou;;]
3030 酷狗直播:[tcp;;;rt-m.kugou;;,tcp;;;kgimg.com;;]
#class shopping 4 购物
4001 淘宝:[tcp;;;taobao;;,tcp;;;alicdn.com;;,tcp;;;tmall.com;;,tcp;;;;;00:d3|01:00,,tcp;;;;;00:d4|01:00,,tcp;;;;;00:d3|01:00]
4002 京东:[tcp;;;360buyimg;;,tcp;;;jd.com;;,tcp;;;jdcdn.com;;,tcp;;;;;00:d5|01:00]
4003 唯品会:[tcp;;;vips-mobile;;,tcp;;;vipshop;;,tcp;;;vip.com;;,tcp;;;vipstatic.com;;,tcp;;;appsimg.com;;]
4004 拼多多:[tcp;;;pinduoduo;;,tcp;;;yangkeduo.com;;,tcp;;;s1p.cdntip.com;;]
4010 饿了么:[tcp;;;eleme;;]
4011 美团:[tcp;;;meituan;;]
4012 闲鱼:[tcp;;;xianyu;;]
4021 转转:[tcp;;;zhuanzhuan;;,tcp;;;zhuanstatic;;]
4005 蘑菇街:[tcp;;;mogujie;;,tcp;;;mogucdn;;,tcp;;;;;00:73|01:ea|02:68|03:fb|04:3f]
4006 苏宁易购:[tcp;;;.suning.;;]
4007 当当网:[tcp;;;.dangdang.com;;]
4008 1号店:[tcp;;;.yhd.com;;]
4009 朴朴超市:[tcp;;;pupumall;;,tcp;;;pupuapi;;]
4013 叮咚买菜:[tcp;;;ddxq.mobi;;]
4014 小米有品:[tcp;;;youpin;;,tcp;;;shopapi.io.mi.com;;]
4015 微店:[tcp;;;weidian;;]
4016 折800:[tcp;;;zhe800.com;;]
4017 HM:[tcp;;;www.hm.com;;,tcp;;;measurement.com;;]
4018 好省:[tcp;;;hzhstb.com;;]
4019 什么值得买:[tcp;;;smzdm.com;;]
4020 大众点评:[tcp;;;dianping.com;;]
4022 网易严选:[tcp;;;yanxuan;;]
4023 识货:[tcp;;;shihuo;;]
4024 考拉海购:[tcp;;;kaola;;]
4025 宜家家居:[tcp;;;ikea.cn;;]
4026 小象优品:[tcp;;;xiaoxiangyoupin;;]
#class music 5 音乐
5001 网易云音乐:[tcp;;;music.163;;,tcp;;;music.126;;]
5002 QQ音乐:[tcp;;;;^/amobile.music.tc.qq.com;,tcp;;;qqmusic;;]
5003 酷狗音乐:[tcp;;;kugou;;,tcp;;;kgimg;;,tcp;;;fanxing;;]
5004 酷我音乐:[tcp;;;.kuwo.cn;;]
5005 喜马拉雅:[tcp;;;.ximalaya.com;;]
5006 千千音乐:[tcp;;;music.taihe.com;;]
5007 虾米音乐:[tcp;;;xiami;;]
5008 音悦台:[tcp;;;yinyuetai.com;;]
5009 豆瓣FM:[tcp;;;douban.fm;;]
5010 唱吧:[tcp;;;changba.com;;]
5011 音乐随心听:[tcp;;;fm.taihe.com;;]
5012 懒人听书:[tcp;;;lrts.me;;]
#class employee 6 招聘
6001 前程无忧:[tcp;;;51job;;]
6002 智联招聘:[tcp;;;zhaopin;;]
6003 猎聘:[tcp;;;liepin;;]
6004 赶集网:[tcp;;;58.com;;,tcp;;;58cdn;;]
6005 同城急聘:[tcp;;;xiaomei;;]
6006 领英:[tcp;;;linkedin;;]
6007 斗米:[tcp;;;doumi;;]
6008 看准:[tcp;;;kanzhun.com;;]
6009 应届生求职:[tcp;;;yingjiesheng.com;;]
6010 中华英才网:[tcp;;;chinahr.com;;]
6011 拉勾网:[tcp;;;lagou.com;;]
6012 大街网:[tcp;;;dajie.com;;]
6013 boss直聘:[tcp;;;zhipin.com;;]
6014 实习僧:[tcp;;;shixiseng.com;;]
#class download 7 下载
7001 迅雷:[udp;12345;;;;,udp;15000;;;;,tcp;;54321;;;,tcp;;12345;;;,udp;6881;;;;,udp;;12346;;;,udp;12346;;;;]
7002 AppStore:[tcp;;;itunes.apple.com;;] HIDE:0
7003 samba共享:[tcp;;445;;;] HIDE:0
7004 ftp文件传输:[tcp;;21;;;] HIDE:0
7005 vivo应用商店:[tcp;;443;appstore.vivo;;,tcp;;443;apkappdefwsdl.vivo;;] HIDE:0
7006 王者荣耀更新:[tcp;;80;;/sgame/;]
7007 天翼云盘:[tcp;;;ctyunapi;;]
7008 腾讯微云:[tcp;;;weiyun.com;;,tcp;;;aegis.qq.com;;,tcp;;;pingtas.qq.com;;,tcp;;443;;;00:77|01:6e|02:73]
7009 坚果云:[tcp;;;jianguoyun;;]
7010 蓝奏云:[tcp;;;pan.lanzou.com;;]
7011 华为云:[tcp;;;cloud.huawei.com;;,tcp;;;hicloud.com;;,tcp;;;myhuaweicloud.cn;;]
7020 windows更新:[tcp;;80;update.microsoft.com;;,tcp;;;windowsupdate.com;;]
7030 向日葵:[tcp;;;oray.com;;,tcp;;;oray.net;;]
7031 TeamViewer:[tcp;;;teamviewer;;]
7032 阿里云盘:[tcp;;;aliyundrive;;]
7035 SSH:[tcp;;;;;00:53|01:53|02:48]
#class website 8 常用网站
8001 百度:[tcp;;;baidu.com;;]
8002 新浪:[tcp;;;sina.com;;]
8003 搜狐:[tcp;;;sohu.com;;]
8004 网易:[tcp;;;163.com;;,tcp;;443;126.com;;]
8005 凤凰网:[tcp;;;ifeng.com;;]
8006 人民网:[tcp;;;people.com.cn;;]
8007 凤凰网:[tcp;;;ifeng.com;;]
8008 中华网:[tcp;;;china.com;;]
8009 hao123:[tcp;;;hao123.com;;,]
8010 2345:[tcp;;;2345.com;;,]
8011 4399游戏:[tcp;;;4399.com;;]
8012 7k7k游戏:[tcp;;;7k7k.com;;]
8013 17173游戏:[tcp;;;17173.com;;]
8014 37网游:[tcp;;;37.com;;]
8015 游民星空:[tcp;;;gamersky.com;;]
8016 游侠网:[tcp;;;ali213.net;;]
8017 世纪佳缘:[tcp;;;jiayuan.com;;]
8018 珍爱网:[tcp;;;zhenai.com;;]
8019 百合网:[tcp;;;baihe.com;;]
8020 天涯社区:[tcp;;;tianya.cn;;]
8021 携程网:[tcp;;;ctrip.com;;]
8022 飞猪:[tcp;;;fliggy.com;;]
8023 12306:[tcp;;;12306.cn;;]
8024 马蜂窝:[tcp;;;mafengwo.cn;;]
8025 途牛:[tcp;;;tuniu.com;;]
8026 穷游网:[tcp;;;qyer.com;;]
8027 驴妈妈:[tcp;;;lvmama.com;;]
8028 同程旅游:[tcp;;;ly.com;;]
8029 太平洋汽车:[tcp;;;pcauto.com.cn;;]
8030 易车网:[tcp;;;bitauto.com;;]
8031 爱卡汽车:[tcp;;;xcar.com.cn;;]
8032 雪球:[tcp;;;xueqiu.com;;]
8033 东方财富:[tcp;;;eastmoney.com;;]
8034 证券之星:[tcp;;;stockstar.com;;]
8035 和讯:[tcp;;;hexun.com;;]
8036 第一财经:[tcp;;;yicai.com;;]
8037 全景网:[tcp;;;p5w.net;;]
8038 中彩网:[tcp;;;zhcw.com;;]
8039 中国体育彩票:[tcp;;;lottery.gov.cn;;]
8040 竞彩网:[tcp;;;sporttery.cn;;]
8041 豆丁:[tcp;;;docin.com;;]
8042 豆瓣:[tcp;;;douban.com;;]
8043 知乎:[tcp;;;zhihu.com;;]
8044 缤客:[tcp;;;booking.com;;]
8046 猫扑:[tcp;;;mop.com;;]
8047 赶集网:[tcp;;;ganji.com;;]
8048 安居客:[tcp;;;anjuke.com;;]
8049 房天下:[tcp;;;fang.com;;]
8050 链家:[tcp;;;lianjia.com;;]
8051 百姓网:[tcp;;;baixing.com;;]
8052 下厨房:[tcp;;;xiachufang.com;;]
8053 大众点评:[tcp;;;dianping.com;;]
8054 58同城:[tcp;;;58.com;;]
8055 天眼查:[tcp;;;tianyancha.com;;]
8056 千图网:[tcp;;;58pic.com;;]
8057 csdn社区:[tcp;;;csdn.net;;]
8058 有道词典:[tcp;;;dict.youdao.com;;]
8059 动漫之家:[tcp;;;dmzj.com;;]
8060 汽车之家:[tcp;;;autohome.com.cn;;]
8061 纵横中文网:[tcp;;;zongheng.com;;]
8062 起点中文网:[tcp;;;qidian.com;;]
8063 飞卢:[tcp;;;faloo.com;;]
8064 潇湘书院:[tcp;;;xxsy.net;;]
8065 cctv5:[tcp;;;sports.cctv.com;;]
8066 虎扑体育:[tcp;;;www.hupu.com;;]
8067 建设银行:[tcp;;;ccb.com;;]
8068 农业银行:[tcp;;;abchina.com;;]
8069 中国银行:[tcp;;;boc.cn;;]
8070 交通银行:[tcp;;;bankcomm.com;;]
8071 招商银行:[tcp;;;cmbchina.com;;]
8072 邮政储蓄:[tcp;;;psbc.com;;]
8073 兴业银行:[tcp;;;cib.com.cn;;]
8074 浦发银行:[tcp;;;spdb.com.cn;;]
8075 中信银行:[tcp;;;citicbank.com;;]
8076 上海银行:[tcp;;;bosc.cn;;]
-99
View File
@@ -1,99 +0,0 @@
#version v22.11.11
#format v2.0
#id name:[proto;sport;dport;host url;request;dict]
#class chat 1 Chat
1001 Facebook:[tcp;;;facebook.com;;]
1002 Whatsapp:[tcp;;;whatsapp;;]
1003 Twitter:[tcp;;;twitter.com;;]
1004 Instagram:[tcp;;;instagram.com;;]
1005 VK:[tcp;;;vk.com;;]
1006 Line:[tcp;;;line;;]
1007 Snapchat:[tcp;;;snapchat.com;;]
1008 Tinder:[tcp;;;tinder.com;;]
#class video 3 Video
3001 YouTube:[tcp;;;youtube;;]
3002 Tiktok:[tcp;;;tiktok;;]
3003 NetFlix:[tcp;;;netflix;;]
3004 Vimeo:[tcp;;;vimeo;;]
3005 DailyMotion:[tcp;;;dailymotion;;]
3006 Hulu:[tcp;;;hulu;;]
3007 Vube:[tcp;;;vube;;]
3008 Twitch:[tcp;;;twitch;;]
3009 LiveLeak:[tcp;;;itemfix;;]
3010 Spotify:[tcp;;;spotify.com;;]
3050 Xvideos:[tcp;;;xvideos.com;;]
3051 Pornhub:[tcp;;;pornhub.com;;]
3052 Xnxx:[tcp;;;xnxx.com;;]
#class shopping 4 Shopping
4001 Amazon:[tcp;;;amazon.com;;]
4002 eBay:[tcp;;;ebay.com;;]
4003 Etsy:[tcp;;;etsy.com;;]
4004 Wish:[tcp;;;wish.com;;]
4005 Alibaba:[tcp;;;alibaba;;]
4006 Aliexpress:[tcp;;;aliexpress.com;;]
4007 Walmart:[tcp;;;walmart.com;;]
4008 Sears:[tcp;;;sears.com;;]
4009 Kohls:[tcp;;;kohls.com;;]
4010 Costco:[tcp;;;costco.com;;]
4011 Asos:[tcp;;;asos.com;;]
4012 Cuyana:[tcp;;;cuyana.com;;]
#class download 7 Download
7001 GooglePlay:[tcp;;;play.google.com;;]
7002 AppStore:[tcp;;;iosapps.itunes.apple.com;;]
7003 WindowsUpdate:[tcp;;80;update.microsoft.com;;,tcp;;;windowsupdate.com;;]
7050 Speedtest:[tcp;;;speedtest.net;;]
7060 samba:[tcp;;445;;;]
7061 ftp:[tcp;;21;;;]
7062 ssh:[tcp;;22;;;]
#class website 8 Website
8001 Google:[tcp;;;www.google.com;;]
8002 Wiki:[tcp;;;wikipedia.com;;]
8003 Yahoo:[tcp;;;yahoo;;]
8004 Apple:[tcp;;;www.apple.com;;]
8010 Reddit:[tcp;;;reddit.com;;]
8011 Outlook:[tcp;;;outlook.live.com;;]
8012 Naver:[tcp;;;naver.com;;]
8013 Fandom:[tcp;;;fandom.com;;]
8015 Globo:[tcp;;;globo.com;;]
8016 Yelp:[tcp;;;yelp.com;;]
8017 Pinterest:[tcp;;;www.pinterest.com;;]
8018 BBC:[tcp;;;www.bbc.com;;]
8020 Linkedin:[tcp;;;linkedin.com;;]
8022 Merriam-webster:[tcp;;;merriam-webster.com;;]
8027 Dictionary:[tcp;;;dictionary.com;;]
8028 Tripadvisor:[tcp;;;tripadvisor.com;;]
8029 Britannica:[tcp;;;britannica.com;;]
8030 Cambridge:[tcp;;;cambridge.org;;]
8032 Weather:[tcp;;;weather.com;;]
8033 Wiktionary:[tcp;;;wiktionary.org;;]
8034 Espn:[tcp;;;espn.com;;]
8035 Microsoft:[tcp;;;microsoft.com;;]
8038 Gsmarena:[tcp;;;gsmarena.com;;]
8039 Webmd:[tcp;;;webmd.com;;]
8040 Craigslist:[tcp;;;craigslist.org;;]
8041 Cricbuzz:[tcp;;;cricbuzz.com;;]
8042 Mayoclinic:[tcp;;;mayoclinic.org;;]
8043 Timeanddate:[tcp;;;timeanddate.com;;]
8044 Espncricinfo:[tcp;;;espncricinfo.com;;]
8045 Healthline:[tcp;;;healthline.com;;]
8047 Rottentomatoes:[tcp;;;rottentomatoes.com;;]
8049 Thefreedictionary:[tcp;;;thefreedictionary.com;;]
8052 Bestbuy:[tcp;;;bestbuy.com;;]
8053 Indeed:[tcp;;;indeed.com;;]
8058 Samsung:[tcp;;;samsung.com;;]
8059 Investopedia:[tcp;;;investopedia.com;;]
8060 Flashscore:[tcp;;;flashscore.com;;]
8061 Steampowered:[tcp;;;steampowered.com;;]
8064 Roblox:[tcp;;;roblox.com;;]
8065 Nordstrom:[tcp;;;nordstrom.com;;]
8066 Thepiratebay:[tcp;;;thepiratebay.org;;]
8067 Indiatimes:[tcp;;;indiatimes.com;;]
8068 Cnbc:[tcp;;;cnbc.com;;]
8069 Ssyoutube:[tcp;;;ssyoutube.com;;]
8070 Adobe:[tcp;;;adobe.com;;]
8071 Speedtest:[tcp;;;speedtest.net;;]
8072 Lowes:[tcp;;;lowes.com;;]
+30
View File
@@ -0,0 +1,30 @@
config global global
option lan_ifname 'br-lan'
option tcp_rst '1'
option theme_mode '1'
option feature_token ''
config appfilter appfilter
option enable 1
config macfilter macfilter
option enable 1
config record 'record'
option enable '1'
option record_time '3'
option app_valid_time '3'
option history_data_size '10'
option history_data_path '/tmp/oaf'
option base_data_path '/tmp/oaf'
config network network
option work_mode 0
config dashboard 'dashboard'
config advanced 'advanced'
option disable_hnat '0'
config status 'status'
option notice_status '0'
+1
View File
@@ -0,0 +1 @@
config whitelist 'whitelist'
-10
View File
@@ -1,10 +0,0 @@
#!/bin/sh
CLASS_NAME_FILE="/tmp/app_class.txt"
f_file=$1
test -z "$f_file" && return
test -f $CLASS_NAME_FILE &&{
rm $CLASS_NAME_FILE
}
cat $f_file |grep "#class" | awk '{print $3 " " $2 " " $4}' >$CLASS_NAME_FILE
Executable → Regular
+4 -2
View File
@@ -1,3 +1,5 @@
#!/bin/sh
. /usr/share/libubox/jshn.sh
. /lib/functions.sh
@@ -7,7 +9,7 @@ if [ "$1" = "1" ] ; then
IS_BOOT=1
fi
DISABLE_HNAT=$(uci -q get appfilter.global.disable_hnat)
DISABLE_HNAT=$(uci -q get fwx.advanced.disable_hnat)
if [ "$DISABLE_HNAT" != "1" ]; then
exit 0
fi
@@ -41,4 +43,4 @@ if [ $IS_BOOT -ne 1 ] ; then
/etc/init.d/firewall reload
fi
fi
+12
View File
@@ -0,0 +1,12 @@
{
"oaf": {
"description": "Grant access to OAF configuration",
"read": {
"uci": [ "fwx"],
"ubus": {
"fwx": ["common"]
}
}
}
}
@@ -0,0 +1,2 @@
config settings 'base'
+3
View File
@@ -0,0 +1,3 @@
config global 'global'
option enable '1'
Whitespace-only changes.
-117
View File
@@ -1,117 +0,0 @@
. /usr/share/libubox/jshn.sh
. /lib/functions.sh
config_apply()
{
test -z "$1" && return 1
if [ -e "/dev/appfilter" ];then
echo "$1" >/dev/appfilter
fi
}
clean_rule()
{
json_init
json_add_int "op" 3
json_add_object "data"
json_str=`json_dump`
config_apply "$json_str"
json_cleanup
}
load_rule()
{
json_init
json_add_int "op" 1
json_add_object "data"
json_add_array "apps"
config_get appid_list rule app_list
if ! test -z "$appid_list";then
for appid in $appid_list:
do
json_add_int "" $appid
done
fi
json_str=`json_dump`
config_apply "$json_str"
json_cleanup
}
load_whitelist_mac()
{
json_init
config_load appfilter
json_add_int "op" 5
json_add_object "data"
json_add_array "mac_list"
config_foreach add_mac_to_array_callback whitelist
json_str=`json_dump`
config_apply "$json_str"
json_cleanup
}
add_mac_to_array_callback() {
local section="$1"
local mac
config_get mac "$section" "mac"
if [ -n "$mac" ]; then
json_add_string "" "$mac"
fi
}
load_mac_list()
{
json_init
config_load appfilter
json_add_int "op" 4
json_add_object "data"
json_add_array "mac_list"
local user_mode=`uci get appfilter.global.user_mode`
if [ x"1" == x"$user_mode" ];then
config_foreach add_mac_to_array_callback af_user
fi
json_str=`json_dump`
config_apply "$json_str"
json_cleanup
}
reload_rule(){
config_load appfilter
clean_rule
load_rule
load_mac_list
load_whitelist_mac
}
reload_base_config(){
! test -d /proc/sys/oaf && return
config_load appfilter
config_get work_mode "global" "work_mode"
config_get lan_ifname "global" "lan_ifname"
config_get user_mode "global" "user_mode"
config_get app_filter_mode "global" "app_filter_mode"
echo "$work_mode" >/proc/sys/oaf/work_mode
echo "$user_mode" >/proc/sys/oaf/user_mode
echo "${app_filter_mode:-0}" >/proc/sys/oaf/app_filter_mode
if [ x"" != x"$lan_ifname" ];then
echo "$lan_ifname" >/proc/sys/oaf/lan_ifname
fi
}
case $1 in
"reload")
reload_base_config
reload_rule
;;
esac
+1
View File
@@ -0,0 +1 @@
7.0.1
File diff suppressed because it is too large. Load diff
+4 -4
View File
@@ -1,6 +1,6 @@
OBJS:=appfilter_user.o appfilter_netlink.o appfilter_ubus.o appfilter_config.o utils.o main.o
EXEC:=oafd
all: $(OBJS)
$(CC) -o $(EXEC) $(OBJS) $(LIBS)
OBJS:=fwx_user.o fwx_netlink.o fwx_ubus.o fwx_stat.o fwx_config.o fwx_feature.o fwx_feature_online.o fwx_custom_feature.o fwx_utils.o main.o fwx_app_filter.o fwx_mac_filter.o fwx_record.o fwx_common.o fwx_system.o fwx_network.o fwx_firewall.o fwx_wireless.o check_main.o fwx_uci.o
EXEC:=oafd
all: $(OBJS)
$(CC) -o $(EXEC) $(OBJS) $(LIBS)
clean:
rm $(EXEC) *.o
-124
View File
@@ -1,124 +0,0 @@
#ifndef __APPFILTER_H__
#define __APPFILTER_H__
#define MIN_INET_ADDR_LEN 7
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include "utils.h"
#define LOG_FILE_PATH "/tmp/log/appfilter.log"
#define OAF_VERSION "6.1.8"
typedef enum {
LOG_LEVEL_ERROR,
LOG_LEVEL_WARN,
LOG_LEVEL_INFO,
LOG_LEVEL_DEBUG
} LogLevel;
extern int current_log_level;
static void af_log(LogLevel level, const char *format, ...){
if (level > current_log_level)
return;
FILE *log_file = fopen(LOG_FILE_PATH, "a");
if (!log_file) {
perror("Failed to open log file");
return;
}
time_t now = time(NULL);
struct tm *t = localtime(&now);
char time_str[20];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", t);
const char *level_str;
switch (level) {
case LOG_LEVEL_DEBUG: level_str = "DEBUG"; break;
case LOG_LEVEL_INFO: level_str = "INFO"; break;
case LOG_LEVEL_WARN: level_str = "WARN"; break;
case LOG_LEVEL_ERROR: level_str = "ERROR"; break;
default: level_str = "UNKNOWN"; break;
}
fprintf(log_file, "[%s] [%s] ", time_str, level_str);
va_list args;
va_start(args, format);
vfprintf(log_file, format, args);
va_end(args);
fclose(log_file);
}
#define LOG_DEBUG(format, ...) af_log(LOG_LEVEL_DEBUG, format, ##__VA_ARGS__)
#define LOG_INFO(format, ...) af_log(LOG_LEVEL_INFO, format, ##__VA_ARGS__)
#define LOG_WARN(format, ...) af_log(LOG_LEVEL_WARN, format, ##__VA_ARGS__)
#define LOG_ERROR(format, ...) af_log(LOG_LEVEL_ERROR, format, ##__VA_ARGS__)
#define MAX_TIME_LIST_LEN 1024
#define MAX_TIME_LIST 64
typedef struct af_time
{
int hour;
int min;
} af_time_t;
typedef struct af_global_config_t{
int enable;
int user_mode;
int work_mode;
int record_enable;
int disable_hnat;
int auto_load_engine;
int tcp_rst;
int disable_quic;
int app_filter_mode; // 0 = specified apps, 1 = all apps
char lan_ifname[16];
}af_global_config_t;
typedef struct time_config{
af_time_t start_time;
af_time_t end_time;
int days[7];
}time_config_t;
typedef struct daily_limit_config {
int enable;
int am_time;
int pm_time;
} daily_limit_config_t;
typedef struct af_time_config_t{
int time_mode;
time_config_t seg_time;
int deny_time;
int allow_time;
int days[7];
int time_num;
time_config_t time_list[MAX_TIME_LIST];
daily_limit_config_t daily_limit[7];
}af_time_config_t;
typedef struct af_config_t{
af_global_config_t global;
af_time_config_t time;
}af_config_t;
typedef struct af_run_time_status{
int deny_time;
int allow_time;
int filter;
int match_time;
int remain_time;
int used_time;
int period_blocked;
}af_run_time_status_t;
extern af_config_t g_af_config;
#endif
-68
View File
@@ -1,68 +0,0 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __APPFILTER_CONFIG_H__
#define __APPFILTER_CONFIG_H__
#include <uci.h>
#define MAX_SUPPORT_APP_NUM 1024
#define MAX_CLASS_NAME_LEN 32
#define MAX_PARAM_LIST_LEN 1024
#include "appfilter_user.h"
extern int g_cur_class_num;
extern int g_app_count;
extern char CLASS_NAME_TABLE[MAX_APP_TYPE][MAX_CLASS_NAME_LEN];
typedef struct app_name_info
{
int id;
char name[64];
} app_name_info_t;
void init_app_name_table(void);
void init_app_class_name_table(void);
char *get_app_name_by_id(int id);
int appfilter_config_alloc(void);
int appfilter_config_free(void);
int config_get_appfilter_enable(void);
int config_get_lan_ip(char *lan_ip, int len);
int config_get_lan_mask(char *lan_mask, int len);
int af_uci_delete(struct uci_context *ctx, char *key);
int af_uci_add_list(struct uci_context *ctx, char *key, char *value);
int af_uci_add_int_list(struct uci_context *ctx, char *key, int value);
int af_uci_del_list(struct uci_context *ctx, char *key, char *value);
int af_uci_get_list_value(struct uci_context *ctx, char *key, char *output, int out_len, char *delimt);
int af_uci_set_value(struct uci_context *ctx, char *key, char *value);
int af_uci_set_int_value(struct uci_context *ctx, char *key, int value);
int af_uci_del_array_value(struct uci_context *ctx, char *key_fmt, int index);
int af_uci_set_array_value(struct uci_context *ctx, char *key_fmt, int index, char *value);
int af_get_uci_list_num(struct uci_context * ctx, char *package, char *section);
int af_uci_get_array_value(struct uci_context *ctx, char *key_fmt, int index, char *output, int out_len);
int af_uci_get_int_value(struct uci_context *ctx, char *key);
int af_uci_get_value(struct uci_context *ctx, char *key, char *output, int out_len);
int af_uci_add_section(struct uci_context * ctx, char *package_name, char *section);
int af_uci_commit(struct uci_context *ctx, const char * package);
char *get_app_name_by_id(int id);
#endif
-279
View File
@@ -1,279 +0,0 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json-c/json.h>
#include "appfilter_user.h"
#include "appfilter_netlink.h"
#include "appfilter.h"
#include "appfilter_config.h"
#define MAX_NL_RCV_BUF_SIZE 4096
#define REPORT_INTERVAL_SECS 60
extern int hash_appid(int appid);
extern unsigned int g_feature_update_time;
void appfilter_nl_handler(struct uloop_fd *u, unsigned int ev)
{
int ret;
int i;
char buf[MAX_NL_RCV_BUF_SIZE];
struct sockaddr_nl nladdr;
struct iovec iov = {buf, sizeof(buf)};
struct nlmsghdr *h;
int type;
int id;
char *mac = NULL;
u_int32_t cur_time = get_timestamp();
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
do
{
ret = recvmsg(u->fd, &msg, 0);
} while ((-1 == ret) && (EINTR == errno));
if (ret < 0)
{
printf("recv msg error\n");
return;
}
else if (0 == ret)
{
return;
}
h = (struct nlmsghdr *)buf;
char *kmsg = (char *)NLMSG_DATA(h);
struct af_msg_hdr *af_hdr = (struct af_msg_hdr *)kmsg;
if (af_hdr->magic != 0xa0b0c0d0)
{
printf("magic error %x\n", af_hdr->magic);
return;
}
if (af_hdr->len <= 0 || af_hdr->len >= MAX_OAF_NETLINK_MSG_LEN)
{
printf("data len error\n");
return;
}
char *kdata = kmsg + sizeof(struct af_msg_hdr);
struct json_object *root = json_tokener_parse(kdata);
if (!root)
{
printf("parse json failed:%s", kdata);
return;
}
LOG_DEBUG("report %s\n", kdata);
struct json_object *mac_obj = json_object_object_get(root, "mac");
if (!mac_obj)
{
printf("parse mac obj failed\n");
json_object_put(root);
return;
}
mac = json_object_get_string(mac_obj);
dev_node_t *node = find_dev_node(mac);
if (!node)
{
node = add_dev_node(mac);
if (!node)
{
goto EXIT;
}
}
struct json_object *ip_obj = json_object_object_get(root, "ip");
if (ip_obj)
strncpy(node->ip, json_object_get_string(ip_obj), sizeof(node->ip));
struct json_object *active_obj = json_object_object_get(root, "active");
if (active_obj) {
node->active = json_object_get_int(active_obj);
if (node->active)
{
// 根据当前时间判断是上午还是下午,分别累加
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
int current_hour = tm_info->tm_hour;
if (current_hour < 12) {
// 上午:0:00-11:59
node->today_am_active_time += 1; //min
} else {
// 下午:12:00-23:59
node->today_pm_active_time += 1; //min
}
}
}
struct json_object *up_flow_obj = json_object_object_get(root, "up_flow");
struct json_object *down_flow_obj = json_object_object_get(root, "down_flow");
unsigned long long total_up_bytes = 0;
unsigned long long total_down_bytes = 0;
if (up_flow_obj) {
node->today_up_bytes += (unsigned long long)json_object_get_int64(up_flow_obj) * 1024;
}
if (down_flow_obj) {
node->today_down_bytes += (unsigned long long)json_object_get_int64(down_flow_obj) * 1024;
}
struct json_object *visit_array = json_object_object_get(root, "visit_info");
if (!visit_array)
{
goto EXIT;
}
for (i = 0; i < json_object_array_length(visit_array); i++)
{
struct json_object *visit_obj = json_object_array_get_idx(visit_array, i);
struct json_object *appid_obj = json_object_object_get(visit_obj, "appid");
struct json_object *action_obj = json_object_object_get(visit_obj, "latest_action");
// old appid may be not in the feature list
if (cur_time - g_feature_update_time < 300){
if (strlen(get_app_name_by_id(json_object_get_int(appid_obj))) == 0){
LOG_INFO("ignore appid %d because it is not in the feature list\n", json_object_get_int(appid_obj));
continue;
}
}
int appid = json_object_get_int(appid_obj);
int action = json_object_get_int(action_obj);
type = appid / 1000;
id = appid % 1000;
if (id <= 0 || type <= 0)
continue;
node->stat[type - 1][id - 1].total_time += REPORT_INTERVAL_SECS;
int hash = hash_appid(appid);
visit_info_t *head = node->visit_htable[hash];
visit_info_t *p = head;
while(p){
if((p->appid == appid) && (cur_time - p->latest_time < 300)){
LOG_DEBUG("match appid = %d\n", appid, cur_time - p->latest_time);
break;
}
p = p->next;
}
if (!p){
p = (visit_info_t *)calloc(1, sizeof(visit_info_t));
p->appid = appid;
p->next = NULL;
p->first_time = cur_time;
add_visit_info_node(&node->visit_htable[hash], p);
}
p->action = action;
p->latest_time = cur_time;
}
EXIT:
json_object_put(root);
}
#define MAX_NL_MSG_LEN 1024
int send_msg_to_kernel(int fd, void *msg, int len)
{
struct sockaddr_nl saddr, daddr;
memset(&daddr, 0, sizeof(daddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0; // to kernel
daddr.nl_groups = 0;
int ret = 0;
struct nlmsghdr *nlh = NULL;
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_NL_MSG_LEN));
nlh->nlmsg_len = NLMSG_SPACE(MAX_NL_MSG_LEN);
nlh->nlmsg_flags = 0;
nlh->nlmsg_type = 0;
nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = DEFAULT_USR_NL_PID;
char msg_buf[MAX_NL_MSG_LEN] = {0};
struct af_msg_hdr *hdr = (struct af_msg_hdr *)msg_buf;
hdr->magic = 0xa0b0c0d0;
hdr->len = len;
char *p_data = msg_buf + sizeof(struct af_msg_hdr);
memcpy(p_data, msg, len);
memcpy(NLMSG_DATA(nlh), msg_buf, len + sizeof(struct af_msg_hdr));
ret = sendto(fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
free(nlh);
if (!ret)
{
perror("sendto error\n");
return -1;
}
return 0;
}
int appfilter_nl_init(void)
{
int fd;
struct sockaddr_nl nls;
fd = socket(AF_NETLINK, SOCK_RAW, OAF_NETLINK_ID);
if (fd < 0)
{
LOG_DEBUG("Connect netlink %d failed %s\n", OAF_NETLINK_ID, strerror(errno));
return -1;
}
memset(&nls, 0, sizeof(struct sockaddr_nl));
nls.nl_pid = DEFAULT_USR_NL_PID;
nls.nl_groups = 0;
nls.nl_family = AF_NETLINK;
if (bind(fd, (void *)&nls, sizeof(struct sockaddr_nl)))
{
LOG_DEBUG("Bind failed %s\n", strerror(errno));
return -1;
}
return fd;
}
-57
View File
@@ -1,57 +0,0 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __APPFILTER_NETLINK_H__
#define __APPFILTER_NETLINK_H__
#define DEFAULT_USR_NL_PID 999
#define OAF_NETLINK_ID 29
#define MAX_OAF_NETLINK_MSG_LEN 1024
#define MAX_AF_MSG_DATA_LEN 800
#define MAX_FEATURE_LINE_LEN 600
struct af_msg_hdr
{
int magic;
int len;
};
enum E_MSG_TYPE
{
AF_MSG_INIT,
AF_MSG_ADD_FEATURE,
AF_MSG_CLEAN_FEATURE,
AF_MSG_MAX
};
typedef struct af_msg
{
int action;
} af_msg_t;
typedef struct af_feature_msg{
af_msg_t hdr;
char feature[MAX_FEATURE_LINE_LEN];
} af_feature_msg_t;
int appfilter_nl_init(void);
void appfilter_nl_handler(struct uloop_fd *u, unsigned int ev);
int send_msg_to_kernel(int fd, void *msg, int len);
#endif
File diff suppressed because it is too large. Load diff
-25
View File
@@ -1,25 +0,0 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __APPFILTER_UBUS_H__
#define __APPFILTER_UBUS_H__
int appfilter_ubus_init(void);
#endif
-1000
View File
@@ -1,1000 +0,0 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <json-c/json.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include "appfilter_config.h"
#include "appfilter.h"
#include "appfilter_user.h"
dev_node_t *dev_hash_table[MAX_DEV_NODE_HASH_SIZE];
int g_cur_user_num = 0;
unsigned int hash_mac(unsigned char *mac)
{
unsigned int hash = 0;
int i;
if (!mac)
return 0;
for (i = 0; mac[i] != '\0' && i < MAX_MAC_LEN; i++) {
hash = hash * 31 + mac[i];
}
return hash & (MAX_DEV_NODE_HASH_SIZE - 1);
}
int hash_appid(int appid)
{
return appid % (MAX_VISIT_HASH_SIZE - 1);
}
void add_visit_info_node(visit_info_t **head, visit_info_t *node)
{
if (*head == NULL)
{
*head = node;
}
else
{
node->next = *head;
*head = node;
}
}
void init_dev_node_htable()
{
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_hash_table[i] = NULL;
}
printf("init dev node htable ok...\n");
}
dev_node_t *add_dev_node(char *mac)
{
unsigned int hash = 0;
hash = hash_mac(mac);
if (hash >= MAX_DEV_NODE_HASH_SIZE)
{
printf("hash code error %d\n", hash);
return NULL;
}
dev_node_t *node = (dev_node_t *)calloc(1, sizeof(dev_node_t));
if (!node)
return NULL;
strncpy(node->mac, mac, sizeof(node->mac));
node->online = 1;
node->online_time = get_timestamp();
if (dev_hash_table[hash] == NULL)
dev_hash_table[hash] = node;
else
{
node->next = dev_hash_table[hash];
dev_hash_table[hash] = node;
}
g_cur_user_num++;
printf("add mac:%s to htable[%d]....success\n", mac, hash);
return node;
}
dev_node_t *find_dev_node(char *mac)
{
unsigned int hash = 0;
dev_node_t *p = NULL;
hash = hash_mac(mac);
if (hash >= MAX_DEV_NODE_HASH_SIZE)
{
printf("hash code error %d\n", hash);
return NULL;
}
p = dev_hash_table[hash];
while (p)
{
if (0 == strncmp(p->mac, mac, sizeof(p->mac)))
{
return p;
}
p = p->next;
}
return NULL;
}
void dev_foreach(void *arg, iter_func iter)
{
int i, j;
dev_node_t *node = NULL;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
iter(arg, node);
node = node->next;
}
}
}
char *format_time(int timetamp)
{
char time_buf[64] = {0};
time_t seconds = timetamp;
struct tm *auth_tm = localtime(&seconds);
strftime(time_buf, sizeof(time_buf), "%Y %m %d %H:%M:%S", auth_tm);
return strdup(time_buf);
}
void update_dev_hostname(void)
{
char line_buf[256] = {0};
char hostname_buf[128] = {0};
char mac_buf[32] = {0};
char ip_buf[32] = {0};
FILE *fp = fopen("/tmp/dhcp.leases", "r");
if (!fp)
{
printf("open dhcp lease file....failed\n");
return;
}
while (fgets(line_buf, sizeof(line_buf), fp))
{
if (strlen(line_buf) <= 16)
continue;
sscanf(line_buf, "%*s %s %s %s", mac_buf, ip_buf, hostname_buf);
dev_node_t *node = find_dev_node(mac_buf);
if (!node)
{
node = add_dev_node(mac_buf);
if (!node)
continue;
strncpy(node->ip, ip_buf, sizeof(node->ip));
node->online = 0;
node->offline_time = get_timestamp();
}
if (strlen(hostname_buf) > 0 && hostname_buf[0] != '*')
{
strncpy(node->hostname, hostname_buf, sizeof(node->hostname));
}
}
fclose(fp);
}
void clean_dev_nickname_iter(void *arg, dev_node_t *node)
{
node->nickname[0] = '\0';
}
void clean_dev_nickname(void)
{
dev_foreach(NULL, clean_dev_nickname_iter);
}
void update_dev_nickname(void)
{
char nickname_buf[128] = {0};
char mac_str[128] = {0};
struct uci_context *uci_ctx = uci_alloc_context();
clean_dev_nickname();
int num = af_get_uci_list_num(uci_ctx, "user_info", "user_info");
for (int i = 0; i < num; i++) {
af_uci_get_array_value(uci_ctx, "user_info.@user_info[%d].mac", i, mac_str, sizeof(mac_str));
dev_node_t *node = find_dev_node(mac_str);
if (!node)
continue;
af_uci_get_array_value(uci_ctx, "user_info.@user_info[%d].nickname", i, nickname_buf, sizeof(nickname_buf));
strncpy(node->nickname, nickname_buf, sizeof(node->nickname));
}
uci_free_context(uci_ctx);
}
void clean_dev_whitelist_flag_iter(void *arg, dev_node_t *node)
{
node->is_whitelist = 0;
}
void clean_dev_whitelist_flag(void)
{
dev_foreach(NULL, clean_dev_whitelist_flag_iter);
}
void update_dev_whitelist_flag(void)
{
clean_dev_whitelist_flag();
dev_node_t *node = NULL;
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
return;
}
char mac_str[128] = {0};
int num = af_get_uci_list_num(uci_ctx, "appfilter", "whitelist");
for (int i = 0; i < num; i++) {
af_uci_get_array_value(uci_ctx, "appfilter.@whitelist[%d].mac", i, mac_str, sizeof(mac_str));
node = find_dev_node(mac_str);
if (node) {
node->is_whitelist = 1;
}
}
uci_free_context(uci_ctx);
}
void clean_dev_selected_flag_iter(void *arg, dev_node_t *node)
{
node->is_selected = 0;
}
void clean_dev_selected_flag(void)
{
dev_foreach(NULL, clean_dev_selected_flag_iter);
}
void update_dev_selected_flag(void)
{
extern af_config_t g_af_config;
int user_mode = g_af_config.global.user_mode;
clean_dev_selected_flag();
if (user_mode == 0) {
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) {
dev_node_t *node = dev_hash_table[i];
while (node) {
if (!node->is_whitelist) {
node->is_selected = 1;
}
node = node->next;
}
}
LOG_DEBUG("Auto mode: all users except whitelist are selected\n");
} else {
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context in update_dev_selected_flag\n");
return;
}
char mac_str[128] = {0};
int num = af_get_uci_list_num(uci_ctx, "appfilter", "af_user");
for (int i = 0; i < num; i++) {
af_uci_get_array_value(uci_ctx, "appfilter.@af_user[%d].mac", i, mac_str, sizeof(mac_str));
dev_node_t *node = find_dev_node(mac_str);
if (node) {
node->is_selected = 1;
}
}
uci_free_context(uci_ctx);
LOG_DEBUG("Manual mode: %d users from af_user config are selected\n", num);
}
}
void clean_dev_online_status(void)
{
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
if (node->online)
{
node->offline_time = get_timestamp();
node->online = 0;
}
node = node->next;
}
}
}
void update_dev_from_kernel(void)
{
char line_buf[256] = {0};
char mac_buf[32] = {0};
char ip_buf[32] = {0};
char ipv6_buf[128] = {0};
unsigned int up_rate = 0;
unsigned int down_rate = 0;
FILE *fp = fopen("/proc/net/af_client", "r");
if (!fp)
{
printf("open client file....failed\n");
return;
}
fgets(line_buf, sizeof(line_buf), fp); // title
while (fgets(line_buf, sizeof(line_buf), fp))
{
int id;
int parsed = sscanf(line_buf, "%d %s %s %s %u %u", &id, mac_buf, ip_buf, ipv6_buf, &up_rate, &down_rate);
if (parsed < 3)
{
printf("invalid line format:%s\n", line_buf);
continue;
}
if (strlen(mac_buf) < 17)
{
printf("invalid mac:%s\n", mac_buf);
continue;
}
dev_node_t *node = find_dev_node(mac_buf);
if (!node)
{
node = add_dev_node(mac_buf);
if (!node)
continue;
strncpy(node->ip, ip_buf, sizeof(node->ip));
}
strncpy(node->ip, ip_buf, sizeof(node->ip));
if (parsed >= 4 && strlen(ipv6_buf) > 0)
{
strncpy(node->ipv6, ipv6_buf, sizeof(node->ipv6));
}
else
{
node->ipv6[0] = '\0';
}
if (parsed >= 5)
{
node->up_rate = up_rate;
}
else
{
node->up_rate = 0;
}
if (parsed >= 6)
{
node->down_rate = down_rate;
}
else
{
node->down_rate = 0;
}
node->online = 1;
}
fclose(fp);
}
void update_dev_online_status(void)
{
update_dev_from_kernel();
}
#define DEV_OFFLINE_TIME (SECONDS_PER_DAY * 7)
int check_dev_expire(void)
{
int i, j;
int count = 0;
int cur_time = get_timestamp();
int offline_time = 0;
int expire_count = 0;
int visit_count = 0;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
if (node->online)
goto NEXT;
visit_count = 0;
offline_time = cur_time - node->offline_time;
if (offline_time > DEV_OFFLINE_TIME)
{
node->expire = 1;
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
while (p_info)
{
p_info->expire = 1;
visit_count++;
p_info = p_info->next;
}
}
expire_count++;
LOG_WARN("dev:%s expired, offline time = %ds, count=%d, visit_count=%d\n",
node->mac, offline_time, expire_count, visit_count);
}
NEXT:
node = node->next;
}
}
return expire_count;
}
void flush_dev_expire_node(void)
{
int i, j;
int count = 0;
dev_node_t *node = NULL;
dev_node_t *prev = NULL;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
prev = NULL;
while (node)
{
if (node->expire)
{
if (NULL == prev)
{
dev_hash_table[i] = node->next;
free(node);
node = dev_hash_table[i];
prev = NULL;
}
else
{
prev->next = node->next;
free(node);
node = prev->next;
}
}
else
{
prev = node;
node = node->next;
}
}
}
}
void flush_offline_users(void)
{
int i, j;
int count = 0;
dev_node_t *node = NULL;
dev_node_t *prev = NULL;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
prev = NULL;
while (node)
{
if (!node->online) // Clear all offline users
{
// Free visit info first
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
while (p_info)
{
visit_info_t *next = p_info->next;
free(p_info);
p_info = next;
}
}
// Remove node from hash table
if (NULL == prev)
{
dev_hash_table[i] = node->next;
free(node);
node = dev_hash_table[i];
prev = NULL;
}
else
{
prev->next = node->next;
free(node);
node = prev->next;
}
count++;
}
else
{
prev = node;
node = node->next;
}
}
}
LOG_WARN("Cleared %d offline users\n", count);
}
void save_user_time_to_file(void)
{
int i;
int count = 0;
FILE *fp = fopen(OAF_USER_FILE, "w");
if (!fp) {
LOG_ERROR("Failed to open file %s for writing\n", OAF_USER_FILE);
return;
}
fprintf(fp, "MAC,AM_Time,PM_Time\n");
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) {
dev_node_t *node = dev_hash_table[i];
while (node) {
fprintf(fp, "%s,%u,%u\n",
node->mac,
node->today_am_active_time,
node->today_pm_active_time);
count++;
node = node->next;
}
}
fclose(fp);
LOG_DEBUG("Saved %d users' time data to %s\n", count, OAF_USER_FILE);
}
void load_user_time_from_file(void)
{
FILE *fp = fopen(OAF_USER_FILE, "r");
if (!fp) {
LOG_DEBUG("File %s not found or cannot be opened, starting with empty data\n", OAF_USER_FILE);
return;
}
char line_buf[256] = {0};
int count = 0;
if (fgets(line_buf, sizeof(line_buf), fp) == NULL) {
fclose(fp);
return;
}
while (fgets(line_buf, sizeof(line_buf), fp)) {
char mac[32] = {0};
unsigned int am_time = 0;
unsigned int pm_time = 0;
if (sscanf(line_buf, "%31[^,],%u,%u", mac, &am_time, &pm_time) == 3) {
dev_node_t *node = find_dev_node(mac);
if (!node) {
// If node doesn't exist, create a new one
node = add_dev_node(mac);
if (!node) {
LOG_WARN("Failed to create device node for MAC=%s\n", mac);
continue;
}
LOG_DEBUG("Created new device node for MAC=%s\n", mac);
}
node->today_am_active_time = am_time;
node->today_pm_active_time = pm_time;
count++;
LOG_DEBUG("Loaded user time: MAC=%s, AM=%u, PM=%u\n", mac, am_time, pm_time);
} else {
LOG_WARN("Failed to parse line: %s\n", line_buf);
}
}
fclose(fp);
LOG_WARN("Loaded %d users' time data from %s\n", count, OAF_USER_FILE);
}
void update_dev_visiting_info(void){
char line_buf[256] = {0};
char mac_buf[32] = {0};
char url_buf[32] = {0};
char app_buf[32] = {0};
char time_buf[32] = {0};
FILE *fp = fopen("/proc/net/af_visit", "r");
if (!fp)
{
printf("open af_visit file....failed\n");
return;
}
fgets(line_buf, sizeof(line_buf), fp); // title
while (fgets(line_buf, sizeof(line_buf), fp))
{
sscanf(line_buf, "%s %s %s", mac_buf, app_buf, url_buf);
dev_node_t *node = find_dev_node(mac_buf);
if (!node)
continue;
if (strcmp(url_buf, "none") == 0) {
node->visiting_url[0] = '\0';
}
else {
strncpy(node->visiting_url, url_buf, sizeof(node->visiting_url));
}
node->visiting_app = atoi(app_buf);
}
fclose(fp);
}
void update_dev_list(void)
{
clean_dev_online_status();
update_dev_hostname();
update_dev_nickname();
update_dev_online_status();
update_dev_visiting_info();
update_dev_selected_flag();
}
void dump_dev_list(void)
{
int i, j;
int count = 0;
char hostname_buf[MAX_HOSTNAME_SIZE] = {0};
char ip_buf[MAX_IP_LEN] = {0};
FILE *fp = fopen(OAF_DEV_LIST_FILE, "w");
if (!fp)
{
return;
}
fprintf(fp, "%-4s %-20s %-20s %-32s %-8s\n", "Id", "Mac Addr", "Ip Addr", "Hostname", "Online");
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
if (node->online != 0)
{
if (strlen(node->hostname) == 0)
strcpy(hostname_buf, "*");
else
strcpy(hostname_buf, node->hostname);
if (strlen(node->ip) == 0)
strcpy(ip_buf, "*");
else
strcpy(ip_buf, node->ip);
fprintf(fp, "%-4d %-20s %-20s %-32s %-8d\n",
i + 1, node->mac, ip_buf, hostname_buf, node->online);
count++;
}
node = node->next;
}
}
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
if (node->online == 0)
{
if (strlen(node->hostname) == 0)
strcpy(hostname_buf, "*");
else
strcpy(hostname_buf, node->hostname);
if (strlen(node->ip) == 0)
strcpy(ip_buf, "*");
else
strcpy(ip_buf, node->ip);
fprintf(fp, "%-4d %-20s %-20s %-32s %-8d\n",
i + 1, node->mac, ip_buf, hostname_buf, node->online);
}
node = node->next;
}
}
EXIT:
fclose(fp);
}
#define MAX_RECORD_TIME (1 * 24 * 60 * 60) // 1day
#define RECORD_REMAIN_TIME (60 * 60) // 1hour
#define INVALID_RECORD_TIME (5 * 60) // 5min
void check_dev_visit_info_expire(void)
{
int i, j;
int count = 0;
int cur_time = get_timestamp();
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
while (p_info)
{
int total_time = p_info->latest_time - p_info->first_time;
int interval_time = cur_time - p_info->first_time;
if (interval_time > MAX_RECORD_TIME || interval_time < 0)
{
p_info->expire = 1;
}
else if (interval_time > RECORD_REMAIN_TIME)
{
if (total_time < INVALID_RECORD_TIME)
p_info->expire = 1;
}
LOG_DEBUG("[%s] appid:%d total_time:%ds interval:%ds, expire = %d\n", node->mac, p_info->appid, total_time, interval_time, p_info->expire);
p_info = p_info->next;
}
}
node = node->next;
}
}
}
void flush_expire_visit_info(void)
{
int i, j;
int count = 0;
visit_info_t *prev = NULL;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
prev = NULL;
while (p_info)
{
if (p_info->expire)
{
LOG_DEBUG("check expire,flush expire visit info: %s, appid=%d\n", node->mac, p_info->appid);
if (NULL == prev)
{
node->visit_htable[j] = p_info->next;
free(p_info);
p_info = node->visit_htable[j];
prev = NULL;
}
else
{
prev->next = p_info->next;
free(p_info);
p_info = prev->next;
}
}
else
{
prev = p_info;
p_info = p_info->next;
}
}
}
node = node->next;
}
}
}
void dump_dev_visit_list(void)
{
int i, j;
int count = 0;
FILE *fp = fopen(OAF_VISIT_LIST_FILE, "w");
if (!fp)
{
return;
}
fprintf(fp, "%-4s %-20s %-20s %-8s %-32s %-32s %-32s %-8s\n", "Id", "Mac Addr",
"Ip Addr", "Appid", "First Time", "Latest Time", "Total Time(s)", "Expire");
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
while (p_info)
{
char *first_time_str = format_time(p_info->first_time);
char *latest_time_str = format_time(p_info->latest_time);
int total_time = p_info->latest_time - p_info->first_time;
fprintf(fp, "%-4d %-20s %-20s %-8d %-32s %-32s %-32d %-4d\n",
count, node->mac, node->ip, p_info->appid, first_time_str,
latest_time_str, total_time, p_info->expire);
if (first_time_str)
free(first_time_str);
if (latest_time_str)
free(latest_time_str);
p_info = p_info->next;
count++;
if (count > 50)
goto EXIT;
}
}
node = node->next;
}
}
EXIT:
fclose(fp);
}
void clean_invalid_app_records(void)
{
int i, j;
int invalid_count = 0;
int total_count = 0;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
for (j = 0; j < MAX_VISIT_HASH_SIZE; j++)
{
visit_info_t *p_info = node->visit_htable[j];
while (p_info)
{
total_count++;
char *app_name = get_app_name_by_id(p_info->appid);
if (app_name && strlen(app_name) == 0)
{
p_info->expire = 1;
invalid_count++;
LOG_DEBUG("clean: MAC=%s, AppID=%d\n", node->mac, p_info->appid);
}
p_info = p_info->next;
}
}
node = node->next;
}
}
if (invalid_count > 0)
{
flush_expire_visit_info();
}
}
void clear_device_app_statistics(void)
{
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
memset(node->stat, 0, sizeof(node->stat));
node = node->next;
}
}
}
void check_and_reset_today_active_time(dev_node_t *node)
{
if (!node)
return;
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
int current_hour = tm_info->tm_hour;
int current_min = tm_info->tm_min;
static int last_reset_hour = -1;
static int last_reset_min = -1;
if (current_hour == 12 && current_min == 0) {
if (last_reset_hour != 12 || last_reset_min != 0) {
LOG_DEBUG("Reset today_am_active_time for %s: %d -> 0 (12:00 reset)\n",
node->mac, node->today_am_active_time);
node->today_am_active_time = 0;
last_reset_hour = 12;
last_reset_min = 0;
}
} else {
last_reset_hour = current_hour;
last_reset_min = current_min;
}
}
void reset_all_users_today_active_time(void)
{
extern af_run_time_status_t g_af_status;
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) {
dev_node_t *node = dev_hash_table[i];
while (node) {
LOG_DEBUG("Reset today active time for %s: am=%d->0, pm=%d->0 (day changed)\n",
node->mac, node->today_am_active_time, node->today_pm_active_time);
node->today_am_active_time = 0;
node->today_pm_active_time = 0;
node = node->next;
}
}
g_af_status.period_blocked = 0;
}
void reset_all_users_today_flow(void)
{
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) {
dev_node_t *node = dev_hash_table[i];
while (node) {
node->today_down_bytes = 0;
node->today_up_bytes = 0;
node = node->next;
}
}
}
void check_all_users_period_time(void)
{
int i;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++)
{
dev_node_t *node = dev_hash_table[i];
while (node)
{
check_and_reset_today_active_time(node);
node = node->next;
}
}
}
void save_user_active_time_to_file(void)
{
FILE *fp = fopen(OAF_USER_FILE, "w");
if (!fp) {
LOG_ERROR("Failed to open file for writing: %s\n", OAF_USER_FILE);
return;
}
fprintf(fp, "mac,today_am_active_time,today_pm_active_time\n");
int i;
int count = 0;
for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) {
dev_node_t *node = dev_hash_table[i];
while (node) {
if (node->today_am_active_time > 0 || node->today_pm_active_time > 0) {
fprintf(fp, "%s,%u,%u\n",
node->mac,
node->today_am_active_time,
node->today_pm_active_time);
count++;
}
node = node->next;
}
}
fclose(fp);
LOG_DEBUG("Saved %d users' active time to %s\n", count, OAF_USER_FILE);
}
-136
View File
@@ -1,136 +0,0 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __FILTER_USER_H__
#define __FILTER_USER_H__
#include <sys/types.h>
#define MAX_IP_LEN 32
#define MAX_MAC_LEN 32
#define MAX_VISIT_HASH_SIZE 64
#define MAX_DEV_NODE_HASH_SIZE 64
#define MAX_HOSTNAME_SIZE 64
#define OAF_VISIT_LIST_FILE "/tmp/visit_list"
#define OAF_DEV_LIST_FILE "/tmp/dev_list"
#define OAF_USER_FILE "/etc/user_list.dat"
#define MIN_VISIT_TIME 5 // default 5s
#define MAX_APP_STAT_NUM 8
#define MAX_VISITLIST_DUMP_NUM 16
#define MAX_APP_TYPE 32
#define MAX_APP_ID_NUM 512
#define MAX_SUPPORT_DEV_NUM 256
#define SECONDS_PER_DAY (24 * 3600)
#define MAX_NICKNAME_SIZE 64
#define MAX_REPORT_URL_LEN 64
typedef struct visit_info
{
int appid;
u_int32_t first_time;
u_int32_t latest_time;
int action;
int expire;
struct visit_info *next;
} visit_info_t;
typedef struct visit_stat
{
u_int32_t total_time;
} visit_stat_t;
typedef struct dev_node
{
char mac[MAX_MAC_LEN];
char ip[MAX_IP_LEN];
char ipv6[64];
char hostname[MAX_HOSTNAME_SIZE];
char nickname[MAX_NICKNAME_SIZE];
int online;
int expire;
u_int32_t offline_time;
u_int32_t online_time;
visit_info_t *visit_htable[MAX_VISIT_HASH_SIZE];
visit_stat_t stat[MAX_APP_TYPE][MAX_APP_ID_NUM]; // todo: list
char visiting_url[MAX_REPORT_URL_LEN];
int visiting_app;
int is_whitelist;
u_int32_t up_rate;
u_int32_t down_rate;
u_int64_t today_up_bytes;
u_int64_t today_down_bytes;
int active;
u_int32_t today_am_active_time;
u_int32_t today_pm_active_time;
int is_selected;
struct dev_node *next;
} dev_node_t;
struct app_visit_info
{
int app_id;
char app_name[32];
int total_time;
};
struct app_visit_stat_info
{
int num;
struct app_visit_info visit_list[MAX_APP_STAT_NUM];
};
typedef void (*iter_func)(void *arg, dev_node_t *dev);
//todo:dev for each
extern dev_node_t *dev_hash_table[MAX_DEV_NODE_HASH_SIZE];
dev_node_t *add_dev_node(char *mac);
void init_dev_node_htable();
void dump_dev_list(void);
void dump_dev_visit_list(void);
dev_node_t *find_dev_node(char *mac);
void dev_foreach(void *arg, iter_func iter);
void add_visit_info_node(visit_info_t **head, visit_info_t *node);
void check_dev_visit_info_expire(void);
void flush_expire_visit_info();
int check_dev_expire(void);
void flush_dev_expire_node(void);
void flush_expire_visit_info(void);
void flush_offline_users(void);
void save_user_time_to_file(void);
void load_user_time_from_file(void);
void update_dev_list(void);
void update_dev_nickname(void);
void update_dev_visiting_info(void);
void update_dev_whitelist_flag(void);
void update_dev_selected_flag(void);
void clean_invalid_app_records(void);
void clear_device_app_statistics(void);
void check_and_reset_today_active_time(dev_node_t *node);
void reset_all_users_today_active_time(void);
void reset_all_users_today_flow(void);
void check_all_users_period_time(void);
void update_dev_online_status(void);
#endif
+82
View File
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include "fwx.h"
#include "fwx_utils.h"
#include "check_main.h"
#define CHECK_INTERVAL 30
#define LOG_DIR_PATH "/tmp/log"
#define LOG_DIR_MAX_SIZE_KB 10240
static pthread_t check_thread;
static int check_thread_running = 0;
static int check_thread_exit = 0;
static void check_and_cleanup_log_dir(void) {
char cmd_buf[256];
char result_buf[64];
memset(result_buf, 0, sizeof(result_buf));
snprintf(cmd_buf, sizeof(cmd_buf), "du -sk %s 2>/dev/null | awk '{print $1}'", LOG_DIR_PATH);
exec_with_result_line(cmd_buf, result_buf, sizeof(result_buf));
if (result_buf[0] == '\0')
return;
int size_kb = atoi(result_buf);
LOG_INFO("check_and_cleanup_log_dir: log dir size = %d KB\n", size_kb);
if (size_kb <= LOG_DIR_MAX_SIZE_KB)
return;
snprintf(cmd_buf, sizeof(cmd_buf), "rm -rf %s/*", LOG_DIR_PATH);
system(cmd_buf);
}
static void* check_thread_func(void *arg) {
LOG_DEBUG("check_thread: thread function started\n");
check_thread_running = 1;
LOG_DEBUG("check_thread: running\n");
check_and_cleanup_log_dir();
while (!check_thread_exit) {
sleep(CHECK_INTERVAL);
if (!check_thread_exit) {
check_and_cleanup_log_dir();
}
}
check_thread_running = 0;
LOG_DEBUG("check_thread: exited\n");
return NULL;
}
int start_check_thread(void) {
int ret;
check_thread_exit = 0;
check_thread_running = 0;
ret = pthread_create(&check_thread, NULL, check_thread_func, NULL);
if (ret != 0) {
LOG_ERROR("Failed to create check_thread: %s\n", strerror(ret));
return -1;
}
LOG_INFO("check_thread: created\n");
return 0;
}
void stop_check_thread(void) {
if (!check_thread_running) {
return;
}
check_thread_exit = 1;
pthread_join(check_thread, NULL);
}
+15
View File
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __CHECK_MAIN_H__
#define __CHECK_MAIN_H__
int start_check_thread(void);
void stop_check_thread(void);
#endif
+128
View File
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_H___
#define __FWX_H___
#define MIN_INET_ADDR_LEN 7
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <json-c/json.h>
#include "fwx_uci.h"
#define API_CODE_SUCCESS 2000
#define API_CODE_ERROR 4000
#define LOG_FILE_PATH "/tmp/log/fwxd.log"
typedef struct fwx_status {
int internet;
} fwx_status_t;
extern fwx_status_t g_fwx_status;
typedef struct fwx_capability {
int wireless_support;
} fwx_capability_t;
extern fwx_capability_t g_fwx_capability;
typedef enum {
LOG_LEVEL_ERROR,
LOG_LEVEL_WARN,
LOG_LEVEL_INFO,
LOG_LEVEL_DEBUG
} LogLevel;
extern int current_log_level;
extern int g_fwxd_debug_mode;
static void af_log(LogLevel level, const char *func, int line, const char *format, ...){
if (level > current_log_level)
return;
FILE *log_file = fopen(LOG_FILE_PATH, "a");
if (!log_file) {
perror("Failed to open log file");
return;
}
time_t now = time(NULL);
struct tm *t = localtime(&now);
char time_str[20];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", t);
const char *level_str;
switch (level) {
case LOG_LEVEL_DEBUG: level_str = "DEBUG"; break;
case LOG_LEVEL_INFO: level_str = "INFO"; break;
case LOG_LEVEL_WARN: level_str = "WARN"; break;
case LOG_LEVEL_ERROR: level_str = "ERROR"; break;
default: level_str = "UNKNOWN"; break;
}
fprintf(log_file, "[%s] [%s] %s:%d ", time_str, level_str, func, line);
va_list args;
va_start(args, format);
vfprintf(log_file, format, args);
va_end(args);
fprintf(log_file, "\n");
fclose(log_file);
}
#define LOG_DEBUG(format, ...) af_log(LOG_LEVEL_DEBUG, __func__, __LINE__, format, ##__VA_ARGS__)
#define LOG_INFO(format, ...) af_log(LOG_LEVEL_INFO, __func__, __LINE__, format, ##__VA_ARGS__)
#define LOG_WARN(format, ...) af_log(LOG_LEVEL_WARN, __func__, __LINE__, format, ##__VA_ARGS__)
#define LOG_ERROR(format, ...) af_log(LOG_LEVEL_ERROR, __func__, __LINE__, format, ##__VA_ARGS__)
#define MAX_TIME_LIST_LEN 1024
#define MAX_TIME_LIST 64
typedef struct af_time
{
int hour;
int min;
} af_time_t;
typedef struct time_config{
af_time_t start_time;
af_time_t end_time;
}time_config_t;
typedef struct weekday_time_config{
af_time_t start_time;
af_time_t end_time;
unsigned char weekday_map[7];
}weekday_time_config_t;
typedef struct fwx_time_config_t{
int time_mode;
weekday_time_config_t seg_time;
int deny_time;
int allow_time;
int time_num;
weekday_time_config_t time_list[MAX_TIME_LIST];
}fwx_time_config_t;
typedef struct fwx_run_time_status{
int deny_time;
int allow_time;
int filter;
int match_time;
int enable;
}fwx_run_time_status_t;
void fwx_init_time_status(fwx_run_time_status_t *status);
int fwx_check_time(fwx_time_config_t *t_config, fwx_run_time_status_t *status);
struct json_object *fwx_gen_api_response_data(int code, struct json_object *data_obj);
#endif
+674
View File
@@ -0,0 +1,674 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <uci.h>
#include "fwx_user.h"
#include "fwx_netlink.h"
#include "fwx_ubus.h"
#include "fwx_config.h"
#include <time.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "fwx.h"
#include <stdio.h>
#include <json-c/json.h>
#include "fwx_utils.h"
#define APPFILTER_RULES_STATE_FILE "/tmp/appfilter_rules_state"
#define APPFILTER_WHITELIST_STATE_FILE "/tmp/appfilter_whitelist_state"
static void set_state_file(const char *file_path) {
FILE *fd = fopen(file_path, "w");
if (fd) {
fprintf(fd, "1");
fclose(fd);
LOG_DEBUG("Set state file: %s\n", file_path);
} else {
LOG_ERROR("Failed to set state file: %s\n", file_path);
}
}
fwx_run_time_status_t g_af_status;
void dev_list_timeout_handler(struct uloop_timeout *t);
static int find_rule_index_by_id(struct uci_context *uci_ctx, int id) {
char id_str_uci[32];
int i;
int num = fwx_uci_get_list_num(uci_ctx, "appfilter", "rule");
for (i = 0; i < num; i++) {
char buf[128];
snprintf(buf, sizeof(buf), "appfilter.@rule[%d].id", i);
if (fwx_uci_get_value(uci_ctx, buf, id_str_uci, sizeof(id_str_uci)) != 0) {
continue;
}
if (atoi(id_str_uci) == id) {
return i;
}
}
return -1; // Not found
}
// Helper function to get option value from uci section
static const char *get_option_value(struct uci_section *s, const char *option_name) {
if (!s || !option_name) return NULL;
struct uci_option *o = uci_lookup_option(s->package->ctx, s, option_name);
if (!o || o->type != UCI_TYPE_STRING) {
return NULL;
}
return o->v.string;
}
static int normalize_app_id_token(const char *raw, char *out, int out_len) {
int start = 0, end = 0, val = 0;
char extra = '\0';
if (!raw || !out || out_len <= 0) return 0;
if (sscanf(raw, "%d-%d%c", &start, &end, &extra) == 2) {
if (start > 0 && end > 0 && start <= end) {
snprintf(out, out_len, "%d-%d", start, end);
return 1;
}
return 0;
}
if (sscanf(raw, "%d%c", &val, &extra) == 1 && val > 0) {
snprintf(out, out_len, "%d", val);
return 1;
}
return 0;
}
struct json_object *fwx_api_get_filter_rules(struct json_object *req_obj) {
struct json_object *data_obj = json_object_new_object();
struct json_object *rules_array = json_object_new_array();
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
// Load appfilter package once
struct uci_package *pkg = NULL;
if (uci_load(uci_ctx, "appfilter", &pkg) != UCI_OK) {
LOG_ERROR("Failed to load appfilter package\n");
uci_free_context(uci_ctx);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
// Traverse all sections using uci_foreach_element
struct uci_element *e;
uci_foreach_element(&pkg->sections, e) {
struct uci_section *s = uci_to_section(e);
// Only process "rule" type sections
if (strcmp(s->type, "rule") != 0) {
continue;
}
LOG_DEBUG("Loading rule: %s\n", s->e.name);
// Get id - required field
const char *id_str = get_option_value(s, "id");
if (!id_str) {
LOG_ERROR("Failed to get id for rule %s, skipping\n", s->e.name);
continue;
}
// Get other fields with defaults
const char *name_str = get_option_value(s, "name");
const char *mode_str = get_option_value(s, "mode");
const char *user_mac_str = get_option_value(s, "user_mac");
const char *user_name_str = get_option_value(s, "user_name");
const char *enabled_str = get_option_value(s, "enabled");
const char *filter_quic_str = get_option_value(s, "filter_quic");
struct json_object *rule_obj = json_object_new_object();
if (!rule_obj) {
LOG_ERROR("Failed to create rule_obj for rule %s\n", s->e.name);
continue;
}
json_object_object_add(rule_obj, "id", json_object_new_int(atoi(id_str)));
json_object_object_add(rule_obj, "name", json_object_new_string(name_str ? name_str : ""));
json_object_object_add(rule_obj, "mode", json_object_new_int(mode_str ? atoi(mode_str) : 1));
json_object_object_add(rule_obj, "user_mac", json_object_new_string(user_mac_str ? user_mac_str : ""));
json_object_object_add(rule_obj, "user_name", json_object_new_string(user_name_str ? user_name_str : ""));
json_object_object_add(rule_obj, "enabled", json_object_new_int(enabled_str ? atoi(enabled_str) : 1));
json_object_object_add(rule_obj, "filter_quic", json_object_new_int(filter_quic_str ? atoi(filter_quic_str) : 0));
// Process time_rule list
struct json_object *time_rules_array = json_object_new_array();
struct uci_option *time_rule_opt = uci_lookup_option(uci_ctx, s, "time_rule");
if (time_rule_opt && time_rule_opt->type == UCI_TYPE_LIST) {
struct uci_element *time_elem;
uci_foreach_element(&time_rule_opt->v.list, time_elem) {
const char *time_rule_str = time_elem->name;
if (!time_rule_str) continue;
LOG_DEBUG("Loading time_rule: %s\n", time_rule_str);
struct json_object *time_rule_obj = json_object_new_object();
struct json_object *weekdays_array = json_object_new_array();
// Parse time_rule string: "weekday1,weekday2,...,start_time,end_time"
char *time_rule_copy = strdup(time_rule_str);
if (time_rule_copy) {
char *saveptr;
char *token = strtok_r(time_rule_copy, ",", &saveptr);
char *start_time = NULL;
char *end_time = NULL;
while (token) {
if (strchr(token, ':') != NULL) {
// This is a time string (HH:MM format)
if (start_time == NULL) {
start_time = token;
} else if (end_time == NULL) {
end_time = token;
break;
}
} else {
// This is a weekday number
int weekday = atoi(token);
if (weekday >= 0 && weekday <= 6) {
json_object_array_add(weekdays_array, json_object_new_int(weekday));
}
}
token = strtok_r(NULL, ",", &saveptr);
}
if (start_time) {
json_object_object_add(time_rule_obj, "start_time", json_object_new_string(start_time));
}
if (end_time) {
json_object_object_add(time_rule_obj, "end_time", json_object_new_string(end_time));
}
free(time_rule_copy);
}
json_object_object_add(time_rule_obj, "weekdays", weekdays_array);
json_object_array_add(time_rules_array, time_rule_obj);
}
}
json_object_object_add(rule_obj, "time_rules", time_rules_array);
// Process app_id list(支持字符串段格式:1001-1005)
struct json_object *app_ids_array = json_object_new_array();
struct uci_option *app_id_opt = uci_lookup_option(uci_ctx, s, "app_id");
if (app_id_opt && app_id_opt->type == UCI_TYPE_LIST) {
struct uci_element *app_elem;
uci_foreach_element(&app_id_opt->v.list, app_elem) {
const char *app_id_str = app_elem->name;
if (!app_id_str) continue;
json_object_array_add(app_ids_array, json_object_new_string(app_id_str));
}
}
json_object_object_add(rule_obj, "app_ids", app_ids_array);
json_object_array_add(rules_array, rule_obj);
LOG_DEBUG("Successfully loaded rule: id=%s, name=%s\n", id_str, name_str ? name_str : "");
}
// Unload package
uci_unload(uci_ctx, pkg);
uci_free_context(uci_ctx);
json_object_object_add(data_obj, "list", rules_array);
LOG_DEBUG("Returning %d rules\n", json_object_array_length(rules_array));
return fwx_gen_api_response_data(API_CODE_SUCCESS, data_obj);
}
struct json_object *fwx_api_add_filter_rule(struct json_object *req_obj) {
struct json_object *name_obj = json_object_object_get(req_obj, "name");
struct json_object *mode_obj = json_object_object_get(req_obj, "mode");
struct json_object *user_mac_obj = json_object_object_get(req_obj, "user_mac");
struct json_object *user_name_obj = json_object_object_get(req_obj, "user_name");
struct json_object *enabled_obj = json_object_object_get(req_obj, "enabled");
struct json_object *filter_quic_obj = json_object_object_get(req_obj, "filter_quic");
struct json_object *time_rules_obj = json_object_object_get(req_obj, "time_rules");
struct json_object *app_ids_obj = json_object_object_get(req_obj, "app_ids");
int i, j;
if (!name_obj || !mode_obj || !time_rules_obj || !app_ids_obj) {
LOG_ERROR("Missing required fields\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
int rule_id = (int)time(NULL);
fwx_uci_add_section(uci_ctx, "appfilter", "rule");
char buf[256];
snprintf(buf, sizeof(buf), "appfilter.@rule[-1].id");
char id_str[32];
snprintf(id_str, sizeof(id_str), "%d", rule_id);
fwx_uci_set_value(uci_ctx, buf, id_str);
snprintf(buf, sizeof(buf), "appfilter.@rule[-1].name");
fwx_uci_set_value(uci_ctx, buf, json_object_get_string(name_obj));
snprintf(buf, sizeof(buf), "appfilter.@rule[-1].mode");
char mode_str[16];
snprintf(mode_str, sizeof(mode_str), "%d", json_object_get_int(mode_obj));
fwx_uci_set_value(uci_ctx, buf, mode_str);
if (user_mac_obj) {
snprintf(buf, sizeof(buf), "appfilter.@rule[-1].user_mac");
fwx_uci_set_value(uci_ctx, buf, json_object_get_string(user_mac_obj));
}
if (user_name_obj) {
snprintf(buf, sizeof(buf), "appfilter.@rule[-1].user_name");
fwx_uci_set_value(uci_ctx, buf, json_object_get_string(user_name_obj));
}
snprintf(buf, sizeof(buf), "appfilter.@rule[-1].enabled");
char enabled_str[16];
int enabled = enabled_obj ? json_object_get_int(enabled_obj) : 1;
snprintf(enabled_str, sizeof(enabled_str), "%d", enabled);
fwx_uci_set_value(uci_ctx, buf, enabled_str);
snprintf(buf, sizeof(buf), "appfilter.@rule[-1].filter_quic");
{
char filter_quic_str[16];
int filter_quic = filter_quic_obj ? json_object_get_int(filter_quic_obj) : 0;
snprintf(filter_quic_str, sizeof(filter_quic_str), "%d", filter_quic ? 1 : 0);
fwx_uci_set_value(uci_ctx, buf, filter_quic_str);
}
int time_rules_len = json_object_array_length(time_rules_obj);
for (i = 0; i < time_rules_len; i++) {
struct json_object *time_rule_obj = json_object_array_get_idx(time_rules_obj, i);
struct json_object *weekdays_obj = json_object_object_get(time_rule_obj, "weekdays");
struct json_object *start_time_obj = json_object_object_get(time_rule_obj, "start_time");
struct json_object *end_time_obj = json_object_object_get(time_rule_obj, "end_time");
if (!weekdays_obj || !start_time_obj || !end_time_obj) {
continue;
}
char time_rule_str[256] = {0};
int weekdays_len = json_object_array_length(weekdays_obj);
for (j = 0; j < weekdays_len; j++) {
struct json_object *weekday_obj = json_object_array_get_idx(weekdays_obj, j);
char weekday_str[16];
snprintf(weekday_str, sizeof(weekday_str), "%d", json_object_get_int(weekday_obj));
if (j > 0) strcat(time_rule_str, ",");
strcat(time_rule_str, weekday_str);
}
strcat(time_rule_str, ",");
strcat(time_rule_str, json_object_get_string(start_time_obj));
strcat(time_rule_str, ",");
strcat(time_rule_str, json_object_get_string(end_time_obj));
snprintf(buf, sizeof(buf), "appfilter.@rule[-1].time_rule");
fwx_uci_add_list(uci_ctx, buf, time_rule_str);
}
int app_ids_len = json_object_array_length(app_ids_obj);
for (i = 0; i < app_ids_len; i++) {
struct json_object *app_id_obj = json_object_array_get_idx(app_ids_obj, i);
const char *raw = json_object_get_string(app_id_obj);
char app_id_str[64] = {0};
if (normalize_app_id_token(raw, app_id_str, sizeof(app_id_str))) {
snprintf(buf, sizeof(buf), "appfilter.@rule[-1].app_id");
fwx_uci_add_list(uci_ctx, buf, app_id_str);
}
}
fwx_uci_commit(uci_ctx, "appfilter");
uci_free_context(uci_ctx);
set_state_file(APPFILTER_RULES_STATE_FILE);
LOG_DEBUG("Added filter rule: id=%d, name=%s\n", rule_id, json_object_get_string(name_obj));
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
struct json_object *fwx_api_update_filter_rule(struct json_object *req_obj) {
struct json_object *id_obj = json_object_object_get(req_obj, "id");
int i, j;
if (!id_obj) {
LOG_ERROR("Missing id field\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
int rule_id = json_object_get_int(id_obj);
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
int index = find_rule_index_by_id(uci_ctx, rule_id);
if (index < 0) {
LOG_ERROR("Rule not found: id=%d\n", rule_id);
uci_free_context(uci_ctx);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
char buf[256];
struct json_object *name_obj = json_object_object_get(req_obj, "name");
if (name_obj) {
snprintf(buf, sizeof(buf), "appfilter.@rule[%d].name", index);
fwx_uci_set_value(uci_ctx, buf, json_object_get_string(name_obj));
}
struct json_object *mode_obj = json_object_object_get(req_obj, "mode");
if (mode_obj) {
snprintf(buf, sizeof(buf), "appfilter.@rule[%d].mode", index);
char mode_str[16];
snprintf(mode_str, sizeof(mode_str), "%d", json_object_get_int(mode_obj));
fwx_uci_set_value(uci_ctx, buf, mode_str);
}
struct json_object *user_mac_obj = json_object_object_get(req_obj, "user_mac");
if (user_mac_obj) {
snprintf(buf, sizeof(buf), "appfilter.@rule[%d].user_mac", index);
fwx_uci_set_value(uci_ctx, buf, json_object_get_string(user_mac_obj));
}
struct json_object *user_name_obj = json_object_object_get(req_obj, "user_name");
if (user_name_obj) {
snprintf(buf, sizeof(buf), "appfilter.@rule[%d].user_name", index);
fwx_uci_set_value(uci_ctx, buf, json_object_get_string(user_name_obj));
}
struct json_object *enabled_obj = json_object_object_get(req_obj, "enabled");
if (enabled_obj) {
snprintf(buf, sizeof(buf), "appfilter.@rule[%d].enabled", index);
char enabled_str[16];
snprintf(enabled_str, sizeof(enabled_str), "%d", json_object_get_int(enabled_obj));
fwx_uci_set_value(uci_ctx, buf, enabled_str);
}
struct json_object *filter_quic_obj = json_object_object_get(req_obj, "filter_quic");
if (filter_quic_obj) {
snprintf(buf, sizeof(buf), "appfilter.@rule[%d].filter_quic", index);
char filter_quic_str[16];
snprintf(filter_quic_str, sizeof(filter_quic_str), "%d", json_object_get_int(filter_quic_obj) ? 1 : 0);
fwx_uci_set_value(uci_ctx, buf, filter_quic_str);
}
snprintf(buf, sizeof(buf), "appfilter.@rule[%d].time_rule", index);
fwx_uci_delete(uci_ctx, buf);
snprintf(buf, sizeof(buf), "appfilter.@rule[%d].app_id", index);
fwx_uci_delete(uci_ctx, buf);
struct json_object *time_rules_obj = json_object_object_get(req_obj, "time_rules");
if (time_rules_obj) {
int time_rules_len = json_object_array_length(time_rules_obj);
for (i = 0; i < time_rules_len; i++) {
struct json_object *time_rule_obj = json_object_array_get_idx(time_rules_obj, i);
struct json_object *weekdays_obj = json_object_object_get(time_rule_obj, "weekdays");
struct json_object *start_time_obj = json_object_object_get(time_rule_obj, "start_time");
struct json_object *end_time_obj = json_object_object_get(time_rule_obj, "end_time");
if (!weekdays_obj || !start_time_obj || !end_time_obj) {
continue;
}
char time_rule_str[256] = {0};
int weekdays_len = json_object_array_length(weekdays_obj);
for (j = 0; j < weekdays_len; j++) {
struct json_object *weekday_obj = json_object_array_get_idx(weekdays_obj, j);
char weekday_str[16];
snprintf(weekday_str, sizeof(weekday_str), "%d", json_object_get_int(weekday_obj));
if (j > 0) strcat(time_rule_str, ",");
strcat(time_rule_str, weekday_str);
}
strcat(time_rule_str, ",");
strcat(time_rule_str, json_object_get_string(start_time_obj));
strcat(time_rule_str, ",");
strcat(time_rule_str, json_object_get_string(end_time_obj));
snprintf(buf, sizeof(buf), "appfilter.@rule[%d].time_rule", index);
fwx_uci_add_list(uci_ctx, buf, time_rule_str);
}
}
struct json_object *app_ids_obj = json_object_object_get(req_obj, "app_ids");
if (app_ids_obj) {
int app_ids_len = json_object_array_length(app_ids_obj);
for (i = 0; i < app_ids_len; i++) {
struct json_object *app_id_obj = json_object_array_get_idx(app_ids_obj, i);
const char *raw = json_object_get_string(app_id_obj);
char app_id_str[64] = {0};
if (normalize_app_id_token(raw, app_id_str, sizeof(app_id_str))) {
snprintf(buf, sizeof(buf), "appfilter.@rule[%d].app_id", index);
fwx_uci_add_list(uci_ctx, buf, app_id_str);
}
}
}
fwx_uci_commit(uci_ctx, "appfilter");
uci_free_context(uci_ctx);
set_state_file(APPFILTER_RULES_STATE_FILE);
LOG_DEBUG("Updated filter rule: id=%d\n", rule_id);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
struct json_object *fwx_api_delete_filter_rule(struct json_object *req_obj) {
struct json_object *id_obj = json_object_object_get(req_obj, "id");
if (!id_obj) {
LOG_ERROR("Missing id field\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
int rule_id = json_object_get_int(id_obj);
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
int index = find_rule_index_by_id(uci_ctx, rule_id);
if (index < 0) {
LOG_ERROR("Rule not found: id=%d\n", rule_id);
uci_free_context(uci_ctx);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
char buf[128];
snprintf(buf, sizeof(buf), "appfilter.@rule[%d]", index);
fwx_uci_delete(uci_ctx, buf);
fwx_uci_commit(uci_ctx, "appfilter");
uci_free_context(uci_ctx);
set_state_file(APPFILTER_RULES_STATE_FILE);
LOG_DEBUG("Deleted filter rule: id=%d\n", rule_id);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
struct json_object *fwx_api_get_appfilter_whitelist(struct json_object *req_obj){
int i;
struct json_object *data_obj = json_object_new_object();
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
struct json_object *mac_array = json_object_new_array();
char mac_str[128] = {0};
int num = fwx_uci_get_list_num(uci_ctx, "appfilter_whitelist", "whitelist_mac");
for (i = 0; i < num; i++) {
fwx_uci_get_array_value(uci_ctx, "appfilter_whitelist.@whitelist_mac[%d].mac", i, mac_str, sizeof(mac_str));
struct json_object *mac_obj = json_object_new_object();
json_object_object_add(mac_obj, "mac", json_object_new_string(mac_str));
client_node_t *dev = find_client_node(mac_str);
if (dev){
json_object_object_add(mac_obj, "nickname", json_object_new_string(dev->nickname));
json_object_object_add(mac_obj, "hostname", json_object_new_string(dev->hostname));
}else{
json_object_object_add(mac_obj, "nickname", json_object_new_string(""));
json_object_object_add(mac_obj, "hostname", json_object_new_string(""));
}
json_object_array_add(mac_array, mac_obj);
}
json_object_object_add(data_obj, "list", mac_array);
uci_free_context(uci_ctx);
return fwx_gen_api_response_data(API_CODE_SUCCESS, data_obj);
}
struct json_object *fwx_api_del_appfilter_whitelist(struct json_object *req_obj){
int i;
LOG_DEBUG("fwx_api_del_appfilter_whitelist\n");
struct json_object *mac_obj = json_object_object_get(req_obj, "mac");
if (!mac_obj) {
LOG_ERROR("mac_obj is NULL\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
LOG_DEBUG("mac: %s\n", json_object_get_string(mac_obj));
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
char mac_str[128] = {0};
int num = fwx_uci_get_list_num(uci_ctx, "appfilter_whitelist", "whitelist_mac");
for (i = 0; i < num; i++) {
fwx_uci_get_array_value(uci_ctx, "appfilter_whitelist.@whitelist_mac[%d].mac", i, mac_str, sizeof(mac_str));
if (strcmp(mac_str, json_object_get_string(mac_obj)) == 0) {
LOG_DEBUG("delete appfilter_whitelist_mac[%d]\n", i);
char buf[128] = {0};
sprintf(buf, "appfilter_whitelist.@whitelist_mac[%d]", i);
fwx_uci_delete(uci_ctx, buf);
break;
}
}
fwx_uci_commit(uci_ctx, "appfilter_whitelist");
uci_free_context(uci_ctx);
set_state_file(APPFILTER_WHITELIST_STATE_FILE);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
struct json_object *fwx_api_add_appfilter_whitelist(struct json_object *req_obj){
int i;
LOG_DEBUG("fwx_api_add_appfilter_whitelist\n");
struct json_object *mac_array = json_object_object_get(req_obj, "mac_list");
if (!mac_array) {
LOG_ERROR("mac_list not found\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
int len = json_object_array_length(mac_array);
for (i = 0; i < len; i++) {
struct json_object *mac_obj = json_object_array_get_idx(mac_array, i);
fwx_uci_add_section(uci_ctx, "appfilter_whitelist", "whitelist_mac");
fwx_uci_set_value(uci_ctx, "appfilter_whitelist.@whitelist_mac[-1].mac", json_object_get_string(mac_obj));
}
fwx_uci_commit(uci_ctx, "appfilter_whitelist");
uci_free_context(uci_ctx);
set_state_file(APPFILTER_WHITELIST_STATE_FILE);
LOG_DEBUG("Added %d MAC addresses to appfilter whitelist\n", len);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
struct json_object *fwx_api_get_app_filter_adv(struct json_object *req_obj) {
struct json_object *data_obj = json_object_new_object();
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
json_object_put(data_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
int enable = fwx_uci_get_int_value(uci_ctx, "fwx.appfilter.enable");
json_object_object_add(data_obj, "enable", json_object_new_int(enable));
uci_free_context(uci_ctx);
return fwx_gen_api_response_data(API_CODE_SUCCESS, data_obj);
}
struct json_object *fwx_api_set_app_filter_adv(struct json_object *req_obj) {
if (!req_obj) {
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
struct json_object *enable_obj = json_object_object_get(req_obj, "enable");
if (!enable_obj) {
LOG_ERROR("Missing enable parameter\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
int enable_value = json_object_get_int(enable_obj);
fwx_uci_set_int_value(uci_ctx, "fwx.appfilter.enable", enable_value);
fwx_uci_commit(uci_ctx, "fwx");
set_state_file(APPFILTER_RULES_STATE_FILE);
uci_free_context(uci_ctx);
LOG_DEBUG("Set appfilter advanced settings\n");
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
+31
View File
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_APP_FILTER_H__
#define __FWX_APP_FILTER_H__
#include "fwx.h"
int fwx_app_filter_init(void);
void update_oaf_status(void);
struct json_object *fwx_api_get_filter_rules(struct json_object *req_obj);
struct json_object *fwx_api_add_filter_rule(struct json_object *req_obj);
struct json_object *fwx_api_update_filter_rule(struct json_object *req_obj);
struct json_object *fwx_api_delete_filter_rule(struct json_object *req_obj);
struct json_object *fwx_api_get_appfilter_whitelist(struct json_object *req_obj);
struct json_object *fwx_api_del_appfilter_whitelist(struct json_object *req_obj);
struct json_object *fwx_api_add_appfilter_whitelist(struct json_object *req_obj);
struct json_object *fwx_api_get_app_filter_base(struct json_object *req_obj);
struct json_object *fwx_api_set_app_filter_base(struct json_object *req_obj);
struct json_object *fwx_api_get_app_filter_adv(struct json_object *req_obj);
struct json_object *fwx_api_set_app_filter_adv(struct json_object *req_obj);
#endif
+133
View File
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include "fwx_user.h"
#include "fwx_netlink.h"
#include "fwx_ubus.h"
#include "fwx_config.h"
#include <time.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "fwx.h"
#include <stdio.h>
#include "fwx_utils.h"
void fwx_init_time_status(fwx_run_time_status_t *status){
status->filter = 0;
status->deny_time = 0;
status->allow_time = 0;
status->match_time = 0;
}
int fwx_check_time_manual(fwx_time_config_t *t_config, fwx_run_time_status_t *status) {
int i;
time_t now = time(NULL);
struct tm *current_time = localtime(&now);
int current_minutes = current_time->tm_hour * 60 + current_time->tm_min;
int current_wday = current_time->tm_wday; // 0=Sunday, 1=Monday, ..., 6=Saturday
LOG_DEBUG("current time: %02d:%02d, weekday: %d\n", current_time->tm_hour, current_time->tm_min, current_wday);
for (i = 0; i < t_config->time_num; i++) {
if (t_config->time_list[i].weekday_map[current_wday] == 0) {
continue; // Skip if weekday is not enabled
}
int start_minutes = t_config->time_list[i].start_time.hour * 60 + t_config->time_list[i].start_time.min;
int end_minutes = t_config->time_list[i].end_time.hour * 60 + t_config->time_list[i].end_time.min;
LOG_DEBUG("check time: %02d:%02d-%02d:%02d, weekday_map[%d]=%d\n",
t_config->time_list[i].start_time.hour, t_config->time_list[i].start_time.min,
t_config->time_list[i].end_time.hour, t_config->time_list[i].end_time.min,
current_wday, t_config->time_list[i].weekday_map[current_wday]);
if (current_minutes >= start_minutes && current_minutes <= end_minutes) {
LOG_DEBUG("current time in time list\n");
status->match_time = 1;
return 1;
}
}
status->match_time = 0;
return 0;
}
int fwx_check_time_dynamic(fwx_time_config_t *t_config, fwx_run_time_status_t *status) {
time_t now = time(NULL);
struct tm *current_time = localtime(&now);
int current_minutes = current_time->tm_hour * 60 + current_time->tm_min;
int start_minutes = t_config->seg_time.start_time.hour * 60 + t_config->seg_time.start_time.min;
int end_minutes = t_config->seg_time.end_time.hour * 60 + t_config->seg_time.end_time.min;
printf("check seg_time: %02d:%02d-%02d:%02d\n",
t_config->seg_time.start_time.hour, t_config->seg_time.start_time.min,
t_config->seg_time.end_time.hour, t_config->seg_time.end_time.min);
if (!(current_minutes >= start_minutes && current_minutes <= end_minutes)) {
printf("current time not in seg_time\n");
fwx_init_time_status(status);
return 0;
}
status->match_time = 1;
if (status->filter == 1) {
status->deny_time++;
if (status->deny_time >= t_config->deny_time) {
status->filter = 0;
status->deny_time = 0;
printf("deny time over, filter = 0");
}
printf("deny_time: %d\n", status->deny_time);
} else {
status->allow_time++;
if (status->allow_time >= t_config->allow_time) {
status->filter = 1;
status->allow_time = 0;
printf("allow time over, filter = 1");
}
printf("allow_time: %d\n", status->allow_time);
}
return status->filter;
}
int fwx_check_time(fwx_time_config_t *t_config, fwx_run_time_status_t *status) {
time_t now = time(NULL);
struct tm *current_time = localtime(&now);
int current_wday = current_time->tm_wday; // 0=Sunday, 1=Monday, ..., 6=Saturday
LOG_DEBUG("current day: %d\n", current_wday);
if (t_config->time_mode == 0) {
LOG_DEBUG("manual mode\n");
return fwx_check_time_manual(t_config, status);
} else {
LOG_DEBUG("dynamic mode\n");
if (t_config->seg_time.weekday_map[current_wday] == 0) {
LOG_DEBUG("current day not in configured days\n");
fwx_init_time_status(status);
return 0;
}
return fwx_check_time_dynamic(t_config, status);
}
}
struct json_object * fwx_gen_api_response_data(int code, struct json_object *data_obj){
struct json_object *root_obj = json_object_new_object();
if (!root_obj)
return NULL;
json_object_object_add(root_obj, "code", json_object_new_int(code));
if (data_obj)
json_object_object_add(root_obj, "data", data_obj);
return root_obj;
}
+232
View File
@@ -0,0 +1,232 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include "fwx_config.h"
#include "fwx.h"
#include "fwx_feature.h"
#include <uci.h>
app_name_info_t app_name_table[MAX_SUPPORT_APP_NUM];
int g_app_count = 0;
static int g_base_app_count = 0;
int g_cur_class_num = 0;
char CLASS_NAME_TABLE[MAX_APP_TYPE][MAX_CLASS_NAME_LEN];
static char *trim_space(char *text)
{
char *end;
if (!text)
return NULL;
while (*text && isspace((unsigned char)*text))
text++;
end = text + strlen(text);
while (end > text && isspace((unsigned char)end[-1]))
*--end = '\0';
return text;
}
static int parse_feature_app_header(const char *line, int *app_id, char *app_name, size_t app_name_len)
{
char header[128];
char *slash;
char *id_text;
char *name_text;
char *endptr;
long id;
const char *colon;
size_t header_len;
if (!line || !app_id || !app_name || app_name_len == 0)
return -1;
colon = strchr(line, ':');
if (!colon)
return -1;
header_len = (size_t)(colon - line);
if (header_len == 0 || header_len >= sizeof(header))
return -1;
memcpy(header, line, header_len);
header[header_len] = '\0';
slash = strchr(header, '~');
if (!slash)
return -1;
*slash = '\0';
id_text = trim_space(header);
name_text = trim_space(slash + 1);
if (!id_text || !id_text[0] || !name_text || !name_text[0])
return -1;
id = strtol(id_text, &endptr, 10);
endptr = trim_space(endptr);
if (id <= 0 || (endptr && endptr[0] != '\0'))
return -1;
*app_id = (int)id;
strncpy(app_name, name_text, app_name_len - 1);
app_name[app_name_len - 1] = '\0';
return 0;
}
int app_icon_exists_by_id(int id)
{
char icon_path[256];
if (id <= 0)
return 0;
snprintf(icon_path, sizeof(icon_path),
"/www/luci-static/resources/oaf/app_icons/%d.png", id);
return access(icon_path, F_OK) == 0;
}
char *get_app_name_by_id(int id)
{
int i;
static char fallback_name[32];
for (i = 0; i < g_app_count; i++)
{
if (id == app_name_table[i].id)
return app_name_table[i].name;
}
if (id > 0) {
snprintf(fallback_name, sizeof(fallback_name), "App%d", id);
return fallback_name;
}
return "";
}
int add_app_name_to_table(int id, const char *name)
{
if (!name || !name[0] || g_app_count >= MAX_SUPPORT_APP_NUM)
return -1;
app_name_table[g_app_count].id = id;
strncpy(app_name_table[g_app_count].name, name,
sizeof(app_name_table[g_app_count].name) - 1);
app_name_table[g_app_count].name[sizeof(app_name_table[g_app_count].name) - 1] = '\0';
g_app_count++;
return 0;
}
int get_base_app_count(void)
{
return g_base_app_count;
}
void init_app_name_table(void)
{
char line_buf[2048] = {0};
const char *feature_data;
size_t feature_len = 0;
size_t offset = 0;
feature_data = fwx_feature_get_data(&feature_len);
if (!feature_data)
return;
memset(app_name_table, 0, sizeof(app_name_table));
g_app_count = 0;
while (fwx_feature_next_line(feature_data, feature_len, &offset,
line_buf, sizeof(line_buf)) != 0) {
int app_id = 0;
char app_name[64] = {0};
if (strstr(line_buf, "#"))
continue;
if (strlen(line_buf) < 10)
continue;
if (parse_feature_app_header(line_buf, &app_id, app_name, sizeof(app_name)) < 0)
continue;
if (g_app_count >= MAX_SUPPORT_APP_NUM)
break;
app_name_table[g_app_count].id = app_id;
strncpy(app_name_table[g_app_count].name, app_name,
sizeof(app_name_table[g_app_count].name) - 1);
g_app_count++;
}
g_base_app_count = g_app_count;
}
void init_app_class_name_table(void)
{
char line_buf[2048] = {0};
const char *feature_data;
size_t feature_len = 0;
size_t offset = 0;
feature_data = fwx_feature_get_data(&feature_len);
if (!feature_data)
return;
memset(CLASS_NAME_TABLE, 0, sizeof(CLASS_NAME_TABLE));
g_cur_class_num = 0;
while (fwx_feature_next_line(feature_data, feature_len, &offset,
line_buf, sizeof(line_buf)) != 0) {
int class_id = 0;
char class_name[MAX_CLASS_NAME_LEN] = {0};
if (strncmp(line_buf, "#class ", 7) != 0)
continue;
if (sscanf(line_buf, "#class %*s %d %31s", &class_id, class_name) != 2 ||
class_id < 1 || class_id > MAX_APP_TYPE)
continue;
strncpy(CLASS_NAME_TABLE[class_id - 1], class_name, MAX_CLASS_NAME_LEN - 1);
if (class_id > g_cur_class_num)
g_cur_class_num = class_id;
}
}
int check_time_valid(char *t)
{
if (!t)
return 0;
if (strlen(t) < 3 || strlen(t) > 5 || (!strstr(t, ":")))
return 0;
else
return 1;
}
int config_get_appfilter_enable(void)
{
int enable = 0;
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return -1;
enable = fwx_uci_get_int_value(ctx, "appfilter.global.enable");
if (enable < 0)
enable = 0;
uci_free_context(ctx);
return enable;
}
int config_get_lan_ip(char *lan_ip, int len)
{
int ret = 0;
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return -1;
ret = fwx_uci_get_value(ctx, "network.lan.ipaddr", lan_ip, len);
uci_free_context(ctx);
return ret;
}
int config_get_lan_mask(char *lan_mask, int len)
{
int ret = 0;
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return -1;
ret = fwx_uci_get_value(ctx, "network.lan.netmask", lan_mask, len);
uci_free_context(ctx);
return ret;
}
+35
View File
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_CONFIG_H__
#define __FWX_CONFIG_H__
#include <uci.h>
#define MAX_SUPPORT_APP_NUM 1024
#define MAX_CLASS_NAME_LEN 32
#include "fwx_user.h"
extern int g_cur_class_num;
extern int g_app_count;
extern char CLASS_NAME_TABLE[MAX_APP_TYPE][MAX_CLASS_NAME_LEN];
typedef struct app_name_info
{
int id;
char name[64];
} app_name_info_t;
void init_app_name_table(void);
void init_app_class_name_table(void);
char *get_app_name_by_id(int id);
int app_icon_exists_by_id(int id);
int appfilter_config_alloc(void);
int appfilter_config_free(void);
int config_get_appfilter_enable(void);
int config_get_lan_ip(char *lan_ip, int len);
int config_get_lan_mask(char *lan_mask, int len);
#endif
File diff suppressed because it is too large. Load diff
+19
View File
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef __FWX_CUSTOM_FEATURE_H__
#define __FWX_CUSTOM_FEATURE_H__
#include <stddef.h>
#include <json-c/json.h>
#define FWX_CUSTOM_FEATURE_PATH "/etc/fwxd/custom_feature.cfg"
int fwx_custom_feature_reload(void);
int fwx_custom_feature_send_to_kernel(int (*send_feature)(char *));
int fwx_custom_feature_add_app_names(void);
void fwx_custom_feature_append_class_apps(struct json_object **class_map,
size_t class_map_len);
struct json_object *fwx_api_get_custom_feature(struct json_object *req_obj);
struct json_object *fwx_api_get_custom_feature_class_list(struct json_object *req_obj);
struct json_object *fwx_api_set_custom_feature(struct json_object *req_obj);
#endif
+379
View File
@@ -0,0 +1,379 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "fwx_feature.h"
#define FEATURE_HEADER_SIZE 24U
#define FEATURE_FORMAT_VERSION 1U
#define FEATURE_ALGORITHM_XTEA_CTR 1U
static const unsigned char feature_magic[4] = {'F', 'W', 'X', 'B'};
static const uint32_t feature_key[4] = {
0x8f4c29a1U, 0x73b6d502U, 0xc14e87f3U, 0x2ad95b60U
};
static char *g_feature_data;
static size_t g_feature_data_len;
static void clear_memory(void *data, size_t len)
{
volatile unsigned char *p = data;
while (p && len-- > 0)
*p++ = 0;
}
static uint16_t get_le16(const unsigned char *p)
{
return (uint16_t)p[0] | ((uint16_t)p[1] << 8);
}
static uint32_t get_le32(const unsigned char *p)
{
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}
static uint64_t get_le64(const unsigned char *p)
{
return (uint64_t)get_le32(p) | ((uint64_t)get_le32(p + 4) << 32);
}
static void put_le16(unsigned char *p, uint16_t value)
{
p[0] = (unsigned char)value;
p[1] = (unsigned char)(value >> 8);
}
static void put_le32(unsigned char *p, uint32_t value)
{
p[0] = (unsigned char)value;
p[1] = (unsigned char)(value >> 8);
p[2] = (unsigned char)(value >> 16);
p[3] = (unsigned char)(value >> 24);
}
static void put_le64(unsigned char *p, uint64_t value)
{
put_le32(p, (uint32_t)value);
put_le32(p + 4, (uint32_t)(value >> 32));
}
static uint32_t feature_crc32(const unsigned char *data, size_t len)
{
uint32_t crc = 0xffffffffU;
size_t i;
int bit;
for (i = 0; i < len; i++) {
crc ^= data[i];
for (bit = 0; bit < 8; bit++)
crc = (crc >> 1) ^ (0xedb88320U & (0U - (crc & 1U)));
}
return ~crc;
}
static void xtea_encrypt_block(uint32_t block[2])
{
uint32_t v0 = block[0];
uint32_t v1 = block[1];
uint32_t sum = 0;
const uint32_t delta = 0x9e3779b9U;
int round;
for (round = 0; round < 32; round++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^
(sum + feature_key[sum & 3U]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^
(sum + feature_key[(sum >> 11) & 3U]);
}
block[0] = v0;
block[1] = v1;
}
static void xtea_ctr_crypt(unsigned char *data, size_t len, uint64_t nonce)
{
size_t offset = 0;
uint64_t counter = nonce;
while (offset < len) {
unsigned char stream[8];
uint32_t block[2];
size_t i;
block[0] = (uint32_t)counter;
block[1] = (uint32_t)(counter >> 32);
xtea_encrypt_block(block);
put_le32(stream, block[0]);
put_le32(stream + 4, block[1]);
for (i = 0; i < sizeof(stream) && offset < len; i++, offset++)
data[offset] ^= stream[i];
counter++;
}
}
static int read_file(const char *path, size_t max_len, unsigned char **data, size_t *len)
{
FILE *fp;
long file_len;
unsigned char *buf;
if (!path || !data || !len)
return -1;
fp = fopen(path, "rb");
if (!fp)
return -1;
if (fseek(fp, 0, SEEK_END) != 0 || (file_len = ftell(fp)) <= 0 ||
(size_t)file_len > max_len || fseek(fp, 0, SEEK_SET) != 0) {
fclose(fp);
return -1;
}
buf = malloc((size_t)file_len);
if (!buf) {
fclose(fp);
return -1;
}
if (fread(buf, 1, (size_t)file_len, fp) != (size_t)file_len) {
free(buf);
fclose(fp);
return -1;
}
fclose(fp);
*data = buf;
*len = (size_t)file_len;
return 0;
}
static int read_nonce(uint64_t *nonce)
{
FILE *fp;
unsigned char buf[8];
fp = fopen("/dev/urandom", "rb");
if (!fp)
return -1;
if (fread(buf, 1, sizeof(buf), fp) != sizeof(buf)) {
fclose(fp);
return -1;
}
fclose(fp);
*nonce = get_le64(buf);
return 0;
}
int fwx_feature_decrypt_file(const char *path, char **data, size_t *data_len)
{
unsigned char *file_data = NULL;
char *plain = NULL;
size_t file_len = 0;
uint32_t plain_len = 0;
uint32_t expected_crc;
uint64_t nonce;
int ret = -1;
if (!data || !data_len)
return -1;
*data = NULL;
*data_len = 0;
if (read_file(path, FEATURE_HEADER_SIZE + FWX_FEATURE_MAX_SIZE, &file_data, &file_len) != 0)
return -1;
if (file_len < FEATURE_HEADER_SIZE || memcmp(file_data, feature_magic, sizeof(feature_magic)) != 0 ||
file_data[4] != FEATURE_FORMAT_VERSION || file_data[5] != FEATURE_ALGORITHM_XTEA_CTR ||
get_le16(file_data + 6) != FEATURE_HEADER_SIZE)
goto out;
plain_len = get_le32(file_data + 8);
expected_crc = get_le32(file_data + 12);
nonce = get_le64(file_data + 16);
if (plain_len == 0 || plain_len > FWX_FEATURE_MAX_SIZE ||
file_len != FEATURE_HEADER_SIZE + (size_t)plain_len)
goto out;
plain = malloc((size_t)plain_len + 1U);
if (!plain)
goto out;
memcpy(plain, file_data + FEATURE_HEADER_SIZE, plain_len);
xtea_ctr_crypt((unsigned char *)plain, plain_len, nonce);
plain[plain_len] = '\0';
if (feature_crc32((const unsigned char *)plain, plain_len) != expected_crc)
goto out;
*data = plain;
*data_len = plain_len;
plain = NULL;
ret = 0;
out:
clear_memory(plain, plain_len);
free(plain);
free(file_data);
return ret;
}
static int copy_file_atomic(const char *source, const char *target,
const char *temporary)
{
unsigned char *data = NULL;
size_t data_len = 0;
FILE *fp = NULL;
int ret = -1;
if (read_file(source,
FEATURE_HEADER_SIZE + FWX_FEATURE_MAX_SIZE,
&data, &data_len) != 0)
return -1;
fp = fopen(temporary, "wb");
if (!fp || fwrite(data, 1, data_len, fp) != data_len ||
fflush(fp) != 0 || fsync(fileno(fp)) != 0)
goto out;
if (fclose(fp) != 0) {
fp = NULL;
goto out;
}
fp = NULL;
if (chmod(temporary, 0644) != 0 ||
rename(temporary, target) != 0)
goto out;
ret = 0;
out:
if (fp)
fclose(fp);
if (ret != 0)
unlink(temporary);
free(data);
return ret;
}
int fwx_feature_validate_candidate(char *version, size_t version_len,
char *format, size_t format_len)
{
char *feature_data = NULL;
size_t feature_len = 0;
size_t offset = 0;
char line[1024];
char parsed_version[64] = {0};
char parsed_format[32] = {0};
int line_ret;
int ret = -1;
if (!version || version_len == 0 || !format || format_len == 0)
return -1;
version[0] = '\0';
format[0] = '\0';
if (fwx_feature_decrypt_file(FWX_FEATURE_CANDIDATE_PATH,
&feature_data, &feature_len) < 0)
return FWX_FEATURE_VALIDATE_DECRYPT_FAILED;
while ((line_ret = fwx_feature_next_line(feature_data, feature_len,
&offset, line, sizeof(line))) != 0) {
if (line_ret < 0)
continue;
if (!strncmp(line, "#version ", 9))
sscanf(line, "#version %63s", parsed_version);
else if (!strncmp(line, "#format ", 8))
sscanf(line, "#format %31s", parsed_format);
if (parsed_version[0] && parsed_format[0])
break;
}
snprintf(version, version_len, "%s", parsed_version);
snprintf(format, format_len, "%s", parsed_format);
if (parsed_version[0] && !strcmp(parsed_format, FWX_FEATURE_DATA_FORMAT))
ret = 0;
else
ret = FWX_FEATURE_VALIDATE_INFO_INVALID;
clear_memory(feature_data, feature_len);
free(feature_data);
return ret;
}
int fwx_feature_apply_candidate(void)
{
const char *backup_temporary = FWX_FEATURE_BACKUP_PATH ".tmp";
const char *target_temporary = FWX_FEATURE_BIN_PATH ".tmp";
if (access(FWX_FEATURE_BIN_PATH, F_OK) == 0 &&
copy_file_atomic(FWX_FEATURE_BIN_PATH, FWX_FEATURE_BACKUP_PATH,
backup_temporary) < 0)
return FWX_FEATURE_APPLY_BACKUP_FAILED;
if (copy_file_atomic(FWX_FEATURE_CANDIDATE_PATH, FWX_FEATURE_BIN_PATH,
target_temporary) < 0)
return FWX_FEATURE_APPLY_TARGET_FAILED;
unlink(FWX_FEATURE_CANDIDATE_PATH);
return 0;
}
int fwx_feature_process_candidate(char *version, size_t version_len,
char *format, size_t format_len,
int *status_code,
void (*stage_callback)(const char *stage))
{
int ret;
if (status_code)
*status_code = 400;
ret = fwx_feature_validate_candidate(version, version_len, format, format_len);
if (ret < 0) {
if (status_code)
*status_code = 401;
unlink(FWX_FEATURE_CANDIDATE_PATH);
return ret;
}
if (stage_callback)
stage_callback("applying");
ret = fwx_feature_apply_candidate();
if (ret < 0) {
unlink(FWX_FEATURE_CANDIDATE_PATH);
return ret;
}
if (status_code)
*status_code = 200;
return 0;
}
int fwx_feature_restore_backup(void)
{
return copy_file_atomic(FWX_FEATURE_BACKUP_PATH, FWX_FEATURE_BIN_PATH,
FWX_FEATURE_BIN_PATH ".restore");
}
void fwx_feature_replace_data(char *data, size_t data_len)
{
clear_memory(g_feature_data, g_feature_data_len);
free(g_feature_data);
g_feature_data = data;
g_feature_data_len = data_len;
}
const char *fwx_feature_get_data(size_t *data_len)
{
if (data_len)
*data_len = g_feature_data_len;
return g_feature_data;
}
int fwx_feature_next_line(const char *data, size_t data_len, size_t *offset,
char *line, size_t line_size)
{
size_t start;
size_t end;
size_t line_len;
if (!data || !offset || !line || line_size == 0 || *offset >= data_len)
return 0;
start = *offset;
end = start;
while (end < data_len && data[end] != '\n')
end++;
*offset = end < data_len ? end + 1U : end;
line_len = end - start;
if (line_len > 0 && data[start + line_len - 1U] == '\r')
line_len--;
if (line_len >= line_size) {
line[0] = '\0';
return -1;
}
memcpy(line, data + start, line_len);
line[line_len] = '\0';
return 1;
}
+37
View File
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef __FWX_FEATURE_H__
#define __FWX_FEATURE_H__
#include <stddef.h>
#define FWX_FEATURE_BIN_PATH "/etc/fwxd/feature.bin"
#define FWX_FEATURE_BACKUP_PATH "/etc/fwxd/feature.bin.bak"
#define FWX_FEATURE_LIST_CACHE_PATH "/etc/fwxd/feature_list.json"
#define FWX_FEATURE_CANDIDATE_PATH "/tmp/feature.bin"
#define FWX_FEATURE_INFO_PATH "/tmp/feature_info.json"
#define FWX_FEATURE_UPGRADE_STATUS_PATH "/tmp/feature_upgrade.status"
#define FWX_FEATURE_DATA_FORMAT "v4.0"
#define FWX_FEATURE_MAX_SIZE (20U * 1024U * 1024U)
#define FWX_FEATURE_VALIDATE_DECRYPT_FAILED -1
#define FWX_FEATURE_VALIDATE_INFO_INVALID -2
#define FWX_FEATURE_APPLY_BACKUP_FAILED -1
#define FWX_FEATURE_APPLY_TARGET_FAILED -2
int fwx_feature_decrypt_file(const char *path, char **data, size_t *data_len);
int fwx_feature_validate_candidate(char *version, size_t version_len,
char *format, size_t format_len);
int fwx_feature_apply_candidate(void);
int fwx_feature_restore_backup(void);
int fwx_feature_process_candidate(char *version, size_t version_len,
char *format, size_t format_len,
int *status_code,
void (*stage_callback)(const char *stage));
int test_encrypt_feature_file(void);
void fwx_feature_replace_data(char *data, size_t data_len);
const char *fwx_feature_get_data(size_t *data_len);
int fwx_feature_next_line(const char *data, size_t data_len, size_t *offset,
char *line, size_t line_size);
#endif
File diff suppressed because it is too large. Load diff
+17
View File
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef __FWX_FEATURE_ONLINE_H__
#define __FWX_FEATURE_ONLINE_H__
#include <json-c/json.h>
#define FWX_FEATURE_UPGRADE_LOCK_PATH "/tmp/feature_upgrade.lock"
int fwx_feature_online_init(void);
void fwx_feature_online_cleanup(void);
struct json_object *fwx_api_get_feature_online_config(struct json_object *req_obj);
struct json_object *fwx_api_set_feature_online_config(struct json_object *req_obj);
struct json_object *fwx_api_get_feature_online_list(struct json_object *req_obj);
struct json_object *fwx_api_start_feature_online_update(struct json_object *req_obj);
struct json_object *fwx_api_get_feature_online_update_status(struct json_object *req_obj);
#endif
+140
View File
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <json-c/json.h>
#include <uci.h>
#include "fwx.h"
#include "fwx_firewall.h"
#include "fwx_uci.h"
static struct json_object *get_basic_setting(void)
{
struct uci_context *ctx = uci_alloc_context();
struct json_object *data_obj = NULL;
struct json_object *basic_obj = NULL;
char syn_flood[16] = {0};
char input[32] = {0};
char output[32] = {0};
char forward[32] = {0};
char fullcone[16] = {0};
if (!ctx) {
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
if (fwx_uci_get_value(ctx, "firewall.@defaults[0].syn_flood",
syn_flood, sizeof(syn_flood)) != UCI_OK) {
LOG_ERROR("Failed to read firewall defaults\n");
uci_free_context(ctx);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
fwx_uci_get_value(ctx, "firewall.@defaults[0].input", input, sizeof(input));
fwx_uci_get_value(ctx, "firewall.@defaults[0].output", output, sizeof(output));
fwx_uci_get_value(ctx, "firewall.@defaults[0].forward", forward, sizeof(forward));
fwx_uci_get_value(ctx, "firewall.@defaults[0].fullcone", fullcone, sizeof(fullcone));
data_obj = json_object_new_object();
basic_obj = json_object_new_object();
if (!data_obj || !basic_obj) {
if (data_obj) {
json_object_put(data_obj);
}
if (basic_obj) {
json_object_put(basic_obj);
}
uci_free_context(ctx);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
json_object_object_add(basic_obj, "syn_flood",
json_object_new_int(atoi(syn_flood)));
json_object_object_add(basic_obj, "input", json_object_new_string(input));
json_object_object_add(basic_obj, "output", json_object_new_string(output));
json_object_object_add(basic_obj, "forward", json_object_new_string(forward));
json_object_object_add(basic_obj, "fullcone",
json_object_new_int(atoi(fullcone)));
json_object_object_add(data_obj, "basic", basic_obj);
uci_free_context(ctx);
return fwx_gen_api_response_data(API_CODE_SUCCESS, data_obj);
}
static struct json_object *set_basic_setting(struct json_object *req_obj)
{
struct json_object *fullcone_obj = NULL;
struct uci_context *ctx = NULL;
int fullcone = 0;
if (!json_object_object_get_ex(req_obj, "fullcone", &fullcone_obj)) {
LOG_ERROR("FullCone setting is missing\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
if (!json_object_is_type(fullcone_obj, json_type_int) &&
!json_object_is_type(fullcone_obj, json_type_boolean)) {
LOG_ERROR("FullCone setting type is invalid\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
fullcone = json_object_get_int(fullcone_obj);
LOG_INFO("set_firewall parsed fullcone: %d\n", fullcone);
if (fullcone != 0 && fullcone != 1) {
LOG_ERROR("Invalid FullCone setting: %d\n", fullcone);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
ctx = uci_alloc_context();
if (!ctx) {
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
fwx_uci_set_int_value(ctx, "firewall.@defaults[0].fullcone", fullcone);
fwx_uci_commit(ctx, "firewall");
uci_free_context(ctx);
system("/etc/init.d/firewall reload >/dev/null 2>&1");
LOG_INFO("set_firewall basic setting applied\n");
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
struct json_object *fwx_api_get_firewall(struct json_object *req_obj)
{
(void)req_obj;
return get_basic_setting();
}
struct json_object *fwx_api_set_firewall(struct json_object *req_obj)
{
struct json_object *action_obj = NULL;
struct json_object *basic_obj = NULL;
struct json_object *response_obj = NULL;
const char *action = NULL;
LOG_INFO("fwx_api_set_firewall request: %s\n",
req_obj ? json_object_to_json_string(req_obj) : "null");
if (!req_obj ||
!json_object_object_get_ex(req_obj, "action", &action_obj)) {
LOG_ERROR("Firewall setting request is invalid\n");
response_obj = fwx_gen_api_response_data(API_CODE_ERROR, NULL);
goto done;
}
action = json_object_get_string(action_obj);
if (action && strcmp(action, "set_basic") == 0) {
if (!json_object_object_get_ex(req_obj, "basic", &basic_obj) ||
!json_object_is_type(basic_obj, json_type_object)) {
LOG_ERROR("Firewall basic setting is invalid\n");
response_obj = fwx_gen_api_response_data(API_CODE_ERROR, NULL);
goto done;
}
response_obj = set_basic_setting(basic_obj);
goto done;
}
LOG_ERROR("Unsupported firewall action: %s\n", action ? action : "");
response_obj = fwx_gen_api_response_data(API_CODE_ERROR, NULL);
done:
LOG_INFO("fwx_api_set_firewall response: %s\n",
response_obj ? json_object_to_json_string(response_obj) : "null");
return response_obj;
}
+10
View File
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef __FWX_FIREWALL_H__
#define __FWX_FIREWALL_H__
#include <json-c/json.h>
struct json_object *fwx_api_get_firewall(struct json_object *req_obj);
struct json_object *fwx_api_set_firewall(struct json_object *req_obj);
#endif
File diff suppressed because it is too large. Load diff
+26
View File
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_MAC_FILTER_H__
#define __FWX_MAC_FILTER_H__
#include "fwx.h"
struct json_object *fwx_api_get_mac_filter_rules(struct json_object *req_obj);
struct json_object *fwx_api_add_mac_filter_rule(struct json_object *req_obj);
struct json_object *fwx_api_update_mac_filter_rule(struct json_object *req_obj);
struct json_object *fwx_api_delete_mac_filter_rule(struct json_object *req_obj);
struct json_object *fwx_api_get_mac_filter_whitelist(struct json_object *req_obj);
struct json_object *fwx_api_add_mac_filter_whitelist(struct json_object *req_obj);
struct json_object *fwx_api_del_mac_filter_whitelist(struct json_object *req_obj);
struct json_object *fwx_api_get_mac_filter_adv(struct json_object *req_obj);
struct json_object *fwx_api_set_mac_filter_adv(struct json_object *req_obj);
#endif
+348
View File
@@ -0,0 +1,348 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json-c/json.h>
#include "fwx_user.h"
#include "fwx_netlink.h"
#include "fwx.h"
#define MAX_NL_RCV_BUF_SIZE 4096
#define REPORT_INTERVAL_SECS 60
extern traffic_stat_t g_global_hourly_traffic[HOURS_PER_DAY];
extern u_int32_t g_global_traffic_date;
void fwx_netlink_handler(struct uloop_fd *u, unsigned int ev)
{
int ret;
int i;
char buf[MAX_NL_RCV_BUF_SIZE];
struct sockaddr_nl nladdr;
struct iovec iov = {buf, sizeof(buf)};
struct nlmsghdr *h;
int type;
int id;
const char *mac = NULL;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
do
{
ret = recvmsg(u->fd, &msg, 0);
} while ((-1 == ret) && (EINTR == errno));
if (ret < 0)
{
printf("recv msg error\n");
return;
}
else if (0 == ret)
{
return;
}
h = (struct nlmsghdr *)buf;
char *kmsg = (char *)NLMSG_DATA(h);
struct fwx_nl_msg_hdr *af_hdr = (struct fwx_nl_msg_hdr *)kmsg;
if (af_hdr->magic != 0xa0b0c0d0)
{
printf("magic error %x\n", af_hdr->magic);
return;
}
if (af_hdr->len <= 0 || af_hdr->len >= MAX_FWX_NETLINK_MSG_LEN)
{
printf("data len error\n");
return;
}
char *kdata = kmsg + sizeof(struct fwx_nl_msg_hdr);
struct json_object *root = json_tokener_parse(kdata);
if (!root)
{
LOG_ERROR("parse json failed:%s", kdata);
return;
}
struct json_object *mac_obj = json_object_object_get(root, "mac");
if (!mac_obj)
{
printf("parse mac obj failed\n");
json_object_put(root);
return;
}
mac = json_object_get_string(mac_obj);
client_node_t *node = find_client_node(mac);
if (!node)
{
node = add_client_node(mac);
if (!node)
{
printf("add dev node failed\n");
json_object_put(root);
return;
}
}
struct json_object *ip_obj = json_object_object_get(root, "ip");
if (ip_obj)
strncpy(node->ip, json_object_get_string(ip_obj), sizeof(node->ip));
struct json_object *ipv6_obj = json_object_object_get(root, "ipv6");
if (ipv6_obj) {
const char *ipv6_str = json_object_get_string(ipv6_obj);
if (ipv6_str && strlen(ipv6_str) > 0) {
strncpy(node->ipv6, ipv6_str, sizeof(node->ipv6) - 1);
node->ipv6[sizeof(node->ipv6) - 1] = '\0';
LOG_DEBUG("fwx_netlink: received ipv6=%s for %s\n", node->ipv6, mac);
} else {
node->ipv6[0] = '\0';
}
} else {
node->ipv6[0] = '\0';
}
struct json_object *active_obj = json_object_object_get(root, "active");
int prev_active = node->active;
if (active_obj) {
node->active = json_object_get_int(active_obj);
LOG_DEBUG("fwx_netlink: received active=%d for %s\n", node->active, mac);
}
struct json_object *up_flow_obj = json_object_object_get(root, "up_flow");
struct json_object *down_flow_obj = json_object_object_get(root, "down_flow");
unsigned long long total_up_bytes = 0;
unsigned long long total_down_bytes = 0;
if (up_flow_obj) {
total_up_bytes = (unsigned long long)json_object_get_int64(up_flow_obj) * 1024;
}
if (down_flow_obj) {
total_down_bytes = (unsigned long long)json_object_get_int64(down_flow_obj) * 1024;
}
LOG_DEBUG("fwx_netlink: received flow data for %s: up_flow=%llu KB (%llu bytes), down_flow=%llu KB (%llu bytes)\n",
mac, total_up_bytes / 1024, total_up_bytes, total_down_bytes / 1024, total_down_bytes);
struct timeval cur_time;
gettimeofday(&cur_time, NULL);
time_t cur_time_t = cur_time.tv_sec;
u_int32_t today_start = get_today_start_timestamp();
if ((u_int32_t)cur_time.tv_sec >= today_start && ((u_int32_t)cur_time.tv_sec - today_start) < 120) {
json_object_put(root);
return;
}
struct tm *tm_info = localtime(&cur_time_t);
int hour = -1;
if (tm_info) {
hour = tm_info->tm_hour;
}
daily_hourly_stat_t *today_stat = NULL;
if (hour >= 0 && hour < HOURS_PER_DAY) {
today_stat = get_today_stat(node);
if (node->online == 1) {
if (today_stat) {
today_stat->hourly_online_time[hour] += REPORT_INTERVAL_SECS;
LOG_DEBUG("fwx_netlink: updated hourly_online_time[%d] for %s: +%d seconds\n",
hour, mac, REPORT_INTERVAL_SECS);
}
}
if (node->active == 1) {
if (today_stat) {
today_stat->hourly_active_time[hour] += REPORT_INTERVAL_SECS;
LOG_DEBUG("fwx_netlink: updated hourly_active_time[%d] for %s: +%d seconds\n",
hour, mac, REPORT_INTERVAL_SECS);
}
}
update_online_session_activity(node, REPORT_INTERVAL_SECS, node->active == 1 ? REPORT_INTERVAL_SECS : 0);
}
if (hour >= 0 && hour < HOURS_PER_DAY && (total_up_bytes > 0 || total_down_bytes > 0)) {
if (today_stat) {
today_stat->hourly_traffic[hour].up_bytes += total_up_bytes;
today_stat->hourly_traffic[hour].down_bytes += total_down_bytes;
LOG_DEBUG("fwx_netlink: updated hourly_traffic[%d] for %s: up_bytes=%llu, down_bytes=%llu\n",
hour, mac, today_stat->hourly_traffic[hour].up_bytes, today_stat->hourly_traffic[hour].down_bytes);
}
u_int32_t today = get_today_start_timestamp();
if (g_global_traffic_date != today) {
memset(g_global_hourly_traffic, 0, sizeof(g_global_hourly_traffic));
g_global_traffic_date = today;
}
g_global_hourly_traffic[hour].up_bytes += total_up_bytes;
g_global_hourly_traffic[hour].down_bytes += total_down_bytes;
}
update_online_session_flow(node, total_up_bytes, total_down_bytes);
struct json_object *visit_array = json_object_object_get(root, "visit_info");
if (!visit_array)
{
json_object_put(root);
return;
}
for (i = 0; i < json_object_array_length(visit_array); i++)
{
struct json_object *visit_obj = json_object_array_get_idx(visit_array, i);
struct json_object *appid_obj = json_object_object_get(visit_obj, "appid");
struct json_object *action_obj = json_object_object_get(visit_obj, "latest_action");
int appid = json_object_get_int(appid_obj);
int action = json_object_get_int(action_obj);
type = appid / 1000;
id = appid % 1000;
if (id <= 0 || type <= 0)
continue;
update_online_session_recent_app(node, appid);
visit_stat_t *stat_node = NULL;
int found_stat = 0;
list_for_each_entry(stat_node, &node->stat_list, list) {
if (stat_node->appid == appid) {
stat_node->total_time += REPORT_INTERVAL_SECS;
found_stat = 1;
break;
}
}
if (!found_stat) {
stat_node = (visit_stat_t *)calloc(1, sizeof(visit_stat_t));
if (stat_node) {
stat_node->appid = appid;
stat_node->total_time = REPORT_INTERVAL_SECS;
INIT_LIST_HEAD(&stat_node->list);
list_add(&stat_node->list, &node->stat_list);
}
}
update_global_app_type_stats(appid, REPORT_INTERVAL_SECS);
visit_info_t *p = NULL;
int found = 0;
int cur_time_sec = cur_time.tv_sec;
list_for_each_entry(p, &node->online_visit, visit) {
if (p->appid == appid && p->action == action) {
p->latest_time = cur_time_sec;
found = 1;
break;
}
}
if (!found) {
p = (visit_info_t *)calloc(1, sizeof(visit_info_t));
if (!p)
continue;
p->appid = appid;
p->first_time = cur_time_sec;
p->latest_time = cur_time_sec;
p->action = action;
INIT_LIST_HEAD(&p->visit);
add_visit_info_node(&node->online_visit, p);
}
}
json_object_put(root);
}
#define MAX_NL_MSG_LEN 1024
int fwx_nl_send_msg_to_kernel(int fd, void *msg, int len)
{
struct sockaddr_nl saddr, daddr;
memset(&daddr, 0, sizeof(daddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0; // to kernel
daddr.nl_groups = 0;
int ret = 0;
struct nlmsghdr *nlh = NULL;
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_NL_MSG_LEN));
nlh->nlmsg_len = NLMSG_SPACE(MAX_NL_MSG_LEN);
nlh->nlmsg_flags = 0;
nlh->nlmsg_type = 0;
nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = DEFAULT_FWX_NL_PID;
char msg_buf[MAX_NL_MSG_LEN] = {0};
struct fwx_nl_msg_hdr *hdr = (struct fwx_nl_msg_hdr *)msg_buf;
hdr->magic = 0xa0b0c0d0;
hdr->len = len;
char *p_data = msg_buf + sizeof(struct fwx_nl_msg_hdr);
memcpy(p_data, msg, len);
memcpy(NLMSG_DATA(nlh), msg_buf, len + sizeof(struct fwx_nl_msg_hdr));
ret = sendto(fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
free(nlh);
if (!ret)
{
perror("sendto error\n");
return -1;
}
return 0;
}
int fwx_netlink_init(void)
{
int fd;
struct sockaddr_nl nls;
fd = socket(AF_NETLINK, SOCK_RAW, FWX_NETLINK_ID);
if (fd < 0)
{
LOG_DEBUG("Connect netlink %d failed %s\n", FWX_NETLINK_ID, strerror(errno));
return -1;
}
memset(&nls, 0, sizeof(struct sockaddr_nl));
nls.nl_pid = DEFAULT_FWX_NL_PID;
nls.nl_groups = 0;
nls.nl_family = AF_NETLINK;
if (bind(fd, (void *)&nls, sizeof(struct sockaddr_nl)))
{
LOG_DEBUG("Bind failed %s\n", strerror(errno));
return -1;
}
return fd;
}
+42
View File
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_NETLINK_H__
#define __FWX_NETLINK_H__
#define DEFAULT_FWX_NL_PID 999
#define FWX_NETLINK_ID 29
#define MAX_FWX_NETLINK_MSG_LEN 1024
#define MAX_AF_MSG_DATA_LEN 800
#define MAX_FEATURE_LINE_LEN 800
struct fwx_nl_msg_hdr
{
int magic;
int len;
};
enum E_FWX_NL_MSG_TYPE
{
FWX_NL_MSG_INIT,
FWX_NL_MSG_ADD_FEATURE,
FWX_NL_MSG_CLEAN_FEATURE,
FWX_NL_MSG_FEATURE_LOAD_DONE,
FWX_NL_MSG_MAX
};
typedef struct fwx_nl_msg
{
int action;
} fwx_nl_msg_t;
typedef struct fwx_nl_feature_msg{
fwx_nl_msg_t hdr;
char feature[MAX_FEATURE_LINE_LEN];
} fwx_nl_feature_msg_t;
int fwx_netlink_init(void);
void fwx_netlink_handler(struct uloop_fd *u, unsigned int ev);
int fwx_nl_send_msg_to_kernel(int fd, void *msg, int len);
#endif
File diff suppressed because it is too large. Load diff
+46
View File
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_NETWORK_H__
#define __FWX_NETWORK_H__
#include <json-c/json.h>
#define MAX_INET_ADDR_LEN 32
typedef struct iface_status{
int proto;
char ip[MAX_INET_ADDR_LEN];
char mask[MAX_INET_ADDR_LEN];
char gateway[MAX_INET_ADDR_LEN];
char dns1[MAX_INET_ADDR_LEN];
char dns2[MAX_INET_ADDR_LEN];
}iface_status_t;
int get_iface_status(char *ifname, iface_status_t *status);
char *get_interface_status_buf(char *ifname);
char *cidr2str(int cidr);
struct json_object *fwx_api_get_lan_list(struct json_object *req_obj);
struct json_object *fwx_api_add_lan(struct json_object *req_obj);
struct json_object *fwx_api_mod_lan(struct json_object *req_obj);
struct json_object *fwx_api_del_lan(struct json_object *req_obj);
struct json_object *fwx_api_get_wan_list(struct json_object *req_obj);
struct json_object *fwx_api_add_wan(struct json_object *req_obj);
struct json_object *fwx_api_mod_wan(struct json_object *req_obj);
struct json_object *fwx_api_del_wan(struct json_object *req_obj);
struct json_object *fwx_api_get_lan_info(struct json_object *req_obj);
struct json_object *fwx_api_set_lan_info(struct json_object *req_obj);
struct json_object *fwx_api_get_wan_info(struct json_object *req_obj);
struct json_object *fwx_api_set_wan_info(struct json_object *req_obj);
struct json_object *fwx_api_get_work_mode(struct json_object *req_obj);
struct json_object *fwx_api_set_work_mode(struct json_object *req_obj);
#endif
+462
View File
@@ -0,0 +1,462 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <uci.h>
#include "fwx_user.h"
#include "fwx_netlink.h"
#include "fwx_ubus.h"
#include "fwx_config.h"
#include <time.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "fwx.h"
#include <stdio.h>
#include <json-c/json.h>
#include "fwx_record.h"
#define RECORD_WHITELIST_STATE_FILE "/tmp/record_whitelist_state"
#define RECORD_WHITELIST_SECTION_TYPE "whitelist"
#define RECORD_WHITELIST_UCI_KEY "fwx_record.@whitelist[0].whitelist"
#define RECORD_WHITELIST_LEGACY_SECTION_TYPE "record"
#define RECORD_WHITELIST_LEGACY_UCI_KEY "fwx_record.@record[0].whitelist"
static void set_state_file(const char *file_path)
{
FILE *fd = fopen(file_path, "w");
if (fd) {
fprintf(fd, "1");
fclose(fd);
LOG_DEBUG("Set state file: %s\n", file_path);
} else {
LOG_ERROR("Failed to set state file: %s\n", file_path);
}
}
static void normalize_mac(const char *src, char *dst, int dst_len)
{
int i = 0;
int start = 0;
int end = 0;
if (!src || !dst || dst_len <= 0) {
return;
}
while (src[start] == ' ' || src[start] == '\t' || src[start] == '\r' || src[start] == '\n') {
start++;
}
end = (int)strlen(src);
while (end > start &&
(src[end - 1] == ' ' || src[end - 1] == '\t' || src[end - 1] == '\r' || src[end - 1] == '\n')) {
end--;
}
for (i = start; i < end && (i - start) < (dst_len - 1); i++) {
dst[i - start] = (char)toupper((unsigned char)src[i]);
}
dst[i - start] = '\0';
}
static int is_valid_mac(const char *mac)
{
int i;
if (!mac || strlen(mac) != 17) {
return 0;
}
for (i = 0; i < 17; i++) {
if (i % 3 == 2) {
if (mac[i] != ':') {
return 0;
}
} else if (!isxdigit((unsigned char)mac[i])) {
return 0;
}
}
return 1;
}
static int mac_exists_in_array(struct json_object *array_obj, const char *mac)
{
int i;
int len;
if (!array_obj || !mac) {
return 0;
}
len = json_object_array_length(array_obj);
for (i = 0; i < len; i++) {
struct json_object *mac_obj = json_object_array_get_idx(array_obj, i);
const char *exist_mac = json_object_get_string(mac_obj);
if (exist_mac && strcasecmp(exist_mac, mac) == 0) {
return 1;
}
}
return 0;
}
static int ensure_record_section(struct uci_context *uci_ctx)
{
int num = 0;
int legacy_num = 0;
char whitelist_buf[4096] = {0};
char token_buf[4096] = {0};
char *token = NULL;
char *save_ptr = NULL;
char norm_mac[32] = {0};
if (!uci_ctx) {
return -1;
}
num = fwx_uci_get_list_num(uci_ctx, "fwx_record", RECORD_WHITELIST_SECTION_TYPE);
if (num > 0) {
return 0;
}
legacy_num = fwx_uci_get_list_num(uci_ctx, "fwx_record", RECORD_WHITELIST_LEGACY_SECTION_TYPE);
if (fwx_uci_add_section(uci_ctx, "fwx_record", RECORD_WHITELIST_SECTION_TYPE) != UCI_OK) {
LOG_ERROR("Failed to add fwx_record whitelist section\n");
return -1;
}
if (legacy_num > 0 &&
fwx_uci_get_list_value(uci_ctx, RECORD_WHITELIST_LEGACY_UCI_KEY,
whitelist_buf, sizeof(whitelist_buf), " ") == 0) {
snprintf(token_buf, sizeof(token_buf), "%s", whitelist_buf);
token = strtok_r(token_buf, " ", &save_ptr);
while (token) {
memset(norm_mac, 0, sizeof(norm_mac));
normalize_mac(token, norm_mac, sizeof(norm_mac));
if (is_valid_mac(norm_mac)) {
fwx_uci_add_list(uci_ctx, RECORD_WHITELIST_UCI_KEY, norm_mac);
}
token = strtok_r(NULL, " ", &save_ptr);
}
}
if (fwx_uci_commit(uci_ctx, "fwx_record") != UCI_OK) {
LOG_ERROR("Failed to commit fwx_record section\n");
return -1;
}
return 0;
}
static int load_record_whitelist_from_uci(struct uci_context *uci_ctx, struct json_object *mac_array)
{
char whitelist_buf[4096] = {0};
char norm_mac[32] = {0};
char *token = NULL;
char *save_ptr = NULL;
int count = 0;
if (!uci_ctx || !mac_array) {
return 0;
}
if (fwx_uci_get_list_value(uci_ctx, RECORD_WHITELIST_UCI_KEY, whitelist_buf, sizeof(whitelist_buf), " ") != 0) {
return 0;
}
token = strtok_r(whitelist_buf, " ", &save_ptr);
while (token) {
memset(norm_mac, 0, sizeof(norm_mac));
normalize_mac(token, norm_mac, sizeof(norm_mac));
if (is_valid_mac(norm_mac) && !mac_exists_in_array(mac_array, norm_mac)) {
json_object_array_add(mac_array, json_object_new_string(norm_mac));
count++;
}
token = strtok_r(NULL, " ", &save_ptr);
}
return count;
}
struct json_object *fwx_api_get_record_whitelist(struct json_object *req_obj)
{
int i;
int page = 1;
int page_size = 15;
int total_num = 0;
int total_page = 1;
int start_idx = 0;
int end_idx = 0;
struct json_object *page_obj = NULL;
struct json_object *page_size_obj = NULL;
struct json_object *data_obj = json_object_new_object();
struct json_object *all_list_obj = json_object_new_array();
struct json_object *page_list_obj = json_object_new_array();
struct uci_context *uci_ctx = NULL;
if (req_obj) {
page_obj = json_object_object_get(req_obj, "page");
page_size_obj = json_object_object_get(req_obj, "page_size");
if (page_obj) {
page = json_object_get_int(page_obj);
if (page < 1) {
page = 1;
}
}
if (page_size_obj) {
page_size = json_object_get_int(page_size_obj);
if (page_size < 1) {
page_size = 15;
}
if (page_size > 200) {
page_size = 200;
}
}
}
uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
json_object_put(data_obj);
json_object_put(all_list_obj);
json_object_put(page_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
ensure_record_section(uci_ctx);
load_record_whitelist_from_uci(uci_ctx, all_list_obj);
uci_free_context(uci_ctx);
total_num = json_object_array_length(all_list_obj);
total_page = (total_num + page_size - 1) / page_size;
if (total_page < 1) {
total_page = 1;
}
if (page > total_page) {
page = total_page;
}
start_idx = (page - 1) * page_size;
end_idx = start_idx + page_size;
if (end_idx > total_num) {
end_idx = total_num;
}
for (i = start_idx; i < end_idx; i++) {
struct json_object *item_obj = json_object_new_object();
const char *mac = NULL;
client_node_t *dev = NULL;
mac = json_object_get_string(json_object_array_get_idx(all_list_obj, i));
if (!mac) {
continue;
}
dev = find_client_node((char *)mac);
json_object_object_add(item_obj, "mac", json_object_new_string(mac));
if (dev) {
json_object_object_add(item_obj, "nickname", json_object_new_string(dev->nickname));
json_object_object_add(item_obj, "hostname", json_object_new_string(dev->hostname));
} else {
json_object_object_add(item_obj, "nickname", json_object_new_string(""));
json_object_object_add(item_obj, "hostname", json_object_new_string(""));
}
json_object_array_add(page_list_obj, item_obj);
}
json_object_object_add(data_obj, "total_num", json_object_new_int(total_num));
json_object_object_add(data_obj, "total_page", json_object_new_int(total_page));
json_object_object_add(data_obj, "page", json_object_new_int(page));
json_object_object_add(data_obj, "page_size", json_object_new_int(page_size));
json_object_object_add(data_obj, "list", page_list_obj);
json_object_put(all_list_obj);
return fwx_gen_api_response_data(API_CODE_SUCCESS, data_obj);
}
struct json_object *fwx_api_add_record_whitelist(struct json_object *req_obj)
{
int i;
int added = 0;
int mac_list_len = 0;
char norm_mac[32] = {0};
struct json_object *mac_list_obj = NULL;
struct json_object *exist_list_obj = json_object_new_array();
struct uci_context *uci_ctx = NULL;
if (!req_obj) {
json_object_put(exist_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
mac_list_obj = json_object_object_get(req_obj, "mac_list");
if (!mac_list_obj) {
LOG_ERROR("mac_list_obj is NULL\n");
json_object_put(exist_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
json_object_put(exist_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
if (ensure_record_section(uci_ctx) != 0) {
uci_free_context(uci_ctx);
json_object_put(exist_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
load_record_whitelist_from_uci(uci_ctx, exist_list_obj);
mac_list_len = json_object_array_length(mac_list_obj);
for (i = 0; i < mac_list_len; i++) {
struct json_object *mac_item = json_object_array_get_idx(mac_list_obj, i);
const char *raw_mac = json_object_get_string(mac_item);
memset(norm_mac, 0, sizeof(norm_mac));
normalize_mac(raw_mac, norm_mac, sizeof(norm_mac));
if (!is_valid_mac(norm_mac)) {
continue;
}
if (mac_exists_in_array(exist_list_obj, norm_mac)) {
continue;
}
fwx_uci_add_list(uci_ctx, RECORD_WHITELIST_UCI_KEY, norm_mac);
json_object_array_add(exist_list_obj, json_object_new_string(norm_mac));
added++;
}
if (added > 0) {
fwx_uci_commit(uci_ctx, "fwx_record");
set_state_file(RECORD_WHITELIST_STATE_FILE);
}
uci_free_context(uci_ctx);
json_object_put(exist_list_obj);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
struct json_object *fwx_api_del_record_whitelist(struct json_object *req_obj)
{
int found = 0;
char norm_mac[32] = {0};
struct json_object *mac_obj = NULL;
struct json_object *exist_list_obj = json_object_new_array();
struct uci_context *uci_ctx = NULL;
if (!req_obj) {
json_object_put(exist_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
mac_obj = json_object_object_get(req_obj, "mac");
if (!mac_obj) {
LOG_ERROR("mac_obj is NULL\n");
json_object_put(exist_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
normalize_mac(json_object_get_string(mac_obj), norm_mac, sizeof(norm_mac));
if (!is_valid_mac(norm_mac)) {
LOG_ERROR("invalid mac: %s\n", norm_mac);
json_object_put(exist_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
json_object_put(exist_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
if (ensure_record_section(uci_ctx) != 0) {
uci_free_context(uci_ctx);
json_object_put(exist_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
load_record_whitelist_from_uci(uci_ctx, exist_list_obj);
if (mac_exists_in_array(exist_list_obj, norm_mac)) {
fwx_uci_del_list(uci_ctx, RECORD_WHITELIST_UCI_KEY, norm_mac);
fwx_uci_commit(uci_ctx, "fwx_record");
set_state_file(RECORD_WHITELIST_STATE_FILE);
found = 1;
}
uci_free_context(uci_ctx);
json_object_put(exist_list_obj);
LOG_DEBUG("Delete record whitelist mac=%s, found=%d\n", norm_mac, found);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
struct json_object *fwx_api_set_record_whitelist(struct json_object *req_obj)
{
int i;
int mac_list_len = 0;
struct json_object *mac_list_obj = NULL;
struct json_object *new_list_obj = json_object_new_array();
struct uci_context *uci_ctx = NULL;
if (!req_obj) {
json_object_put(new_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
mac_list_obj = json_object_object_get(req_obj, "mac_list");
if (!mac_list_obj) {
LOG_ERROR("mac_list_obj is NULL\n");
json_object_put(new_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
json_object_put(new_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
if (ensure_record_section(uci_ctx) != 0) {
uci_free_context(uci_ctx);
json_object_put(new_list_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
fwx_uci_delete(uci_ctx, RECORD_WHITELIST_UCI_KEY);
mac_list_len = json_object_array_length(mac_list_obj);
for (i = 0; i < mac_list_len; i++) {
char norm_mac[32] = {0};
struct json_object *mac_item = json_object_array_get_idx(mac_list_obj, i);
const char *raw_mac = json_object_get_string(mac_item);
normalize_mac(raw_mac, norm_mac, sizeof(norm_mac));
if (!is_valid_mac(norm_mac)) {
continue;
}
if (mac_exists_in_array(new_list_obj, norm_mac)) {
continue;
}
fwx_uci_add_list(uci_ctx, RECORD_WHITELIST_UCI_KEY, norm_mac);
json_object_array_add(new_list_obj, json_object_new_string(norm_mac));
}
fwx_uci_commit(uci_ctx, "fwx_record");
set_state_file(RECORD_WHITELIST_STATE_FILE);
uci_free_context(uci_ctx);
json_object_put(new_list_obj);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
+15
View File
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_RECORD_H__
#define __FWX_RECORD_H__
#include "fwx.h"
struct json_object *fwx_api_get_record_whitelist(struct json_object *req_obj);
struct json_object *fwx_api_add_record_whitelist(struct json_object *req_obj);
struct json_object *fwx_api_del_record_whitelist(struct json_object *req_obj);
struct json_object *fwx_api_set_record_whitelist(struct json_object *req_obj);
#endif
+214
View File
@@ -0,0 +1,214 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fwx.h"
#include "fwx_stat.h"
#define SESSION_MAX_POINTS (24 * 60)
#define SESSION_SAMPLE_INTERVAL_SEC 5
#define SESSION_DETAIL_MAX_POINTS (5 * 60 / SESSION_SAMPLE_INTERVAL_SEC)
#define SESSION_SAMPLES_PER_MINUTE (60 / SESSION_SAMPLE_INTERVAL_SEC)
typedef struct session_point {
int value;
} session_point_t;
typedef struct session_ring {
session_point_t *points;
int max_points;
int start;
int count;
} session_ring_t;
typedef struct session_stat_ctx {
session_point_t minute_points[SESSION_MAX_POINTS];
session_point_t detail_points[SESSION_DETAIL_MAX_POINTS];
session_ring_t minute_ring;
session_ring_t detail_ring;
int detail_sample_count;
int inited;
} session_stat_ctx_t;
static session_stat_ctx_t g_session_stat;
static void session_stat_init(void)
{
if (g_session_stat.inited) {
return;
}
memset(&g_session_stat, 0, sizeof(g_session_stat));
g_session_stat.minute_ring.points = g_session_stat.minute_points;
g_session_stat.minute_ring.max_points = SESSION_MAX_POINTS;
g_session_stat.detail_ring.points = g_session_stat.detail_points;
g_session_stat.detail_ring.max_points = SESSION_DETAIL_MAX_POINTS;
g_session_stat.inited = 1;
}
int fwx_stat_read_conntrack_count(void)
{
const char *path = "/proc/sys/net/netfilter/nf_conntrack_count";
char line[64] = {0};
FILE *fp = fopen(path, "r");
char *endptr = NULL;
long v;
if (!fp) {
return 0;
}
if (!fgets(line, sizeof(line), fp)) {
fclose(fp);
return 0;
}
fclose(fp);
v = strtol(line, &endptr, 10);
if (endptr == line || v < 0) {
return 0;
}
return (int)v;
}
static void session_ring_append(session_ring_t *ring, int value)
{
int idx;
if (!ring || !ring->points || ring->max_points <= 0) {
return;
}
if (ring->count < ring->max_points) {
idx = (ring->start + ring->count) % ring->max_points;
ring->count++;
} else {
idx = ring->start;
ring->start = (ring->start + 1) % ring->max_points;
}
ring->points[idx].value = value;
}
void fwx_session_stat_tick(void)
{
int current = fwx_stat_read_conntrack_count();
session_stat_init();
session_ring_append(&g_session_stat.detail_ring, current);
g_session_stat.detail_sample_count++;
if (g_session_stat.detail_sample_count >= SESSION_SAMPLES_PER_MINUTE) {
session_ring_append(&g_session_stat.minute_ring, current);
g_session_stat.detail_sample_count = 0;
}
}
struct json_object *fwx_api_get_history_session(struct json_object *req_obj)
{
const char *range = "hour";
int minutes = 60;
int current;
int i;
int start_offset = 0;
struct json_object *data_obj = json_object_new_object();
struct json_object *list_obj = json_object_new_array();
long long sum = 0;
int sample_count = 0;
int peak = 0;
int avg = 0;
int is_5min = 0;
int total_count = 0;
int ring_start = 0;
int ring_max = 0;
session_point_t *points = NULL;
struct json_object *range_obj = req_obj ? json_object_object_get(req_obj, "range") : NULL;
struct json_object *minutes_obj = req_obj ? json_object_object_get(req_obj, "minutes") : NULL;
session_ring_t *ring = NULL;
session_stat_init();
if (range_obj) {
const char *range_str = json_object_get_string(range_obj);
if (range_str && strlen(range_str) > 0) {
range = range_str;
}
}
if (range && (!strcmp(range, "5min") || !strcmp(range, "5m"))) {
is_5min = 1;
minutes = 5;
} else if (minutes_obj) {
int tmp = json_object_get_int(minutes_obj);
if (tmp > 0) {
minutes = tmp;
}
} else if (range && strcmp(range, "day") == 0) {
minutes = 24 * 60;
} else {
minutes = 60;
}
if (is_5min) {
ring = &g_session_stat.detail_ring;
} else {
ring = &g_session_stat.minute_ring;
if (minutes > SESSION_MAX_POINTS) {
minutes = SESSION_MAX_POINTS;
}
if (minutes < 1) {
minutes = 1;
}
}
total_count = ring->count;
ring_start = ring->start;
ring_max = ring->max_points;
points = ring->points;
current = fwx_stat_read_conntrack_count();
if (is_5min) {
if (total_count > SESSION_DETAIL_MAX_POINTS) {
start_offset = total_count - SESSION_DETAIL_MAX_POINTS;
}
} else if (total_count > minutes) {
start_offset = total_count - minutes;
}
for (i = start_offset; i < total_count; i++) {
int idx = (ring_start + i) % ring_max;
session_point_t *pt = &points[idx];
json_object_array_add(list_obj, json_object_new_int(pt->value));
sum += pt->value;
sample_count++;
if (sample_count == 1 || pt->value > peak) {
peak = pt->value;
}
}
if (sample_count == 0) {
json_object_array_add(list_obj, json_object_new_int(current));
avg = current;
peak = current;
} else {
avg = (int)((sum + (sample_count / 2)) / sample_count);
if (current > peak) {
peak = current;
}
}
if (is_5min) {
json_object_object_add(data_obj, "range", json_object_new_string("5min"));
json_object_object_add(data_obj, "minutes", json_object_new_int(5));
} else {
json_object_object_add(data_obj, "range", json_object_new_string((minutes >= 24 * 60) ? "day" : "hour"));
json_object_object_add(data_obj, "minutes", json_object_new_int(minutes));
}
json_object_object_add(data_obj, "current", json_object_new_int(current));
json_object_object_add(data_obj, "avg", json_object_new_int(avg));
json_object_object_add(data_obj, "peak", json_object_new_int(peak));
json_object_object_add(data_obj, "list", list_obj);
return fwx_gen_api_response_data(API_CODE_SUCCESS, data_obj);
}
+15
View File
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_STAT_H__
#define __FWX_STAT_H__
#include <json-c/json.h>
int fwx_stat_read_conntrack_count(void);
void fwx_session_stat_tick(void);
struct json_object *fwx_api_get_history_session(struct json_object *req_obj);
#endif
+292
View File
@@ -0,0 +1,292 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <time.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <json-c/json.h>
#include <uci.h>
#include "fwx.h"
#include "fwx_user.h"
#include "fwx_netlink.h"
#include "fwx_ubus.h"
#include "fwx_config.h"
#include "fwx_utils.h"
#include "fwx_uci.h"
static void ensure_fwx_advanced_section(struct uci_context *uci_ctx)
{
char section_type[32] = {0};
if (fwx_uci_get_value(uci_ctx, "fwx.advanced", section_type, sizeof(section_type)) != 0)
fwx_uci_set_value(uci_ctx, "fwx.advanced", "advanced");
}
static void ensure_fwx_status_section(struct uci_context *uci_ctx)
{
char section_type[32] = {0};
if (fwx_uci_get_value(uci_ctx, "fwx.status", section_type, sizeof(section_type)) != 0)
fwx_uci_set_value(uci_ctx, "fwx.status", "status");
}
int fwx_get_disable_hnat(void)
{
int disable_hnat = 0;
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx)
return 0;
disable_hnat = fwx_uci_get_int_value(uci_ctx, "fwx.advanced.disable_hnat");
uci_free_context(uci_ctx);
return disable_hnat == 1 ? 1 : 0;
}
int fwx_get_notice_status(void)
{
int notice_status = 0;
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx)
return 0;
notice_status = fwx_uci_get_int_value(uci_ctx, "fwx.status.notice_status");
uci_free_context(uci_ctx);
return notice_status == 1 ? 1 : 0;
}
struct json_object *fwx_api_get_system_info(struct json_object *req_obj) {
struct json_object *data_obj = json_object_new_object();
struct json_object *fwx_obj = json_object_new_object();
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
json_object_put(fwx_obj);
json_object_put(data_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
char lan_ifname[32] = {0};
int ret = fwx_uci_get_value(uci_ctx, "fwx.global.lan_ifname", lan_ifname, sizeof(lan_ifname) - 1);
if (ret != 0) {
strncpy(lan_ifname, "br-lan", sizeof(lan_ifname) - 1);
}
char theme_mode_str[8] = {0};
int theme_mode = 0;
ret = fwx_uci_get_value(uci_ctx, "fwx.global.theme_mode", theme_mode_str, sizeof(theme_mode_str) - 1);
if (ret == 0) {
theme_mode = atoi(theme_mode_str);
}
json_object_object_add(fwx_obj, "lan_ifname", json_object_new_string(lan_ifname));
json_object_object_add(fwx_obj, "theme_mode", json_object_new_int(theme_mode));
json_object_object_add(data_obj, "fwx", fwx_obj);
uci_free_context(uci_ctx);
return fwx_gen_api_response_data(API_CODE_SUCCESS, data_obj);
}
struct json_object *fwx_api_set_system_info(struct json_object *req_obj) {
if (!req_obj) {
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
struct json_object *fwx_obj = json_object_object_get(req_obj, "fwx");
if (!fwx_obj) {
LOG_ERROR("Missing fwx parameter\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
struct json_object *lan_ifname_obj = json_object_object_get(fwx_obj, "lan_ifname");
if (!lan_ifname_obj) {
LOG_ERROR("Missing lan_ifname parameter\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
const char *lan_ifname = json_object_get_string(lan_ifname_obj);
if (!lan_ifname || strlen(lan_ifname) == 0) {
LOG_ERROR("Invalid lan_ifname value\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
if (strlen(lan_ifname) < 2 || strlen(lan_ifname) > 16) {
LOG_ERROR("lan_ifname length invalid\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
struct json_object *theme_mode_obj = json_object_object_get(fwx_obj, "theme_mode");
int theme_mode = 0; // 默认值为0(light)
if (theme_mode_obj) {
if (json_object_get_type(theme_mode_obj) == json_type_int) {
theme_mode = json_object_get_int(theme_mode_obj);
} else if (json_object_get_type(theme_mode_obj) == json_type_string) {
theme_mode = atoi(json_object_get_string(theme_mode_obj));
}
// 验证值只能是0或1
if (theme_mode != 0 && theme_mode != 1) {
LOG_ERROR("Invalid theme_mode value, must be 0 or 1\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
}
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
fwx_uci_set_value(uci_ctx, "fwx.global.lan_ifname", (char *)lan_ifname);
char theme_mode_str[8] = {0};
snprintf(theme_mode_str, sizeof(theme_mode_str), "%d", theme_mode);
fwx_uci_set_value(uci_ctx, "fwx.global.theme_mode", theme_mode_str);
fwx_uci_commit(uci_ctx, "fwx");
update_fwx_proc_value("lan_ifname", lan_ifname);
uci_free_context(uci_ctx);
LOG_DEBUG("Set system config: lan_ifname=%s, theme_mode=%d\n", lan_ifname, theme_mode);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
int fwx_get_tcp_rst(void)
{
int tcp_rst = 1;
struct uci_context *uci_ctx = uci_alloc_context();
if (!uci_ctx)
return 1;
tcp_rst = fwx_uci_get_int_value(uci_ctx, "fwx.global.tcp_rst");
uci_free_context(uci_ctx);
return tcp_rst == 0 ? 0 : 1;
}
struct json_object *fwx_api_get_tcp_rst(struct json_object *req_obj)
{
struct json_object *data_obj = json_object_new_object();
(void)req_obj;
json_object_object_add(data_obj, "tcp_rst", json_object_new_int(fwx_get_tcp_rst()));
return fwx_gen_api_response_data(API_CODE_SUCCESS, data_obj);
}
struct json_object *fwx_api_set_tcp_rst(struct json_object *req_obj)
{
struct json_object *tcp_rst_obj;
int tcp_rst;
struct uci_context *uci_ctx;
if (!req_obj)
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
tcp_rst_obj = json_object_object_get(req_obj, "tcp_rst");
if (!tcp_rst_obj) {
LOG_ERROR("Missing tcp_rst parameter\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
tcp_rst = json_object_get_int(tcp_rst_obj) == 0 ? 0 : 1;
uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
fwx_uci_set_int_value(uci_ctx, "fwx.global.tcp_rst", tcp_rst);
fwx_uci_commit(uci_ctx, "fwx");
uci_free_context(uci_ctx);
update_fwx_proc_u32_value("tcp_rst", tcp_rst);
LOG_DEBUG("Set tcp_rst=%d\n", tcp_rst);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
struct json_object *fwx_api_get_advanced_settings(struct json_object *req_obj)
{
struct json_object *data_obj = json_object_new_object();
json_object_object_add(data_obj, "disable_hnat", json_object_new_int(fwx_get_disable_hnat()));
return fwx_gen_api_response_data(API_CODE_SUCCESS, data_obj);
}
struct json_object *fwx_api_set_advanced_settings(struct json_object *req_obj)
{
struct json_object *disable_hnat_obj;
int disable_hnat;
struct uci_context *uci_ctx;
if (!req_obj)
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
disable_hnat_obj = json_object_object_get(req_obj, "disable_hnat");
if (!disable_hnat_obj) {
LOG_ERROR("Missing disable_hnat parameter\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
disable_hnat = json_object_get_int(disable_hnat_obj) == 1 ? 1 : 0;
uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
ensure_fwx_advanced_section(uci_ctx);
fwx_uci_set_int_value(uci_ctx, "fwx.advanced.disable_hnat", disable_hnat);
fwx_uci_commit(uci_ctx, "fwx");
uci_free_context(uci_ctx);
if (disable_hnat == 1)
system("/usr/bin/hnat.sh >/dev/null 2>&1");
LOG_DEBUG("Set advanced settings: disable_hnat=%d\n", disable_hnat);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
struct json_object *fwx_api_set_dashboard_notice_status(struct json_object *req_obj)
{
struct json_object *notice_status_obj;
int notice_status = 1;
struct uci_context *uci_ctx;
if (req_obj) {
notice_status_obj = json_object_object_get(req_obj, "notice_status");
if (notice_status_obj)
notice_status = json_object_get_int(notice_status_obj);
}
notice_status = notice_status == 1 ? 1 : 0;
uci_ctx = uci_alloc_context();
if (!uci_ctx) {
LOG_ERROR("Failed to allocate UCI context\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
ensure_fwx_status_section(uci_ctx);
fwx_uci_set_int_value(uci_ctx, "fwx.status.notice_status", notice_status);
fwx_uci_commit(uci_ctx, "fwx");
uci_free_context(uci_ctx);
LOG_DEBUG("Set dashboard notice_status=%d\n", notice_status);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
+22
View File
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_SYSTEM_H__
#define __FWX_SYSTEM_H__
#include <json-c/json.h>
struct json_object *get_system_status(void);
int fwx_get_disable_hnat(void);
int fwx_get_notice_status(void);
struct json_object *fwx_api_get_system_info(struct json_object *req_obj);
struct json_object *fwx_api_set_system_info(struct json_object *req_obj);
struct json_object *fwx_api_get_tcp_rst(struct json_object *req_obj);
struct json_object *fwx_api_set_tcp_rst(struct json_object *req_obj);
struct json_object *fwx_api_get_advanced_settings(struct json_object *req_obj);
struct json_object *fwx_api_set_advanced_settings(struct json_object *req_obj);
struct json_object *fwx_api_set_dashboard_notice_status(struct json_object *req_obj);
#endif
File diff suppressed because it is too large. Load diff
+9
View File
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __APPFILTER_UBUS_H__
#define __APPFILTER_UBUS_H__
int fwx_ubus_init(void);
#endif
@@ -1,42 +1,19 @@
/*
Copyright (C) 2020 Derry <destan19@126.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "appfilter_config.h"
#include "appfilter.h"
#include <uci.h>
app_name_info_t app_name_table[MAX_SUPPORT_APP_NUM];
int g_app_count = 0;
int g_cur_class_num = 0;
char CLASS_NAME_TABLE[MAX_APP_TYPE][MAX_CLASS_NAME_LEN];
#include "fwx_uci.h"
const char *config_path = "./config";
static struct uci_context *uci_ctx = NULL;
static struct uci_package *uci_appfilter;
int af_uci_get_int_value(struct uci_context *ctx, char *key)
int fwx_uci_get_int_value(struct uci_context *ctx, char *key)
{
struct uci_element *e;
struct uci_ptr ptr;
@@ -73,7 +50,7 @@ done:
}
int af_uci_get_value(struct uci_context *ctx, char *key, char *output, int out_len)
int fwx_uci_get_value(struct uci_context *ctx, char *key, char *output, int out_len)
{
struct uci_element *e;
struct uci_ptr ptr;
@@ -112,7 +89,7 @@ done:
}
int af_uci_delete(struct uci_context *ctx, char *key)
int fwx_uci_delete(struct uci_context *ctx, char *key)
{
struct uci_element *e;
struct uci_ptr ptr;
@@ -136,7 +113,7 @@ int af_uci_delete(struct uci_context *ctx, char *key)
int af_uci_add_list(struct uci_context *ctx, char *key, char *value)
int fwx_uci_add_list(struct uci_context *ctx, char *key, char *value)
{
struct uci_element *e;
struct uci_ptr ptr;
@@ -163,7 +140,7 @@ int af_uci_add_list(struct uci_context *ctx, char *key, char *value)
}
int af_uci_get_list_value(struct uci_context *ctx, char *key, char *output, int out_len, char *delimt)
int fwx_uci_get_list_value(struct uci_context *ctx, char *key, char *output, int out_len, char *delimt)
{
struct uci_element *e;
struct uci_ptr ptr;
@@ -212,7 +189,7 @@ done:
}
int af_uci_add_int_list(struct uci_context *ctx, char *key, int value)
int fwx_uci_add_int_list(struct uci_context *ctx, char *key, int value)
{
struct uci_element *e;
struct uci_ptr ptr;
@@ -234,7 +211,7 @@ int af_uci_add_int_list(struct uci_context *ctx, char *key, int value)
return ret;
}
int af_uci_del_list(struct uci_context *ctx, char *key, char *value)
int fwx_uci_del_list(struct uci_context *ctx, char *key, char *value)
{
struct uci_element *e;
struct uci_ptr ptr;
@@ -257,7 +234,7 @@ int af_uci_del_list(struct uci_context *ctx, char *key, char *value)
}
int af_uci_set_value(struct uci_context *ctx, char *key, char *value)
int fwx_uci_set_value(struct uci_context *ctx, char *key, char *value)
{
struct uci_element *e;
struct uci_ptr ptr;
@@ -281,7 +258,7 @@ int af_uci_set_value(struct uci_context *ctx, char *key, char *value)
return ret;
}
int af_uci_set_int_value(struct uci_context *ctx, char *key, int value)
int fwx_uci_set_int_value(struct uci_context *ctx, char *key, int value)
{
struct uci_element *e;
struct uci_ptr ptr;
@@ -304,19 +281,19 @@ int af_uci_set_int_value(struct uci_context *ctx, char *key, int value)
return ret;
}
int af_uci_del_array_value(struct uci_context *ctx, char *key_fmt, int index){
int fwx_uci_del_array_value(struct uci_context *ctx, char *key_fmt, int index){
char key[128] = {0};
sprintf(key, key_fmt, index);
return af_uci_delete(ctx, key);
return fwx_uci_delete(ctx, key);
}
int af_uci_set_array_value(struct uci_context *ctx, char *key_fmt, int index, char *value){
int fwx_uci_set_array_value(struct uci_context *ctx, char *key_fmt, int index, char *value){
char key[128] = {0};
sprintf(key, key_fmt, index);
return af_uci_set_value(ctx, key, value);
return fwx_uci_set_value(ctx, key, value);
}
int af_uci_commit(struct uci_context *ctx, const char * package) {
int fwx_uci_commit(struct uci_context *ctx, const char * package) {
struct uci_ptr ptr;
int ret = UCI_OK;
if (!package){
@@ -337,7 +314,7 @@ done:
return UCI_OK;
}
int af_get_uci_list_num(struct uci_context * ctx, char *package, char *section){
int fwx_uci_get_list_num(struct uci_context * ctx, char *package, char *section){
int count = 0;
struct uci_ptr p;
struct uci_element *e;
@@ -356,14 +333,14 @@ int af_get_uci_list_num(struct uci_context * ctx, char *package, char *section){
uci_unload(ctx, pkg);
return count;
}
int af_uci_get_array_value(struct uci_context *ctx, char *key_fmt, int index, char *output, int out_len)
int fwx_uci_get_array_value(struct uci_context *ctx, char *key_fmt, int index, char *output, int out_len)
{
char key[128] = {0};
sprintf(key, key_fmt, index);
return af_uci_get_value(ctx, key, output, out_len);
return fwx_uci_get_value(ctx, key, output, out_len);
}
int af_uci_add_section(struct uci_context * ctx, char *package_name, char *section)
int fwx_uci_add_section(struct uci_context * ctx, char *package_name, char *section)
{
struct uci_section *s = NULL;
struct uci_package *p = NULL;
@@ -381,9 +358,8 @@ done:
fprintf(stdout, "%s\n", s->e.name);
return ret;
}
//
static struct uci_package *
config_init_package(const char *config)
static struct uci_package *fwx_uci_get_package(const char *config)
{
struct uci_context *ctx = uci_ctx;
struct uci_package *p = NULL;
@@ -393,8 +369,8 @@ config_init_package(const char *config)
ctx = uci_alloc_context();
uci_ctx = ctx;
ctx->flags &= ~UCI_FLAG_STRICT;
//if (config_path)
// uci_set_confdir(ctx, config_path);
}
else
{
@@ -408,145 +384,3 @@ config_init_package(const char *config)
return p;
}
char *get_app_name_by_id(int id)
{
int i;
for (i = 0; i < g_app_count; i++)
{
if (id == app_name_table[i].id)
return app_name_table[i].name;
}
return "";
}
void init_app_name_table(void)
{
int count = 0;
char line_buf[2048] = {0};
FILE *fp = fopen("/tmp/feature.cfg", "r");
if (!fp)
{
printf("open file failed\n");
return;
}
g_app_count = 0;
while (fgets(line_buf, sizeof(line_buf), fp))
{
if (strstr(line_buf, "#"))
continue;
if (strlen(line_buf) < 10)
continue;
if (!strstr(line_buf, ":"))
continue;
char *pos1 = strstr(line_buf, ":");
char app_info_buf[128] = {0};
int app_id;
char app_name[64] = {0};
memset(app_name, 0x0, sizeof(app_name));
strncpy(app_info_buf, line_buf, pos1 - line_buf);
sscanf(app_info_buf, "%d %s", &app_id, app_name);
app_name_table[g_app_count].id = app_id;
strcpy(app_name_table[g_app_count].name, app_name);
g_app_count++;
}
fclose(fp);
}
void init_app_class_name_table(void)
{
char line_buf[2048] = {0};
int class_id;
char class_name[64] = {0};
FILE *fp = fopen("/tmp/app_class.txt", "r");
if (!fp)
{
printf("open file failed\n");
return;
}
g_cur_class_num = 0;
while (fgets(line_buf, sizeof(line_buf), fp))
{
sscanf(line_buf, "%d %*s %s", &class_id, class_name);
strcpy(CLASS_NAME_TABLE[class_id - 1], class_name);
g_cur_class_num++;
}
fclose(fp);
}
//00:00 9:1
int check_time_valid(char *t)
{
if (!t)
return 0;
if (strlen(t) < 3 || strlen(t) > 5 || (!strstr(t, ":")))
return 0;
else
return 1;
}
int config_get_appfilter_enable(void)
{
int enable = 0;
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return -1;
enable = af_uci_get_int_value(ctx, "appfilter.global.enable");
if (enable < 0)
enable = 0;
uci_free_context(ctx);
return enable;
}
int config_get_lan_ip(char *lan_ip, int len)
{
int ret = 0;
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return -1;
ret = af_uci_get_value(ctx, "network.lan.ipaddr", lan_ip, len);
uci_free_context(ctx);
return ret;
}
int config_get_lan_mask(char *lan_mask, int len)
{
int ret = 0;
struct uci_context *ctx = uci_alloc_context();
if (!ctx)
return -1;
ret = af_uci_get_value(ctx, "network.lan.netmask", lan_mask, len);
uci_free_context(ctx);
return ret;
}
int appfilter_config_alloc(void)
{
char *err;
uci_appfilter = config_init_package("appfilter");
if (!uci_appfilter)
{
uci_get_errorstr(uci_ctx, &err, NULL);
printf("Failed to load appfilter config (%s)\n", err);
free(err);
return -1;
}
return 0;
}
int appfilter_config_free(void)
{
if (uci_appfilter)
{
uci_unload(uci_ctx, uci_appfilter);
uci_appfilter = NULL;
}
if (uci_ctx)
{
uci_free_context(uci_ctx);
uci_ctx = NULL;
}
}
+27
View File
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_UCI_H__
#define __FWX_UCI_H__
#include <uci.h>
#define MAX_PARAM_LIST_LEN 1024
int fwx_uci_get_int_value(struct uci_context *ctx, char *key);
int fwx_uci_get_value(struct uci_context *ctx, char *key, char *output, int out_len);
int fwx_uci_add_list(struct uci_context *ctx, char *key, char *value);
int fwx_uci_get_list_value(struct uci_context *ctx, char *key, char *output, int out_len, char *delimt);
int fwx_uci_add_int_list(struct uci_context *ctx, char *key, int value);
int fwx_uci_del_list(struct uci_context *ctx, char *key, char *value);
int fwx_uci_set_value(struct uci_context *ctx, char *key, char *value);
int fwx_uci_set_int_value(struct uci_context *ctx, char *key, int value);
int fwx_uci_del_array_value(struct uci_context *ctx, char *key_fmt, int index);
int fwx_uci_set_array_value(struct uci_context *ctx, char *key_fmt, int index, char *value);
int fwx_uci_get_list_num(struct uci_context * ctx, char *package, char *section);
int fwx_uci_get_array_value(struct uci_context *ctx, char *key_fmt, int index, char *output, int out_len);
int fwx_uci_add_section(struct uci_context * ctx, char *package_name, char *section);
int fwx_uci_commit(struct uci_context *ctx, const char * package);
int fwx_uci_delete(struct uci_context *ctx, char *key);
#endif
File diff suppressed because it is too large. Load diff
+247
View File
@@ -0,0 +1,247 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <sys/types.h>
#include <libubox/list.h>
#ifndef __FILTER_USER_H__
#define __FILTER_USER_H__
#define MAX_IP_LEN 32
#define MAX_MAC_LEN 32
#define MAX_HOSTNAME_SIZE 64
#define OAF_VISIT_LIST_FILE "/tmp/visit_list"
#define OAF_DEV_LIST_FILE "/tmp/dev_list"
#define MIN_VISIT_TIME 5 // default 5s
#define MAX_APP_STAT_NUM 8
#define MAX_VISITLIST_DUMP_NUM 16
#define MAX_APP_TYPE 16
#define MAX_APP_ID_NUM 128
#define MAX_SUPPORT_DEV_NUM 64
#define SECONDS_PER_DAY (24 * 3600)
#define MAX_NICKNAME_SIZE 64
#define HOURS_PER_DAY 24
#define TOP_APP_PER_HOUR 3
#define MAX_DAILY_STAT_DAYS 30
#define MAX_TOP_APPS_PER_DAY 10
#define MAX_RECORD_DAY 30
#define MAX_RECENT_APPS 5
#define MAX_USER_RECORDS 4096
typedef struct visit_info
{
int appid;
u_int32_t first_time;
u_int32_t latest_time;
int action;
int expire;
struct list_head visit;
} visit_info_t;
typedef struct online_offline_record
{
int type;
u_int32_t timestamp;
unsigned long long duration;
struct list_head record;
} online_offline_record_t;
typedef struct online_session_stat
{
unsigned long long up_bytes;
unsigned long long down_bytes;
unsigned long long online_duration;
unsigned long long active_duration;
int recent_apps[MAX_RECENT_APPS];
int recent_app_count;
u_int32_t start_time;
} online_session_stat_t;
typedef struct user_record
{
int action;
u_int32_t timestamp;
char mac[MAX_MAC_LEN];
char nickname[MAX_NICKNAME_SIZE];
char hostname[MAX_HOSTNAME_SIZE];
unsigned long long up_bytes;
unsigned long long down_bytes;
unsigned long long online_duration;
unsigned long long active_duration;
int recent_apps[MAX_RECENT_APPS];
int recent_app_count;
struct list_head list;
} user_record_t;
typedef struct visit_stat
{
int appid;
unsigned long long total_time;
struct list_head list;
} visit_stat_t;
typedef struct global_app_type_record
{
int app_type;
unsigned long long time_delta;
u_int32_t timestamp;
struct list_head list;
} global_app_type_record_t;
typedef struct traffic_stat
{
unsigned long long up_bytes;
unsigned long long down_bytes;
} traffic_stat_t;
typedef struct daily_hourly_stat
{
u_int32_t date;
int is_today;
int hourly_top_apps[HOURS_PER_DAY][TOP_APP_PER_HOUR];
traffic_stat_t hourly_traffic[HOURS_PER_DAY];
unsigned long long hourly_online_time[HOURS_PER_DAY];
unsigned long long hourly_active_time[HOURS_PER_DAY];
} daily_hourly_stat_t;
typedef struct daily_top_apps_stat
{
u_int32_t date;
int is_today;
int count;
struct {
int appid;
unsigned long long total_time;
} apps[MAX_TOP_APPS_PER_DAY];
} daily_top_apps_stat_t;
#define MAX_REPORT_URL_LEN 64
typedef struct client_node
{
char mac[MAX_MAC_LEN];
char ip[MAX_IP_LEN];
char ipv6[128];
unsigned int up_rate;
unsigned int down_rate;
int rssi;
unsigned int rx_rate;
unsigned int tx_rate;
char band[16];
char wifi_ifname[64];
int is_wireless;
int wireless_online;
char hostname[MAX_HOSTNAME_SIZE];
char nickname[MAX_NICKNAME_SIZE];
int online;
int expire;
u_int32_t offline_time;
u_int32_t online_time;
struct list_head online_visit;
struct list_head visit;
struct list_head stat_list;
struct list_head online_offline_records;
int mf_user_loaded;
char visiting_url[MAX_REPORT_URL_LEN];
int visiting_app;
int active;
int last_online_state;
int session_online_recorded;
online_session_stat_t online_session;
daily_hourly_stat_t daily_stats;
daily_top_apps_stat_t daily_top_apps_stats;
struct list_head client;
} client_node_t;
struct app_visit_info
{
int app_id;
char app_name[32];
int total_time;
};
struct app_visit_stat_info
{
int num;
struct app_visit_info visit_list[MAX_APP_STAT_NUM];
};
typedef void (*iter_func)(void *arg, client_node_t *client);
extern struct list_head client_list;
extern struct list_head user_record_list;
int get_timestamp(void);
client_node_t *add_client_node(char *mac);
void init_client_list(void);
void add_debug_test_users(void);
void dump_client_list(void);
void dump_client_visit_list(void);
client_node_t *find_client_node(const char *mac);
void client_foreach(void *arg, iter_func iter);
void add_visit_info_node(struct list_head *visit_list, visit_info_t *node);
void check_client_visit_info_expire(void);
void flush_expire_visit_info(void);
int check_client_expire(void);
void flush_expire_client_node(void);
void move_expired_online_visit_to_offline(void);
void update_client_list(void);
void update_client_nickname(void);
void update_client_visiting_info(void);
void refresh_client_wireless_status(void);
void update_hourly_top_apps(client_node_t *client);
void get_hourly_top_apps(client_node_t *client, int hour, int *appids, int max_count);
int get_hour_from_timestamp(u_int32_t timestamp);
unsigned long long get_visit_duration_in_hour(visit_info_t *visit, int target_hour);
daily_hourly_stat_t *get_today_stat(client_node_t *client);
daily_hourly_stat_t *load_history_stat_from_file(client_node_t *client, u_int32_t date);
void save_daily_stats_to_file(client_node_t *client, u_int32_t date);
void check_and_archive_all_clients(void);
u_int32_t get_today_start_timestamp(void);
void update_daily_top_apps(client_node_t *client);
daily_top_apps_stat_t *get_today_top_apps_stat(client_node_t *client);
daily_top_apps_stat_t *load_history_top_apps_stat_from_file(client_node_t *client, u_int32_t date);
void save_daily_top_apps_stats_to_file(client_node_t *client, u_int32_t date);
void save_client_visit_data_to_file(client_node_t *client, u_int32_t date);
void init_client_visit_db(void);
int add_mock_visit_records_to_db(int record_count, int mac_count, unsigned long long *old_size_bytes, unsigned long long *new_size_bytes);
const char *get_client_data_root_dir(void);
const char *get_history_data_root_dir(void);
const char *get_client_data_base_dir(void);
void reset_client_data_base_dir_cache(void);
void load_client_backup_from_files(void);
void save_client_backup_to_file(client_node_t *client);
void save_all_client_backup_to_files(void);
void load_app_valid_time_config(void);
int get_app_valid_time(void);
void check_and_cleanup_history_data_by_size(void);
void archive_and_save_client_visits(void);
void delete_client_record_files(const char *mac, const char *start_date, const char *end_date, const char *delete_type);
void update_global_app_type_stats(int appid, unsigned long long time_delta);
void cleanup_expired_hourly_stats(void);
void get_global_daily_app_type_stats(unsigned long long *type_time_array);
void get_global_hourly_app_type_stats(unsigned long long *type_time_array);
struct json_object *fwx_api_get_global_app_type_stats(struct json_object *req_obj);
void save_global_traffic_stats_to_file(u_int32_t date);
void get_global_traffic_stats(traffic_stat_t *traffic_array);
struct json_object *fwx_api_get_global_traffic_stats(struct json_object *req_obj);
void reset_online_session_stat(client_node_t *client, u_int32_t start_time);
void update_online_session_flow(client_node_t *client, unsigned long long up_bytes, unsigned long long down_bytes);
void update_online_session_activity(client_node_t *client, int online_seconds, int active_seconds);
void update_online_session_recent_app(client_node_t *client, int appid);
void add_user_record(client_node_t *client, int action, u_int32_t timestamp);
#endif
+337
View File
@@ -0,0 +1,337 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <stdint.h>
#include "fwx_utils.h"
char *str_trim(char *s) {
char *start, *last, *bk;
int len;
start = s;
while (isspace(*start))
start++;
bk = last = s + strlen(s) - 1;
while (last > start && isspace(*last))
last--;
if ((s != start) || (bk != last)) {
len = last - start + 1;
strncpy(s, start, len);
s[len] = '\0';
}
return s;
}
int exec_with_result_line(char *cmd, char *result, int len)
{
FILE *fp = NULL;
if (!cmd || !result || !len)
return -1;
fp = popen(cmd, "r");
if (!fp)
return -1;
fgets(result, len, fp);
str_trim(result);
pclose(fp);
return 0;
}
int fwx_send_msg_to_kernel(char *buf){
if (access("/dev/fwx", F_OK) != 0) {
return 0; // Device doesn't exist, silently skip
}
FILE *fp = fopen("/dev/fwx", "w");
if (fp) {
fprintf(fp, "%s", buf);
fclose(fp);
}
return 0;
}
int check_same_network(char *ip1, char *netmask, char *ip2) {
struct in_addr addr1, addr2, mask;
if (inet_pton(AF_INET, ip1, &addr1) != 1) {
printf("Invalid IP address: %s\n", ip1);
return -1;
}
if (inet_pton(AF_INET, netmask, &mask) != 1) {
printf("Invalid netmask: %s\n", netmask);
return -1;
}
if (inet_pton(AF_INET, ip2, &addr2) != 1) {
printf("Invalid IP address: %s\n", ip2);
return -1;
}
if ((addr1.s_addr & mask.s_addr) == (addr2.s_addr & mask.s_addr)) {
return 1;
} else {
return 0;
}
}
int af_read_file_value(const char *file_path, char *value, int value_len) {
FILE *file = fopen(file_path, "r");
if (!file) {
perror("Failed to open file");
return -1;
}
if (fgets(value, value_len, file) == NULL) {
perror("Failed to read line from file");
fclose(file);
return -1;
}
size_t len = strlen(value);
if (len > 0 && value[len - 1] == '\n') {
value[len - 1] = '\0';
}
fclose(file);
return 0;
}
int af_read_file_int_value(const char *file_path, int *value) {
char line_buf[128] = {0};
if (af_read_file_value(file_path, line_buf, sizeof(line_buf)) < 0){
return -1;
}
*value = atoi(line_buf);
return 0;
}
/**
* Parse time_str from UCI format into time period structures
* Format: "HH:MM-HH:MM-w1,w2,w3 HH:MM-HH:MM-w4,w5 ..."
* Example: "00:00-23:59-2,3,6 00:00-02:05-1,2,3,0"
*/
int fwx_parse_time_str(const char *time_str, fwx_time_period_t *periods, int max_periods) {
if (!time_str || !periods || max_periods <= 0) {
return -1;
}
int period_count = 0;
char *save_ptr1 = NULL;
char *save_ptr2 = NULL;
char time_str_copy[512] = {0};
strncpy(time_str_copy, time_str, sizeof(time_str_copy) - 1);
char *time_period = strtok_r(time_str_copy, " ", &save_ptr1);
while (time_period && period_count < max_periods) {
fwx_time_period_t *period = &periods[period_count];
memset(period, 0, sizeof(fwx_time_period_t));
char start[16] = {0};
char end[16] = {0};
char weekdays[64] = {0};
char *first_delim = strchr(time_period, '-');
if (!first_delim) {
time_period = strtok_r(NULL, " ", &save_ptr1);
continue;
}
strncpy(start, time_period, first_delim - time_period);
start[first_delim - time_period] = '\0';
char *second_delim = strchr(first_delim + 1, '-');
if (second_delim) {
strncpy(end, first_delim + 1, second_delim - first_delim - 1);
end[second_delim - first_delim - 1] = '\0';
strncpy(weekdays, second_delim + 1, sizeof(weekdays) - 1);
} else {
strncpy(end, first_delim + 1, sizeof(end) - 1);
}
strncpy(period->start_time, start, sizeof(period->start_time) - 1);
strncpy(period->end_time, end, sizeof(period->end_time) - 1);
if (strlen(weekdays) > 0) {
char weekdays_copy[64] = {0};
strncpy(weekdays_copy, weekdays, sizeof(weekdays_copy) - 1);
char *weekday_str = strtok_r(weekdays_copy, ",", &save_ptr2);
while (weekday_str && period->weekday_count < MAX_WEEKDAYS) {
int weekday = atoi(weekday_str);
if (weekday >= 0 && weekday <= 6) {
period->weekdays[period->weekday_count] = weekday;
period->weekday_count++;
}
weekday_str = strtok_r(NULL, ",", &save_ptr2);
}
}
period_count++;
time_period = strtok_r(NULL, " ", &save_ptr1);
}
return period_count;
}
void update_fwx_proc_value(char *key, char *value){
char cmd_buf[128] = {0};
char file_path[128] = {0};
char old_value[128] = {0};
sprintf(file_path, "/proc/sys/fwx/%s", key);
af_read_file_value(file_path, old_value, sizeof(old_value));
if (strcmp(old_value, value) != 0){
sprintf(cmd_buf, "echo %s >/proc/sys/fwx/%s", value, key);
system(cmd_buf);
}
}
void update_fwx_proc_u32_value(char *key, u_int32_t value){
char buf[32] = {0};
sprintf(buf, "%u", value);
update_fwx_proc_value(key, buf);
}
#define MD5_F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define MD5_G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
#define MD5_H(x, y, z) ((x) ^ (y) ^ (z))
#define MD5_I(x, y, z) ((y) ^ ((x) | ~(z)))
#define MD5_ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
static const uint32_t md5_k[64] = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
};
static const uint8_t md5_s[64] = {
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
};
static uint32_t md5_load_le32(const uint8_t *p)
{
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}
static void md5_store_le32(uint8_t *p, uint32_t v)
{
p[0] = (uint8_t)v;
p[1] = (uint8_t)(v >> 8);
p[2] = (uint8_t)(v >> 16);
p[3] = (uint8_t)(v >> 24);
}
static void md5_transform(uint32_t *state, const uint8_t block[64])
{
uint32_t a = state[0];
uint32_t b = state[1];
uint32_t c = state[2];
uint32_t d = state[3];
uint32_t x[16];
int i;
for (i = 0; i < 16; i++)
x[i] = md5_load_le32(block + i * 4);
for (i = 0; i < 64; i++) {
uint32_t f;
int g;
if (i < 16) {
f = MD5_F(b, c, d);
g = i;
} else if (i < 32) {
f = MD5_G(b, c, d);
g = (5 * i + 1) & 15;
} else if (i < 48) {
f = MD5_H(b, c, d);
g = (3 * i + 5) & 15;
} else {
f = MD5_I(b, c, d);
g = (7 * i) & 15;
}
f = f + a + md5_k[i] + x[g];
a = d;
d = c;
c = b;
b = b + MD5_ROTL(f, md5_s[i]);
}
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
void fwx_md5(const unsigned char *data, size_t len, unsigned char digest[16])
{
uint32_t state[4] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476};
uint64_t bit_len = (uint64_t)len * 8;
uint8_t block[64];
size_t rem;
int i;
while (len >= 64) {
md5_transform(state, data);
data += 64;
len -= 64;
}
memset(block, 0, sizeof(block));
memcpy(block, data, len);
block[len] = 0x80;
rem = len + 1;
if (rem > 56) {
md5_transform(state, block);
memset(block, 0, sizeof(block));
}
md5_store_le32(block + 56, (uint32_t)bit_len);
md5_store_le32(block + 60, (uint32_t)(bit_len >> 32));
md5_transform(state, block);
for (i = 0; i < 4; i++)
md5_store_le32(digest + i * 4, state[i]);
}
+29
View File
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __UTILS_H__
#define __UTILS_H__
#include <sys/types.h>
#define MAX_WEEKDAYS 7
typedef struct fwx_time_period {
char start_time[16];
char end_time[16];
int weekdays[MAX_WEEKDAYS];
int weekday_count;
} fwx_time_period_t;
char *str_trim(char *s);
int exec_with_result_line(char *cmd, char *result, int len);
int check_same_network(char *ip1, char *netmask, char *ip2);
int af_read_file_value(const char *file_path, char *value, int value_len);
int af_read_file_int_value(const char *file_path, int *value);
int fwx_send_msg_to_kernel(char *buf);
int fwx_parse_time_str(const char *time_str, fwx_time_period_t *periods, int max_periods);
void update_fwx_proc_value(char *key, char *value);
void update_fwx_proc_u32_value(char *key, u_int32_t value);
void fwx_md5(const unsigned char *data, size_t len, unsigned char digest[16]);
#endif
+502
View File
@@ -0,0 +1,502 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <json-c/json.h>
#include <uci.h>
#include "fwx.h"
#include "fwx_wireless.h"
#define MAX_WIRELESS_SECTION_NUM 64
#define MAX_WIRELESS_NAME_LEN 64
static int get_wireless_option_value(struct uci_context *ctx, const char *section_name, const char *option_name, char *out, size_t out_len)
{
char uci_key[128] = {0};
if (!ctx || !section_name || !option_name || !out || out_len == 0) {
return -1;
}
snprintf(uci_key, sizeof(uci_key), "wireless.%s.%s", section_name, option_name);
if (fwx_uci_get_value(ctx, uci_key, out, (int)out_len) == 0) {
return 0;
}
out[0] = '\0';
return -1;
}
static int set_wireless_option_value(struct uci_context *ctx, const char *section_name, const char *option_name, const char *value)
{
char uci_key[128] = {0};
if (!ctx || !section_name || !option_name || !value) {
return -1;
}
snprintf(uci_key, sizeof(uci_key), "wireless.%s.%s", section_name, option_name);
return fwx_uci_set_value(ctx, uci_key, (char *)value);
}
static int delete_wireless_option_value(struct uci_context *ctx, const char *section_name, const char *option_name)
{
char uci_key[128] = {0};
if (!ctx || !section_name || !option_name) {
return -1;
}
snprintf(uci_key, sizeof(uci_key), "wireless.%s.%s", section_name, option_name);
return fwx_uci_delete(ctx, uci_key);
}
static int collect_wireless_sections(struct uci_context *ctx,
char radio_names[][MAX_WIRELESS_NAME_LEN], int *radio_num,
char iface_names[][MAX_WIRELESS_NAME_LEN], int *iface_num)
{
struct uci_package *pkg = NULL;
struct uci_element *e = NULL;
if (!ctx || !radio_names || !radio_num || !iface_names || !iface_num) {
return -1;
}
*radio_num = 0;
*iface_num = 0;
if (uci_load(ctx, "wireless", &pkg) != UCI_OK) {
return -1;
}
uci_foreach_element(&pkg->sections, e) {
struct uci_section *s = uci_to_section(e);
if (strcmp(s->type, "wifi-device") == 0) {
if (*radio_num < MAX_WIRELESS_SECTION_NUM) {
snprintf(radio_names[*radio_num], MAX_WIRELESS_NAME_LEN, "%s", e->name);
(*radio_num)++;
}
continue;
}
if (strcmp(s->type, "wifi-iface") == 0) {
if (*iface_num < MAX_WIRELESS_SECTION_NUM) {
snprintf(iface_names[*iface_num], MAX_WIRELESS_NAME_LEN, "%s", e->name);
(*iface_num)++;
}
}
}
if (pkg) {
uci_unload(ctx, pkg);
}
return 0;
}
static int find_first_iface_by_radio(struct uci_context *ctx, const char *radio_name,
char iface_names[][MAX_WIRELESS_NAME_LEN], int iface_num,
char *out_iface, size_t out_iface_len)
{
int i = 0;
if (!ctx || !radio_name || !iface_names || !out_iface || out_iface_len == 0) {
return -1;
}
for (i = 0; i < iface_num; i++) {
char device[64] = {0};
if (get_wireless_option_value(ctx, iface_names[i], "device", device, sizeof(device)) == 0 &&
strcmp(device, radio_name) == 0) {
snprintf(out_iface, out_iface_len, "%s", iface_names[i]);
return 0;
}
}
return -1;
}
static int is_iface_exists(char iface_names[][MAX_WIRELESS_NAME_LEN], int iface_num, const char *section_name)
{
int i = 0;
if (!iface_names || !section_name) {
return 0;
}
for (i = 0; i < iface_num; i++) {
if (strcmp(iface_names[i], section_name) == 0) {
return 1;
}
}
return 0;
}
static const char *format_band_label(const char *band, const char *hwmode, char *buf, size_t buf_len)
{
if (!buf || buf_len == 0) {
return "Unknown";
}
if (band && band[0] != '\0') {
if (strcmp(band, "2g") == 0) {
snprintf(buf, buf_len, "2.4G");
} else if (strcmp(band, "5g") == 0) {
snprintf(buf, buf_len, "5G");
} else if (strcmp(band, "6g") == 0) {
snprintf(buf, buf_len, "6G");
} else if (strcmp(band, "60g") == 0) {
snprintf(buf, buf_len, "60G");
} else {
snprintf(buf, buf_len, "%s", band);
}
return buf;
}
if (hwmode && hwmode[0] != '\0') {
if (strstr(hwmode, "11ad") || strstr(hwmode, "11ay")) {
snprintf(buf, buf_len, "60G");
} else if (strstr(hwmode, "11a")) {
snprintf(buf, buf_len, "5G");
} else {
snprintf(buf, buf_len, "2.4G");
}
return buf;
}
snprintf(buf, buf_len, "Unknown");
return buf;
}
static int parse_hidden_value(const char *hidden)
{
if (!hidden) {
return 0;
}
if (strcmp(hidden, "1") == 0 ||
strcasecmp(hidden, "true") == 0 ||
strcasecmp(hidden, "yes") == 0 ||
strcasecmp(hidden, "on") == 0) {
return 1;
}
return 0;
}
static int json_to_bool(struct json_object *obj, int default_value)
{
enum json_type type;
const char *val_str = NULL;
if (!obj) {
return default_value;
}
type = json_object_get_type(obj);
if (type == json_type_boolean || type == json_type_int) {
return json_object_get_int(obj) ? 1 : 0;
}
if (type == json_type_string) {
val_str = json_object_get_string(obj);
return parse_hidden_value(val_str);
}
return default_value;
}
static int is_open_encryption(const char *encryption)
{
if (!encryption || encryption[0] == '\0') {
return 1;
}
if (strcmp(encryption, "none") == 0) {
return 1;
}
if (strncmp(encryption, "owe", 3) == 0) {
return 1;
}
return 0;
}
static int apply_ssid_item(struct uci_context *ctx, struct json_object *item,
char iface_names[][MAX_WIRELESS_NAME_LEN], int iface_num)
{
struct json_object *radio_obj = NULL;
struct json_object *section_obj = NULL;
struct json_object *ssid_obj = NULL;
struct json_object *password_obj = NULL;
struct json_object *encryption_obj = NULL;
struct json_object *hidden_obj = NULL;
struct json_object *isolate_obj = NULL;
const char *radio_name = NULL;
const char *section_name = NULL;
const char *password = NULL;
char encryption[64] = {0};
char iface_name[MAX_WIRELESS_NAME_LEN] = {0};
int has_password = 0;
int has_encryption = 0;
int has_change = 0;
int hidden = 0;
int isolate = 0;
char hidden_str[4] = {0};
char isolate_str[4] = {0};
if (!ctx || !item || json_object_get_type(item) != json_type_object) {
return 0;
}
json_object_object_get_ex(item, "radio", &radio_obj);
json_object_object_get_ex(item, "section", &section_obj);
json_object_object_get_ex(item, "ssid", &ssid_obj);
has_password = json_object_object_get_ex(item, "password", &password_obj);
has_encryption = json_object_object_get_ex(item, "encryption", &encryption_obj);
json_object_object_get_ex(item, "hidden", &hidden_obj);
json_object_object_get_ex(item, "isolate", &isolate_obj);
radio_name = radio_obj ? json_object_get_string(radio_obj) : NULL;
section_name = section_obj ? json_object_get_string(section_obj) : NULL;
if (section_name && section_name[0] != '\0' && is_iface_exists(iface_names, iface_num, section_name)) {
snprintf(iface_name, sizeof(iface_name), "%s", section_name);
}
if (iface_name[0] == '\0' && radio_name && radio_name[0] != '\0') {
find_first_iface_by_radio(ctx, radio_name, iface_names, iface_num, iface_name, sizeof(iface_name));
}
if (iface_name[0] == '\0') {
LOG_WARN("set_wireless_base_setting skip item, iface not found, radio=%s section=%s\n",
radio_name ? radio_name : "", section_name ? section_name : "");
return 0;
}
if (ssid_obj) {
const char *ssid = json_object_get_string(ssid_obj);
if (set_wireless_option_value(ctx, iface_name, "ssid", ssid ? ssid : "") == 0) {
has_change = 1;
}
}
if (has_encryption) {
const char *enc_value = json_object_get_string(encryption_obj);
if (!enc_value || enc_value[0] == '\0') {
enc_value = "none";
}
snprintf(encryption, sizeof(encryption), "%s", enc_value);
if (set_wireless_option_value(ctx, iface_name, "encryption", encryption) == 0) {
has_change = 1;
}
} else {
if (get_wireless_option_value(ctx, iface_name, "encryption", encryption, sizeof(encryption)) != 0 ||
encryption[0] == '\0') {
snprintf(encryption, sizeof(encryption), "none");
}
}
if (hidden_obj) {
hidden = json_to_bool(hidden_obj, 0);
snprintf(hidden_str, sizeof(hidden_str), "%d", hidden ? 1 : 0);
if (set_wireless_option_value(ctx, iface_name, "hidden", hidden_str) == 0) {
has_change = 1;
}
}
if (isolate_obj) {
isolate = json_to_bool(isolate_obj, 0);
snprintf(isolate_str, sizeof(isolate_str), "%d", isolate ? 1 : 0);
if (set_wireless_option_value(ctx, iface_name, "isolate", isolate_str) == 0) {
has_change = 1;
}
}
if (is_open_encryption(encryption)) {
int del_key = delete_wireless_option_value(ctx, iface_name, "key");
int del_key1 = delete_wireless_option_value(ctx, iface_name, "key1");
if (del_key == 0 || del_key1 == 0) {
has_change = 1;
}
} else if (has_password) {
password = json_object_get_string(password_obj);
if (password && password[0] != '\0') {
if (set_wireless_option_value(ctx, iface_name, "key", password) == 0) {
has_change = 1;
}
} else {
if (delete_wireless_option_value(ctx, iface_name, "key") == 0) {
has_change = 1;
}
}
}
return has_change;
}
struct json_object *fwx_api_get_wireless_base_setting(struct json_object *req_obj)
{
struct uci_context *ctx = NULL;
struct json_object *data_obj = NULL;
struct json_object *ssid_list_obj = NULL;
char radio_names[MAX_WIRELESS_SECTION_NUM][MAX_WIRELESS_NAME_LEN] = {{0}};
char iface_names[MAX_WIRELESS_SECTION_NUM][MAX_WIRELESS_NAME_LEN] = {{0}};
int radio_num = 0;
int iface_num = 0;
int i = 0;
(void)req_obj;
data_obj = json_object_new_object();
ssid_list_obj = json_object_new_array();
if (!data_obj || !ssid_list_obj) {
if (ssid_list_obj) {
json_object_put(ssid_list_obj);
}
if (data_obj) {
json_object_put(data_obj);
}
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
ctx = uci_alloc_context();
if (!ctx) {
json_object_put(ssid_list_obj);
json_object_put(data_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
if (collect_wireless_sections(ctx, radio_names, &radio_num, iface_names, &iface_num) != 0) {
uci_free_context(ctx);
json_object_put(ssid_list_obj);
json_object_put(data_obj);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
for (i = 0; i < radio_num; i++) {
struct json_object *ssid_obj = NULL;
char iface_name[MAX_WIRELESS_NAME_LEN] = {0};
char ssid[128] = {0};
char key[128] = {0};
char encryption[64] = {0};
char hidden[16] = {0};
char isolate[16] = {0};
char band[16] = {0};
char hwmode[32] = {0};
char band_label[16] = {0};
if (find_first_iface_by_radio(ctx, radio_names[i], iface_names, iface_num, iface_name, sizeof(iface_name)) != 0) {
continue;
}
get_wireless_option_value(ctx, iface_name, "ssid", ssid, sizeof(ssid));
get_wireless_option_value(ctx, iface_name, "key", key, sizeof(key));
get_wireless_option_value(ctx, iface_name, "encryption", encryption, sizeof(encryption));
get_wireless_option_value(ctx, iface_name, "hidden", hidden, sizeof(hidden));
get_wireless_option_value(ctx, iface_name, "isolate", isolate, sizeof(isolate));
get_wireless_option_value(ctx, radio_names[i], "band", band, sizeof(band));
get_wireless_option_value(ctx, radio_names[i], "hwmode", hwmode, sizeof(hwmode));
ssid_obj = json_object_new_object();
if (!ssid_obj) {
continue;
}
json_object_object_add(ssid_obj, "radio", json_object_new_string(radio_names[i]));
json_object_object_add(ssid_obj, "section", json_object_new_string(iface_name));
json_object_object_add(ssid_obj, "band", json_object_new_string(format_band_label(band, hwmode, band_label, sizeof(band_label))));
json_object_object_add(ssid_obj, "ssid", json_object_new_string(ssid));
json_object_object_add(ssid_obj, "password", json_object_new_string(key));
json_object_object_add(ssid_obj, "encryption", json_object_new_string(encryption[0] ? encryption : "none"));
json_object_object_add(ssid_obj, "hidden", json_object_new_int(parse_hidden_value(hidden)));
json_object_object_add(ssid_obj, "isolate", json_object_new_int(parse_hidden_value(isolate)));
json_object_array_add(ssid_list_obj, ssid_obj);
}
json_object_object_add(data_obj, "ssid_list", ssid_list_obj);
uci_free_context(ctx);
return fwx_gen_api_response_data(API_CODE_SUCCESS, data_obj);
}
struct json_object *fwx_api_set_wireless_base_setting(struct json_object *req_obj)
{
struct uci_context *ctx = NULL;
struct json_object *ssid_list_obj = NULL;
enum json_type ssid_list_type;
char radio_names[MAX_WIRELESS_SECTION_NUM][MAX_WIRELESS_NAME_LEN] = {{0}};
char iface_names[MAX_WIRELESS_SECTION_NUM][MAX_WIRELESS_NAME_LEN] = {{0}};
int radio_num = 0;
int iface_num = 0;
int i = 0;
int list_len = 0;
int has_change = 0;
int processed_num = 0;
if (!req_obj) {
LOG_ERROR("set_wireless_base_setting: req_obj is null\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
LOG_INFO("set_wireless_base_setting req=%s\n", json_object_to_json_string_ext(req_obj, JSON_C_TO_STRING_PLAIN));
ssid_list_obj = json_object_object_get(req_obj, "ssid_list");
if (!ssid_list_obj) {
LOG_ERROR("set_wireless_base_setting: missing ssid_list\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
ssid_list_type = json_object_get_type(ssid_list_obj);
if (ssid_list_type != json_type_array && ssid_list_type != json_type_object) {
LOG_ERROR("set_wireless_base_setting: invalid ssid_list type=%d\n", ssid_list_type);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
ctx = uci_alloc_context();
if (!ctx) {
LOG_ERROR("set_wireless_base_setting: alloc uci ctx failed\n");
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
if (collect_wireless_sections(ctx, radio_names, &radio_num, iface_names, &iface_num) != 0) {
LOG_ERROR("set_wireless_base_setting: collect_wireless_sections failed\n");
uci_free_context(ctx);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
if (ssid_list_type == json_type_array) {
list_len = json_object_array_length(ssid_list_obj);
for (i = 0; i < list_len; i++) {
struct json_object *item = json_object_array_get_idx(ssid_list_obj, i);
has_change |= apply_ssid_item(ctx, item, iface_names, iface_num);
processed_num++;
}
} else {
json_object_object_foreach(ssid_list_obj, key, val) {
(void)key;
has_change |= apply_ssid_item(ctx, val, iface_names, iface_num);
processed_num++;
}
}
LOG_INFO("set_wireless_base_setting: list_type=%d processed=%d changed=%d\n",
ssid_list_type, processed_num, has_change);
if (has_change) {
if (fwx_uci_commit(ctx, "wireless") != UCI_OK) {
LOG_ERROR("set_wireless_base_setting: commit wireless failed\n");
uci_free_context(ctx);
return fwx_gen_api_response_data(API_CODE_ERROR, NULL);
}
system("wifi reload");
}
uci_free_context(ctx);
return fwx_gen_api_response_data(API_CODE_SUCCESS, NULL);
}
+13
View File
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright(c) 2026 destan19(TT) <www.fanchmwrt.com>
*/
#ifndef __FWX_WIRELESS_H__
#define __FWX_WIRELESS_H__
#include <json-c/json.h>
struct json_object *fwx_api_get_wireless_base_setting(struct json_object *req_obj);
struct json_object *fwx_api_set_wireless_base_setting(struct json_object *req_obj);
#endif
+404 -748
View File
File diff suppressed because it is too large. Load diff
-107
View File
@@ -1,107 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/time.h>
char *str_trim(char *s) {
char *start, *last, *bk;
int len;
start = s;
while (isspace(*start))
start++;
bk = last = s + strlen(s) - 1;
while (last > start && isspace(*last))
last--;
if ((s != start) || (bk != last)) {
len = last - start + 1;
strncpy(s, start, len);
s[len] = '\0';
}
return s;
}
int exec_with_result_line(char *cmd, char *result, int len)
{
FILE *fp = NULL;
if (!cmd || !result || !len)
return -1;
fp = popen(cmd, "r");
if (!fp)
return -1;
fgets(result, len, fp);
str_trim(result);
pclose(fp);
return 0;
}
unsigned int get_timestamp(void)
{
struct timeval cur_time;
gettimeofday(&cur_time, NULL);
return cur_time.tv_sec;
}
int check_same_network(char *ip1, char *netmask, char *ip2) {
struct in_addr addr1, addr2, mask;
if (inet_pton(AF_INET, ip1, &addr1) != 1) {
printf("Invalid IP address: %s\n", ip1);
return -1;
}
if (inet_pton(AF_INET, netmask, &mask) != 1) {
printf("Invalid netmask: %s\n", netmask);
return -1;
}
if (inet_pton(AF_INET, ip2, &addr2) != 1) {
printf("Invalid IP address: %s\n", ip2);
return -1;
}
if ((addr1.s_addr & mask.s_addr) == (addr2.s_addr & mask.s_addr)) {
return 1;
} else {
return 0;
}
}
int af_read_file_value(const char *file_path, char *value, int value_len) {
FILE *file = fopen(file_path, "r");
if (!file) {
//perror("Failed to open file");
return -1;
}
if (fgets(value, value_len, file) == NULL) {
perror("Failed to read line from file");
fclose(file);
return -2;
}
size_t len = strlen(value);
if (len > 0 && value[len - 1] == '\n') {
value[len - 1] = '\0';
}
fclose(file);
return 0;
}
int af_read_file_int_value(const char *file_path, int *value) {
char line_buf[128] = {0};
if (af_read_file_value(file_path, line_buf, sizeof(line_buf)) < 0){
return -1;
}
*value = atoi(line_buf);
return 0;
}
-9
View File
@@ -1,9 +0,0 @@
#ifndef __UTILS_H__
#define __UTILS_H__
char *str_trim(char *s);
int exec_with_result_line(char *cmd, char *result, int len);
int check_same_network(char *ip1, char *netmask, char *ip2);
int af_read_file_value(const char *file_path, char *value, int value_len);
int af_read_file_int_value(const char *file_path, int *value);
unsigned int get_timestamp(void);
#endif