summaryrefslogtreecommitdiff
path: root/src/extensions/l2tp_protocol_plug.c
blob: 104a350dd13a09171f54145dace816c13e3703b0 (plain)
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
#ifdef __cplusplus
extern "C" {
#endif

#include "sapp_api.h"
#include "sapp_private_api.h"
#include "sapp_declaration.h"
/*
	l2tp������, 
	1)���ڻ�ȡȫ�ֲ��ID, ���洢��g_l2tp_plugid;
	2)���������Ϊl2tp, ���ڴ���һ��"l2tp"���Э����������,
	  ������ҵ������Ҫ���ص�"l2tp"��;
	3)ʶ���Ƿ���L2tpЭ��, ��UDP_stream�м�¼���stream_carry_up_layer_tunnel_type.
	
	���RFC: RFC2661.
*/
const int l2tp_protocol_version_VERSION_20171220 = 20171220;
extern MESA_htable_handle *g_l2tp_htable_array; /* �ڴ������HASH������Э���������ڳ�ʼ���׶���� */
extern unsigned short g_l2tp_plugid;
extern int parse_l2tpv2_hdr(const struct l2tp_hdr_v2 *pl2tphdrv2, struct layer_addr_l2tp *l2tpaddr, size_t left_len);


/*
	����ֵ: 
		ƽ̨��APP_STATE_xxx;
*/
static inline int l2tp_call_biz_plug(const struct streaminfo *a_udp, struct l2tp_info_pri *l2tp_pri, 
								const void *ip_hdr)
{
	int biz_plug_ret;
	int pro_plug_ret = APP_STATE_GIVEME;
	
	biz_plug_ret = PROT_PROCESS(&l2tp_pri->ssinfo, &l2tp_pri->biz_pme, a_udp->threadnum, (struct streaminfo * )a_udp, ip_hdr);

	/* clear status */
	l2tp_pri->ssinfo.prot_flag = TUNNEL_PHONY_PROT_FLAG;
	l2tp_pri->ssinfo.buf = NULL;
	l2tp_pri->ssinfo.buflen = 0;

	if(biz_plug_ret & PROT_STATE_DROPPKT){
		pro_plug_ret |= APP_STATE_DROPPKT;
	}

	if(biz_plug_ret & PROT_STATE_DROPME){
		pro_plug_ret |= APP_STATE_DROPME;
	}
	
	return pro_plug_ret;
}

static inline void l2tp_context_init(struct l2tp_info_pri *l2tp_pri, int thread_seq, const struct streaminfo *a_udp)
{
	memset(l2tp_pri, 0, sizeof(struct l2tp_info_pri)); 

	l2tp_pri->tunnel_context.tunnel_type = STREAM_TYPE_L2TP;

	l2tp_pri->ssinfo.prot_flag = TUNNEL_PHONY_PROT_FLAG;
	l2tp_pri->ssinfo.plugid = g_l2tp_plugid;
	l2tp_pri->ssinfo.session_state = SESSION_STATE_PENDING;
	l2tp_pri->thread_seq = thread_seq;
	l2tp_pri->my_stream = a_udp;
}



static int l2tp_insert_to_stream_hash(int tid, struct l2tp_stream_key *l2tp_key, const struct streaminfo *a_udp)
{
	struct l2tp_info_pri *l2tp_pri;
	int hash_ret;

	/* ��Ϊ�����������ȴ���HASH�ڵ�, ͬʱ����һ���յ�l2tp_pri�ṹ, HASH���е�Ԫ����data_channel������ֹʱ, ��dataͨ����ɾ�� */
	l2tp_pri = (struct l2tp_info_pri *)dictator_malloc(tid, sizeof(struct l2tp_info_pri));
	l2tp_context_init(l2tp_pri, tid, a_udp);
	l2tp_pri->ssinfo.session_state = 0; /* PPTP���ݴ���׶�, �������ж�pending, data, close״̬, ���жϵ�ǰsession-stateֵ���� */
	memcpy(&l2tp_pri->l2tp_key, l2tp_key, sizeof(struct l2tp_stream_key));

	/* TODO 2, ����Ƕ�׵�ַ���� */
	hash_ret = MESA_htable_add(g_l2tp_htable_array[tid], (const uchar *)l2tp_key, sizeof(struct l2tp_stream_key), l2tp_pri);
	if(hash_ret < 0){
		dictator_free(tid,  l2tp_pri);
	}else{
#ifdef MESA_HTABLE_VERSION_MACRO
	#if MESA_HTABLE_VERSION_MACRO >= 20170104
		int len, max_times;
		double avg_times;
		len = sizeof(int);
		MESA_htable_get_opt(g_l2tp_htable_array[tid], MHO_HASH_SEARCH_MAX_TIMES, &max_times, &len);

		len = sizeof(double);
		MESA_htable_get_opt(g_l2tp_htable_array[tid], MHO_HASH_SEARCH_AVG_TIMES, &avg_times, &len);

		if(max_times > sapp_global_mthread[tid].l2tp_hash_max_search_times){
			sapp_global_mthread[tid].l2tp_hash_max_search_times = max_times;
			sapp_runtime_log(30, "l2tp hash max search times: %d, thread seq:%d.\n", max_times, tid);
		}
		if(avg_times > sapp_global_mthread[tid].l2tp_hash_avg_search_times){
			sapp_global_mthread[tid].l2tp_hash_avg_search_times = avg_times;
			sapp_runtime_log(30, "l2tp hash avg search times: %.2f, thread seq:%d.\n", avg_times, tid);
		}
	#endif
#else	
		if(hash_ret > 29){
			/* 
				2017-01-03 lijia add, �������Ӵ��ж�:
				������HASH�㷨ֻ����IP��, (�˿���Ȼʹ��, ��ͨ������1701),
				��HASH��ͻ����ʱ, ������������PPTP��, ��ֹ����������ʱ̫��, ��֤��Ӱ������ҵ��. 
			*/
			MESA_htable_del(g_l2tp_htable_array[tid], (const uchar *)l2tp_key, sizeof(struct l2tp_stream_key), NULL);
		}
#endif		
	}
	return hash_ret;
}

static char l2tp_ctrl_pkt_parse(const struct streaminfo *a_udp, struct l2tp_info_pri *l2tp_pri, 
			const struct l2tp_hdr_v2 *pl2tphdrv2, const struct mesa_udp_hdr *udph)
{
	struct layer_addr_l2tp l2tp_addr;
	int l2tp_addr_actual_len;
	const struct l2tp_avp *pavp;
	const unsigned short *p_msg_type;
	const struct streaminfo_private *a_udp_pr = (const struct streaminfo_private *)a_udp;
	struct l2tp_stream_key *l2tp_key;
	int udp_payload_len = ntohs(udph->uh_ulen) - sizeof(struct mesa_udp_hdr);
	l2tp_addr_actual_len = parse_l2tpv2_hdr(pl2tphdrv2,  &l2tp_addr, udp_payload_len);
	if((l2tp_addr_actual_len < 0) || (l2tp_addr_actual_len > 16)){ /* 2017-12-20, �������ж�, ���Ȳ��ܳ���Χ */
		return APP_STATE_DROPME;
	}	

	if(a_udp->pudpdetail->datalen - l2tp_addr_actual_len <= sizeof(struct l2tp_avp)){
		/* û�п���AVP, ͨ����ZLB��, ���ô��� */
		return APP_STATE_GIVEME;
	}

	pavp = (const struct l2tp_avp *)((char *)pl2tphdrv2 + l2tp_addr_actual_len);
	//int avp_len = L2TP_AVP_GET_LEN(pavp->M_H_rsvd_len_union);

	p_msg_type = (const unsigned short *)((char *)pavp + sizeof(struct l2tp_avp));

	l2tp_key = &l2tp_pri->l2tp_key;

	if(0 == ntohs(pavp->attribute_type)){
		switch(ntohs(*p_msg_type)){
			case L2TP_CTRL_MSG_SCCRQ:
				/* TODO 2, ����AVP����, ����������¿ɻ�ȡ�Է�tunnel_id */
			break;

			case L2TP_CTRL_MSG_SCCRP:
				/* TODO 2, ����AVP����, ����������¿ɻ�ȡ�Է�tunnel_id */
			break;
			
			case L2TP_CTRL_MSG_ICRQ:
				/* TODO 2, ����AVP����, ����������¿ɻ�ȡ�Է�session_id */
			break;
			
			case L2TP_CTRL_MSG_ICRP:
				/* TODO 2, ����AVP����, ����������¿ɻ�ȡ�Է�session_id */
			{
				/* NOTE: �˴���IP��ַ��UDP�˿�Ҫ��ԭʼ���л�ȡ, ��Ϊstream->addr�ĵ�ַ����˿���Դ�Ĺ���, �����Ѿ�ת������ */
				const struct mesa_ip4_hdr *ip4hdr = (const struct mesa_ip4_hdr *)((char *)udph - a_udp_pr->offset_to_ip_hdr);
				l2tp_key->sip = ip4hdr->ip_dst.s_addr; /* ���� */
				l2tp_key->dip = ip4hdr->ip_src.s_addr; /* ���� */
				l2tp_key->sport = udph->uh_dport; /* ���� */
				l2tp_key->dport = udph->uh_sport; /* ���� */
				l2tp_key->dip_side_tunnel_id = l2tp_addr.l2tpun.l2tp_addr_v2.tunnelid_C2S;
				l2tp_key->dip_side_session_id = l2tp_addr.l2tpun.l2tp_addr_v2.sessionid_C2S;				
				/* �Բ��id��ICCN���л�ȡ */
			}				
			break;

			case L2TP_CTRL_MSG_ICCN:
			{
				/* NOTE: �˴���IP��ַ��UDP�˿�Ҫ��ԭʼ���л�ȡ, ��Ϊstream->addr�ĵ�ַ����˿���Դ�Ĺ���, �����Ѿ�ת������ */
				const struct mesa_ip4_hdr *ip4hdr = (const struct mesa_ip4_hdr *)((char *)udph - a_udp_pr->offset_to_ip_hdr);
				
				l2tp_key->sip = ip4hdr->ip_src.s_addr;
				l2tp_key->dip = ip4hdr->ip_dst.s_addr;
				l2tp_key->sport = udph->uh_sport;
				l2tp_key->dport = udph->uh_dport;
				l2tp_key->sip_side_tunnel_id = l2tp_addr.l2tpun.l2tp_addr_v2.tunnelid_C2S;
				l2tp_key->sip_side_session_id = l2tp_addr.l2tpun.l2tp_addr_v2.sessionid_C2S;
				/* �Բ��id��ICRP���л�ȡ */
			}
			break;

			case L2TP_CTRL_MSG_STOP_CCN:
			{
				const struct mesa_ip4_hdr *ip4hdr = (const struct mesa_ip4_hdr *)((char *)udph - a_udp_pr->offset_to_ip_hdr);
				/* �������ӽ��� */
				l2tp_pri->ssinfo.session_state = SESSION_STATE_CLOSE;
				l2tp_call_biz_plug(a_udp, l2tp_pri, ip4hdr);		
				/* �ڷ��غ�, l2tp_protocol_entry()�д�HASH��ɾ�� */
				return APP_STATE_DROPME;
			}
			break;

			default:
			break;
		}
	}else{
		;/* ��������AVP�ݲ����� */
	}

	if((l2tp_key->sip_side_tunnel_id != 0)
	 && (l2tp_key->sip_side_session_id != 0)
	 && (l2tp_key->dip_side_tunnel_id != 0)
	 && (l2tp_key->dip_side_session_id != 0)){
	 	/* ��ȡ����˫��id, insert to hash */
		l2tp_insert_to_stream_hash(a_udp->threadnum, l2tp_key, a_udp); 
	} 

	return APP_STATE_GIVEME;
}

static int l2tp_process(const struct streaminfo *a_udp, struct l2tp_info_pri *l2tp_pri, 
					const void *ip_hdr, const struct mesa_udp_hdr *udph)
{
	const struct l2tp_hdr_v2 *pl2tphdrv2;
	int ret = APP_STATE_GIVEME;
	
	pl2tphdrv2 = (const struct l2tp_hdr_v2 *)((char *)udph + sizeof(struct mesa_udp_hdr));

	if(OP_STATE_PENDING == a_udp->opstate){
		l2tp_pri->ssinfo.prot_flag = L2TP_OPT_LINK_TYPE;
		l2tp_pri->tunnel_context.l2tp_info.link_type = TUNNEL_CHANNEL_TYPE_CONTROL;
		l2tp_pri->ssinfo.buf = (void *)&l2tp_pri->tunnel_context.l2tp_info.link_type;
		l2tp_pri->ssinfo.buflen = sizeof(int);
		l2tp_call_biz_plug(a_udp, l2tp_pri, ip_hdr);
	}

	if(1 == pl2tphdrv2->type){
		ret = l2tp_ctrl_pkt_parse(a_udp, l2tp_pri, pl2tphdrv2, udph);
	}
	
	return ret;
}

char l2tp_protocol_entry(const struct streaminfo *a_udp,  void **pme, int thread_seq, 
				const void *ip_hdr)
{
	struct l2tp_info_pri *l2tp_pri;
	int pro_plug_ret = APP_STATE_GIVEME;
	const struct mesa_udp_hdr *udph;
	const struct mesa_ip4_hdr *ip4hdr = (const struct mesa_ip4_hdr *)ip_hdr;	

	switch(a_udp->opstate){
		case OP_STATE_PENDING:
		{
			/* l2tp��ʶ���ƶ���sapp���ò�� udp_l2tp_identify_entry() ,���ⲻ����l2tp_protocol�Ͳ���ʶ��l2tp���������� */
			struct streaminfo_private *a_udp_pr = (struct streaminfo_private *)a_udp;
			if(0 == (a_udp_pr->stream_carry_up_layer_tunnel_type & STREAM_TUNNLE_L2TP)){
				return APP_STATE_DROPME;
			}

			l2tp_pri = (struct l2tp_info_pri *)dictator_malloc(thread_seq, sizeof(struct l2tp_info_pri));
			l2tp_context_init(l2tp_pri, thread_seq, a_udp);
			*pme = l2tp_pri;
			udph = (const struct mesa_udp_hdr *)((char *)ip_hdr + ip4hdr->ip_hl * 4);
			pro_plug_ret = l2tp_process(a_udp, l2tp_pri, ip_hdr, udph);
		}	
		break;

		case OP_STATE_DATA:
			l2tp_pri = (struct l2tp_info_pri *)(*pme);
			l2tp_pri->ssinfo.session_state = SESSION_STATE_DATA;
			udph = (const struct mesa_udp_hdr *)((char *)ip_hdr + ip4hdr->ip_hl * 4);
			pro_plug_ret = l2tp_process(a_udp, l2tp_pri, ip_hdr, udph);
		break;

		case OP_STATE_CLOSE:
			l2tp_pri = (struct l2tp_info_pri *)(*pme);
			l2tp_pri->ssinfo.session_state = SESSION_STATE_CLOSE;
			l2tp_call_biz_plug(a_udp, l2tp_pri, ip_hdr);	
			pro_plug_ret = APP_STATE_DROPME;
		break;

		default:
		break;
	}

	if(APP_STATE_DROPME == pro_plug_ret){
		if(*pme != NULL){
			MESA_htable_del(g_l2tp_htable_array[a_udp->threadnum], (const uchar *)&l2tp_pri->l2tp_key, sizeof(struct l2tp_stream_key), NULL);
			dictator_free(thread_seq, *pme);
		}
	}
	
	return pro_plug_ret;
}


/* ǿ����̭�ص�, ��Ҫ������ҵ����� */
static void l2tp_stream_data_free_passive(void *data)
{
	struct l2tp_info_pri *l2tp_data_channel_pri = (struct l2tp_info_pri *)data;
	unsigned char tid = l2tp_data_channel_pri->thread_seq;

	/* 
	    NOTE:
	    l2tp hashԪ�ز�����������, ���ڵײ�UDP������, 
	    �ڿ�����������������stopCCN����, ��ײ�UDP����ʱ��̭ʱ, 
	    ��Ҫ���������ӵ�l2tp_data_pri���, ��֪ͨҵ����.
	*/
	l2tp_data_channel_pri->ssinfo.session_state = SESSION_STATE_CLOSE;
	PROT_PROCESS(&l2tp_data_channel_pri->ssinfo, &l2tp_data_channel_pri->biz_pme, tid, (struct streaminfo *)l2tp_data_channel_pri->my_stream, NULL);

	dictator_free(tid, data);
}

static uint l2tp_stream_key2index(const MESA_htable_handle table, const uchar * key, uint size)
{
	const struct l2tp_stream_key *l2tp_key = (const struct l2tp_stream_key *)key;
	/* NOTE:
	   ����ʹ��tunnel_id��session_id��Ϊkey, 
	   ��Ϊ��ǰ��ֻ��һ��������id, ��һ������û��, ����˫��hash��һ�� 
	 */
	unsigned int i;
	unsigned char *ptr;
	unsigned int seed = 13131;
	unsigned int hash = 0;
	unsigned int sip, dip;

	if(l2tp_key->sip >= l2tp_key->dip){
		sip = l2tp_key->sip;
		dip = l2tp_key->dip;
	}else{
		sip = l2tp_key->dip;
		dip = l2tp_key->sip;
	}
	
	ptr = (unsigned char *)&sip;
	for(i = 0; i < sizeof(int); i++){
		hash = hash * seed + (*ptr++);
	}

	ptr = (unsigned char *)&dip;
	for(i = 0; i < sizeof(int); i++){
		hash = hash * seed + (*ptr++);
	}

	return hash;
}


static int l2tp_stream_key_comp(const uchar * key1, uint size1, const uchar * key2, uint size2)
{
	const struct l2tp_stream_key *l2tp_key1 = (const struct l2tp_stream_key *)key1;
	const struct l2tp_stream_key *l2tp_key2 = (const struct l2tp_stream_key *)key2;	

	/* ���ж�IP�Ͷ˿�, ������������ */
	if((l2tp_key1->sip == l2tp_key2->sip) 
	  && (l2tp_key1->dip == l2tp_key2->dip)
	  && (l2tp_key1->sport == l2tp_key2->sport) 
	  && (l2tp_key1->dport == l2tp_key2->dport)){ 
		if((l2tp_key1->sip_side_tunnel_id == l2tp_key2->sip_side_tunnel_id)
		 &&(l2tp_key1->sip_side_session_id == l2tp_key2->sip_side_session_id)){
			return 0;
		}
		if((l2tp_key1->dip_side_tunnel_id == l2tp_key2->dip_side_tunnel_id)
		 &&(l2tp_key1->dip_side_session_id == l2tp_key2->dip_side_session_id)){
			return 0;
		}		
	}
	/* ����Ƚ� */
      else if((l2tp_key1->sip == l2tp_key2->dip) 
	  && (l2tp_key1->dip == l2tp_key2->sip)
	  && (l2tp_key1->sport == l2tp_key2->dport) 
	  && (l2tp_key1->dport == l2tp_key2->sport)){ 
		if((l2tp_key1->sip_side_tunnel_id == l2tp_key2->dip_side_tunnel_id)
		 &&(l2tp_key1->sip_side_session_id == l2tp_key2->dip_side_session_id)){
			return 0;
		}
		if((l2tp_key1->dip_side_tunnel_id == l2tp_key2->sip_side_tunnel_id)
		 &&(l2tp_key1->dip_side_session_id == l2tp_key2->sip_side_session_id)){
			return 0;
		}	
	}

	/* TODO 1, �ж�Ƕ�׵�ַ����, Ŀǰ���ж���������IP, PORT��call_id */
	return -1;
}


static uchar *l2tp_stream_key_dup(const uchar *key, uint key_size)
{
	const struct l2tp_stream_key *pptp_stack_key = (const struct l2tp_stream_key *)key;
	struct l2tp_stream_key *l2tp_heap_key;

	/* TODO 1, ��Ҫ��l2tp_stream_key_free()����ƥ��, ��Ϊdictator_malloc(), ����threadnum, freeʱҲҪ�ñ�׼free() */
	l2tp_heap_key = (struct l2tp_stream_key *)malloc(sizeof(struct l2tp_stream_key));
	memcpy(l2tp_heap_key, pptp_stack_key, sizeof(struct l2tp_stream_key));

	return (uchar *)l2tp_heap_key;
}

static void l2tp_stream_key_free(uchar *key, uint key_size)
{
	/* TODO 1, ��Ҫ��l2tp_stream_key_dup()����ƥ��, ��Ϊdictator_malloc(), ��Ҫ����threadnum */
	free(key);
}

/* ����PPTP ����, ���ڴ洢data�׶ε� struct pptp_info_pri�ṹ */
static MESA_htable_handle l2tp_stream_hash_create(void)
{
	MESA_htable_handle htable;
	int opt_int;
	
	htable = MESA_htable_born();

	opt_int = 0;
	MESA_htable_set_opt(htable, MHO_THREAD_SAFE, &opt_int, sizeof(int));

	opt_int = 1024 * 512;
	MESA_htable_set_opt(htable, MHO_HASH_SLOT_SIZE, &opt_int, sizeof(int));

	/* ������HASH��ͻ���������޷�����, �迿��ʱ��̭���� */
	opt_int = 100;
	MESA_htable_set_opt(htable, MHO_EXPIRE_TIME, &opt_int, sizeof(int));

	/* L2TP������������ȡ���ڵײ�UDP��������, ����������̭�����ֵ */
	opt_int = 0;
	MESA_htable_set_opt(htable, MHO_HASH_MAX_ELEMENT_NUM, (void *)&opt_int, sizeof(int));

	MESA_htable_set_opt(htable, MHO_CBFUN_DATA_FREE, (void *)&l2tp_stream_data_free_passive, sizeof(void *));
	
	MESA_htable_set_opt(htable, MHO_CBFUN_KEY_TO_INDEX, (void *)&l2tp_stream_key2index, sizeof(void *));

	MESA_htable_set_opt(htable, MHO_CBFUN_KEY_COMPARE, (void *)&l2tp_stream_key_comp, sizeof(void *));	

	MESA_htable_set_opt(htable, MHO_CBFUN_COMPLEX_KEY_DUP, (void *)&l2tp_stream_key_dup, sizeof(void *));	

	MESA_htable_set_opt(htable, MHO_CBFUN_COMPLEX_KEY_FREE, (void *)&l2tp_stream_key_free, sizeof(void *));

	MESA_htable_mature(htable);

	return htable;
}

void l2tp_protocol_funstat(unsigned long long protflag)
{
	;
}

long long l2tp_protocol_flag_change(char* flag_str)
{
	return (long long)0x7FFFFFFFFFFFFFFFL;
}

void l2tp_protocol_get_plugid(unsigned short plugid)
{
	g_l2tp_plugid = plugid;
}

int l2tp_protocol_init(void)
{
	int thread_cnt = get_thread_count();
	int i;
	
	g_l2tp_htable_array = (MESA_htable_handle *)malloc(sizeof(MESA_htable_handle) * thread_cnt);
	memset(g_l2tp_htable_array, 0, sizeof(MESA_htable_handle) * thread_cnt);

	for(i = 0; i < thread_cnt; i++){
		g_l2tp_htable_array[i] = l2tp_stream_hash_create();
	}

	return g_l2tp_plugid;
}

void l2tp_protocol_destroy(void)
{
	;
}

#ifdef __cplusplus
}
#endif