// SPDX-License-Identifier: GPL-2.0-only | |
/* | |
* Copyright (C) 2018-2020 Oplus. All rights reserved. | |
*/ | |
/**************************************************************** | |
** ------------------ Revision History:------------------------ | |
** <author> <data> <version > <desc> | |
** Asiga 2019/07/31 1.0 build this module | |
****************************************************************/ | |
#include <linux/delay.h> | |
#include <linux/types.h> | |
#include <linux/module.h> | |
#include <linux/qrtr.h> | |
#include <linux/atomic.h> | |
#include <linux/err.h> | |
#include <linux/ip.h> | |
#include <linux/ipv6.h> | |
#include <linux/netlink.h> | |
#include <linux/version.h> | |
#include <net/tcp.h> | |
#include <net/oplus_nwpower.h> | |
static void tcp_output_hook_work_callback(struct work_struct *work); | |
static void tcp_input_hook_work_callback(struct work_struct *work); | |
static void tcp_output_tcpsynretrans_hook_work_callback(struct work_struct *work); | |
static void tcp_input_tcpsynretrans_hook_work_callback(struct work_struct *work); | |
static int nwpower_send_to_user(int msg_type, char *msg_data, int msg_len); | |
/*Add for feature switch*/ | |
static atomic_t qrtr_wakeup_hook_boot = ATOMIC_INIT(0); | |
static atomic_t ipa_wakeup_hook_boot = ATOMIC_INIT(0); | |
static atomic_t tcpsynretrans_hook_boot = ATOMIC_INIT(0); | |
/*Add for qmi wakeup msg*/ | |
#define GLINK_MODEM_NODE_ID 0x3 | |
#define GLINK_ADSP_NODE_ID 0x5 | |
#define GLINK_CDSP_NODE_ID 0xa | |
#define GLINK_SLPI_NODE_ID 0x9 | |
#define GLINK_NPU_NODE_ID 0xb | |
#define OPLUS_MAX_QRTR_SERVICE_LEN 120 | |
atomic_t qrtr_first_msg = ATOMIC_INIT(0); | |
u64 oplus_nw_wakeup[OPLUS_NW_WAKEUP_SUM] = {0}; | |
static u64 service_wakeup_times[OPLUS_MAX_QRTR_SERVICE_LEN][4] = {{0}}; | |
/*Add for ipa wakeup msg*/ | |
#define OPLUS_MAX_RECORD_IP_LEN 60 | |
#define OPLUS_TRANSMISSION_INTERVAL 3 * 1000 | |
#define OPLUS_TCP_RETRANSMISSION_INTERVAL 1 * 1000 | |
#define OPLUS_MAX_RECORD_APP_WAKEUP_LEN 100 | |
struct tcp_hook_struct { | |
u32 uid; | |
u32 pid; | |
bool is_ipv6; | |
u32 ipv4_addr; | |
u64 ipv6_addr1; | |
u64 ipv6_addr2; | |
u64 set[OPLUS_MAX_RECORD_IP_LEN*3]; | |
}; | |
struct tcp_hook_simple_struct { | |
u32 count; | |
u64 set[OPLUS_MAX_RECORD_APP_WAKEUP_LEN*3+1]; | |
}; | |
static struct tcp_hook_simple_struct app_wakeup_monitor_list = { | |
.set = {0}, | |
}; | |
static bool tcp_input_sch_work = false; | |
static atomic_t tcp_is_input = ATOMIC_INIT(0);/*1=v4_input,2=v6_input,3=output,0=default*/ | |
static struct timespec tcp_last_transmission_stamp; | |
static struct tcp_hook_struct tcp_output_list = { | |
.is_ipv6 = false, | |
.set = {0}, | |
}; | |
static struct tcp_hook_struct tcp_input_list = { | |
.is_ipv6 = false, | |
.set = {0}, | |
}; | |
static struct tcp_hook_struct tcp_output_retrans_list = { | |
.is_ipv6 = false, | |
.set = {0}, | |
}; | |
static struct tcp_hook_struct tcp_input_retrans_list = { | |
.is_ipv6 = false, | |
.set = {0}, | |
}; | |
DECLARE_WORK(tcp_output_hook_work, tcp_output_hook_work_callback); | |
DECLARE_WORK(tcp_input_hook_work, tcp_input_hook_work_callback); | |
DECLARE_WORK(tcp_output_tcpsynretrans_hook_work, tcp_output_tcpsynretrans_hook_work_callback); | |
DECLARE_WORK(tcp_input_tcpsynretrans_hook_work, tcp_input_tcpsynretrans_hook_work_callback); | |
/*Add for modem eap buffer*/ | |
u64 oplus_mdaci_nw_wakeup[OPLUS_NW_WAKEUP_SUM] = {0}; | |
static u64 mdaci_service_wakeup_times[OPLUS_MAX_QRTR_SERVICE_LEN][4] = {{0}}; | |
static struct tcp_hook_struct mdaci_tcp_output_list = { | |
.is_ipv6 = false, | |
.set = {0}, | |
}; | |
static struct tcp_hook_struct mdaci_tcp_input_list = { | |
.is_ipv6 = false, | |
.set = {0}, | |
}; | |
static struct tcp_hook_struct mdaci_tcp_output_retrans_list = { | |
.is_ipv6 = false, | |
.set = {0}, | |
}; | |
static struct tcp_hook_struct mdaci_tcp_input_retrans_list = { | |
.is_ipv6 = false, | |
.set = {0}, | |
}; | |
/*Add for Netlink*/ | |
enum{ | |
NW_POWER_ANDROID_PID = 0x11, | |
NW_POWER_BOOT_MONITOR = 0x12, | |
NW_POWER_STOP_MONITOR = 0x13, | |
NW_POWER_STOP_MONITOR_UNSL = 0x14, | |
NW_POWER_UNSL_MONITOR = 0x15, | |
NW_POWER_REQUEST_MDACI = 0x16, | |
NW_POWER_REPORT_MDACI = 0x17, | |
NW_POWER_BLACK_LIST = 0x18, | |
NW_POWER_REQUEST_BLACK_REJECT = 0x19, | |
NW_POWER_REPORT_BLACK_REJECT = 0x1A, | |
NW_POWER_REQUEST_APP_WAKEUP = 0x1D, | |
NW_POWER_REPORT_APP_WAKEUP = 0x1E, | |
NW_POWER_REPORT_MDACI_APP_WAKEUP = 0x1F, | |
}; | |
static DEFINE_MUTEX(netlink_mutex); | |
static u32 oplus_nwpower_pid = 0; | |
static struct sock *oplus_nwpower_sock; | |
/*Add for unsl wakeup msg*/ | |
#define KERNEL_UNSL_MONITOR_LEN 7 | |
static u64 wakeup_unsl_msg[KERNEL_UNSL_MONITOR_LEN] = {0}; | |
#define KERNEL_UNSL_APP_WAKEUP_LEN 300 | |
static u32 blacklist_len = 0; | |
static u32 blacklist_uid[KERNEL_UNSL_APP_WAKEUP_LEN] = {0}; | |
#define OPLUS_MAX_RECORD_BLACK_REJECT_LEN 100 | |
#define KERNEL_UNSL_BLACK_REJECT_LEN 201 | |
static u32 record_blacklist_reject_index = 0; | |
static u64 blacklist_reject_uid[KERNEL_UNSL_BLACK_REJECT_LEN] = {0}; | |
/*Add for mdaci wakeup apps*/ | |
static struct tcp_hook_simple_struct mdaci_app_wakeup_monitor_list = { | |
.set = {0}, | |
}; | |
static uid_t get_uid_from_sock(const struct sock *sk) | |
{ | |
uid_t sk_uid; | |
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)) | |
const struct file *filp = NULL; | |
#endif | |
if(NULL == sk || !sk_fullsock(sk) || NULL == sk->sk_socket) { | |
return 0; | |
} | |
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)) | |
filp = sk->sk_socket->file; | |
if(NULL == filp) { | |
return 0; | |
} | |
sk_uid = __kuid_val(filp->f_cred->fsuid); | |
#else | |
sk_uid = __kuid_val(sk->sk_uid); | |
#endif | |
return sk_uid; | |
} | |
static void nwpower_unsl_blacklist_reject(void) { | |
if (record_blacklist_reject_index > 0) { | |
blacklist_reject_uid[0] = record_blacklist_reject_index; | |
nwpower_send_to_user(NW_POWER_REPORT_BLACK_REJECT, (char*)blacklist_reject_uid, sizeof(blacklist_reject_uid)); | |
memset(blacklist_reject_uid, 0x0, OPLUS_MAX_RECORD_BLACK_REJECT_LEN * sizeof(u32)); | |
record_blacklist_reject_index = 0; | |
} | |
} | |
extern bool oplus_check_socket_in_blacklist(int is_input, struct socket *sock) { | |
int i = 0; | |
uid_t uid = 0; | |
struct timespec now_ts; | |
if (blacklist_len > 0 && sock && sock->sk && (sock->sk->sk_family == 2 || sock->sk->sk_family == 10)) { | |
uid = get_uid_from_sock(sock->sk); | |
if(uid == 0) { | |
return false; | |
} | |
now_ts = current_kernel_time(); | |
for (i = 0; i < blacklist_len; i++) { | |
if(blacklist_uid[i] == uid) { | |
if (is_input != 1) { | |
if (record_blacklist_reject_index >= OPLUS_MAX_RECORD_BLACK_REJECT_LEN) { | |
nwpower_unsl_blacklist_reject(); | |
} | |
if (record_blacklist_reject_index < OPLUS_MAX_RECORD_BLACK_REJECT_LEN) { | |
blacklist_reject_uid[record_blacklist_reject_index*2+1] = now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000; | |
blacklist_reject_uid[record_blacklist_reject_index*2+2] = (u64)uid << 32 | is_input; | |
record_blacklist_reject_index++; | |
} | |
} | |
printk("[oplus_netcontroller] blacklist reject, stamp=%ld, is_input=%d, uid=%u", | |
now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000, is_input, uid); | |
return true; | |
} | |
} | |
return false; | |
} | |
return false; | |
} | |
static int nwpower_set_blacklist_uids(struct nlmsghdr *nlh) { | |
u32 *data; | |
int i = 0; | |
data = (u32 *)NLMSG_DATA(nlh); | |
memset(blacklist_uid, 0x0, KERNEL_UNSL_APP_WAKEUP_LEN * sizeof(u32)); | |
blacklist_len = *data; | |
for (i = 0; i < blacklist_len; i++) { | |
if (i >= KERNEL_UNSL_APP_WAKEUP_LEN) { | |
printk("[oplus_netcontroller] uids length exceed the limit!"); | |
break; | |
} else { | |
blacklist_uid[i] = *(data + i + 1); | |
} | |
} | |
return 0; | |
} | |
extern void oplus_match_modem_wakeup(void) { | |
atomic_set(&qrtr_first_msg, 1); | |
oplus_nw_wakeup[OPLUS_NW_MPSS]++; | |
oplus_mdaci_nw_wakeup[OPLUS_NW_MPSS]++; | |
} | |
extern void oplus_match_wlan_wakeup(void) { | |
oplus_nw_wakeup[OPLUS_NW_WIFI]++; | |
oplus_mdaci_nw_wakeup[OPLUS_NW_WIFI]++; | |
} | |
static void match_qrtr_new_service_port(int id, int port, u64 qrtr[][4]) { | |
int i; | |
for (i = 0; i < OPLUS_MAX_QRTR_SERVICE_LEN; ++i) { | |
if (qrtr[i][0] == 0) { | |
qrtr[i][0] = 1; | |
qrtr[i][1] = id; | |
qrtr[i][2] = port; | |
break; | |
} else { | |
if (qrtr[i][1] == id && qrtr[i][2] == port) { | |
break; | |
} | |
} | |
} | |
} | |
static void match_qrtr_del_service_port(int id, u64 qrtr[][4]) { | |
int i; | |
for (i = 0; i < OPLUS_MAX_QRTR_SERVICE_LEN; ++i) { | |
if (qrtr[i][0] == 1 && qrtr[i][1] == id) { | |
qrtr[i][0] = 0; | |
break; | |
} | |
} | |
} | |
extern void oplus_match_qrtr_service_port(int type, int id, int port) { | |
/* | |
if (type == QRTR_TYPE_NEW_SERVER) { | |
match_qrtr_new_service_port(id, port, service_wakeup_times); | |
match_qrtr_new_service_port(id, port, mdaci_service_wakeup_times); | |
} else if (type == QRTR_TYPE_DEL_SERVER) { | |
match_qrtr_del_service_port(id, service_wakeup_times); | |
match_qrtr_del_service_port(id, mdaci_service_wakeup_times); | |
} | |
*/ | |
} | |
static void __oplus_match_qrtr_wakeup(int src_node, int src_port, int dst_port, | |
unsigned int arg1, unsigned int arg2, u64 qrtr[][4], u64 wakeup[OPLUS_NW_WAKEUP_SUM], bool prt) { | |
int i; | |
int repeat[4] = {0}; | |
int repeat_index = 0; | |
if (atomic_read(&qrtr_wakeup_hook_boot) == 1 && atomic_read(&qrtr_first_msg) == 1) { | |
for (i = 0; i < OPLUS_MAX_QRTR_SERVICE_LEN; ++i) { | |
if (qrtr[i][0] == 1 && (qrtr[i][2] == src_port || qrtr[i][2] == dst_port)) { | |
if (repeat_index < 4) repeat[repeat_index++] = i; | |
} | |
} | |
if (repeat_index == 1) { | |
qrtr[repeat[0]][3]++; | |
if (prt) | |
printk("[oplus_nwpower] QrtrWakeup: ServiceID: %d, NodeID: %d, PortID: %d, Msg: [%08x %08x], Count: %d", | |
qrtr[repeat[0]][1], src_node, qrtr[repeat[0]][2], arg1, arg2, qrtr[repeat[0]][3]); | |
} else if (repeat_index > 1) { | |
qrtr[repeat[repeat_index-1]][3]++; | |
if (prt) | |
printk("[oplus_nwpower] QrtrWakeup: ServiceID: [%d/%d/%d/%d], NodeID: %d, PortID: %d, Msg: [%08x %08x], Count: %d", | |
qrtr[repeat[0]][1], qrtr[repeat[1]][1], | |
repeat_index > 2 ? qrtr[repeat[2]][1]:-1, | |
repeat_index > 3 ? qrtr[repeat[3]][1]:-1, | |
src_node, qrtr[repeat[repeat_index-1]][2], arg1, arg2, qrtr[repeat[repeat_index-1]][3]); | |
} else { | |
if (prt) | |
printk("[oplus_nwpower] QrtrWakeup: ServiceID: %d, NodeID: %d, PortID: %d, Msg: [%08x %08x], Count: %d", | |
-1, src_node, -1, arg1, arg2, -1); | |
} | |
wakeup[OPLUS_NW_QRTR]++; | |
if (src_node == GLINK_MODEM_NODE_ID) { | |
wakeup[OPLUS_NW_MD]++; | |
} else if (src_node == GLINK_ADSP_NODE_ID) { | |
} else if (src_node == GLINK_CDSP_NODE_ID) { | |
} else if (src_node == GLINK_SLPI_NODE_ID) { | |
} else if (src_node == GLINK_NPU_NODE_ID) { | |
} | |
} | |
} | |
extern void oplus_match_qrtr_wakeup(int src_node, int src_port, int dst_port, unsigned int arg1, unsigned int arg2) { | |
__oplus_match_qrtr_wakeup(src_node, src_port, dst_port, arg1, arg2, service_wakeup_times, oplus_nw_wakeup, true); | |
__oplus_match_qrtr_wakeup(src_node, src_port, dst_port, arg1, arg2, mdaci_service_wakeup_times, oplus_mdaci_nw_wakeup, false); | |
atomic_set(&qrtr_first_msg, 0); | |
} | |
extern void oplus_update_qrtr_flag(int flag) { | |
if (atomic_read(&qrtr_wakeup_hook_boot) == 1) { | |
atomic_set(&qrtr_first_msg, flag); | |
} | |
} | |
static void print_qrtr_wakeup(bool unsl, u64 qrtr[][4], u64 wakeup[OPLUS_NW_WAKEUP_SUM], bool prt) { | |
u64 temp[5][4] = {{0}}; | |
u64 max_count = 0; | |
u64 max_count_id = 0; | |
int j; | |
int i; | |
int k; | |
for (j = 0; j < 5; ++j) { | |
for (i = 0; i < OPLUS_MAX_QRTR_SERVICE_LEN; ++i) { | |
if (qrtr[i][0] == 1 && qrtr[i][3] > max_count) { | |
max_count = qrtr[i][3]; | |
max_count_id = i; | |
} | |
} | |
for (k = 0;k < 4; ++k) { | |
temp[j][k] = qrtr[max_count_id][k]; | |
} | |
max_count = 0; | |
qrtr[max_count_id][3] = 0; | |
if (unsl) { | |
if (temp[j][3] > 0) wakeup_unsl_msg[j] = temp[j][2] << 32 | ((u32)temp[j][1] << 16 | (u16)temp[j][3]); | |
} | |
if (temp[j][3] > 0) printk("[oplus_nwpower] QrtrWakeupMax[%d]: ServiceID: %d, PortID: %d, Count: %d", | |
j, temp[j][1], temp[j][2], temp[j][3]); | |
} | |
if (unsl) { | |
wakeup_unsl_msg[5] = (wakeup[OPLUS_NW_MD] << 48) | | |
((wakeup[OPLUS_NW_QRTR] & 0xFFFF) << 32) | | |
((wakeup[OPLUS_NW_WIFI] & 0xFFFF) << 16) | | |
(wakeup[OPLUS_NW_MPSS] & 0xFFFF); | |
} | |
if (prt) | |
printk("[oplus_nwpower] AllWakeups: Mpss: %d, Qrtr: %d, Modem: %d, WiFi: %d", | |
wakeup[OPLUS_NW_MPSS], wakeup[OPLUS_NW_QRTR], wakeup[OPLUS_NW_MD], wakeup[OPLUS_NW_WIFI]); | |
} | |
extern void oplus_match_ipa_ip_wakeup(int type, struct sk_buff *skb) { | |
struct timespec now_ts; | |
struct iphdr *tmp_v4iph; | |
struct ipv6hdr *tmp_v6iph; | |
tcp_input_sch_work = false; | |
if (atomic_read(&ipa_wakeup_hook_boot) == 1) { | |
if (atomic_read(&tcp_is_input) == 0) { | |
now_ts = current_kernel_time(); | |
if (((now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000) - (tcp_last_transmission_stamp.tv_sec * 1000 + tcp_last_transmission_stamp.tv_nsec / 1000000)) | |
> OPLUS_TRANSMISSION_INTERVAL) { | |
if (type == OPLUS_TCP_TYPE_V4) { | |
tmp_v4iph = ip_hdr(skb); | |
atomic_set(&tcp_is_input, OPLUS_TCP_TYPE_V4); | |
tcp_input_list.ipv4_addr = tmp_v4iph->saddr; | |
tcp_input_list.is_ipv6 = false; | |
mdaci_tcp_input_list.ipv4_addr = tcp_input_list.ipv4_addr; | |
mdaci_tcp_input_list.is_ipv6 = false; | |
} else { | |
tmp_v6iph = ipv6_hdr(skb); | |
atomic_set(&tcp_is_input, OPLUS_TCP_TYPE_V6); | |
tcp_input_list.ipv6_addr1 = (u64)ntohl(tmp_v6iph->saddr.s6_addr32[0]) << 32 | ntohl(tmp_v6iph->saddr.s6_addr32[1]); | |
tcp_input_list.ipv6_addr2 = (u64)ntohl(tmp_v6iph->saddr.s6_addr32[2]) << 32 | ntohl(tmp_v6iph->saddr.s6_addr32[3]); | |
tcp_input_list.is_ipv6 = true; | |
mdaci_tcp_input_list.ipv6_addr1 = tcp_input_list.ipv6_addr1; | |
mdaci_tcp_input_list.ipv6_addr2 = tcp_input_list.ipv6_addr2; | |
mdaci_tcp_input_list.is_ipv6 = true; | |
} | |
tcp_input_list.uid = 0; | |
tcp_input_list.pid = 0; | |
mdaci_tcp_input_list.uid = 0; | |
mdaci_tcp_input_list.pid = 0; | |
} | |
} | |
tcp_last_transmission_stamp = current_kernel_time(); | |
} | |
} | |
extern void oplus_match_ipa_tcp_wakeup(int type, struct sock *sk) { | |
if (atomic_read(&ipa_wakeup_hook_boot) == 1) { | |
if (atomic_read(&tcp_is_input) == type && !tcp_input_sch_work) { | |
if (sk->sk_state != TCP_TIME_WAIT) { | |
tcp_input_list.uid = get_uid_from_sock(sk); | |
tcp_input_list.pid = sk->sk_oplus_pid; | |
mdaci_tcp_input_list.uid = tcp_input_list.uid; | |
mdaci_tcp_input_list.pid = tcp_input_list.pid; | |
} | |
schedule_work(&tcp_input_hook_work); | |
tcp_input_sch_work = true; | |
} | |
sk->oplus_last_rcv_stamp[0] = sk->oplus_last_rcv_stamp[1]; | |
sk->oplus_last_rcv_stamp[1] = tcp_last_transmission_stamp.tv_sec * 1000 + tcp_last_transmission_stamp.tv_nsec / 1000000; | |
} | |
} | |
extern void oplus_ipa_schedule_work(void) { | |
if (atomic_read(&ipa_wakeup_hook_boot) == 1 && atomic_read(&tcp_is_input) == 1 && !tcp_input_sch_work) { | |
schedule_work(&tcp_input_hook_work); | |
tcp_input_sch_work = true; | |
} | |
} | |
extern void oplus_match_tcp_output(struct sock *sk) { | |
struct timespec now_ts; | |
if (atomic_read(&ipa_wakeup_hook_boot) == 1) { | |
if (atomic_read(&tcp_is_input) == 0) { | |
now_ts = current_kernel_time(); | |
if (((now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000) - (tcp_last_transmission_stamp.tv_sec * 1000 + tcp_last_transmission_stamp.tv_nsec / 1000000)) | |
> OPLUS_TRANSMISSION_INTERVAL) { | |
atomic_set(&tcp_is_input, 3); | |
if (sk->sk_v6_daddr.s6_addr32[0] == 0 && sk->sk_v6_daddr.s6_addr32[1] == 0) { | |
tcp_output_list.ipv4_addr = sk->sk_daddr; | |
tcp_output_list.is_ipv6 = false; | |
mdaci_tcp_output_list.ipv4_addr = tcp_output_list.ipv4_addr; | |
mdaci_tcp_output_list.is_ipv6 = false; | |
} else { | |
tcp_output_list.ipv6_addr1 = (u64)ntohl(sk->sk_v6_daddr.s6_addr32[0]) << 32 | ntohl(sk->sk_v6_daddr.s6_addr32[1]); | |
tcp_output_list.ipv6_addr2 = (u64)ntohl(sk->sk_v6_daddr.s6_addr32[2]) << 32 | ntohl(sk->sk_v6_daddr.s6_addr32[3]); | |
tcp_output_list.is_ipv6 = true; | |
mdaci_tcp_output_list.ipv6_addr1 = tcp_output_list.ipv6_addr1; | |
mdaci_tcp_output_list.ipv6_addr2 = tcp_output_list.ipv6_addr2; | |
mdaci_tcp_output_list.is_ipv6 = true; | |
} | |
tcp_output_list.uid = get_uid_from_sock(sk); | |
tcp_output_list.pid = sk->sk_oplus_pid; | |
mdaci_tcp_output_list.uid = tcp_output_list.uid; | |
mdaci_tcp_output_list.pid = tcp_output_list.pid; | |
schedule_work(&tcp_output_hook_work); | |
} | |
} | |
tcp_last_transmission_stamp = current_kernel_time(); | |
sk->oplus_last_send_stamp[0] = sk->oplus_last_send_stamp[1]; | |
sk->oplus_last_send_stamp[1] = tcp_last_transmission_stamp.tv_sec * 1000 + tcp_last_transmission_stamp.tv_nsec / 1000000; | |
} | |
} | |
extern void oplus_match_tcp_input_retrans(struct sock *sk) { | |
struct timespec now_ts; | |
if (atomic_read(&tcpsynretrans_hook_boot) == 1) { | |
now_ts = current_kernel_time(); | |
if (((now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000) - sk->oplus_last_rcv_stamp[0]) > OPLUS_TCP_RETRANSMISSION_INTERVAL) { | |
if (sk->sk_v6_daddr.s6_addr32[0] == 0 && sk->sk_v6_daddr.s6_addr32[1] == 0) { | |
tcp_input_retrans_list.ipv4_addr = sk->sk_daddr; | |
tcp_input_retrans_list.is_ipv6 = false; | |
mdaci_tcp_input_retrans_list.ipv4_addr = tcp_input_retrans_list.ipv4_addr; | |
mdaci_tcp_input_retrans_list.is_ipv6 = false; | |
} else { | |
tcp_input_retrans_list.ipv6_addr1 = (u64)ntohl(sk->sk_v6_daddr.s6_addr32[0]) << 32 | ntohl(sk->sk_v6_daddr.s6_addr32[1]); | |
tcp_input_retrans_list.ipv6_addr2 = (u64)ntohl(sk->sk_v6_daddr.s6_addr32[2]) << 32 | ntohl(sk->sk_v6_daddr.s6_addr32[3]); | |
tcp_input_retrans_list.is_ipv6 = true; | |
mdaci_tcp_input_retrans_list.ipv6_addr1 = tcp_input_retrans_list.ipv6_addr1; | |
mdaci_tcp_input_retrans_list.ipv6_addr2 = tcp_input_retrans_list.ipv6_addr2; | |
mdaci_tcp_input_retrans_list.is_ipv6 = true; | |
} | |
tcp_input_retrans_list.uid = get_uid_from_sock(sk); | |
tcp_input_retrans_list.pid = sk->sk_oplus_pid; | |
mdaci_tcp_input_retrans_list.uid = tcp_input_retrans_list.uid; | |
mdaci_tcp_input_retrans_list.pid = tcp_input_retrans_list.pid; | |
schedule_work(&tcp_input_tcpsynretrans_hook_work); | |
} | |
} | |
} | |
extern void oplus_match_tcp_output_retrans(struct sock *sk) { | |
struct timespec now_ts; | |
if (atomic_read(&tcpsynretrans_hook_boot) == 1) { | |
now_ts = current_kernel_time(); | |
if (((now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000) - sk->oplus_last_send_stamp[0]) > OPLUS_TCP_RETRANSMISSION_INTERVAL) { | |
if (sk->sk_v6_daddr.s6_addr32[0] == 0 && sk->sk_v6_daddr.s6_addr32[1] == 0) { | |
tcp_output_retrans_list.ipv4_addr = sk->sk_daddr; | |
tcp_output_retrans_list.is_ipv6 = false; | |
mdaci_tcp_output_retrans_list.ipv4_addr = tcp_output_retrans_list.ipv4_addr; | |
mdaci_tcp_output_retrans_list.is_ipv6 = false; | |
} else { | |
tcp_output_retrans_list.ipv6_addr1 = (u64)ntohl(sk->sk_v6_daddr.s6_addr32[0]) << 32 | ntohl(sk->sk_v6_daddr.s6_addr32[1]); | |
tcp_output_retrans_list.ipv6_addr2 = (u64)ntohl(sk->sk_v6_daddr.s6_addr32[2]) << 32 | ntohl(sk->sk_v6_daddr.s6_addr32[3]); | |
tcp_output_retrans_list.is_ipv6 = true; | |
mdaci_tcp_output_retrans_list.ipv6_addr1 = tcp_output_retrans_list.ipv6_addr1; | |
mdaci_tcp_output_retrans_list.ipv6_addr2 = tcp_output_retrans_list.ipv6_addr2; | |
mdaci_tcp_output_retrans_list.is_ipv6 = true; | |
} | |
tcp_output_retrans_list.uid = get_uid_from_sock(sk); | |
tcp_output_retrans_list.pid = sk->sk_oplus_pid; | |
mdaci_tcp_output_retrans_list.uid = tcp_output_retrans_list.uid; | |
mdaci_tcp_output_retrans_list.pid = tcp_output_retrans_list.pid; | |
schedule_work(&tcp_output_tcpsynretrans_hook_work); | |
} | |
} | |
} | |
static void tcp_hook_insert_sort(struct tcp_hook_struct *pval) { | |
int i; | |
int j; | |
u64 count = 0; | |
u64 temp_sort[3] = {0}; | |
for (i = 1; i < OPLUS_MAX_RECORD_IP_LEN; ++i) { | |
temp_sort[0] = pval->set[3*i]; | |
temp_sort[1] = pval->set[3*i+1]; | |
temp_sort[2] = pval->set[3*i+2]; | |
if (temp_sort[0] == 0 && temp_sort[1] == 0 && temp_sort[2] != 0) { | |
count = (temp_sort[2] & 0xFFFC000000000000) >> 50; | |
} else { | |
count = temp_sort[2] & 0xFFFFFFFF; | |
} | |
j = i - 1; | |
while (j >= 0) { | |
if ((pval->set[3*j] == 0) && (pval->set[3*j+1] == 0) && (pval->set[3*j+2] != 0)) { | |
if (count > (pval->set[3*j+2] & 0xFFFC000000000000) >> 50) { | |
pval->set[3*(j+1)] = pval->set[3*j]; | |
pval->set[3*(j+1)+1] = pval->set[3*j+1]; | |
pval->set[3*(j+1)+2] = pval->set[3*j+2]; | |
--j; | |
} else { | |
break; | |
} | |
} else { | |
if (count > (pval->set[3*j+2] & 0xFFFFFFFF)) { | |
pval->set[3*(j+1)] = pval->set[3*j]; | |
pval->set[3*(j+1)+1] = pval->set[3*j+1]; | |
pval->set[3*(j+1)+2] = pval->set[3*j+2]; | |
--j; | |
} else { | |
break; | |
} | |
} | |
} | |
pval->set[3*(j+1)] = temp_sort[0]; | |
pval->set[3*(j+1)+1] = temp_sort[1]; | |
pval->set[3*(j+1)+2] = temp_sort[2]; | |
} | |
} | |
static int match_tcp_hook(struct tcp_hook_struct *pval) { | |
int i; | |
u32 uid = 0; | |
u64 count = 0; | |
bool handle = false; | |
for (i = 0; i < OPLUS_MAX_RECORD_IP_LEN; ++i) { | |
if (pval->is_ipv6) { | |
if (pval->ipv6_addr1 == pval->set[3*i] && pval->ipv6_addr2 == pval->set[3*i+1]) { | |
count = pval->set[3*i+2] & 0xFFFFFFFF; | |
uid = (pval->set[3*i+2] & 0xFFFFFFFF00000000) >> 32; | |
if (uid == 0) { | |
pval->set[3*i+2] = (u64)(pval->uid) << 32 | (u32)(++count); | |
} else { | |
pval->set[3*i+2] = (pval->set[3*i+2] & 0xFFFFFFFF00000000) | (u32)(++count); | |
} | |
handle = true; | |
break; | |
} else if (pval->set[3*i+2] == 0) { | |
pval->set[3*i] = pval->ipv6_addr1; | |
pval->set[3*i+1] = pval->ipv6_addr2; | |
count = 1; | |
pval->set[3*i+2] = (u64)(pval->uid) << 32 | (u32)(count); | |
handle = true; | |
break; | |
} | |
} else { | |
if (pval->ipv4_addr == (pval->set[3*i+2] & 0xFFFFFFFF)) { | |
count = (pval->set[3*i+2] & 0xFFFC000000000000) >> 50; | |
uid = (pval->set[3*i+2] & 0x3FFFF00000000) >> 32; | |
if (uid == 0) { | |
count++; | |
count = count << 18 | (pval->uid & 0x3FFFF); | |
pval->set[3*i+2] = count << 32 | (pval->set[3*i+2] & 0xFFFFFFFF); | |
} else { | |
pval->set[3*i+2] = (++count) << 50 | (pval->set[3*i+2] & 0x3FFFFFFFFFFFF); | |
} | |
handle = true; | |
break; | |
} else if (pval->set[3*i+2] == 0) { | |
count = 1 << 18 | (pval->uid & 0x3FFFF); | |
pval->set[3*i+2] = count << 32 | pval->ipv4_addr; | |
handle = true; | |
break; | |
} | |
} | |
} | |
if (!handle) { | |
tcp_hook_insert_sort(pval); | |
i = OPLUS_MAX_RECORD_IP_LEN - 1; | |
if (pval->is_ipv6) { | |
pval->set[3*i] = pval->ipv6_addr1; | |
pval->set[3*i+1] = pval->ipv6_addr2; | |
count = 1; | |
pval->set[3*i+2] = (u64)(pval->uid) << 32 | (u32)(count); | |
} else { | |
pval->set[3*i] = 0; | |
pval->set[3*i+1] = 0; | |
count = 1 << 18 | (pval->uid & 0x3FFFF); | |
pval->set[3*i+2] = (u64)count << 32 | pval->ipv4_addr; | |
} | |
} | |
return i; | |
} | |
static bool tcp_monitor_check_uid_in_whitelist(int uid) { | |
int i = 0; | |
if (blacklist_len > 0) { | |
for (i = 0; i < blacklist_len; i++) { | |
if (i >= KERNEL_UNSL_APP_WAKEUP_LEN) { | |
printk("[oplus_netcontroller] uids length exceed the limit!"); | |
return true; | |
} | |
if(blacklist_uid[i] == uid) { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
static void nwpower_unsl_app_wakeup(void) | |
{ | |
nwpower_send_to_user(NW_POWER_REPORT_APP_WAKEUP, (char*)app_wakeup_monitor_list.set, sizeof(app_wakeup_monitor_list.set)); | |
app_wakeup_monitor_list.count = 0; | |
memset(app_wakeup_monitor_list.set, 0x0, sizeof(app_wakeup_monitor_list.set)); | |
} | |
static void app_wakeup_monitor(struct tcp_hook_simple_struct *pval, bool is_block, bool is_input, int pid, int uid) { | |
struct timespec now_ts = current_kernel_time(); | |
int block = is_block ? 1:0; | |
int input = is_input ? 1:0; | |
int whitelist = tcp_monitor_check_uid_in_whitelist(uid) ? 1:0; | |
if (pval->count < OPLUS_MAX_RECORD_APP_WAKEUP_LEN) { | |
pval->set[1+pval->count*3] = (u64)pid << 32 | uid; | |
pval->set[1+pval->count*3+1] = | |
((u64)block << 48) | | |
((u64)(input & 0xFFFF) << 32) | | |
((u64)(whitelist & 0xFFFF) << 16) | | |
(0xFFFF); | |
pval->set[1+pval->count*3+2] = now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000; | |
/* | |
printk("[oplus_nwpower] count:%d, block:%d, input:%d, pid:%d, uid:%d, stamp:%ld", | |
pval->set[0], block, input, pid, uid, pval->set[1+pval->count*3+2]); | |
*/ | |
pval->set[0] = (++pval->count); | |
} else { | |
printk("[oplus_nwpower] warning! OPLUS_MAX_RECORD_APP_WAKEUP_LEN reached"); | |
} | |
} | |
static void tcp_output_hook_work_callback(struct work_struct *work) { | |
int i = match_tcp_hook(&tcp_output_list); | |
match_tcp_hook(&mdaci_tcp_output_list); | |
app_wakeup_monitor(&app_wakeup_monitor_list, false, false, tcp_output_list.pid, tcp_output_list.uid); | |
app_wakeup_monitor(&mdaci_app_wakeup_monitor_list, false, false, tcp_output_list.pid, tcp_output_list.uid); | |
if (tcp_output_list.is_ipv6) { | |
printk("[oplus_nwpower] IPAOutputWakeup: [%ld,****], %d, %d, %d", | |
tcp_output_list.ipv6_addr1, | |
tcp_output_list.pid, tcp_output_list.uid, tcp_output_list.set[3*i+2] & 0xFFFFFFFF); | |
} else { | |
printk("[oplus_nwpower] IPAOutputWakeup: %#X, %d, %d, %d", | |
tcp_output_list.ipv4_addr & 0xFFFFFF, tcp_output_list.pid, tcp_output_list.uid, | |
(tcp_output_list.set[3*i+2] & 0xFFFC000000000000) >> 50); | |
} | |
atomic_set(&tcp_is_input, 0); | |
} | |
static void tcp_input_hook_work_callback(struct work_struct *work) { | |
int i = match_tcp_hook(&tcp_input_list); | |
match_tcp_hook(&mdaci_tcp_input_list); | |
app_wakeup_monitor(&app_wakeup_monitor_list, false, true, tcp_input_list.pid, tcp_input_list.uid); | |
app_wakeup_monitor(&mdaci_app_wakeup_monitor_list, false, true, tcp_input_list.pid, tcp_input_list.uid); | |
if (tcp_input_list.is_ipv6) { | |
printk("[oplus_nwpower] IPAInputWakeup: [%ld,****], %d, %d, %d", | |
tcp_input_list.ipv6_addr1, | |
tcp_input_list.pid, tcp_input_list.uid, tcp_input_list.set[3*i+2] & 0xFFFFFFFF); | |
} else { | |
printk("[oplus_nwpower] IPAInputWakeup: %#X, %d, %d, %d", | |
tcp_input_list.ipv4_addr & 0xFFFFFF, tcp_input_list.pid, tcp_input_list.uid, | |
(tcp_input_list.set[3*i+2] & 0xFFFC000000000000) >> 50); | |
} | |
atomic_set(&tcp_is_input, 0); | |
} | |
static void tcp_output_tcpsynretrans_hook_work_callback(struct work_struct *work) { | |
int i = match_tcp_hook(&tcp_output_retrans_list); | |
match_tcp_hook(&mdaci_tcp_output_retrans_list); | |
if (tcp_output_retrans_list.is_ipv6) { | |
if (tcp_output_retrans_list.ipv6_addr1 == 0 && tcp_output_retrans_list.ipv6_addr2 == 0) | |
return; | |
printk("[oplus_nwpower] TCPOutputRetrans: [%ld,****], %d, %d, %d", | |
tcp_output_retrans_list.ipv6_addr1, | |
tcp_output_retrans_list.pid, tcp_output_retrans_list.uid, tcp_output_retrans_list.set[3*i+2] & 0xFFFFFFFF); | |
} else { | |
if (tcp_output_retrans_list.ipv4_addr == 0) | |
return; | |
printk("[oplus_nwpower] TCPOutputRetrans: %#X, %d, %d, %d", | |
tcp_output_retrans_list.ipv4_addr & 0xFFFFFF, tcp_output_retrans_list.pid, tcp_output_retrans_list.uid, | |
(tcp_output_retrans_list.set[3*i+2] & 0xFFFC000000000000) >> 50); | |
} | |
} | |
static void tcp_input_tcpsynretrans_hook_work_callback(struct work_struct *work) { | |
int i = match_tcp_hook(&tcp_input_retrans_list); | |
match_tcp_hook(&mdaci_tcp_input_retrans_list); | |
if (tcp_input_retrans_list.is_ipv6) { | |
if (tcp_input_retrans_list.ipv6_addr1 == 0 && tcp_input_retrans_list.ipv6_addr2 == 0) | |
return; | |
printk("[oplus_nwpower] TCPInputRetrans: [%ld,****], %d, %d, %d", | |
tcp_input_retrans_list.ipv6_addr1, | |
tcp_input_retrans_list.pid, tcp_input_retrans_list.uid, tcp_input_retrans_list.set[3*i+2] & 0xFFFFFFFF); | |
} else { | |
if (tcp_input_retrans_list.ipv4_addr == 0) | |
return; | |
printk("[oplus_nwpower] TCPInputRetrans: %#X, %d, %d, %d", | |
tcp_input_retrans_list.ipv4_addr & 0xFFFFFF, tcp_input_retrans_list.pid, tcp_input_retrans_list.uid, | |
(tcp_input_retrans_list.set[3*i+2] & 0xFFFC000000000000) >> 50); | |
} | |
} | |
static int print_tcp_wakeup(const char *type, struct tcp_hook_struct *pval, bool prt) { | |
int i; | |
u32 count; | |
u32 tcp_wakeup_times = 0; | |
tcp_hook_insert_sort(pval); | |
for (i = 0; i < 5;++i) { | |
if ((pval->set[3*i] == 0) && (pval->set[3*i+1] == 0) && (pval->set[3*i+2] != 0)) { | |
count = (pval->set[3*i+2] & 0xFFFC000000000000) >> 50; | |
if (prt && count > 0) { | |
printk("[oplus_nwpower] IPA%sMAX[%d]: %#X, %d, %d", | |
type, i, (pval->set[3*i+2] & 0xFFFFFFFF) & 0xFFFFFF, | |
(pval->set[3*i+2] & 0x3FFFF00000000) >> 32, count); | |
} | |
} else { | |
count = pval->set[3*i+2] & 0xFFFFFFFF; | |
if (prt && count > 0) { | |
printk("[oplus_nwpower] IPA%sMAX[%d]: [%ld,****], %d, %d", | |
type, i, pval->set[3*i], | |
pval->set[3*i+2] >> 32, count); | |
} | |
} | |
} | |
for (i = 0; i < OPLUS_MAX_RECORD_IP_LEN;++i) { | |
if ((pval->set[3*i] == 0) && (pval->set[3*i+1] == 0) && (pval->set[3*i+2] != 0)) { | |
count = (pval->set[3*i+2] & 0xFFFC000000000000) >> 50; | |
} else { | |
count = pval->set[3*i+2] & 0xFFFFFFFF; | |
} | |
if (count > 0) { | |
tcp_wakeup_times += count; | |
} | |
} | |
return tcp_wakeup_times; | |
} | |
static void print_ipa_wakeup(bool unsl, struct tcp_hook_struct *ptcp_in, struct tcp_hook_struct *ptcp_out, | |
struct tcp_hook_struct *ptcp_re_in, struct tcp_hook_struct *ptcp_re_out, u64 wakeup[OPLUS_NW_WAKEUP_SUM], bool prt) { | |
wakeup[OPLUS_NW_TCP_IN] = print_tcp_wakeup("Input", ptcp_in, prt); | |
wakeup[OPLUS_NW_TCP_OUT] = print_tcp_wakeup("Output", ptcp_out, prt); | |
wakeup[OPLUS_NW_TCP_RE_IN] = print_tcp_wakeup("InputRetrans", ptcp_re_in, prt); | |
wakeup[OPLUS_NW_TCP_RE_OUT] = print_tcp_wakeup("OutputRetrans", ptcp_re_out, prt); | |
if (prt) | |
printk("[oplus_nwpower] IPAAllWakeups: TCPInput: %d, TCPOutput: %d, TCPInputRetrans: %d, TCPOutputRetrans: %d", | |
wakeup[OPLUS_NW_TCP_IN], wakeup[OPLUS_NW_TCP_OUT], wakeup[OPLUS_NW_TCP_RE_IN], wakeup[OPLUS_NW_TCP_RE_OUT]); | |
if (unsl) { | |
wakeup_unsl_msg[6] = (wakeup[OPLUS_NW_TCP_IN] << 48) | | |
((wakeup[OPLUS_NW_TCP_OUT] & 0xFFFF) << 32) | | |
((wakeup[OPLUS_NW_TCP_RE_IN] & 0xFFFF) << 16) | | |
(wakeup[OPLUS_NW_TCP_RE_OUT] & 0xFFFF); | |
} | |
} | |
static void reset_count(u64 qrtr[][4], struct tcp_hook_struct *ptcp_in, struct tcp_hook_struct *ptcp_out, | |
struct tcp_hook_struct *ptcp_re_in, struct tcp_hook_struct *ptcp_re_out, u64 wakeup[OPLUS_NW_WAKEUP_SUM]) { | |
int i; | |
int j; | |
for (i = 0; i < OPLUS_MAX_QRTR_SERVICE_LEN; ++i) { | |
if (qrtr[i][0] == 1) { | |
qrtr[i][3] = 0; | |
} | |
} | |
for (i = 0; i < OPLUS_MAX_RECORD_IP_LEN; ++i) { | |
for (j = 0; j < 3; ++j) { | |
ptcp_in->set[i+j] = 0; | |
ptcp_out->set[i+j] = 0; | |
ptcp_re_in->set[i+j] = 0; | |
ptcp_re_out->set[i+j] = 0; | |
} | |
} | |
for (i = 0; i < OPLUS_NW_WAKEUP_SUM; ++i) { | |
wakeup[i] = 0; | |
} | |
} | |
static void nwpower_hook_on(void) { | |
atomic_set(&qrtr_wakeup_hook_boot, 1); | |
atomic_set(&ipa_wakeup_hook_boot, 1); | |
atomic_set(&tcpsynretrans_hook_boot, 1); | |
} | |
static void nwpower_hook_off(bool unsl) { | |
atomic_set(&qrtr_wakeup_hook_boot, 0); | |
atomic_set(&ipa_wakeup_hook_boot, 0); | |
atomic_set(&tcpsynretrans_hook_boot, 0); | |
print_qrtr_wakeup(unsl, service_wakeup_times, oplus_nw_wakeup, true); | |
print_ipa_wakeup(unsl, &tcp_input_list, &tcp_output_list, &tcp_input_retrans_list, &tcp_output_retrans_list, oplus_nw_wakeup, true); | |
reset_count(service_wakeup_times, &tcp_input_list, &tcp_output_list, &tcp_input_retrans_list, &tcp_output_retrans_list, oplus_nw_wakeup); | |
app_wakeup_monitor_list.count = 0; | |
memset(app_wakeup_monitor_list.set, 0x0, sizeof(app_wakeup_monitor_list.set)); | |
if (unsl) { | |
nwpower_send_to_user(NW_POWER_UNSL_MONITOR, (char*)wakeup_unsl_msg, sizeof(wakeup_unsl_msg)); | |
memset(wakeup_unsl_msg, 0x0, sizeof(wakeup_unsl_msg)); | |
} | |
} | |
static void nwpower_unsl_mdaci(void) { | |
print_qrtr_wakeup(true, mdaci_service_wakeup_times, oplus_mdaci_nw_wakeup, true); | |
print_ipa_wakeup(true, &mdaci_tcp_input_list, &mdaci_tcp_output_list, | |
&mdaci_tcp_input_retrans_list, &mdaci_tcp_output_retrans_list, oplus_mdaci_nw_wakeup, true); | |
reset_count(mdaci_service_wakeup_times, &mdaci_tcp_input_list, &mdaci_tcp_output_list, | |
&mdaci_tcp_input_retrans_list, &mdaci_tcp_output_retrans_list, oplus_mdaci_nw_wakeup); | |
nwpower_send_to_user(NW_POWER_REPORT_MDACI, (char*)wakeup_unsl_msg, sizeof(wakeup_unsl_msg)); | |
memset(wakeup_unsl_msg, 0x0, sizeof(wakeup_unsl_msg)); | |
nwpower_send_to_user(NW_POWER_REPORT_MDACI_APP_WAKEUP, (char*)mdaci_app_wakeup_monitor_list.set, sizeof(mdaci_app_wakeup_monitor_list.set)); | |
mdaci_app_wakeup_monitor_list.count = 0; | |
memset(mdaci_app_wakeup_monitor_list.set, 0x0, sizeof(mdaci_app_wakeup_monitor_list.set)); | |
} | |
static int nwpower_send_to_user(int msg_type, char *msg_data, int msg_len) { | |
int ret = 0; | |
struct sk_buff *skb; | |
struct nlmsghdr *nlh; | |
if (oplus_nwpower_pid == 0) { | |
printk("[oplus_nwpower] netlink: oplus_nwpower_pid = 0.\n"); | |
return -1; | |
} | |
skb = alloc_skb(NLMSG_SPACE(msg_len), GFP_ATOMIC); | |
if (skb == NULL) { | |
printk("[oplus_nwpower] netlink: alloc_skb failed.\n"); | |
return -2; | |
} | |
nlh = nlmsg_put(skb, 0, 0, msg_type, NLMSG_ALIGN(msg_len), 0); | |
if (nlh == NULL) { | |
printk("[oplus_nwpower] netlink: nlmsg_put failed.\n"); | |
nlmsg_free(skb); | |
return -3; | |
} | |
nlh->nlmsg_len = NLMSG_HDRLEN + NLMSG_ALIGN(msg_len); | |
if(msg_data != NULL) { | |
memcpy((char*)NLMSG_DATA(nlh), msg_data, msg_len); | |
} | |
NETLINK_CB(skb).portid = 0; | |
NETLINK_CB(skb).dst_group = 0; | |
ret = netlink_unicast(oplus_nwpower_sock, skb, oplus_nwpower_pid, MSG_DONTWAIT); | |
if(ret < 0) { | |
printk(KERN_ERR "[oplus_nwpower] netlink: netlink_unicast failed, ret = %d.\n", ret); | |
return -4; | |
} | |
return 0; | |
} | |
static int nwpower_netlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { | |
int ret = 0; | |
switch (nlh->nlmsg_type) { | |
case NW_POWER_ANDROID_PID: | |
oplus_nwpower_pid = NETLINK_CB(skb).portid; | |
printk("[oplus_nwpower] netlink: oplus_nwpower_pid = %d", oplus_nwpower_pid); | |
break; | |
case NW_POWER_BOOT_MONITOR: | |
nwpower_hook_on(); | |
printk("[oplus_nwpower] netlink: hook_on"); | |
break; | |
case NW_POWER_STOP_MONITOR: | |
nwpower_hook_off(false); | |
printk("[oplus_nwpower] netlink: hook_off"); | |
break; | |
case NW_POWER_STOP_MONITOR_UNSL: | |
nwpower_hook_off(true); | |
printk("[oplus_nwpower] netlink: hook_off_unsl"); | |
break; | |
case NW_POWER_BLACK_LIST: | |
ret = nwpower_set_blacklist_uids(nlh); | |
break; | |
case NW_POWER_REQUEST_BLACK_REJECT: | |
nwpower_unsl_blacklist_reject(); | |
printk("[oplus_nwpower] netlink: unsl_blacklist_reject"); | |
break; | |
case NW_POWER_REQUEST_APP_WAKEUP: | |
nwpower_unsl_app_wakeup(); | |
printk("[oplus_nwpower] netlink: unsl_app_wakeup"); | |
break; | |
case NW_POWER_REQUEST_MDACI: | |
nwpower_unsl_mdaci(); | |
printk("[oplus_nwpower] netlink: unsl_mdaci"); | |
break; | |
default: | |
return -EINVAL; | |
} | |
return ret; | |
} | |
static void nwpower_netlink_rcv(struct sk_buff *skb) { | |
mutex_lock(&netlink_mutex); | |
netlink_rcv_skb(skb, &nwpower_netlink_rcv_msg); | |
mutex_unlock(&netlink_mutex); | |
} | |
static int nwpower_netlink_init(void) { | |
struct netlink_kernel_cfg cfg = { | |
.input = nwpower_netlink_rcv, | |
}; | |
oplus_nwpower_sock = netlink_kernel_create(&init_net, NETLINK_OPLUS_NWPOWERSTATE, &cfg); | |
return oplus_nwpower_sock == NULL ? -ENOMEM : 0; | |
} | |
static void nwpower_netlink_exit(void) { | |
netlink_kernel_release(oplus_nwpower_sock); | |
oplus_nwpower_sock = NULL; | |
} | |
static int __init nwpower_init(void) { | |
int ret = 0; | |
ret = nwpower_netlink_init(); | |
if (ret < 0) { | |
printk("[oplus_nwpower] netlink: failed to init netlink.\n"); | |
} else { | |
printk("[oplus_nwpower] netlink: init netlink successfully.\n"); | |
} | |
return ret; | |
} | |
static void __exit nwpower_fini(void) { | |
nwpower_netlink_exit(); | |
} | |
module_init(nwpower_init); | |
module_exit(nwpower_fini); |