/****************************************************************************
**
** 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();
}
}
syntax highlighted by Code2HTML, v. 0.9.1