Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

10
net/llc/Kconfig Normal file
View File

@@ -0,0 +1,10 @@
config LLC
tristate
depends on NET
config LLC2
tristate "ANSI/IEEE 802.2 LLC type 2 Support"
select LLC
help
This is a Logical Link Layer type 2, connection oriented support.
Select this if you want to have support for PF_LLC sockets.

24
net/llc/Makefile Normal file
View File

@@ -0,0 +1,24 @@
###########################################################################
# Makefile for the Linux 802.2 LLC (fully-functional) layer.
#
# Copyright (c) 1997 by Procom Technology,Inc.
# 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
#
# This program can be redistributed or modified under the terms of the
# GNU General Public License as published by the Free Software Foundation.
# This program is distributed without any warranty or implied warranty
# of merchantability or fitness for a particular purpose.
#
# See the GNU General Public License for more details.
###########################################################################
obj-$(CONFIG_LLC) += llc.o
llc-y := llc_core.o llc_input.o llc_output.o
obj-$(CONFIG_LLC2) += llc2.o
llc2-y := llc_if.o llc_c_ev.o llc_c_ac.o llc_conn.o llc_c_st.o llc_pdu.o \
llc_sap.o llc_s_ac.o llc_s_ev.o llc_s_st.o af_llc.o llc_station.o
llc2-$(CONFIG_PROC_FS) += llc_proc.o

1079
net/llc/af_llc.c Normal file

File diff suppressed because it is too large Load Diff

1514
net/llc/llc_c_ac.c Normal file

File diff suppressed because it is too large Load Diff

769
net/llc/llc_c_ev.c Normal file
View File

@@ -0,0 +1,769 @@
/*
* llc_c_ev.c - Connection component state transition event qualifiers
*
* A 'state' consists of a number of possible event matching functions,
* the actions associated with each being executed when that event is
* matched; a 'state machine' accepts events in a serial fashion from an
* event queue. Each event is passed to each successive event matching
* function until a match is made (the event matching function returns
* success, or '0') or the list of event matching functions is exhausted.
* If a match is made, the actions associated with the event are executed
* and the state is changed to that event's transition state. Before some
* events are recognized, even after a match has been made, a certain
* number of 'event qualifier' functions must also be executed. If these
* all execute successfully, then the event is finally executed.
*
* These event functions must return 0 for success, to show a matched
* event, of 1 if the event does not match. Event qualifier functions
* must return a 0 for success or a non-zero for failure. Each function
* is simply responsible for verifying one single thing and returning
* either a success or failure.
*
* All of followed event functions are described in 802.2 LLC Protocol
* standard document except two functions that we added that will explain
* in their comments, at below.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <net/llc_conn.h>
#include <net/llc_sap.h>
#include <net/sock.h>
#include <net/llc_c_ev.h>
#include <net/llc_pdu.h>
#if 1
#define dprintk(args...) printk(KERN_DEBUG args)
#else
#define dprintk(args...)
#endif
extern u16 llc_circular_between(u8 a, u8 b, u8 c);
/**
* llc_util_ns_inside_rx_window - check if sequence number is in rx window
* @ns: sequence number of received pdu.
* @vr: sequence number which receiver expects to receive.
* @rw: receive window size of receiver.
*
* Checks if sequence number of received PDU is in range of receive
* window. Returns 0 for success, 1 otherwise
*/
static u16 llc_util_ns_inside_rx_window(u8 ns, u8 vr, u8 rw)
{
return !llc_circular_between(vr, ns,
(vr + rw - 1) % LLC_2_SEQ_NBR_MODULO);
}
/**
* llc_util_nr_inside_tx_window - check if sequence number is in tx window
* @sk: current connection.
* @nr: N(R) of received PDU.
*
* This routine checks if N(R) of received PDU is in range of transmit
* window; on the other hand checks if received PDU acknowledges some
* outstanding PDUs that are in transmit window. Returns 0 for success, 1
* otherwise.
*/
static u16 llc_util_nr_inside_tx_window(struct sock *sk, u8 nr)
{
u8 nr1, nr2;
struct sk_buff *skb;
struct llc_pdu_sn *pdu;
struct llc_sock *llc = llc_sk(sk);
int rc = 0;
if (llc->dev->flags & IFF_LOOPBACK)
goto out;
rc = 1;
if (!skb_queue_len(&llc->pdu_unack_q))
goto out;
skb = skb_peek(&llc->pdu_unack_q);
pdu = llc_pdu_sn_hdr(skb);
nr1 = LLC_I_GET_NS(pdu);
skb = skb_peek_tail(&llc->pdu_unack_q);
pdu = llc_pdu_sn_hdr(skb);
nr2 = LLC_I_GET_NS(pdu);
rc = !llc_circular_between(nr1, nr, (nr2 + 1) % LLC_2_SEQ_NBR_MODULO);
out:
return rc;
}
int llc_conn_ev_conn_req(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
return ev->prim == LLC_CONN_PRIM &&
ev->prim_type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_data_req(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
return ev->prim == LLC_DATA_PRIM &&
ev->prim_type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_disc_req(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
return ev->prim == LLC_DISC_PRIM &&
ev->prim_type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_rst_req(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
return ev->prim == LLC_RESET_PRIM &&
ev->prim_type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_local_busy_detected(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
return ev->type == LLC_CONN_EV_TYPE_SIMPLE &&
ev->prim_type == LLC_CONN_EV_LOCAL_BUSY_DETECTED ? 0 : 1;
}
int llc_conn_ev_local_busy_cleared(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
return ev->type == LLC_CONN_EV_TYPE_SIMPLE &&
ev->prim_type == LLC_CONN_EV_LOCAL_BUSY_CLEARED ? 0 : 1;
}
int llc_conn_ev_rx_bad_pdu(struct sock *sk, struct sk_buff *skb)
{
return 1;
}
int llc_conn_ev_rx_disc_cmd_pbit_set_x(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_CMD(pdu) == LLC_2_PDU_CMD_DISC ? 0 : 1;
}
int llc_conn_ev_rx_dm_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_RSP(pdu) == LLC_2_PDU_RSP_DM ? 0 : 1;
}
int llc_conn_ev_rx_frmr_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_RSP(pdu) == LLC_2_PDU_RSP_FRMR ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return llc_conn_space(sk, skb) &&
LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_I(pdu) &&
LLC_I_PF_IS_0(pdu) &&
LLC_I_GET_NS(pdu) == llc_sk(sk)->vR ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return llc_conn_space(sk, skb) &&
LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_I(pdu) &&
LLC_I_PF_IS_1(pdu) &&
LLC_I_GET_NS(pdu) == llc_sk(sk)->vR ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns(struct sock *sk,
struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_I(pdu) &&
LLC_I_PF_IS_0(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns(struct sock *sk,
struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_I(pdu) &&
LLC_I_PF_IS_1(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_x_inval_ns(struct sock *sk,
struct sk_buff *skb)
{
struct llc_pdu_sn * pdu = llc_pdu_sn_hdr(skb);
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
u16 rc = LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_I(pdu) && ns != vr &&
llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
if (!rc)
dprintk("%s: matched, state=%d, ns=%d, vr=%d\n",
__FUNCTION__, llc_sk(sk)->state, ns, vr);
return rc;
}
int llc_conn_ev_rx_i_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return llc_conn_space(sk, skb) &&
LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_I(pdu) &&
LLC_I_PF_IS_0(pdu) &&
LLC_I_GET_NS(pdu) == llc_sk(sk)->vR ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_I(pdu) &&
LLC_I_PF_IS_1(pdu) &&
LLC_I_GET_NS(pdu) == llc_sk(sk)->vR ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return llc_conn_space(sk, skb) &&
LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_I(pdu) &&
LLC_I_GET_NS(pdu) == llc_sk(sk)->vR ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns(struct sock *sk,
struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_I(pdu) &&
LLC_I_PF_IS_0(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns(struct sock *sk,
struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_I(pdu) &&
LLC_I_PF_IS_1(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_x_unexpd_ns(struct sock *sk,
struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_I(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_x_inval_ns(struct sock *sk,
struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
u16 rc = LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_I(pdu) && ns != vr &&
llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
if (!rc)
dprintk("%s: matched, state=%d, ns=%d, vr=%d\n",
__FUNCTION__, llc_sk(sk)->state, ns, vr);
return rc;
}
int llc_conn_ev_rx_rej_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_REJ ? 0 : 1;
}
int llc_conn_ev_rx_rej_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_REJ ? 0 : 1;
}
int llc_conn_ev_rx_rej_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_REJ ? 0 : 1;
}
int llc_conn_ev_rx_rej_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_REJ ? 0 : 1;
}
int llc_conn_ev_rx_rej_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_REJ ? 0 : 1;
}
int llc_conn_ev_rx_rnr_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_RNR ? 0 : 1;
}
int llc_conn_ev_rx_rnr_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_RNR ? 0 : 1;
}
int llc_conn_ev_rx_rnr_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_RNR ? 0 : 1;
}
int llc_conn_ev_rx_rnr_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_RNR ? 0 : 1;
}
int llc_conn_ev_rx_rr_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_RR ? 0 : 1;
}
int llc_conn_ev_rx_rr_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_RR ? 0 : 1;
}
int llc_conn_ev_rx_rr_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return llc_conn_space(sk, skb) &&
LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_RR ? 0 : 1;
}
int llc_conn_ev_rx_rr_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
return llc_conn_space(sk, skb) &&
LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_RR ? 0 : 1;
}
int llc_conn_ev_rx_sabme_cmd_pbit_set_x(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return LLC_PDU_IS_CMD(pdu) && LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_CMD(pdu) == LLC_2_PDU_CMD_SABME ? 0 : 1;
}
int llc_conn_ev_rx_ua_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb)
{
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return LLC_PDU_IS_RSP(pdu) && LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_RSP(pdu) == LLC_2_PDU_RSP_UA ? 0 : 1;
}
int llc_conn_ev_rx_xxx_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb)
{
u16 rc = 1;
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
if (LLC_PDU_IS_CMD(pdu)) {
if (LLC_PDU_TYPE_IS_I(pdu) || LLC_PDU_TYPE_IS_S(pdu)) {
if (LLC_I_PF_IS_1(pdu))
rc = 0;
} else if (LLC_PDU_TYPE_IS_U(pdu) && LLC_U_PF_IS_1(pdu))
rc = 0;
}
return rc;
}
int llc_conn_ev_rx_xxx_cmd_pbit_set_x(struct sock *sk, struct sk_buff *skb)
{
u16 rc = 1;
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
if (LLC_PDU_IS_CMD(pdu)) {
if (LLC_PDU_TYPE_IS_I(pdu) || LLC_PDU_TYPE_IS_S(pdu))
rc = 0;
else if (LLC_PDU_TYPE_IS_U(pdu))
switch (LLC_U_PDU_CMD(pdu)) {
case LLC_2_PDU_CMD_SABME:
case LLC_2_PDU_CMD_DISC:
rc = 0;
break;
}
}
return rc;
}
int llc_conn_ev_rx_xxx_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb)
{
u16 rc = 1;
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
if (LLC_PDU_IS_RSP(pdu)) {
if (LLC_PDU_TYPE_IS_I(pdu) || LLC_PDU_TYPE_IS_S(pdu)) {
if (LLC_I_PF_IS_1(pdu))
rc = 0;
} else if (LLC_PDU_TYPE_IS_U(pdu))
switch (LLC_U_PDU_RSP(pdu)) {
case LLC_2_PDU_RSP_UA:
case LLC_2_PDU_RSP_DM:
case LLC_2_PDU_RSP_FRMR:
if (LLC_U_PF_IS_1(pdu))
rc = 0;
break;
}
}
return rc;
}
int llc_conn_ev_rx_xxx_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb)
{
u16 rc = 1;
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
if (LLC_PDU_IS_RSP(pdu)) {
if (LLC_PDU_TYPE_IS_I(pdu) || LLC_PDU_TYPE_IS_S(pdu))
rc = 0;
else if (LLC_PDU_TYPE_IS_U(pdu))
switch (LLC_U_PDU_RSP(pdu)) {
case LLC_2_PDU_RSP_UA:
case LLC_2_PDU_RSP_DM:
case LLC_2_PDU_RSP_FRMR:
rc = 0;
break;
}
}
return rc;
}
int llc_conn_ev_rx_zzz_cmd_pbit_set_x_inval_nr(struct sock *sk,
struct sk_buff *skb)
{
u16 rc = 1;
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
u8 vs = llc_sk(sk)->vS;
u8 nr = LLC_I_GET_NR(pdu);
if (LLC_PDU_IS_CMD(pdu) &&
(LLC_PDU_TYPE_IS_I(pdu) || LLC_PDU_TYPE_IS_S(pdu)) &&
nr != vs && llc_util_nr_inside_tx_window(sk, nr)) {
dprintk("%s: matched, state=%d, vs=%d, nr=%d\n",
__FUNCTION__, llc_sk(sk)->state, vs, nr);
rc = 0;
}
return rc;
}
int llc_conn_ev_rx_zzz_rsp_fbit_set_x_inval_nr(struct sock *sk,
struct sk_buff *skb)
{
u16 rc = 1;
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
u8 vs = llc_sk(sk)->vS;
u8 nr = LLC_I_GET_NR(pdu);
if (LLC_PDU_IS_RSP(pdu) &&
(LLC_PDU_TYPE_IS_I(pdu) || LLC_PDU_TYPE_IS_S(pdu)) &&
nr != vs && llc_util_nr_inside_tx_window(sk, nr)) {
rc = 0;
dprintk("%s: matched, state=%d, vs=%d, nr=%d\n",
__FUNCTION__, llc_sk(sk)->state, vs, nr);
}
return rc;
}
int llc_conn_ev_rx_any_frame(struct sock *sk, struct sk_buff *skb)
{
return 0;
}
int llc_conn_ev_p_tmr_exp(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
return ev->type != LLC_CONN_EV_TYPE_P_TMR;
}
int llc_conn_ev_ack_tmr_exp(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
return ev->type != LLC_CONN_EV_TYPE_ACK_TMR;
}
int llc_conn_ev_rej_tmr_exp(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
return ev->type != LLC_CONN_EV_TYPE_REJ_TMR;
}
int llc_conn_ev_busy_tmr_exp(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
return ev->type != LLC_CONN_EV_TYPE_BUSY_TMR;
}
int llc_conn_ev_init_p_f_cycle(struct sock *sk, struct sk_buff *skb)
{
return 1;
}
int llc_conn_ev_tx_buffer_full(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
return ev->type == LLC_CONN_EV_TYPE_SIMPLE &&
ev->prim_type == LLC_CONN_EV_TX_BUFF_FULL ? 0 : 1;
}
/* Event qualifier functions
*
* these functions simply verify the value of a state flag associated with
* the connection and return either a 0 for success or a non-zero value
* for not-success; verify the event is the type we expect
*/
int llc_conn_ev_qlfy_data_flag_eq_1(struct sock *sk, struct sk_buff *skb)
{
return llc_sk(sk)->data_flag != 1;
}
int llc_conn_ev_qlfy_data_flag_eq_0(struct sock *sk, struct sk_buff *skb)
{
return llc_sk(sk)->data_flag;
}
int llc_conn_ev_qlfy_data_flag_eq_2(struct sock *sk, struct sk_buff *skb)
{
return llc_sk(sk)->data_flag != 2;
}
int llc_conn_ev_qlfy_p_flag_eq_1(struct sock *sk, struct sk_buff *skb)
{
return llc_sk(sk)->p_flag != 1;
}
/**
* conn_ev_qlfy_last_frame_eq_1 - checks if frame is last in tx window
* @sk: current connection structure.
* @skb: current event.
*
* This function determines when frame which is sent, is last frame of
* transmit window, if it is then this function return zero else return
* one. This function is used for sending last frame of transmit window
* as I-format command with p-bit set to one. Returns 0 if frame is last
* frame, 1 otherwise.
*/
int llc_conn_ev_qlfy_last_frame_eq_1(struct sock *sk, struct sk_buff *skb)
{
return !(skb_queue_len(&llc_sk(sk)->pdu_unack_q) + 1 == llc_sk(sk)->k);
}
/**
* conn_ev_qlfy_last_frame_eq_0 - checks if frame isn't last in tx window
* @sk: current connection structure.
* @skb: current event.
*
* This function determines when frame which is sent, isn't last frame of
* transmit window, if it isn't then this function return zero else return
* one. Returns 0 if frame isn't last frame, 1 otherwise.
*/
int llc_conn_ev_qlfy_last_frame_eq_0(struct sock *sk, struct sk_buff *skb)
{
return skb_queue_len(&llc_sk(sk)->pdu_unack_q) + 1 == llc_sk(sk)->k;
}
int llc_conn_ev_qlfy_p_flag_eq_0(struct sock *sk, struct sk_buff *skb)
{
return llc_sk(sk)->p_flag;
}
int llc_conn_ev_qlfy_p_flag_eq_f(struct sock *sk, struct sk_buff *skb)
{
u8 f_bit;
llc_pdu_decode_pf_bit(skb, &f_bit);
return llc_sk(sk)->p_flag == f_bit ? 0 : 1;
}
int llc_conn_ev_qlfy_remote_busy_eq_0(struct sock *sk, struct sk_buff *skb)
{
return llc_sk(sk)->remote_busy_flag;
}
int llc_conn_ev_qlfy_remote_busy_eq_1(struct sock *sk, struct sk_buff *skb)
{
return !llc_sk(sk)->remote_busy_flag;
}
int llc_conn_ev_qlfy_retry_cnt_lt_n2(struct sock *sk, struct sk_buff *skb)
{
return !(llc_sk(sk)->retry_count < llc_sk(sk)->n2);
}
int llc_conn_ev_qlfy_retry_cnt_gte_n2(struct sock *sk, struct sk_buff *skb)
{
return !(llc_sk(sk)->retry_count >= llc_sk(sk)->n2);
}
int llc_conn_ev_qlfy_s_flag_eq_1(struct sock *sk, struct sk_buff *skb)
{
return !llc_sk(sk)->s_flag;
}
int llc_conn_ev_qlfy_s_flag_eq_0(struct sock *sk, struct sk_buff *skb)
{
return llc_sk(sk)->s_flag;
}
int llc_conn_ev_qlfy_cause_flag_eq_1(struct sock *sk, struct sk_buff *skb)
{
return !llc_sk(sk)->cause_flag;
}
int llc_conn_ev_qlfy_cause_flag_eq_0(struct sock *sk, struct sk_buff *skb)
{
return llc_sk(sk)->cause_flag;
}
int llc_conn_ev_qlfy_set_status_conn(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
ev->status = LLC_STATUS_CONN;
return 0;
}
int llc_conn_ev_qlfy_set_status_disc(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
ev->status = LLC_STATUS_DISC;
return 0;
}
int llc_conn_ev_qlfy_set_status_failed(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
ev->status = LLC_STATUS_FAILED;
return 0;
}
int llc_conn_ev_qlfy_set_status_remote_busy(struct sock *sk,
struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
ev->status = LLC_STATUS_REMOTE_BUSY;
return 0;
}
int llc_conn_ev_qlfy_set_status_refuse(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
ev->status = LLC_STATUS_REFUSE;
return 0;
}
int llc_conn_ev_qlfy_set_status_conflict(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
ev->status = LLC_STATUS_CONFLICT;
return 0;
}
int llc_conn_ev_qlfy_set_status_rst_done(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
ev->status = LLC_STATUS_RESET_DONE;
return 0;
}

4946
net/llc/llc_c_st.c Normal file

File diff suppressed because it is too large Load Diff

915
net/llc/llc_conn.c Normal file
View File

@@ -0,0 +1,915 @@
/*
* llc_conn.c - Driver routines for connection component.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/init.h>
#include <net/llc_sap.h>
#include <net/llc_conn.h>
#include <net/sock.h>
#include <linux/tcp.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_ac.h>
#include <net/llc_c_st.h>
#include <net/llc_pdu.h>
#if 0
#define dprintk(args...) printk(KERN_DEBUG args)
#else
#define dprintk(args...)
#endif
static int llc_find_offset(int state, int ev_type);
static void llc_conn_send_pdus(struct sock *sk);
static int llc_conn_service(struct sock *sk, struct sk_buff *skb);
static int llc_exec_conn_trans_actions(struct sock *sk,
struct llc_conn_state_trans *trans,
struct sk_buff *ev);
static struct llc_conn_state_trans *llc_qualify_conn_ev(struct sock *sk,
struct sk_buff *skb);
/* Offset table on connection states transition diagram */
static int llc_offset_table[NBR_CONN_STATES][NBR_CONN_EV];
/**
* llc_conn_state_process - sends event to connection state machine
* @sk: connection
* @skb: occurred event
*
* Sends an event to connection state machine. After processing event
* (executing it's actions and changing state), upper layer will be
* indicated or confirmed, if needed. Returns 0 for success, 1 for
* failure. The socket lock has to be held before calling this function.
*/
int llc_conn_state_process(struct sock *sk, struct sk_buff *skb)
{
int rc;
struct llc_sock *llc = llc_sk(sk);
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
/*
* We have to hold the skb, because llc_conn_service will kfree it in
* the sending path and we need to look at the skb->cb, where we encode
* llc_conn_state_ev.
*/
skb_get(skb);
ev->ind_prim = ev->cfm_prim = 0;
rc = llc_conn_service(sk, skb); /* sending event to state machine */
if (rc) {
printk(KERN_ERR "%s: llc_conn_service failed\n", __FUNCTION__);
goto out_kfree_skb;
}
if (!ev->ind_prim && !ev->cfm_prim) {
/* indicate or confirm not required */
if (!skb->list)
goto out_kfree_skb;
goto out_skb_put;
}
if (ev->ind_prim && ev->cfm_prim) /* Paranoia */
skb_get(skb);
switch (ev->ind_prim) {
case LLC_DATA_PRIM:
llc_save_primitive(skb, LLC_DATA_PRIM);
if (sock_queue_rcv_skb(sk, skb)) {
/*
* shouldn't happen
*/
printk(KERN_ERR "%s: sock_queue_rcv_skb failed!\n",
__FUNCTION__);
kfree_skb(skb);
}
break;
case LLC_CONN_PRIM: {
struct sock *parent = skb->sk;
skb->sk = sk;
skb_queue_tail(&parent->sk_receive_queue, skb);
sk->sk_state_change(parent);
}
break;
case LLC_DISC_PRIM:
sock_hold(sk);
if (sk->sk_type == SOCK_STREAM &&
sk->sk_state == TCP_ESTABLISHED) {
sk->sk_shutdown = SHUTDOWN_MASK;
sk->sk_socket->state = SS_UNCONNECTED;
sk->sk_state = TCP_CLOSE;
if (!sock_flag(sk, SOCK_DEAD)) {
sk->sk_state_change(sk);
sock_set_flag(sk, SOCK_DEAD);
}
}
kfree_skb(skb);
sock_put(sk);
break;
case LLC_RESET_PRIM:
/*
* FIXME:
* RESET is not being notified to upper layers for now
*/
printk(KERN_INFO "%s: received a reset ind!\n", __FUNCTION__);
kfree_skb(skb);
break;
default:
if (ev->ind_prim) {
printk(KERN_INFO "%s: received unknown %d prim!\n",
__FUNCTION__, ev->ind_prim);
kfree_skb(skb);
}
/* No indication */
break;
}
switch (ev->cfm_prim) {
case LLC_DATA_PRIM:
if (!llc_data_accept_state(llc->state))
sk->sk_write_space(sk);
else
rc = llc->failed_data_req = 1;
break;
case LLC_CONN_PRIM:
if (sk->sk_type == SOCK_STREAM &&
sk->sk_state == TCP_SYN_SENT) {
if (ev->status) {
sk->sk_socket->state = SS_UNCONNECTED;
sk->sk_state = TCP_CLOSE;
} else {
sk->sk_socket->state = SS_CONNECTED;
sk->sk_state = TCP_ESTABLISHED;
}
sk->sk_state_change(sk);
}
break;
case LLC_DISC_PRIM:
sock_hold(sk);
if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSING) {
sk->sk_socket->state = SS_UNCONNECTED;
sk->sk_state = TCP_CLOSE;
sk->sk_state_change(sk);
}
sock_put(sk);
break;
case LLC_RESET_PRIM:
/*
* FIXME:
* RESET is not being notified to upper layers for now
*/
printk(KERN_INFO "%s: received a reset conf!\n", __FUNCTION__);
break;
default:
if (ev->cfm_prim) {
printk(KERN_INFO "%s: received unknown %d prim!\n",
__FUNCTION__, ev->cfm_prim);
break;
}
goto out_skb_put; /* No confirmation */
}
out_kfree_skb:
kfree_skb(skb);
out_skb_put:
kfree_skb(skb);
return rc;
}
void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb)
{
/* queue PDU to send to MAC layer */
skb_queue_tail(&sk->sk_write_queue, skb);
llc_conn_send_pdus(sk);
}
/**
* llc_conn_rtn_pdu - sends received data pdu to upper layer
* @sk: Active connection
* @skb: Received data frame
*
* Sends received data pdu to upper layer (by using indicate function).
* Prepares service parameters (prim and prim_data). calling indication
* function will be done in llc_conn_state_process.
*/
void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
ev->ind_prim = LLC_DATA_PRIM;
}
/**
* llc_conn_resend_i_pdu_as_cmd - resend all all unacknowledged I PDUs
* @sk: active connection
* @nr: NR
* @first_p_bit: p_bit value of first pdu
*
* Resend all unacknowledged I PDUs, starting with the NR; send first as
* command PDU with P bit equal first_p_bit; if more than one send
* subsequent as command PDUs with P bit equal zero (0).
*/
void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit)
{
struct sk_buff *skb;
struct llc_pdu_sn *pdu;
u16 nbr_unack_pdus;
struct llc_sock *llc;
u8 howmany_resend = 0;
llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);
if (!nbr_unack_pdus)
goto out;
/*
* Process unack PDUs only if unack queue is not empty; remove
* appropriate PDUs, fix them up, and put them on mac_pdu_q.
*/
llc = llc_sk(sk);
while ((skb = skb_dequeue(&llc->pdu_unack_q)) != NULL) {
pdu = llc_pdu_sn_hdr(skb);
llc_pdu_set_cmd_rsp(skb, LLC_PDU_CMD);
llc_pdu_set_pf_bit(skb, first_p_bit);
skb_queue_tail(&sk->sk_write_queue, skb);
first_p_bit = 0;
llc->vS = LLC_I_GET_NS(pdu);
howmany_resend++;
}
if (howmany_resend > 0)
llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;
/* any PDUs to re-send are queued up; start sending to MAC */
llc_conn_send_pdus(sk);
out:;
}
/**
* llc_conn_resend_i_pdu_as_rsp - Resend all unacknowledged I PDUs
* @sk: active connection.
* @nr: NR
* @first_f_bit: f_bit value of first pdu.
*
* Resend all unacknowledged I PDUs, starting with the NR; send first as
* response PDU with F bit equal first_f_bit; if more than one send
* subsequent as response PDUs with F bit equal zero (0).
*/
void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit)
{
struct sk_buff *skb;
u16 nbr_unack_pdus;
struct llc_sock *llc = llc_sk(sk);
u8 howmany_resend = 0;
llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);
if (!nbr_unack_pdus)
goto out;
/*
* Process unack PDUs only if unack queue is not empty; remove
* appropriate PDUs, fix them up, and put them on mac_pdu_q
*/
while ((skb = skb_dequeue(&llc->pdu_unack_q)) != NULL) {
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
llc_pdu_set_cmd_rsp(skb, LLC_PDU_RSP);
llc_pdu_set_pf_bit(skb, first_f_bit);
skb_queue_tail(&sk->sk_write_queue, skb);
first_f_bit = 0;
llc->vS = LLC_I_GET_NS(pdu);
howmany_resend++;
}
if (howmany_resend > 0)
llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;
/* any PDUs to re-send are queued up; start sending to MAC */
llc_conn_send_pdus(sk);
out:;
}
/**
* llc_conn_remove_acked_pdus - Removes acknowledged pdus from tx queue
* @sk: active connection
* nr: NR
* how_many_unacked: size of pdu_unack_q after removing acked pdus
*
* Removes acknowledged pdus from transmit queue (pdu_unack_q). Returns
* the number of pdus that removed from queue.
*/
int llc_conn_remove_acked_pdus(struct sock *sk, u8 nr, u16 *how_many_unacked)
{
int pdu_pos, i;
struct sk_buff *skb;
struct llc_pdu_sn *pdu;
int nbr_acked = 0;
struct llc_sock *llc = llc_sk(sk);
int q_len = skb_queue_len(&llc->pdu_unack_q);
if (!q_len)
goto out;
skb = skb_peek(&llc->pdu_unack_q);
pdu = llc_pdu_sn_hdr(skb);
/* finding position of last acked pdu in queue */
pdu_pos = ((int)LLC_2_SEQ_NBR_MODULO + (int)nr -
(int)LLC_I_GET_NS(pdu)) % LLC_2_SEQ_NBR_MODULO;
for (i = 0; i < pdu_pos && i < q_len; i++) {
skb = skb_dequeue(&llc->pdu_unack_q);
if (skb)
kfree_skb(skb);
nbr_acked++;
}
out:
*how_many_unacked = skb_queue_len(&llc->pdu_unack_q);
return nbr_acked;
}
/**
* llc_conn_send_pdus - Sends queued PDUs
* @sk: active connection
*
* Sends queued pdus to MAC layer for transmission.
*/
static void llc_conn_send_pdus(struct sock *sk)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&sk->sk_write_queue)) != NULL) {
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
if (LLC_PDU_TYPE_IS_I(pdu) &&
!(skb->dev->flags & IFF_LOOPBACK)) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
skb_queue_tail(&llc_sk(sk)->pdu_unack_q, skb);
if (!skb2)
break;
skb = skb2;
}
dev_queue_xmit(skb);
}
}
/**
* llc_conn_service - finds transition and changes state of connection
* @sk: connection
* @skb: happened event
*
* This function finds transition that matches with happened event, then
* executes related actions and finally changes state of connection.
* Returns 0 for success, 1 for failure.
*/
static int llc_conn_service(struct sock *sk, struct sk_buff *skb)
{
int rc = 1;
struct llc_sock *llc = llc_sk(sk);
struct llc_conn_state_trans *trans;
if (llc->state > NBR_CONN_STATES)
goto out;
rc = 0;
trans = llc_qualify_conn_ev(sk, skb);
if (trans) {
rc = llc_exec_conn_trans_actions(sk, trans, skb);
if (!rc && trans->next_state != NO_STATE_CHANGE) {
llc->state = trans->next_state;
if (!llc_data_accept_state(llc->state))
sk->sk_state_change(sk);
}
}
out:
return rc;
}
/**
* llc_qualify_conn_ev - finds transition for event
* @sk: connection
* @skb: happened event
*
* This function finds transition that matches with happened event.
* Returns pointer to found transition on success, %NULL otherwise.
*/
static struct llc_conn_state_trans *llc_qualify_conn_ev(struct sock *sk,
struct sk_buff *skb)
{
struct llc_conn_state_trans **next_trans;
llc_conn_ev_qfyr_t *next_qualifier;
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
struct llc_sock *llc = llc_sk(sk);
struct llc_conn_state *curr_state =
&llc_conn_state_table[llc->state - 1];
/* search thru events for this state until
* list exhausted or until no more
*/
for (next_trans = curr_state->transitions +
llc_find_offset(llc->state - 1, ev->type);
(*next_trans)->ev; next_trans++) {
if (!((*next_trans)->ev)(sk, skb)) {
/* got POSSIBLE event match; the event may require
* qualification based on the values of a number of
* state flags; if all qualifications are met (i.e.,
* if all qualifying functions return success, or 0,
* then this is THE event we're looking for
*/
for (next_qualifier = (*next_trans)->ev_qualifiers;
next_qualifier && *next_qualifier &&
!(*next_qualifier)(sk, skb); next_qualifier++)
/* nothing */;
if (!next_qualifier || !*next_qualifier)
/* all qualifiers executed successfully; this is
* our transition; return it so we can perform
* the associated actions & change the state
*/
return *next_trans;
}
}
return NULL;
}
/**
* llc_exec_conn_trans_actions - executes related actions
* @sk: connection
* @trans: transition that it's actions must be performed
* @skb: event
*
* Executes actions that is related to happened event. Returns 0 for
* success, 1 to indicate failure of at least one action.
*/
static int llc_exec_conn_trans_actions(struct sock *sk,
struct llc_conn_state_trans *trans,
struct sk_buff *skb)
{
int rc = 0;
llc_conn_action_t *next_action;
for (next_action = trans->ev_actions;
next_action && *next_action; next_action++) {
int rc2 = (*next_action)(sk, skb);
if (rc2 == 2) {
rc = rc2;
break;
} else if (rc2)
rc = 1;
}
return rc;
}
/**
* llc_lookup_established - Finds connection for the remote/local sap/mac
* @sap: SAP
* @daddr: address of remote LLC (MAC + SAP)
* @laddr: address of local LLC (MAC + SAP)
*
* Search connection list of the SAP and finds connection using the remote
* mac, remote sap, local mac, and local sap. Returns pointer for
* connection found, %NULL otherwise.
*/
struct sock *llc_lookup_established(struct llc_sap *sap, struct llc_addr *daddr,
struct llc_addr *laddr)
{
struct sock *rc;
struct hlist_node *node;
read_lock_bh(&sap->sk_list.lock);
sk_for_each(rc, node, &sap->sk_list.list) {
struct llc_sock *llc = llc_sk(rc);
if (llc->laddr.lsap == laddr->lsap &&
llc->daddr.lsap == daddr->lsap &&
llc_mac_match(llc->laddr.mac, laddr->mac) &&
llc_mac_match(llc->daddr.mac, daddr->mac)) {
sock_hold(rc);
goto found;
}
}
rc = NULL;
found:
read_unlock_bh(&sap->sk_list.lock);
return rc;
}
/**
* llc_lookup_listener - Finds listener for local MAC + SAP
* @sap: SAP
* @laddr: address of local LLC (MAC + SAP)
*
* Search connection list of the SAP and finds connection listening on
* local mac, and local sap. Returns pointer for parent socket found,
* %NULL otherwise.
*/
static struct sock *llc_lookup_listener(struct llc_sap *sap,
struct llc_addr *laddr)
{
struct sock *rc;
struct hlist_node *node;
read_lock_bh(&sap->sk_list.lock);
sk_for_each(rc, node, &sap->sk_list.list) {
struct llc_sock *llc = llc_sk(rc);
if (rc->sk_type == SOCK_STREAM && rc->sk_state == TCP_LISTEN &&
llc->laddr.lsap == laddr->lsap &&
(llc_mac_match(llc->laddr.mac, laddr->mac) ||
llc_mac_null(llc->laddr.mac))) {
sock_hold(rc);
goto found;
}
}
rc = NULL;
found:
read_unlock_bh(&sap->sk_list.lock);
return rc;
}
/**
* llc_data_accept_state - designates if in this state data can be sent.
* @state: state of connection.
*
* Returns 0 if data can be sent, 1 otherwise.
*/
u8 llc_data_accept_state(u8 state)
{
return state != LLC_CONN_STATE_NORMAL && state != LLC_CONN_STATE_BUSY &&
state != LLC_CONN_STATE_REJ;
}
/**
* find_next_offset - finds offset for next category of transitions
* @state: state table.
* @offset: start offset.
*
* Finds offset of next category of transitions in transition table.
* Returns the start index of next category.
*/
static u16 find_next_offset(struct llc_conn_state *state, u16 offset)
{
u16 cnt = 0;
struct llc_conn_state_trans **next_trans;
for (next_trans = state->transitions + offset;
(*next_trans)->ev; next_trans++)
++cnt;
return cnt;
}
/**
* llc_build_offset_table - builds offset table of connection
*
* Fills offset table of connection state transition table
* (llc_offset_table).
*/
void __init llc_build_offset_table(void)
{
struct llc_conn_state *curr_state;
int state, ev_type, next_offset;
for (state = 0; state < NBR_CONN_STATES; state++) {
curr_state = &llc_conn_state_table[state];
next_offset = 0;
for (ev_type = 0; ev_type < NBR_CONN_EV; ev_type++) {
llc_offset_table[state][ev_type] = next_offset;
next_offset += find_next_offset(curr_state,
next_offset) + 1;
}
}
}
/**
* llc_find_offset - finds start offset of category of transitions
* @state: state of connection
* @ev_type: type of happened event
*
* Finds start offset of desired category of transitions. Returns the
* desired start offset.
*/
static int llc_find_offset(int state, int ev_type)
{
int rc = 0;
/* at this stage, llc_offset_table[..][2] is not important. it is for
* init_pf_cycle and I don't know what is it.
*/
switch (ev_type) {
case LLC_CONN_EV_TYPE_PRIM:
rc = llc_offset_table[state][0]; break;
case LLC_CONN_EV_TYPE_PDU:
rc = llc_offset_table[state][4]; break;
case LLC_CONN_EV_TYPE_SIMPLE:
rc = llc_offset_table[state][1]; break;
case LLC_CONN_EV_TYPE_P_TMR:
case LLC_CONN_EV_TYPE_ACK_TMR:
case LLC_CONN_EV_TYPE_REJ_TMR:
case LLC_CONN_EV_TYPE_BUSY_TMR:
rc = llc_offset_table[state][3]; break;
}
return rc;
}
/**
* llc_sap_add_socket - adds a socket to a SAP
* @sap: SAP
* @sk: socket
*
* This function adds a socket to sk_list of a SAP.
*/
void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk)
{
write_lock_bh(&sap->sk_list.lock);
llc_sk(sk)->sap = sap;
sk_add_node(sk, &sap->sk_list.list);
write_unlock_bh(&sap->sk_list.lock);
}
/**
* llc_sap_remove_socket - removes a socket from SAP
* @sap: SAP
* @sk: socket
*
* This function removes a connection from sk_list.list of a SAP if
* the connection was in this list.
*/
void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk)
{
write_lock_bh(&sap->sk_list.lock);
sk_del_node_init(sk);
write_unlock_bh(&sap->sk_list.lock);
}
/**
* llc_conn_rcv - sends received pdus to the connection state machine
* @sk: current connection structure.
* @skb: received frame.
*
* Sends received pdus to the connection state machine.
*/
static int llc_conn_rcv(struct sock* sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
struct llc_sock *llc = llc_sk(sk);
if (!llc->dev)
llc->dev = skb->dev;
ev->type = LLC_CONN_EV_TYPE_PDU;
ev->reason = 0;
return llc_conn_state_process(sk, skb);
}
void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_addr saddr, daddr;
struct sock *sk;
llc_pdu_decode_sa(skb, saddr.mac);
llc_pdu_decode_ssap(skb, &saddr.lsap);
llc_pdu_decode_da(skb, daddr.mac);
llc_pdu_decode_dsap(skb, &daddr.lsap);
sk = llc_lookup_established(sap, &saddr, &daddr);
if (!sk) {
/*
* Didn't find an active connection; verify if there
* is a listening socket for this llc addr
*/
struct llc_sock *llc;
struct sock *parent = llc_lookup_listener(sap, &daddr);
if (!parent) {
dprintk("llc_lookup_listener failed!\n");
goto drop;
}
sk = llc_sk_alloc(parent->sk_family, GFP_ATOMIC, parent->sk_prot);
if (!sk) {
sock_put(parent);
goto drop;
}
llc = llc_sk(sk);
memcpy(&llc->laddr, &daddr, sizeof(llc->laddr));
memcpy(&llc->daddr, &saddr, sizeof(llc->daddr));
llc_sap_add_socket(sap, sk);
sock_hold(sk);
sock_put(parent);
skb->sk = parent;
} else
skb->sk = sk;
bh_lock_sock(sk);
if (!sock_owned_by_user(sk))
llc_conn_rcv(sk, skb);
else {
dprintk("%s: adding to backlog...\n", __FUNCTION__);
llc_set_backlog_type(skb, LLC_PACKET);
sk_add_backlog(sk, skb);
}
bh_unlock_sock(sk);
sock_put(sk);
return;
drop:
kfree_skb(skb);
}
#undef LLC_REFCNT_DEBUG
#ifdef LLC_REFCNT_DEBUG
static atomic_t llc_sock_nr;
#endif
/**
* llc_release_sockets - releases all sockets in a sap
* @sap: sap to release its sockets
*
* Releases all connections of a sap. Returns 0 if all actions complete
* successfully, nonzero otherwise
*/
int llc_release_sockets(struct llc_sap *sap)
{
int rc = 0;
struct sock *sk;
struct hlist_node *node;
write_lock_bh(&sap->sk_list.lock);
sk_for_each(sk, node, &sap->sk_list.list) {
llc_sk(sk)->state = LLC_CONN_STATE_TEMP;
if (llc_send_disc(sk))
rc = 1;
}
write_unlock_bh(&sap->sk_list.lock);
return rc;
}
/**
* llc_backlog_rcv - Processes rx frames and expired timers.
* @sk: LLC sock (p8022 connection)
* @skb: queued rx frame or event
*
* This function processes frames that has received and timers that has
* expired during sending an I pdu (refer to data_req_handler). frames
* queue by llc_rcv function (llc_mac.c) and timers queue by timer
* callback functions(llc_c_ac.c).
*/
static int llc_backlog_rcv(struct sock *sk, struct sk_buff *skb)
{
int rc = 0;
struct llc_sock *llc = llc_sk(sk);
if (llc_backlog_type(skb) == LLC_PACKET) {
if (llc->state > 1) /* not closed */
rc = llc_conn_rcv(sk, skb);
else
goto out_kfree_skb;
} else if (llc_backlog_type(skb) == LLC_EVENT) {
/* timer expiration event */
if (llc->state > 1) /* not closed */
rc = llc_conn_state_process(sk, skb);
else
goto out_kfree_skb;
} else {
printk(KERN_ERR "%s: invalid skb in backlog\n", __FUNCTION__);
goto out_kfree_skb;
}
out:
return rc;
out_kfree_skb:
kfree_skb(skb);
goto out;
}
/**
* llc_sk_init - Initializes a socket with default llc values.
* @sk: socket to initialize.
*
* Initializes a socket with default llc values.
*/
static void llc_sk_init(struct sock* sk)
{
struct llc_sock *llc = llc_sk(sk);
llc->state = LLC_CONN_STATE_ADM;
llc->inc_cntr = llc->dec_cntr = 2;
llc->dec_step = llc->connect_step = 1;
init_timer(&llc->ack_timer.timer);
llc->ack_timer.expire = LLC_ACK_TIME;
llc->ack_timer.timer.data = (unsigned long)sk;
llc->ack_timer.timer.function = llc_conn_ack_tmr_cb;
init_timer(&llc->pf_cycle_timer.timer);
llc->pf_cycle_timer.expire = LLC_P_TIME;
llc->pf_cycle_timer.timer.data = (unsigned long)sk;
llc->pf_cycle_timer.timer.function = llc_conn_pf_cycle_tmr_cb;
init_timer(&llc->rej_sent_timer.timer);
llc->rej_sent_timer.expire = LLC_REJ_TIME;
llc->rej_sent_timer.timer.data = (unsigned long)sk;
llc->rej_sent_timer.timer.function = llc_conn_rej_tmr_cb;
init_timer(&llc->busy_state_timer.timer);
llc->busy_state_timer.expire = LLC_BUSY_TIME;
llc->busy_state_timer.timer.data = (unsigned long)sk;
llc->busy_state_timer.timer.function = llc_conn_busy_tmr_cb;
llc->n2 = 2; /* max retransmit */
llc->k = 2; /* tx win size, will adjust dynam */
llc->rw = 128; /* rx win size (opt and equal to
* tx_win of remote LLC) */
skb_queue_head_init(&llc->pdu_unack_q);
sk->sk_backlog_rcv = llc_backlog_rcv;
}
/**
* llc_sk_alloc - Allocates LLC sock
* @family: upper layer protocol family
* @priority: for allocation (%GFP_KERNEL, %GFP_ATOMIC, etc)
*
* Allocates a LLC sock and initializes it. Returns the new LLC sock
* or %NULL if there's no memory available for one
*/
struct sock *llc_sk_alloc(int family, int priority, struct proto *prot)
{
struct sock *sk = sk_alloc(family, priority, prot, 1);
if (!sk)
goto out;
llc_sk_init(sk);
sock_init_data(NULL, sk);
#ifdef LLC_REFCNT_DEBUG
atomic_inc(&llc_sock_nr);
printk(KERN_DEBUG "LLC socket %p created in %s, now we have %d alive\n", sk,
__FUNCTION__, atomic_read(&llc_sock_nr));
#endif
out:
return sk;
}
/**
* llc_sk_free - Frees a LLC socket
* @sk - socket to free
*
* Frees a LLC socket
*/
void llc_sk_free(struct sock *sk)
{
struct llc_sock *llc = llc_sk(sk);
llc->state = LLC_CONN_OUT_OF_SVC;
/* Stop all (possibly) running timers */
llc_conn_ac_stop_all_timers(sk, NULL);
#ifdef DEBUG_LLC_CONN_ALLOC
printk(KERN_INFO "%s: unackq=%d, txq=%d\n", __FUNCTION__,
skb_queue_len(&llc->pdu_unack_q),
skb_queue_len(&sk->sk_write_queue));
#endif
skb_queue_purge(&sk->sk_receive_queue);
skb_queue_purge(&sk->sk_write_queue);
skb_queue_purge(&llc->pdu_unack_q);
#ifdef LLC_REFCNT_DEBUG
if (atomic_read(&sk->sk_refcnt) != 1) {
printk(KERN_DEBUG "Destruction of LLC sock %p delayed in %s, cnt=%d\n",
sk, __FUNCTION__, atomic_read(&sk->sk_refcnt));
printk(KERN_DEBUG "%d LLC sockets are still alive\n",
atomic_read(&llc_sock_nr));
} else {
atomic_dec(&llc_sock_nr);
printk(KERN_DEBUG "LLC socket %p released in %s, %d are still alive\n", sk,
__FUNCTION__, atomic_read(&llc_sock_nr));
}
#endif
sock_put(sk);
}
/**
* llc_sk_reset - resets a connection
* @sk: LLC socket to reset
*
* Resets a connection to the out of service state. Stops its timers
* and frees any frames in the queues of the connection.
*/
void llc_sk_reset(struct sock *sk)
{
struct llc_sock *llc = llc_sk(sk);
llc_conn_ac_stop_all_timers(sk, NULL);
skb_queue_purge(&sk->sk_write_queue);
skb_queue_purge(&llc->pdu_unack_q);
llc->remote_busy_flag = 0;
llc->cause_flag = 0;
llc->retry_count = 0;
llc_conn_set_p_flag(sk, 0);
llc->f_flag = 0;
llc->s_flag = 0;
llc->ack_pf = 0;
llc->first_pdu_Ns = 0;
llc->ack_must_be_send = 0;
llc->dec_step = 1;
llc->inc_cntr = 2;
llc->dec_cntr = 2;
llc->X = 0;
llc->failed_data_req = 0 ;
llc->last_nr = 0;
}

179
net/llc/llc_core.c Normal file
View File

@@ -0,0 +1,179 @@
/*
* llc_core.c - Minimum needed routines for sap handling and module init/exit
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/if_ether.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/init.h>
#include <net/llc.h>
LIST_HEAD(llc_sap_list);
DEFINE_RWLOCK(llc_sap_list_lock);
unsigned char llc_station_mac_sa[ETH_ALEN];
/**
* llc_sap_alloc - allocates and initializes sap.
*
* Allocates and initializes sap.
*/
static struct llc_sap *llc_sap_alloc(void)
{
struct llc_sap *sap = kmalloc(sizeof(*sap), GFP_ATOMIC);
if (sap) {
memset(sap, 0, sizeof(*sap));
sap->state = LLC_SAP_STATE_ACTIVE;
memcpy(sap->laddr.mac, llc_station_mac_sa, ETH_ALEN);
rwlock_init(&sap->sk_list.lock);
}
return sap;
}
/**
* llc_add_sap - add sap to station list
* @sap: Address of the sap
*
* Adds a sap to the LLC's station sap list.
*/
static void llc_add_sap(struct llc_sap *sap)
{
write_lock_bh(&llc_sap_list_lock);
list_add_tail(&sap->node, &llc_sap_list);
write_unlock_bh(&llc_sap_list_lock);
}
/**
* llc_del_sap - del sap from station list
* @sap: Address of the sap
*
* Removes a sap to the LLC's station sap list.
*/
static void llc_del_sap(struct llc_sap *sap)
{
write_lock_bh(&llc_sap_list_lock);
list_del(&sap->node);
write_unlock_bh(&llc_sap_list_lock);
}
/**
* llc_sap_find - searchs a SAP in station
* @sap_value: sap to be found
*
* Searchs for a sap in the sap list of the LLC's station upon the sap ID.
* Returns the sap or %NULL if not found.
*/
struct llc_sap *llc_sap_find(unsigned char sap_value)
{
struct llc_sap* sap;
read_lock_bh(&llc_sap_list_lock);
list_for_each_entry(sap, &llc_sap_list, node)
if (sap->laddr.lsap == sap_value)
goto out;
sap = NULL;
out:
read_unlock_bh(&llc_sap_list_lock);
return sap;
}
/**
* llc_sap_open - open interface to the upper layers.
* @lsap: SAP number.
* @func: rcv func for datalink protos
*
* Interface function to upper layer. Each one who wants to get a SAP
* (for example NetBEUI) should call this function. Returns the opened
* SAP for success, NULL for failure.
*/
struct llc_sap *llc_sap_open(unsigned char lsap,
int (*func)(struct sk_buff *skb,
struct net_device *dev,
struct packet_type *pt))
{
struct llc_sap *sap = llc_sap_find(lsap);
if (sap) { /* SAP already exists */
sap = NULL;
goto out;
}
sap = llc_sap_alloc();
if (!sap)
goto out;
sap->laddr.lsap = lsap;
sap->rcv_func = func;
llc_add_sap(sap);
out:
return sap;
}
/**
* llc_sap_close - close interface for upper layers.
* @sap: SAP to be closed.
*
* Close interface function to upper layer. Each one who wants to
* close an open SAP (for example NetBEUI) should call this function.
* Removes this sap from the list of saps in the station and then
* frees the memory for this sap.
*/
void llc_sap_close(struct llc_sap *sap)
{
WARN_ON(!hlist_empty(&sap->sk_list.list));
llc_del_sap(sap);
kfree(sap);
}
static struct packet_type llc_packet_type = {
.type = __constant_htons(ETH_P_802_2),
.func = llc_rcv,
};
static struct packet_type llc_tr_packet_type = {
.type = __constant_htons(ETH_P_TR_802_2),
.func = llc_rcv,
};
static int __init llc_init(void)
{
if (dev_base->next)
memcpy(llc_station_mac_sa, dev_base->next->dev_addr, ETH_ALEN);
else
memset(llc_station_mac_sa, 0, ETH_ALEN);
dev_add_pack(&llc_packet_type);
dev_add_pack(&llc_tr_packet_type);
return 0;
}
static void __exit llc_exit(void)
{
dev_remove_pack(&llc_packet_type);
dev_remove_pack(&llc_tr_packet_type);
}
module_init(llc_init);
module_exit(llc_exit);
EXPORT_SYMBOL(llc_station_mac_sa);
EXPORT_SYMBOL(llc_sap_list);
EXPORT_SYMBOL(llc_sap_list_lock);
EXPORT_SYMBOL(llc_sap_find);
EXPORT_SYMBOL(llc_sap_open);
EXPORT_SYMBOL(llc_sap_close);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Procom 1997, Jay Schullist 2001, Arnaldo C. Melo 2001-2003");
MODULE_DESCRIPTION("LLC IEEE 802.2 core support");

157
net/llc/llc_if.c Normal file
View File

@@ -0,0 +1,157 @@
/*
* llc_if.c - Defines LLC interface to upper layer
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/tcp.h>
#include <asm/errno.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_s_ev.h>
#include <net/llc_conn.h>
#include <net/sock.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_ac.h>
#include <net/llc_c_st.h>
u8 llc_mac_null_var[IFHWADDRLEN];
/**
* llc_build_and_send_pkt - Connection data sending for upper layers.
* @sk: connection
* @skb: packet to send
*
* This function is called when upper layer wants to send data using
* connection oriented communication mode. During sending data, connection
* will be locked and received frames and expired timers will be queued.
* Returns 0 for success, -ECONNABORTED when the connection already
* closed and -EBUSY when sending data is not permitted in this state or
* LLC has send an I pdu with p bit set to 1 and is waiting for it's
* response.
*/
int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb)
{
struct llc_conn_state_ev *ev;
int rc = -ECONNABORTED;
struct llc_sock *llc = llc_sk(sk);
if (llc->state == LLC_CONN_STATE_ADM)
goto out;
rc = -EBUSY;
if (llc_data_accept_state(llc->state)) { /* data_conn_refuse */
llc->failed_data_req = 1;
goto out;
}
if (llc->p_flag) {
llc->failed_data_req = 1;
goto out;
}
ev = llc_conn_ev(skb);
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->prim = LLC_DATA_PRIM;
ev->prim_type = LLC_PRIM_TYPE_REQ;
skb->dev = llc->dev;
rc = llc_conn_state_process(sk, skb);
out:
return rc;
}
/**
* llc_establish_connection - Called by upper layer to establish a conn
* @sk: connection
* @lmac: local mac address
* @dmac: destination mac address
* @dsap: destination sap
*
* Upper layer calls this to establish an LLC connection with a remote
* machine. This function packages a proper event and sends it connection
* component state machine. Success or failure of connection
* establishment will inform to upper layer via calling it's confirm
* function and passing proper information.
*/
int llc_establish_connection(struct sock *sk, u8 *lmac, u8 *dmac, u8 dsap)
{
int rc = -EISCONN;
struct llc_addr laddr, daddr;
struct sk_buff *skb;
struct llc_sock *llc = llc_sk(sk);
struct sock *existing;
laddr.lsap = llc->sap->laddr.lsap;
daddr.lsap = dsap;
memcpy(daddr.mac, dmac, sizeof(daddr.mac));
memcpy(laddr.mac, lmac, sizeof(laddr.mac));
existing = llc_lookup_established(llc->sap, &daddr, &laddr);
if (existing) {
if (existing->sk_state == TCP_ESTABLISHED) {
sk = existing;
goto out_put;
} else
sock_put(existing);
}
sock_hold(sk);
rc = -ENOMEM;
skb = alloc_skb(0, GFP_ATOMIC);
if (skb) {
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->prim = LLC_CONN_PRIM;
ev->prim_type = LLC_PRIM_TYPE_REQ;
rc = llc_conn_state_process(sk, skb);
}
out_put:
sock_put(sk);
return rc;
}
/**
* llc_send_disc - Called by upper layer to close a connection
* @sk: connection to be closed
*
* Upper layer calls this when it wants to close an established LLC
* connection with a remote machine. This function packages a proper event
* and sends it to connection component state machine. Returns 0 for
* success, 1 otherwise.
*/
int llc_send_disc(struct sock *sk)
{
u16 rc = 1;
struct llc_conn_state_ev *ev;
struct sk_buff *skb;
sock_hold(sk);
if (sk->sk_type != SOCK_STREAM || sk->sk_state != TCP_ESTABLISHED ||
llc_sk(sk)->state == LLC_CONN_STATE_ADM ||
llc_sk(sk)->state == LLC_CONN_OUT_OF_SVC)
goto out;
/*
* Postpone unassigning the connection from its SAP and returning the
* connection until all ACTIONs have been completely executed
*/
skb = alloc_skb(0, GFP_ATOMIC);
if (!skb)
goto out;
sk->sk_state = TCP_CLOSING;
ev = llc_conn_ev(skb);
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->prim = LLC_DISC_PRIM;
ev->prim_type = LLC_PRIM_TYPE_REQ;
rc = llc_conn_state_process(sk, skb);
out:
sock_put(sk);
return rc;
}

189
net/llc/llc_input.c Normal file
View File

@@ -0,0 +1,189 @@
/*
* llc_input.c - Minimal input path for LLC
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <net/llc.h>
#include <net/llc_pdu.h>
#include <net/llc_sap.h>
#if 0
#define dprintk(args...) printk(KERN_DEBUG args)
#else
#define dprintk(args...)
#endif
/*
* Packet handler for the station, registerable because in the minimal
* LLC core that is taking shape only the very minimal subset of LLC that
* is needed for things like IPX, Appletalk, etc will stay, with all the
* rest in the llc1 and llc2 modules.
*/
static void (*llc_station_handler)(struct sk_buff *skb);
/*
* Packet handlers for LLC_DEST_SAP and LLC_DEST_CONN.
*/
static void (*llc_type_handlers[2])(struct llc_sap *sap,
struct sk_buff *skb);
void llc_add_pack(int type, void (*handler)(struct llc_sap *sap,
struct sk_buff *skb))
{
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
llc_type_handlers[type - 1] = handler;
}
void llc_remove_pack(int type)
{
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
llc_type_handlers[type - 1] = NULL;
}
void llc_set_station_handler(void (*handler)(struct sk_buff *skb))
{
llc_station_handler = handler;
}
/**
* llc_pdu_type - returns which LLC component must handle for PDU
* @skb: input skb
*
* This function returns which LLC component must handle this PDU.
*/
static __inline__ int llc_pdu_type(struct sk_buff *skb)
{
int type = LLC_DEST_CONN; /* I-PDU or S-PDU type */
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
if ((pdu->ctrl_1 & LLC_PDU_TYPE_MASK) != LLC_PDU_TYPE_U)
goto out;
switch (LLC_U_PDU_CMD(pdu)) {
case LLC_1_PDU_CMD_XID:
case LLC_1_PDU_CMD_UI:
case LLC_1_PDU_CMD_TEST:
type = LLC_DEST_SAP;
break;
case LLC_2_PDU_CMD_SABME:
case LLC_2_PDU_CMD_DISC:
case LLC_2_PDU_RSP_UA:
case LLC_2_PDU_RSP_DM:
case LLC_2_PDU_RSP_FRMR:
break;
default:
type = LLC_DEST_INVALID;
break;
}
out:
return type;
}
/**
* llc_fixup_skb - initializes skb pointers
* @skb: This argument points to incoming skb
*
* Initializes internal skb pointer to start of network layer by deriving
* length of LLC header; finds length of LLC control field in LLC header
* by looking at the two lowest-order bits of the first control field
* byte; field is either 3 or 4 bytes long.
*/
static inline int llc_fixup_skb(struct sk_buff *skb)
{
u8 llc_len = 2;
struct llc_pdu_sn *pdu;
if (!pskb_may_pull(skb, sizeof(*pdu)))
return 0;
pdu = (struct llc_pdu_sn *)skb->data;
if ((pdu->ctrl_1 & LLC_PDU_TYPE_MASK) == LLC_PDU_TYPE_U)
llc_len = 1;
llc_len += 2;
skb->h.raw += llc_len;
skb_pull(skb, llc_len);
if (skb->protocol == htons(ETH_P_802_2)) {
u16 pdulen = eth_hdr(skb)->h_proto,
data_size = ntohs(pdulen) - llc_len;
skb_trim(skb, data_size);
}
return 1;
}
/**
* llc_rcv - 802.2 entry point from net lower layers
* @skb: received pdu
* @dev: device that receive pdu
* @pt: packet type
*
* When the system receives a 802.2 frame this function is called. It
* checks SAP and connection of received pdu and passes frame to
* llc_{station,sap,conn}_rcv for sending to proper state machine. If
* the frame is related to a busy connection (a connection is sending
* data now), it queues this frame in the connection's backlog.
*/
int llc_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt)
{
struct llc_sap *sap;
struct llc_pdu_sn *pdu;
int dest;
/*
* When the interface is in promisc. mode, drop all the crap that it
* receives, do not try to analyse it.
*/
if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) {
dprintk("%s: PACKET_OTHERHOST\n", __FUNCTION__);
goto drop;
}
skb = skb_share_check(skb, GFP_ATOMIC);
if (unlikely(!skb))
goto out;
if (unlikely(!llc_fixup_skb(skb)))
goto drop;
pdu = llc_pdu_sn_hdr(skb);
if (unlikely(!pdu->dsap)) /* NULL DSAP, refer to station */
goto handle_station;
sap = llc_sap_find(pdu->dsap);
if (unlikely(!sap)) {/* unknown SAP */
dprintk("%s: llc_sap_find(%02X) failed!\n", __FUNCTION__,
pdu->dsap);
goto drop;
}
/*
* First the upper layer protocols that don't need the full
* LLC functionality
*/
if (sap->rcv_func) {
sap->rcv_func(skb, dev, pt);
goto out;
}
dest = llc_pdu_type(skb);
if (unlikely(!dest || !llc_type_handlers[dest - 1]))
goto drop;
llc_type_handlers[dest - 1](sap, skb);
out:
return 0;
drop:
kfree_skb(skb);
goto out;
handle_station:
if (!llc_station_handler)
goto drop;
llc_station_handler(skb);
goto out;
}
EXPORT_SYMBOL(llc_add_pack);
EXPORT_SYMBOL(llc_remove_pack);
EXPORT_SYMBOL(llc_set_station_handler);

107
net/llc/llc_output.c Normal file
View File

@@ -0,0 +1,107 @@
/*
* llc_output.c - LLC minimal output path
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License version 2 for more details.
*/
#include <linux/if_arp.h>
#include <linux/if_tr.h>
#include <linux/netdevice.h>
#include <linux/trdevice.h>
#include <linux/skbuff.h>
#include <net/llc.h>
#include <net/llc_pdu.h>
/**
* llc_mac_hdr_init - fills MAC header fields
* @skb: Address of the frame to initialize its MAC header
* @sa: The MAC source address
* @da: The MAC destination address
*
* Fills MAC header fields, depending on MAC type. Returns 0, If MAC type
* is a valid type and initialization completes correctly 1, otherwise.
*/
int llc_mac_hdr_init(struct sk_buff *skb, unsigned char *sa, unsigned char *da)
{
int rc = 0;
switch (skb->dev->type) {
#ifdef CONFIG_TR
case ARPHRD_IEEE802_TR: {
struct net_device *dev = skb->dev;
struct trh_hdr *trh;
skb->mac.raw = skb_push(skb, sizeof(*trh));
trh = tr_hdr(skb);
trh->ac = AC;
trh->fc = LLC_FRAME;
if (sa)
memcpy(trh->saddr, sa, dev->addr_len);
else
memset(trh->saddr, 0, dev->addr_len);
if (da) {
memcpy(trh->daddr, da, dev->addr_len);
tr_source_route(skb, trh, dev);
skb->mac.raw = skb->data;
}
break;
}
#endif
case ARPHRD_ETHER:
case ARPHRD_LOOPBACK: {
unsigned short len = skb->len;
struct ethhdr *eth;
skb->mac.raw = skb_push(skb, sizeof(*eth));
eth = eth_hdr(skb);
eth->h_proto = htons(len);
memcpy(eth->h_dest, da, ETH_ALEN);
memcpy(eth->h_source, sa, ETH_ALEN);
break;
}
default:
printk(KERN_WARNING "device type not supported: %d\n",
skb->dev->type);
rc = -EINVAL;
}
return rc;
}
/**
* llc_build_and_send_ui_pkt - unitdata request interface for upper layers
* @sap: sap to use
* @skb: packet to send
* @dmac: destination mac address
* @dsap: destination sap
*
* Upper layers calls this function when upper layer wants to send data
* using connection-less mode communication (UI pdu).
*
* Accept data frame from network layer to be sent using connection-
* less mode communication; timeout/retries handled by network layer;
* package primitive as an event and send to SAP event handler
*/
int llc_build_and_send_ui_pkt(struct llc_sap *sap, struct sk_buff *skb,
unsigned char *dmac, unsigned char dsap)
{
int rc;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
dsap, LLC_PDU_CMD);
llc_pdu_init_as_ui_cmd(skb);
rc = llc_mac_hdr_init(skb, skb->dev->dev_addr, dmac);
if (!rc)
rc = dev_queue_xmit(skb);
return rc;
}
EXPORT_SYMBOL(llc_mac_hdr_init);
EXPORT_SYMBOL(llc_build_and_send_ui_pkt);

20
net/llc/llc_output.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef LLC_OUTPUT_H
#define LLC_OUTPUT_H
/*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License version 2 for more details.
*/
struct sk_buff;
int llc_mac_hdr_init(struct sk_buff *skb, unsigned char *sa, unsigned char *da);
#endif /* LLC_OUTPUT_H */

372
net/llc/llc_pdu.c Normal file
View File

@@ -0,0 +1,372 @@
/*
* llc_pdu.c - access to PDU internals
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <net/llc_pdu.h>
static void llc_pdu_decode_pdu_type(struct sk_buff *skb, u8 *type);
static u8 llc_pdu_get_pf_bit(struct llc_pdu_sn *pdu);
void llc_pdu_set_cmd_rsp(struct sk_buff *skb, u8 pdu_type)
{
llc_pdu_un_hdr(skb)->ssap |= pdu_type;
}
/**
* pdu_set_pf_bit - sets poll/final bit in LLC header
* @pdu_frame: input frame that p/f bit must be set into it.
* @bit_value: poll/final bit (0 or 1).
*
* This function sets poll/final bit in LLC header (based on type of PDU).
* in I or S pdus, p/f bit is right bit of fourth byte in header. in U
* pdus p/f bit is fifth bit of third byte.
*/
void llc_pdu_set_pf_bit(struct sk_buff *skb, u8 bit_value)
{
u8 pdu_type;
struct llc_pdu_sn *pdu;
llc_pdu_decode_pdu_type(skb, &pdu_type);
pdu = llc_pdu_sn_hdr(skb);
switch (pdu_type) {
case LLC_PDU_TYPE_I:
case LLC_PDU_TYPE_S:
pdu->ctrl_2 = (pdu->ctrl_2 & 0xFE) | bit_value;
break;
case LLC_PDU_TYPE_U:
pdu->ctrl_1 |= (pdu->ctrl_1 & 0xEF) | (bit_value << 4);
break;
}
}
/**
* llc_pdu_decode_pf_bit - extracs poll/final bit from LLC header
* @skb: input skb that p/f bit must be extracted from it
* @pf_bit: poll/final bit (0 or 1)
*
* This function extracts poll/final bit from LLC header (based on type of
* PDU). In I or S pdus, p/f bit is right bit of fourth byte in header. In
* U pdus p/f bit is fifth bit of third byte.
*/
void llc_pdu_decode_pf_bit(struct sk_buff *skb, u8 *pf_bit)
{
u8 pdu_type;
struct llc_pdu_sn *pdu;
llc_pdu_decode_pdu_type(skb, &pdu_type);
pdu = llc_pdu_sn_hdr(skb);
switch (pdu_type) {
case LLC_PDU_TYPE_I:
case LLC_PDU_TYPE_S:
*pf_bit = pdu->ctrl_2 & LLC_S_PF_BIT_MASK;
break;
case LLC_PDU_TYPE_U:
*pf_bit = (pdu->ctrl_1 & LLC_U_PF_BIT_MASK) >> 4;
break;
}
}
/**
* llc_pdu_init_as_disc_cmd - Builds DISC PDU
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
*
* Builds a pdu frame as a DISC command.
*/
void llc_pdu_init_as_disc_cmd(struct sk_buff *skb, u8 p_bit)
{
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_2_PDU_CMD_DISC;
pdu->ctrl_1 |= ((p_bit & 1) << 4) & LLC_U_PF_BIT_MASK;
}
/**
* llc_pdu_init_as_i_cmd - builds I pdu
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
* @ns: The sequence number of the data PDU
* @nr: The seq. number of the expected I PDU from the remote
*
* Builds a pdu frame as an I command.
*/
void llc_pdu_init_as_i_cmd(struct sk_buff *skb, u8 p_bit, u8 ns, u8 nr)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_I;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= (p_bit & LLC_I_PF_BIT_MASK); /* p/f bit */
pdu->ctrl_1 |= (ns << 1) & 0xFE; /* set N(S) in bits 2..8 */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
}
/**
* llc_pdu_init_as_rej_cmd - builds REJ PDU
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
* @nr: The seq. number of the expected I PDU from the remote
*
* Builds a pdu frame as a REJ command.
*/
void llc_pdu_init_as_rej_cmd(struct sk_buff *skb, u8 p_bit, u8 nr)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_CMD_REJ;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= p_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
}
/**
* llc_pdu_init_as_rnr_cmd - builds RNR pdu
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
* @nr: The seq. number of the expected I PDU from the remote
*
* Builds a pdu frame as an RNR command.
*/
void llc_pdu_init_as_rnr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_CMD_RNR;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= p_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
}
/**
* llc_pdu_init_as_rr_cmd - Builds RR pdu
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
* @nr: The seq. number of the expected I PDU from the remote
*
* Builds a pdu frame as an RR command.
*/
void llc_pdu_init_as_rr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_CMD_RR;
pdu->ctrl_2 = p_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
}
/**
* llc_pdu_init_as_sabme_cmd - builds SABME pdu
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
*
* Builds a pdu frame as an SABME command.
*/
void llc_pdu_init_as_sabme_cmd(struct sk_buff *skb, u8 p_bit)
{
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_2_PDU_CMD_SABME;
pdu->ctrl_1 |= ((p_bit & 1) << 4) & LLC_U_PF_BIT_MASK;
}
/**
* llc_pdu_init_as_dm_rsp - builds DM response pdu
* @skb: Address of the skb to build
* @f_bit: The F bit to set in the PDU
*
* Builds a pdu frame as a DM response.
*/
void llc_pdu_init_as_dm_rsp(struct sk_buff *skb, u8 f_bit)
{
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_2_PDU_RSP_DM;
pdu->ctrl_1 |= ((f_bit & 1) << 4) & LLC_U_PF_BIT_MASK;
}
/**
* llc_pdu_init_as_frmr_rsp - builds FRMR response PDU
* @skb: Address of the frame to build
* @prev_pdu: The rejected PDU frame
* @f_bit: The F bit to set in the PDU
* @vs: tx state vari value for the data link conn at the rejecting LLC
* @vr: rx state var value for the data link conn at the rejecting LLC
* @vzyxw: completely described in the IEEE Std 802.2 document (Pg 55)
*
* Builds a pdu frame as a FRMR response.
*/
void llc_pdu_init_as_frmr_rsp(struct sk_buff *skb, struct llc_pdu_sn *prev_pdu,
u8 f_bit, u8 vs, u8 vr, u8 vzyxw)
{
struct llc_frmr_info *frmr_info;
u8 prev_pf = 0;
u8 *ctrl;
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_2_PDU_RSP_FRMR;
pdu->ctrl_1 |= ((f_bit & 1) << 4) & LLC_U_PF_BIT_MASK;
frmr_info = (struct llc_frmr_info *)&pdu->ctrl_2;
ctrl = (u8 *)&prev_pdu->ctrl_1;
FRMR_INFO_SET_REJ_CNTRL(frmr_info,ctrl);
FRMR_INFO_SET_Vs(frmr_info, vs);
FRMR_INFO_SET_Vr(frmr_info, vr);
prev_pf = llc_pdu_get_pf_bit(prev_pdu);
FRMR_INFO_SET_C_R_BIT(frmr_info, prev_pf);
FRMR_INFO_SET_INVALID_PDU_CTRL_IND(frmr_info, vzyxw);
FRMR_INFO_SET_INVALID_PDU_INFO_IND(frmr_info, vzyxw);
FRMR_INFO_SET_PDU_INFO_2LONG_IND(frmr_info, vzyxw);
FRMR_INFO_SET_PDU_INVALID_Nr_IND(frmr_info, vzyxw);
FRMR_INFO_SET_PDU_INVALID_Ns_IND(frmr_info, vzyxw);
skb_put(skb, 5);
}
/**
* llc_pdu_init_as_rr_rsp - builds RR response pdu
* @skb: Address of the skb to build
* @f_bit: The F bit to set in the PDU
* @nr: The seq. number of the expected data PDU from the remote
*
* Builds a pdu frame as an RR response.
*/
void llc_pdu_init_as_rr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_RSP_RR;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= f_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
}
/**
* llc_pdu_init_as_rej_rsp - builds REJ response pdu
* @skb: Address of the skb to build
* @f_bit: The F bit to set in the PDU
* @nr: The seq. number of the expected data PDU from the remote
*
* Builds a pdu frame as a REJ response.
*/
void llc_pdu_init_as_rej_rsp(struct sk_buff *skb, u8 f_bit, u8 nr)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_RSP_REJ;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= f_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
}
/**
* llc_pdu_init_as_rnr_rsp - builds RNR response pdu
* @skb: Address of the frame to build
* @f_bit: The F bit to set in the PDU
* @nr: The seq. number of the expected data PDU from the remote
*
* Builds a pdu frame as an RNR response.
*/
void llc_pdu_init_as_rnr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr)
{
struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_RSP_RNR;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= f_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
}
/**
* llc_pdu_init_as_ua_rsp - builds UA response pdu
* @skb: Address of the frame to build
* @f_bit: The F bit to set in the PDU
*
* Builds a pdu frame as a UA response.
*/
void llc_pdu_init_as_ua_rsp(struct sk_buff *skb, u8 f_bit)
{
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_2_PDU_RSP_UA;
pdu->ctrl_1 |= ((f_bit & 1) << 4) & LLC_U_PF_BIT_MASK;
}
/**
* llc_pdu_decode_pdu_type - designates PDU type
* @skb: input skb that type of it must be designated.
* @type: type of PDU (output argument).
*
* This function designates type of PDU (I, S or U).
*/
static void llc_pdu_decode_pdu_type(struct sk_buff *skb, u8 *type)
{
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
if (pdu->ctrl_1 & 1) {
if ((pdu->ctrl_1 & LLC_PDU_TYPE_U) == LLC_PDU_TYPE_U)
*type = LLC_PDU_TYPE_U;
else
*type = LLC_PDU_TYPE_S;
} else
*type = LLC_PDU_TYPE_I;
}
/**
* llc_pdu_get_pf_bit - extracts p/f bit of input PDU
* @pdu: pointer to LLC header.
*
* This function extracts p/f bit of input PDU. at first examines type of
* PDU and then extracts p/f bit. Returns the p/f bit.
*/
static u8 llc_pdu_get_pf_bit(struct llc_pdu_sn *pdu)
{
u8 pdu_type;
u8 pf_bit = 0;
if (pdu->ctrl_1 & 1) {
if ((pdu->ctrl_1 & LLC_PDU_TYPE_U) == LLC_PDU_TYPE_U)
pdu_type = LLC_PDU_TYPE_U;
else
pdu_type = LLC_PDU_TYPE_S;
} else
pdu_type = LLC_PDU_TYPE_I;
switch (pdu_type) {
case LLC_PDU_TYPE_I:
case LLC_PDU_TYPE_S:
pf_bit = pdu->ctrl_2 & LLC_S_PF_BIT_MASK;
break;
case LLC_PDU_TYPE_U:
pf_bit = (pdu->ctrl_1 & LLC_U_PF_BIT_MASK) >> 4;
break;
}
return pf_bit;
}

267
net/llc/llc_proc.c Normal file
View File

@@ -0,0 +1,267 @@
/*
* proc_llc.c - proc interface for LLC
*
* Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
* 2002-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/errno.h>
#include <linux/seq_file.h>
#include <net/sock.h>
#include <net/llc.h>
#include <net/llc_c_ac.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_st.h>
#include <net/llc_conn.h>
static void llc_ui_format_mac(struct seq_file *seq, unsigned char *mac)
{
seq_printf(seq, "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
static struct sock *llc_get_sk_idx(loff_t pos)
{
struct list_head *sap_entry;
struct llc_sap *sap;
struct hlist_node *node;
struct sock *sk = NULL;
list_for_each(sap_entry, &llc_sap_list) {
sap = list_entry(sap_entry, struct llc_sap, node);
read_lock_bh(&sap->sk_list.lock);
sk_for_each(sk, node, &sap->sk_list.list) {
if (!pos)
goto found;
--pos;
}
read_unlock_bh(&sap->sk_list.lock);
}
sk = NULL;
found:
return sk;
}
static void *llc_seq_start(struct seq_file *seq, loff_t *pos)
{
loff_t l = *pos;
read_lock_bh(&llc_sap_list_lock);
return l ? llc_get_sk_idx(--l) : SEQ_START_TOKEN;
}
static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct sock* sk, *next;
struct llc_sock *llc;
struct llc_sap *sap;
++*pos;
if (v == SEQ_START_TOKEN) {
sk = llc_get_sk_idx(0);
goto out;
}
sk = v;
next = sk_next(sk);
if (next) {
sk = next;
goto out;
}
llc = llc_sk(sk);
sap = llc->sap;
read_unlock_bh(&sap->sk_list.lock);
sk = NULL;
for (;;) {
if (sap->node.next == &llc_sap_list)
break;
sap = list_entry(sap->node.next, struct llc_sap, node);
read_lock_bh(&sap->sk_list.lock);
if (!hlist_empty(&sap->sk_list.list)) {
sk = sk_head(&sap->sk_list.list);
break;
}
read_unlock_bh(&sap->sk_list.lock);
}
out:
return sk;
}
static void llc_seq_stop(struct seq_file *seq, void *v)
{
if (v && v != SEQ_START_TOKEN) {
struct sock *sk = v;
struct llc_sock *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
read_unlock_bh(&sap->sk_list.lock);
}
read_unlock_bh(&llc_sap_list_lock);
}
static int llc_seq_socket_show(struct seq_file *seq, void *v)
{
struct sock* sk;
struct llc_sock *llc;
if (v == SEQ_START_TOKEN) {
seq_puts(seq, "SKt Mc local_mac_sap remote_mac_sap "
" tx_queue rx_queue st uid link\n");
goto out;
}
sk = v;
llc = llc_sk(sk);
/* FIXME: check if the address is multicast */
seq_printf(seq, "%2X %2X ", sk->sk_type, 0);
if (llc->dev)
llc_ui_format_mac(seq, llc->dev->dev_addr);
else
seq_printf(seq, "00:00:00:00:00:00");
seq_printf(seq, "@%02X ", llc->sap->laddr.lsap);
llc_ui_format_mac(seq, llc->daddr.mac);
seq_printf(seq, "@%02X %8d %8d %2d %3d %4d\n", llc->daddr.lsap,
atomic_read(&sk->sk_wmem_alloc),
atomic_read(&sk->sk_rmem_alloc),
sk->sk_state,
sk->sk_socket ? SOCK_INODE(sk->sk_socket)->i_uid : -1,
llc->link);
out:
return 0;
}
static char *llc_conn_state_names[] = {
[LLC_CONN_STATE_ADM] = "adm",
[LLC_CONN_STATE_SETUP] = "setup",
[LLC_CONN_STATE_NORMAL] = "normal",
[LLC_CONN_STATE_BUSY] = "busy",
[LLC_CONN_STATE_REJ] = "rej",
[LLC_CONN_STATE_AWAIT] = "await",
[LLC_CONN_STATE_AWAIT_BUSY] = "await_busy",
[LLC_CONN_STATE_AWAIT_REJ] = "await_rej",
[LLC_CONN_STATE_D_CONN] = "d_conn",
[LLC_CONN_STATE_RESET] = "reset",
[LLC_CONN_STATE_ERROR] = "error",
[LLC_CONN_STATE_TEMP] = "temp",
};
static int llc_seq_core_show(struct seq_file *seq, void *v)
{
struct sock* sk;
struct llc_sock *llc;
if (v == SEQ_START_TOKEN) {
seq_puts(seq, "Connection list:\n"
"dsap state retr txw rxw pf ff sf df rs cs "
"tack tpfc trs tbs blog busr\n");
goto out;
}
sk = v;
llc = llc_sk(sk);
seq_printf(seq, " %02X %-10s %3d %3d %3d %2d %2d %2d %2d %2d %2d "
"%4d %4d %3d %3d %4d %4d\n",
llc->daddr.lsap, llc_conn_state_names[llc->state],
llc->retry_count, llc->k, llc->rw, llc->p_flag, llc->f_flag,
llc->s_flag, llc->data_flag, llc->remote_busy_flag,
llc->cause_flag, timer_pending(&llc->ack_timer.timer),
timer_pending(&llc->pf_cycle_timer.timer),
timer_pending(&llc->rej_sent_timer.timer),
timer_pending(&llc->busy_state_timer.timer),
!!sk->sk_backlog.tail, !!sock_owned_by_user(sk));
out:
return 0;
}
static struct seq_operations llc_seq_socket_ops = {
.start = llc_seq_start,
.next = llc_seq_next,
.stop = llc_seq_stop,
.show = llc_seq_socket_show,
};
static struct seq_operations llc_seq_core_ops = {
.start = llc_seq_start,
.next = llc_seq_next,
.stop = llc_seq_stop,
.show = llc_seq_core_show,
};
static int llc_seq_socket_open(struct inode *inode, struct file *file)
{
return seq_open(file, &llc_seq_socket_ops);
}
static int llc_seq_core_open(struct inode *inode, struct file *file)
{
return seq_open(file, &llc_seq_core_ops);
}
static struct file_operations llc_seq_socket_fops = {
.owner = THIS_MODULE,
.open = llc_seq_socket_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static struct file_operations llc_seq_core_fops = {
.owner = THIS_MODULE,
.open = llc_seq_core_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static struct proc_dir_entry *llc_proc_dir;
int __init llc_proc_init(void)
{
int rc = -ENOMEM;
struct proc_dir_entry *p;
llc_proc_dir = proc_mkdir("llc", proc_net);
if (!llc_proc_dir)
goto out;
llc_proc_dir->owner = THIS_MODULE;
p = create_proc_entry("socket", S_IRUGO, llc_proc_dir);
if (!p)
goto out_socket;
p->proc_fops = &llc_seq_socket_fops;
p = create_proc_entry("core", S_IRUGO, llc_proc_dir);
if (!p)
goto out_core;
p->proc_fops = &llc_seq_core_fops;
rc = 0;
out:
return rc;
out_core:
remove_proc_entry("socket", llc_proc_dir);
out_socket:
remove_proc_entry("llc", proc_net);
goto out;
}
void llc_proc_exit(void)
{
remove_proc_entry("socket", llc_proc_dir);
remove_proc_entry("core", llc_proc_dir);
remove_proc_entry("llc", proc_net);
}

205
net/llc/llc_s_ac.c Normal file
View File

@@ -0,0 +1,205 @@
/*
* llc_s_ac.c - actions performed during sap state transition.
*
* Description :
* Functions in this module are implementation of sap component actions.
* Details of actions can be found in IEEE-802.2 standard document.
* All functions have one sap and one event as input argument. All of
* them return 0 On success and 1 otherwise.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <net/llc.h>
#include <net/llc_pdu.h>
#include <net/llc_s_ac.h>
#include <net/llc_s_ev.h>
#include <net/llc_sap.h>
#include "llc_output.h"
/**
* llc_sap_action_unit_data_ind - forward UI PDU to network layer
* @sap: SAP
* @skb: the event to forward
*
* Received a UI PDU from MAC layer; forward to network layer as a
* UNITDATA INDICATION; verify our event is the kind we expect
*/
int llc_sap_action_unitdata_ind(struct llc_sap *sap, struct sk_buff *skb)
{
llc_sap_rtn_pdu(sap, skb);
return 0;
}
/**
* llc_sap_action_send_ui - sends UI PDU resp to UNITDATA REQ to MAC layer
* @sap: SAP
* @skb: the event to send
*
* Sends a UI PDU to the MAC layer in response to a UNITDATA REQUEST
* primitive from the network layer. Verifies event is a primitive type of
* event. Verify the primitive is a UNITDATA REQUEST.
*/
int llc_sap_action_send_ui(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
int rc;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, ev->saddr.lsap,
ev->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_ui_cmd(skb);
rc = llc_mac_hdr_init(skb, ev->saddr.mac, ev->daddr.mac);
if (!rc)
rc = dev_queue_xmit(skb);
return rc;
}
/**
* llc_sap_action_send_xid_c - send XID PDU as response to XID REQ
* @sap: SAP
* @skb: the event to send
*
* Send a XID command PDU to MAC layer in response to a XID REQUEST
* primitive from the network layer. Verify event is a primitive type
* event. Verify the primitive is a XID REQUEST.
*/
int llc_sap_action_send_xid_c(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
int rc;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, ev->saddr.lsap,
ev->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_xid_cmd(skb, LLC_XID_NULL_CLASS_2, 0);
rc = llc_mac_hdr_init(skb, ev->saddr.mac, ev->daddr.mac);
if (!rc)
rc = dev_queue_xmit(skb);
return rc;
}
/**
* llc_sap_action_send_xid_r - send XID PDU resp to MAC for received XID
* @sap: SAP
* @skb: the event to send
*
* Send XID response PDU to MAC in response to an earlier received XID
* command PDU. Verify event is a PDU type event
*/
int llc_sap_action_send_xid_r(struct llc_sap *sap, struct sk_buff *skb)
{
u8 mac_da[ETH_ALEN], mac_sa[ETH_ALEN], dsap;
int rc = 1;
struct sk_buff *nskb;
llc_pdu_decode_sa(skb, mac_da);
llc_pdu_decode_da(skb, mac_sa);
llc_pdu_decode_ssap(skb, &dsap);
nskb = llc_alloc_frame();
if (!nskb)
goto out;
nskb->dev = skb->dev;
llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, sap->laddr.lsap, dsap,
LLC_PDU_RSP);
llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 0);
rc = llc_mac_hdr_init(nskb, mac_sa, mac_da);
if (!rc)
rc = dev_queue_xmit(nskb);
out:
return rc;
}
/**
* llc_sap_action_send_test_c - send TEST PDU to MAC in resp to TEST REQ
* @sap: SAP
* @skb: the event to send
*
* Send a TEST command PDU to the MAC layer in response to a TEST REQUEST
* primitive from the network layer. Verify event is a primitive type
* event; verify the primitive is a TEST REQUEST.
*/
int llc_sap_action_send_test_c(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
int rc;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, ev->saddr.lsap,
ev->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_test_cmd(skb);
rc = llc_mac_hdr_init(skb, ev->saddr.mac, ev->daddr.mac);
if (!rc)
rc = dev_queue_xmit(skb);
return rc;
}
int llc_sap_action_send_test_r(struct llc_sap *sap, struct sk_buff *skb)
{
u8 mac_da[ETH_ALEN], mac_sa[ETH_ALEN], dsap;
struct sk_buff *nskb;
int rc = 1;
llc_pdu_decode_sa(skb, mac_da);
llc_pdu_decode_da(skb, mac_sa);
llc_pdu_decode_ssap(skb, &dsap);
nskb = llc_alloc_frame();
if (!nskb)
goto out;
nskb->dev = skb->dev;
llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, sap->laddr.lsap, dsap,
LLC_PDU_RSP);
llc_pdu_init_as_test_rsp(nskb, skb);
rc = llc_mac_hdr_init(nskb, mac_sa, mac_da);
if (!rc)
rc = dev_queue_xmit(nskb);
out:
return rc;
}
/**
* llc_sap_action_report_status - report data link status to layer mgmt
* @sap: SAP
* @skb: the event to send
*
* Report data link status to layer management. Verify our event is the
* kind we expect.
*/
int llc_sap_action_report_status(struct llc_sap *sap, struct sk_buff *skb)
{
return 0;
}
/**
* llc_sap_action_xid_ind - send XID PDU resp to net layer via XID IND
* @sap: SAP
* @skb: the event to send
*
* Send a XID response PDU to the network layer via a XID INDICATION
* primitive.
*/
int llc_sap_action_xid_ind(struct llc_sap *sap, struct sk_buff *skb)
{
llc_sap_rtn_pdu(sap, skb);
return 0;
}
/**
* llc_sap_action_test_ind - send TEST PDU to net layer via TEST IND
* @sap: SAP
* @skb: the event to send
*
* Send a TEST response PDU to the network layer via a TEST INDICATION
* primitive. Verify our event is a PDU type event.
*/
int llc_sap_action_test_ind(struct llc_sap *sap, struct sk_buff *skb)
{
llc_sap_rtn_pdu(sap, skb);
return 0;
}

115
net/llc/llc_s_ev.c Normal file
View File

@@ -0,0 +1,115 @@
/*
* llc_s_ev.c - Defines SAP component events
*
* The followed event functions are SAP component events which are described
* in 802.2 LLC protocol standard document.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/socket.h>
#include <net/sock.h>
#include <net/llc_if.h>
#include <net/llc_s_ev.h>
#include <net/llc_pdu.h>
int llc_sap_ev_activation_req(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
return ev->type == LLC_SAP_EV_TYPE_SIMPLE &&
ev->prim_type == LLC_SAP_EV_ACTIVATION_REQ ? 0 : 1;
}
int llc_sap_ev_rx_ui(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return ev->type == LLC_SAP_EV_TYPE_PDU && LLC_PDU_IS_CMD(pdu) &&
LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_UI ? 0 : 1;
}
int llc_sap_ev_unitdata_req(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
return ev->type == LLC_SAP_EV_TYPE_PRIM &&
ev->prim == LLC_DATAUNIT_PRIM &&
ev->prim_type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_sap_ev_xid_req(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
return ev->type == LLC_SAP_EV_TYPE_PRIM &&
ev->prim == LLC_XID_PRIM &&
ev->prim_type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_sap_ev_rx_xid_c(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return ev->type == LLC_SAP_EV_TYPE_PDU && LLC_PDU_IS_CMD(pdu) &&
LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID ? 0 : 1;
}
int llc_sap_ev_rx_xid_r(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return ev->type == LLC_SAP_EV_TYPE_PDU && LLC_PDU_IS_RSP(pdu) &&
LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID ? 0 : 1;
}
int llc_sap_ev_test_req(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
return ev->type == LLC_SAP_EV_TYPE_PRIM &&
ev->prim == LLC_TEST_PRIM &&
ev->prim_type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_sap_ev_rx_test_c(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return ev->type == LLC_SAP_EV_TYPE_PDU && LLC_PDU_IS_CMD(pdu) &&
LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST ? 0 : 1;
}
int llc_sap_ev_rx_test_r(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return ev->type == LLC_SAP_EV_TYPE_PDU && LLC_PDU_IS_RSP(pdu) &&
LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_TEST ? 0 : 1;
}
int llc_sap_ev_deactivation_req(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
return ev->type == LLC_SAP_EV_TYPE_SIMPLE &&
ev->prim_type == LLC_SAP_EV_DEACTIVATION_REQ ? 0 : 1;
}

183
net/llc/llc_s_st.c Normal file
View File

@@ -0,0 +1,183 @@
/*
* llc_s_st.c - Defines SAP component state machine transitions.
*
* The followed transitions are SAP component state machine transitions
* which are described in 802.2 LLC protocol standard document.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/types.h>
#include <net/llc_if.h>
#include <net/llc_s_ev.h>
#include <net/llc_s_ac.h>
#include <net/llc_s_st.h>
/* dummy last-transition indicator; common to all state transition groups
* last entry for this state
* all members are zeros, .bss zeroes it
*/
static struct llc_sap_state_trans llc_sap_state_trans_end;
/* state LLC_SAP_STATE_INACTIVE transition for
* LLC_SAP_EV_ACTIVATION_REQ event
*/
static llc_sap_action_t llc_sap_inactive_state_actions_1[] = {
[0] = llc_sap_action_report_status,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_inactive_state_trans_1 = {
.ev = llc_sap_ev_activation_req,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_inactive_state_actions_1,
};
/* array of pointers; one to each transition */
static struct llc_sap_state_trans *llc_sap_inactive_state_transitions[] = {
[0] = &llc_sap_inactive_state_trans_1,
[1] = &llc_sap_state_trans_end,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_UI event */
static llc_sap_action_t llc_sap_active_state_actions_1[] = {
[0] = llc_sap_action_unitdata_ind,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_1 = {
.ev = llc_sap_ev_rx_ui,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_1,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_UNITDATA_REQ event */
static llc_sap_action_t llc_sap_active_state_actions_2[] = {
[0] = llc_sap_action_send_ui,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_2 = {
.ev = llc_sap_ev_unitdata_req,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_2,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_XID_REQ event */
static llc_sap_action_t llc_sap_active_state_actions_3[] = {
[0] = llc_sap_action_send_xid_c,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_3 = {
.ev = llc_sap_ev_xid_req,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_3,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_XID_C event */
static llc_sap_action_t llc_sap_active_state_actions_4[] = {
[0] = llc_sap_action_send_xid_r,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_4 = {
.ev = llc_sap_ev_rx_xid_c,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_4,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_XID_R event */
static llc_sap_action_t llc_sap_active_state_actions_5[] = {
[0] = llc_sap_action_xid_ind,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_5 = {
.ev = llc_sap_ev_rx_xid_r,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_5,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_TEST_REQ event */
static llc_sap_action_t llc_sap_active_state_actions_6[] = {
[0] = llc_sap_action_send_test_c,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_6 = {
.ev = llc_sap_ev_test_req,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_6,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_TEST_C event */
static llc_sap_action_t llc_sap_active_state_actions_7[] = {
[0] = llc_sap_action_send_test_r,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_7 = {
.ev = llc_sap_ev_rx_test_c,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_7
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_TEST_R event */
static llc_sap_action_t llc_sap_active_state_actions_8[] = {
[0] = llc_sap_action_test_ind,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_8 = {
.ev = llc_sap_ev_rx_test_r,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_8,
};
/* state LLC_SAP_STATE_ACTIVE transition for
* LLC_SAP_EV_DEACTIVATION_REQ event
*/
static llc_sap_action_t llc_sap_active_state_actions_9[] = {
[0] = llc_sap_action_report_status,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_9 = {
.ev = llc_sap_ev_deactivation_req,
.next_state = LLC_SAP_STATE_INACTIVE,
.ev_actions = llc_sap_active_state_actions_9
};
/* array of pointers; one to each transition */
static struct llc_sap_state_trans *llc_sap_active_state_transitions[] = {
[0] = &llc_sap_active_state_trans_2,
[1] = &llc_sap_active_state_trans_1,
[2] = &llc_sap_active_state_trans_3,
[3] = &llc_sap_active_state_trans_4,
[4] = &llc_sap_active_state_trans_5,
[5] = &llc_sap_active_state_trans_6,
[6] = &llc_sap_active_state_trans_7,
[7] = &llc_sap_active_state_trans_8,
[8] = &llc_sap_active_state_trans_9,
[9] = &llc_sap_state_trans_end,
};
/* SAP state transition table */
struct llc_sap_state llc_sap_state_table[LLC_NR_SAP_STATES] = {
[LLC_SAP_STATE_INACTIVE - 1] = {
.curr_state = LLC_SAP_STATE_INACTIVE,
.transitions = llc_sap_inactive_state_transitions,
},
[LLC_SAP_STATE_ACTIVE - 1] = {
.curr_state = LLC_SAP_STATE_ACTIVE,
.transitions = llc_sap_active_state_transitions,
},
};

316
net/llc/llc_sap.c Normal file
View File

@@ -0,0 +1,316 @@
/*
* llc_sap.c - driver routines for SAP component.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <net/llc.h>
#include <net/llc_if.h>
#include <net/llc_conn.h>
#include <net/llc_pdu.h>
#include <net/llc_sap.h>
#include <net/llc_s_ac.h>
#include <net/llc_s_ev.h>
#include <net/llc_s_st.h>
#include <net/sock.h>
#include <linux/tcp.h>
#include <linux/llc.h>
/**
* llc_alloc_frame - allocates sk_buff for frame
*
* Allocates an sk_buff for frame and initializes sk_buff fields.
* Returns allocated skb or %NULL when out of memory.
*/
struct sk_buff *llc_alloc_frame(void)
{
struct sk_buff *skb = alloc_skb(128, GFP_ATOMIC);
if (skb) {
skb_reserve(skb, 50);
skb->nh.raw = skb->h.raw = skb->data;
skb->protocol = htons(ETH_P_802_2);
skb->dev = dev_base->next;
skb->mac.raw = skb->head;
}
return skb;
}
void llc_save_primitive(struct sk_buff* skb, u8 prim)
{
struct sockaddr_llc *addr = llc_ui_skb_cb(skb);
/* save primitive for use by the user. */
addr->sllc_family = skb->sk->sk_family;
addr->sllc_arphrd = skb->dev->type;
addr->sllc_test = prim == LLC_TEST_PRIM;
addr->sllc_xid = prim == LLC_XID_PRIM;
addr->sllc_ua = prim == LLC_DATAUNIT_PRIM;
llc_pdu_decode_sa(skb, addr->sllc_mac);
llc_pdu_decode_ssap(skb, &addr->sllc_sap);
}
/**
* llc_sap_rtn_pdu - Informs upper layer on rx of an UI, XID or TEST pdu.
* @sap: pointer to SAP
* @skb: received pdu
*/
void llc_sap_rtn_pdu(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
switch (LLC_U_PDU_RSP(pdu)) {
case LLC_1_PDU_CMD_TEST:
ev->prim = LLC_TEST_PRIM; break;
case LLC_1_PDU_CMD_XID:
ev->prim = LLC_XID_PRIM; break;
case LLC_1_PDU_CMD_UI:
ev->prim = LLC_DATAUNIT_PRIM; break;
}
ev->ind_cfm_flag = LLC_IND;
}
/**
* llc_find_sap_trans - finds transition for event
* @sap: pointer to SAP
* @skb: happened event
*
* This function finds transition that matches with happened event.
* Returns the pointer to found transition on success or %NULL for
* failure.
*/
static struct llc_sap_state_trans *llc_find_sap_trans(struct llc_sap *sap,
struct sk_buff* skb)
{
int i = 0;
struct llc_sap_state_trans *rc = NULL;
struct llc_sap_state_trans **next_trans;
struct llc_sap_state *curr_state = &llc_sap_state_table[sap->state - 1];
/*
* Search thru events for this state until list exhausted or until
* its obvious the event is not valid for the current state
*/
for (next_trans = curr_state->transitions; next_trans[i]->ev; i++)
if (!next_trans[i]->ev(sap, skb)) {
rc = next_trans[i]; /* got event match; return it */
break;
}
return rc;
}
/**
* llc_exec_sap_trans_actions - execute actions related to event
* @sap: pointer to SAP
* @trans: pointer to transition that it's actions must be performed
* @skb: happened event.
*
* This function executes actions that is related to happened event.
* Returns 0 for success and 1 for failure of at least one action.
*/
static int llc_exec_sap_trans_actions(struct llc_sap *sap,
struct llc_sap_state_trans *trans,
struct sk_buff *skb)
{
int rc = 0;
llc_sap_action_t *next_action = trans->ev_actions;
for (; next_action && *next_action; next_action++)
if ((*next_action)(sap, skb))
rc = 1;
return rc;
}
/**
* llc_sap_next_state - finds transition, execs actions & change SAP state
* @sap: pointer to SAP
* @skb: happened event
*
* This function finds transition that matches with happened event, then
* executes related actions and finally changes state of SAP. It returns
* 0 on success and 1 for failure.
*/
static int llc_sap_next_state(struct llc_sap *sap, struct sk_buff *skb)
{
int rc = 1;
struct llc_sap_state_trans *trans;
if (sap->state > LLC_NR_SAP_STATES)
goto out;
trans = llc_find_sap_trans(sap, skb);
if (!trans)
goto out;
/*
* Got the state to which we next transition; perform the actions
* associated with this transition before actually transitioning to the
* next state
*/
rc = llc_exec_sap_trans_actions(sap, trans, skb);
if (rc)
goto out;
/*
* Transition SAP to next state if all actions execute successfully
*/
sap->state = trans->next_state;
out:
return rc;
}
/**
* llc_sap_state_process - sends event to SAP state machine
* @sap: sap to use
* @skb: pointer to occurred event
*
* After executing actions of the event, upper layer will be indicated
* if needed(on receiving an UI frame). sk can be null for the
* datalink_proto case.
*/
static void llc_sap_state_process(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
/*
* We have to hold the skb, because llc_sap_next_state
* will kfree it in the sending path and we need to
* look at the skb->cb, where we encode llc_sap_state_ev.
*/
skb_get(skb);
ev->ind_cfm_flag = 0;
llc_sap_next_state(sap, skb);
if (ev->ind_cfm_flag == LLC_IND) {
if (skb->sk->sk_state == TCP_LISTEN)
kfree_skb(skb);
else {
llc_save_primitive(skb, ev->prim);
/* queue skb to the user. */
if (sock_queue_rcv_skb(skb->sk, skb))
kfree_skb(skb);
}
}
kfree_skb(skb);
}
/**
* llc_build_and_send_test_pkt - TEST interface for upper layers.
* @sap: sap to use
* @skb: packet to send
* @dmac: destination mac address
* @dsap: destination sap
*
* This function is called when upper layer wants to send a TEST pdu.
* Returns 0 for success, 1 otherwise.
*/
void llc_build_and_send_test_pkt(struct llc_sap *sap,
struct sk_buff *skb, u8 *dmac, u8 dsap)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
ev->saddr.lsap = sap->laddr.lsap;
ev->daddr.lsap = dsap;
memcpy(ev->saddr.mac, skb->dev->dev_addr, IFHWADDRLEN);
memcpy(ev->daddr.mac, dmac, IFHWADDRLEN);
ev->type = LLC_SAP_EV_TYPE_PRIM;
ev->prim = LLC_TEST_PRIM;
ev->prim_type = LLC_PRIM_TYPE_REQ;
llc_sap_state_process(sap, skb);
}
/**
* llc_build_and_send_xid_pkt - XID interface for upper layers
* @sap: sap to use
* @skb: packet to send
* @dmac: destination mac address
* @dsap: destination sap
*
* This function is called when upper layer wants to send a XID pdu.
* Returns 0 for success, 1 otherwise.
*/
void llc_build_and_send_xid_pkt(struct llc_sap *sap, struct sk_buff *skb,
u8 *dmac, u8 dsap)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
ev->saddr.lsap = sap->laddr.lsap;
ev->daddr.lsap = dsap;
memcpy(ev->saddr.mac, skb->dev->dev_addr, IFHWADDRLEN);
memcpy(ev->daddr.mac, dmac, IFHWADDRLEN);
ev->type = LLC_SAP_EV_TYPE_PRIM;
ev->prim = LLC_XID_PRIM;
ev->prim_type = LLC_PRIM_TYPE_REQ;
llc_sap_state_process(sap, skb);
}
/**
* llc_sap_rcv - sends received pdus to the sap state machine
* @sap: current sap component structure.
* @skb: received frame.
*
* Sends received pdus to the sap state machine.
*/
static void llc_sap_rcv(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
ev->type = LLC_SAP_EV_TYPE_PDU;
ev->reason = 0;
llc_sap_state_process(sap, skb);
}
/**
* llc_lookup_dgram - Finds dgram socket for the local sap/mac
* @sap: SAP
* @laddr: address of local LLC (MAC + SAP)
*
* Search socket list of the SAP and finds connection using the local
* mac, and local sap. Returns pointer for socket found, %NULL otherwise.
*/
static struct sock *llc_lookup_dgram(struct llc_sap *sap,
struct llc_addr *laddr)
{
struct sock *rc;
struct hlist_node *node;
read_lock_bh(&sap->sk_list.lock);
sk_for_each(rc, node, &sap->sk_list.list) {
struct llc_sock *llc = llc_sk(rc);
if (rc->sk_type == SOCK_DGRAM &&
llc->laddr.lsap == laddr->lsap &&
llc_mac_match(llc->laddr.mac, laddr->mac)) {
sock_hold(rc);
goto found;
}
}
rc = NULL;
found:
read_unlock_bh(&sap->sk_list.lock);
return rc;
}
void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb)
{
struct llc_addr laddr;
struct sock *sk;
llc_pdu_decode_da(skb, laddr.mac);
llc_pdu_decode_dsap(skb, &laddr.lsap);
sk = llc_lookup_dgram(sap, &laddr);
if (sk) {
skb->sk = sk;
llc_sap_rcv(sap, skb);
sock_put(sk);
} else
kfree_skb(skb);
}

713
net/llc/llc_station.c Normal file
View File

@@ -0,0 +1,713 @@
/*
* llc_station.c - station component of LLC
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <net/llc.h>
#include <net/llc_sap.h>
#include <net/llc_conn.h>
#include <net/llc_c_ac.h>
#include <net/llc_s_ac.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_st.h>
#include <net/llc_s_ev.h>
#include <net/llc_s_st.h>
#include <net/llc_pdu.h>
/**
* struct llc_station - LLC station component
*
* SAP and connection resource manager, one per adapter.
*
* @state - state of station
* @xid_r_count - XID response PDU counter
* @mac_sa - MAC source address
* @sap_list - list of related SAPs
* @ev_q - events entering state mach.
* @mac_pdu_q - PDUs ready to send to MAC
*/
struct llc_station {
u8 state;
u8 xid_r_count;
struct timer_list ack_timer;
u8 retry_count;
u8 maximum_retry;
struct {
struct sk_buff_head list;
spinlock_t lock;
} ev_q;
struct sk_buff_head mac_pdu_q;
};
/* Types of events (possible values in 'ev->type') */
#define LLC_STATION_EV_TYPE_SIMPLE 1
#define LLC_STATION_EV_TYPE_CONDITION 2
#define LLC_STATION_EV_TYPE_PRIM 3
#define LLC_STATION_EV_TYPE_PDU 4 /* command/response PDU */
#define LLC_STATION_EV_TYPE_ACK_TMR 5
#define LLC_STATION_EV_TYPE_RPT_STATUS 6
/* Events */
#define LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK 1
#define LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK 2
#define LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY 3
#define LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY 4
#define LLC_STATION_EV_RX_NULL_DSAP_XID_C 5
#define LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ 6
#define LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ 7
#define LLC_STATION_EV_RX_NULL_DSAP_TEST_C 8
#define LLC_STATION_EV_DISABLE_REQ 9
struct llc_station_state_ev {
u8 type;
u8 prim;
u8 prim_type;
u8 reason;
struct list_head node; /* node in station->ev_q.list */
};
static __inline__ struct llc_station_state_ev *
llc_station_ev(struct sk_buff *skb)
{
return (struct llc_station_state_ev *)skb->cb;
}
typedef int (*llc_station_ev_t)(struct sk_buff *skb);
#define LLC_STATION_STATE_DOWN 1 /* initial state */
#define LLC_STATION_STATE_DUP_ADDR_CHK 2
#define LLC_STATION_STATE_UP 3
#define LLC_NBR_STATION_STATES 3 /* size of state table */
typedef int (*llc_station_action_t)(struct sk_buff *skb);
/* Station component state table structure */
struct llc_station_state_trans {
llc_station_ev_t ev;
u8 next_state;
llc_station_action_t *ev_actions;
};
struct llc_station_state {
u8 curr_state;
struct llc_station_state_trans **transitions;
};
static struct llc_station llc_main_station;
static int llc_stat_ev_enable_with_dup_addr_check(struct sk_buff *skb)
{
struct llc_station_state_ev *ev = llc_station_ev(skb);
return ev->type == LLC_STATION_EV_TYPE_SIMPLE &&
ev->prim_type ==
LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK ? 0 : 1;
}
static int llc_stat_ev_enable_without_dup_addr_check(struct sk_buff *skb)
{
struct llc_station_state_ev *ev = llc_station_ev(skb);
return ev->type == LLC_STATION_EV_TYPE_SIMPLE &&
ev->prim_type ==
LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK ? 0 : 1;
}
static int llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry(struct sk_buff *skb)
{
struct llc_station_state_ev *ev = llc_station_ev(skb);
return ev->type == LLC_STATION_EV_TYPE_ACK_TMR &&
llc_main_station.retry_count <
llc_main_station.maximum_retry ? 0 : 1;
}
static int llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry(struct sk_buff *skb)
{
struct llc_station_state_ev *ev = llc_station_ev(skb);
return ev->type == LLC_STATION_EV_TYPE_ACK_TMR &&
llc_main_station.retry_count ==
llc_main_station.maximum_retry ? 0 : 1;
}
static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb)
{
struct llc_station_state_ev *ev = llc_station_ev(skb);
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return ev->type == LLC_STATION_EV_TYPE_PDU &&
LLC_PDU_IS_CMD(pdu) && /* command PDU */
LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID &&
!pdu->dsap ? 0 : 1; /* NULL DSAP value */
}
static int llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq(struct sk_buff *skb)
{
struct llc_station_state_ev *ev = llc_station_ev(skb);
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return ev->type == LLC_STATION_EV_TYPE_PDU &&
LLC_PDU_IS_RSP(pdu) && /* response PDU */
LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID &&
!pdu->dsap && /* NULL DSAP value */
!llc_main_station.xid_r_count ? 0 : 1;
}
static int llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq(struct sk_buff *skb)
{
struct llc_station_state_ev *ev = llc_station_ev(skb);
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return ev->type == LLC_STATION_EV_TYPE_PDU &&
LLC_PDU_IS_RSP(pdu) && /* response PDU */
LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID &&
!pdu->dsap && /* NULL DSAP value */
llc_main_station.xid_r_count == 1 ? 0 : 1;
}
static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb)
{
struct llc_station_state_ev *ev = llc_station_ev(skb);
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
return ev->type == LLC_STATION_EV_TYPE_PDU &&
LLC_PDU_IS_CMD(pdu) && /* command PDU */
LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST &&
!pdu->dsap ? 0 : 1; /* NULL DSAP */
}
static int llc_stat_ev_disable_req(struct sk_buff *skb)
{
struct llc_station_state_ev *ev = llc_station_ev(skb);
return ev->type == LLC_STATION_EV_TYPE_PRIM &&
ev->prim == LLC_DISABLE_PRIM &&
ev->prim_type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
/**
* llc_station_send_pdu - queues PDU to send
* @skb: Address of the PDU
*
* Queues a PDU to send to the MAC layer.
*/
static void llc_station_send_pdu(struct sk_buff *skb)
{
skb_queue_tail(&llc_main_station.mac_pdu_q, skb);
while ((skb = skb_dequeue(&llc_main_station.mac_pdu_q)) != NULL)
if (dev_queue_xmit(skb))
break;
}
static int llc_station_ac_start_ack_timer(struct sk_buff *skb)
{
mod_timer(&llc_main_station.ack_timer, jiffies + LLC_ACK_TIME * HZ);
return 0;
}
static int llc_station_ac_set_retry_cnt_0(struct sk_buff *skb)
{
llc_main_station.retry_count = 0;
return 0;
}
static int llc_station_ac_inc_retry_cnt_by_1(struct sk_buff *skb)
{
llc_main_station.retry_count++;
return 0;
}
static int llc_station_ac_set_xid_r_cnt_0(struct sk_buff *skb)
{
llc_main_station.xid_r_count = 0;
return 0;
}
static int llc_station_ac_inc_xid_r_cnt_by_1(struct sk_buff *skb)
{
llc_main_station.xid_r_count++;
return 0;
}
static int llc_station_ac_send_null_dsap_xid_c(struct sk_buff *skb)
{
int rc = 1;
struct sk_buff *nskb = llc_alloc_frame();
if (!nskb)
goto out;
llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, 0, LLC_PDU_CMD);
llc_pdu_init_as_xid_cmd(nskb, LLC_XID_NULL_CLASS_2, 127);
rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, llc_station_mac_sa);
if (rc)
goto free;
llc_station_send_pdu(nskb);
out:
return rc;
free:
kfree_skb(skb);
goto out;
}
static int llc_station_ac_send_xid_r(struct sk_buff *skb)
{
u8 mac_da[ETH_ALEN], dsap;
int rc = 1;
struct sk_buff* nskb = llc_alloc_frame();
if (!nskb)
goto out;
rc = 0;
nskb->dev = skb->dev;
llc_pdu_decode_sa(skb, mac_da);
llc_pdu_decode_ssap(skb, &dsap);
llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127);
rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, mac_da);
if (rc)
goto free;
llc_station_send_pdu(nskb);
out:
return rc;
free:
kfree_skb(skb);
goto out;
}
static int llc_station_ac_send_test_r(struct sk_buff *skb)
{
u8 mac_da[ETH_ALEN], dsap;
int rc = 1;
struct sk_buff *nskb = llc_alloc_frame();
if (!nskb)
goto out;
rc = 0;
nskb->dev = skb->dev;
llc_pdu_decode_sa(skb, mac_da);
llc_pdu_decode_ssap(skb, &dsap);
llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
llc_pdu_init_as_test_rsp(nskb, skb);
rc = llc_mac_hdr_init(nskb, llc_station_mac_sa, mac_da);
if (rc)
goto free;
llc_station_send_pdu(nskb);
out:
return rc;
free:
kfree_skb(skb);
goto out;
}
static int llc_station_ac_report_status(struct sk_buff *skb)
{
return 0;
}
/* COMMON STATION STATE transitions */
/* dummy last-transition indicator; common to all state transition groups
* last entry for this state
* all members are zeros, .bss zeroes it
*/
static struct llc_station_state_trans llc_stat_state_trans_end;
/* DOWN STATE transitions */
/* state transition for LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK event */
static llc_station_action_t llc_stat_down_state_actions_1[] = {
[0] = llc_station_ac_start_ack_timer,
[1] = llc_station_ac_set_retry_cnt_0,
[2] = llc_station_ac_set_xid_r_cnt_0,
[3] = llc_station_ac_send_null_dsap_xid_c,
[4] = NULL,
};
static struct llc_station_state_trans llc_stat_down_state_trans_1 = {
.ev = llc_stat_ev_enable_with_dup_addr_check,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_down_state_actions_1,
};
/* state transition for LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK event */
static llc_station_action_t llc_stat_down_state_actions_2[] = {
[0] = llc_station_ac_report_status, /* STATION UP */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_down_state_trans_2 = {
.ev = llc_stat_ev_enable_without_dup_addr_check,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_down_state_actions_2,
};
/* array of pointers; one to each transition */
static struct llc_station_state_trans *llc_stat_dwn_state_trans[] = {
[0] = &llc_stat_down_state_trans_1,
[1] = &llc_stat_down_state_trans_2,
[2] = &llc_stat_state_trans_end,
};
/* UP STATE transitions */
/* state transition for LLC_STATION_EV_DISABLE_REQ event */
static llc_station_action_t llc_stat_up_state_actions_1[] = {
[0] = llc_station_ac_report_status, /* STATION DOWN */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_up_state_trans_1 = {
.ev = llc_stat_ev_disable_req,
.next_state = LLC_STATION_STATE_DOWN,
.ev_actions = llc_stat_up_state_actions_1,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */
static llc_station_action_t llc_stat_up_state_actions_2[] = {
[0] = llc_station_ac_send_xid_r,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_up_state_trans_2 = {
.ev = llc_stat_ev_rx_null_dsap_xid_c,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_up_state_actions_2,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */
static llc_station_action_t llc_stat_up_state_actions_3[] = {
[0] = llc_station_ac_send_test_r,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_up_state_trans_3 = {
.ev = llc_stat_ev_rx_null_dsap_test_c,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_up_state_actions_3,
};
/* array of pointers; one to each transition */
static struct llc_station_state_trans *llc_stat_up_state_trans [] = {
[0] = &llc_stat_up_state_trans_1,
[1] = &llc_stat_up_state_trans_2,
[2] = &llc_stat_up_state_trans_3,
[3] = &llc_stat_state_trans_end,
};
/* DUP ADDR CHK STATE transitions */
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_1[] = {
[0] = llc_station_ac_inc_xid_r_cnt_by_1,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_1 = {
.ev = llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_dupaddr_state_actions_1,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_2[] = {
[0] = llc_station_ac_report_status, /* DUPLICATE ADDRESS FOUND */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_2 = {
.ev = llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq,
.next_state = LLC_STATION_STATE_DOWN,
.ev_actions = llc_stat_dupaddr_state_actions_2,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */
static llc_station_action_t llc_stat_dupaddr_state_actions_3[] = {
[0] = llc_station_ac_send_xid_r,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_3 = {
.ev = llc_stat_ev_rx_null_dsap_xid_c,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_dupaddr_state_actions_3,
};
/* state transition for LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_4[] = {
[0] = llc_station_ac_start_ack_timer,
[1] = llc_station_ac_inc_retry_cnt_by_1,
[2] = llc_station_ac_set_xid_r_cnt_0,
[3] = llc_station_ac_send_null_dsap_xid_c,
[4] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_4 = {
.ev = llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_dupaddr_state_actions_4,
};
/* state transition for LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_5[] = {
[0] = llc_station_ac_report_status, /* STATION UP */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_5 = {
.ev = llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_dupaddr_state_actions_5,
};
/* state transition for LLC_STATION_EV_DISABLE_REQ event */
static llc_station_action_t llc_stat_dupaddr_state_actions_6[] = {
[0] = llc_station_ac_report_status, /* STATION DOWN */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_6 = {
.ev = llc_stat_ev_disable_req,
.next_state = LLC_STATION_STATE_DOWN,
.ev_actions = llc_stat_dupaddr_state_actions_6,
};
/* array of pointers; one to each transition */
static struct llc_station_state_trans *llc_stat_dupaddr_state_trans[] = {
[0] = &llc_stat_dupaddr_state_trans_6, /* Request */
[1] = &llc_stat_dupaddr_state_trans_4, /* Timer */
[2] = &llc_stat_dupaddr_state_trans_5,
[3] = &llc_stat_dupaddr_state_trans_1, /* Receive frame */
[4] = &llc_stat_dupaddr_state_trans_2,
[5] = &llc_stat_dupaddr_state_trans_3,
[6] = &llc_stat_state_trans_end,
};
static struct llc_station_state
llc_station_state_table[LLC_NBR_STATION_STATES] = {
[LLC_STATION_STATE_DOWN - 1] = {
.curr_state = LLC_STATION_STATE_DOWN,
.transitions = llc_stat_dwn_state_trans,
},
[LLC_STATION_STATE_DUP_ADDR_CHK - 1] = {
.curr_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.transitions = llc_stat_dupaddr_state_trans,
},
[LLC_STATION_STATE_UP - 1] = {
.curr_state = LLC_STATION_STATE_UP,
.transitions = llc_stat_up_state_trans,
},
};
/**
* llc_exec_station_trans_actions - executes actions for transition
* @trans: Address of the transition
* @skb: Address of the event that caused the transition
*
* Executes actions of a transition of the station state machine. Returns
* 0 if all actions complete successfully, nonzero otherwise.
*/
static u16 llc_exec_station_trans_actions(struct llc_station_state_trans *trans,
struct sk_buff *skb)
{
u16 rc = 0;
llc_station_action_t *next_action = trans->ev_actions;
for (; next_action && *next_action; next_action++)
if ((*next_action)(skb))
rc = 1;
return rc;
}
/**
* llc_find_station_trans - finds transition for this event
* @skb: Address of the event
*
* Search thru events of the current state of the station until list
* exhausted or it's obvious that the event is not valid for the current
* state. Returns the address of the transition if cound, %NULL otherwise.
*/
static struct llc_station_state_trans *
llc_find_station_trans(struct sk_buff *skb)
{
int i = 0;
struct llc_station_state_trans *rc = NULL;
struct llc_station_state_trans **next_trans;
struct llc_station_state *curr_state =
&llc_station_state_table[llc_main_station.state - 1];
for (next_trans = curr_state->transitions; next_trans[i]->ev; i++)
if (!next_trans[i]->ev(skb)) {
rc = next_trans[i];
break;
}
return rc;
}
/**
* llc_station_free_ev - frees an event
* @skb: Address of the event
*
* Frees an event.
*/
static void llc_station_free_ev(struct sk_buff *skb)
{
struct llc_station_state_ev *ev = llc_station_ev(skb);
if (ev->type == LLC_STATION_EV_TYPE_PDU)
kfree_skb(skb);
}
/**
* llc_station_next_state - processes event and goes to the next state
* @skb: Address of the event
*
* Processes an event, executes any transitions related to that event and
* updates the state of the station.
*/
static u16 llc_station_next_state(struct sk_buff *skb)
{
u16 rc = 1;
struct llc_station_state_trans *trans;
if (llc_main_station.state > LLC_NBR_STATION_STATES)
goto out;
trans = llc_find_station_trans(skb);
if (trans) {
/* got the state to which we next transition; perform the
* actions associated with this transition before actually
* transitioning to the next state
*/
rc = llc_exec_station_trans_actions(trans, skb);
if (!rc)
/* transition station to next state if all actions
* execute successfully; done; wait for next event
*/
llc_main_station.state = trans->next_state;
} else
/* event not recognized in current state; re-queue it for
* processing again at a later time; return failure
*/
rc = 0;
out:
llc_station_free_ev(skb);
return rc;
}
/**
* llc_station_service_events - service events in the queue
*
* Get an event from the station event queue (if any); attempt to service
* the event; if event serviced, get the next event (if any) on the event
* queue; if event not service, re-queue the event on the event queue and
* attempt to service the next event; when serviced all events in queue,
* finished; if don't transition to different state, just service all
* events once; if transition to new state, service all events again.
* Caller must hold llc_main_station.ev_q.lock.
*/
static void llc_station_service_events(void)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&llc_main_station.ev_q.list)) != NULL)
llc_station_next_state(skb);
}
/**
* llc_station_state_process: queue event and try to process queue.
* @skb: Address of the event
*
* Queues an event (on the station event queue) for handling by the
* station state machine and attempts to process any queued-up events.
*/
static void llc_station_state_process(struct sk_buff *skb)
{
spin_lock_bh(&llc_main_station.ev_q.lock);
skb_queue_tail(&llc_main_station.ev_q.list, skb);
llc_station_service_events();
spin_unlock_bh(&llc_main_station.ev_q.lock);
}
static void llc_station_ack_tmr_cb(unsigned long timeout_data)
{
struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC);
if (skb) {
struct llc_station_state_ev *ev = llc_station_ev(skb);
ev->type = LLC_STATION_EV_TYPE_ACK_TMR;
llc_station_state_process(skb);
}
}
/*
* llc_station_rcv - send received pdu to the station state machine
* @skb: received frame.
*
* Sends data unit to station state machine.
*/
static void llc_station_rcv(struct sk_buff *skb)
{
struct llc_station_state_ev *ev = llc_station_ev(skb);
ev->type = LLC_STATION_EV_TYPE_PDU;
ev->reason = 0;
llc_station_state_process(skb);
}
int __init llc_station_init(void)
{
u16 rc = -ENOBUFS;
struct sk_buff *skb;
struct llc_station_state_ev *ev;
skb_queue_head_init(&llc_main_station.mac_pdu_q);
skb_queue_head_init(&llc_main_station.ev_q.list);
spin_lock_init(&llc_main_station.ev_q.lock);
init_timer(&llc_main_station.ack_timer);
llc_main_station.ack_timer.data = (unsigned long)&llc_main_station;
llc_main_station.ack_timer.function = llc_station_ack_tmr_cb;
skb = alloc_skb(0, GFP_ATOMIC);
if (!skb)
goto out;
rc = 0;
llc_set_station_handler(llc_station_rcv);
ev = llc_station_ev(skb);
memset(ev, 0, sizeof(*ev));
llc_main_station.ack_timer.expires = jiffies + 3 * HZ;
llc_main_station.maximum_retry = 1;
llc_main_station.state = LLC_STATION_STATE_DOWN;
ev->type = LLC_STATION_EV_TYPE_SIMPLE;
ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK;
rc = llc_station_next_state(skb);
out:
return rc;
}
void __exit llc_station_exit(void)
{
llc_set_station_handler(NULL);
}