1 #define MODULE_LOG_PREFIX "cache"
4 #include "module-cacheex.h"
5 #include "module-cw-cycle-check.h"
6 #include "oscam-cache.h"
8 #include "oscam-client.h"
10 #include "oscam-garbage.h"
11 #include "oscam-lock.h"
12 #include "oscam-net.h"
13 #include "oscam-string.h"
14 #include "oscam-time.h"
15 #include "oscam-hashtable.h"
18 // CACHE functions **************************************************************+
21 struct s_pushclient
*next_push
;
26 uint8_t odd_even
; //odd/even byte (0x80 0x81)
27 uint8_t cwc_cycletime
;
28 uint8_t cwc_next_cw_cycle
;
29 uint8_t got_bad_cwc
; //used by cycle check
30 uint16_t caid
; //first caid received
31 uint32_t prid
; //first prid received
32 uint16_t srvid
; //first srvid received
33 struct s_reader
*selected_reader
; //first answering: reader
34 struct s_client
*cacheex_src
; //first answering: cacheex client
36 uint64_t grp
; //updated grp
37 uint8_t csp
; //updated if answer from csp
38 uint8_t cacheex
; //updated if answer from cacheex
39 uint8_t localcards
; //updated if answer from local cards (or proxy using localcards option)
40 uint8_t proxy
; //updated if answer from local reader
42 uint32_t count
; //count of same cws receved
45 pthread_rwlock_t pushout_client_lock
;
46 struct s_pushclient
*pushout_client
; //list of clients that pushing cw
49 node ht_node
; //node for hash table
50 node ll_node
; //node for linked list
53 typedef struct cache_t
{
56 struct timeb upd_time
; //updated time. Update time at each cw got
57 struct timeb first_recv_time
; //time of first cw received
60 node ht_node
; //node for hash table
61 node ll_node
; //node for linked list
65 static pthread_rwlock_t cache_lock
;
66 static hash_table ht_cache
;
68 static int8_t cache_init_done
= 0;
70 void init_cache(void){
71 init_hash_table(&ht_cache
, &ll_cache
);
72 if (pthread_rwlock_init(&cache_lock
,NULL
) != 0)
73 { cs_log("Error creating lock cache_lock!"); }
75 { cache_init_done
= 1; }
78 void free_cache(void){
81 deinitialize_hash_table(&ht_cache
);
82 pthread_rwlock_destroy(&cache_lock
);
85 uint32_t cache_size(void){
89 return count_hash_table(&ht_cache
);
92 static uint8_t count_sort(CW
*a
, CW
*b
){
93 if (a
->count
== b
->count
) return 0;
94 return (a
->count
> b
->count
) ? -1 : 1; //DESC order by count
97 uint8_t check_is_pushed(void *cwp
, struct s_client
*cl
){
99 struct s_pushclient
*cl_tmp
;
103 SAFE_RWLOCK_RDLOCK(&cw
->pushout_client_lock
);
104 for (cl_tmp
= cw
->pushout_client
; cl_tmp
; cl_tmp
= cl_tmp
->next_push
) {
112 SAFE_RWLOCK_UNLOCK(&cw
->pushout_client_lock
);
113 SAFE_RWLOCK_WRLOCK(&cw
->pushout_client_lock
);
115 struct s_pushclient
*new_push_client
;
116 if(cs_malloc(&new_push_client
, sizeof(struct s_pushclient
))){
117 new_push_client
->cl
=cl
;
119 new_push_client
->next_push
=cw
->pushout_client
;
120 cw
->pushout_client
=new_push_client
;
123 SAFE_RWLOCK_UNLOCK(&cw
->pushout_client_lock
);
126 SAFE_RWLOCK_UNLOCK(&cw
->pushout_client_lock
);
131 uint8_t get_odd_even(ECM_REQUEST
*er
){
132 return (er
->ecm
[0] != 0x80 && er
->ecm
[0] != 0x81 ? 0 : er
->ecm
[0]);
136 CW
*get_first_cw(ECMHASH
*ecmhash
, ECM_REQUEST
*er
){
137 if(!ecmhash
) return NULL
;
142 j
= get_first_node_list(&ecmhash
->ll_cw
);
144 cw
= get_data_from_node(j
);
146 if(cw
&& cw
->odd_even
== get_odd_even(er
) && !cw
->got_bad_cwc
)
155 static int compare_csp_hash(const void *arg
, const void *obj
){
156 uint32_t h
= ((const ECMHASH
*)obj
)->csp_hash
;
157 return memcmp(arg
, &h
, 4);
160 static int compare_cw(const void *arg
, const void *obj
){
161 return memcmp(arg
, ((const CW
*)obj
)->cw
, 16);
164 static bool cwcycle_check_cache(struct s_client
*cl
, ECM_REQUEST
*er
, CW
*cw
)
166 (void)cl
; (void)er
; (void)cw
;
167 #ifdef CW_CYCLE_CHECK
170 uint8_t cwc_ct
= cw
->cwc_cycletime
> 0 ? cw
->cwc_cycletime
: 0;
171 uint8_t cwc_ncwc
= cw
->cwc_next_cw_cycle
< 2 ? cw
->cwc_next_cw_cycle
: 2;
172 if(checkcwcycle(cl
, er
, NULL
, cw
->cw
, 0, cwc_ct
, cwc_ncwc
) != 0)
174 cs_log_dbg(D_CWC
| D_LB
, "{client %s, caid %04X, srvid %04X} [check_cache] cyclecheck passed ecm in INT. cache.", (cl
? cl
->account
->usr
: "-"), er
->caid
, er
->srvid
);
176 cs_log_dbg(D_CWC
, "cyclecheck [BAD CW Cycle] from Int. Cache detected.. {client %s, caid %04X, srvid %04X} [check_cache] -> skip cache answer", (cl
? cl
->account
->usr
: "-"), er
->caid
, er
->srvid
);
177 cw
->got_bad_cwc
= 1; // no need to check it again
185 * This function returns cw (mostly received) in cache for er, or NULL if not found.
187 * - If found, DON'T forget to free returned ecm, because it is a copy useful to get data
188 * - If found, and cacheex_src client of returned ecm is not NULL, and we want to access it,
189 * remember to check for its validity (client structure is still existent)
190 * E.g.: if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill)
191 * We don't want make this stuff here to avoid useless cpu time if outside function we would not access to it.
193 struct ecm_request_t
*check_cache(ECM_REQUEST
*er
, struct s_client
*cl
)
195 if(!cache_init_done
|| !er
->csp_hash
) return NULL
;
197 ECM_REQUEST
*ecm
= NULL
;
200 uint64_t grp
= cl
?cl
->grp
:0;
202 SAFE_RWLOCK_RDLOCK(&cache_lock
);
204 result
= find_hash_table(&ht_cache
, &er
->csp_hash
, sizeof(uint32_t),&compare_csp_hash
);
205 cw
= get_first_cw(result
, er
);
210 cw
->csp
//csp have no grp!
212 !grp
//csp client(no grp) searching for cache
217 cw
->grp
//ecm group --> only when readers/ex-clients answer (e_found) it
225 //if preferlocalcards=2 for this ecm request, we can server ONLY cw from localcards readers until stage<3
226 if(er
->preferlocalcards
==2 && !cw
->localcards
&& er
->stage
<3){
230 CWCHECK check_cw
= get_cwcheck(er
);
231 if((!cw
->proxy
&& !cw
->localcards
) //cw received from ONLY cacheex/csp peers
232 && check_cw
.counter
>1
233 && cw
->count
< check_cw
.counter
234 && (check_cw
.mode
|| !er
->cacheex_wait_time_expired
)
240 if (!cwcycle_check_cache(cl
, er
, cw
))
243 if (cs_malloc(&ecm
, sizeof(ECM_REQUEST
))){
246 memcpy(ecm
->cw
, cw
->cw
, 16);
248 ecm
->selected_reader
= cw
->selected_reader
;
249 ecm
->cwc_cycletime
= cw
->cwc_cycletime
;
250 ecm
->cwc_next_cw_cycle
= cw
->cwc_next_cw_cycle
;
251 ecm
->cacheex_src
= cw
->cacheex_src
;
252 ecm
->cw_count
= cw
->count
;
257 SAFE_RWLOCK_UNLOCK(&cache_lock
);
261 static void cacheex_cache_add(ECM_REQUEST
*er
, ECMHASH
*result
, CW
*cw
, bool add_new_cw
)
263 (void)er
; (void)result
; (void)cw
; (void)add_new_cw
;
266 cacheex_cache_push(er
);
268 //cacheex debug log lines and cw diff stuff
269 if(!check_client(er
->cacheex_src
))
274 debug_ecm(D_CACHEEX
| D_CSP
, "got duplicate pushed ECM %s from %s", buf
, er
->from_csp
? "csp" : username(er
->cacheex_src
));
278 debug_ecm(D_CACHEEX
|D_CSP
, "got pushed ECM %s from %s", buf
, er
->from_csp
? "csp" : username(er
->cacheex_src
));
279 CW
*cw_first
= get_first_cw(result
, er
);
283 //compare er cw with mostly counted cached cw
284 if(memcmp(er
->cw
, cw_first
->cw
, sizeof(er
->cw
)) != 0)
286 er
->cacheex_src
->cwcacheexerrcw
++;
287 if (er
->cacheex_src
->account
)
288 er
->cacheex_src
->account
->cwcacheexerrcw
++;
290 if (((0x0200| 0x0800) & cs_dblevel
)) //avoid useless operations if debug is not enabled
292 char cw1
[16*3+2], cw2
[16*3+2];
293 cs_hexdump(0, er
->cw
, 16, cw1
, sizeof(cw1
));
294 cs_hexdump(0, cw_first
->cw
, 16, cw2
, sizeof(cw2
));
296 char ip1
[20]="", ip2
[20]="";
297 if (check_client(er
->cacheex_src
))
298 cs_strncpy(ip1
, cs_inet_ntoa(er
->cacheex_src
->ip
), sizeof(ip1
));
299 if (check_client(cw_first
->cacheex_src
))
300 cs_strncpy(ip2
, cs_inet_ntoa(cw_first
->cacheex_src
->ip
), sizeof(ip2
));
301 else if (cw_first
->selected_reader
&& check_client(cw_first
->selected_reader
->client
))
302 cs_strncpy(ip2
, cs_inet_ntoa(cw_first
->selected_reader
->client
->ip
), sizeof(ip2
));
304 debug_ecm(D_CACHEEX
| D_CSP
, "WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s ", buf
,
305 er
->from_csp
? "csp" : username(er
->cacheex_src
), ip1
,
306 check_client(cw_first
->cacheex_src
)?username(cw_first
->cacheex_src
):(cw_first
->selected_reader
?cw_first
->selected_reader
->label
:"unknown/csp"), ip2
,
313 void add_cache(ECM_REQUEST
*er
){
314 if(!cache_init_done
|| !er
->csp_hash
) return;
316 ECMHASH
*result
= NULL
;
318 bool add_new_cw
=false;
320 SAFE_RWLOCK_WRLOCK(&cache_lock
);
322 //add csp_hash to cache
323 result
= find_hash_table(&ht_cache
, &er
->csp_hash
, sizeof(uint32_t), &compare_csp_hash
);
325 if(cs_malloc(&result
, sizeof(ECMHASH
))){
326 result
->csp_hash
= er
->csp_hash
;
327 init_hash_table(&result
->ht_cw
, &result
->ll_cw
);
328 cs_ftime(&result
->first_recv_time
);
330 add_hash_table(&ht_cache
, &result
->ht_node
, &ll_cache
, &result
->ll_node
, result
, &result
->csp_hash
, sizeof(uint32_t));
333 SAFE_RWLOCK_UNLOCK(&cache_lock
);
334 cs_log("ERROR: NO added HASH to cache!!");
339 cs_ftime(&result
->upd_time
); //need to be updated at each cw! We use it for deleting this hash when no more cws arrive inside max_cache_time!
342 //add cw to this csp hash
343 cw
= find_hash_table(&result
->ht_cw
, er
->cw
, sizeof(er
->cw
), &compare_cw
);
347 if(count_hash_table(&result
->ht_cw
)>=10){ //max 10 different cws stored
348 SAFE_RWLOCK_UNLOCK(&cache_lock
);
353 if(cs_malloc(&cw
, sizeof(CW
))){
354 memcpy(cw
->cw
, er
->cw
, sizeof(er
->cw
));
355 cw
->odd_even
= get_odd_even(er
);
356 cw
->cwc_cycletime
= er
->cwc_cycletime
;
357 cw
->cwc_next_cw_cycle
= er
->cwc_next_cw_cycle
;
366 cw
->srvid
= er
->srvid
;
367 cw
->selected_reader
=er
->selected_reader
;
368 cw
->cacheex_src
=er
->cacheex_src
;
369 cw
->pushout_client
= NULL
;
372 if (pthread_rwlock_init(&cw
->pushout_client_lock
, NULL
) == 0)
375 cs_log("Error creating lock pushout_client_lock!");
380 add_hash_table(&result
->ht_cw
, &cw
->ht_node
, &result
->ll_cw
, &cw
->ll_node
, cw
, cw
->cw
, sizeof(er
->cw
));
386 cs_log("ERROR: NO added CW to cache!! Re-trying...");
391 //update if answered from csp/cacheex/local_proxy
392 if(er
->from_cacheex
) cw
->cacheex
= 1;
393 if(er
->from_csp
) cw
->csp
= 1;
394 if(!er
->cacheex_src
){
395 if(is_localreader(er
->selected_reader
, er
)) cw
->localcards
=1;
399 //always update group and counter
403 //sort cw_list by counter (DESC order)
405 sort_list(&result
->ll_cw
, count_sort
);
407 SAFE_RWLOCK_UNLOCK(&cache_lock
);
409 cacheex_cache_add(er
, result
, cw
, add_new_cw
);
412 void cleanup_cache(bool force
){
415 struct s_pushclient
*pc
, *nxt
;
416 node
*i
,*i_next
,*j
,*j_next
;
419 int64_t gone_first
, gone_upd
;
425 SAFE_RWLOCK_WRLOCK(&cache_lock
);
427 i
= get_first_node_list(&ll_cache
);
430 ecmhash
= get_data_from_node(i
);
438 gone_first
= comp_timeb(&now
, &ecmhash
->first_recv_time
);
439 gone_upd
= comp_timeb(&now
, &ecmhash
->upd_time
);
441 if(!force
&& gone_first
<=(cfg
.max_cache_time
*1000)){ //not continue, useless check for nexts one!
445 if(force
|| gone_upd
>(cfg
.max_cache_time
*1000)){
447 j
= get_first_node_list(&ecmhash
->ll_cw
);
451 cw
= get_data_from_node(j
);
453 pthread_rwlock_destroy(&cw
->pushout_client_lock
);
454 pc
= cw
->pushout_client
;
455 cw
->pushout_client
=NULL
;
462 remove_elem_list(&ecmhash
->ll_cw
, &cw
->ll_node
);
463 remove_elem_hash_table(&ecmhash
->ht_cw
, &cw
->ht_node
);
470 deinitialize_hash_table(&ecmhash
->ht_cw
);
471 remove_elem_list(&ll_cache
, &ecmhash
->ll_node
);
472 remove_elem_hash_table(&ht_cache
, &ecmhash
->ht_node
);
479 SAFE_RWLOCK_UNLOCK(&cache_lock
);