ff5c11fefd
Approved by: kib (mentor)
637 lines
15 KiB
C
637 lines
15 KiB
C
/*-
|
|
* Synchronous Frame Relay link level subroutines.
|
|
* ANSI T1.617-compaible link management signaling
|
|
* implemented for Frame Relay mode.
|
|
* Cisco-type Frame Relay framing added, thanks Alex Tutubalin.
|
|
* Only one DLCI per channel for now.
|
|
*
|
|
* Copyright (C) 1994-2000 Cronyx Engineering.
|
|
* Author: Serge Vakulenko, <vak@cronyx.ru>
|
|
*
|
|
* Copyright (C) 1999-2004 Cronyx Engineering.
|
|
* Author: Kurakin Roman, <rik@cronyx.ru>
|
|
*
|
|
* This software is distributed with NO WARRANTIES, not even the implied
|
|
* warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* Authors grant any other persons or organisations a permission to use,
|
|
* modify and redistribute this software in source and binary forms,
|
|
* as long as this message is kept with the software, all derivative
|
|
* works or modified versions.
|
|
*
|
|
* $Cronyx Id: if_spppfr.c,v 1.1.2.10 2004/06/29 09:02:30 rik Exp $
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
|
|
#if defined(__FreeBSD__)
|
|
#include "opt_inet.h"
|
|
#include "opt_inet6.h"
|
|
#include "opt_ipx.h"
|
|
#endif
|
|
|
|
#ifdef NetBSD1_3
|
|
# if NetBSD1_3 > 6
|
|
# include "opt_inet.h"
|
|
# include "opt_inet6.h"
|
|
# include "opt_iso.h"
|
|
# endif
|
|
#endif
|
|
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/module.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/syslog.h>
|
|
#if defined(__FreeBSD__)
|
|
#include <sys/random.h>
|
|
#endif
|
|
#include <sys/malloc.h>
|
|
#include <sys/mbuf.h>
|
|
|
|
#if defined (__OpenBSD__)
|
|
#include <sys/md5k.h>
|
|
#else
|
|
#include <sys/md5.h>
|
|
#endif
|
|
|
|
#include <net/if.h>
|
|
#include <net/netisr.h>
|
|
#include <net/if_types.h>
|
|
#include <net/route.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#include <net/slcompress.h>
|
|
|
|
#if defined (__NetBSD__) || defined (__OpenBSD__)
|
|
#include <machine/cpu.h> /* XXX for softnet */
|
|
#endif
|
|
|
|
#include <machine/stdarg.h>
|
|
|
|
#include <netinet/in_var.h>
|
|
#ifdef INET
|
|
#include <netinet/ip.h>
|
|
#include <netinet/tcp.h>
|
|
#endif
|
|
|
|
#if defined (__FreeBSD__) || defined (__OpenBSD__)
|
|
# include <netinet/if_ether.h>
|
|
#else
|
|
# include <net/ethertypes.h>
|
|
#endif
|
|
|
|
#ifdef IPX
|
|
#include <netipx/ipx.h>
|
|
#include <netipx/ipx_if.h>
|
|
#endif
|
|
|
|
#include <net/if_sppp.h>
|
|
|
|
/*
|
|
* Frame Relay.
|
|
*/
|
|
#define FR_UI 0x03 /* Unnumbered Information */
|
|
#define FR_IP 0xCC /* IP protocol identifier */
|
|
#define FR_PADDING 0x00 /* NLPID padding */
|
|
#define FR_SIGNALING 0x08 /* Q.933/T1.617 signaling identifier */
|
|
#define FR_SNAP 0x80 /* NLPID snap */
|
|
|
|
/*
|
|
* Header flags.
|
|
*/
|
|
#define FR_DE 0x02 /* discard eligibility */
|
|
#define FR_FECN 0x04 /* forward notification */
|
|
#define FR_BECN 0x08 /* backward notification */
|
|
|
|
/*
|
|
* Signaling message types.
|
|
*/
|
|
#define FR_MSG_ENQUIRY 0x75 /* status enquiry */
|
|
#define FR_MSG_STATUS 0x7d /* status */
|
|
|
|
#define FR_ENQUIRY_SIZE 14
|
|
|
|
/*
|
|
* Message field types.
|
|
*/
|
|
#define FR_FLD_RTYPE 0x01 /* report type */
|
|
#define FR_FLD_VERIFY 0x03 /* link verification */
|
|
#define FR_FLD_PVC 0x07 /* PVC status */
|
|
#define FR_FLD_LSHIFT5 0x95 /* locking shift 5 */
|
|
|
|
/*
|
|
* Report types.
|
|
*/
|
|
#define FR_RTYPE_FULL 0 /* full status */
|
|
#define FR_RTYPE_SHORT 1 /* link verification only */
|
|
#define FR_RTYPE_SINGLE 2 /* single PVC status */
|
|
|
|
/* PVC status field. */
|
|
#define FR_DLCI_DELETE 0x04 /* PVC is deleted */
|
|
#define FR_DLCI_ACTIVE 0x02 /* PVC is operational */
|
|
#define FR_DLCI_NEW 0x08 /* PVC is new */
|
|
|
|
struct arp_req {
|
|
unsigned short htype; /* hardware type = ARPHRD_FRELAY */
|
|
unsigned short ptype; /* protocol type = ETHERTYPE_IP */
|
|
unsigned char halen; /* hardware address length = 2 */
|
|
unsigned char palen; /* protocol address length = 4 */
|
|
unsigned short op; /* ARP/RARP/InARP request/reply */
|
|
unsigned short hsource; /* hardware source address */
|
|
unsigned short psource1; /* protocol source */
|
|
unsigned short psource2;
|
|
unsigned short htarget; /* hardware target address */
|
|
unsigned short ptarget1; /* protocol target */
|
|
unsigned short ptarget2;
|
|
} __packed;
|
|
|
|
#if defined(__FreeBSD__) && __FreeBSD_version < 501113
|
|
#define SPP_FMT "%s%d: "
|
|
#define SPP_ARGS(ifp) (ifp)->if_name, (ifp)->if_unit
|
|
#else
|
|
#define SPP_FMT "%s: "
|
|
#define SPP_ARGS(ifp) (ifp)->if_xname
|
|
#endif
|
|
|
|
/* almost every function needs these */
|
|
#define STDDCL \
|
|
struct ifnet *ifp = SP2IFP(sp); \
|
|
int debug = ifp->if_flags & IFF_DEBUG
|
|
|
|
static void sppp_fr_arp (struct sppp *sp, struct arp_req *req, u_short addr);
|
|
static void sppp_fr_signal (struct sppp *sp, unsigned char *h, int len);
|
|
|
|
void sppp_fr_input (struct sppp *sp, struct mbuf *m)
|
|
{
|
|
STDDCL;
|
|
u_char *h = mtod (m, u_char*);
|
|
int isr = -1;
|
|
int dlci, hlen, proto;
|
|
|
|
/* Get the DLCI number. */
|
|
if (m->m_pkthdr.len < 10) {
|
|
bad: m_freem (m);
|
|
return;
|
|
}
|
|
dlci = (h[0] << 2 & 0x3f0) | (h[1] >> 4 & 0x0f);
|
|
|
|
/* Process signaling packets. */
|
|
if (dlci == 0) {
|
|
sppp_fr_signal (sp, h, m->m_pkthdr.len);
|
|
m_freem (m);
|
|
return;
|
|
}
|
|
|
|
if (dlci != sp->fr_dlci) {
|
|
if (debug)
|
|
printf (SPP_FMT "Received packet from invalid DLCI %d\n",
|
|
SPP_ARGS(ifp), dlci);
|
|
goto bad;
|
|
}
|
|
|
|
/* Process the packet. */
|
|
if (ntohs (*(short*) (h+2)) == ETHERTYPE_IP) {
|
|
/* Prehistoric IP framing? */
|
|
h[2] = FR_UI;
|
|
h[3] = FR_IP;
|
|
}
|
|
if (h[2] != FR_UI) {
|
|
if (debug)
|
|
printf (SPP_FMT "Invalid frame relay header flag 0x%02x\n",
|
|
SPP_ARGS(ifp), h[2]);
|
|
goto bad;
|
|
}
|
|
switch (h[3]) {
|
|
default:
|
|
if (debug)
|
|
printf (SPP_FMT "Unsupported NLPID 0x%02x\n",
|
|
SPP_ARGS(ifp), h[3]);
|
|
goto bad;
|
|
|
|
case FR_PADDING:
|
|
if (h[4] != FR_SNAP) {
|
|
if (debug)
|
|
printf (SPP_FMT "Bad NLPID 0x%02x\n",
|
|
SPP_ARGS(ifp), h[4]);
|
|
goto bad;
|
|
}
|
|
if (h[5] || h[6] || h[7]) {
|
|
if (debug)
|
|
printf (SPP_FMT "Bad OID 0x%02x-0x%02x-0x%02x\n",
|
|
SPP_ARGS(ifp),
|
|
h[5], h[6], h[7]);
|
|
goto bad;
|
|
}
|
|
proto = ntohs (*(short*) (h+8));
|
|
if (proto == ETHERTYPE_ARP) {
|
|
/* Process the ARP request. */
|
|
if (m->m_pkthdr.len != 10 + sizeof (struct arp_req)) {
|
|
if (debug)
|
|
printf (SPP_FMT "Bad ARP request size = %d bytes\n",
|
|
SPP_ARGS(ifp),
|
|
m->m_pkthdr.len);
|
|
goto bad;
|
|
}
|
|
sppp_fr_arp (sp, (struct arp_req*) (h + 10),
|
|
h[0] << 8 | h[1]);
|
|
m_freem (m);
|
|
return;
|
|
}
|
|
hlen = 10;
|
|
break;
|
|
|
|
case FR_IP:
|
|
proto = ETHERTYPE_IP;
|
|
hlen = 4;
|
|
break;
|
|
}
|
|
|
|
/* Remove frame relay header. */
|
|
m_adj (m, hlen);
|
|
|
|
switch (proto) {
|
|
default:
|
|
++ifp->if_noproto;
|
|
drop: ++ifp->if_ierrors;
|
|
++ifp->if_iqdrops;
|
|
m_freem (m);
|
|
return;
|
|
#ifdef INET
|
|
case ETHERTYPE_IP:
|
|
isr = NETISR_IP;
|
|
break;
|
|
#endif
|
|
#ifdef IPX
|
|
case ETHERTYPE_IPX:
|
|
isr = NETISR_IPX;
|
|
break;
|
|
#endif
|
|
#ifdef NETATALK
|
|
case ETHERTYPE_AT:
|
|
isr = NETISR_ATALK;
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
if (! (ifp->if_flags & IFF_UP))
|
|
goto drop;
|
|
|
|
M_SETFIB(m, ifp->if_fib);
|
|
|
|
/* Check queue. */
|
|
if (netisr_queue(isr, m)) { /* (0) on success. */
|
|
if (debug)
|
|
log(LOG_DEBUG, SPP_FMT "protocol queue overflow\n",
|
|
SPP_ARGS(ifp));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add the frame relay header to the packet.
|
|
* For IP the header length is 4 bytes,
|
|
* for all other protocols - 10 bytes (RFC 1490).
|
|
*/
|
|
struct mbuf *sppp_fr_header (struct sppp *sp, struct mbuf *m,
|
|
int family)
|
|
{
|
|
STDDCL;
|
|
u_char *h;
|
|
int type, hlen;
|
|
|
|
/* Prepend the space for Frame Relay header. */
|
|
hlen = (family == AF_INET) ? 4 : 10;
|
|
M_PREPEND (m, hlen, M_DONTWAIT);
|
|
if (! m)
|
|
return 0;
|
|
h = mtod (m, u_char*);
|
|
|
|
/* Fill the header. */
|
|
h[0] = sp->fr_dlci >> 2 & 0xfc;
|
|
h[1] = sp->fr_dlci << 4 | 1;
|
|
h[2] = FR_UI;
|
|
|
|
switch (family) {
|
|
default:
|
|
if (debug)
|
|
printf (SPP_FMT "Cannot handle address family %d\n",
|
|
SPP_ARGS(ifp), family);
|
|
m_freem (m);
|
|
return 0;
|
|
#ifdef INET
|
|
case AF_INET:
|
|
#if 0 /* Crashes on fragmented packets */
|
|
/*
|
|
* Set the discard eligibility bit, if:
|
|
* 1) no fragmentation
|
|
* 2) length > 400 bytes
|
|
* 3a) the protocol is UDP or
|
|
* 3b) TCP data (no control bits)
|
|
*/
|
|
{
|
|
struct ip *ip = (struct ip*) (h + hlen);
|
|
struct tcphdr *tcp = (struct tcphdr*) ((long*)ip + ip->ip_hl);
|
|
|
|
if (! (ip->ip_off & ~IP_DF) && ip->ip_len > 400 &&
|
|
(ip->ip_p == IPPROTO_UDP ||
|
|
ip->ip_p == IPPROTO_TCP && ! tcp->th_flags))
|
|
h[1] |= FR_DE;
|
|
}
|
|
#endif
|
|
h[3] = FR_IP;
|
|
return m;
|
|
#endif
|
|
#ifdef IPX
|
|
case AF_IPX:
|
|
type = ETHERTYPE_IPX;
|
|
break;
|
|
#endif
|
|
#ifdef NS
|
|
case AF_NS:
|
|
type = 0x8137;
|
|
break;
|
|
#endif
|
|
#ifdef NETATALK
|
|
case AF_APPLETALK:
|
|
type = ETHERTYPE_AT;
|
|
break;
|
|
#endif
|
|
}
|
|
h[3] = FR_PADDING;
|
|
h[4] = FR_SNAP;
|
|
h[5] = 0;
|
|
h[6] = 0;
|
|
h[7] = 0;
|
|
*(short*) (h+8) = htons(type);
|
|
return m;
|
|
}
|
|
|
|
/*
|
|
* Send periodical frame relay link verification messages via DLCI 0.
|
|
* Called every 10 seconds (default value of T391 timer is 10 sec).
|
|
* Every 6-th message is a full status request
|
|
* (default value of N391 counter is 6).
|
|
*/
|
|
void sppp_fr_keepalive (struct sppp *sp)
|
|
{
|
|
STDDCL;
|
|
unsigned char *h, *p;
|
|
struct mbuf *m;
|
|
|
|
MGETHDR (m, M_DONTWAIT, MT_DATA);
|
|
if (! m)
|
|
return;
|
|
m->m_pkthdr.rcvif = 0;
|
|
|
|
h = mtod (m, u_char*);
|
|
p = h;
|
|
*p++ = 0; /* DLCI = 0 */
|
|
*p++ = 1;
|
|
*p++ = FR_UI;
|
|
*p++ = FR_SIGNALING; /* NLPID = UNI call control */
|
|
|
|
*p++ = 0; /* call reference length = 0 */
|
|
*p++ = FR_MSG_ENQUIRY; /* message type = status enquiry */
|
|
|
|
*p++ = FR_FLD_LSHIFT5; /* locking shift 5 */
|
|
|
|
*p++ = FR_FLD_RTYPE; /* report type field */
|
|
*p++ = 1; /* report type length = 1 */
|
|
if (sp->pp_seq[IDX_LCP] % 6)
|
|
*p++ = FR_RTYPE_SHORT; /* link verification only */
|
|
else
|
|
*p++ = FR_RTYPE_FULL; /* full status needed */
|
|
|
|
if (sp->pp_seq[IDX_LCP] >= 255)
|
|
sp->pp_seq[IDX_LCP] = 0;
|
|
*p++ = FR_FLD_VERIFY; /* link verification type field */
|
|
*p++ = 2; /* link verification field length = 2 */
|
|
*p++ = ++sp->pp_seq[IDX_LCP]; /* our sequence number */
|
|
*p++ = sp->pp_rseq[IDX_LCP]; /* last received sequence number */
|
|
|
|
m->m_pkthdr.len = m->m_len = p - h;
|
|
if (debug)
|
|
printf (SPP_FMT "send lmi packet, seq=%d, rseq=%d\n",
|
|
SPP_ARGS(ifp), (u_char) sp->pp_seq[IDX_LCP],
|
|
(u_char) sp->pp_rseq[IDX_LCP]);
|
|
|
|
if (! IF_HANDOFF_ADJ(&sp->pp_cpq, m, ifp, 3))
|
|
++ifp->if_oerrors;
|
|
}
|
|
|
|
/*
|
|
* Process the frame relay Inverse ARP request.
|
|
*/
|
|
static void sppp_fr_arp (struct sppp *sp, struct arp_req *req,
|
|
u_short his_hardware_address)
|
|
{
|
|
STDDCL;
|
|
struct mbuf *m;
|
|
struct arp_req *reply;
|
|
u_char *h;
|
|
u_short my_hardware_address;
|
|
u_long his_ip_address, my_ip_address;
|
|
|
|
if ((ntohs (req->htype) != ARPHRD_FRELAY ||
|
|
ntohs (req->htype) != 16) || /* for BayNetworks routers */
|
|
ntohs (req->ptype) != ETHERTYPE_IP) {
|
|
if (debug)
|
|
printf (SPP_FMT "Invalid ARP hardware/protocol type = 0x%x/0x%x\n",
|
|
SPP_ARGS(ifp),
|
|
ntohs (req->htype), ntohs (req->ptype));
|
|
return;
|
|
}
|
|
if (req->halen != 2 || req->palen != 4) {
|
|
if (debug)
|
|
printf (SPP_FMT "Invalid ARP hardware/protocol address length = %d/%d\n",
|
|
SPP_ARGS(ifp),
|
|
req->halen, req->palen);
|
|
return;
|
|
}
|
|
switch (ntohs (req->op)) {
|
|
default:
|
|
if (debug)
|
|
printf (SPP_FMT "Invalid ARP op = 0x%x\n",
|
|
SPP_ARGS(ifp), ntohs (req->op));
|
|
return;
|
|
|
|
case ARPOP_INVREPLY:
|
|
/* Ignore. */
|
|
return;
|
|
|
|
case ARPOP_INVREQUEST:
|
|
my_hardware_address = ntohs (req->htarget);
|
|
his_ip_address = ntohs (req->psource1) << 16 |
|
|
ntohs (req->psource2);
|
|
my_ip_address = ntohs (req->ptarget1) << 16 |
|
|
ntohs (req->ptarget2);
|
|
break;
|
|
}
|
|
if (debug)
|
|
printf (SPP_FMT "got ARP request, source=0x%04x/%d.%d.%d.%d, target=0x%04x/%d.%d.%d.%d\n",
|
|
SPP_ARGS(ifp), ntohs (req->hsource),
|
|
(unsigned char) (his_ip_address >> 24),
|
|
(unsigned char) (his_ip_address >> 16),
|
|
(unsigned char) (his_ip_address >> 8),
|
|
(unsigned char) his_ip_address,
|
|
my_hardware_address,
|
|
(unsigned char) (my_ip_address >> 24),
|
|
(unsigned char) (my_ip_address >> 16),
|
|
(unsigned char) (my_ip_address >> 8),
|
|
(unsigned char) my_ip_address);
|
|
|
|
sppp_get_ip_addrs (sp, &my_ip_address, 0, 0);
|
|
if (! my_ip_address)
|
|
return; /* nothing to reply */
|
|
|
|
if (debug)
|
|
printf (SPP_FMT "send ARP reply, source=0x%04x/%d.%d.%d.%d, target=0x%04x/%d.%d.%d.%d\n",
|
|
SPP_ARGS(ifp), my_hardware_address,
|
|
(unsigned char) (my_ip_address >> 24),
|
|
(unsigned char) (my_ip_address >> 16),
|
|
(unsigned char) (my_ip_address >> 8),
|
|
(unsigned char) my_ip_address,
|
|
his_hardware_address,
|
|
(unsigned char) (his_ip_address >> 24),
|
|
(unsigned char) (his_ip_address >> 16),
|
|
(unsigned char) (his_ip_address >> 8),
|
|
(unsigned char) his_ip_address);
|
|
|
|
/* Send the Inverse ARP reply. */
|
|
MGETHDR (m, M_DONTWAIT, MT_DATA);
|
|
if (! m)
|
|
return;
|
|
m->m_pkthdr.len = m->m_len = 10 + sizeof (*reply);
|
|
m->m_pkthdr.rcvif = 0;
|
|
|
|
h = mtod (m, u_char*);
|
|
reply = (struct arp_req*) (h + 10);
|
|
|
|
h[0] = his_hardware_address >> 8;
|
|
h[1] = his_hardware_address;
|
|
h[2] = FR_UI;
|
|
h[3] = FR_PADDING;
|
|
h[4] = FR_SNAP;
|
|
h[5] = 0;
|
|
h[6] = 0;
|
|
h[7] = 0;
|
|
*(short*) (h+8) = htons (ETHERTYPE_ARP);
|
|
|
|
reply->htype = htons (ARPHRD_FRELAY);
|
|
reply->ptype = htons (ETHERTYPE_IP);
|
|
reply->halen = 2;
|
|
reply->palen = 4;
|
|
reply->op = htons (ARPOP_INVREPLY);
|
|
reply->hsource = htons (my_hardware_address);
|
|
reply->psource1 = htonl (my_ip_address);
|
|
reply->psource2 = htonl (my_ip_address) >> 16;
|
|
reply->htarget = htons (his_hardware_address);
|
|
reply->ptarget1 = htonl (his_ip_address);
|
|
reply->ptarget2 = htonl (his_ip_address) >> 16;
|
|
|
|
if (! IF_HANDOFF_ADJ(&sp->pp_cpq, m, ifp, 3))
|
|
++ifp->if_oerrors;
|
|
}
|
|
|
|
/*
|
|
* Process the input signaling packet (DLCI 0).
|
|
* The implemented protocol is ANSI T1.617 Annex D.
|
|
*/
|
|
static void sppp_fr_signal (struct sppp *sp, unsigned char *h, int len)
|
|
{
|
|
STDDCL;
|
|
u_char *p;
|
|
int dlci;
|
|
|
|
if (h[2] != FR_UI || h[3] != FR_SIGNALING || h[4] != 0) {
|
|
if (debug)
|
|
printf (SPP_FMT "Invalid signaling header\n",
|
|
SPP_ARGS(ifp));
|
|
bad: if (debug) {
|
|
printf ("%02x", *h++);
|
|
while (--len > 0)
|
|
printf ("-%02x", *h++);
|
|
printf ("\n");
|
|
}
|
|
return;
|
|
}
|
|
if (h[5] == FR_MSG_ENQUIRY) {
|
|
if (len == FR_ENQUIRY_SIZE &&
|
|
h[12] == (u_char) sp->pp_seq[IDX_LCP]) {
|
|
sp->pp_seq[IDX_LCP] = random();
|
|
printf (SPP_FMT "loopback detected\n",
|
|
SPP_ARGS(ifp));
|
|
}
|
|
return;
|
|
}
|
|
if (h[5] != FR_MSG_STATUS) {
|
|
if (debug)
|
|
printf (SPP_FMT "Unknown signaling message: 0x%02x\n",
|
|
SPP_ARGS(ifp), h[5]);
|
|
goto bad;
|
|
}
|
|
|
|
/* Parse message fields. */
|
|
for (p=h+6; p<h+len; ) {
|
|
switch (*p) {
|
|
default:
|
|
if (debug)
|
|
printf (SPP_FMT "Unknown signaling field 0x%x\n",
|
|
SPP_ARGS(ifp), *p);
|
|
break;
|
|
case FR_FLD_LSHIFT5:
|
|
case FR_FLD_RTYPE:
|
|
/* Ignore. */
|
|
break;
|
|
case FR_FLD_VERIFY:
|
|
if (p[1] != 2) {
|
|
if (debug)
|
|
printf (SPP_FMT "Invalid signaling verify field length %d\n",
|
|
SPP_ARGS(ifp), p[1]);
|
|
break;
|
|
}
|
|
sp->pp_rseq[IDX_LCP] = p[2];
|
|
if (debug) {
|
|
printf (SPP_FMT "got lmi reply rseq=%d, seq=%d",
|
|
SPP_ARGS(ifp), p[2], p[3]);
|
|
if (p[3] != (u_char) sp->pp_seq[IDX_LCP])
|
|
printf (" (really %d)",
|
|
(u_char) sp->pp_seq[IDX_LCP]);
|
|
printf ("\n");
|
|
}
|
|
break;
|
|
case FR_FLD_PVC:
|
|
if (p[1] < 3) {
|
|
if (debug)
|
|
printf (SPP_FMT "Invalid PVC status length %d\n",
|
|
SPP_ARGS(ifp), p[1]);
|
|
break;
|
|
}
|
|
dlci = (p[2] << 4 & 0x3f0) | (p[3] >> 3 & 0x0f);
|
|
if (! sp->fr_dlci)
|
|
sp->fr_dlci = dlci;
|
|
if (sp->fr_status != p[4])
|
|
printf (SPP_FMT "DLCI %d %s%s\n",
|
|
SPP_ARGS(ifp), dlci,
|
|
p[4] & FR_DLCI_DELETE ? "deleted" :
|
|
p[4] & FR_DLCI_ACTIVE ? "active" : "passive",
|
|
p[4] & FR_DLCI_NEW ? ", new" : "");
|
|
sp->fr_status = p[4];
|
|
break;
|
|
}
|
|
if (*p & 0x80)
|
|
++p;
|
|
else if (p < h+len+1 && p[1])
|
|
p += 2 + p[1];
|
|
else {
|
|
if (debug)
|
|
printf (SPP_FMT "Invalid signaling field 0x%x\n",
|
|
SPP_ARGS(ifp), *p);
|
|
goto bad;
|
|
}
|
|
}
|
|
}
|