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 **************************************************************+
22 struct s_pushclient
*next_push
;
28 uint8_t odd_even
; // odd/even byte (0x80 0x81)
29 uint8_t cwc_cycletime
;
30 uint8_t cwc_next_cw_cycle
;
31 uint8_t got_bad_cwc
; // used by cycle check
32 uint16_t caid
; // first caid received
33 uint32_t prid
; // first prid received
34 uint16_t srvid
; // first srvid received
35 struct s_reader
*selected_reader
; // first answering: reader
36 struct s_client
*cacheex_src
; // first answering: cacheex client
37 uint64_t grp
; // updated grp
38 uint8_t csp
; // updated if answer from csp
39 uint8_t cacheex
; // updated if answer from cacheex
40 uint8_t localcards
; // updated if answer from local cards (or proxy using localcards option)
41 uint8_t proxy
; // updated if answer from local reader
42 uint32_t count
; // count of same cws receved
44 pthread_rwlock_t pushout_client_lock
;
45 struct s_pushclient
*pushout_client
; // list of clients that pushing cw
47 node ht_node
; // node for hash table
48 node ll_node
; // node for linked list
51 typedef struct cache_t
55 struct timeb upd_time
; // updated time. Update time at each cw got
56 struct timeb first_recv_time
; // time of first cw received
58 node ht_node
; // node for hash table
59 node ll_node
; // node for linked list
62 static pthread_rwlock_t cache_lock
;
63 static hash_table ht_cache
;
65 static int8_t cache_init_done
= 0;
69 init_hash_table(&ht_cache
, &ll_cache
);
70 if (pthread_rwlock_init(&cache_lock
,NULL
) != 0)
71 { cs_log("Error creating lock cache_lock!"); }
73 { cache_init_done
= 1; }
80 deinitialize_hash_table(&ht_cache
);
81 pthread_rwlock_destroy(&cache_lock
);
84 uint32_t cache_size(void)
89 return count_hash_table(&ht_cache
);
92 static uint8_t count_sort(CW
*a
, CW
*b
)
94 if (a
->count
== b
->count
) return 0;
95 return (a
->count
> b
->count
) ? -1 : 1; // DESC order by count
98 uint8_t check_is_pushed(void *cwp
, struct s_client
*cl
)
100 struct s_pushclient
*cl_tmp
;
104 SAFE_RWLOCK_RDLOCK(&cw
->pushout_client_lock
);
105 for (cl_tmp
= cw
->pushout_client
; cl_tmp
; cl_tmp
= cl_tmp
->next_push
)
116 SAFE_RWLOCK_UNLOCK(&cw
->pushout_client_lock
);
117 SAFE_RWLOCK_WRLOCK(&cw
->pushout_client_lock
);
119 struct s_pushclient
*new_push_client
;
120 if(cs_malloc(&new_push_client
, sizeof(struct s_pushclient
)))
122 new_push_client
->cl
=cl
;
124 new_push_client
->next_push
=cw
->pushout_client
;
125 cw
->pushout_client
=new_push_client
;
128 SAFE_RWLOCK_UNLOCK(&cw
->pushout_client_lock
);
133 SAFE_RWLOCK_UNLOCK(&cw
->pushout_client_lock
);
138 uint8_t get_odd_even(ECM_REQUEST
*er
)
140 return (er
->ecm
[0] != 0x80 && er
->ecm
[0] != 0x81 ? 0 : er
->ecm
[0]);
144 CW
*get_first_cw(ECMHASH
*ecmhash
, ECM_REQUEST
*er
)
146 if(!ecmhash
) return NULL
;
151 j
= get_first_node_list(&ecmhash
->ll_cw
);
153 cw
= get_data_from_node(j
);
155 if(cw
&& cw
->odd_even
== get_odd_even(er
) && !cw
->got_bad_cwc
)
164 static int compare_csp_hash(const void *arg
, const void *obj
)
166 uint32_t h
= ((const ECMHASH
*)obj
)->csp_hash
;
167 return memcmp(arg
, &h
, 4);
170 static int compare_cw(const void *arg
, const void *obj
)
172 return memcmp(arg
, ((const CW
*)obj
)->cw
, 16);
175 static bool cwcycle_check_cache(struct s_client
*cl
, ECM_REQUEST
*er
, CW
*cw
)
177 (void)cl
; (void)er
; (void)cw
;
179 #ifdef CW_CYCLE_CHECK
183 uint8_t cwc_ct
= cw
->cwc_cycletime
> 0 ? cw
->cwc_cycletime
: 0;
184 uint8_t cwc_ncwc
= cw
->cwc_next_cw_cycle
< 2 ? cw
->cwc_next_cw_cycle
: 2;
185 if(checkcwcycle(cl
, er
, NULL
, cw
->cw
, 0, cwc_ct
, cwc_ncwc
) != 0)
187 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
);
191 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
);
192 cw
->got_bad_cwc
= 1; // no need to check it again
200 * This function returns cw (mostly received) in cache for er, or NULL if not found.
202 * - If found, DON'T forget to free returned ecm, because it is a copy useful to get data
203 * - If found, and cacheex_src client of returned ecm is not NULL, and we want to access it,
204 * remember to check for its validity (client structure is still existent)
205 * E.g.: if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill)
206 * We don't want make this stuff here to avoid useless cpu time if outside function we would not access to it.
208 struct ecm_request_t
*check_cache(ECM_REQUEST
*er
, struct s_client
*cl
)
210 if(!cache_init_done
|| !er
->csp_hash
) return NULL
;
212 ECM_REQUEST
*ecm
= NULL
;
215 uint64_t grp
= cl
?cl
->grp
:0;
217 SAFE_RWLOCK_RDLOCK(&cache_lock
);
219 result
= find_hash_table(&ht_cache
, &er
->csp_hash
, sizeof(uint32_t),&compare_csp_hash
);
220 cw
= get_first_cw(result
, er
);
224 if(cw
->csp
// csp have no grp!
225 || !grp
// csp client(no grp) searching for cache
226 || (grp
&& cw
->grp
// ecm group --> only when readers/ex-clients answer (e_found) it
230 //if preferlocalcards=2 for this ecm request, we can server ONLY cw from localcards readers until stage<3
231 if(er
->preferlocalcards
==2 && !cw
->localcards
&& er
->stage
<3){
235 CWCHECK check_cw
= get_cwcheck(er
);
237 if((!cw
->proxy
&& !cw
->localcards
) // cw received from ONLY cacheex/csp peers
238 && check_cw
.counter
>1
239 && cw
->count
< check_cw
.counter
240 && (check_cw
.mode
|| !er
->cacheex_wait_time_expired
))
246 if (!cwcycle_check_cache(cl
, er
, cw
))
249 if (cs_malloc(&ecm
, sizeof(ECM_REQUEST
)))
253 memcpy(ecm
->cw
, cw
->cw
, 16);
255 ecm
->selected_reader
= cw
->selected_reader
;
256 ecm
->cwc_cycletime
= cw
->cwc_cycletime
;
257 ecm
->cwc_next_cw_cycle
= cw
->cwc_next_cw_cycle
;
258 ecm
->cacheex_src
= cw
->cacheex_src
;
259 ecm
->cw_count
= cw
->count
;
264 SAFE_RWLOCK_UNLOCK(&cache_lock
);
268 static void cacheex_cache_add(ECM_REQUEST
*er
, ECMHASH
*result
, CW
*cw
, bool add_new_cw
)
270 (void)er
; (void)result
; (void)cw
; (void)add_new_cw
;
273 cacheex_cache_push(er
);
275 // cacheex debug log lines and cw diff stuff
276 if(!check_client(er
->cacheex_src
))
281 debug_ecm(D_CACHEEX
| D_CSP
, "got duplicate pushed ECM %s from %s", buf
, er
->from_csp
? "csp" : username(er
->cacheex_src
));
285 debug_ecm(D_CACHEEX
|D_CSP
, "got pushed ECM %s from %s", buf
, er
->from_csp
? "csp" : username(er
->cacheex_src
));
286 CW
*cw_first
= get_first_cw(result
, er
);
290 // compare er cw with mostly counted cached cw
291 if(memcmp(er
->cw
, cw_first
->cw
, sizeof(er
->cw
)) != 0)
293 er
->cacheex_src
->cwcacheexerrcw
++;
294 if (er
->cacheex_src
->account
)
295 er
->cacheex_src
->account
->cwcacheexerrcw
++;
297 if (((0x0200| 0x0800) & cs_dblevel
)) // avoid useless operations if debug is not enabled
299 char cw1
[16*3+2], cw2
[16*3+2];
300 cs_hexdump(0, er
->cw
, 16, cw1
, sizeof(cw1
));
301 cs_hexdump(0, cw_first
->cw
, 16, cw2
, sizeof(cw2
));
303 char ip1
[20]="", ip2
[20]="";
304 if (check_client(er
->cacheex_src
))
305 cs_strncpy(ip1
, cs_inet_ntoa(er
->cacheex_src
->ip
), sizeof(ip1
));
306 if (check_client(cw_first
->cacheex_src
))
307 cs_strncpy(ip2
, cs_inet_ntoa(cw_first
->cacheex_src
->ip
), sizeof(ip2
));
308 else if (cw_first
->selected_reader
&& check_client(cw_first
->selected_reader
->client
))
309 cs_strncpy(ip2
, cs_inet_ntoa(cw_first
->selected_reader
->client
->ip
), sizeof(ip2
));
311 debug_ecm(D_CACHEEX
| D_CSP
, "WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s ", buf
,
312 er
->from_csp
? "csp" : username(er
->cacheex_src
), ip1
,
313 check_client(cw_first
->cacheex_src
)?username(cw_first
->cacheex_src
):(cw_first
->selected_reader
?cw_first
->selected_reader
->label
:"unknown/csp"), ip2
,
320 void add_cache(ECM_REQUEST
*er
)
322 if(!cache_init_done
|| !er
->csp_hash
) return;
324 ECMHASH
*result
= NULL
;
326 bool add_new_cw
=false;
328 SAFE_RWLOCK_WRLOCK(&cache_lock
);
330 // add csp_hash to cache
331 result
= find_hash_table(&ht_cache
, &er
->csp_hash
, sizeof(uint32_t), &compare_csp_hash
);
333 if(cs_malloc(&result
, sizeof(ECMHASH
))){
334 result
->csp_hash
= er
->csp_hash
;
335 init_hash_table(&result
->ht_cw
, &result
->ll_cw
);
336 cs_ftime(&result
->first_recv_time
);
338 add_hash_table(&ht_cache
, &result
->ht_node
, &ll_cache
, &result
->ll_node
, result
, &result
->csp_hash
, sizeof(uint32_t));
341 SAFE_RWLOCK_UNLOCK(&cache_lock
);
342 cs_log("ERROR: NO added HASH to cache!!");
347 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!
349 //add cw to this csp hash
350 cw
= find_hash_table(&result
->ht_cw
, er
->cw
, sizeof(er
->cw
), &compare_cw
);
354 if(count_hash_table(&result
->ht_cw
) >= 10) // max 10 different cws stored
356 SAFE_RWLOCK_UNLOCK(&cache_lock
);
362 if(cs_malloc(&cw
, sizeof(CW
)))
364 memcpy(cw
->cw
, er
->cw
, sizeof(er
->cw
));
365 cw
->odd_even
= get_odd_even(er
);
366 cw
->cwc_cycletime
= er
->cwc_cycletime
;
367 cw
->cwc_next_cw_cycle
= er
->cwc_next_cw_cycle
;
376 cw
->srvid
= er
->srvid
;
377 cw
->selected_reader
=er
->selected_reader
;
378 cw
->cacheex_src
=er
->cacheex_src
;
379 cw
->pushout_client
= NULL
;
383 if (pthread_rwlock_init(&cw
->pushout_client_lock
, NULL
) == 0)
386 cs_log("Error creating lock pushout_client_lock!");
390 add_hash_table(&result
->ht_cw
, &cw
->ht_node
, &result
->ll_cw
, &cw
->ll_node
, cw
, cw
->cw
, sizeof(er
->cw
));
395 cs_log("ERROR: NO added CW to cache!! Re-trying...");
400 // update if answered from csp/cacheex/local_proxy
401 if(er
->from_cacheex
) cw
->cacheex
= 1;
402 if(er
->from_csp
) cw
->csp
= 1;
405 if(is_localreader(er
->selected_reader
, er
)) cw
->localcards
=1;
409 // always update group and counter
413 // sort cw_list by counter (DESC order)
415 sort_list(&result
->ll_cw
, count_sort
);
417 SAFE_RWLOCK_UNLOCK(&cache_lock
);
419 cacheex_cache_add(er
, result
, cw
, add_new_cw
);
422 void cleanup_cache(bool force
)
426 struct s_pushclient
*pc
, *nxt
;
427 node
*i
,*i_next
,*j
,*j_next
;
430 int64_t gone_first
, gone_upd
;
435 SAFE_RWLOCK_WRLOCK(&cache_lock
);
437 i
= get_first_node_list(&ll_cache
);
441 ecmhash
= get_data_from_node(i
);
450 gone_first
= comp_timeb(&now
, &ecmhash
->first_recv_time
);
451 gone_upd
= comp_timeb(&now
, &ecmhash
->upd_time
);
453 if(!force
&& gone_first
<=(cfg
.max_cache_time
*1000)) // not continue, useless check for nexts one!
458 if(force
|| gone_upd
>(cfg
.max_cache_time
*1000))
460 j
= get_first_node_list(&ecmhash
->ll_cw
);
464 cw
= get_data_from_node(j
);
467 pthread_rwlock_destroy(&cw
->pushout_client_lock
);
468 pc
= cw
->pushout_client
;
469 cw
->pushout_client
=NULL
;
476 remove_elem_list(&ecmhash
->ll_cw
, &cw
->ll_node
);
477 remove_elem_hash_table(&ecmhash
->ht_cw
, &cw
->ht_node
);
483 deinitialize_hash_table(&ecmhash
->ht_cw
);
484 remove_elem_list(&ll_cache
, &ecmhash
->ll_node
);
485 remove_elem_hash_table(&ht_cache
, &ecmhash
->ht_node
);
490 SAFE_RWLOCK_UNLOCK(&cache_lock
);