/**************************************************************************** ** ** File: l2tp.c ** ** Author: Mike Borella ** ** Comments: Dump L2TP header information ** ** $Id: l2tp.c,v 1.10 2001/10/12 21:45:02 mborella Exp $ ** ** 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 Library 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. ** ****************************************************************************/ #include "l2tp.h" #include "ppp.h" #define L2TP_AVP_SIZE 6 /* * Control message types */ #define L2TP_CTRL_RESERVED1 0 #define L2TP_CTRL_SCCRQ 1 #define L2TP_CTRL_SCCRP 2 #define L2TP_CTRL_SCCCN 3 #define L2TP_CTRL_STOPCCN 4 #define L2TP_CTRL_RESERVED2 5 #define L2TP_CTRL_HELLO 6 #define L2TP_CTRL_OCRQ 7 #define L2TP_CTRL_OCRP 8 #define L2TP_CTRL_OCCN 9 #define L2TP_CTRL_ICRQ 10 #define L2TP_CTRL_ICRP 11 #define L2TP_CTRL_ICCN 12 #define L2TP_CTRL_RESERVED3 13 #define L2TP_CTRL_CDN 14 #define L2TP_CTRL_WEN 15 #define L2TP_CTRL_SLI 16 /* * L2tp control message type map */ strmap_t l2tp_ctrl_map[] = { { L2TP_CTRL_RESERVED1, "Reserved" }, { L2TP_CTRL_SCCRQ, "Start-Control-Connection-Request" }, { L2TP_CTRL_SCCRP, "Start-Control-Connection-Reply" }, { L2TP_CTRL_SCCCN, "Stop-Control-Connection-Connected" }, { L2TP_CTRL_STOPCCN, "Stop-Control-Connection-Notification" }, { L2TP_CTRL_RESERVED2, "Reserved" }, { L2TP_CTRL_HELLO, "Hello" }, { L2TP_CTRL_OCRQ, "Outgoing-Call-Request" }, { L2TP_CTRL_OCRP, "Outgoing-Call-Reply" }, { L2TP_CTRL_OCCN, "Outgoing-Call-Connected" }, { L2TP_CTRL_ICRQ, "Incoming-Call-Request" }, { L2TP_CTRL_ICRP, "Incoming-Call-Reply" }, { L2TP_CTRL_ICCN, "Incoming-Call-Connected" }, { L2TP_CTRL_RESERVED3, "Reserved" }, { L2TP_CTRL_CDN, "Call-Disconnect-Notify" }, { L2TP_CTRL_WEN, "WAN-Error-Notify" }, { L2TP_CTRL_SLI, "Set-Link-Info" }, { 0, "" } }; /* * L2TP AVPs */ #define L2TP_AVP_MESSAGETYPE 0 #define L2TP_AVP_RESULTCODE 1 #define L2TP_AVP_PROTOCOLVERSION 2 #define L2TP_AVP_FRAMINGCAP 3 #define L2TP_AVP_BEARERCAP 4 #define L2TP_AVP_TIEBREAKER 5 #define L2TP_AVP_FIRMWAREREV 6 #define L2TP_AVP_HOSTNAME 7 #define L2TP_AVP_VENDORNAME 8 #define L2TP_AVP_ASSIGNEDTUNNELID 9 #define L2TP_AVP_RECEIVEWINDOWSIZE 10 #define L2TP_AVP_CHALLENGE 11 #define L2TP_AVP_Q931CAUSECODE 12 #define L2TP_AVP_RESPONSE 13 #define L2TP_AVP_ASSIGNEDSESSIONID 14 #define L2TP_AVP_CALLSERIALNUMBER 15 #define L2TP_AVP_MINIMUMBPS 16 #define L2TP_AVP_MAXIMUMBPS 17 #define L2TP_AVP_BEARERTYPE 18 #define L2TP_AVP_FRAMINGTYPE 19 #define L2TP_AVP_RESERVED1 20 #define L2TP_AVP_CALLEDNUMBER 21 #define L2TP_AVP_CALLINGNUMBER 22 #define L2TP_AVP_SUBADDRESS 23 #define L2TP_AVP_TXCONNECTSPEED 24 #define L2TP_AVP_PHYSICALCHANNELID 25 #define L2TP_AVP_INITIALRECVLCP 26 #define L2TP_AVP_LASTSENTLCP 27 #define L2TP_AVP_LASTRECVLCP 28 #define L2TP_AVP_PROXYAUTHTYPE 29 #define L2TP_AVP_PROXYAUTHNAME 30 #define L2TP_AVP_PROXYAUTHCHALLENGE 31 #define L2TP_AVP_PROXYAUTHID 32 #define L2TP_AVP_PROXYAUTHRESPONSE 33 #define L2TP_AVP_CALLERRORS 34 #define L2TP_AVP_ACCM 35 #define L2TP_AVP_RANDOMVECTOR 36 #define L2TP_AVP_PRIVATEGROUP 37 #define L2TP_AVP_RXCONNECTSPEED 38 #define L2TP_AVP_SEQUENCINGREQUIRED 39 /* * L2TP AVP map */ strmap_t l2tp_avp_map[] = { { L2TP_AVP_MESSAGETYPE, "message type" }, { L2TP_AVP_RESULTCODE, "result code" }, { L2TP_AVP_PROTOCOLVERSION, "protocol version" }, { L2TP_AVP_FRAMINGCAP, "framing capabilities" }, { L2TP_AVP_BEARERCAP, "bearer capabilities" }, { L2TP_AVP_TIEBREAKER, "tie breaker" }, { L2TP_AVP_FIRMWAREREV, "firmware revision" }, { L2TP_AVP_HOSTNAME, "host name" }, { L2TP_AVP_VENDORNAME, "vendor name" }, { L2TP_AVP_ASSIGNEDTUNNELID, "assigned tunnel ID" }, { L2TP_AVP_RECEIVEWINDOWSIZE, "receive window size" }, { L2TP_AVP_CHALLENGE, "challenge" }, { L2TP_AVP_Q931CAUSECODE, "Q.931 cause code" }, { L2TP_AVP_RESPONSE, "response" }, { L2TP_AVP_ASSIGNEDSESSIONID, "assigned session ID" }, { L2TP_AVP_CALLSERIALNUMBER, "call serial number" }, { L2TP_AVP_MINIMUMBPS, "minimum BPS" }, { L2TP_AVP_MAXIMUMBPS, "maximum BPS" }, { L2TP_AVP_BEARERTYPE, "bearer type" }, { L2TP_AVP_FRAMINGTYPE, "framing type" }, { L2TP_AVP_RESERVED1, "reserved" }, { L2TP_AVP_CALLEDNUMBER, "called number" }, { L2TP_AVP_CALLINGNUMBER, "calling number" }, { L2TP_AVP_SUBADDRESS, "subaddress" }, { L2TP_AVP_TXCONNECTSPEED, "transmit connect speed" }, { L2TP_AVP_PHYSICALCHANNELID, "physical channel ID" }, { L2TP_AVP_INITIALRECVLCP, "initial receive LCP" }, { L2TP_AVP_LASTSENTLCP, "last sent LCP" }, { L2TP_AVP_LASTRECVLCP, "last receive LCP" }, { L2TP_AVP_PROXYAUTHTYPE, "proxy authentication type" }, { L2TP_AVP_PROXYAUTHNAME, "proxy authentication name" }, { L2TP_AVP_PROXYAUTHCHALLENGE,"proxy authentication challenge" }, { L2TP_AVP_PROXYAUTHID, "proxy authentication ID" }, { L2TP_AVP_PROXYAUTHRESPONSE, "proxy authentication response" }, { L2TP_AVP_CALLERRORS, "call errors" }, { L2TP_AVP_ACCM, "ACCM" }, { L2TP_AVP_RANDOMVECTOR, "random vector" }, { L2TP_AVP_PRIVATEGROUP, "private group" }, { L2TP_AVP_RXCONNECTSPEED, "receive connect speed" }, { L2TP_AVP_SEQUENCINGREQUIRED,"sequencing required" }, { 0, "" } }; /* * Message type AVP (generic part) * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |M|H| rsvd | Length | Vendor ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Attribute Type | Attribute Value... * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * [until Length is reached]... | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * */ typedef struct l2tpavp { #if defined(WORDS_BIGENDIAN) u_int8_t m:1, h:1, zeros:4, length_high:2; #else u_int16_t length_high:2, zeros:4, h:1, m:1; #endif u_int8_t length_low; u_int16_t vendor_id; u_int16_t attribute_type; } l2tpavp_t; /* * First two bytes of l2tp header * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |T|L|x|x|S|x|O|P|x|x|x|x| Ver | Length (opt) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Tunnel ID | Session ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Ns (opt) | Nr (opt) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Offset Size (opt) | Offset pad... (opt) * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * */ typedef struct l2tphdr { #if defined(WORDS_BIGENDIAN) u_int8_t t_bit:1, l_bit:1, reserved1:2, s_bit:1, reserved2:1, o_bit:1, p_bit:1; #else u_int8_t p_bit:1, o_bit:1, reserved2:1, s_bit:1, reserved1:2, l_bit:1, t_bit:1; #endif #if defined(WORD_BIGENDIAN) u_int8_t reserved3:4, version:4; #else u_int8_t version:4, reserved3:4; #endif } l2tphdr_t; extern struct arg_t * my_args; /*---------------------------------------------------------------------------- ** ** dump_l2tp_avp() ** ** Parse L2TP AVP subheader ** ** Returns a -1 on error so that we can fall out of the calling loop. ** **---------------------------------------------------------------------------- */ int dump_l2tp_avp(packet_t * pkt, u_int16_t *n) { l2tpavp_t avp; int8_t m, h, zeros; int16_t length; /* * Get the avp */ if (get_packet_bytes((u_int8_t *) &avp, pkt, L2TP_AVP_SIZE) == 0) return -1; *n = *n + L2TP_AVP_SIZE; /* * conversions */ m = avp.m; h = avp.h; zeros = avp.zeros; length = avp.length_high * 256 + avp.length_low; avp.vendor_id = ntohs(avp.vendor_id); avp.attribute_type = ntohs(avp.attribute_type); /* * display */ if (my_args->m) { } else { display_strmap("AVP type", avp.attribute_type, l2tp_avp_map); display(" M (mandatory)", (u_int8_t *) &m, 1, DISP_DEC); display(" H (hidden)", (u_int8_t *) &h, 1, DISP_DEC); display(" Reserved", (u_int8_t *) &zeros, 1, DISP_DEC); display(" Length", (u_int8_t *) &length, 2, DISP_DEC); display(" Vendor ID", (u_int8_t *) &avp.vendor_id, 2, DISP_DEC); } /* * Now we can deal with the attribute, the length of which is the * given length - 6 */ if (length > 6) { u_int8_t * attribute; int real_length = length - 6; attribute = (u_int8_t *) my_malloc(real_length + 1); if (get_packet_bytes((u_int8_t *) attribute, pkt, real_length) == 0) { my_free(attribute); return -1; } *n = *n + real_length; if (my_args->m) { switch(avp.attribute_type) { case L2TP_AVP_MESSAGETYPE: { u_int16_t attr; memcpy((void *) &attr, (void *) attribute, 2); attr = ntohs(attr); display_minimal_string(map2str(l2tp_ctrl_map, attr)); display_minimal_string(" "); } break; default: break; } } else { switch(avp.attribute_type) { case L2TP_AVP_MESSAGETYPE: { u_int16_t attr; memcpy((void *) &attr, (void *) attribute, 2); attr = ntohs(attr); display_strmap(" Message type", attr, l2tp_ctrl_map); } break; case L2TP_AVP_HOSTNAME: case L2TP_AVP_VENDORNAME: /* all of these attributes are text strings */ attribute[real_length] = '\0'; display_string(" Name", attribute); break; case L2TP_AVP_ASSIGNEDTUNNELID: case L2TP_AVP_ASSIGNEDSESSIONID: case L2TP_AVP_RECEIVEWINDOWSIZE: case L2TP_AVP_FIRMWAREREV: case L2TP_AVP_PROXYAUTHTYPE: { /* all of these attributes are 2 bytes */ u_int16_t value; memcpy((void *) &value, (void *) attribute, 2); value = ntohs(value); display(" Value", (u_int8_t *) &value, 2, DISP_DEC); } break; case L2TP_AVP_CALLSERIALNUMBER: case L2TP_AVP_MINIMUMBPS: case L2TP_AVP_MAXIMUMBPS: case L2TP_AVP_TXCONNECTSPEED: case L2TP_AVP_PHYSICALCHANNELID: { /* all of these attributes are 4 bytes */ u_int32_t value; memcpy((void *) &value, (void *) attribute, 4); value = ntohl(value); display(" Value", (u_int8_t *) &value, 4, DISP_DEC); } break; case L2TP_AVP_PROTOCOLVERSION: { u_int8_t ver; u_int8_t rev; ver = attribute[0]; rev = attribute[1]; display(" Version", (u_int8_t *) &ver, 1, DISP_DEC); display(" Revision", (u_int8_t *) &rev, 1, DISP_DEC); } break; default: /* display as hex for now */ display(" Attribute", (u_int8_t *) attribute, real_length, DISP_HEX_MULTILINE); break; } } /* free memory */ my_free(attribute); } return 0; } /*---------------------------------------------------------------------------- ** ** dump_l2tp() ** ** Parse L2TP packet and dump fields ** **---------------------------------------------------------------------------- */ void dump_l2tp(packet_t *pkt) { l2tphdr_t hdr; u_int8_t t, l, r1, s, r2, o, p, r3, v; u_int16_t tunnel_id; u_int16_t session_id; u_int16_t bytes_read = 0; u_int16_t length; /* Set the layer */ set_layer(LAYER_APPLICATION); /* * Read the first byte, determine what to do next */ if (get_packet_bytes((u_int8_t *) &hdr, pkt, 2) == 0) return; bytes_read += 2; /* * conversions */ t = hdr.t_bit; l = hdr.l_bit; r1 = hdr.reserved1; s = hdr.s_bit; r2 = hdr.reserved2; o = hdr.o_bit; p = hdr.p_bit; r3 = hdr.reserved3; v = hdr.version; /* * Display the first byte */ if (my_args->m) { if (t) display_minimal_string("| L2TP control "); else display_minimal_string("| L2TP data "); } else { /* announcement */ if (t) display_header_banner("L2TP control"); else display_header_banner("L2TP data"); display("T (type)", (u_int8_t *) &t, 1, DISP_DEC); display("L (length present)", (u_int8_t *) &l, 1, DISP_DEC); display("Reserved", (u_int8_t *) &r1, 1, DISP_DEC); display("S (sequence present)", (u_int8_t *) &s, 1, DISP_DEC); display("Reserved", (u_int8_t *) &r2, 1, DISP_DEC); display("O (offset present)", (u_int8_t *) &o, 1, DISP_DEC); display("P (priority)", (u_int8_t *) &p, 1, DISP_DEC); display("Reserved", (u_int8_t *) &r3, 1, DISP_DEC); display("Version", (u_int8_t *) &v, 1, DISP_DEC); } /* * If the length bit is set, get the length field and display it */ if (l) { /* get it */ if (get_packet_bytes((u_int8_t *) &length, pkt, 2) == 0) return; bytes_read += 2; /* convert */ length = ntohs(length); if (!my_args->m) display("Length", (u_int8_t *) &length, 2, DISP_DEC); } /* * Get the tunnel ID and session ID fields */ if (get_packet_bytes((u_int8_t *) &tunnel_id, pkt, 2) == 0) return; if (get_packet_bytes((u_int8_t *) &session_id, pkt, 2) == 0) return; bytes_read += 4; /* convert */ tunnel_id = ntohs(tunnel_id); session_id = ntohs(session_id); /* display */ if (my_args->m) { display_minimal_string("tunnel "); display_minimal((u_int8_t *) &tunnel_id, 2, DISP_DEC); display_minimal_string(" "); display_minimal_string("session "); display_minimal((u_int8_t *) &session_id, 2, DISP_DEC); display_minimal_string(" "); } else { display("Tunnel ID", (u_int8_t *) &tunnel_id, 2, DISP_DEC); display("Session ID", (u_int8_t *) &session_id, 2, DISP_DEC); } /* * If the sequence bit is set, get the seqno fields and display them */ if (s) { u_int16_t ns; u_int16_t nr; /* get them */ if (get_packet_bytes((u_int8_t *) &ns, pkt, 2) == 0) return; if (get_packet_bytes((u_int8_t *) &nr, pkt, 2) == 0) return; bytes_read += 4; /* convert */ ns = ntohs(ns); nr = ntohs(nr); /* display */ if (!my_args->m) { display("Seqno (Ns)", (u_int8_t *) &ns, 2, DISP_DEC); display("Received seqno (Nr)", (u_int8_t *) &nr, 2, DISP_DEC); } } /* * If the offset bit is set, get the offset and pad */ if (o) { u_int16_t offset; u_int8_t * pad; /* get the offset */ if (get_packet_bytes((u_int8_t *) &offset, pkt, 2) == 0) return; bytes_read += 2; /* convert */ offset = ntohs(offset); /* display */ if (!my_args->m) display("Offset", (u_int8_t *) &offset, 2, DISP_DEC); /* allocate memory for pad */ pad = (u_int8_t *) my_malloc(offset+1); /* get the pad */ if (get_packet_bytes((u_int8_t *) &pad, pkt, offset) == 0) { my_free(pad); return; } bytes_read += offset; /* display the offset in hex */ if (!my_args->m) display("Offset pad", pad, offset, DISP_HEX_MULTILINE); /* Free memory for the offset */ my_free(pad); } /* * For control messages, get the avp's. For data messages, parse the * PPP header that should be on top of the L2TP header... */ if (t) { /* grab all of the avp's, one by one */ while(bytes_read < length) if (dump_l2tp_avp(pkt, &bytes_read) == -1) break; /* dump the hex buffer */ hexbuffer_flush(); } else { dump_ppp(pkt); /* dump the hex buffer */ hexbuffer_flush(); } }