hc
2023-02-13 e440ec23c5a540cdd3f7464e8779219be6fd3d95
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/etherdevice.h>
#include <linux/ipv6.h>
#include <linux/ip.h>
#include <net/ndisc.h>
#include <linux/suspend.h>
#include <linux/workqueue.h>
#include <linux/kallsyms.h>
#include "msg.h"
#include "tracer.h"
#include "cmdevt.h"
#include "rx_msg.h"
 
#define WAKEUP_TIME_EXPIRED 500
 
static struct deauth_trace deauth;
static void deauth_reason_worker(struct work_struct *work);
static DECLARE_WORK(deauth_worker, deauth_reason_worker);
 
static void deauth_reason_worker(struct work_struct *work)
{
   int i, j;
   struct deauth_info *dinfo;
 
   wl_info("deauth reason dump: == START ==\n");
   for (i = 0; i < SPRDWL_MODE_MAX; i++) {
       for (j = 0; j < MAX_DEAUTH_REASON; j++) {
           dinfo = &deauth.deauth_mode[i];
           if (dinfo->local_deauth[j] != 0)
               wl_info("mode[%d] local reason[%d]:%ld times\n",
                   i, j, dinfo->local_deauth[j]);
 
           if (dinfo->remote_deauth[j] != 0)
               wl_info("mode[%d] remote reason[%d]:%ld times\n",
                   i, j, dinfo->remote_deauth[j]);
       }
   }
 
   wl_info("deauth reason dump: == END ==\n");
}
 
void trace_deauth_reason(int mode, u16 reason_code, int dirction)
{
   struct deauth_info *dinfo;
 
   if (reason_code > MAX_DEAUTH_REASON) {
       wl_info("deauth reason:%d not record\n", reason_code);
       return;
   }
 
   dinfo = &deauth.deauth_mode[mode];
   spin_lock_bh(&deauth.lock);
   switch (dirction) {
   case LOCAL_EVENT:
       dinfo->local_deauth[reason_code]++;
       break;
   default:
       dinfo->remote_deauth[reason_code]++;
       break;
   }
 
   spin_unlock_bh(&deauth.lock);
 
   schedule_work(&deauth_worker);
}
 
static void trace_rx_icmp6_wake(struct wakeup_trace *tracer, void *data)
{
   void *iphdr_addr;
   struct ipv6hdr *ipv6_hdr;
   struct icmp6hdr *icmp6_hdr;
 
   iphdr_addr = data + sizeof(struct ethhdr);
   ipv6_hdr = iphdr_addr;
   if (ipv6_hdr->nexthdr == IPPROTO_ICMPV6) {
       tracer->pkt_type_dtl.icmp6_pkt_cnt++;
 
       icmp6_hdr = (struct icmp6hdr *)(iphdr_addr + sizeof(*ipv6_hdr));
       switch (icmp6_hdr->icmp6_type) {
       case NDISC_ROUTER_ADVERTISEMENT:
           tracer->pkt_type_dtl.icmp6_ra_cnt++;
           break;
 
       case NDISC_NEIGHBOUR_SOLICITATION:
           tracer->pkt_type_dtl.icmp6_ns_cnt++;
           break;
 
       case NDISC_NEIGHBOUR_ADVERTISEMENT:
           tracer->pkt_type_dtl.icmp6_na_cnt++;
           break;
 
       default:
           break;
       }
   }
}
 
static void trace_rx_data_wake(struct wakeup_trace *tracer, void *data)
{
   void *iphdr_addr = data;
   struct ethhdr *ether_hdr = data;
   struct iphdr *ipv4_hdr;
 
   if (is_broadcast_ether_addr(ether_hdr->h_dest)) {
       tracer->rx_data_dtl.rx_brdcst_cnt++;
   } else if (is_multicast_ether_addr(ether_hdr->h_dest)) {
       switch (ntohs(ether_hdr->h_proto)) {
       case ETH_P_IP:
           tracer->rx_data_dtl.rx_mc_dtl.ipv4_mc_cnt++;
           break;
       case ETH_P_IPV6:
           tracer->rx_data_dtl.rx_mc_dtl.ipv6_mc_cnt++;
           trace_rx_icmp6_wake(tracer, ether_hdr);
           break;
       default:
           tracer->rx_data_dtl.rx_mc_dtl.other_mc_cnt++;
           break;
       }
 
   } else { /*unicast*/
       tracer->rx_data_dtl.rx_unicast_cnt++;
       iphdr_addr += sizeof(*ether_hdr);
       switch (ntohs(ether_hdr->h_proto)) {
       case ETH_P_IP:
           ipv4_hdr = iphdr_addr;
           if (ipv4_hdr->protocol == IPPROTO_ICMP)
               tracer->pkt_type_dtl.icmp_pkt_cnt++;
           break;
 
       case ETH_P_IPV6:
           trace_rx_icmp6_wake(tracer, ether_hdr);
           break;
       default:
           wl_info("recv proto = 0x%x\n",
               ntohs(ether_hdr->h_proto));
           break;
       }
   }
}
 
void trace_rx_wakeup(struct wakeup_trace *tracer, void *data, void *rdata)
{
   int type;
   struct rx_msdu_desc *msdu;
 
   if (!SPRD_HEAD_GET_RESUME_BIT(data))
       return;
 
   tracer->resume_flag = 0;
 
   type = SPRDWL_HEAD_GET_TYPE(data);
   switch (type) {
   /* commands or events between command WIFI_CMD_POWER_SAVE
    * and its respone just consider as one wake up source
    */
   case SPRDWL_TYPE_CMD:
   case SPRDWL_TYPE_EVENT:
       tracer->total_cmd_event_wake++;
       wl_info("wake up by cmd/event[%d] (%d times)\n",
           type, tracer->total_cmd_event_wake);
       break;
 
   case SPRDWL_TYPE_DATA_SPECIAL:
       tracer->total_rx_data_wake++;
 
       msdu = (struct rx_msdu_desc *)rdata;
       trace_rx_data_wake(tracer, rdata + msdu->msdu_offset);
       wl_info("wake up by data (%d times)\n",
           tracer->total_rx_data_wake);
       break;
 
   case SPRDWL_TYPE_PKT_LOG:
       tracer->total_local_wake++;
       wl_info("wake up by pkt log (%d times)\n",
           tracer->total_local_wake);
       break;
 
   default:
       wl_info("wake up source untrace type = 0x%x\n", type);
       break;
   }
}
 
void trace_info_init(void)
{
   spin_lock_init(&deauth.lock);
}
 
void trace_info_deinit(void)
{
   cancel_work_sync(&deauth_worker);
}