Changelog for beta 9013V1.1w
[tomato.git] / release / src / router / rp-l2tp / session.c
blobe88a5680fddbb88f39865c00d5c046eb1818f7a9
1 /***********************************************************************
3 * session.c
5 * Code for managing L2TP sessions
7 * Copyright (C) 2001 by Roaring Penguin Software Inc.
9 * This software may be distributed under the terms of the GNU General
10 * Public License, Version 2, or (at your option) any later version.
12 * LIC: GPL
14 ***********************************************************************/
16 static char const RCSID[] =
17 "$Id: session.c,v 1.1.48.1 2005/08/08 12:05:25 honor Exp $";
19 #include "l2tp.h"
20 #include <stddef.h>
21 #include <string.h>
22 #include <stdarg.h>
23 #include <stdio.h>
25 static uint16_t session_make_sid(l2tp_tunnel *tunnel);
26 static void session_set_state(l2tp_session *session, int state);
27 static void session_send_sli(l2tp_session *session);
29 /* Registered LNS incoming-call handlers */
30 static l2tp_lns_handler *lns_handlers = NULL;
32 /* Registered LAC handlers */
33 static l2tp_lac_handler *lac_handlers = NULL;
35 static uint32_t call_serial_number = 0;
37 static char *state_names[] = {
38 "idle", "wait-tunnel", "wait-reply", "wait-connect", "established"
41 /**********************************************************************
42 * %FUNCTION: session_compute_hash
43 * %ARGUMENTS:
44 * data -- a session
45 * %RETURNS:
46 * The session ID
47 * %DESCRIPTION:
48 * Returns a hash value for a session
49 ***********************************************************************/
50 static unsigned int
51 session_compute_hash(void *data)
53 return (unsigned int) ((l2tp_session *) data)->my_id;
57 /**********************************************************************
58 * %FUNCTION: session_compare
59 * %ARGUMENTS:
60 * d1, d2 -- two sessions
61 * %RETURNS:
62 * 0 if sids are equal, non-zero otherwise
63 * %DESCRIPTION:
64 * Compares two sessions
65 ***********************************************************************/
66 static int
67 session_compare(void *d1, void *d2)
69 return ((l2tp_session *) d1)->my_id != ((l2tp_session *) d2)->my_id;
72 /**********************************************************************
73 * %FUNCTION: session_hash_init
74 * %ARGUMENTS:
75 * tab -- hash table
76 * %RETURNS:
77 * Nothing
78 * %DESCRIPTION:
79 * Initializes hash table of sessions
80 ***********************************************************************/
81 void
82 l2tp_session_hash_init(hash_table *tab)
84 hash_init(tab, offsetof(l2tp_session, hash_by_my_id),
85 session_compute_hash, session_compare);
88 /**********************************************************************
89 * %FUNCTION: session_free
90 * %ARGUMENTS:
91 * ses -- a session to free
92 * %RETURNS:
93 * Nothing
94 * %DESCRIPTION:
95 * Frees a session, closing down all resources associated with it.
96 ***********************************************************************/
97 void
98 l2tp_session_free(l2tp_session *ses, char const *reason, int may_reestablish)
100 session_set_state(ses, SESSION_IDLE);
101 DBG(l2tp_db(DBG_SESSION, "session_free(%s) %s\n",
102 l2tp_debug_session_to_str(ses), reason));
103 if (ses->call_ops && ses->call_ops->close) {
104 ses->call_ops->close(ses, reason, may_reestablish);
106 memset(ses, 0, sizeof(l2tp_session));
107 free(ses);
110 /**********************************************************************
111 * %FUNCTION: session_call_lns
112 * %ARGUMENTS:
113 * peer -- L2TP peer
114 * calling_number -- calling phone number (or MAC address or whatever...)
115 * private -- private data to be stored in session structure
116 * %RETURNS:
117 * A newly-allocated session, or NULL if session could not be created
118 * %DESCRIPTION:
119 * Initiates session setup. Once call is active, established() will be
120 * called.
121 ***********************************************************************/
122 l2tp_session *
123 l2tp_session_call_lns(l2tp_peer *peer,
124 char const *calling_number,
125 EventSelector *es,
126 void *private)
128 l2tp_session *ses;
129 l2tp_tunnel *tunnel;
131 /* Find a tunnel to the peer */
132 tunnel = l2tp_tunnel_find_for_peer(peer, es);
133 if (!tunnel) return NULL;
135 /* Do we have call ops? */
136 if (!peer->lac_ops) {
137 l2tp_set_errmsg("Cannot act as LAC for peer");
138 return NULL;
141 /* Allocate session */
142 ses = malloc(sizeof(l2tp_session));
143 if (!ses) {
144 l2tp_set_errmsg("session_call_lns: out of memory");
145 return NULL;
148 /* Init fields */
149 memset(ses, 0, sizeof(l2tp_session));
150 ses->we_are_lac = 1;
151 ses->tunnel = tunnel;
152 ses->my_id = session_make_sid(tunnel);
153 ses->call_ops = peer->lac_ops;
154 ses->state = SESSION_WAIT_TUNNEL;
155 strncpy(ses->calling_number, calling_number, MAX_HOSTNAME);
156 ses->calling_number[MAX_HOSTNAME-1] = 0;
157 ses->private = private;
158 ses->snooping = 1;
159 ses->send_accm = 0xFFFFFFFF;
160 ses->recv_accm = 0xFFFFFFFF;
162 /* Add it to the tunnel */
163 l2tp_tunnel_add_session(ses);
165 return ses;
168 /**********************************************************************
169 * %FUNCTION: session_make_sid
170 * %ARGUMENTS:
171 * tunnel -- an L2TP tunnel
172 * %RETURNS:
173 * An unused random session ID
174 ***********************************************************************/
175 static uint16_t
176 session_make_sid(l2tp_tunnel *tunnel)
178 uint16_t sid;
179 while(1) {
180 L2TP_RANDOM_FILL(sid);
181 if (!sid) continue;
182 if (!l2tp_tunnel_find_session(tunnel, sid)) return sid;
186 /**********************************************************************
187 * %FUNCTION: session_notify_open
188 * %ARGUMENTS:
189 * ses -- an L2TP session
190 * %RETURNS:
191 * Nothing
192 * %DESCRIPTION:
193 * Called when tunnel has been established
194 ***********************************************************************/
195 void
196 l2tp_session_notify_tunnel_open(l2tp_session *ses)
198 uint16_t u16;
199 uint32_t u32;
200 l2tp_dgram *dgram;
201 l2tp_tunnel *tunnel = ses->tunnel;
203 if (ses->state != SESSION_WAIT_TUNNEL) return;
205 /* Send ICRQ */
206 DBG(l2tp_db(DBG_SESSION, "Session %s tunnel open\n",
207 l2tp_debug_session_to_str(ses)));
209 dgram = l2tp_dgram_new_control(MESSAGE_ICRQ, ses->tunnel->assigned_id,
211 if (!dgram) {
212 l2tp_set_errmsg("Could not establish session - out of memory");
213 l2tp_tunnel_delete_session(ses, "Out of memory", 0);
214 return;
217 /* assigned session ID */
218 u16 = htons(ses->my_id);
219 l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
220 sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_SESSION_ID, &u16);
222 /* Call serial number */
223 u32 = htonl(call_serial_number);
224 call_serial_number++;
225 l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
226 sizeof(u32), VENDOR_IETF, AVP_CALL_SERIAL_NUMBER, &u32);
228 /* Ship it out */
229 l2tp_tunnel_xmit_control_message(tunnel, dgram);
231 session_set_state(ses, SESSION_WAIT_REPLY);
234 /**********************************************************************
235 * %FUNCTION: session_set_state
236 * %ARGUMENTS:
237 * session -- the session
238 * state -- new state
239 * %RETURNS:
240 * Nothing
241 ***********************************************************************/
242 static void
243 session_set_state(l2tp_session *session, int state)
245 if (state == session->state) return;
246 DBG(l2tp_db(DBG_SESSION, "session(%s) state %s -> %s\n",
247 l2tp_debug_session_to_str(session),
248 state_names[session->state],
249 state_names[state]));
250 session->state = state;
253 /**********************************************************************
254 * %FUNCTION: session_register_lns_handler
255 * %ARGUMENTS:
256 * handler -- LNS handler
257 * %RETURNS:
258 * -1 on error, 0 if all is OK
259 * %DESCRIPTION:
260 * Registers an LNS handler for incoming call requests
261 ***********************************************************************/
263 l2tp_session_register_lns_handler(l2tp_lns_handler *handler)
265 l2tp_lns_handler *prev = lns_handlers;
267 if (l2tp_session_find_lns_handler(handler->handler_name)) {
268 l2tp_set_errmsg("LNS Handler named %s already exists",
269 handler->handler_name);
270 return -1;
272 /* Add to end of handler list */
273 handler->next = NULL;
274 if (!prev) {
275 lns_handlers = handler;
276 return 0;
278 while (prev->next) {
279 prev = prev->next;
281 prev->next = handler;
282 return 0;
285 /**********************************************************************
286 * %FUNCTION: session_register_lac_handler
287 * %ARGUMENTS:
288 * handler -- LAC handler
289 * %RETURNS:
290 * -1 on error, 0 if all is OK
291 * %DESCRIPTION:
292 * Registers an LAC handler for incoming call requests
293 ***********************************************************************/
295 l2tp_session_register_lac_handler(l2tp_lac_handler *handler)
297 l2tp_lac_handler *prev = lac_handlers;
299 if (l2tp_session_find_lac_handler(handler->handler_name)) {
300 l2tp_set_errmsg("LAC Handler named %s already exists",
301 handler->handler_name);
302 return -1;
304 /* Add to end of handler list */
305 handler->next = NULL;
306 if (!prev) {
307 lac_handlers = handler;
308 return 0;
310 while (prev->next) {
311 prev = prev->next;
313 prev->next = handler;
314 return 0;
317 /**********************************************************************
318 * %FUNCTION: session_send_CDN
319 * %ARGUMENTS:
320 * ses -- which session to terminate
321 * result_code -- result code
322 * error_code -- error code
323 * fmt -- printf-style format string for error message
324 * %RETURNS:
325 * Nothing
326 * %DESCRIPTION:
327 * Sends CDN with specified result code and message.
328 ***********************************************************************/
329 void
330 l2tp_session_send_CDN(l2tp_session *ses,
331 int result_code,
332 int error_code,
333 char const *fmt, ...)
335 char buf[256];
336 va_list ap;
337 l2tp_tunnel *tunnel = ses->tunnel;
338 uint16_t len;
339 l2tp_dgram *dgram;
340 uint16_t u16;
342 /* Build the buffer for the result-code AVP */
343 buf[0] = result_code / 256;
344 buf[1] = result_code & 255;
345 buf[2] = error_code / 256;
346 buf[3] = error_code & 255;
348 va_start(ap, fmt);
349 vsnprintf(buf+4, 256-4, fmt, ap);
350 buf[255] = 0;
351 va_end(ap);
353 DBG(l2tp_db(DBG_SESSION, "session_send_CDN(%s): %s\n",
354 l2tp_debug_session_to_str(ses), buf+4));
356 len = 4 + strlen(buf+4);
357 /* Build the datagram */
358 dgram = l2tp_dgram_new_control(MESSAGE_CDN, tunnel->assigned_id,
359 ses->assigned_id);
360 if (!dgram) return;
362 /* Add assigned session ID */
363 u16 = htons(ses->my_id);
364 l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
365 sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_SESSION_ID, &u16);
367 /* Add result code */
368 l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
369 len, VENDOR_IETF, AVP_RESULT_CODE, buf);
371 /* TODO: Clean up */
372 session_set_state(ses, SESSION_IDLE);
374 /* Ship it out */
375 l2tp_tunnel_xmit_control_message(tunnel, dgram);
377 /* Free session */
378 l2tp_tunnel_delete_session(ses, buf+4, 0);
381 /**********************************************************************
382 * %FUNCTION: session_lns_handle_incoming_call
383 * %ARGUMENTS:
384 * tunnel -- tunnel on which ICRQ arrived
385 * sid -- assigned session ID
386 * dgram -- the ICRQ datagram
387 * %RETURNS:
388 * Nothing
389 * %DESCRIPTION:
390 * Handles ICRQ. If we find an LNS handler willing to take the call,
391 * send ICRP. Otherwise, send CDN.
392 ***********************************************************************/
393 void
394 l2tp_session_lns_handle_incoming_call(l2tp_tunnel *tunnel,
395 uint16_t sid,
396 l2tp_dgram *dgram,
397 char const *calling_number)
399 l2tp_call_ops *ops = tunnel->peer->lns_ops;
400 l2tp_session *ses;
401 uint16_t u16;
403 /* Allocate a session */
404 ses = malloc(sizeof(l2tp_session));
405 if (!ses) {
406 l2tp_set_errmsg("session_lns_handle_incoming_call: out of memory");
407 return;
409 /* Init fields */
410 memset(ses, 0, sizeof(l2tp_session));
411 ses->we_are_lac = 0;
412 ses->tunnel = tunnel;
413 ses->my_id = session_make_sid(tunnel);
414 ses->assigned_id = sid;
415 ses->state = SESSION_IDLE;
416 strncpy(ses->calling_number, calling_number, MAX_HOSTNAME);
417 ses->calling_number[MAX_HOSTNAME-1] = 0;
418 ses->private = NULL;
419 ses->snooping = 1;
420 ses->send_accm = 0xFFFFFFFF;
421 ses->recv_accm = 0xFFFFFFFF;
423 l2tp_tunnel_add_session(ses);
425 if (!ops || !ops->establish) {
426 l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR,
427 ERROR_OUT_OF_RESOURCES,
428 "No LNS handler willing to accept call");
429 return;
432 ses->call_ops = ops;
434 /* Send ICRP */
435 dgram = l2tp_dgram_new_control(MESSAGE_ICRP, ses->tunnel->assigned_id,
436 ses->assigned_id);
437 if (!dgram) {
438 /* Ugh... not much chance of this working... */
439 l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR, ERROR_OUT_OF_RESOURCES,
440 "Out of memory");
441 return;
444 /* Add assigned session ID */
445 u16 = htons(ses->my_id);
446 l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
447 sizeof(u16), VENDOR_IETF, AVP_ASSIGNED_SESSION_ID, &u16);
449 /* Set session state */
450 session_set_state(ses, SESSION_WAIT_CONNECT);
452 /* Ship ICRP */
453 l2tp_tunnel_xmit_control_message(tunnel, dgram);
456 /**********************************************************************
457 * %FUNCTION: session_handle_CDN
458 * %ARGUMENTS:
459 * ses -- the session
460 * dgram -- the datagram
461 * %RETURNS:
462 * Nothing
463 * %DESCRIPTION:
464 * Handles a CDN by destroying session
465 ***********************************************************************/
466 void
467 l2tp_session_handle_CDN(l2tp_session *ses,
468 l2tp_dgram *dgram)
470 char buf[1024];
471 unsigned char *val;
472 uint16_t len;
473 val = l2tp_dgram_search_avp(dgram, ses->tunnel, NULL, NULL, &len,
474 VENDOR_IETF, AVP_RESULT_CODE);
475 if (!val || len < 4) {
476 l2tp_tunnel_delete_session(ses, "Received CDN", 1);
477 } else {
478 uint16_t result_code, error_code;
479 char *msg;
480 result_code = ((uint16_t) val[0]) * 256 + (uint16_t) val[1];
481 error_code = ((uint16_t) val[2]) * 256 + (uint16_t) val[3];
482 if (len > 4) {
483 msg = (char *) &val[4];
484 } else {
485 msg = "";
487 snprintf(buf, sizeof(buf), "Received CDN: result-code = %d, error-code = %d, message = '%.*s'", result_code, error_code, (int) len-4, msg);
488 buf[1023] = 0;
489 l2tp_tunnel_delete_session(ses, buf, 1);
493 /**********************************************************************
494 * %FUNCTION: session_handle_ICRP
495 * %ARGUMENTS:
496 * ses -- the session
497 * dgram -- the datagram
498 * %RETURNS:
499 * Nothing
500 * %DESCRIPTION:
501 * Handles an ICRP
502 ***********************************************************************/
503 void
504 l2tp_session_handle_ICRP(l2tp_session *ses,
505 l2tp_dgram *dgram)
507 uint16_t u16;
508 unsigned char *val;
509 uint16_t len;
510 uint32_t u32;
512 int mandatory, hidden;
513 l2tp_tunnel *tunnel = ses->tunnel;
515 /* Get assigned session ID */
516 val = l2tp_dgram_search_avp(dgram, tunnel, &mandatory, &hidden, &len,
517 VENDOR_IETF, AVP_ASSIGNED_SESSION_ID);
518 if (!val) {
519 l2tp_set_errmsg("No assigned session-ID in ICRP");
520 return;
522 if (!l2tp_dgram_validate_avp(VENDOR_IETF, AVP_ASSIGNED_SESSION_ID,
523 len, mandatory)) {
524 l2tp_set_errmsg("Invalid assigned session-ID in ICRP");
525 return;
528 /* Set assigned session ID */
529 u16 = ((uint16_t) val[0]) * 256 + (uint16_t) val[1];
531 if (!u16) {
532 l2tp_set_errmsg("Invalid assigned session-ID in ICRP");
533 return;
536 ses->assigned_id = u16;
538 /* If state is not WAIT_REPLY, fubar */
539 if (ses->state != SESSION_WAIT_REPLY) {
540 l2tp_session_send_CDN(ses, RESULT_FSM_ERROR, 0, "Received ICRP for session in state %s", state_names[ses->state]);
541 return;
544 /* Tell PPP code that call has been established */
545 if (ses->call_ops && ses->call_ops->establish) {
546 if (ses->call_ops->establish(ses) < 0) {
547 l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR, ERROR_VENDOR_SPECIFIC,
548 "%s", l2tp_get_errmsg());
549 return;
553 /* Send ICCN */
554 dgram = l2tp_dgram_new_control(MESSAGE_ICCN, tunnel->assigned_id,
555 ses->assigned_id);
556 if (!dgram) {
557 /* Ugh... not much chance of this working... */
558 l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR, ERROR_OUT_OF_RESOURCES,
559 "Out of memory");
560 return;
563 /* TODO: Speed, etc. are faked for now. */
565 /* Connect speed */
566 u32 = htonl(57600);
567 l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
568 sizeof(u32), VENDOR_IETF, AVP_TX_CONNECT_SPEED, &u32);
570 /* Framing Type */
571 u32 = htonl(1);
572 l2tp_dgram_add_avp(dgram, tunnel, MANDATORY,
573 sizeof(u32), VENDOR_IETF, AVP_FRAMING_TYPE, &u32);
575 /* Ship it out */
576 l2tp_tunnel_xmit_control_message(tunnel, dgram);
578 /* Set session state */
579 session_set_state(ses, SESSION_ESTABLISHED);
580 ses->tunnel->peer->fail = 0;
584 /**********************************************************************
585 * %FUNCTION: session_handle_ICCN
586 * %ARGUMENTS:
587 * ses -- the session
588 * dgram -- the datagram
589 * %RETURNS:
590 * Nothing
591 * %DESCRIPTION:
592 * Handles an ICCN
593 ***********************************************************************/
594 void
595 l2tp_session_handle_ICCN(l2tp_session *ses,
596 l2tp_dgram *dgram)
598 unsigned char *val;
599 int mandatory, hidden;
600 uint16_t len, vendor, type;
601 int err = 0;
603 l2tp_tunnel *tunnel = ses->tunnel;
605 /* If state is not WAIT_CONNECT, fubar */
606 if (ses->state != SESSION_WAIT_CONNECT) {
607 l2tp_session_send_CDN(ses, RESULT_FSM_ERROR, 0,
608 "Received ICCN for session in state %s",
609 state_names[ses->state]);
610 return;
613 /* Set session state */
614 session_set_state(ses, SESSION_ESTABLISHED);
615 ses->tunnel->peer->fail = 0;
617 /* Pull out and examine AVP's */
618 while(1) {
619 val = l2tp_dgram_pull_avp(dgram, tunnel, &mandatory, &hidden,
620 &len, &vendor, &type, &err);
621 if (!val) {
622 if (err) {
623 l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR,
624 ERROR_BAD_VALUE, "%s", l2tp_get_errmsg());
625 return;
627 break;
629 if (vendor != VENDOR_IETF) {
630 if (!mandatory) continue;
631 l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR,
632 ERROR_UNKNOWN_AVP_WITH_M_BIT,
633 "Unknown mandatory attribute (vendor=%d, type=%d) in %s",
634 (int) vendor, (int) type,
635 l2tp_debug_avp_type_to_str(dgram->msg_type));
636 return;
638 switch(type) {
639 case AVP_SEQUENCING_REQUIRED:
640 ses->sequencing_required = 1;
641 break;
646 /* Start the call */
647 if (ses->call_ops->establish(ses) < 0) {
648 l2tp_session_send_CDN(ses, RESULT_GENERAL_ERROR,
649 ERROR_OUT_OF_RESOURCES,
650 "No LNS handler willing to accept call");
651 return;
656 /**********************************************************************
657 * %FUNCTION: session_find_lns_handler
658 * %ARGUMENTS:
659 * name -- name of handler
660 * %RETURNS:
661 * Pointer to the handler if found, NULL if not
662 * %DESCRIPTION:
663 * Searches for an LNS handler by name
664 ***********************************************************************/
665 l2tp_lns_handler *
666 l2tp_session_find_lns_handler(char const *name)
668 l2tp_lns_handler *cur = lns_handlers;
669 while(cur) {
670 if (!strcmp(name, cur->handler_name)) return cur;
671 cur = cur->next;
673 return NULL;
676 /**********************************************************************
677 * %FUNCTION: session_find_lac_handler
678 * %ARGUMENTS:
679 * name -- name of handler
680 * %RETURNS:
681 * Pointer to the handler if found, NULL if not
682 * %DESCRIPTION:
683 * Searches for an LAC handler by name
684 ***********************************************************************/
685 l2tp_lac_handler *
686 l2tp_session_find_lac_handler(char const *name)
688 l2tp_lac_handler *cur = lac_handlers;
689 while(cur) {
690 if (!strcmp(name, cur->handler_name)) return cur;
691 cur = cur->next;
693 return NULL;
696 /**********************************************************************
697 * %FUNCTION: l2tp_session_state_name
698 * %ARGUMENTS:
699 * ses -- the session
700 * %RETURNS:
701 * The name of the session's state
702 ***********************************************************************/
703 char const *
704 l2tp_session_state_name(l2tp_session *ses)
706 return state_names[ses->state];
709 /**********************************************************************
710 * %FUNCTION: l2tp_session_lcp_snoop
711 * %ARGUMENTS:
712 * ses -- L2TP session structure
713 * buf -- PPP frame
714 * len -- length of PPP frame
715 * incoming -- if 1, frame is coming from L2TP tunnel. If 0, frame is
716 * going to L2TP tunnel.
717 * %RETURNS:
718 * Nothing
719 * %DESCRIPTION:
720 * Snoops on LCP negotiation. Used to send SLI to LAC if we're an LNS.
721 ***********************************************************************/
722 void
723 l2tp_session_lcp_snoop(l2tp_session *ses,
724 unsigned char const *buf,
725 int len,
726 int incoming)
728 unsigned int protocol;
729 int stated_len;
730 int opt, opt_len;
731 int reject;
732 unsigned char const *opt_data;
733 uint32_t accm;
735 /* If we are LAC, do not snoop */
736 if (ses->we_are_lac) {
737 DBG(l2tp_db(DBG_SNOOP, "Turning off snooping: We are LAC.\n"));
738 ses->snooping = 0;
739 return;
742 /* Get protocol */
743 if (buf[0] & 0x01) {
744 /* Compressed protcol field */
745 protocol = buf[0];
746 } else {
747 protocol = ((unsigned int) buf[0]) * 256 + buf[1];
750 /* If it's a network protocol, stop snooping */
751 if (protocol <= 0x3fff) {
752 DBG(l2tp_db(DBG_SNOOP, "Turning off snooping: Network protocol %04x found.\n", protocol));
753 ses->snooping = 0;
754 return;
757 /* If it's not LCP, do not snoop */
758 if (protocol != 0xc021) {
759 return;
762 /* Skip protocol; go to packet data */
763 buf += 2;
764 len -= 2;
766 /* Unreasonably short frame?? */
767 if (len <= 0) return;
769 /* Look for Configure-Ack or Configure-Reject code */
770 if (buf[0] != 2 && buf[0] != 4) return;
772 reject = (buf[0] == 4);
774 stated_len = ((unsigned int) buf[2]) * 256 + buf[3];
776 /* Something fishy with length field? */
777 if (stated_len > len) return;
779 /* Skip to options */
780 len = stated_len - 4;
781 buf += 4;
783 while (len > 0) {
784 /* Pull off an option */
785 opt = buf[0];
786 opt_len = buf[1];
787 opt_data = &buf[2];
788 if (opt_len > len || opt_len < 2) break;
789 len -= opt_len;
790 buf += opt_len;
791 DBG(l2tp_db(DBG_SNOOP, "Found option type %02x; len %d\n",
792 opt, opt_len));
793 /* We are specifically interested in ACCM */
794 if (opt == 0x02 && opt_len == 0x06) {
795 if (reject) {
796 /* ACCM negotiation REJECTED; use default */
797 accm = 0xFFFFFFFF;
798 DBG(l2tp_db(DBG_SNOOP, "Rejected ACCM negotiation; defaulting (%s)\n", incoming ? "incoming" : "outgoing"));
799 /* ??? Is this right? */
800 ses->recv_accm = accm;
801 ses->send_accm = accm;
802 ses->got_recv_accm = 1;
803 ses->got_send_accm = 1;
804 } else {
805 memcpy(&accm, opt_data, sizeof(accm));
806 DBG(l2tp_db(DBG_SNOOP, "Found ACCM of %08x (%s)\n", accm, incoming ? "incoming" : "outgoing"));
807 if (incoming) {
808 ses->recv_accm = accm;
809 ses->got_recv_accm = 1;
810 } else {
811 ses->send_accm = accm;
812 ses->got_send_accm = 1;
816 if (ses->got_recv_accm && ses->got_send_accm) {
817 DBG(l2tp_db(DBG_SNOOP, "Sending SLI: Send ACCM = %08x; Receive ACCM = %08x\n", ses->send_accm, ses->recv_accm));
818 session_send_sli(ses);
819 ses->got_recv_accm = 0;
820 ses->got_send_accm = 0;
825 /**********************************************************************
826 * %FUNCTION: session_send_sli
827 * %ARGUMENTS:
828 * ses -- the session
829 * %RETURNS:
830 * Nothing
831 * %DESCRIPTION:
832 * Sends an SLI message with send/receive ACCM's.
833 ***********************************************************************/
834 void
835 session_send_sli(l2tp_session *ses)
837 l2tp_dgram *dgram;
839 unsigned char buf[10];
840 memset(buf, 0, 2);
841 memcpy(buf+2, &ses->send_accm, 4);
842 memcpy(buf+6, &ses->recv_accm, 4);
844 dgram = l2tp_dgram_new_control(MESSAGE_SLI, ses->tunnel->assigned_id,
845 ses->assigned_id);
846 if (!dgram) return;
848 /* Add ACCM */
849 l2tp_dgram_add_avp(dgram, ses->tunnel, MANDATORY,
850 sizeof(buf), VENDOR_IETF, AVP_ACCM, buf);
852 /* Ship it out */
853 l2tp_tunnel_xmit_control_message(ses->tunnel, dgram);
854 ses->sent_sli = 1;