-
Notifications
You must be signed in to change notification settings - Fork 2
/
l2tp_session.c
5386 lines (4703 loc) · 188 KB
/
l2tp_session.c
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
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*****************************************************************************
* Copyright (C) 2004,2005,2006,2007,2008 Katalix Systems Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*****************************************************************************/
/* L2TP session implementation.
*
* Sessions are scoped by tunnel and are identified by a tunnel_id /
* session_id pair. For management convenience, sessions can also be
* given an administrative name.
*
* Sessions are created either by management request or by an L2TP
* control protocol request from a remote peer. There are 4 types, all
* of which are implemented here using four different state machines.
*/
#define _GNU_SOURCE
#include <string.h> /* for strndup() */
#include "usl.h"
#include "l2tp_private.h"
#include "l2tp_rpc.h"
#ifndef aligned_u64
/* should be defined in sys/types.h */
#define aligned_u64 unsigned long long __attribute__((aligned(8)))
#endif
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_pppox.h>
#include <linux/ppp_defs.h>
#include <linux/if_ppp.h>
#include <linux/if_pppol2tp.h>
#ifdef DEBUG
#define L2TP_SESSION_CHECK(_sess, _tunnel) \
do { \
if ((_tunnel != NULL) && (_tunnel != _sess->my_tunnel)) { \
l2tp_log(LOG_ERR, "session check: bad tunnel (%p %p)", \
_tunnel, _sess->my_tunnel); \
_tunnel = _sess->my_tunnel; \
} \
} while(0)
#else
#define L2TP_SESSION_CHECK(_sess, _tunnel) do { } while(0)
#endif /* DEBUG */
/* Session context. One per session.
*/
struct l2tp_session {
struct usl_list_head list; /* our session list (all tunnels) */
struct usl_list_head session_list; /* tunnel's session list */
struct usl_hlist_node session_id_hlist; /* tunnel's hashed session id list */
struct usl_list_head detached_list; /* detached session list (persisting) */
struct usl_fsm_instance fsmi;
struct l2tp_tunnel *my_tunnel;
enum l2tp_api_session_type type;
int open_event;
int close_event;
int use_count;
struct usl_timer *timer;
struct usl_timer *retry_timer;
struct l2tp_session_config config;
struct {
uint16_t session_id;
uint16_t peer_session_id;
uint16_t created_by_admin:1;
uint16_t persist:1;
uint16_t detached:1;
uint16_t tunnel_down:1;
uint16_t pad1;
uint32_t call_serial_number;
uint32_t physical_channel_id;
char *create_time;
} status;
struct {
struct l2tp_avp_minimum_bps minimum_bps;
struct l2tp_avp_maximum_bps maximum_bps;
struct l2tp_avp_connect_speed connect_speed;
struct l2tp_avp_rx_connect_speed rx_connect_speed;
struct l2tp_avp_proxy_auth_type proxy_auth_type;
int sequencing_required:1;
struct l2tp_avp_proxy_auth_id proxy_auth_id;
struct l2tp_avp_proxy_auth_name *proxy_auth_name;
struct l2tp_avp_proxy_auth_challenge *proxy_auth_challenge;
int proxy_auth_challenge_len;
struct l2tp_avp_proxy_auth_response *proxy_auth_response;
int proxy_auth_response_len;
struct l2tp_avp_priv_group_id *priv_group_id;
struct l2tp_avp_framing_type framing_type;
struct l2tp_avp_bearer_type bearer_type;
struct l2tp_avp_call_serial_number call_serial_number;
struct l2tp_avp_physical_channel_id physical_channel_id;
struct l2tp_avp_initial_rcvd_lcp_confreq *initial_rcvd_lcp_confreq;
int initial_rcvd_lcp_confreq_len;
struct l2tp_avp_last_sent_lcp_confreq *last_sent_lcp_confreq;
int last_sent_lcp_confreq_len;
struct l2tp_avp_last_rcvd_lcp_confreq *last_rcvd_lcp_confreq;
int last_rcvd_lcp_confreq_len;
struct l2tp_avp_called_number *called_number;
struct l2tp_avp_calling_number *calling_number;
struct l2tp_avp_sub_address *sub_address;
struct l2tp_avp_call_errors *call_errors;
struct l2tp_avp_accm *accm;
struct l2tp_avp_q931_cause_code *q931_cause_code;
int q931_cause_code_len;
} peer;
/* Temporary state, filled in by FSM actions */
struct l2tp_avp_result_code *result_code;
int result_code_len;
struct l2tp_avp_q931_cause_code *q931_cause_code;
int q931_cause_code_len;
uint32_t send_accm;
uint32_t recv_accm;
/* event hook debounce */
int sent_created_event:1;
int sent_up_event:1;
int sent_down_event:1;
int sent_deleted_event:1;
int cleaned_up:1;
};
/* Session profile context.
*/
struct l2tp_session_profile {
struct usl_list_head list;
char *profile_name;
uint32_t flags;
int sequencing_required:1;
int use_sequence_numbers:1;
int no_ppp:1;
int reorder_timeout;
int trace_flags;
char *ppp_profile_name;
char *priv_group_id;
uint32_t minimum_bps;
uint32_t maximum_bps;
uint32_t connect_speed;
uint32_t rx_connect_speed;
uint16_t framing_type_sync:1;
uint16_t framing_type_async:1;
uint16_t bearer_type_digital:1;
uint16_t bearer_type_analog:1;
uint16_t use_ppp_proxy:1;
int use_count;
};
/* Hooks, overridable by plugins */
int (*l2tp_session_created_hook)(struct l2tp_session const *session, uint16_t tunnel_id, uint16_t session_id) = NULL;
int (*l2tp_session_deleted_hook)(struct l2tp_session const *session, uint16_t tunnel_id, uint16_t session_id) = NULL;
int (*l2tp_session_modified_hook)(struct l2tp_session const *session, uint16_t tunnel_id, uint16_t session_id) = NULL;
int (*l2tp_session_up_hook)(struct l2tp_session const *session, uint16_t tunnel_id, uint16_t session_id, uint16_t peer_tunnel_id, uint16_t peer_session_id) = NULL;
int (*l2tp_session_down_hook)(struct l2tp_session const *session, uint16_t tunnel_id, uint16_t session_id) = NULL;
int (*l2tp_session_get_stats_hook)(struct l2tp_session const *session, uint16_t tunnel_id, uint16_t session_id, struct pppol2tp_ioc_stats *stats) = NULL;
int (*l2tp_session_open_bearer_hook)(struct l2tp_session const *session, const char *called_number) = NULL;
int (*l2tp_session_close_bearer_hook)(struct l2tp_session const *session, const char *called_number) = NULL;
void (*l2tp_session_ppp_created_hook)(struct l2tp_session const *session, uint16_t tunnel_id, uint16_t session_id, int unit) = NULL;
void (*l2tp_session_ppp_deleted_hook)(struct l2tp_session const *session, uint16_t tunnel_id, uint16_t session_id) = NULL;
static int l2tp_session_queue_event(uint16_t tunnel_id, uint16_t session_id, int event);
static struct l2tp_session *l2tp_session_alloc(struct l2tp_tunnel *tunnel, enum l2tp_api_session_type session_type, uint16_t session_id,
char *session_profile_name, int created_by_admin, int *result);
static void l2tp_session_free(struct l2tp_session *session);
static int l2tp_session_link(struct l2tp_session *session);
static void l2tp_session_unlink(struct l2tp_session *session, int force);
static struct l2tp_session_profile *l2tp_session_profile_find(const char *name);
static void l2tp_session_persist(struct l2tp_session *session);
static void l2tp_session_tunnel_detach(struct l2tp_tunnel *tunnel, struct l2tp_session *session);
enum l2tp_session_msg_action { MSG_ACCEPT, MSG_DENY };
static int l2tp_session_map_event(enum l2tp_api_session_type session_type, int msg_type, enum l2tp_session_msg_action accept);
/* Local data */
static struct l2tp_session_profile *l2tp_session_defaults;
static USL_LIST_HEAD(l2tp_session_list);
static USL_LIST_HEAD(l2tp_session_detached_list);
static USL_LIST_HEAD(l2tp_session_profile_list);
static uint32_t l2tp_session_call_serial_number = 0;
static int l2tp_session_persist_pend_timeout;
static int l2tp_session_establish_timeout;
static int l2tp_session_count;
static int l2tp_session_max_count;
static int l2tp_session_event_pipe[2] = { -1, -1 };
/* If we need to set a result code but we run out of memory, use
* this preallocated one.
*/
#define L2TP_SES_EMERG_RESULT_CODE_SIZE 128
static struct l2tp_avp_result_code *l2tp_session_emergency_result_code;
#undef RESC
#undef ERRC
#define RESC(x) L2TP_AVP_RESULT_CDN_##x
#define ERRC(x) L2TP_AVP_ERROR_##x
/* This table is used to derive English phrases for well-known L2TP session errors.
* See RFC2661.
*/
static const struct l2tp_result_codes l2tp_session_cdn_result_codes[] = {
{ RESC(RESERVED), ERRC(NO_ERROR), "Reserved" },
{ RESC(LOST_CARRIER), ERRC(NO_ERROR), "Call disconnected due to loss of carrier" },
{ RESC(GENERAL_ERROR), ERRC(NO_ERROR), "Call disconnected for the reason indicated in error code" },
{ RESC(GENERAL_ERROR), ERRC(NO_TUNNEL_YET), "No control connection exists yet for this LAC-LNS pair" },
{ RESC(GENERAL_ERROR), ERRC(BAD_LENGTH), "Length is wrong" },
{ RESC(GENERAL_ERROR), ERRC(BAD_VALUE), "One of the field values was out of range or reserved field was non-zero" },
{ RESC(GENERAL_ERROR), ERRC(NO_RESOURCE), "Insufficient resources to handle this operation now" },
{ RESC(GENERAL_ERROR), ERRC(BAD_SESSION_ID), "The Session ID is invalid in this context" },
{ RESC(GENERAL_ERROR), ERRC(VENDOR_ERROR), "A generic vendor-specific error occurred" },
{ RESC(GENERAL_ERROR), ERRC(TRY_ANOTHER), "Try another" },
{ RESC(GENERAL_ERROR), ERRC(MBIT_SHUTDOWN), "Unknown mandatory AVP received" },
{ RESC(ADMIN), ERRC(NO_ERROR), "Call disconnected for administrative reasons" },
{ RESC(NO_RESOURCES), ERRC(NO_ERROR), "Call failed due to lack of resources" },
{ RESC(NOT_AVAILABLE), ERRC(NO_ERROR), "Call failed due to no configuration or support" },
{ RESC(INVALID_DEST), ERRC(NO_ERROR), "Invalid destination" },
{ RESC(NO_CARRIER), ERRC(NO_ERROR), "Call failed due to no carrier detected" },
{ RESC(BUSY_SIGNAL), ERRC(NO_ERROR), "Call failed due to detection of a busy signal" },
{ RESC(NO_DIAL_TONE), ERRC(NO_ERROR), "Call failed due to lack of a dial tone" },
{ RESC(NO_ANSWER), ERRC(NO_ERROR), "Call was not established within time allotted" },
{ RESC(INVALID_XPRT), ERRC(NO_ERROR), "Call was connected but no appropriate framing was detected" },
{ -1, -1, NULL },
};
/*****************************************************************************
* Public interface
*****************************************************************************/
/* Log a message if the session's trace flags are enabled for the
* message category.
*/
void l2tp_session_log(struct l2tp_session const *session, int category, int level, const char *fmt, ...)
{
if ((session != NULL) && (category & session->config.trace_flags)) {
va_list ap;
va_start(ap, fmt);
l2tp_vlog(level, fmt, ap);
va_end(ap);
}
}
/* Give external modules access to the tunnel via the session.
*/
struct l2tp_tunnel *l2tp_session_get_tunnel(struct l2tp_session const *session)
{
return session->my_tunnel;
}
/* Give external modules visibility of the session name.
*/
const char *l2tp_session_get_name(struct l2tp_session const *session)
{
return session->fsmi.name;
}
/* For use by plugins.
*/
void l2tp_session_get_call_info(struct l2tp_session const *session, uint16_t *session_id, uint16_t *peer_session_id,
uint32_t *call_serial_number, uint32_t *physical_channel_id)
{
if (session_id != NULL) {
*session_id = session->status.session_id;
}
if (peer_session_id != NULL) {
*peer_session_id = session->status.peer_session_id;
}
if (call_serial_number != NULL) {
*call_serial_number = session->status.call_serial_number;
}
if (physical_channel_id != NULL) {
*physical_channel_id = session->status.physical_channel_id;
}
}
/* For access to a session's config. Used by plugins.
*/
struct l2tp_session_config const *l2tp_session_get_config(struct l2tp_session const *session)
{
return &session->config;
}
/* For access to the configured session establish timeout. Used by plugins..
*/
int l2tp_session_get_establish_timeout(void)
{
return l2tp_session_establish_timeout;
}
/* Allow plugins to tell if a session is created by local admin.
*/
int l2tp_session_is_created_by_admin(struct l2tp_session const *session)
{
return session->status.created_by_admin ? 1 : 0;
}
/* Easy way to tell if the session is at the LNS.
*/
int l2tp_session_is_lns(struct l2tp_session const *session)
{
if ((session->type == L2TP_API_SESSION_TYPE_LNIC) ||
(session->type == L2TP_API_SESSION_TYPE_LNOC)) {
return TRUE;
}
return FALSE;
}
/*****************************************************************************
* Internal implementation
*****************************************************************************/
static void l2tp_session_fsm_log(struct usl_fsm_instance const *fsmi, int level, const char *fmt, ...)
{
struct l2tp_session *session = ((void *) fsmi) - offsetof(struct l2tp_session, fsmi);
if (session->config.trace_flags & L2TP_FSM) {
va_list ap;
va_start(ap, fmt);
l2tp_vlog(level, fmt, ap);
va_end(ap);
}
}
static void l2tp_session_log_error(struct l2tp_session *session,
struct l2tp_avp_result_code *result_code,
int result_code_len)
{
const struct l2tp_result_codes *entry = &l2tp_session_cdn_result_codes[0];
if ((result_code == NULL) || (result_code_len < 4)) {
return;
}
while (entry->result_code >= 0) {
if (entry->result_code == result_code->result_code) {
if (entry->error_code == result_code->error_code) {
l2tp_session_log(session, L2TP_PROTOCOL, LOG_INFO, "PROTO: session %hu/%hu, CDN error %d/%d: %s%s%s",
session->my_tunnel ? l2tp_tunnel_id(session->my_tunnel) : 0,
session->status.session_id,
result_code->result_code,
result_code->error_code,
entry->error_string ? entry->error_string : "",
(result_code_len > sizeof(*result_code)) &&
result_code->error_message ? " - " : "",
(result_code_len > sizeof(*result_code)) &&
result_code->error_message ? result_code->error_message : "");
break;
}
}
entry++;
}
}
/* Called when something goes wrong in the session. Sets result code
* info in the session which is sent to the peer in certain L2TP
* protocol messages. As is typical with L2TP protocol messages, the
* data may be different length depending on the result code.
*/
static void l2tp_session_set_result(struct l2tp_session *session, uint16_t result_code,
uint16_t error_code, char *error_string)
{
int len = 2;
L2TP_DEBUG(L2TP_FUNC, "%s: tunl %s: code=%hu error=%hu msg=%s", __func__,
session->fsmi.name, result_code, error_code, error_string ? error_string : "");
/* Don't overwrite a result that is already present */
if (session->result_code != NULL) {
L2TP_DEBUG(L2TP_FUNC, "%s: tunl %s: preserving current data: code=%hu error=%hu msg=%s", __func__,
session->fsmi.name, session->result_code->result_code,
session->result_code->error_code,
session->result_code_len > sizeof(*session->result_code) ? session->result_code->error_message : "");
goto out;
}
/* Build result_code data structure */
if ((error_code != 0) || (error_string != NULL)) {
len += 2;
if (error_string != NULL) {
len += strlen(error_string) + 1;
}
}
session->result_code = malloc(len <= 5 ? 5 : len);
if (session->result_code == NULL) {
/* Use emergency result code */
session->result_code = l2tp_session_emergency_result_code;
}
session->result_code->result_code = result_code;
if (len > 2) {
session->result_code->error_code = error_code;
} else {
session->result_code->error_code = 0;
}
if (len > 4) {
strncpy(&session->result_code->error_message[0], error_string, len - sizeof(struct l2tp_avp_result_code));
} else {
session->result_code->error_message[0] = '\0';
}
session->result_code_len = len;
out:
return;
}
/* Convert Unix errno into a reasonable result code.
*/
static void l2tp_session_set_result_from_error_code(struct l2tp_session *session, int error_code)
{
char buf[80];
switch (error_code) {
case -ENOMEM:
l2tp_session_set_result(session, L2TP_AVP_RESULT_CDN_GENERAL_ERROR, L2TP_AVP_ERROR_NO_RESOURCE, NULL);
break;
case -EBADMSG:
l2tp_session_set_result(session, L2TP_AVP_RESULT_CDN_GENERAL_ERROR, L2TP_AVP_ERROR_BAD_VALUE, "unable to parse message");
break;
case -EINVAL:
l2tp_session_set_result(session, L2TP_AVP_RESULT_CDN_GENERAL_ERROR, L2TP_AVP_ERROR_BAD_VALUE, "bad message");
break;
case -EAGAIN:
l2tp_session_set_result(session, L2TP_AVP_RESULT_CDN_GENERAL_ERROR, L2TP_AVP_ERROR_TRY_ANOTHER, NULL);
break;
case -EOPNOTSUPP:
l2tp_session_set_result(session, L2TP_AVP_RESULT_CDN_NOT_AVAILABLE, 0, NULL);
break;
default:
sprintf(&buf[0], "error %d", error_code);
l2tp_session_set_result(session, L2TP_AVP_RESULT_CDN_GENERAL_ERROR, L2TP_AVP_ERROR_NO_ERROR, buf);
}
}
/* Come here to pass an event to the session's state machine. The
* event is handled in-line.
*/
static void l2tp_session_handle_event(struct l2tp_session *session, int event)
{
if (event < 0) {
l2tp_session_set_result_from_error_code(session, event);
/* If the event can't be mapped, we must close the session */
event = session->close_event;
}
/* Bump the use count on the session while the event is handled in case the event
* cause the session to be deleted. The context must not be deleted until after
* the FSM event handler has returned.
*/
if (session->my_tunnel != NULL) {
l2tp_session_inc_use_count(session);
usl_fsm_handle_event(&session->fsmi, event, session->my_tunnel, session, NULL);
l2tp_session_dec_use_count(session);
}
}
static int l2tp_session_param_defaults(struct l2tp_session *session, char *profile_name)
{
struct l2tp_session_profile *profile;
char *session_profile_name = NULL;
char *ppp_profile_name = NULL;
l2tp_tunnel_get_profile_names(session->my_tunnel, NULL,
&session_profile_name, &ppp_profile_name);
L2TP_DEBUG(L2TP_API, "tunl %s: Default session/ppp profiles from tunnel: '%s/%s'",
session->fsmi.name,
session_profile_name ? session_profile_name : "NULL",
ppp_profile_name ? ppp_profile_name : "NULL");
if ((profile_name != NULL) && (profile_name[0] != '\0')) {
L2TP_DEBUG(L2TP_API, "tunl %s: Deriving defaults using session profile '%s'", session->fsmi.name, profile_name);
} else {
profile_name = session_profile_name;
}
profile = l2tp_session_profile_find(profile_name);
if (profile == NULL) {
L2TP_DEBUG(L2TP_API, "Session profile '%s' not found", profile_name);
return -L2TP_ERR_SESSION_PROFILE_NOT_FOUND;
}
/* Override the ppp profile name set in the tunnel with the one set in
* the session profile.
*/
if ((profile->ppp_profile_name != NULL) &&
(strcmp(profile->ppp_profile_name, L2TP_API_PPP_PROFILE_DEFAULT_PROFILE_NAME) != 0)) {
ppp_profile_name = profile->ppp_profile_name;
}
l2tp_session_log(session, L2TP_FUNC, LOG_INFO, "FUNC: tunl %s: using session/ppp profiles '%s/%s' for defaults",
session->fsmi.name, profile->profile_name, ppp_profile_name);
if (strcmp(profile->profile_name, L2TP_API_SESSION_PROFILE_DEFAULT_PROFILE_NAME) != 0) {
session->config.profile_name = strdup(profile->profile_name);
if (l2tp_test_is_show_profile_usage()) {
session->config.flags |= L2TP_API_SESSION_FLAG_PROFILE_NAME;
}
}
if (strcmp(ppp_profile_name, L2TP_API_PPP_PROFILE_DEFAULT_PROFILE_NAME) != 0) {
session->config.ppp_profile_name = strdup(ppp_profile_name);
if (l2tp_test_is_show_profile_usage()) {
session->config.flags |= L2TP_API_SESSION_FLAG_PPP_PROFILE_NAME;
}
}
session->config.trace_flags = profile->trace_flags & l2tp_tunnel_get_trace_flags(session->my_tunnel);
session->config.sequencing_required = profile->sequencing_required;
session->config.use_sequence_numbers = profile->use_sequence_numbers;
session->config.no_ppp = profile->no_ppp;
session->config.reorder_timeout = profile->reorder_timeout;
session->config.framing_type_sync = profile->framing_type_sync ? -1 : 0;
session->config.framing_type_async = profile->framing_type_async ? -1 : 0;
session->config.bearer_type_digital = profile->bearer_type_digital ? -1 : 0;
session->config.bearer_type_analog = profile->bearer_type_analog ? -1 : 0;
session->config.minimum_bps = profile->minimum_bps;
session->config.maximum_bps = profile->maximum_bps;
session->config.connect_speed = profile->connect_speed;
session->config.rx_connect_speed = profile->rx_connect_speed;
session->config.use_ppp_proxy = profile->use_ppp_proxy;
session->config.mtu = l2tp_tunnel_get_mtu(session->my_tunnel);
session->config.do_pmtu_discovery = l2tp_tunnel_get_mtu_discovery(session->my_tunnel);
if (profile->priv_group_id != NULL) {
session->config.priv_group_id = strdup(profile->priv_group_id);
}
return 0;
}
/*****************************************************************************
* State change hook helpers.
* These functions drive the plugins, telling them of sessions created,
* deleted and up/down events. We guarantee that the session lifecycle
* will see created, up, down and deleted events (in that order).
*****************************************************************************/
static int l2tp_session_created_ind(struct l2tp_tunnel *tunnel, struct l2tp_session *session)
{
int result = 0;
L2TP_DEBUG(L2TP_FUNC, "%s: tunl %s", __func__, session->fsmi.name);
if ((l2tp_session_created_hook != NULL) && (!session->sent_created_event)) {
session->sent_created_event = 1;
session->sent_deleted_event = 0;
l2tp_session_inc_use_count(session);
result = (*l2tp_session_created_hook)(session, l2tp_tunnel_id(tunnel), session->status.session_id);
l2tp_session_dec_use_count(session);
}
return result;
}
static int l2tp_session_deleted_ind(struct l2tp_tunnel *tunnel, struct l2tp_session *session)
{
int result = 0;
L2TP_DEBUG(L2TP_FUNC, "%s: tunl %s", __func__, session->fsmi.name);
if ((l2tp_session_deleted_hook != NULL) && (!session->sent_deleted_event)) {
session->sent_deleted_event = 1;
session->sent_created_event = 0;
l2tp_session_inc_use_count(session);
result = (*l2tp_session_deleted_hook)(session, l2tp_tunnel_id(tunnel), session->status.session_id);
l2tp_session_dec_use_count(session);
}
return result;
}
static int l2tp_session_down_ind(struct l2tp_tunnel *tunnel, struct l2tp_session *session)
{
int result = 0;
L2TP_DEBUG(L2TP_FUNC, "%s: tunl %s", __func__, session->fsmi.name);
if ((l2tp_session_down_hook != NULL) && (!session->sent_down_event)) {
session->sent_down_event = 1;
session->sent_up_event = 0;
l2tp_session_inc_use_count(session);
result = (*l2tp_session_down_hook)(session, l2tp_tunnel_id(tunnel), session->status.session_id);
l2tp_session_dec_use_count(session);
}
return result;
}
static int l2tp_session_up_ind(struct l2tp_tunnel *tunnel, struct l2tp_session *session)
{
int result = 0;
L2TP_DEBUG(L2TP_FUNC, "%s: tunl %s", __func__, session->fsmi.name);
if (session->retry_timer != NULL) {
/* Session is up so stop retry timer */
usl_timer_stop(session->retry_timer);
}
if ((l2tp_session_up_hook != NULL) && (!session->sent_up_event)) {
session->sent_up_event = 1;
session->sent_down_event = 0;
l2tp_session_inc_use_count(session);
result = (*l2tp_session_up_hook)(session, l2tp_tunnel_id(tunnel), session->status.session_id,
l2tp_tunnel_peer_id(tunnel), session->status.peer_session_id);
l2tp_session_dec_use_count(session);
}
return result;
}
/*****************************************************************************
* Session context management.
*****************************************************************************/
static struct l2tp_session *l2tp_session_find_by_id(struct l2tp_tunnel *tunnel, uint16_t session_id)
{
struct usl_hlist_head *session_list = l2tp_tunnel_session_id_hlist(tunnel, session_id);
struct usl_hlist_node *tmp;
struct usl_hlist_node *walk;
struct l2tp_session *session;
usl_hlist_for_each(walk, tmp, session_list) {
session = usl_hlist_entry(walk, struct l2tp_session, session_id_hlist);
if (session->status.session_id == session_id) {
l2tp_test_session_id_hash_inc_stats(1);
return session;
}
l2tp_test_session_id_hash_inc_stats(0);
}
return NULL;
}
static struct l2tp_session *l2tp_session_find_by_name(struct l2tp_tunnel *tunnel, char *session_name)
{
struct usl_list_head *session_list = l2tp_tunnel_session_list(tunnel);
struct usl_list_head *tmp;
struct usl_list_head *walk;
struct l2tp_session *session;
usl_list_for_each(walk, tmp, session_list) {
session = usl_list_entry(walk, struct l2tp_session, session_list);
if ((session->config.session_name != NULL) && (strcmp(session->config.session_name, session_name) == 0)) {
return session;
}
}
return NULL;
}
static struct l2tp_session *l2tp_session_find(uint16_t tunnel_id, uint16_t session_id)
{
struct l2tp_tunnel *tunnel;
/* Find the tunnel context */
if (tunnel_id == 0) {
return NULL;
}
tunnel = l2tp_tunnel_find_by_id(tunnel_id);
if (tunnel == NULL) {
return NULL;
}
return l2tp_session_find_by_id(tunnel, session_id);
}
static uint16_t l2tp_session_allocate_id(struct l2tp_tunnel *tunnel)
{
uint16_t session_id;
int tries;
struct l2tp_session *session;
for (tries = 0; tries < 10; tries++) {
if (!l2tp_test_is_no_random_ids()) {
session_id = l2tp_make_random_id();
} else {
session_id = l2tp_test_alloc_session_id();
}
if (session_id == 0) {
continue;
}
session = l2tp_session_find_by_id(tunnel, session_id);
if (session == NULL) {
return session_id;
}
}
return 0;
}
/*****************************************************************************
* Session profiles
*****************************************************************************/
static struct l2tp_session_profile *l2tp_session_profile_find(const char *name)
{
struct usl_list_head *tmp;
struct usl_list_head *walk;
struct l2tp_session_profile *profile;
usl_list_for_each(walk, tmp, &l2tp_session_profile_list) {
profile = usl_list_entry(walk, struct l2tp_session_profile, list);
if (strcmp(&profile->profile_name[0], name) == 0) {
return profile;
}
}
return NULL;
}
/* For use by PPP and plugins to obtain session and ppp profile names
* being used for a given session.
*/
int l2tp_session_profile_names_get(uint16_t tunnel_id, uint16_t session_id, char **session_profile_name, char **ppp_profile_name)
{
char *default_session_profile_name;
char *default_ppp_profile_name;
int result = 0;
struct l2tp_session *session;
struct l2tp_tunnel *tunnel;
if ((tunnel_id == 0) || (session_id == 0)) {
result = -L2TP_ERR_SESSION_SPEC_MISSING;
goto out;
}
session = l2tp_session_find(tunnel_id, session_id);
if (session == NULL) {
result = -L2TP_ERR_SESSION_NOT_FOUND;
goto out;
}
tunnel = session->my_tunnel;
if (tunnel == NULL) {
tunnel = l2tp_tunnel_find_by_id(tunnel_id);
if (tunnel == NULL) {
result = -L2TP_ERR_TUNNEL_NOT_FOUND;
goto out;
}
}
l2tp_tunnel_get_profile_names(tunnel, NULL, &default_session_profile_name, &default_ppp_profile_name);
if (session_profile_name != NULL) {
*session_profile_name = session->config.profile_name ? session->config.profile_name : default_session_profile_name;
}
if (ppp_profile_name != NULL) {
*ppp_profile_name = session->config.ppp_profile_name ? session->config.ppp_profile_name : default_ppp_profile_name;
}
out:
return result;
}
/* When a message is received, it is decoded into an AVP array. We
* come here to store the AVPs in the session context.
*/
static int l2tp_session_store_avps(struct l2tp_session *session, struct l2tp_tunnel *tunnel,
struct l2tp_avp_desc *avps)
{
int result = 0;
int avp_len;
if (avps[L2TP_AVP_TYPE_BEARER_TYPE].value != NULL) {
session->peer.bearer_type.value = avps[L2TP_AVP_TYPE_BEARER_TYPE].value->bearer_type.value;
}
if (avps[L2TP_AVP_TYPE_FRAMING_TYPE].value != NULL) {
session->peer.framing_type.value = avps[L2TP_AVP_TYPE_FRAMING_TYPE].value->framing_type.value;
}
if (avps[L2TP_AVP_TYPE_PHYSICAL_CHANNEL_ID].value != NULL) {
session->peer.physical_channel_id.value = avps[L2TP_AVP_TYPE_BEARER_TYPE].value->physical_channel_id.value;
}
if (avps[L2TP_AVP_TYPE_CALLING_NUMBER].value != NULL) {
if (session->peer.calling_number != NULL) {
free(session->peer.calling_number);
}
session->peer.calling_number = (void *) strdup(avps[L2TP_AVP_TYPE_CALLING_NUMBER].value->calling_number.string);
if (session->peer.calling_number == NULL) {
goto out_nomem;
}
}
if (avps[L2TP_AVP_TYPE_CALLED_NUMBER].value != NULL) {
if (session->peer.called_number != NULL) {
free(session->peer.called_number);
}
session->peer.called_number = (void *) strdup(avps[L2TP_AVP_TYPE_CALLED_NUMBER].value->called_number.string);
if (session->peer.called_number == NULL) {
goto out_nomem;
}
}
if (avps[L2TP_AVP_TYPE_SUB_ADDRESS].value != NULL) {
if (session->peer.sub_address != NULL) {
free(session->peer.sub_address);
}
session->peer.sub_address = (void *) strdup(avps[L2TP_AVP_TYPE_SUB_ADDRESS].value->sub_address.string);
if (session->peer.sub_address == NULL) {
goto out_nomem;
}
}
if (avps[L2TP_AVP_TYPE_Q931_CAUSE_CODE].value != NULL) {
if (session->peer.q931_cause_code != NULL) {
free(session->peer.q931_cause_code);
}
avp_len = avps[L2TP_AVP_TYPE_Q931_CAUSE_CODE].value_len;
if (avp_len < 4) avp_len = 4;
session->peer.q931_cause_code = malloc(avp_len + 1);
memcpy(session->peer.q931_cause_code, avps[L2TP_AVP_TYPE_Q931_CAUSE_CODE].value, avp_len);
session->peer.q931_cause_code->advisory_msg[avp_len - 4] = '\0';
}
if (avps[L2TP_AVP_TYPE_CALL_SERIAL_NUMBER].value != NULL) {
session->peer.call_serial_number.value = avps[L2TP_AVP_TYPE_CALL_SERIAL_NUMBER].value->call_serial_number.value;
}
if (avps[L2TP_AVP_TYPE_MINIMUM_BPS].value != NULL) {
session->peer.minimum_bps.value = avps[L2TP_AVP_TYPE_MINIMUM_BPS].value->minimum_bps.value;
}
if (avps[L2TP_AVP_TYPE_MAXIMUM_BPS].value != NULL) {
session->peer.maximum_bps.value = avps[L2TP_AVP_TYPE_MAXIMUM_BPS].value->maximum_bps.value;
}
if (avps[L2TP_AVP_TYPE_CONNECT_SPEED].value != NULL) {
session->peer.connect_speed.value = avps[L2TP_AVP_TYPE_CONNECT_SPEED].value->connect_speed.value;
}
if (avps[L2TP_AVP_TYPE_RX_CONNECT_SPEED].value != NULL) {
session->peer.rx_connect_speed.value = avps[L2TP_AVP_TYPE_RX_CONNECT_SPEED].value->rx_connect_speed.value;
}
if (avps[L2TP_AVP_TYPE_PHYSICAL_CHANNEL_ID].value != NULL) {
session->peer.physical_channel_id.value = avps[L2TP_AVP_TYPE_PHYSICAL_CHANNEL_ID].value->physical_channel_id.value;
}
if (avps[L2TP_AVP_TYPE_PRIV_GROUP_ID].value != NULL) {
if (session->peer.priv_group_id != NULL) {
free(session->peer.priv_group_id);
}
avp_len = avps[L2TP_AVP_TYPE_PRIV_GROUP_ID].value_len;
session->peer.priv_group_id = malloc(avp_len + 1);
if (session->peer.priv_group_id == NULL) {
l2tp_stats.no_avp_resources++;
goto out_nomem;
}
memcpy(&session->peer.priv_group_id->string[0], avps[L2TP_AVP_TYPE_PRIV_GROUP_ID].value, avp_len);
session->peer.priv_group_id->string[avp_len] = '\0';
}
if (avps[L2TP_AVP_TYPE_SEQUENCING_REQUIRED].value != NULL) {
session->peer.sequencing_required = 1;
}
if (avps[L2TP_AVP_TYPE_INITIAL_RCVD_LCP_CONFREQ].value != NULL) {
if (session->peer.initial_rcvd_lcp_confreq != NULL) {
free(session->peer.initial_rcvd_lcp_confreq);
}
avp_len = avps[L2TP_AVP_TYPE_INITIAL_RCVD_LCP_CONFREQ].value_len;
session->peer.initial_rcvd_lcp_confreq = malloc(avp_len);
if (session->peer.initial_rcvd_lcp_confreq == NULL) {
l2tp_stats.no_avp_resources++;
goto out_nomem;
}
memcpy(&session->peer.initial_rcvd_lcp_confreq[0], avps[L2TP_AVP_TYPE_INITIAL_RCVD_LCP_CONFREQ].value, avp_len);
session->peer.initial_rcvd_lcp_confreq_len = avp_len;
}
if (avps[L2TP_AVP_TYPE_LAST_SENT_LCP_CONFREQ].value != NULL) {
if (session->peer.last_sent_lcp_confreq != NULL) {
free(session->peer.last_sent_lcp_confreq);
}
avp_len = avps[L2TP_AVP_TYPE_LAST_SENT_LCP_CONFREQ].value_len;
session->peer.last_sent_lcp_confreq = malloc(avp_len);
if (session->peer.last_sent_lcp_confreq == NULL) {
l2tp_stats.no_avp_resources++;
goto out_nomem;
}
memcpy(&session->peer.last_sent_lcp_confreq[0], avps[L2TP_AVP_TYPE_LAST_SENT_LCP_CONFREQ].value, avp_len);
session->peer.last_sent_lcp_confreq_len = avp_len;
}
if (avps[L2TP_AVP_TYPE_LAST_RCVD_LCP_CONFREQ].value != NULL) {
if (session->peer.last_rcvd_lcp_confreq != NULL) {
free(session->peer.last_rcvd_lcp_confreq);
}
avp_len = avps[L2TP_AVP_TYPE_LAST_RCVD_LCP_CONFREQ].value_len;
session->peer.last_rcvd_lcp_confreq = malloc(avp_len);
if (session->peer.last_rcvd_lcp_confreq == NULL) {
l2tp_stats.no_avp_resources++;
goto out_nomem;
}
memcpy(&session->peer.last_rcvd_lcp_confreq[0], avps[L2TP_AVP_TYPE_LAST_RCVD_LCP_CONFREQ].value, avp_len);
session->peer.last_rcvd_lcp_confreq_len = avp_len;
}
if (avps[L2TP_AVP_TYPE_PROXY_AUTH_TYPE].value != NULL) {
session->peer.proxy_auth_type.value = avps[L2TP_AVP_TYPE_PROXY_AUTH_TYPE].value->proxy_auth_type.value;
}
if (avps[L2TP_AVP_TYPE_PROXY_AUTH_NAME].value != NULL) {
if (session->peer.proxy_auth_name != NULL) {
free(session->peer.proxy_auth_name);
}
avp_len = avps[L2TP_AVP_TYPE_PROXY_AUTH_NAME].value_len;
session->peer.proxy_auth_name = malloc(avp_len + 1);
if (session->peer.proxy_auth_name == NULL) {
l2tp_stats.no_avp_resources++;
goto out_nomem;
}
memcpy(&session->peer.proxy_auth_name->string[0], avps[L2TP_AVP_TYPE_PROXY_AUTH_NAME].value, avp_len);
session->peer.proxy_auth_name->string[avp_len] = '\0';
}
if (avps[L2TP_AVP_TYPE_PROXY_AUTH_CHALLENGE].value != NULL) {
if (session->peer.proxy_auth_challenge != NULL) {
free(session->peer.proxy_auth_challenge);
}
avp_len = avps[L2TP_AVP_TYPE_PROXY_AUTH_CHALLENGE].value_len;
session->peer.proxy_auth_challenge = malloc(avp_len);
if (session->peer.proxy_auth_challenge == NULL) {
l2tp_stats.no_avp_resources++;
goto out_nomem;
}
memcpy(&session->peer.proxy_auth_challenge[0], avps[L2TP_AVP_TYPE_PROXY_AUTH_CHALLENGE].value, avp_len);
session->peer.proxy_auth_challenge_len = avp_len;
}
if (avps[L2TP_AVP_TYPE_PROXY_AUTH_ID].value != NULL) {
session->peer.proxy_auth_id.id = avps[L2TP_AVP_TYPE_PROXY_AUTH_ID].value->proxy_auth_id.id;
}
if (avps[L2TP_AVP_TYPE_PROXY_AUTH_RESPONSE].value != NULL) {
if (session->peer.proxy_auth_response != NULL) {
free(session->peer.proxy_auth_response);
}
avp_len = avps[L2TP_AVP_TYPE_PROXY_AUTH_RESPONSE].value_len;
session->peer.proxy_auth_response = malloc(avp_len);
if (session->peer.proxy_auth_response == NULL) {
l2tp_stats.no_avp_resources++;
goto out_nomem;
}
memcpy(&session->peer.proxy_auth_response[0], avps[L2TP_AVP_TYPE_PROXY_AUTH_RESPONSE].value, avp_len);
session->peer.proxy_auth_response_len = avp_len;
}
if (avps[L2TP_AVP_TYPE_CALL_ERRORS].value != NULL) {
if (session->peer.call_errors != NULL) {
free(session->peer.call_errors);
}
avp_len = avps[L2TP_AVP_TYPE_CALL_ERRORS].value_len;
if (avp_len < sizeof(struct l2tp_avp_call_errors)) {
goto out_badmsg;
}
session->peer.call_errors = calloc(1, sizeof(struct l2tp_avp_call_errors));
if (session->peer.call_errors == NULL) {
l2tp_stats.no_avp_resources++;
goto out_nomem;
}
session->peer.call_errors->crc_errors = avps[L2TP_AVP_TYPE_CALL_ERRORS].value->call_errors.crc_errors;
session->peer.call_errors->framing_errors = avps[L2TP_AVP_TYPE_CALL_ERRORS].value->call_errors.framing_errors;
session->peer.call_errors->hardware_overruns = avps[L2TP_AVP_TYPE_CALL_ERRORS].value->call_errors.hardware_overruns;
session->peer.call_errors->buffer_overruns = avps[L2TP_AVP_TYPE_CALL_ERRORS].value->call_errors.buffer_overruns;
session->peer.call_errors->timeout_errors = avps[L2TP_AVP_TYPE_CALL_ERRORS].value->call_errors.timeout_errors;
session->peer.call_errors->alignment_errors = avps[L2TP_AVP_TYPE_CALL_ERRORS].value->call_errors.alignment_errors;
}
if (avps[L2TP_AVP_TYPE_ACCM].value != NULL) {
if (session->peer.accm != NULL) {
free(session->peer.accm);
}
avp_len = avps[L2TP_AVP_TYPE_ACCM].value_len;
if (avp_len < sizeof(struct l2tp_avp_accm)) {
goto out_badmsg;
}
session->peer.accm = calloc(1, sizeof(struct l2tp_avp_accm));
if (session->peer.accm == NULL) {
l2tp_stats.no_avp_resources++;
goto out_nomem;
}
session->peer.accm->send_accm = avps[L2TP_AVP_TYPE_ACCM].value->accm.send_accm;
session->peer.accm->recv_accm = avps[L2TP_AVP_TYPE_ACCM].value->accm.recv_accm;
}
out:
return result;
out_nomem:
l2tp_stats.no_session_resources++;
result = -ENOMEM;
goto out;
out_badmsg:
l2tp_stats.bad_rcvd_frames++;
result = -EBADMSG;
goto out;
}
/* Timer callback to timeout session establishment.
*/
static void l2tp_session_establish_timer_expired(void *arg)