Final development LAC functionality.
[l2tpns.git] / l2tplac.c
1 /*
2 * Add functionality "LAC" to l2tpns.
3 * Used to forward a ppp session to another "LNS".
4 */
5 #include <errno.h>
6 #include <string.h>
7
8 #include "md5.h"
9 #include "l2tpns.h"
10 #include "util.h"
11
12 #include "l2tplac.h"
13
14 /* sequence diagram: Client <--> LAC <--> LNS1 <--> LNS2
15 *
16 * LCP Negotiation
17 * Client <-------------------> LAC
18 * Challenge (CHAP/PAP)
19 * Client <-------------------> LAC
20 * SCCRQ
21 * LAC --------------------> LNS1 (Tunnel Open)
22 * SCCRP
23 * LAC <-------------------- LNS1 (Tunnel Open)
24 * SCCCN
25 * LAC --------------------> LNS1 (Tunnel Open)
26 * ZLB
27 * LAC <-------------------- LNS1 (Tunnel Open)
28 * ICRQ
29 * LAC --------------------> LNS1 (Session Open)
30 * ICRP
31 * LAC <-------------------- LNS1 (Session Open)
32 * ICCN
33 * LAC --------------------> LNS1 (Session Open)
34 * ZLB
35 * LAC <-------------------- LNS1 (Session Open)
36 * LCP Negotiation
37 * Client <---------------------------------------------> LNS1
38 * Challenge (CHAP/PAP)
39 * Client <---------------------------------------------> LNS1
40 * SCCRQ
41 * LNS1 --------------------> LNS2 (Tunnel Open)
42 * SCCRP
43 * LNS1 <-------------------- LNS2 (Tunnel Open)
44 * SCCCN
45 * LNS1 --------------------> LNS2 (Tunnel Open)
46 * ZLB
47 * LNS1 <-------------------- LNS2 (Tunnel Open)
48 * ICRQ
49 * LNS1 --------------------> LNS2 (Session Open)
50 * ICRP
51 * LNS1 <-------------------- LNS2 (Session Open)
52 * ICCN
53 * LNS1 --------------------> LNS2 (Session Open)
54 * ZLB
55 * LNS1 <-------------------- LNS2 (Session Open)
56 * LCP Negotiation
57 * Client <------------------------------------------------------------------------> LNS2
58 * PAP/CHAP Authentification
59 * Client <------------------------------------------------------------------------> LNS2
60 * DATA (ppp)
61 * Client <------------------------------------------------------------------------> LNS2
62 * */
63
64 typedef struct
65 {
66 uint32_t tunnel_type;
67 uint32_t tunnel_medium_type;
68 in_addr_t tunnel_server_endpoint; /* IP remote LNS */
69 char tunnel_password[64]; /* l2tpsecret remote LNS */
70 char tunnel_assignment_id[256];
71 } tunnelrlnst;
72
73 // Max Radius Tunnels by remote LNS
74 #define MAXTAGTUNNEL 0x20
75 static tunnelrlnst ptunnelrlns[MAXTAGTUNNEL];
76
77 /*
78 * Possible configrlns states
79 * CONFRLNSFREE -> CONFRLNSSET -> CONFRLNSFREE
80 */
81 enum
82 {
83 CONFRLNSFREE = 0, // Not in use
84 CONFRLNSSET, // Config Set
85 CONFRLNSSETBYRADIUS // Config Set
86 };
87
88 // struct remote lns
89 typedef struct
90 {
91 int state; // conf state (tunnelstate enum)
92 in_addr_t ip; // Ip for far end
93 uint16_t port; // port for far end
94 hasht auth; // request authenticator
95 char strmaskuser[MAXUSER];
96 char l2tp_secret[64]; // L2TP shared secret
97 char tunnel_assignment_id[256];
98 }
99 configrlns;
100
101 configrlns *pconfigrlns = NULL;
102
103 // Init data structures
104 void lac_initremotelnsdata()
105 {
106 confrlnsidt i;
107
108 if ( !(pconfigrlns = shared_malloc(sizeof(pconfigrlns[0]) * MAXRLNSTUNNEL)) )
109 {
110 LOG(0, 0, 0, "Error doing malloc for tunnels lac: %s\n", strerror(errno));
111 exit(1);
112 }
113
114 memset(pconfigrlns, 0, sizeof(pconfigrlns[0]) * MAXRLNSTUNNEL);
115
116 // Mark all the conf as free.
117 for (i = 1; i < MAXRLNSTUNNEL; i++)
118 pconfigrlns[i].state = CONFRLNSFREE; // mark it as not filled in.
119
120 config->highest_rlnsid = 0;
121
122 lac_reset_rad_tag_tunnel_ctxt();
123 }
124
125 // Reset Radius TAG tunnel context
126 void lac_reset_rad_tag_tunnel_ctxt()
127 {
128 memset(ptunnelrlns, 0, sizeof(ptunnelrlns[0]) * MAXTAGTUNNEL);
129 }
130
131 // Add tunnel_type radius TAG tunnel to context
132 void lac_set_rad_tag_tunnel_type(uint8_t tag, uint32_t tunnel_type)
133 {
134 if (tag < MAXTAGTUNNEL)
135 ptunnelrlns[tag].tunnel_type = tunnel_type;
136 }
137
138 // Add tunnel_medium_type Radius TAG tunnel to context
139 void lac_set_rad_tag_tunnel_medium_type(uint8_t tag, uint32_t tunnel_medium_type)
140 {
141 if (tag < MAXTAGTUNNEL)
142 ptunnelrlns[tag].tunnel_medium_type = tunnel_medium_type;
143 }
144
145 // Add tunnel_server_endpoint Radius TAG tunnel to context
146 void lac_set_rad_tag_tunnel_serv_endpt(uint8_t tag, char *tunnel_server_endpoint)
147 {
148 if (tag < MAXTAGTUNNEL)
149 {
150 ptunnelrlns[tag].tunnel_server_endpoint = ntohl(inet_addr(tunnel_server_endpoint));
151 }
152 }
153
154 // Add tunnel_password Radius TAG tunnel to context
155 void lac_set_rad_tag_tunnel_password(uint8_t tag, char *tunnel_password)
156 {
157 if ((tag < MAXTAGTUNNEL) && (strlen(tunnel_password) < 64))
158 {
159 strcpy(ptunnelrlns[tag].tunnel_password, tunnel_password);
160 }
161 }
162
163 // Add tunnel_assignment_id Radius TAG tunnel to context
164 void lac_set_rad_tag_tunnel_assignment_id(uint8_t tag, char *tunnel_assignment_id)
165 {
166 if ((tag < MAXTAGTUNNEL) && (strlen(tunnel_assignment_id) < 256))
167 {
168 strcpy(ptunnelrlns[tag].tunnel_assignment_id, tunnel_assignment_id);
169 }
170 }
171
172 // Select a tunnel_assignment_id
173 int lac_rad_select_assignment_id(sessionidt s, char *assignment_id)
174 {
175 int idtag;
176 int nbtagfound = 0;
177 int bufidtag[MAXTAGTUNNEL];
178
179 for (idtag = 0; idtag < MAXTAGTUNNEL; ++idtag)
180 {
181 if (ptunnelrlns[idtag].tunnel_type == 0)
182 continue;
183 else if (ptunnelrlns[idtag].tunnel_type != 3) // 3 == L2TP tunnel type
184 LOG(1, s, session[s].tunnel, "Error, Only L2TP tunnel type supported\n");
185 else if (ptunnelrlns[idtag].tunnel_medium_type != 1)
186 LOG(1, s, session[s].tunnel, "Error, Only IP tunnel medium type supported\n");
187 else if (ptunnelrlns[idtag].tunnel_server_endpoint == 0)
188 LOG(1, s, session[s].tunnel, "Error, Bad IP tunnel server endpoint \n");
189 else if (strlen(ptunnelrlns[idtag].tunnel_assignment_id) > 0)
190 {
191 bufidtag[nbtagfound] = idtag;
192 nbtagfound++;
193 }
194 }
195
196 if (nbtagfound > 0)
197 {
198 // random between 0 and nbtagfound-1
199 idtag = (rand() % nbtagfound);
200
201 if (idtag >= nbtagfound)
202 idtag = 0; //Sanity checks.
203
204 strcpy(assignment_id, ptunnelrlns[bufidtag[idtag]].tunnel_assignment_id);
205 return 1;
206 }
207
208 // Error no tunnel_assignment_id found
209 return 0;
210 }
211
212 // Save the 'radius tag tunnels' context on global configuration
213 void lac_save_rad_tag_tunnels(sessionidt s)
214 {
215 confrlnsidt idrlns;
216 int idtag;
217
218 for (idtag = 0; idtag < MAXTAGTUNNEL; ++idtag)
219 {
220 if (ptunnelrlns[idtag].tunnel_type == 0)
221 continue;
222 else if (ptunnelrlns[idtag].tunnel_type != 3) // 3 == L2TP tunnel type
223 LOG(1, s, session[s].tunnel, "Error, Only L2TP tunnel type supported\n");
224 else if (ptunnelrlns[idtag].tunnel_medium_type != 1)
225 LOG(1, s, session[s].tunnel, "Error, Only IP tunnel medium type supported\n");
226 else if (ptunnelrlns[idtag].tunnel_server_endpoint == 0)
227 LOG(1, s, session[s].tunnel, "Error, Bad IP tunnel server endpoint \n");
228 else if (strlen(ptunnelrlns[idtag].tunnel_assignment_id) <= 0)
229 LOG(1, s, session[s].tunnel, "Error, No tunnel_assignment_id \n");
230 else if (ptunnelrlns[idtag].tunnel_server_endpoint == ntohl(config->bind_address))
231 LOG(0, s, session[s].tunnel, "Error, IP Remote LNS == IP local bind address (%s) !!!\n", fmtaddr(config->bind_address, 0));
232 else
233 {
234 for (idrlns = 1; idrlns < MAXRLNSTUNNEL; ++idrlns)
235 {
236 if (pconfigrlns[idrlns].state == CONFRLNSFREE)
237 {
238 pconfigrlns[idrlns].ip = ptunnelrlns[idtag].tunnel_server_endpoint;
239 pconfigrlns[idrlns].port = L2TPPORT; //Default L2TP port
240 strcpy(pconfigrlns[idrlns].l2tp_secret, ptunnelrlns[idtag].tunnel_password);
241 strcpy(pconfigrlns[idrlns].tunnel_assignment_id, ptunnelrlns[idtag].tunnel_assignment_id);
242
243 config->highest_rlnsid = idrlns;
244
245 pconfigrlns[idrlns].state = CONFRLNSSETBYRADIUS;
246
247 break;
248 }
249 else if (pconfigrlns[idrlns].state == CONFRLNSSETBYRADIUS)
250 {
251 if ( (pconfigrlns[idrlns].ip == ptunnelrlns[idtag].tunnel_server_endpoint) &&
252 (strcmp(pconfigrlns[idrlns].tunnel_assignment_id, ptunnelrlns[idtag].tunnel_assignment_id) == 0) )
253 {
254 // l2tp_secret may be changed
255 strcpy(pconfigrlns[idrlns].l2tp_secret, ptunnelrlns[idtag].tunnel_password);
256 pconfigrlns[idrlns].port = L2TPPORT; //Default L2TP poart
257
258 if (config->highest_rlnsid < idrlns) config->highest_rlnsid = idrlns;
259
260 break;
261 }
262 }
263 }
264
265 if (idrlns >= MAXRLNSTUNNEL)
266 {
267 LOG(0, s, session[s].tunnel, "No more Remote LNS Conf Free\n");
268 return;
269 }
270 }
271 }
272 }
273
274 // Create Remote LNS a Tunnel or Session
275 static int lac_create_tunnelsession(tunnelidt t, sessionidt s, confrlnsidt i_conf, char * puser)
276 {
277 if (t == 0)
278 {
279 if (main_quit == QUIT_SHUTDOWN) return 0;
280
281 // Start Open Tunnel
282 if (!(t = lac_new_tunnel()))
283 {
284 LOG(1, 0, 0, "No more tunnels\n");
285 STAT(tunnel_overflow);
286 return 0;
287 }
288 lac_tunnelclear(t);
289 tunnel[t].ip = pconfigrlns[i_conf].ip;
290 tunnel[t].port = pconfigrlns[i_conf].port;
291 tunnel[t].window = 4; // default window
292 tunnel[t].isremotelns = i_conf;
293 STAT(tunnel_created);
294
295 random_data(pconfigrlns[i_conf].auth, sizeof(pconfigrlns[i_conf].auth));
296
297 LOG(2, 0, t, "Create New tunnel to REMOTE LNS %s for user %s\n", fmtaddr(htonl(tunnel[t].ip), 0), puser);
298 lac_send_SCCRQ(t, pconfigrlns[i_conf].auth, sizeof(pconfigrlns[i_conf].auth));
299 }
300 else if (tunnel[t].state == TUNNELOPEN)
301 {
302 if (main_quit != QUIT_SHUTDOWN)
303 {
304
305 /**********************/
306 /** Open New session **/
307 /**********************/
308 sessionidt new_sess = sessionfree;
309
310 sessionfree = session[new_sess].next;
311 memset(&session[new_sess], 0, sizeof(session[new_sess]));
312
313 if (new_sess > config->cluster_highest_sessionid)
314 config->cluster_highest_sessionid = new_sess;
315
316 session[new_sess].opened = time_now;
317 session[new_sess].tunnel = t;
318 session[new_sess].last_packet = session[s].last_data = time_now;
319
320 session[new_sess].ppp.phase = Establish;
321 session[new_sess].ppp.lcp = Starting;
322 session[s].ppp.phase = Establish;
323
324 LOG(2, 0, t, "Open New session to REMOTE LNS %s for user: %s\n", fmtaddr(htonl(tunnel[t].ip), 0), puser);
325 // Sent ICRQ Incoming-call-request
326 lac_send_ICRQ(t, new_sess);
327
328 // Set session to forward to another LNS
329 session[s].forwardtosession = new_sess;
330 session[new_sess].forwardtosession = s;
331 strncpy(session[s].user, puser, sizeof(session[s].user) - 1);
332 strncpy(session[new_sess].user, puser, sizeof(session[new_sess].user) - 1);
333
334 STAT(session_created);
335 }
336 else
337 {
338 lac_tunnelshutdown(t, "Shutting down", 6, 0, 0);
339 }
340 }
341 else
342 {
343 /** TODO **/
344 LOG(1, 0, t, "(REMOTE LNS) tunnel is not open\n");
345 }
346
347 return 1;
348 }
349 // Check if session must be forwarded to another LNS
350 // return 1 if the session must be forwarded (and Creating a tunnel/session has been started)
351 // else 0.
352 // Note: check from the configuration read on the startup-config (see setforward)
353 int lac_conf_forwardtoremotelns(sessionidt s, char * puser)
354 {
355 tunnelidt t, j;
356 confrlnsidt i;
357
358 for (i = 1; i <= config->highest_rlnsid ; ++i)
359 {
360 if ( (pconfigrlns[i].state == CONFRLNSSET) && (NULL != strstr(puser, pconfigrlns[i].strmaskuser)) )
361 {
362 t = 0;
363 for (j = 0; j <= config->cluster_highest_tunnelid ; ++j)
364 {
365 if ((tunnel[j].isremotelns) &&
366 (tunnel[j].ip == pconfigrlns[i].ip) &&
367 (tunnel[j].port == pconfigrlns[i].port) &&
368 (tunnel[j].state != TUNNELDIE))
369 {
370 t = j;
371 if (tunnel[t].isremotelns != i)
372 {
373 if ( (tunnel[t].state == TUNNELOPEN) || (tunnel[t].state == TUNNELOPENING) )
374 {
375 LOG(1, 0, t, "Tunnel Remote LNS ID inconsistency (IP RLNS:%s)\n",
376 fmtaddr(htonl(pconfigrlns[i].ip), 0));
377
378 tunnel[t].isremotelns = i;
379 }
380 else t = 0;
381 }
382 break;
383 }
384 }
385
386 return lac_create_tunnelsession(t, s, i, puser);
387 }
388 }
389
390 return 0;
391 }
392
393 // return 1 if the session must be forwarded (and Creating a tunnel/session has been started)
394 // else 0.
395 // Note: Started from a radius response
396 int lac_rad_forwardtoremotelns(sessionidt s, char *assignment_id, char * puser)
397 {
398 tunnelidt t, j;
399 confrlnsidt i;
400
401 for (i = 1; i <= config->highest_rlnsid ; ++i)
402 {
403 if ((pconfigrlns[i].state == CONFRLNSSETBYRADIUS) &&
404 (strcmp(pconfigrlns[i].tunnel_assignment_id, assignment_id) == 0))
405 {
406 t = 0;
407 for (j = 1; j <= config->cluster_highest_tunnelid ; ++j)
408 {
409 if ((tunnel[j].isremotelns == i) &&
410 (tunnel[j].ip == pconfigrlns[i].ip) &&
411 (tunnel[j].port == pconfigrlns[i].port) &&
412 (tunnel[j].state != TUNNELDIE))
413 {
414 if ( (tunnel[j].state == TUNNELOPEN) ||
415 (tunnel[j].state == TUNNELOPENING) )
416 {
417 t = j;
418 LOG(3, 0, t, "Tunnel Remote LNS already open(ing) (RLNS IP:%s)\n", fmtaddr(htonl(pconfigrlns[i].ip), 0));
419 break;
420 }
421 }
422 }
423
424 return lac_create_tunnelsession(t, s, i, puser);
425 }
426 }
427
428 return 0;
429 }
430
431 // Calcul the remote LNS auth
432 void lac_calc_rlns_auth(tunnelidt t, uint8_t id, uint8_t *out)
433 {
434 MD5_CTX ctx;
435 confrlnsidt idrlns;
436
437 idrlns = tunnel[t].isremotelns;
438
439 MD5_Init(&ctx);
440 MD5_Update(&ctx, &id, 1);
441 MD5_Update(&ctx, pconfigrlns[idrlns].l2tp_secret, strlen(pconfigrlns[idrlns].l2tp_secret));
442 MD5_Update(&ctx, pconfigrlns[idrlns].auth, 16);
443 MD5_Final(out, &ctx);
444 }
445
446 // Forward session to LAC or Remote LNS
447 int lac_session_forward(uint8_t *buf, int len, sessionidt sess, uint16_t proto)
448 {
449 uint16_t t = 0, s = 0;
450 uint8_t *p = buf + 2; // First word L2TP options
451
452 s = session[sess].forwardtosession;
453 if (session[s].forwardtosession != sess)
454 {
455 LOG(0, sess, session[sess].tunnel, "Link Session (%u) broken\n", s);
456 return 0;
457 }
458
459 t = session[s].tunnel;
460 if (t >= MAXTUNNEL)
461 {
462 LOG(1, s, t, "Session with invalid tunnel ID\n");
463 return 0;
464 }
465
466 if ((!tunnel[t].isremotelns) && (!tunnel[session[sess].tunnel].isremotelns))
467 {
468 LOG(0, sess, session[sess].tunnel, "Link Tunnel Session (%u) broken\n", s);
469 return 0;
470 }
471
472 if (*buf & 0x40)
473 { // length
474 p += 2;
475 }
476
477 *(uint16_t *) p = htons(tunnel[t].far); // tunnel
478 p += 2;
479 *(uint16_t *) p = htons(session[s].far); // session
480 p += 2;
481
482 if (*buf & 0x08)
483 { // ns/nr
484 *(uint16_t *) p = htons(tunnel[t].ns); // sequence
485 p += 2;
486 *(uint16_t *) p = htons(tunnel[t].nr); // sequence
487 p += 2;
488 }
489
490 if ((proto == PPPIP) || (proto == PPPMP) ||(proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]))
491 {
492 session[sess].last_packet = session[sess].last_data = time_now;
493 // Update STAT IN
494 increment_counter(&session[sess].cin, &session[sess].cin_wrap, len);
495 session[sess].cin_delta += len;
496 session[sess].pin++;
497 sess_local[sess].cin += len;
498 sess_local[sess].pin++;
499
500 session[s].last_data = time_now;
501 // Update STAT OUT
502 increment_counter(&session[s].cout, &session[s].cout_wrap, len); // byte count
503 session[s].cout_delta += len;
504 session[s].pout++;
505 sess_local[s].cout += len;
506 sess_local[s].pout++;
507 }
508 else
509 session[sess].last_packet = time_now;
510
511 tunnelsend(buf, len, t); // send it...
512
513 return 1;
514 }
515
516 // Add new Remote LNS from CLI
517 // return:
518 // 0 = Error
519 // 1 = New Remote LNS conf ADD
520 // 2 = Remote LNS Conf Updated
521 int lac_addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *SecretRemoteLNS)
522 {
523 confrlnsidt idrlns;
524
525 for (idrlns = 1; idrlns < MAXRLNSTUNNEL; ++idrlns)
526 {
527 if (pconfigrlns[idrlns].state == CONFRLNSFREE)
528 {
529 snprintf((char *) pconfigrlns[idrlns].strmaskuser, sizeof(pconfigrlns[idrlns].strmaskuser), "%s", mask);
530 pconfigrlns[idrlns].ip = ntohl(inet_addr(IP_RemoteLNS));
531 pconfigrlns[idrlns].port = atoi(Port_RemoteLNS);
532 snprintf((char *) pconfigrlns[idrlns].l2tp_secret, sizeof(pconfigrlns[idrlns].l2tp_secret), "%s", SecretRemoteLNS);
533
534 config->highest_rlnsid = idrlns;
535
536 pconfigrlns[idrlns].state = CONFRLNSSET;
537
538 return 1;
539 }
540 else if ((pconfigrlns[idrlns].state == CONFRLNSSET) && (strcmp(pconfigrlns[idrlns].strmaskuser, mask) == 0))
541 {
542 if ( (pconfigrlns[idrlns].ip != ntohl(inet_addr(IP_RemoteLNS))) ||
543 (pconfigrlns[idrlns].port != atoi(Port_RemoteLNS)) ||
544 (strcmp(pconfigrlns[idrlns].l2tp_secret, SecretRemoteLNS) != 0) )
545 {
546 memset(&pconfigrlns[idrlns], 0, sizeof(pconfigrlns[idrlns]));
547 snprintf((char *) pconfigrlns[idrlns].strmaskuser, sizeof(pconfigrlns[idrlns].strmaskuser), "%s", mask);
548 pconfigrlns[idrlns].ip = ntohl(inet_addr(IP_RemoteLNS));
549 pconfigrlns[idrlns].port = atoi(Port_RemoteLNS);
550 snprintf((char *) pconfigrlns[idrlns].l2tp_secret, sizeof(pconfigrlns[idrlns].l2tp_secret), "%s", SecretRemoteLNS);
551
552 if (config->highest_rlnsid < idrlns) config->highest_rlnsid = idrlns;
553
554 pconfigrlns[idrlns].state = CONFRLNSSET;
555 // Conf Updated, the tunnel must be dropped
556 return 2;
557 }
558
559 return 1;
560 }
561 }
562
563 LOG(0, 0, 0, "No more Remote LNS Conf Free\n");
564
565 return 0;
566 }
567
568 // Cli Show remote LNS defined
569 int lac_cli_show_remotelns(confrlnsidt idrlns, char *strout)
570 {
571 if (idrlns > config->highest_rlnsid)
572 return 0;
573
574 if (idrlns == 0)
575 // Show Summary
576 sprintf(strout, "%15s %3s %-32s %-32s %11s %7s %10s",
577 "IP Remote LNS",
578 "TID",
579 "l2tp secret",
580 "assignment Id",
581 "File/Radius",
582 "State",
583 "Count Sess");
584 else
585 {
586 tunnelidt t, tfound = 0;
587 sessionidt s;
588 int countsess = 0;
589 char state[20];
590
591 strcpy(state, "Close");
592 for (t = 0; t <= config->cluster_highest_tunnelid ; ++t)
593 {
594 if ((tunnel[t].isremotelns == idrlns) &&
595 (tunnel[t].ip == pconfigrlns[idrlns].ip) &&
596 (tunnel[t].port == pconfigrlns[idrlns].port) &&
597 (tunnel[t].state != TUNNELDIE))
598 {
599 if (tunnel[t].state == TUNNELOPENING)
600 strcpy(state, "Opening");
601 else if (tunnel[t].state == TUNNELOPEN)
602 strcpy(state, "Open");
603
604 for (s = 1; s <= config->cluster_highest_sessionid ; ++s)
605 if (session[s].tunnel == t)
606 countsess++;
607 tfound = t;
608 break;
609 }
610 }
611
612 sprintf(strout, "%15s %3u %-32s %-32s %11s %7s %10u",
613 fmtaddr(htonl(pconfigrlns[idrlns].ip), 0),
614 tfound,
615 pconfigrlns[idrlns].l2tp_secret,
616 pconfigrlns[idrlns].tunnel_assignment_id,
617 (pconfigrlns[idrlns].state == CONFRLNSSET?"File":(pconfigrlns[idrlns].state == CONFRLNSSETBYRADIUS?"Radius":"Free")),
618 state,
619 countsess);
620 }
621
622 return 1;
623 }