mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
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.
|
25
net/llc/Makefile
Normal file
25
net/llc/Makefile
Normal file
|
@ -0,0 +1,25 @@
|
|||
###########################################################################
|
||||
# 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
|
||||
llc2-$(CONFIG_SYSCTL) += sysctl_net_llc.o
|
1253
net/llc/af_llc.c
Normal file
1253
net/llc/af_llc.c
Normal file
File diff suppressed because it is too large
Load diff
1444
net/llc/llc_c_ac.c
Normal file
1444
net/llc/llc_c_ac.c
Normal file
File diff suppressed because it is too large
Load diff
748
net/llc/llc_c_ev.c
Normal file
748
net/llc/llc_c_ev.c
Normal file
|
@ -0,0 +1,748 @@
|
|||
/*
|
||||
* 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_ac.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
|
||||
|
||||
/**
|
||||
* 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_empty(&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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
|
||||
const u8 vr = llc_sk(sk)->vR;
|
||||
const 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)
|
||||
{
|
||||
const struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
|
||||
const u8 vr = llc_sk(sk)->vR;
|
||||
const 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)
|
||||
{
|
||||
const struct llc_pdu_sn * pdu = llc_pdu_sn_hdr(skb);
|
||||
const u8 vr = llc_sk(sk)->vR;
|
||||
const u8 ns = LLC_I_GET_NS(pdu);
|
||||
const 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",
|
||||
__func__, 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
|
||||
const u8 vr = llc_sk(sk)->vR;
|
||||
const 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)
|
||||
{
|
||||
const struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
|
||||
const u8 vr = llc_sk(sk)->vR;
|
||||
const 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)
|
||||
{
|
||||
const struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
|
||||
const u8 vr = llc_sk(sk)->vR;
|
||||
const 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)
|
||||
{
|
||||
const struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
|
||||
const u8 vr = llc_sk(sk)->vR;
|
||||
const u8 ns = LLC_I_GET_NS(pdu);
|
||||
const 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",
|
||||
__func__, 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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;
|
||||
const 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;
|
||||
const 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_x(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
u16 rc = 1;
|
||||
const 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;
|
||||
const struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
|
||||
const u8 vs = llc_sk(sk)->vS;
|
||||
const 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",
|
||||
__func__, 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;
|
||||
const struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
|
||||
const u8 vs = llc_sk(sk)->vS;
|
||||
const 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",
|
||||
__func__, 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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)
|
||||
{
|
||||
const 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
1016
net/llc/llc_conn.c
Normal file
1016
net/llc/llc_conn.c
Normal file
File diff suppressed because it is too large
Load diff
168
net/llc/llc_core.c
Normal file
168
net/llc/llc_core.c
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* 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/net_namespace.h>
|
||||
#include <net/llc.h>
|
||||
|
||||
LIST_HEAD(llc_sap_list);
|
||||
static DEFINE_SPINLOCK(llc_sap_list_lock);
|
||||
|
||||
/**
|
||||
* llc_sap_alloc - allocates and initializes sap.
|
||||
*
|
||||
* Allocates and initializes sap.
|
||||
*/
|
||||
static struct llc_sap *llc_sap_alloc(void)
|
||||
{
|
||||
struct llc_sap *sap = kzalloc(sizeof(*sap), GFP_ATOMIC);
|
||||
int i;
|
||||
|
||||
if (sap) {
|
||||
/* sap->laddr.mac - leave as a null, it's filled by bind */
|
||||
sap->state = LLC_SAP_STATE_ACTIVE;
|
||||
spin_lock_init(&sap->sk_lock);
|
||||
for (i = 0; i < LLC_SK_LADDR_HASH_ENTRIES; i++)
|
||||
INIT_HLIST_NULLS_HEAD(&sap->sk_laddr_hash[i], i);
|
||||
atomic_set(&sap->refcnt, 1);
|
||||
}
|
||||
return sap;
|
||||
}
|
||||
|
||||
static struct llc_sap *__llc_sap_find(unsigned char sap_value)
|
||||
{
|
||||
struct llc_sap *sap;
|
||||
|
||||
list_for_each_entry(sap, &llc_sap_list, node)
|
||||
if (sap->laddr.lsap == sap_value)
|
||||
goto out;
|
||||
sap = NULL;
|
||||
out:
|
||||
return sap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* If the sap is found it will be refcounted and the user will have to do
|
||||
* a llc_sap_put after use.
|
||||
* Returns the sap or %NULL if not found.
|
||||
*/
|
||||
struct llc_sap *llc_sap_find(unsigned char sap_value)
|
||||
{
|
||||
struct llc_sap *sap;
|
||||
|
||||
rcu_read_lock_bh();
|
||||
sap = __llc_sap_find(sap_value);
|
||||
if (sap)
|
||||
llc_sap_hold(sap);
|
||||
rcu_read_unlock_bh();
|
||||
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 net_device *orig_dev))
|
||||
{
|
||||
struct llc_sap *sap = NULL;
|
||||
|
||||
spin_lock_bh(&llc_sap_list_lock);
|
||||
if (__llc_sap_find(lsap)) /* SAP already exists */
|
||||
goto out;
|
||||
sap = llc_sap_alloc();
|
||||
if (!sap)
|
||||
goto out;
|
||||
sap->laddr.lsap = lsap;
|
||||
sap->rcv_func = func;
|
||||
list_add_tail_rcu(&sap->node, &llc_sap_list);
|
||||
out:
|
||||
spin_unlock_bh(&llc_sap_list_lock);
|
||||
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(sap->sk_count);
|
||||
|
||||
spin_lock_bh(&llc_sap_list_lock);
|
||||
list_del_rcu(&sap->node);
|
||||
spin_unlock_bh(&llc_sap_list_lock);
|
||||
|
||||
synchronize_rcu();
|
||||
|
||||
kfree(sap);
|
||||
}
|
||||
|
||||
static struct packet_type llc_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_802_2),
|
||||
.func = llc_rcv,
|
||||
};
|
||||
|
||||
static struct packet_type llc_tr_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_TR_802_2),
|
||||
.func = llc_rcv,
|
||||
};
|
||||
|
||||
static int __init llc_init(void)
|
||||
{
|
||||
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_sap_list);
|
||||
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");
|
154
net/llc/llc_if.c
Normal file
154
net/llc/llc_if.c
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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/gfp.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.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>
|
||||
#include <net/tcp_states.h>
|
||||
|
||||
/**
|
||||
* 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 (unlikely(llc->state == LLC_CONN_STATE_ADM))
|
||||
goto out;
|
||||
rc = -EBUSY;
|
||||
if (unlikely(llc_data_accept_state(llc->state) || /* data_conn_refuse */
|
||||
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;
|
||||
skb_set_owner_w(skb, sk);
|
||||
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;
|
||||
skb_set_owner_w(skb, sk);
|
||||
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;
|
||||
}
|
||||
|
226
net/llc/llc_input.c
Normal file
226
net/llc/llc_input.c
Normal file
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* 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 <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/net_namespace.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))
|
||||
{
|
||||
smp_wmb(); /* ensure initialisation is complete before it's called */
|
||||
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;
|
||||
synchronize_net();
|
||||
}
|
||||
|
||||
void llc_set_station_handler(void (*handler)(struct sk_buff *skb))
|
||||
{
|
||||
/* Ensure initialisation is complete before it's called */
|
||||
if (handler)
|
||||
smp_wmb();
|
||||
|
||||
llc_station_handler = handler;
|
||||
|
||||
if (!handler)
|
||||
synchronize_net();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_un *pdu;
|
||||
|
||||
if (unlikely(!pskb_may_pull(skb, sizeof(*pdu))))
|
||||
return 0;
|
||||
|
||||
pdu = (struct llc_pdu_un *)skb->data;
|
||||
if ((pdu->ctrl_1 & LLC_PDU_TYPE_MASK) == LLC_PDU_TYPE_U)
|
||||
llc_len = 1;
|
||||
llc_len += 2;
|
||||
|
||||
if (unlikely(!pskb_may_pull(skb, llc_len)))
|
||||
return 0;
|
||||
|
||||
skb->transport_header += llc_len;
|
||||
skb_pull(skb, llc_len);
|
||||
if (skb->protocol == htons(ETH_P_802_2)) {
|
||||
__be16 pdulen = eth_hdr(skb)->h_proto;
|
||||
s32 data_size = ntohs(pdulen) - llc_len;
|
||||
|
||||
if (data_size < 0 ||
|
||||
!pskb_may_pull(skb, data_size))
|
||||
return 0;
|
||||
if (unlikely(pskb_trim_rcsum(skb, data_size)))
|
||||
return 0;
|
||||
}
|
||||
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 net_device *orig_dev)
|
||||
{
|
||||
struct llc_sap *sap;
|
||||
struct llc_pdu_sn *pdu;
|
||||
int dest;
|
||||
int (*rcv)(struct sk_buff *, struct net_device *,
|
||||
struct packet_type *, struct net_device *);
|
||||
void (*sta_handler)(struct sk_buff *skb);
|
||||
void (*sap_handler)(struct llc_sap *sap, struct sk_buff *skb);
|
||||
|
||||
if (!net_eq(dev_net(dev), &init_net))
|
||||
goto drop;
|
||||
|
||||
/*
|
||||
* 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", __func__);
|
||||
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", __func__,
|
||||
pdu->dsap);
|
||||
goto drop;
|
||||
}
|
||||
/*
|
||||
* First the upper layer protocols that don't need the full
|
||||
* LLC functionality
|
||||
*/
|
||||
rcv = rcu_dereference(sap->rcv_func);
|
||||
dest = llc_pdu_type(skb);
|
||||
sap_handler = dest ? ACCESS_ONCE(llc_type_handlers[dest - 1]) : NULL;
|
||||
if (unlikely(!sap_handler)) {
|
||||
if (rcv)
|
||||
rcv(skb, dev, pt, orig_dev);
|
||||
else
|
||||
kfree_skb(skb);
|
||||
} else {
|
||||
if (rcv) {
|
||||
struct sk_buff *cskb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (cskb)
|
||||
rcv(cskb, dev, pt, orig_dev);
|
||||
}
|
||||
sap_handler(sap, skb);
|
||||
}
|
||||
llc_sap_put(sap);
|
||||
out:
|
||||
return 0;
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
goto out;
|
||||
handle_station:
|
||||
sta_handler = ACCESS_ONCE(llc_station_handler);
|
||||
if (!sta_handler)
|
||||
goto drop;
|
||||
sta_handler(skb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(llc_add_pack);
|
||||
EXPORT_SYMBOL(llc_remove_pack);
|
||||
EXPORT_SYMBOL(llc_set_station_handler);
|
79
net/llc/llc_output.c
Normal file
79
net/llc/llc_output.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/export.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,
|
||||
const unsigned char *sa, const unsigned char *da)
|
||||
{
|
||||
int rc = -EINVAL;
|
||||
|
||||
switch (skb->dev->type) {
|
||||
case ARPHRD_ETHER:
|
||||
case ARPHRD_LOOPBACK:
|
||||
rc = dev_hard_header(skb, skb->dev, ETH_P_802_2, da, sa,
|
||||
skb->len);
|
||||
if (rc > 0)
|
||||
rc = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
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 (likely(!rc))
|
||||
rc = dev_queue_xmit(skb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(llc_mac_hdr_init);
|
||||
EXPORT_SYMBOL(llc_build_and_send_ui_pkt);
|
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, sizeof(struct llc_frmr_info));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
277
net/llc/llc_proc.c
Normal file
277
net/llc/llc_proc.c
Normal file
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* 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/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/net_namespace.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, u8 *addr)
|
||||
{
|
||||
seq_printf(seq, "%pM", addr);
|
||||
}
|
||||
|
||||
static struct sock *llc_get_sk_idx(loff_t pos)
|
||||
{
|
||||
struct llc_sap *sap;
|
||||
struct sock *sk = NULL;
|
||||
int i;
|
||||
|
||||
list_for_each_entry_rcu(sap, &llc_sap_list, node) {
|
||||
spin_lock_bh(&sap->sk_lock);
|
||||
for (i = 0; i < LLC_SK_LADDR_HASH_ENTRIES; i++) {
|
||||
struct hlist_nulls_head *head = &sap->sk_laddr_hash[i];
|
||||
struct hlist_nulls_node *node;
|
||||
|
||||
sk_nulls_for_each(sk, node, head) {
|
||||
if (!pos)
|
||||
goto found; /* keep the lock */
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&sap->sk_lock);
|
||||
}
|
||||
sk = NULL;
|
||||
found:
|
||||
return sk;
|
||||
}
|
||||
|
||||
static void *llc_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
loff_t l = *pos;
|
||||
|
||||
rcu_read_lock_bh();
|
||||
return l ? llc_get_sk_idx(--l) : SEQ_START_TOKEN;
|
||||
}
|
||||
|
||||
static struct sock *laddr_hash_next(struct llc_sap *sap, int bucket)
|
||||
{
|
||||
struct hlist_nulls_node *node;
|
||||
struct sock *sk = NULL;
|
||||
|
||||
while (++bucket < LLC_SK_LADDR_HASH_ENTRIES)
|
||||
sk_nulls_for_each(sk, node, &sap->sk_laddr_hash[bucket])
|
||||
goto out;
|
||||
|
||||
out:
|
||||
return sk;
|
||||
}
|
||||
|
||||
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_nulls_next(sk);
|
||||
if (next) {
|
||||
sk = next;
|
||||
goto out;
|
||||
}
|
||||
llc = llc_sk(sk);
|
||||
sap = llc->sap;
|
||||
sk = laddr_hash_next(sap, llc_sk_laddr_hashfn(sap, &llc->laddr));
|
||||
if (sk)
|
||||
goto out;
|
||||
spin_unlock_bh(&sap->sk_lock);
|
||||
list_for_each_entry_continue_rcu(sap, &llc_sap_list, node) {
|
||||
spin_lock_bh(&sap->sk_lock);
|
||||
sk = laddr_hash_next(sap, -1);
|
||||
if (sk)
|
||||
break; /* keep the lock */
|
||||
spin_unlock_bh(&sap->sk_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;
|
||||
|
||||
spin_unlock_bh(&sap->sk_lock);
|
||||
}
|
||||
rcu_read_unlock_bh();
|
||||
}
|
||||
|
||||
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 {
|
||||
u8 addr[6] = {0,0,0,0,0,0};
|
||||
llc_ui_format_mac(seq, addr);
|
||||
}
|
||||
seq_printf(seq, "@%02X ", llc->sap->laddr.lsap);
|
||||
llc_ui_format_mac(seq, llc->daddr.mac);
|
||||
seq_printf(seq, "@%02X %8d %8d %2d %3u %4d\n", llc->daddr.lsap,
|
||||
sk_wmem_alloc_get(sk),
|
||||
sk_rmem_alloc_get(sk) - llc->copied_seq,
|
||||
sk->sk_state,
|
||||
from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
|
||||
llc->link);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const 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 const 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 const 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 const 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 const 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", init_net.proc_net);
|
||||
if (!llc_proc_dir)
|
||||
goto out;
|
||||
|
||||
p = proc_create("socket", S_IRUGO, llc_proc_dir, &llc_seq_socket_fops);
|
||||
if (!p)
|
||||
goto out_socket;
|
||||
|
||||
p = proc_create("core", S_IRUGO, llc_proc_dir, &llc_seq_core_fops);
|
||||
if (!p)
|
||||
goto out_core;
|
||||
|
||||
rc = 0;
|
||||
out:
|
||||
return rc;
|
||||
out_core:
|
||||
remove_proc_entry("socket", llc_proc_dir);
|
||||
out_socket:
|
||||
remove_proc_entry("llc", init_net.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", init_net.proc_net);
|
||||
}
|
208
net/llc/llc_s_ac.c
Normal file
208
net/llc/llc_s_ac.c
Normal file
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
|
||||
/**
|
||||
* 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 (likely(!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 (likely(!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(NULL, skb->dev, LLC_PDU_TYPE_U,
|
||||
sizeof(struct llc_xid_info));
|
||||
if (!nskb)
|
||||
goto out;
|
||||
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 (likely(!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 (likely(!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;
|
||||
u32 data_size;
|
||||
|
||||
llc_pdu_decode_sa(skb, mac_da);
|
||||
llc_pdu_decode_da(skb, mac_sa);
|
||||
llc_pdu_decode_ssap(skb, &dsap);
|
||||
|
||||
/* The test request command is type U (llc_len = 3) */
|
||||
data_size = ntohs(eth_hdr(skb)->h_proto) - 3;
|
||||
nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, data_size);
|
||||
if (!nskb)
|
||||
goto out;
|
||||
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 (likely(!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,
|
||||
},
|
||||
};
|
439
net/llc/llc_sap.c
Normal file
439
net/llc/llc_sap.c
Normal file
|
@ -0,0 +1,439 @@
|
|||
/*
|
||||
* 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 <net/tcp_states.h>
|
||||
#include <linux/llc.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static int llc_mac_header_len(unsigned short devtype)
|
||||
{
|
||||
switch (devtype) {
|
||||
case ARPHRD_ETHER:
|
||||
case ARPHRD_LOOPBACK:
|
||||
return sizeof(struct ethhdr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* llc_alloc_frame - allocates sk_buff for frame
|
||||
* @dev: network device this skb will be sent over
|
||||
* @type: pdu type to allocate
|
||||
* @data_size: data size to allocate
|
||||
*
|
||||
* 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(struct sock *sk, struct net_device *dev,
|
||||
u8 type, u32 data_size)
|
||||
{
|
||||
int hlen = type == LLC_PDU_TYPE_U ? 3 : 4;
|
||||
struct sk_buff *skb;
|
||||
|
||||
hlen += llc_mac_header_len(dev->type);
|
||||
skb = alloc_skb(hlen + data_size, GFP_ATOMIC);
|
||||
|
||||
if (skb) {
|
||||
skb_reset_mac_header(skb);
|
||||
skb_reserve(skb, hlen);
|
||||
skb_reset_network_header(skb);
|
||||
skb_reset_transport_header(skb);
|
||||
skb->protocol = htons(ETH_P_802_2);
|
||||
skb->dev = dev;
|
||||
if (sk != NULL)
|
||||
skb_set_owner_w(skb, sk);
|
||||
}
|
||||
return skb;
|
||||
}
|
||||
|
||||
void llc_save_primitive(struct sock *sk, struct sk_buff *skb, u8 prim)
|
||||
{
|
||||
struct sockaddr_llc *addr;
|
||||
|
||||
/* save primitive for use by the user. */
|
||||
addr = llc_ui_skb_cb(skb);
|
||||
|
||||
memset(addr, 0, sizeof(*addr));
|
||||
addr->sllc_family = 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->sk, 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 sock *sk)
|
||||
{
|
||||
struct llc_sap_state_ev *ev = llc_sap_ev(skb);
|
||||
|
||||
ev->type = LLC_SAP_EV_TYPE_PDU;
|
||||
ev->reason = 0;
|
||||
skb->sk = sk;
|
||||
llc_sap_state_process(sap, skb);
|
||||
}
|
||||
|
||||
static inline bool llc_dgram_match(const struct llc_sap *sap,
|
||||
const struct llc_addr *laddr,
|
||||
const struct sock *sk)
|
||||
{
|
||||
struct llc_sock *llc = llc_sk(sk);
|
||||
|
||||
return sk->sk_type == SOCK_DGRAM &&
|
||||
llc->laddr.lsap == laddr->lsap &&
|
||||
ether_addr_equal(llc->laddr.mac, laddr->mac);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
const struct llc_addr *laddr)
|
||||
{
|
||||
struct sock *rc;
|
||||
struct hlist_nulls_node *node;
|
||||
int slot = llc_sk_laddr_hashfn(sap, laddr);
|
||||
struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];
|
||||
|
||||
rcu_read_lock_bh();
|
||||
again:
|
||||
sk_nulls_for_each_rcu(rc, node, laddr_hb) {
|
||||
if (llc_dgram_match(sap, laddr, rc)) {
|
||||
/* Extra checks required by SLAB_DESTROY_BY_RCU */
|
||||
if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
|
||||
goto again;
|
||||
if (unlikely(llc_sk(rc)->sap != sap ||
|
||||
!llc_dgram_match(sap, laddr, rc))) {
|
||||
sock_put(rc);
|
||||
continue;
|
||||
}
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
rc = NULL;
|
||||
/*
|
||||
* if the nulls value we got at the end of this lookup is
|
||||
* not the expected one, we must restart lookup.
|
||||
* We probably met an item that was moved to another chain.
|
||||
*/
|
||||
if (unlikely(get_nulls_value(node) != slot))
|
||||
goto again;
|
||||
found:
|
||||
rcu_read_unlock_bh();
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline bool llc_mcast_match(const struct llc_sap *sap,
|
||||
const struct llc_addr *laddr,
|
||||
const struct sk_buff *skb,
|
||||
const struct sock *sk)
|
||||
{
|
||||
struct llc_sock *llc = llc_sk(sk);
|
||||
|
||||
return sk->sk_type == SOCK_DGRAM &&
|
||||
llc->laddr.lsap == laddr->lsap &&
|
||||
llc->dev == skb->dev;
|
||||
}
|
||||
|
||||
static void llc_do_mcast(struct llc_sap *sap, struct sk_buff *skb,
|
||||
struct sock **stack, int count)
|
||||
{
|
||||
struct sk_buff *skb1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
skb1 = skb_clone(skb, GFP_ATOMIC);
|
||||
if (!skb1) {
|
||||
sock_put(stack[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
llc_sap_rcv(sap, skb1, stack[i]);
|
||||
sock_put(stack[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets.
|
||||
* @sap: SAP
|
||||
* @laddr: address of local LLC (MAC + SAP)
|
||||
*
|
||||
* Search socket list of the SAP and finds connections with same sap.
|
||||
* Deliver clone to each.
|
||||
*/
|
||||
static void llc_sap_mcast(struct llc_sap *sap,
|
||||
const struct llc_addr *laddr,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
int i = 0, count = 256 / sizeof(struct sock *);
|
||||
struct sock *sk, *stack[count];
|
||||
struct llc_sock *llc;
|
||||
struct hlist_head *dev_hb = llc_sk_dev_hash(sap, skb->dev->ifindex);
|
||||
|
||||
spin_lock_bh(&sap->sk_lock);
|
||||
hlist_for_each_entry(llc, dev_hb, dev_hash_node) {
|
||||
|
||||
sk = &llc->sk;
|
||||
|
||||
if (!llc_mcast_match(sap, laddr, skb, sk))
|
||||
continue;
|
||||
|
||||
sock_hold(sk);
|
||||
if (i < count)
|
||||
stack[i++] = sk;
|
||||
else {
|
||||
llc_do_mcast(sap, skb, stack, i);
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&sap->sk_lock);
|
||||
|
||||
llc_do_mcast(sap, skb, stack, i);
|
||||
}
|
||||
|
||||
|
||||
void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb)
|
||||
{
|
||||
struct llc_addr laddr;
|
||||
|
||||
llc_pdu_decode_da(skb, laddr.mac);
|
||||
llc_pdu_decode_dsap(skb, &laddr.lsap);
|
||||
|
||||
if (is_multicast_ether_addr(laddr.mac)) {
|
||||
llc_sap_mcast(sap, &laddr, skb);
|
||||
kfree_skb(skb);
|
||||
} else {
|
||||
struct sock *sk = llc_lookup_dgram(sap, &laddr);
|
||||
if (sk) {
|
||||
llc_sap_rcv(sap, skb, sk);
|
||||
sock_put(sk);
|
||||
} else
|
||||
kfree_skb(skb);
|
||||
}
|
||||
}
|
125
net/llc/llc_station.c
Normal file
125
net/llc/llc_station.c
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.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>
|
||||
|
||||
static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb)
|
||||
{
|
||||
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
|
||||
|
||||
return 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_test_c(struct sk_buff *skb)
|
||||
{
|
||||
struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
|
||||
|
||||
return 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_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(NULL, skb->dev, LLC_PDU_TYPE_U,
|
||||
sizeof(struct llc_xid_info));
|
||||
|
||||
if (!nskb)
|
||||
goto out;
|
||||
rc = 0;
|
||||
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, skb->dev->dev_addr, mac_da);
|
||||
if (unlikely(rc))
|
||||
goto free;
|
||||
dev_queue_xmit(nskb);
|
||||
out:
|
||||
return rc;
|
||||
free:
|
||||
kfree_skb(nskb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int llc_station_ac_send_test_r(struct sk_buff *skb)
|
||||
{
|
||||
u8 mac_da[ETH_ALEN], dsap;
|
||||
int rc = 1;
|
||||
u32 data_size;
|
||||
struct sk_buff *nskb;
|
||||
|
||||
/* The test request command is type U (llc_len = 3) */
|
||||
data_size = ntohs(eth_hdr(skb)->h_proto) - 3;
|
||||
nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, data_size);
|
||||
|
||||
if (!nskb)
|
||||
goto out;
|
||||
rc = 0;
|
||||
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, skb->dev->dev_addr, mac_da);
|
||||
if (unlikely(rc))
|
||||
goto free;
|
||||
dev_queue_xmit(nskb);
|
||||
out:
|
||||
return rc;
|
||||
free:
|
||||
kfree_skb(nskb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
if (llc_stat_ev_rx_null_dsap_xid_c(skb))
|
||||
llc_station_ac_send_xid_r(skb);
|
||||
else if (llc_stat_ev_rx_null_dsap_test_c(skb))
|
||||
llc_station_ac_send_test_r(skb);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
void __init llc_station_init(void)
|
||||
{
|
||||
llc_set_station_handler(llc_station_rcv);
|
||||
}
|
||||
|
||||
void llc_station_exit(void)
|
||||
{
|
||||
llc_set_station_handler(NULL);
|
||||
}
|
78
net/llc/sysctl_net_llc.c
Normal file
78
net/llc/sysctl_net_llc.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* sysctl_net_llc.c: sysctl interface to LLC net subsystem.
|
||||
*
|
||||
* Arnaldo Carvalho de Melo <acme@conectiva.com.br>
|
||||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/llc.h>
|
||||
|
||||
#ifndef CONFIG_SYSCTL
|
||||
#error This file should not be compiled without CONFIG_SYSCTL defined
|
||||
#endif
|
||||
|
||||
static struct ctl_table llc2_timeout_table[] = {
|
||||
{
|
||||
.procname = "ack",
|
||||
.data = &sysctl_llc2_ack_timeout,
|
||||
.maxlen = sizeof(long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.procname = "busy",
|
||||
.data = &sysctl_llc2_busy_timeout,
|
||||
.maxlen = sizeof(long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.procname = "p",
|
||||
.data = &sysctl_llc2_p_timeout,
|
||||
.maxlen = sizeof(long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.procname = "rej",
|
||||
.data = &sysctl_llc2_rej_timeout,
|
||||
.maxlen = sizeof(long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_jiffies,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct ctl_table llc_station_table[] = {
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct ctl_table_header *llc2_timeout_header;
|
||||
static struct ctl_table_header *llc_station_header;
|
||||
|
||||
int __init llc_sysctl_init(void)
|
||||
{
|
||||
llc2_timeout_header = register_net_sysctl(&init_net, "net/llc/llc2/timeout", llc2_timeout_table);
|
||||
llc_station_header = register_net_sysctl(&init_net, "net/llc/station", llc_station_table);
|
||||
|
||||
if (!llc2_timeout_header || !llc_station_header) {
|
||||
llc_sysctl_exit();
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void llc_sysctl_exit(void)
|
||||
{
|
||||
if (llc2_timeout_header) {
|
||||
unregister_net_sysctl_table(llc2_timeout_header);
|
||||
llc2_timeout_header = NULL;
|
||||
}
|
||||
if (llc_station_header) {
|
||||
unregister_net_sysctl_table(llc_station_header);
|
||||
llc_station_header = NULL;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue