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:
10
net/llc/Kconfig
Normal file
10
net/llc/Kconfig
Normal 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
24
net/llc/Makefile
Normal 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
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
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
769
net/llc/llc_c_ev.c
Normal 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
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
915
net/llc/llc_conn.c
Normal 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
179
net/llc/llc_core.c
Normal 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
157
net/llc/llc_if.c
Normal 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
189
net/llc/llc_input.c
Normal 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
107
net/llc/llc_output.c
Normal 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
20
net/llc/llc_output.h
Normal 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
372
net/llc/llc_pdu.c
Normal 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
267
net/llc/llc_proc.c
Normal 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
205
net/llc/llc_s_ac.c
Normal 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
115
net/llc/llc_s_ev.c
Normal 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
183
net/llc/llc_s_st.c
Normal 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
316
net/llc/llc_sap.c
Normal 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
713
net/llc/llc_station.c
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user