Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-davinci
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
linux
linux-davinci
Commits
e022c2f0
Commit
e022c2f0
authored
Aug 14, 2008
by
Krzysztof Hałasa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
WAN: new synchronous PPP implementation for generic HDLC.
Signed-off-by:
Krzysztof Hałasa
<
khc@pm.waw.pl
>
parent
e1f024eb
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
606 additions
and
52 deletions
+606
-52
Documentation/networking/generic-hdlc.txt
Documentation/networking/generic-hdlc.txt
+4
-4
drivers/net/wan/Makefile
drivers/net/wan/Makefile
+1
-1
drivers/net/wan/hdlc_ppp.c
drivers/net/wan/hdlc_ppp.c
+601
-47
No files found.
Documentation/networking/generic-hdlc.txt
View file @
e022c2f0
...
...
@@ -3,15 +3,15 @@ Krzysztof Halasa <khc@pm.waw.pl>
Generic HDLC layer currently supports:
1. Frame Relay (ANSI, CCITT, Cisco and no LMI)
.
1. Frame Relay (ANSI, CCITT, Cisco and no LMI)
- Normal (routed) and Ethernet-bridged (Ethernet device emulation)
interfaces can share a single PVC.
- ARP support (no InARP support in the kernel - there is an
experimental InARP user-space daemon available on:
http://www.kernel.org/pub/linux/utils/net/hdlc/).
2. raw HDLC - either IP (IPv4) interface or Ethernet device emulation
.
3. Cisco HDLC
.
4. PPP
(uses syncppp.c).
2. raw HDLC - either IP (IPv4) interface or Ethernet device emulation
3. Cisco HDLC
4. PPP
5. X.25 (uses X.25 routines).
Generic HDLC is a protocol driver only - it needs a low-level driver
...
...
drivers/net/wan/Makefile
View file @
e022c2f0
...
...
@@ -14,7 +14,7 @@ obj-$(CONFIG_HDLC_RAW) += hdlc_raw.o
obj-$(CONFIG_HDLC_RAW_ETH)
+=
hdlc_raw_eth.o
obj-$(CONFIG_HDLC_CISCO)
+=
hdlc_cisco.o
obj-$(CONFIG_HDLC_FR)
+=
hdlc_fr.o
obj-$(CONFIG_HDLC_PPP)
+=
hdlc_ppp.o
syncppp.o
obj-$(CONFIG_HDLC_PPP)
+=
hdlc_ppp.o
obj-$(CONFIG_HDLC_X25)
+=
hdlc_x25.o
pc300-y
:=
pc300_drv.o
...
...
drivers/net/wan/hdlc_ppp.c
View file @
e022c2f0
...
...
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* Point-to-point protocol support
*
* Copyright (C) 1999 - 200
6
Krzysztof Halasa <khc@pm.waw.pl>
* Copyright (C) 1999 - 200
8
Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
...
...
@@ -18,87 +18,632 @@
#include <linux/module.h>
#include <linux/pkt_sched.h>
#include <linux/poll.h>
#include <linux/rtnetlink.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <net/syncppp.h>
#include <linux/spinlock.h>
#define DEBUG_CP 0
/* also bytes# to dump */
#define DEBUG_STATE 0
#define DEBUG_HARD_HEADER 0
#define HDLC_ADDR_ALLSTATIONS 0xFF
#define HDLC_CTRL_UI 0x03
#define PID_LCP 0xC021
#define PID_IP 0x0021
#define PID_IPCP 0x8021
#define PID_IPV6 0x0057
#define PID_IPV6CP 0x8057
enum
{
IDX_LCP
=
0
,
IDX_IPCP
,
IDX_IPV6CP
,
IDX_COUNT
};
enum
{
CP_CONF_REQ
=
1
,
CP_CONF_ACK
,
CP_CONF_NAK
,
CP_CONF_REJ
,
CP_TERM_REQ
,
CP_TERM_ACK
,
CP_CODE_REJ
,
LCP_PROTO_REJ
,
LCP_ECHO_REQ
,
LCP_ECHO_REPLY
,
LCP_DISC_REQ
,
CP_CODES
};
#if DEBUG_CP
static
const
char
*
const
code_names
[
CP_CODES
]
=
{
"0"
,
"ConfReq"
,
"ConfAck"
,
"ConfNak"
,
"ConfRej"
,
"TermReq"
,
"TermAck"
,
"CodeRej"
,
"ProtoRej"
,
"EchoReq"
,
"EchoReply"
,
"Discard"
};
static
char
debug_buffer
[
64
+
3
*
DEBUG_CP
];
#endif
enum
{
LCP_OPTION_MRU
=
1
,
LCP_OPTION_ACCM
,
LCP_OPTION_MAGIC
=
5
};
struct
hdlc_header
{
u8
address
;
u8
control
;
__be16
protocol
;
};
struct
cp_header
{
u8
code
;
u8
id
;
__be16
len
;
};
struct
proto
{
struct
net_device
*
dev
;
struct
timer_list
timer
;
unsigned
long
timeout
;
u16
pid
;
/* protocol ID */
u8
state
;
u8
cr_id
;
/* ID of last Configuration-Request */
u8
restart_counter
;
};
struct
ppp
{
struct
proto
protos
[
IDX_COUNT
];
spinlock_t
lock
;
unsigned
long
last_pong
;
unsigned
int
req_timeout
,
cr_retries
,
term_retries
;
unsigned
int
keepalive_interval
,
keepalive_timeout
;
u8
seq
;
/* local sequence number for requests */
u8
echo_id
;
/* ID of last Echo-Request (LCP) */
};
struct
ppp_state
{
struct
ppp_device
pppdev
;
struct
ppp_device
*
syncppp_ptr
;
int
(
*
old_change_mtu
)(
struct
net_device
*
dev
,
int
new_mtu
);
enum
{
CLOSED
=
0
,
STOPPED
,
STOPPING
,
REQ_SENT
,
ACK_RECV
,
ACK_SENT
,
OPENED
,
STATES
,
STATE_MASK
=
0xF
};
enum
{
START
=
0
,
STOP
,
TO_GOOD
,
TO_BAD
,
RCR_GOOD
,
RCR_BAD
,
RCA
,
RCN
,
RTR
,
RTA
,
RUC
,
RXJ_GOOD
,
RXJ_BAD
,
EVENTS
};
enum
{
INV
=
0x10
,
IRC
=
0x20
,
ZRC
=
0x40
,
SCR
=
0x80
,
SCA
=
0x100
,
SCN
=
0x200
,
STR
=
0x400
,
STA
=
0x800
,
SCJ
=
0x1000
};
#if DEBUG_STATE
static
const
char
*
const
state_names
[
STATES
]
=
{
"Closed"
,
"Stopped"
,
"Stopping"
,
"ReqSent"
,
"AckRecv"
,
"AckSent"
,
"Opened"
};
static
const
char
*
const
event_names
[
EVENTS
]
=
{
"Start"
,
"Stop"
,
"TO+"
,
"TO-"
,
"RCR+"
,
"RCR-"
,
"RCA"
,
"RCN"
,
"RTR"
,
"RTA"
,
"RUC"
,
"RXJ+"
,
"RXJ-"
};
#endif
static
struct
sk_buff_head
tx_queue
;
/* used when holding the spin lock */
static
int
ppp_ioctl
(
struct
net_device
*
dev
,
struct
ifreq
*
ifr
);
static
inline
struct
ppp
*
get_ppp
(
struct
net_device
*
dev
)
{
return
(
struct
ppp
*
)
dev_to_hdlc
(
dev
)
->
state
;
}
static
inline
struct
p
pp_state
*
state
(
hdlc_device
*
hdlc
)
static
inline
struct
p
roto
*
get_proto
(
struct
net_device
*
dev
,
u16
pid
)
{
return
(
struct
ppp_state
*
)(
hdlc
->
state
);
struct
ppp
*
ppp
=
get_ppp
(
dev
);
switch
(
pid
)
{
case
PID_LCP
:
return
&
ppp
->
protos
[
IDX_LCP
];
case
PID_IPCP
:
return
&
ppp
->
protos
[
IDX_IPCP
];
case
PID_IPV6CP
:
return
&
ppp
->
protos
[
IDX_IPV6CP
];
default:
return
NULL
;
}
}
static
inline
const
char
*
proto_name
(
u16
pid
)
{
switch
(
pid
)
{
case
PID_LCP
:
return
"LCP"
;
case
PID_IPCP
:
return
"IPCP"
;
case
PID_IPV6CP
:
return
"IPV6CP"
;
default:
return
NULL
;
}
}
static
int
ppp_open
(
struct
net_device
*
dev
)
static
__be16
ppp_type_trans
(
struct
sk_buff
*
skb
,
struct
net_device
*
dev
)
{
hdlc_device
*
hdlc
=
dev_to_hdlc
(
dev
);
int
(
*
old_ioctl
)(
struct
net_device
*
,
struct
ifreq
*
,
int
);
int
result
;
struct
hdlc_header
*
data
=
(
struct
hdlc_header
*
)
skb
->
data
;
dev
->
ml_priv
=
&
state
(
hdlc
)
->
syncppp_ptr
;
state
(
hdlc
)
->
syncppp_ptr
=
&
state
(
hdlc
)
->
pppdev
;
state
(
hdlc
)
->
pppdev
.
dev
=
dev
;
if
(
skb
->
len
<
sizeof
(
struct
hdlc_header
))
return
htons
(
ETH_P_HDLC
);
if
(
data
->
address
!=
HDLC_ADDR_ALLSTATIONS
||
data
->
control
!=
HDLC_CTRL_UI
)
return
htons
(
ETH_P_HDLC
);
old_ioctl
=
dev
->
do_ioctl
;
state
(
hdlc
)
->
old_change_mtu
=
dev
->
change_mtu
;
sppp_attach
(
&
state
(
hdlc
)
->
pppdev
);
/* sppp_attach nukes them. We don't need syncppp's ioctl */
dev
->
do_ioctl
=
old_ioctl
;
state
(
hdlc
)
->
pppdev
.
sppp
.
pp_flags
&=
~
PP_CISCO
;
dev
->
type
=
ARPHRD_PPP
;
result
=
sppp_open
(
dev
);
if
(
result
)
{
sppp_detach
(
dev
);
return
result
;
switch
(
data
->
protocol
)
{
case
__constant_htons
(
PID_IP
):
skb_pull
(
skb
,
sizeof
(
struct
hdlc_header
)
);
return
htons
(
ETH_P_IP
);
case
__constant_htons
(
PID_IPV6
):
skb_pull
(
skb
,
sizeof
(
struct
hdlc_header
))
;
return
htons
(
ETH_P_IPV6
);
default:
return
htons
(
ETH_P_HDLC
)
;
}
}
return
0
;
static
int
ppp_hard_header
(
struct
sk_buff
*
skb
,
struct
net_device
*
dev
,
u16
type
,
const
void
*
daddr
,
const
void
*
saddr
,
unsigned
int
len
)
{
struct
hdlc_header
*
data
;
#if DEBUG_HARD_HEADER
printk
(
KERN_DEBUG
"%s: ppp_hard_header() called
\n
"
,
dev
->
name
);
#endif
skb_push
(
skb
,
sizeof
(
struct
hdlc_header
));
data
=
(
struct
hdlc_header
*
)
skb
->
data
;
data
->
address
=
HDLC_ADDR_ALLSTATIONS
;
data
->
control
=
HDLC_CTRL_UI
;
switch
(
type
)
{
case
ETH_P_IP
:
data
->
protocol
=
htons
(
PID_IP
);
break
;
case
ETH_P_IPV6
:
data
->
protocol
=
htons
(
PID_IPV6
);
break
;
case
PID_LCP
:
case
PID_IPCP
:
case
PID_IPV6CP
:
data
->
protocol
=
htons
(
type
);
break
;
default:
/* unknown protocol */
data
->
protocol
=
0
;
}
return
sizeof
(
struct
hdlc_header
);
}
static
void
ppp_tx_flush
(
void
)
{
struct
sk_buff
*
skb
;
while
((
skb
=
skb_dequeue
(
&
tx_queue
))
!=
NULL
)
dev_queue_xmit
(
skb
);
}
static
void
ppp_close
(
struct
net_device
*
dev
)
static
void
ppp_tx_cp
(
struct
net_device
*
dev
,
u16
pid
,
u8
code
,
u8
id
,
unsigned
int
len
,
const
void
*
data
)
{
hdlc_device
*
hdlc
=
dev_to_hdlc
(
dev
);
struct
sk_buff
*
skb
;
struct
cp_header
*
cp
;
unsigned
int
magic_len
=
0
;
static
u32
magic
;
#if DEBUG_CP
int
i
;
char
*
ptr
;
#endif
if
(
pid
==
PID_LCP
&&
(
code
==
LCP_ECHO_REQ
||
code
==
LCP_ECHO_REPLY
))
magic_len
=
sizeof
(
magic
);
skb
=
dev_alloc_skb
(
sizeof
(
struct
hdlc_header
)
+
sizeof
(
struct
cp_header
)
+
magic_len
+
len
);
if
(
!
skb
)
{
printk
(
KERN_WARNING
"%s: out of memory in ppp_tx_cp()
\n
"
,
dev
->
name
);
return
;
}
skb_reserve
(
skb
,
sizeof
(
struct
hdlc_header
));
cp
=
(
struct
cp_header
*
)
skb_put
(
skb
,
sizeof
(
struct
cp_header
));
cp
->
code
=
code
;
cp
->
id
=
id
;
cp
->
len
=
htons
(
sizeof
(
struct
cp_header
)
+
magic_len
+
len
);
if
(
magic_len
)
memcpy
(
skb_put
(
skb
,
magic_len
),
&
magic
,
magic_len
);
if
(
len
)
memcpy
(
skb_put
(
skb
,
len
),
data
,
len
);
#if DEBUG_CP
BUG_ON
(
code
>=
CP_CODES
);
ptr
=
debug_buffer
;
*
ptr
=
'\x0'
;
for
(
i
=
0
;
i
<
min_t
(
unsigned
int
,
magic_len
+
len
,
DEBUG_CP
);
i
++
)
{
sprintf
(
ptr
,
" %02X"
,
skb
->
data
[
sizeof
(
struct
cp_header
)
+
i
]);
ptr
+=
strlen
(
ptr
);
}
printk
(
KERN_DEBUG
"%s: TX %s [%s id 0x%X]%s
\n
"
,
dev
->
name
,
proto_name
(
pid
),
code_names
[
code
],
id
,
debug_buffer
);
#endif
sppp_close
(
dev
);
sppp_detach
(
dev
);
ppp_hard_header
(
skb
,
dev
,
pid
,
NULL
,
NULL
,
0
);
dev
->
change_mtu
=
state
(
hdlc
)
->
old_change_mtu
;
dev
->
mtu
=
HDLC_MAX_MTU
;
dev
->
hard_header_len
=
16
;
skb
->
priority
=
TC_PRIO_CONTROL
;
skb
->
dev
=
dev
;
skb_reset_network_header
(
skb
);
skb_queue_tail
(
&
tx_queue
,
skb
);
}
/* State transition table (compare STD-51)
Events Actions
TO+ = Timeout with counter > 0 irc = Initialize-Restart-Count
TO- = Timeout with counter expired zrc = Zero-Restart-Count
RCR+ = Receive-Configure-Request (Good) scr = Send-Configure-Request
RCR- = Receive-Configure-Request (Bad)
RCA = Receive-Configure-Ack sca = Send-Configure-Ack
RCN = Receive-Configure-Nak/Rej scn = Send-Configure-Nak/Rej
RTR = Receive-Terminate-Request str = Send-Terminate-Request
RTA = Receive-Terminate-Ack sta = Send-Terminate-Ack
RUC = Receive-Unknown-Code scj = Send-Code-Reject
RXJ+ = Receive-Code-Reject (permitted)
or Receive-Protocol-Reject
RXJ- = Receive-Code-Reject (catastrophic)
or Receive-Protocol-Reject
*/
static
int
cp_table
[
EVENTS
][
STATES
]
=
{
/* CLOSED STOPPED STOPPING REQ_SENT ACK_RECV ACK_SENT OPENED
0 1 2 3 4 5 6 */
{
IRC
|
SCR
|
3
,
INV
,
INV
,
INV
,
INV
,
INV
,
INV
},
/* START */
{
INV
,
0
,
0
,
0
,
0
,
0
,
0
},
/* STOP */
{
INV
,
INV
,
STR
|
2
,
SCR
|
3
,
SCR
|
3
,
SCR
|
5
,
INV
},
/* TO+ */
{
INV
,
INV
,
1
,
1
,
1
,
1
,
INV
},
/* TO- */
{
STA
|
0
,
IRC
|
SCR
|
SCA
|
5
,
2
,
SCA
|
5
,
SCA
|
6
,
SCA
|
5
,
SCR
|
SCA
|
5
},
/* RCR+ */
{
STA
|
0
,
IRC
|
SCR
|
SCN
|
3
,
2
,
SCN
|
3
,
SCN
|
4
,
SCN
|
3
,
SCR
|
SCN
|
3
},
/* RCR- */
{
STA
|
0
,
STA
|
1
,
2
,
IRC
|
4
,
SCR
|
3
,
6
,
SCR
|
3
},
/* RCA */
{
STA
|
0
,
STA
|
1
,
2
,
IRC
|
SCR
|
3
,
SCR
|
3
,
IRC
|
SCR
|
5
,
SCR
|
3
},
/* RCN */
{
STA
|
0
,
STA
|
1
,
STA
|
2
,
STA
|
3
,
STA
|
3
,
STA
|
3
,
ZRC
|
STA
|
2
},
/* RTR */
{
0
,
1
,
1
,
3
,
3
,
5
,
SCR
|
3
},
/* RTA */
{
SCJ
|
0
,
SCJ
|
1
,
SCJ
|
2
,
SCJ
|
3
,
SCJ
|
4
,
SCJ
|
5
,
SCJ
|
6
},
/* RUC */
{
0
,
1
,
2
,
3
,
3
,
5
,
6
},
/* RXJ+ */
{
0
,
1
,
1
,
1
,
1
,
1
,
IRC
|
STR
|
2
},
/* RXJ- */
};
static
__be16
ppp_type_trans
(
struct
sk_buff
*
skb
,
struct
net_device
*
dev
)
/* SCA: RCR+ must supply id, len and data
SCN: RCR- must supply code, id, len and data
STA: RTR must supply id
SCJ: RUC must supply CP packet len and data */
static
void
ppp_cp_event
(
struct
net_device
*
dev
,
u16
pid
,
u16
event
,
u8
code
,
u8
id
,
unsigned
int
len
,
void
*
data
)
{
return
__constant_htons
(
ETH_P_WAN_PPP
);
int
old_state
,
action
;
struct
ppp
*
ppp
=
get_ppp
(
dev
);
struct
proto
*
proto
=
get_proto
(
dev
,
pid
);
old_state
=
proto
->
state
;
BUG_ON
(
old_state
>=
STATES
);
BUG_ON
(
event
>=
EVENTS
);
#if DEBUG_STATE
printk
(
KERN_DEBUG
"%s: %s ppp_cp_event(%s) %s ...
\n
"
,
dev
->
name
,
proto_name
(
pid
),
event_names
[
event
],
state_names
[
proto
->
state
]);
#endif
action
=
cp_table
[
event
][
old_state
];
proto
->
state
=
action
&
STATE_MASK
;
if
(
action
&
(
SCR
|
STR
))
/* set Configure-Req/Terminate-Req timer */
mod_timer
(
&
proto
->
timer
,
proto
->
timeout
=
jiffies
+
ppp
->
req_timeout
*
HZ
);
if
(
action
&
ZRC
)
proto
->
restart_counter
=
0
;
if
(
action
&
IRC
)
proto
->
restart_counter
=
(
proto
->
state
==
STOPPING
)
?
ppp
->
term_retries
:
ppp
->
cr_retries
;
if
(
action
&
SCR
)
/* send Configure-Request */
ppp_tx_cp
(
dev
,
pid
,
CP_CONF_REQ
,
proto
->
cr_id
=
++
ppp
->
seq
,
0
,
NULL
);
if
(
action
&
SCA
)
/* send Configure-Ack */
ppp_tx_cp
(
dev
,
pid
,
CP_CONF_ACK
,
id
,
len
,
data
);
if
(
action
&
SCN
)
/* send Configure-Nak/Reject */
ppp_tx_cp
(
dev
,
pid
,
code
,
id
,
len
,
data
);
if
(
action
&
STR
)
/* send Terminate-Request */
ppp_tx_cp
(
dev
,
pid
,
CP_TERM_REQ
,
++
ppp
->
seq
,
0
,
NULL
);
if
(
action
&
STA
)
/* send Terminate-Ack */
ppp_tx_cp
(
dev
,
pid
,
CP_TERM_ACK
,
id
,
0
,
NULL
);
if
(
action
&
SCJ
)
/* send Code-Reject */
ppp_tx_cp
(
dev
,
pid
,
CP_CODE_REJ
,
++
ppp
->
seq
,
len
,
data
);
if
(
old_state
!=
OPENED
&&
proto
->
state
==
OPENED
)
{
printk
(
KERN_INFO
"%s: %s up
\n
"
,
dev
->
name
,
proto_name
(
pid
));
if
(
pid
==
PID_LCP
)
{
netif_dormant_off
(
dev
);
ppp_cp_event
(
dev
,
PID_IPCP
,
START
,
0
,
0
,
0
,
NULL
);
ppp_cp_event
(
dev
,
PID_IPV6CP
,
START
,
0
,
0
,
0
,
NULL
);
ppp
->
last_pong
=
jiffies
;
mod_timer
(
&
proto
->
timer
,
proto
->
timeout
=
jiffies
+
ppp
->
keepalive_interval
*
HZ
);
}
}
if
(
old_state
==
OPENED
&&
proto
->
state
!=
OPENED
)
{
printk
(
KERN_INFO
"%s: %s down
\n
"
,
dev
->
name
,
proto_name
(
pid
));
if
(
pid
==
PID_LCP
)
{
netif_dormant_on
(
dev
);
ppp_cp_event
(
dev
,
PID_IPCP
,
STOP
,
0
,
0
,
0
,
NULL
);
ppp_cp_event
(
dev
,
PID_IPV6CP
,
STOP
,
0
,
0
,
0
,
NULL
);
}
}
if
(
old_state
!=
CLOSED
&&
proto
->
state
==
CLOSED
)
del_timer
(
&
proto
->
timer
);
#if DEBUG_STATE
printk
(
KERN_DEBUG
"%s: %s ppp_cp_event(%s) ... %s
\n
"
,
dev
->
name
,
proto_name
(
pid
),
event_names
[
event
],
state_names
[
proto
->
state
]);
#endif
}
static
void
ppp_cp_parse_cr
(
struct
net_device
*
dev
,
u16
pid
,
u8
id
,
unsigned
int
len
,
u8
*
data
)
{
static
u8
const
valid_accm
[
6
]
=
{
LCP_OPTION_ACCM
,
6
,
0
,
0
,
0
,
0
};
u8
*
opt
,
*
out
;
unsigned
int
nak_len
=
0
,
rej_len
=
0
;
if
(
!
(
out
=
kmalloc
(
len
,
GFP_ATOMIC
)))
{
dev
->
stats
.
rx_dropped
++
;
return
;
/* out of memory, ignore CR packet */
}
for
(
opt
=
data
;
len
;
len
-=
opt
[
1
],
opt
+=
opt
[
1
])
{
if
(
len
<
2
||
len
<
opt
[
1
])
{
dev
->
stats
.
rx_errors
++
;
return
;
/* bad packet, drop silently */
}
if
(
pid
==
PID_LCP
)
switch
(
opt
[
0
])
{
case
LCP_OPTION_MRU
:
continue
;
/* MRU always OK and > 1500 bytes? */
case
LCP_OPTION_ACCM
:
/* async control character map */
if
(
!
memcmp
(
opt
,
valid_accm
,
sizeof
(
valid_accm
)))
continue
;
if
(
!
rej_len
)
{
/* NAK it */
memcpy
(
out
+
nak_len
,
valid_accm
,
sizeof
(
valid_accm
));
nak_len
+=
sizeof
(
valid_accm
);
continue
;
}
break
;
case
LCP_OPTION_MAGIC
:
if
(
opt
[
1
]
!=
6
||
(
!
opt
[
2
]
&&
!
opt
[
3
]
&&
!
opt
[
4
]
&&
!
opt
[
5
]))
break
;
/* reject invalid magic number */
continue
;
}
/* reject this option */
memcpy
(
out
+
rej_len
,
opt
,
opt
[
1
]);
rej_len
+=
opt
[
1
];
}
if
(
rej_len
)
ppp_cp_event
(
dev
,
pid
,
RCR_BAD
,
CP_CONF_REJ
,
id
,
rej_len
,
out
);
else
if
(
nak_len
)
ppp_cp_event
(
dev
,
pid
,
RCR_BAD
,
CP_CONF_NAK
,
id
,
nak_len
,
out
);
else
ppp_cp_event
(
dev
,
pid
,
RCR_GOOD
,
CP_CONF_ACK
,
id
,
len
,
data
);
kfree
(
out
);
}
static
int
ppp_rx
(
struct
sk_buff
*
skb
)
{
struct
hdlc_header
*
hdr
=
(
struct
hdlc_header
*
)
skb
->
data
;
struct
net_device
*
dev
=
skb
->
dev
;
struct
ppp
*
ppp
=
get_ppp
(
dev
);
struct
proto
*
proto
;
struct
cp_header
*
cp
;
unsigned
long
flags
;
unsigned
int
len
;
u16
pid
;
#if DEBUG_CP
int
i
;
char
*
ptr
;
#endif
spin_lock_irqsave
(
&
ppp
->
lock
,
flags
);
/* Check HDLC header */
if
(
skb
->
len
<
sizeof
(
struct
hdlc_header
))
goto
rx_error
;
cp
=
(
struct
cp_header
*
)
skb_pull
(
skb
,
sizeof
(
struct
hdlc_header
));
if
(
hdr
->
address
!=
HDLC_ADDR_ALLSTATIONS
||
hdr
->
control
!=
HDLC_CTRL_UI
)
goto
rx_error
;
pid
=
ntohs
(
hdr
->
protocol
);
proto
=
get_proto
(
dev
,
pid
);
if
(
!
proto
)
{
if
(
ppp
->
protos
[
IDX_LCP
].
state
==
OPENED
)
ppp_tx_cp
(
dev
,
PID_LCP
,
LCP_PROTO_REJ
,
++
ppp
->
seq
,
skb
->
len
+
2
,
&
hdr
->
protocol
);
goto
rx_error
;
}
len
=
ntohs
(
cp
->
len
);
if
(
len
<
sizeof
(
struct
cp_header
)
/* no complete CP header? */
||
skb
->
len
<
len
/* truncated packet? */
)
goto
rx_error
;
skb_pull
(
skb
,
sizeof
(
struct
cp_header
));
len
-=
sizeof
(
struct
cp_header
);
/* HDLC and CP headers stripped from skb */
#if DEBUG_CP
if
(
cp
->
code
<
CP_CODES
)
sprintf
(
debug_buffer
,
"[%s id 0x%X]"
,
code_names
[
cp
->
code
],
cp
->
id
);
else
sprintf
(
debug_buffer
,
"[code %u id 0x%X]"
,
cp
->
code
,
cp
->
id
);
ptr
=
debug_buffer
+
strlen
(
debug_buffer
);
for
(
i
=
0
;
i
<
min_t
(
unsigned
int
,
len
,
DEBUG_CP
);
i
++
)
{
sprintf
(
ptr
,
" %02X"
,
skb
->
data
[
i
]);
ptr
+=
strlen
(
ptr
);
}
printk
(
KERN_DEBUG
"%s: RX %s %s
\n
"
,
dev
->
name
,
proto_name
(
pid
),
debug_buffer
);
#endif
/* LCP only */
if
(
pid
==
PID_LCP
)
switch
(
cp
->
code
)
{
case
LCP_PROTO_REJ
:
pid
=
ntohs
(
*
(
__be16
*
)
skb
->
data
);
if
(
pid
==
PID_LCP
||
pid
==
PID_IPCP
||
pid
==
PID_IPV6CP
)
ppp_cp_event
(
dev
,
pid
,
RXJ_BAD
,
0
,
0
,
0
,
NULL
);
goto
out
;
case
LCP_ECHO_REQ
:
/* send Echo-Reply */
if
(
len
>=
4
&&
proto
->
state
==
OPENED
)
ppp_tx_cp
(
dev
,
PID_LCP
,
LCP_ECHO_REPLY
,
cp
->
id
,
len
-
4
,
skb
->
data
+
4
);
goto
out
;
case
LCP_ECHO_REPLY
:
if
(
cp
->
id
==
ppp
->
echo_id
)
ppp
->
last_pong
=
jiffies
;
goto
out
;
case
LCP_DISC_REQ
:
/* discard */
goto
out
;
}
/* LCP, IPCP and IPV6CP */
switch
(
cp
->
code
)
{
case
CP_CONF_REQ
:
ppp_cp_parse_cr
(
dev
,
pid
,
cp
->
id
,
len
,
skb
->
data
);
goto
out
;
case
CP_CONF_ACK
:
if
(
cp
->
id
==
proto
->
cr_id
)
ppp_cp_event
(
dev
,
pid
,
RCA
,
0
,
0
,
0
,
NULL
);
goto
out
;
case
CP_CONF_REJ
:
case
CP_CONF_NAK
:
if
(
cp
->
id
==
proto
->
cr_id
)
ppp_cp_event
(
dev
,
pid
,
RCN
,
0
,
0
,
0
,
NULL
);
goto
out
;
case
CP_TERM_REQ
:
ppp_cp_event
(
dev
,
pid
,
RTR
,
0
,
cp
->
id
,
0
,
NULL
);
goto
out
;
case
CP_TERM_ACK
:
ppp_cp_event
(
dev
,
pid
,
RTA
,
0
,
0
,
0
,
NULL
);
goto
out
;
case
CP_CODE_REJ
:
ppp_cp_event
(
dev
,
pid
,
RXJ_BAD
,
0
,
0
,
0
,
NULL
);
goto
out
;
default:
len
+=
sizeof
(
struct
cp_header
);
if
(
len
>
dev
->
mtu
)
len
=
dev
->
mtu
;
ppp_cp_event
(
dev
,
pid
,
RUC
,
0
,
0
,
len
,
cp
);
goto
out
;
}
goto
out
;
rx_error:
dev
->
stats
.
rx_errors
++
;
out:
spin_unlock_irqrestore
(
&
ppp
->
lock
,
flags
);
dev_kfree_skb_any
(
skb
);
ppp_tx_flush
();
return
NET_RX_DROP
;
}
static
void
ppp_timer
(
unsigned
long
arg
)
{
struct
proto
*
proto
=
(
struct
proto
*
)
arg
;
struct
ppp
*
ppp
=
get_ppp
(
proto
->
dev
);
unsigned
long
flags
;
spin_lock_irqsave
(
&
ppp
->
lock
,
flags
);
switch
(
proto
->
state
)
{
case
STOPPING
:
case
REQ_SENT
:
case
ACK_RECV
:
case
ACK_SENT
:
if
(
proto
->
restart_counter
)
{
ppp_cp_event
(
proto
->
dev
,
proto
->
pid
,
TO_GOOD
,
0
,
0
,
0
,
NULL
);
proto
->
restart_counter
--
;
}
else
ppp_cp_event
(
proto
->
dev
,
proto
->
pid
,
TO_BAD
,
0
,
0
,
0
,
NULL
);
break
;
case
OPENED
:
if
(
proto
->
pid
!=
PID_LCP
)
break
;
if
(
time_after
(
jiffies
,
ppp
->
last_pong
+
ppp
->
keepalive_timeout
*
HZ
))
{
printk
(
KERN_INFO
"%s: Link down
\n
"
,
proto
->
dev
->
name
);
ppp_cp_event
(
proto
->
dev
,
PID_LCP
,
STOP
,
0
,
0
,
0
,
NULL
);
ppp_cp_event
(
proto
->
dev
,
PID_LCP
,
START
,
0
,
0
,
0
,
NULL
);
}
else
{
/* send keep-alive packet */
ppp
->
echo_id
=
++
ppp
->
seq
;
ppp_tx_cp
(
proto
->
dev
,
PID_LCP
,
LCP_ECHO_REQ
,
ppp
->
echo_id
,
0
,
NULL
);
proto
->
timer
.
expires
=
jiffies
+
ppp
->
keepalive_interval
*
HZ
;
add_timer
(
&
proto
->
timer
);
}
break
;
}
spin_unlock_irqrestore
(
&
ppp
->
lock
,
flags
);
ppp_tx_flush
();
}
static
void
ppp_start
(
struct
net_device
*
dev
)
{
struct
ppp
*
ppp
=
get_ppp
(
dev
);
int
i
;
for
(
i
=
0
;
i
<
IDX_COUNT
;
i
++
)
{
struct
proto
*
proto
=
&
ppp
->
protos
[
i
];
proto
->
dev
=
dev
;
init_timer
(
&
proto
->
timer
);
proto
->
timer
.
function
=
ppp_timer
;
proto
->
timer
.
data
=
(
unsigned
long
)
proto
;
proto
->
state
=
CLOSED
;
}
ppp
->
protos
[
IDX_LCP
].
pid
=
PID_LCP
;
ppp
->
protos
[
IDX_IPCP
].
pid
=
PID_IPCP
;
ppp
->
protos
[
IDX_IPV6CP
].
pid
=
PID_IPV6CP
;
ppp_cp_event
(
dev
,
PID_LCP
,
START
,
0
,
0
,
0
,
NULL
);
}
static
void
ppp_stop
(
struct
net_device
*
dev
)
{
ppp_cp_event
(
dev
,
PID_LCP
,
STOP
,
0
,
0
,
0
,
NULL
);
}
static
struct
hdlc_proto
proto
=
{
.
open
=
ppp_open
,
.
close
=
ppp_close
,
.
start
=
ppp_start
,
.
stop
=
ppp_stop
,
.
type_trans
=
ppp_type_trans
,
.
ioctl
=
ppp_ioctl
,
.
netif_rx
=
ppp_rx
,
.
module
=
THIS_MODULE
,
};
static
const
struct
header_ops
ppp_header_ops
=
{
.
create
=
ppp_hard_header
,
};
static
int
ppp_ioctl
(
struct
net_device
*
dev
,
struct
ifreq
*
ifr
)
{
hdlc_device
*
hdlc
=
dev_to_hdlc
(
dev
);
struct
ppp
*
ppp
;
int
result
;
switch
(
ifr
->
ifr_settings
.
type
)
{
...
...
@@ -109,25 +654,35 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
return
0
;
/* return protocol only, no settable parameters */
case
IF_PROTO_PPP
:
if
(
!
capable
(
CAP_NET_ADMIN
))
if
(
!
capable
(
CAP_NET_ADMIN
))
return
-
EPERM
;
if
(
dev
->
flags
&
IFF_UP
)
if
(
dev
->
flags
&
IFF_UP
)
return
-
EBUSY
;
/* no settable parameters */
result
=
hdlc
->
attach
(
dev
,
ENCODING_NRZ
,
PARITY_CRC16_PR1_CCITT
);
result
=
hdlc
->
attach
(
dev
,
ENCODING_NRZ
,
PARITY_CRC16_PR1_CCITT
);
if
(
result
)
return
result
;
result
=
attach_hdlc_protocol
(
dev
,
&
proto
,
sizeof
(
struct
ppp_state
));
result
=
attach_hdlc_protocol
(
dev
,
&
proto
,
sizeof
(
struct
ppp
));
if
(
result
)
return
result
;
ppp
=
get_ppp
(
dev
);
spin_lock_init
(
&
ppp
->
lock
);
ppp
->
req_timeout
=
2
;
ppp
->
cr_retries
=
10
;
ppp
->
term_retries
=
2
;
ppp
->
keepalive_interval
=
10
;
ppp
->
keepalive_timeout
=
60
;
dev
->
hard_start_xmit
=
hdlc
->
xmit
;
dev
->
hard_header_len
=
sizeof
(
struct
hdlc_header
);
dev
->
header_ops
=
&
ppp_header_ops
;
dev
->
type
=
ARPHRD_PPP
;
netif_dormant_o
ff
(
dev
);
netif_dormant_o
n
(
dev
);
return
0
;
}
...
...
@@ -137,12 +692,11 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
static
int
__init
mod_init
(
void
)
{
skb_queue_head_init
(
&
tx_queue
);
register_hdlc_protocol
(
&
proto
);
return
0
;
}
static
void
__exit
mod_exit
(
void
)
{
unregister_hdlc_protocol
(
&
proto
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment