net/llc/*.c

Forward port of LLC from 2.4 to 2.5. This is the forward port of the LLC stack
released by Procom Inc. for Linux 2.0.30, I have heavily modified it to make
it similar to other Linux network stacks, using of struct sk_buff to represent
in-transit packets and doing massive code cleanups.

Jay Schullist contributed support for BSD Sockets, as the original code had
only a simple in kernel API for use by upper layer protocols, such as the
NetBEUI stack also provided by Procom for 2.0.30.

This code is basically what I had previously submitted to Alan Cox for his
2.4-ac series and that is even shipped in source form, in the Red Hat 7.3
kernel package, plus cleanups wrt standard syntax for labeled elements and
further use of this C construct to make the code more resilient to editing
mistakes, using the compiler to further check the source code.

TODO:

Make it completely SMP safe, as the reports of successful usage up to now and
the testing is done on UP.

Completely remove the old LLC code in the kernel, that is still there for things
like Appletalk, IPX, etc to use, also check that all these protocols work
correctly with this new LLC stack.

This code is already being used in the linux-sna project and Jay Schullist
has been developing support for things like DLSw and other protocols that works
on top of 802.2.

I'll be releasing patches with the NetBEUI stack and updated samba-2.0.6 patches
for use with NetBEUI and this LLC stack in the future. But the NetBEUI code
is available already in my kernel.org ftp area at:

ftp://ftp.kernel.org/pub/linux/kernel/people/acme.

Please report problems to me or the linux-sna mailing list, instructions on how
to subscribe are available at http://www.linux-sna.org website.
parent d0f0cde1
...@@ -944,6 +944,11 @@ W: http://www.linuxppc.org/ ...@@ -944,6 +944,11 @@ W: http://www.linuxppc.org/
L: linuxppc-dev@lists.linuxppc.org L: linuxppc-dev@lists.linuxppc.org
S: Maintained S: Maintained
LLC (802.2)
P: Arnaldo Carvalho de Melo
M: acme@conectiva.com.br
S: Maintained
LINUX FOR 64BIT POWERPC LINUX FOR 64BIT POWERPC
P: David Engebretsen P: David Engebretsen
M: engebret@us.ibm.com M: engebret@us.ibm.com
......
...@@ -28,13 +28,15 @@ ...@@ -28,13 +28,15 @@
* load-locked/store-conditional cpus (ALPHA/MIPS/PPC) and * load-locked/store-conditional cpus (ALPHA/MIPS/PPC) and
* compare-and-swap cpus (Sparc64). So we control which * compare-and-swap cpus (Sparc64). So we control which
* implementation to use with a __BRLOCK_USE_ATOMICS define. -DaveM * implementation to use with a __BRLOCK_USE_ATOMICS define. -DaveM
*
* Added BR_LLC_LOCK for use in net/core/ext8022.c -acme
*/ */
/* Register bigreader lock indices here. */ /* Register bigreader lock indices here. */
enum brlock_indices { enum brlock_indices {
BR_GLOBALIRQ_LOCK, BR_GLOBALIRQ_LOCK,
BR_NETPROTO_LOCK, BR_NETPROTO_LOCK,
BR_LLC_LOCK,
__BR_END __BR_END
}; };
......
#ifndef __LINUX_LLC_H
#define __LINUX_LLC_H
/*
* IEEE 802.2 User Interface SAPs for Linux, data structures and indicators.
*
* Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
*
* 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.
*/
#define __LLC_SOCK_SIZE__ 28 /* sizeof(sockaddr_llc), word align. */
struct sockaddr_llc {
sa_family_t sllc_family; /* AF_LLC */
sa_family_t sllc_arphrd; /* ARPHRD_ETHER */
unsigned char sllc_test;
unsigned char sllc_xid;
unsigned char sllc_ua; /* UA data, only for SOCK_STREAM. */
unsigned char sllc_dsap;
unsigned char sllc_ssap;
unsigned char sllc_dmac[IFHWADDRLEN];
unsigned char sllc_smac[IFHWADDRLEN];
unsigned char sllc_mmac[IFHWADDRLEN];
unsigned char __pad[__LLC_SOCK_SIZE__ - sizeof(sa_family_t) * 2 -
sizeof(unsigned char) * 5 - IFHWADDRLEN * 3];
};
/* sockopt definitions. */
enum llc_sockopts {
LLC_OPT_UNKNOWN = 0,
LLC_OPT_RETRY, /* max retrans attempts. */
LLC_OPT_SIZE, /* max PDU size (octets). */
LLC_OPT_ACK_TMR_EXP, /* ack expire time (secs). */
LLC_OPT_P_TMR_EXP, /* pf cycle expire time (secs). */
LLC_OPT_REJ_TMR_EXP, /* rej sent expire time (secs). */
LLC_OPT_BUSY_TMR_EXP, /* busy state expire time (secs). */
LLC_OPT_TX_WIN, /* tx window size. */
LLC_OPT_RX_WIN, /* rx window size. */
LLC_OPT_MAX
};
#define LLC_OPT_MAX_RETRY 100
#define LLC_OPT_MAX_SIZE 4196
#define LLC_OPT_MAX_WIN 127
#define LLC_OPT_MAX_ACK_TMR_EXP 60
#define LLC_OPT_MAX_P_TMR_EXP 60
#define LLC_OPT_MAX_REJ_TMR_EXP 60
#define LLC_OPT_MAX_BUSY_TMR_EXP 60
/* LLC SAP types. */
#define LLC_SAP_NULL 0x00 /* NULL SAP. */
#define LLC_SAP_LLC 0x02 /* LLC Sublayer Managment. */
#define LLC_SAP_SNA 0x04 /* SNA Path Control. */
#define LLC_SAP_PNM 0x0E /* Proway Network Managment. */
#define LLC_SAP_IP 0x06 /* TCP/IP. */
#define LLC_SAP_BSPAN 0x42 /* Bridge Spanning Tree Proto */
#define LLC_SAP_MMS 0x4E /* Manufacturing Message Srv. */
#define LLC_SAP_8208 0x7E /* ISO 8208 */
#define LLC_SAP_3COM 0x80 /* 3COM. */
#define LLC_SAP_PRO 0x8E /* Proway Active Station List */
#define LLC_SAP_SNAP 0xAA /* SNAP. */
#define LLC_SAP_BANYAN 0xBC /* Banyan. */
#define LLC_SAP_IPX 0xE0 /* IPX/SPX. */
#define LLC_SAP_NETBEUI 0xF0 /* NetBEUI. */
#define LLC_SAP_LANMGR 0xF4 /* LanManager. */
#define LLC_SAP_IMPL 0xF8 /* IMPL */
#define LLC_SAP_DISC 0xFC /* Discovery */
#define LLC_SAP_OSI 0xFE /* OSI Network Layers. */
#define LLC_SAP_LAR 0xDC /* LAN Address Resolution */
#define LLC_SAP_RM 0xD4 /* Resource Management */
#define LLC_SAP_GLOBAL 0xFF /* Global SAP. */
#ifdef __KERNEL__
#define LLC_SAP_DYN_START 0xC0
#define LLC_SAP_DYN_STOP 0xDE
#define LLC_SAP_DYN_TRIES 4
struct sock;
struct llc_ui_opt {
u16 link; /* network layer link number */
struct llc_sap *sap; /* pointer to parent SAP */
struct sock *core_sk;
struct net_device *dev; /* device to send to remote */
struct sockaddr_llc addr; /* address sock is bound to */
};
#define llc_ui_sk(__sk) ((struct llc_ui_opt *)(__sk)->protinfo)
#define llc_ui_skb_cb(__skb) ((struct sockaddr_llc *)&((__skb)->cb[0]))
#ifdef CONFIG_LLC_UI
extern int llc_ui_init(void);
extern void llc_ui_exit(void);
#else
#define llc_ui_init()
#define llc_ui_exit()
#endif
#endif /* __KERNEL__ */
#endif /* __LINUX_LLC_H */
...@@ -2,15 +2,24 @@ ...@@ -2,15 +2,24 @@
#define _NET_INET_DATALINK_H_ #define _NET_INET_DATALINK_H_
struct datalink_proto { struct datalink_proto {
unsigned short type_len; unsigned short type_len;
unsigned char type[8]; unsigned char type[8];
const char *string_name; const char *string_name;
unsigned short header_length;
int (*rcvfunc)(struct sk_buff *, struct net_device *, union {
struct packet_type *); struct llc_pinfo *llc;
void (*datalink_header)(struct datalink_proto *, struct sk_buff *, } ll_pinfo;
unsigned char *);
struct datalink_proto *next; struct llc_sc_info *llc_sc;
struct sock *sock;
unsigned short header_length;
int (*rcvfunc)(struct sk_buff *, struct net_device *,
struct packet_type *);
void (*datalink_header)(struct datalink_proto *, struct sk_buff *,
unsigned char *);
struct datalink_proto *next;
}; };
#endif #endif
#ifndef LLC_ACTN_H
#define LLC_ACTN_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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.
*/
/* Station component state transition actions */
#define LLC_STATION_AC_START_ACK_TMR 1
#define LLC_STATION_AC_SET_RETRY_CNT_0 2
#define LLC_STATION_AC_INC_RETRY_CNT_BY_1 3
#define LLC_STATION_AC_SET_XID_R_CNT_0 4
#define LLC_STATION_AC_INC_XID_R_CNT_BY_1 5
#define LLC_STATION_AC_SEND_NULL_DSAP_XID_C 6
#define LLC_STATION_AC_SEND_XID_R 7
#define LLC_STATION_AC_SEND_TEST_R 8
#define LLC_STATION_AC_REPORT_STATUS 9
/* All station state event action functions look like this */
typedef int (*llc_station_action_t)(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_start_ack_timer(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_set_retry_cnt_0(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_inc_retry_cnt_by_1(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_set_xid_r_cnt_0(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_inc_xid_r_cnt_by_1(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_send_null_dsap_xid_c(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_send_xid_r(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_send_test_r(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_report_status(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_report_status(struct llc_station *station,
struct llc_station_state_ev *ev);
#endif /* LLC_ACTN_H */
#ifndef LLC_C_AC_H
#define LLC_C_AC_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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.
*/
/* Connection component state transition actions */
/*
* Connection state transition actions
* (Fb = F bit; Pb = P bit; Xb = X bit)
*/
#define LLC_CONN_AC_CLR_REMOTE_BUSY 1
#define LLC_CONN_AC_CONN_IND 2
#define LLC_CONN_AC_CONN_CONFIRM 3
#define LLC_CONN_AC_DATA_IND 4
#define LLC_CONN_AC_DISC_IND 5
#define LLC_CONN_AC_RESET_IND 6
#define LLC_CONN_AC_RESET_CONFIRM 7
#define LLC_CONN_AC_REPORT_STATUS 8
#define LLC_CONN_AC_CLR_REMOTE_BUSY_IF_Fb_EQ_1 9
#define LLC_CONN_AC_STOP_REJ_TMR_IF_DATA_FLAG_EQ_2 10
#define LLC_CONN_AC_SEND_DISC_CMD_Pb_SET_X 11
#define LLC_CONN_AC_SEND_DM_RSP_Fb_SET_Pb 12
#define LLC_CONN_AC_SEND_DM_RSP_Fb_SET_1 13
#define LLC_CONN_AC_SEND_DM_RSP_Fb_SET_F_FLAG 14
#define LLC_CONN_AC_SEND_FRMR_RSP_Fb_SET_X 15
#define LLC_CONN_AC_RESEND_FRMR_RSP_Fb_SET_0 16
#define LLC_CONN_AC_RESEND_FRMR_RSP_Fb_SET_Pb 17
#define LLC_CONN_AC_SEND_I_CMD_Pb_SET_1 18
#define LLC_CONN_AC_RESEND_I_CMD_Pb_SET_1 19
#define LLC_CONN_AC_RESEND_I_CMD_Pb_SET_1_OR_SEND_RR 20
#define LLC_CONN_AC_SEND_I_XXX_Xb_SET_0 21
#define LLC_CONN_AC_RESEND_I_XXX_Xb_SET_0 22
#define LLC_CONN_AC_RESEND_I_XXX_Xb_SET_0_OR_SEND_RR 23
#define LLC_CONN_AC_RESEND_I_RSP_Fb_SET_1 24
#define LLC_CONN_AC_SEND_REJ_CMD_Pb_SET_1 25
#define LLC_CONN_AC_SEND_REJ_RSP_Fb_SET_1 26
#define LLC_CONN_AC_SEND_REJ_XXX_Xb_SET_0 27
#define LLC_CONN_AC_SEND_RNR_CMD_Pb_SET_1 28
#define LLC_CONN_AC_SEND_RNR_RSP_Fb_SET_1 29
#define LLC_CONN_AC_SEND_RNR_XXX_Xb_SET_0 30
#define LLC_CONN_AC_SET_REMOTE_BUSY 31
#define LLC_CONN_AC_OPTIONAL_SEND_RNR_XXX_Xb_SET_0 32
#define LLC_CONN_AC_SEND_RR_CMD_Pb_SET_1 33
#define LLC_CONN_AC_SEND_ACK_CMD_Pb_SET_1 34
#define LLC_CONN_AC_SEND_RR_RSP_Fb_SET_1 35
#define LLC_CONN_AC_SEND_ACK_RSP_Fb_SET_1 36
#define LLC_CONN_AC_SEND_RR_XXX_Xb_SET_0 37
#define LLC_CONN_AC_SEND_ACK_XXX_Xb_SET_0 38
#define LLC_CONN_AC_SEND_SABME_CMD_Pb_SET_X 39
#define LLC_CONN_AC_SEND_UA_RSP_Fb_SET_Pb 40
#define LLC_CONN_AC_SEND_UA_RSP_Fb_SET_F_FLAG 41
#define LLC_CONN_AC_S_FLAG_SET_0 42
#define LLC_CONN_AC_S_FLAG_SET_1 43
#define LLC_CONN_AC_START_P_TMR 44
#define LLC_CONN_AC_START_ACK_TMR 45
#define LLC_CONN_AC_START_REJ_TMR 46
#define LLC_CONN_AC_START_ACK_TMR_IF_NOT_RUNNING 47
#define LLC_CONN_AC_STOP_ACK_TMR 48
#define LLC_CONN_AC_STOP_P_TMR 49
#define LLC_CONN_AC_STOP_REJ_TMR 50
#define LLC_CONN_AC_STOP_ALL_TMRS 51
#define LLC_CONN_AC_STOP_OTHER_TMRS 52
#define LLC_CONN_AC_UPDATE_Nr_RECEIVED 53
#define LLC_CONN_AC_UPDATE_P_FLAG 54
#define LLC_CONN_AC_DATA_FLAG_SET_2 55
#define LLC_CONN_AC_DATA_FLAG_SET_0 56
#define LLC_CONN_AC_DATA_FLAG_SET_1 57
#define LLC_CONN_AC_DATA_FLAG_SET_1_IF_DATA_FLAG_EQ_0 58
#define LLC_CONN_AC_P_FLAG_SET_0 59
#define LLC_CONN_AC_P_FLAG_SET_P 60
#define LLC_CONN_AC_REMOTE_BUSY_SET_0 61
#define LLC_CONN_AC_RETRY_CNT_SET_0 62
#define LLC_CONN_AC_RETRY_CNT_INC_BY_1 63
#define LLC_CONN_AC_Vr_SET_0 64
#define LLC_CONN_AC_Vr_INC_BY_1 65
#define LLC_CONN_AC_Vs_SET_0 66
#define LLC_CONN_AC_Vs_SET_Nr 67
#define LLC_CONN_AC_F_FLAG_SET_P 68
#define LLC_CONN_AC_STOP_SENDACK_TMR 70
#define LLC_CONN_AC_START_SENDACK_TMR_IF_NOT_RUNNING 71
typedef int (*llc_conn_action_t)(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_clear_remote_busy(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_conn_ind(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_conn_confirm(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_data_ind(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_disc_ind(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_rst_ind(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_rst_confirm(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_report_status(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_clear_remote_busy_if_f_eq_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_disc_cmd_p_set_x(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_dm_rsp_f_set_p(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_dm_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_dm_rsp_f_set_f_flag(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_frmr_rsp_f_set_x(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_frmr_rsp_f_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_frmr_rsp_f_set_p(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_cmd_p_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_i_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_i_cmd_p_set_1_or_send_rr(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_i_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_i_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rej_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rej_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rej_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rnr_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rnr_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rnr_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_remote_busy(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_opt_send_rnr_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rr_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_ack_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rr_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_ack_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rr_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_ack_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_sabme_cmd_p_set_x(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_ua_rsp_f_set_f_flag(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_ua_rsp_f_set_p(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_s_flag_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_s_flag_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_start_p_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_start_ack_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_start_rej_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_start_ack_tmr_if_not_running(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_ack_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_p_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_rej_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_all_timers(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_other_timers(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_upd_nr_received(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_inc_tx_win_size(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_dec_tx_win_size(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_upd_p_flag(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_data_flag_2(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_data_flag_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_data_flag_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_data_flag_1_if_data_flag_eq_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_p_flag_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_p_flag_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_remote_busy_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_retry_cnt_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_cause_flag_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_cause_flag_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_inc_retry_cnt_by_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_vr_0(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_inc_vr_by_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_vs_0(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_vs_nr(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_rst_vs(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_upd_vs(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_f_flag_p(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_disc(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_reset(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_disc_confirm(struct sock* sk, struct llc_conn_state_ev *ev);
extern u8 llc_circular_between(u8 a, u8 b, u8 c);
extern int llc_conn_ac_send_ack_if_needed(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_inc_npta_value(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_adjust_npta_by_rr(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_adjust_npta_by_rnr(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_rst_sendack_flag(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rr_rsp_f_set_ackpf(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_rsp_f_set_ackpf(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_rsp_as_ack(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_as_ack(struct sock* sk,
struct llc_conn_state_ev *ev);
#endif /* LLC_C_AC_H */
#ifndef LLC_C_EV_H
#define LLC_C_EV_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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.
*/
/* Connection component state transition event qualifiers */
/* Types of events (possible values in 'ev->type') */
#define LLC_CONN_EV_TYPE_SIMPLE 1
#define LLC_CONN_EV_TYPE_CONDITION 2
#define LLC_CONN_EV_TYPE_PRIM 3
#define LLC_CONN_EV_TYPE_PDU 4 /* command/response PDU */
#define LLC_CONN_EV_TYPE_ACK_TMR 5
#define LLC_CONN_EV_TYPE_P_TMR 6
#define LLC_CONN_EV_TYPE_REJ_TMR 7
#define LLC_CONN_EV_TYPE_BUSY_TMR 8
#define LLC_CONN_EV_TYPE_RPT_STATUS 9
#define LLC_CONN_EV_TYPE_SENDACK_TMR 10
#define NBR_CONN_EV 5
/* Connection events which cause state transitions when fully qualified */
#define LLC_CONN_EV_CONN_REQ 1
#define LLC_CONN_EV_CONN_RESP 2
#define LLC_CONN_EV_DATA_REQ 3
#define LLC_CONN_EV_DISC_REQ 4
#define LLC_CONN_EV_RESET_REQ 5
#define LLC_CONN_EV_RESET_RESP 6
#define LLC_CONN_EV_LOCAL_BUSY_DETECTED 7
#define LLC_CONN_EV_LOCAL_BUSY_CLEARED 8
#define LLC_CONN_EV_RX_BAD_PDU 9
#define LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X 10
#define LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X 11
#define LLC_CONN_EV_RX_FRMR_RSP_Fbit_SET_X 12
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_X 13
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_X_UNEXPD_Ns 14
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_X_INVAL_Ns 15
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_X 16
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_X_UNEXPD_Ns 17
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_X_INVAL_Ns 18
#define LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_X 19
#define LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_X 20
#define LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_X 21
#define LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_X 22
#define LLC_CONN_EV_RX_RR_CMD_Pbit_SET_X 23
#define LLC_CONN_EV_RX_RR_RSP_Fbit_SET_X 24
#define LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X 25
#define LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X 26
#define LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_X 27
#define LLC_CONN_EV_RX_XXX_RSP_Fbit_SET_X 28
#define LLC_CONN_EV_RX_XXX_YYY 29
#define LLC_CONN_EV_RX_ZZZ_CMD_Pbit_SET_X_INVAL_Nr 30
#define LLC_CONN_EV_RX_ZZZ_RSP_Fbit_SET_X_INVAL_Nr 31
#define LLC_CONN_EV_P_TMR_EXP 32
#define LLC_CONN_EV_ACK_TMR_EXP 33
#define LLC_CONN_EV_REJ_TMR_EXP 34
#define LLC_CONN_EV_BUSY_TMR_EXP 35
#define LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_1 36
#define LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_0 37
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns 38
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns 39
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns 40
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns 41
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 42
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 43
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 44
#define LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 45
#define LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 46
#define LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 47
#define LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 48
#define LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 49
#define LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 50
#define LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 51
#define LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 52
#define LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 53
#define LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 54
#define LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 55
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_1 56
#define LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_1 57
#define LLC_CONN_EV_RX_XXX_RSP_Fbit_SET_1 58
#define LLC_CONN_EV_TX_BUFF_FULL 59
#define LLC_CONN_EV_INIT_P_F_CYCLE 100
/*
* Connection event qualifiers; for some events a certain combination of
* these qualifiers must be TRUE before event recognized valid for state;
* these constants act as indexes into the Event Qualifier function
* table
*/
#define LLC_CONN_EV_QFY_DATA_FLAG_EQ_1 1
#define LLC_CONN_EV_QFY_DATA_FLAG_EQ_0 2
#define LLC_CONN_EV_QFY_DATA_FLAG_EQ_2 3
#define LLC_CONN_EV_QFY_P_FLAG_EQ_1 4
#define LLC_CONN_EV_QFY_P_FLAG_EQ_0 5
#define LLC_CONN_EV_QFY_P_FLAG_EQ_Fbit 6
#define LLC_CONN_EV_QFY_REMOTE_BUSY_EQ_0 7
#define LLC_CONN_EV_QFY_RETRY_CNT_LT_N2 8
#define LLC_CONN_EV_QFY_RETRY_CNT_GTE_N2 9
#define LLC_CONN_EV_QFY_S_FLAG_EQ_1 10
#define LLC_CONN_EV_QFY_S_FLAG_EQ_0 11
#define LLC_CONN_EV_QFY_INIT_P_F_CYCLE 12
/* Event data interface; what is sent in an event package */
/* Event LLC_CONN_EV_TYPE_SIMPLE interface */
struct llc_conn_ev_simple_if {
u8 ev;
};
/* Event LLC_CONN_EV_TYPE_PRIM interface */
struct llc_conn_ev_prim_if {
u8 prim; /* connect, disconnect, reset, ... */
u8 type; /* request, indicate, response, conf */
struct llc_prim_if_block *data;
};
/* Event LLC_CONN_EV_TYPE_PDU interface */
struct llc_conn_ev_pdu_if {
u8 ev;
u8 reason;
struct sk_buff *skb;
};
/* Event interface for timer-generated events */
struct llc_conn_ev_tmr_if {
struct sock *sk;
u32 component_handle;
void *timer_specific;
};
struct llc_conn_ev_rpt_sts_if {
u8 status;
};
union llc_conn_ev_if {
struct llc_conn_ev_simple_if a; /* 'a' for simple, easy ... */
struct llc_conn_ev_prim_if prim;
struct llc_conn_ev_pdu_if pdu;
struct llc_conn_ev_tmr_if tmr;
struct llc_conn_ev_rpt_sts_if rsts; /* report status */
};
struct llc_conn_state_ev {
u8 type;
u8 status;
u8 flag;
struct llc_prim_if_block *ind_prim;
struct llc_prim_if_block *cfm_prim;
union llc_conn_ev_if data;
};
typedef int (*llc_conn_ev_t)(struct sock *sk, struct llc_conn_state_ev *ev);
typedef int (*llc_conn_ev_qfyr_t)(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_conn_req(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_conn_resp(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_data_req(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_disc_req(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rst_req(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rst_resp(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_local_busy_detected(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_local_busy_cleared(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_bad_pdu(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_disc_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_dm_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_frmr_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_cmd_pbit_set_x_inval_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_x_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_x_inval_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rej_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_sabme_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_ua_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_xxx_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_xxx_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_xxx_yyy(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_zzz_cmd_pbit_set_x_inval_nr(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_zzz_rsp_fbit_set_x_inval_nr(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_p_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_ack_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rej_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_busy_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_any_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_sendack_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
/* NOT_USED functions and their variations */
extern int llc_conn_ev_rx_xxx_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_xxx_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_xxx_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rr_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rr_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rr_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rr_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rnr_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rnr_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rnr_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rnr_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rej_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rej_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rej_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rej_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_any_frame(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_tx_buffer_full(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_init_p_f_cycle(struct sock *sk,
struct llc_conn_state_ev *ev);
/* Available connection action qualifiers */
extern int llc_conn_ev_qlfy_data_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_data_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_data_flag_eq_2(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_p_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_last_frame_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_last_frame_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_p_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_p_flag_eq_f(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_remote_busy_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_remote_busy_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_retry_cnt_lt_n2(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_retry_cnt_gte_n2(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_s_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_s_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_cause_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_cause_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_init_p_f_cycle(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_conn(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_disc(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_failed(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_impossible(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_remote_busy(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_received(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_refuse(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_conflict(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_rst_done(struct sock *sk,
struct llc_conn_state_ev *ev);
#endif /* LLC_C_EV_H */
#ifndef LLC_C_ST_H
#define LLC_C_ST_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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.
*/
/* Connection component state management */
/* connection states */
#define LLC_CONN_OUT_OF_SVC 0 /* prior to allocation */
#define LLC_CONN_STATE_ADM 1 /* disc, initial state */
#define LLC_CONN_STATE_SETUP 2 /* disconnected state */
#define LLC_CONN_STATE_NORMAL 3 /* connected state */
#define LLC_CONN_STATE_BUSY 4 /* connected state */
#define LLC_CONN_STATE_REJ 5 /* connected state */
#define LLC_CONN_STATE_AWAIT 6 /* connected state */
#define LLC_CONN_STATE_AWAIT_BUSY 7 /* connected state */
#define LLC_CONN_STATE_AWAIT_REJ 8 /* connected state */
#define LLC_CONN_STATE_D_CONN 9 /* disconnected state */
#define LLC_CONN_STATE_RESET 10 /* disconnected state */
#define LLC_CONN_STATE_ERROR 11 /* disconnected state */
#define LLC_CONN_STATE_TEMP 12 /* disconnected state */
#define NBR_CONN_STATES 12 /* size of state table */
#define NO_STATE_CHANGE 100
/* Connection state table structure */
struct llc_conn_state_trans {
llc_conn_ev_t ev;
u8 next_state;
llc_conn_ev_qfyr_t *ev_qualifiers;
llc_conn_action_t *ev_actions;
};
struct llc_conn_state {
u8 current_state;
struct llc_conn_state_trans **transitions;
};
extern struct llc_conn_state llc_conn_state_table[];
#endif /* LLC_C_ST_H */
#ifndef LLC_CONN_H
#define LLC_CONN_H
/*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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/timer.h>
#include <net/llc_if.h>
#undef DEBUG_LLC_CONN_ALLOC
struct llc_timer {
struct timer_list timer;
u8 running; /* timer is running or no */
u16 expire; /* timer expire time */
};
struct llc_opt {
struct list_head node; /* entry in sap->sk_list.list */
struct sock *sk; /* sock that has this llc_opt */
void *handler; /* for upper layers usage */
u8 state; /* state of connection */
struct llc_sap *sap; /* pointer to parent SAP */
struct llc_addr laddr; /* lsap/mac pair */
struct llc_addr daddr; /* dsap/mac pair */
struct net_device *dev; /* device to send to remote */
u8 retry_count; /* number of retries */
u8 ack_must_be_send;
u8 first_pdu_Ns;
u8 npta;
struct llc_timer ack_timer;
struct llc_timer pf_cycle_timer;
struct llc_timer rej_sent_timer;
struct llc_timer busy_state_timer; /* ind busy clr at remote LLC */
u8 vS; /* seq# next in-seq I-PDU tx'd*/
u8 vR; /* seq# next in-seq I-PDU rx'd*/
u32 n2; /* max nbr re-tx's for timeout*/
u32 n1; /* max nbr octets in I PDU */
u8 k; /* tx window size; max = 127 */
u8 rw; /* rx window size; max = 127 */
u8 p_flag; /* state flags */
u8 f_flag;
u8 s_flag;
u8 data_flag;
u8 remote_busy_flag;
u8 cause_flag;
struct sk_buff_head pdu_unack_q; /* PUDs sent/waiting ack */
u16 link; /* network layer link number */
u8 X; /* a temporary variable */
u8 ack_pf; /* this flag indicates what is
the P-bit of acknowledge */
u8 failed_data_req; /* recognize that already exist a
failed llc_data_req_handler
(tx_buffer_full or unacceptable
state */
u8 dec_step;
u8 inc_cntr;
u8 dec_cntr;
u8 connect_step;
u8 last_nr; /* NR of last pdu recieved */
u32 rx_pdu_hdr; /* used for saving header of last pdu
received and caused sending FRMR.
Used for resending FRMR */
#ifdef DEBUG_LLC_CONN_ALLOC
char *f_alloc, /* function that allocated this connection */
*f_free; /* function that freed this connection */
int l_alloc, /* line that allocated this connection */
l_free; /* line that freed this connection */
#endif
};
#define llc_sk(__sk) ((struct llc_opt *)(__sk)->protinfo)
struct llc_conn_state_ev;
extern struct sock *__llc_sock_alloc(void);
extern void __llc_sock_free(struct sock *sk, u8 free);
#ifdef DEBUG_LLC_CONN_ALLOC
#define dump_stack() printk(KERN_INFO "call trace: %p, %p, %p\n", \
__builtin_return_address(0), \
__builtin_return_address(1), \
__builtin_return_address(2));
#define llc_sock_alloc() ({ \
struct sock *__sk = __llc_sock_alloc(); \
if (__sk) { \
llc_sk(__sk)->f_alloc = __FUNCTION__; \
llc_sk(__sk)->l_alloc = __LINE__; \
} \
__sk;})
#define __llc_sock_assert(__sk) \
if (llc_sk(__sk)->f_free) { \
printk(KERN_ERR \
"%p conn (alloc'd @ %s(%d)) " \
"already freed @ %s(%d) " \
"being used again @ %s(%d)\n", \
llc_sk(__sk), \
llc_sk(__sk)->f_alloc, llc_sk(__sk)->l_alloc, \
llc_sk(__sk)->f_free, llc_sk(__sk)->l_free, \
__FUNCTION__, __LINE__); \
dump_stack();
#define llc_sock_free(__sk) \
{ \
__llc_sock_assert(__sk) \
} else { \
__llc_sock_free(__sk, 0); \
llc_sk(__sk)->f_free = __FUNCTION__; \
llc_sk(__sk)->l_free = __LINE__; \
} \
}
#define llc_sock_assert(__sk) \
{ \
__llc_sock_assert(__sk); \
return; } \
}
#define llc_sock_assert_ret(__sk, __ret) \
{ \
__llc_sock_assert(__sk); \
return __ret; } \
}
#else /* DEBUG_LLC_CONN_ALLOC */
#define llc_sock_alloc() __llc_sock_alloc()
#define llc_sock_free(__sk) __llc_sock_free(__sk, 1)
#define llc_sock_assert(__sk)
#define llc_sock_assert_ret(__sk)
#endif /* DEBUG_LLC_CONN_ALLOC */
extern void llc_sock_reset(struct sock *sk);
extern int llc_sock_init(struct sock *sk);
/* Access to a connection */
extern struct llc_conn_state_ev *llc_conn_alloc_ev(struct sock *sk);
extern int llc_conn_send_ev(struct sock *sk, struct llc_conn_state_ev *ev);
extern void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb);
extern void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb,
struct llc_conn_state_ev *ev);
extern void llc_conn_free_ev(struct llc_conn_state_ev *ev);
extern void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr,
u8 first_p_bit);
extern void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr,
u8 first_f_bit);
extern int llc_conn_remove_acked_pdus(struct sock *conn, u8 nr,
u16 *how_many_unacked);
extern struct sock *llc_find_sock(struct llc_sap *sap, struct llc_addr *daddr,
struct llc_addr *laddr);
extern u8 llc_data_accept_state(u8 state);
extern void llc_build_offset_table(void);
#endif /* LLC_CONN_H */
#ifndef LLC_EVNT_H
#define LLC_EVNT_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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.
*/
/* Station component state transition events */
/* Types of events (possible values in 'ev->type') */
#define LLC_STATION_EV_TYPE_SIMPLE 1
#define LLC_STATION_EV_TYPE_CONDITION 2
#define LLC_STATION_EV_TYPE_PRIM 3
#define LLC_STATION_EV_TYPE_PDU 4 /* command/response PDU */
#define LLC_STATION_EV_TYPE_ACK_TMR 5
#define LLC_STATION_EV_TYPE_RPT_STATUS 6
/* Events */
#define LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK 1
#define LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK 2
#define LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY 3
#define LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY 4
#define LLC_STATION_EV_RX_NULL_DSAP_XID_C 5
#define LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ 6
#define LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ 7
#define LLC_STATION_EV_RX_NULL_DSAP_TEST_C 8
#define LLC_STATION_EV_DISABLE_REQ 9
/* Interfaces for various types of supported events */
struct llc_stat_ev_simple_if {
u8 ev;
};
struct llc_stat_ev_prim_if {
u8 prim; /* connect, disconnect, reset, ... */
u8 type; /* request, indicate, response, confirm */
};
struct llc_stat_ev_pdu_if {
u8 reason;
struct sk_buff *skb;
};
struct llc_stat_ev_tmr_if {
void *timer_specific;
};
struct llc_stat_ev_rpt_sts_if {
u8 status;
};
union llc_stat_ev_if {
struct llc_stat_ev_simple_if a; /* 'a' for simple, easy ... */
struct llc_stat_ev_prim_if prim;
struct llc_stat_ev_pdu_if pdu;
struct llc_stat_ev_tmr_if tmr;
struct llc_stat_ev_rpt_sts_if rsts; /* report status */
};
struct llc_station_state_ev {
u8 type;
union llc_stat_ev_if data;
struct list_head node; /* node in station->ev_q.list */
};
typedef int (*llc_station_ev_t)(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_enable_with_dup_addr_check(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_enable_without_dup_addr_check(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry(struct llc_station *
station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_rx_null_dsap_xid_c(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_rx_null_dsap_test_c(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_disable_req(struct llc_station *station,
struct llc_station_state_ev *ev);
#endif /* LLC_EVNT_H */
#ifndef LLC_IF_H
#define LLC_IF_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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.
*/
/* Defines LLC interface to network layer */
/* Available primitives */
#include <linux/if.h>
#define LLC_DATAUNIT_PRIM 0
#define LLC_CONN_PRIM 1
#define LLC_DATA_PRIM 2
#define LLC_DISC_PRIM 3
#define LLC_RESET_PRIM 4
#define LLC_FLOWCONTROL_PRIM 5
#define LLC_DISABLE_PRIM 6
#define LLC_XID_PRIM 7
#define LLC_TEST_PRIM 8
#define LLC_SAP_ACTIVATION 9
#define LLC_SAP_DEACTIVATION 10
#define LLC_NBR_PRIMITIVES 11
#define LLC_IND 1
#define LLC_CONFIRM 2
/* Primitive type */
#define LLC_PRIM_TYPE_REQ 1
#define LLC_PRIM_TYPE_IND 2
#define LLC_PRIM_TYPE_RESP 3
#define LLC_PRIM_TYPE_CONFIRM 4
/* Reset reasons, remote entity or local LLC */
#define LLC_RESET_REASON_REMOTE 1
#define LLC_RESET_REASON_LOCAL 2
/* Disconnect reasons */
#define LLC_DISC_REASON_RX_DM_RSP_PDU 0
#define LLC_DISC_REASON_RX_DISC_CMD_PDU 1
#define LLC_DISC_REASON_ACK_TMR_EXP 2
/* Confirm reasons */
#define LLC_STATUS_CONN 0 /* connect confirm & reset confirm */
#define LLC_STATUS_DISC 1 /* connect confirm & reset confirm */
#define LLC_STATUS_FAILED 2 /* connect confirm & reset confirm */
#define LLC_STATUS_IMPOSSIBLE 3 /* connect confirm */
#define LLC_STATUS_RECEIVED 4 /* data conn */
#define LLC_STATUS_REMOTE_BUSY 5 /* data conn */
#define LLC_STATUS_REFUSE 6 /* data conn */
#define LLC_STATUS_CONFLICT 7 /* disconnect conn */
#define LLC_STATUS_RESET_DONE 8 /* */
/* Structures and types */
/* SAP/MAC Address pair */
struct llc_addr {
u8 lsap;
u8 mac[IFHWADDRLEN];
};
/* Primitive-specific data */
struct llc_prim_conn {
struct llc_addr saddr; /* used by request only */
struct llc_addr daddr; /* used by request only */
u8 status; /* reason for failure */
u8 pri; /* service_class */
struct net_device *dev;
struct sock *sk; /* returned from REQUEST */
void *handler; /* upper layer use,
stored in llc_opt->handler */
u16 link;
struct sk_buff *skb; /* received SABME */
};
struct llc_prim_disc {
struct sock *sk;
u16 link;
u8 reason; /* not used by request */
};
struct llc_prim_reset {
struct sock *sk;
u16 link;
u8 reason; /* used only by indicate */
};
struct llc_prim_flow_ctrl {
struct sock *sk;
u16 link;
u32 amount;
};
struct llc_prim_data {
struct sock *sk;
u16 link;
u8 pri;
struct sk_buff *skb; /* pointer to frame */
u8 status; /* reason */
};
/* Sending data in conection-less mode */
struct llc_prim_unit_data {
struct llc_addr saddr;
struct llc_addr daddr;
u8 pri;
struct sk_buff *skb; /* pointer to frame */
u8 lfb; /* largest frame bit (TR) */
};
struct llc_prim_xid {
struct llc_addr saddr;
struct llc_addr daddr;
u8 pri;
struct sk_buff *skb;
};
struct llc_prim_test {
struct llc_addr saddr;
struct llc_addr daddr;
u8 pri;
struct sk_buff *skb; /* pointer to frame */
};
union llc_u_prim_data {
struct llc_prim_conn conn;
struct llc_prim_disc disc;
struct llc_prim_reset res;
struct llc_prim_flow_ctrl fc;
struct llc_prim_data data; /* data */
struct llc_prim_unit_data udata; /* unit data */
struct llc_prim_xid xid;
struct llc_prim_test test;
};
struct llc_sap;
/* Information block passed with all called primitives */
struct llc_prim_if_block {
struct llc_sap *sap;
u8 prim;
union llc_u_prim_data *data;
};
typedef int (*llc_prim_call_t)(struct llc_prim_if_block *prim_if);
extern struct llc_sap *llc_sap_open(llc_prim_call_t network_indicate,
llc_prim_call_t network_confirm, u8 lsap);
extern void llc_sap_close(struct llc_sap *sap);
#endif /* LLC_IF_H */
#ifndef LLC_MAC_H
#define LLC_MAC_H
/*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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.
*/
/* Defines MAC-layer interface to LLC layer */
extern int mac_send_pdu(struct sk_buff *skb);
extern int mac_indicate(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt);
extern struct net_device *mac_dev_peer(struct net_device *current_dev,
int type, u8 *mac);
extern int llc_pdu_router(struct llc_sap *sap, struct sock *sk,
struct sk_buff *skb, u8 type);
extern u16 lan_hdrs_init(struct sk_buff *skb, u8 *sa, u8 *da);
#endif /* LLC_MAC_H */
#ifndef LLC_MAIN_H
#define LLC_MAIN_H
/*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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.
*/
#define LLC_EVENT 1
#define LLC_PACKET 2
#define LLC_TYPE_1 1
#define LLC_TYPE_2 2
#define LLC_P_TIME 2
#define LLC_ACK_TIME 3
#define LLC_REJ_TIME 3
#define LLC_BUSY_TIME 3
#define LLC_SENDACK_TIME 50
#define LLC_DEST_INVALID 0 /* Invalid LLC PDU type */
#define LLC_DEST_SAP 1 /* Type 1 goes here */
#define LLC_DEST_CONN 2 /* Type 2 goes here */
/* LLC Layer global default parameters */
#define LLC_GLOBAL_DEFAULT_MAX_NBR_SAPS 4
#define LLC_GLOBAL_DEFAULT_MAX_NBR_CONNS 64
extern struct llc_prim_if_block llc_ind_prim, llc_cfm_prim;
/* LLC station component (SAP and connection resource manager) */
/* Station component; one per adapter */
struct llc_station {
u8 state; /* state of station */
u8 xid_r_count; /* XID response PDU counter */
struct timer_list ack_timer;
u8 ack_tmr_running; /* 1 or 0 */
u8 retry_count;
u8 maximum_retry;
u8 mac_sa[6]; /* MAC source address */
struct {
spinlock_t lock;
struct list_head list;
} sap_list; /* list of related SAPs */
struct {
spinlock_t lock;
struct list_head list;
} ev_q; /* events entering state mach. */
struct sk_buff_head mac_pdu_q; /* PDUs ready to send to MAC */
};
struct llc_station_state_ev;
extern struct llc_sap *llc_sap_alloc(void);
extern void llc_sap_save(struct llc_sap *sap);
extern void llc_free_sap(struct llc_sap *sap);
extern struct llc_sap *llc_sap_find(u8 lsap);
extern struct llc_station *llc_station_get(void);
extern struct llc_station_state_ev *
llc_station_alloc_ev(struct llc_station *station);
extern void llc_station_send_ev(struct llc_station *station,
struct llc_station_state_ev *ev);
extern void llc_station_send_pdu(struct llc_station *station,
struct sk_buff *skb);
extern struct sk_buff *llc_alloc_frame(void);
#endif /* LLC_MAIN_H */
#ifndef LLC_PDU_H
#define LLC_PDU_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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.
*/
/* LLC PDU structure */
/* Lengths of frame formats */
#define LLC_PDU_LEN_I 4 /* header and 2 control bytes */
#define LLC_PDU_LEN_S 4
#define LLC_PDU_LEN_U 3 /* header and 1 control byte */
/* Known SAP addresses */
#define LLC_GLOBAL_SAP 0xFF
#define LLC_NULL_SAP 0x00 /* not network-layer visible */
#define LLC_MGMT_INDIV 0x02 /* station LLC mgmt indiv addr */
#define LLC_MGMT_GRP 0x03 /* station LLC mgmt group addr */
#define LLC_RDE_SAP 0xA6 /* route ... */
/* SAP field bit masks */
#define LLC_ISO_RESERVED_SAP 0x02
#define LLC_SAP_GROUP_DSAP 0x01
#define LLC_SAP_RESP_SSAP 0x01
/* Group/individual DSAP indicator is DSAP field */
#define LLC_PDU_GROUP_DSAP_MASK 0x01
#define LLC_PDU_IS_GROUP_DSAP(pdu) \
((pdu->dsap & LLC_PDU_GROUP_DSAP_MASK) ? 0 : 1)
#define LLC_PDU_IS_INDIV_DSAP(pdu) \
(!(pdu->dsap & LLC_PDU_GROUP_DSAP_MASK) ? 0 : 1)
/* Command/response PDU indicator in SSAP field */
#define LLC_PDU_CMD_RSP_MASK 0x01
#define LLC_PDU_CMD 0
#define LLC_PDU_RSP 1
#define LLC_PDU_IS_CMD(pdu) ((pdu->ssap & LLC_PDU_RSP) ? 1 : 0)
#define LLC_PDU_IS_RSP(pdu) ((pdu->ssap & LLC_PDU_RSP) ? 0 : 1)
/* Get PDU type from 2 lowest-order bits of control field first byte */
#define LLC_PDU_TYPE_I_MASK 0x01 /* 16-bit control field */
#define LLC_PDU_TYPE_S_MASK 0x03
#define LLC_PDU_TYPE_U_MASK 0x03 /* 8-bit control field */
#define LLC_PDU_TYPE_MASK 0x03
#define LLC_PDU_TYPE_I 0 /* first bit */
#define LLC_PDU_TYPE_S 1 /* first two bits */
#define LLC_PDU_TYPE_U 3 /* first two bits */
#define LLC_PDU_TYPE_IS_I(pdu) \
((!(pdu->ctrl_1 & LLC_PDU_TYPE_I_MASK)) ? 0 : 1)
#define LLC_PDU_TYPE_IS_U(pdu) \
(((pdu->ctrl_1 & LLC_PDU_TYPE_U_MASK) == LLC_PDU_TYPE_U) ? 0 : 1)
#define LLC_PDU_TYPE_IS_S(pdu) \
(((pdu->ctrl_1 & LLC_PDU_TYPE_S_MASK) == LLC_PDU_TYPE_S) ? 0 : 1)
/* U-format PDU control field masks */
#define LLC_U_PF_BIT_MASK 0x10 /* P/F bit mask */
#define LLC_U_PF_IS_1(pdu) ((pdu->ctrl_1 & LLC_U_PF_BIT_MASK) ? 0 : 1)
#define LLC_U_PF_IS_0(pdu) ((!(pdu->ctrl_1 & LLC_U_PF_BIT_MASK)) ? 0 : 1)
#define LLC_U_PDU_CMD_MASK 0xEC /* cmd/rsp mask */
#define LLC_U_PDU_CMD(pdu) (pdu->ctrl_1 & LLC_U_PDU_CMD_MASK)
#define LLC_U_PDU_RSP(pdu) (pdu->ctrl_1 & LLC_U_PDU_CMD_MASK)
#define LLC_1_PDU_CMD_UI 0x00 /* Type 1 cmds/rsps */
#define LLC_1_PDU_CMD_XID 0xAC
#define LLC_1_PDU_CMD_TEST 0xE0
#define LLC_2_PDU_CMD_SABME 0x6C /* Type 2 cmds/rsps */
#define LLC_2_PDU_CMD_DISC 0x40
#define LLC_2_PDU_RSP_UA 0x60
#define LLC_2_PDU_RSP_DM 0x0C
#define LLC_2_PDU_RSP_FRMR 0x84
/* Type 1 operations */
/* XID information field bit masks */
/* LLC format identifier (byte 1) */
#define LLC_XID_FMT_ID 0x81 /* first byte must be this */
/* LLC types/classes identifier (byte 2) */
#define LLC_XID_CLASS_ZEROS_MASK 0xE0 /* these must be zeros */
#define LLC_XID_CLASS_MASK 0x1F /* AND with byte to get below */
#define LLC_XID_NULL_CLASS_1 0x01 /* if NULL LSAP...use these */
#define LLC_XID_NULL_CLASS_2 0x03
#define LLC_XID_NULL_CLASS_3 0x05
#define LLC_XID_NULL_CLASS_4 0x07
#define LLC_XID_NNULL_TYPE_1 0x01 /* if non-NULL LSAP...use these */
#define LLC_XID_NNULL_TYPE_2 0x02
#define LLC_XID_NNULL_TYPE_3 0x04
#define LLC_XID_NNULL_TYPE_1_2 0x03
#define LLC_XID_NNULL_TYPE_1_3 0x05
#define LLC_XID_NNULL_TYPE_2_3 0x06
#define LLC_XID_NNULL_ALL 0x07
/* Sender Receive Window (byte 3) */
#define LLC_XID_RW_MASK 0xFE /* AND with value to get below */
#define LLC_XID_MIN_RW 0x02 /* lowest-order bit always zero */
/* Type 2 operations */
#define LLC_2_SEQ_NBR_MODULO ((u8) 128)
/* I-PDU masks ('ctrl' is I-PDU control word) */
#define LLC_I_GET_NS(pdu) (u8)((pdu->ctrl_1 & 0xFE) >> 1)
#define LLC_I_GET_NR(pdu) (u8)((pdu->ctrl_2 & 0xFE) >> 1)
#define LLC_I_PF_BIT_MASK 0x01
#define LLC_I_PF_IS_0(pdu) ((!(pdu->ctrl_2 & LLC_I_PF_BIT_MASK)) ? 0 : 1)
#define LLC_I_PF_IS_1(pdu) ((pdu->ctrl_2 & LLC_I_PF_BIT_MASK) ? 0 : 1)
/* S-PDU supervisory commands and responses */
#define LLC_S_PDU_CMD_MASK 0x0C
#define LLC_S_PDU_CMD(pdu) (pdu->ctrl_1 & LLC_S_PDU_CMD_MASK)
#define LLC_S_PDU_RSP(pdu) (pdu->ctrl_1 & LLC_S_PDU_CMD_MASK)
#define LLC_2_PDU_CMD_RR 0x00 /* rx ready cmd */
#define LLC_2_PDU_RSP_RR 0x00 /* rx ready rsp */
#define LLC_2_PDU_CMD_REJ 0x08 /* reject PDU cmd */
#define LLC_2_PDU_RSP_REJ 0x08 /* reject PDU rsp */
#define LLC_2_PDU_CMD_RNR 0x04 /* rx not ready cmd */
#define LLC_2_PDU_RSP_RNR 0x04 /* rx not ready rsp */
#define LLC_S_PF_BIT_MASK 0x01
#define LLC_S_PF_IS_0(pdu) ((!(pdu->ctrl_2 & LLC_S_PF_BIT_MASK)) ? 0 : 1)
#define LLC_S_PF_IS_1(pdu) ((pdu->ctrl_2 & LLC_S_PF_BIT_MASK) ? 0 : 1)
#define PDU_SUPV_GET_Nr(pdu) ((pdu->ctrl_2 & 0xFE) >> 1)
#define PDU_GET_NEXT_Vr(sn) (++sn & ~LLC_2_SEQ_NBR_MODULO)
/* FRMR information field macros */
#define FRMR_INFO_LENGTH 5 /* 5 bytes of information */
/*
* info is pointer to FRMR info field structure; 'rej_ctrl' is byte pointer
* (if U-PDU) or word pointer to rejected PDU control field
*/
#define FRMR_INFO_SET_REJ_CNTRL(info,rej_ctrl) \
info->rej_pdu_ctrl = ((*((u8 *) rej_ctrl) & \
LLC_PDU_TYPE_U) != LLC_PDU_TYPE_U ? \
(u16)*((u16 *) rej_ctrl) : \
(((u16) *((u8 *) rej_ctrl)) & 0x00FF))
/*
* Info is pointer to FRMR info field structure; 'vs' is a byte containing
* send state variable value in low-order 7 bits (insure the lowest-order
* bit remains zero (0))
*/
#define FRMR_INFO_SET_Vs(info,vs) (info->curr_ssv = (((u8) vs) << 1))
#define FRMR_INFO_SET_Vr(info,vr) (info->curr_rsv = (((u8) vr) << 1))
/*
* Info is pointer to FRMR info field structure; 'cr' is a byte containing
* the C/R bit value in the low-order bit
*/
#define FRMR_INFO_SET_C_R_BIT(info, cr) (info->curr_rsv |= (((u8) cr) & 0x01))
/*
* In the remaining five macros, 'info' is pointer to FRMR info field
* structure; 'ind' is a byte containing the bit value to set in the
* lowest-order bit)
*/
#define FRMR_INFO_SET_INVALID_PDU_CTRL_IND(info, ind) \
(info->ind_bits = ((info->ind_bits & 0xFE) | (((u8) ind) & 0x01)))
#define FRMR_INFO_SET_INVALID_PDU_INFO_IND(info, ind) \
(info->ind_bits = ( (info->ind_bits & 0xFD) | (((u8) ind) & 0x02)))
#define FRMR_INFO_SET_PDU_INFO_2LONG_IND(info, ind) \
(info->ind_bits = ( (info->ind_bits & 0xFB) | (((u8) ind) & 0x04)))
#define FRMR_INFO_SET_PDU_INVALID_Nr_IND(info, ind) \
(info->ind_bits = ( (info->ind_bits & 0xF7) | (((u8) ind) & 0x08)))
#define FRMR_INFO_SET_PDU_INVALID_Ns_IND(info, ind) \
(info->ind_bits = ( (info->ind_bits & 0xEF) | (((u8) ind) & 0x10)))
/* Sequence-numbered PDU format (4 bytes in length) */
typedef struct llc_pdu_sn {
u8 dsap;
u8 ssap;
u8 ctrl_1;
u8 ctrl_2;
} llc_pdu_sn_t;
/* Un-numbered PDU format (3 bytes in length) */
typedef struct llc_pdu_un {
u8 dsap;
u8 ssap;
u8 ctrl_1;
} llc_pdu_un_t;
/* LLC Type 1 XID command/response information fields format */
typedef struct llc_xid_info {
u8 fmt_id; /* always 0x18 for LLC */
u8 type; /* different if NULL/non-NULL LSAP */
u8 rw; /* sender receive window */
} llc_xid_info_t;
/* LLC Type 2 FRMR response information field format */
typedef struct llc_frmr_info {
u16 rej_pdu_ctrl; /* bits 1-8 if U-PDU */
u8 curr_ssv; /* current send state variable val */
u8 curr_rsv; /* current receive state variable */
u8 ind_bits; /* indicator bits set with macro */
} llc_frmr_info_t;
extern void llc_pdu_set_cmd_rsp(struct sk_buff *skb, u8 type);
extern void llc_pdu_set_pf_bit(struct sk_buff *skb, u8 bit_value);
extern int llc_pdu_decode_pf_bit(struct sk_buff *skb, u8 *pf_bit);
extern int llc_pdu_decode_cr_bit(struct sk_buff *skb, u8 *cr_bit);
extern int llc_pdu_decode_sa(struct sk_buff *skb, u8 *sa);
extern int llc_pdu_decode_da(struct sk_buff *skb, u8 *ds);
extern int llc_pdu_decode_dsap(struct sk_buff *skb, u8 *dsap);
extern int llc_pdu_decode_ssap(struct sk_buff *skb, u8 *ssap);
extern int llc_decode_pdu_type(struct sk_buff *skb, u8 *destination);
extern void llc_pdu_header_init(struct sk_buff *skb, u8 pdu_type, u8 ssap,
u8 dsap, u8 cr);
extern int llc_pdu_init_as_ui_cmd(struct sk_buff *skb);
extern int llc_pdu_init_as_xid_cmd(struct sk_buff *skb, u8 svcs_supported,
u8 rx_window);
extern int llc_pdu_init_as_test_cmd(struct sk_buff *skb);
extern int llc_pdu_init_as_disc_cmd(struct sk_buff *skb, u8 p_bit);
extern int llc_pdu_init_as_i_cmd(struct sk_buff *skb, u8 p_bit, u8 ns, u8 nr);
extern int llc_pdu_init_as_rej_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
extern int llc_pdu_init_as_rnr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
extern int llc_pdu_init_as_rr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
extern int llc_pdu_init_as_sabme_cmd(struct sk_buff *skb, u8 p_bit);
extern int llc_pdu_init_as_dm_rsp(struct sk_buff *skb, u8 f_bit);
extern int llc_pdu_init_as_xid_rsp(struct sk_buff *skb, u8 svcs_supported,
u8 rx_window);
extern int llc_pdu_init_as_test_rsp(struct sk_buff *skb,
struct sk_buff *ev_skb);
extern int llc_pdu_init_as_frmr_rsp(struct sk_buff *skb, llc_pdu_sn_t *prev_pdu,
u8 f_bit, u8 vs, u8 vr, u8 vzyxw);
extern int llc_pdu_init_as_rr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
extern int llc_pdu_init_as_rej_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
extern int llc_pdu_init_as_rnr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
extern int llc_pdu_init_as_ua_rsp(struct sk_buff *skb, u8 f_bit);
#endif /* LLC_PDU_H */
#ifndef LLC_S_AC_H
#define LLC_S_AC_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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.
*/
/* SAP component actions */
#define SAP_ACT_UNITDATA_IND 1
#define SAP_ACT_SEND_UI 2
#define SAP_ACT_SEND_XID_C 3
#define SAP_ACT_SEND_XID_R 4
#define SAP_ACT_SEND_TEST_C 5
#define SAP_ACT_SEND_TEST_R 6
#define SAP_ACT_REPORT_STATUS 7
#define SAP_ACT_XID_IND 8
#define SAP_ACT_TEST_IND 9
/* All action functions must look like this */
typedef int (*llc_sap_action_t)(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_unitdata_ind(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_send_ui(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_send_xid_c(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_send_xid_r(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_send_test_c(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_send_test_r(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_report_status(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_xid_ind(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_test_ind(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
#endif /* LLC_S_AC_H */
#ifndef LLC_S_EV_H
#define LLC_S_EV_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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.
*/
/* Defines SAP component events */
/* Types of events (possible values in 'ev->type') */
#define LLC_SAP_EV_TYPE_SIMPLE 1
#define LLC_SAP_EV_TYPE_CONDITION 2
#define LLC_SAP_EV_TYPE_PRIM 3
#define LLC_SAP_EV_TYPE_PDU 4 /* command/response PDU */
#define LLC_SAP_EV_TYPE_ACK_TMR 5
#define LLC_SAP_EV_TYPE_RPT_STATUS 6
#define LLC_SAP_EV_ACTIVATION_REQ 1
#define LLC_SAP_EV_RX_UI 2
#define LLC_SAP_EV_UNITDATA_REQ 3
#define LLC_SAP_EV_XID_REQ 4
#define LLC_SAP_EV_RX_XID_C 5
#define LLC_SAP_EV_RX_XID_R 6
#define LLC_SAP_EV_TEST_REQ 7
#define LLC_SAP_EV_RX_TEST_C 8
#define LLC_SAP_EV_RX_TEST_R 9
#define LLC_SAP_EV_DEACTIVATION_REQ 10
/* Interfaces for various types of supported events */
struct llc_sap_ev_simple_if {
u8 ev;
};
struct llc_prim_if_block;
struct llc_sap_ev_prim_if {
u8 prim; /* connect, disconnect, reset, ... */
u8 type; /* request, indicate, response, conf */
struct llc_prim_if_block *data;
};
struct llc_sap_ev_pdu_if {
u8 ev;
u8 reason;
struct sk_buff *skb;
};
struct llc_sap_ev_tmr_if {
void *timer_specific;
};
struct llc_sap_ev_rpt_sts_if {
u8 status;
};
union llc_sap_ev_if {
struct llc_sap_ev_simple_if a; /* 'a' for simple, easy ... */
struct llc_sap_ev_prim_if prim;
struct llc_sap_ev_pdu_if pdu;
struct llc_sap_ev_tmr_if tmr;
struct llc_sap_ev_rpt_sts_if rsts; /* report status */
};
struct llc_prim_if_block;
struct llc_sap_state_ev {
u8 type;
u8 ind_cfm_flag;
struct llc_prim_if_block *prim;
union llc_sap_ev_if data;
};
struct llc_sap;
typedef int (*llc_sap_ev_t)(struct llc_sap *sap, struct llc_sap_state_ev *ev);
extern int llc_sap_ev_activation_req(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_rx_ui(struct llc_sap *sap, struct llc_sap_state_ev *ev);
extern int llc_sap_ev_unitdata_req(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_xid_req(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_rx_xid_c(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_rx_xid_r(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_test_req(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_rx_test_c(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_rx_test_r(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_deactivation_req(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
#endif /* LLC_S_EV_H */
#ifndef LLC_S_ST_H
#define LLC_S_ST_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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.
*/
/* Defines SAP component states */
#define LLC_SAP_STATE_INACTIVE 1
#define LLC_SAP_STATE_ACTIVE 2
#define LLC_NBR_SAP_STATES 2 /* size of state table */
/* structures and types */
/* SAP state table structure */
struct llc_sap_state_trans {
llc_sap_ev_t ev;
u8 next_state;
llc_sap_action_t *ev_actions;
};
struct llc_sap_state {
u8 curr_state;
struct llc_sap_state_trans **transitions;
};
/* only access to SAP state table */
extern struct llc_sap_state llc_sap_state_table[LLC_NBR_SAP_STATES];
#endif /* LLC_S_ST_H */
#ifndef LLC_SAP_H
#define LLC_SAP_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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/skbuff.h>
/* Defines the SAP component */
struct llc_sap {
u8 state;
struct llc_station *parent_station;
u8 p_bit; /* only lowest-order bit used */
u8 f_bit; /* only lowest-order bit used */
llc_prim_call_t req; /* provided by LLC layer */
llc_prim_call_t resp; /* provided by LLC layer */
llc_prim_call_t ind; /* provided by network layer */
llc_prim_call_t conf; /* provided by network layer */
struct llc_addr laddr; /* SAP value in this 'lsap' */
struct list_head node; /* entry in station sap_list */
struct {
spinlock_t lock;
struct list_head list;
} sk_list; /* LLC sockets this one manages */
struct sk_buff_head mac_pdu_q; /* PDUs ready to send to MAC */
};
struct llc_sap_state_ev;
extern void llc_sap_assign_sock(struct llc_sap *sap, struct sock *sk);
extern void llc_sap_unassign_sock(struct llc_sap *sap, struct sock *sk);
extern void llc_sap_send_ev(struct llc_sap *sap, struct llc_sap_state_ev *ev);
extern void llc_sap_rtn_pdu(struct llc_sap *sap, struct sk_buff *skb,
struct llc_sap_state_ev *ev);
extern void llc_sap_send_pdu(struct llc_sap *sap, struct sk_buff *skb);
extern struct llc_sap_state_ev *llc_sap_alloc_ev(struct llc_sap *sap);
#endif /* LLC_SAP_H */
#ifndef LLC_STAT_H
#define LLC_STAT_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 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.
*/
/* Station component state table */
/* Station component states */
#define LLC_STATION_STATE_DOWN 1 /* initial state */
#define LLC_STATION_STATE_DUP_ADDR_CHK 2
#define LLC_STATION_STATE_UP 3
#define LLC_NBR_STATION_STATES 3 /* size of state table */
/* Station component state table structure */
struct llc_station_state_trans {
llc_station_ev_t ev;
u8 next_state;
llc_station_action_t *ev_actions;
};
struct llc_station_state {
u8 curr_state;
struct llc_station_state_trans **transitions;
};
extern struct llc_station_state llc_station_state_table[LLC_NBR_STATION_STATES];
#endif /* LLC_STAT_H */
#ifndef _NET_P8022_H #ifndef _NET_P8022_H
#define _NET_P8022_H #define _NET_P8022_H
extern struct datalink_proto *register_8022_client(unsigned char type,
extern struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct net_device *, struct packet_type *)); int (*rcvfunc)
(struct sk_buff *,
struct net_device *,
struct packet_type *));
extern void unregister_8022_client(unsigned char type); extern void unregister_8022_client(unsigned char type);
#endif #endif
...@@ -11,11 +11,10 @@ ...@@ -11,11 +11,10 @@
* matches. The control byte is ignored and handling of such items * matches. The control byte is ignored and handling of such items
* is up to the routine passed the frame. * is up to the routine passed the frame.
* *
* Unlike the 802.3 datalink we have a list of 802.2 entries as there * Unlike the 802.3 datalink we have a list of 802.2 entries as
* are multiple protocols to demux. The list is currently short (3 or * there are multiple protocols to demux. The list is currently
* 4 entries at most). The current demux assumes this. * short (3 or 4 entries at most). The current demux assumes this.
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
...@@ -25,8 +24,13 @@ ...@@ -25,8 +24,13 @@
#include <linux/init.h> #include <linux/init.h>
#include <net/p8022.h> #include <net/p8022.h>
static struct datalink_proto *p8022_list = NULL; extern void llc_register_sap(unsigned char sap,
int (*rcvfunc)(struct sk_buff *,
struct net_device *,
struct packet_type *));
extern void llc_unregister_sap(unsigned char sap);
static struct datalink_proto *p8022_list;
/* /*
* We don't handle the loopback SAP stuff, the extended * We don't handle the loopback SAP stuff, the extended
* 802.2 command set, multicast SAP identifiers and non UI * 802.2 command set, multicast SAP identifiers and non UI
...@@ -34,91 +38,68 @@ static struct datalink_proto *p8022_list = NULL; ...@@ -34,91 +38,68 @@ static struct datalink_proto *p8022_list = NULL;
* IP and Appletalk phase 2. See the llc_* routines for * IP and Appletalk phase 2. See the llc_* routines for
* support libraries if your protocol needs these. * support libraries if your protocol needs these.
*/ */
static struct datalink_proto *find_8022_client(unsigned char type) static struct datalink_proto *find_8022_client(unsigned char type)
{ {
struct datalink_proto *proto; struct datalink_proto *proto = p8022_list;
for (proto = p8022_list;
((proto != NULL) && (*(proto->type) != type));
proto = proto->next)
;
while (proto && *(proto->type) != type)
proto = proto->next;
return proto; return proto;
} }
int p8022_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) int p8022_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt)
{ {
struct datalink_proto *proto; struct datalink_proto *proto;
int rc = 0;
proto = find_8022_client(*(skb->h.raw)); proto = find_8022_client(*(skb->h.raw));
if (proto != NULL) if (!proto) {
{ skb->sk = NULL;
skb->h.raw += 3; kfree_skb(skb);
skb->nh.raw += 3; goto out;
skb_pull(skb,3);
return proto->rcvfunc(skb, dev, pt);
} }
skb->h.raw += 3;
skb->sk = NULL; skb->nh.raw += 3;
kfree_skb(skb); skb_pull(skb, 3);
return 0; rc = proto->rcvfunc(skb, dev, pt);
out: return rc;
} }
static void p8022_datalink_header(struct datalink_proto *dl, static void p8022_datalink_header(struct datalink_proto *dl,
struct sk_buff *skb, unsigned char *dest_node) struct sk_buff *skb, unsigned char *dest_node)
{ {
struct net_device *dev = skb->dev; struct net_device *dev = skb->dev;
unsigned char *rawp; unsigned char *rawp = skb_push(skb, 3);
rawp = skb_push(skb,3);
*rawp++ = dl->type[0]; *rawp++ = dl->type[0];
*rawp++ = dl->type[0]; *rawp++ = dl->type[0];
*rawp = 0x03; /* UI */ *rawp = 0x03; /* UI */
dev->hard_header(skb, dev, ETH_P_802_3, dest_node, NULL, skb->len); dev->hard_header(skb, dev, ETH_P_802_3, dest_node, NULL, skb->len);
} }
static struct packet_type p8022_packet_type = struct datalink_proto *register_8022_client(unsigned char type,
{ int (*rcvfunc)(struct sk_buff *,
0, /* MUTTER ntohs(ETH_P_8022),*/ struct net_device *,
NULL, /* All devices */ struct packet_type *))
p8022_rcv,
NULL,
NULL,
};
EXPORT_SYMBOL(register_8022_client);
EXPORT_SYMBOL(unregister_8022_client);
static int __init p8022_init(void)
{
p8022_packet_type.type=htons(ETH_P_802_2);
dev_add_pack(&p8022_packet_type);
return 0;
}
module_init(p8022_init);
struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct net_device *, struct packet_type *))
{ {
struct datalink_proto *proto; struct datalink_proto *proto = NULL;
if (find_8022_client(type) != NULL) if (find_8022_client(type))
return NULL; goto out;
proto = kmalloc(sizeof(*proto), GFP_ATOMIC);
proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC); if (proto) {
if (proto != NULL) { proto->type[0] = type;
proto->type[0] = type; proto->type_len = 1;
proto->type_len = 1; proto->rcvfunc = rcvfunc;
proto->rcvfunc = rcvfunc; proto->header_length = 3;
proto->header_length = 3; proto->datalink_header = p8022_datalink_header;
proto->datalink_header = p8022_datalink_header; proto->string_name = "802.2";
proto->string_name = "802.2"; proto->next = p8022_list;
proto->next = p8022_list; p8022_list = proto;
p8022_list = proto; llc_register_sap(type, p8022_rcv);
} }
out: return proto;
return proto;
} }
void unregister_8022_client(unsigned char type) void unregister_8022_client(unsigned char type)
...@@ -128,17 +109,18 @@ void unregister_8022_client(unsigned char type) ...@@ -128,17 +109,18 @@ void unregister_8022_client(unsigned char type)
save_flags(flags); save_flags(flags);
cli(); cli();
while (*clients) {
while ((tmp = *clients) != NULL) tmp = *clients;
{
if (tmp->type[0] == type) { if (tmp->type[0] == type) {
*clients = tmp->next; *clients = tmp->next;
kfree(tmp); kfree(tmp);
llc_unregister_sap(type);
break; break;
} else {
clients = &tmp->next;
} }
clients = &tmp->next;
} }
restore_flags(flags); restore_flags(flags);
} }
EXPORT_SYMBOL(register_8022_client);
EXPORT_SYMBOL(unregister_8022_client);
...@@ -64,11 +64,12 @@ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET ...@@ -64,11 +64,12 @@ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25 tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC tristate 'ANSI/IEEE 802.2 Data link layer protocol' CONFIG_LLC
if [ "$CONFIG_LLC" != "n" ]; then
# When NETBEUI is added the following line will be a tristate
define_bool CONFIG_LLC_UI y
fi
bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT
# if [ "$CONFIG_LLC" = "y" ]; then
# bool ' Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
# fi
if [ "$CONFIG_INET" = "y" ]; then if [ "$CONFIG_INET" = "y" ]; then
tristate 'Acorn Econet/AUN protocols (EXPERIMENTAL)' CONFIG_ECONET tristate 'Acorn Econet/AUN protocols (EXPERIMENTAL)' CONFIG_ECONET
if [ "$CONFIG_ECONET" != "n" ]; then if [ "$CONFIG_ECONET" != "n" ]; then
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
O_TARGET := network.o O_TARGET := network.o
mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink llc sched
export-objs := netsyms.o export-objs := netsyms.o
subdir-y := core ethernet subdir-y := core ethernet
...@@ -44,8 +44,8 @@ subdir-$(CONFIG_SUNRPC) += sunrpc ...@@ -44,8 +44,8 @@ subdir-$(CONFIG_SUNRPC) += sunrpc
subdir-$(CONFIG_ATM) += atm subdir-$(CONFIG_ATM) += atm
subdir-$(CONFIG_DECNET) += decnet subdir-$(CONFIG_DECNET) += decnet
subdir-$(CONFIG_ECONET) += econet subdir-$(CONFIG_ECONET) += econet
subdir-$(CONFIG_VLAN_8021Q) += 8021q subdir-$(CONFIG_VLAN_8021Q) += 8021q
subdir-$(CONFIG_LLC) += llc
obj-y := socket.o $(join $(subdir-y), $(patsubst %,/%.o,$(notdir $(subdir-y)))) obj-y := socket.o $(join $(subdir-y), $(patsubst %,/%.o,$(notdir $(subdir-y))))
ifeq ($(CONFIG_NET),y) ifeq ($(CONFIG_NET),y)
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
O_TARGET := core.o O_TARGET := core.o
export-objs := netfilter.o profile.o export-objs := ext8022.o netfilter.o profile.o
obj-y := sock.o skbuff.o iovec.o datagram.o scm.o obj-y := sock.o skbuff.o iovec.o datagram.o scm.o
...@@ -23,6 +23,10 @@ obj-$(CONFIG_FILTER) += filter.o ...@@ -23,6 +23,10 @@ obj-$(CONFIG_FILTER) += filter.o
obj-$(CONFIG_NET) += dev.o dev_mcast.o dst.o neighbour.o rtnetlink.o utils.o obj-$(CONFIG_NET) += dev.o dev_mcast.o dst.o neighbour.o rtnetlink.o utils.o
ifneq ($(CONFIG_LLC),n)
obj-y += ext8022.o
endif
obj-$(CONFIG_NETFILTER) += netfilter.o obj-$(CONFIG_NETFILTER) += netfilter.o
obj-$(CONFIG_NET_DIVERT) += dv.o obj-$(CONFIG_NET_DIVERT) += dv.o
obj-$(CONFIG_NET_PROFILE) += profile.o obj-$(CONFIG_NET_PROFILE) += profile.o
......
/*
* (ext8022.c)
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/brlock.h>
typedef int (*func_type)(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt);
static int llc_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *);
static func_type llc_sap_table[128];
static int llc_users;
static struct packet_type llc_packet_type = {
type: __constant_htons(ETH_P_802_2),
func: llc_rcv,
};
static struct packet_type llc_tr_packet_type = {
type: __constant_htons(ETH_P_TR_802_2),
func: llc_rcv,
};
static int llc_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt)
{
unsigned char n = (*(skb->h.raw)) >> 1;
br_read_lock(BR_LLC_LOCK);
if (llc_sap_table[n])
llc_sap_table[n](skb, dev, pt);
else
kfree_skb(skb);
br_read_unlock(BR_LLC_LOCK);
return 0;
}
void llc_register_sap(unsigned char sap, func_type rcvfunc)
{
sap >>= 1;
br_write_lock_bh(BR_LLC_LOCK);
llc_sap_table[sap] = rcvfunc;
if (!llc_users) {
dev_add_pack(&llc_packet_type);
dev_add_pack(&llc_tr_packet_type);
}
llc_users++;
br_write_unlock_bh(BR_LLC_LOCK);
}
void llc_unregister_sap(unsigned char sap)
{
sap >>= 1;
br_write_lock_bh(BR_LLC_LOCK);
llc_sap_table[sap] = NULL;
if (!--llc_users) {
dev_remove_pack(&llc_packet_type);
dev_remove_pack(&llc_tr_packet_type);
}
br_write_unlock_bh(BR_LLC_LOCK);
}
EXPORT_SYMBOL(llc_register_sap);
EXPORT_SYMBOL(llc_unregister_sap);
###########################################################################
# Makefile for the Linux 802.2 LLC (fully-functional) layer.
#
# Note 1! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definition is now in the main makefile...
#
# Copyright (c) 1997 by Procom Technology,Inc.
# 2001 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.
###########################################################################
O_TARGET := llc.o
obj-y := llc_if.o llc_c_ev.o llc_c_ac.o llc_mac.o llc_sap.o llc_s_st.o \
llc_main.o llc_s_ac.o llc_conn.o llc_c_st.o llc_stat.o llc_actn.o \
llc_s_ev.o llc_evnt.o llc_pdu.o
ifeq ($(CONFIG_LLC_UI),y)
obj-y += llc_sock.o
endif
# Objects that export symbols.
export-objs := llc_if.o
ifeq ($(CONFIG_LLC),m)
obj-m += $(O_TARGET)
endif
include $(TOPDIR)/Rules.make
/*
* llc_actn.c - Implementation of actions of station component of LLC
*
* Description :
* Functions in this module are implementation of station component actions.
* Details of actions can be found in IEEE-802.2 standard document.
* All functions have one station and one event as input argument. All of
* them return 0 On success and 1 otherwise.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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_if.h>
#include <net/llc_main.h>
#include <net/llc_evnt.h>
#include <net/llc_pdu.h>
#include <net/llc_mac.h>
static void llc_station_ack_tmr_callback(unsigned long timeout_data);
int llc_station_ac_start_ack_timer(struct llc_station *station,
struct llc_station_state_ev *ev)
{
del_timer(&station->ack_timer);
station->ack_timer.expires = jiffies + LLC_ACK_TIME * HZ;
station->ack_timer.data = (unsigned long)station;
station->ack_timer.function = llc_station_ack_tmr_callback;
add_timer(&station->ack_timer);
station->ack_tmr_running = 1;
return 0;
}
int llc_station_ac_set_retry_cnt_0(struct llc_station *station,
struct llc_station_state_ev *ev)
{
station->retry_count = 0;
return 0;
}
int llc_station_ac_inc_retry_cnt_by_1(struct llc_station *station,
struct llc_station_state_ev *ev)
{
station->retry_count++;
return 0;
}
int llc_station_ac_set_xid_r_cnt_0(struct llc_station *station,
struct llc_station_state_ev *ev)
{
station->xid_r_count = 0;
return 0;
}
int llc_station_ac_inc_xid_r_cnt_by_1(struct llc_station *station,
struct llc_station_state_ev *ev)
{
station->xid_r_count++;
return 0;
}
int llc_station_ac_send_null_dsap_xid_c(struct llc_station *station,
struct llc_station_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (!skb)
goto out;
rc = 0;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, 0, 0, LLC_PDU_CMD);
llc_pdu_init_as_xid_cmd(skb, LLC_XID_NULL_CLASS_2, 127);
lan_hdrs_init(skb, station->mac_sa, station->mac_sa);
llc_station_send_pdu(station, skb);
out:
return rc;
}
int llc_station_ac_send_xid_r(struct llc_station *station,
struct llc_station_state_ev *ev)
{
u8 mac_da[ETH_ALEN], dsap;
int rc = 1;
struct sk_buff *ev_skb;
struct sk_buff* skb = llc_alloc_frame();
if (!skb)
goto out;
rc = 0;
ev_skb = ev->data.pdu.skb;
skb->dev = ev_skb->dev;
llc_pdu_decode_sa(ev_skb, mac_da);
llc_pdu_decode_ssap(ev_skb, &dsap);
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
llc_pdu_init_as_xid_rsp(skb, LLC_XID_NULL_CLASS_2, 127);
lan_hdrs_init(skb, station->mac_sa, mac_da);
llc_station_send_pdu(station, skb);
out:
return rc;
}
int llc_station_ac_send_test_r(struct llc_station *station,
struct llc_station_state_ev *ev)
{
u8 mac_da[ETH_ALEN], dsap;
int rc = 1;
struct sk_buff *ev_skb;
struct sk_buff *skb = llc_alloc_frame();
if (!skb)
goto out;
rc = 0;
ev_skb = ev->data.pdu.skb;
skb->dev = ev_skb->dev;
llc_pdu_decode_sa(ev_skb, mac_da);
llc_pdu_decode_ssap(ev_skb, &dsap);
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
llc_pdu_init_as_test_rsp(skb, ev_skb);
lan_hdrs_init(skb, station->mac_sa, mac_da);
llc_station_send_pdu(station, skb);
out:
return rc;
}
int llc_station_ac_report_status(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return 0;
}
static void llc_station_ack_tmr_callback(unsigned long timeout_data)
{
struct llc_station *station = (struct llc_station *)timeout_data;
struct llc_station_state_ev *ev;
station->ack_tmr_running = 0;
ev = llc_station_alloc_ev(station);
if (ev) {
ev->type = LLC_STATION_EV_TYPE_ACK_TMR;
ev->data.tmr.timer_specific = NULL;
llc_station_send_ev(station, ev);
}
}
/*
* llc_c_ac.c - actions performed during connection state transition.
*
* Description:
* Functions in this module are implementation of connection component actions
* Details of actions can be found in IEEE-802.2 standard document.
* All functions have one connection and one event as input argument. All of
* them return 0 On success and 1 otherwise.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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_main.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_ac.h>
#include <net/llc_c_st.h>
#include <net/llc_pdu.h>
#include <net/llc_mac.h>
static void llc_conn_pf_cycle_tmr_cb(unsigned long timeout_data);
static void llc_conn_ack_tmr_cb(unsigned long timeout_data);
static void llc_conn_rej_tmr_cb(unsigned long timeout_data);
static void llc_conn_busy_tmr_cb(unsigned long timeout_data);
static int llc_conn_ac_inc_vs_by_1(struct sock *sk,
struct llc_conn_state_ev *ev);
static void llc_process_tmr_ev(struct sock *sk, struct llc_conn_state_ev *ev);
static int llc_conn_ac_data_confirm(struct sock *sk,
struct llc_conn_state_ev *ev);
#define INCORRECT 0
int llc_conn_ac_clear_remote_busy(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (llc->remote_busy_flag) {
u8 nr;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
llc->remote_busy_flag = 0;
del_timer(&llc->busy_state_timer.timer);
llc->busy_state_timer.running = 0;
nr = LLC_I_GET_NR(rx_pdu);
llc_conn_resend_i_pdu_as_cmd(sk, nr, 0);
}
return 0;
}
int llc_conn_ac_conn_ind(struct sock *sk, struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = ev->data.pdu.skb;
union llc_u_prim_data *prim_data = llc_ind_prim.data;
struct llc_prim_if_block *prim = &llc_ind_prim;
struct llc_sap *sap;
struct llc_opt *llc = llc_sk(sk);
llc_pdu_decode_dsap(skb, &prim_data->conn.daddr.lsap);
sap = llc_sap_find(prim_data->conn.daddr.lsap);
if (sap) {
llc_pdu_decode_sa(skb, llc->daddr.mac);
llc_pdu_decode_da(skb, llc->laddr.mac);
llc->dev = skb->dev;
prim_data->conn.pri = 0;
prim_data->conn.sk = sk;
prim_data->conn.dev = skb->dev;
memcpy(&prim_data->conn.daddr, &llc->laddr, sizeof(llc->laddr));
memcpy(&prim_data->conn.saddr, &llc->daddr, sizeof(llc->daddr));
prim->data = prim_data;
prim->prim = LLC_CONN_PRIM;
prim->sap = llc->sap;
ev->flag = 1;
ev->ind_prim = prim;
rc = 0;
}
return rc;
}
int llc_conn_ac_conn_confirm(struct sock *sk, struct llc_conn_state_ev *ev)
{
union llc_u_prim_data *prim_data = llc_cfm_prim.data;
struct sk_buff *skb = ev->data.pdu.skb;
/* FIXME: wtf, this is global, so the whole thing is really
* non reentrant...
*/
struct llc_prim_if_block *prim = &llc_cfm_prim;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
prim_data->conn.sk = sk;
prim_data->conn.pri = 0;
prim_data->conn.status = ev->status;
prim_data->conn.link = llc->link;
if (skb)
prim_data->conn.dev = skb->dev;
else
printk(KERN_ERR __FUNCTION__ "ev->data.pdu.skb == NULL\n");
prim->data = prim_data;
prim->prim = LLC_CONN_PRIM;
prim->sap = sap;
ev->flag = 1;
ev->cfm_prim = prim;
return 0;
}
static int llc_conn_ac_data_confirm(struct sock *sk,
struct llc_conn_state_ev *ev)
{
struct llc_prim_if_block *prim = &llc_cfm_prim;
union llc_u_prim_data *prim_data = llc_cfm_prim.data;
prim_data->data.sk = sk;
prim_data->data.pri = 0;
prim_data->data.link = llc_sk(sk)->link;
prim_data->data.status = LLC_STATUS_RECEIVED;
prim_data->data.skb = NULL;
prim->data = prim_data;
prim->prim = LLC_DATA_PRIM;
prim->sap = llc_sk(sk)->sap;
ev->flag = 1;
ev->cfm_prim = prim;
return 0;
}
int llc_conn_ac_data_ind(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_conn_rtn_pdu(sk, ev->data.pdu.skb, ev);
return 0;
}
int llc_conn_ac_disc_ind(struct sock *sk, struct llc_conn_state_ev *ev)
{
u8 reason = 0;
int rc = 1;
union llc_u_prim_data *prim_data = llc_ind_prim.data;
struct llc_prim_if_block *prim = &llc_ind_prim;
if (ev->type == LLC_CONN_EV_TYPE_PDU) {
llc_pdu_un_t *rx_pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_RSP(rx_pdu) &&
!LLC_PDU_TYPE_IS_U(rx_pdu) &&
LLC_U_PDU_RSP(rx_pdu) == LLC_2_PDU_RSP_DM) {
reason = LLC_DISC_REASON_RX_DM_RSP_PDU;
rc = 0;
} else if (!LLC_PDU_IS_CMD(rx_pdu) &&
!LLC_PDU_TYPE_IS_U(rx_pdu) &&
LLC_U_PDU_CMD(rx_pdu) == LLC_2_PDU_CMD_DISC) {
reason = LLC_DISC_REASON_RX_DISC_CMD_PDU;
rc = 0;
}
} else if (ev->type == LLC_CONN_EV_TYPE_ACK_TMR) {
reason = LLC_DISC_REASON_ACK_TMR_EXP;
rc = 0;
} else {
reason = 0;
rc = 1;
}
if (!rc) {
prim_data->disc.sk = sk;
prim_data->disc.reason = reason;
prim_data->disc.link = llc_sk(sk)->link;
prim->data = prim_data;
prim->prim = LLC_DISC_PRIM;
prim->sap = llc_sk(sk)->sap;
ev->flag = 1;
ev->ind_prim = prim;
}
return rc;
}
int llc_conn_ac_disc_confirm(struct sock *sk, struct llc_conn_state_ev *ev)
{
union llc_u_prim_data *prim_data = llc_cfm_prim.data;
struct llc_prim_if_block *prim = &llc_cfm_prim;
prim_data->disc.sk = sk;
prim_data->disc.reason = ev->status;
prim_data->disc.link = llc_sk(sk)->link;
prim->data = prim_data;
prim->prim = LLC_DISC_PRIM;
prim->sap = llc_sk(sk)->sap;
ev->flag = 1;
ev->cfm_prim = prim;
return 0;
}
int llc_conn_ac_rst_ind(struct sock *sk, struct llc_conn_state_ev *ev)
{
u8 reason = 0;
int rc = 1;
llc_pdu_un_t *rx_pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
union llc_u_prim_data *prim_data = llc_ind_prim.data;
struct llc_prim_if_block *prim = &llc_ind_prim;
struct llc_opt *llc = llc_sk(sk);
switch (ev->type) {
case LLC_CONN_EV_TYPE_PDU:
if (!LLC_PDU_IS_RSP(rx_pdu) &&
!LLC_PDU_TYPE_IS_U(rx_pdu) &&
LLC_U_PDU_RSP(rx_pdu) == LLC_2_PDU_RSP_FRMR) {
reason = LLC_RESET_REASON_LOCAL;
rc = 0;
} else if (!LLC_PDU_IS_CMD(rx_pdu) &&
!LLC_PDU_TYPE_IS_U(rx_pdu) &&
LLC_U_PDU_CMD(rx_pdu) ==
LLC_2_PDU_CMD_SABME) {
reason = LLC_RESET_REASON_REMOTE;
rc = 0;
} else {
reason = 0;
rc = 1;
}
break;
case LLC_CONN_EV_TYPE_ACK_TMR:
case LLC_CONN_EV_TYPE_P_TMR:
case LLC_CONN_EV_TYPE_REJ_TMR:
case LLC_CONN_EV_TYPE_BUSY_TMR:
if (llc->retry_count > llc->n2) {
reason = LLC_RESET_REASON_LOCAL;
rc = 0;
} else
rc = 1;
break;
}
if (!rc) {
prim_data->res.sk = sk;
prim_data->res.reason = reason;
prim_data->res.link = llc->link;
prim->data = prim_data;
prim->prim = LLC_RESET_PRIM;
prim->sap = llc->sap;
ev->flag = 1;
ev->ind_prim = prim;
}
return rc;
}
int llc_conn_ac_rst_confirm(struct sock *sk, struct llc_conn_state_ev *ev)
{
union llc_u_prim_data *prim_data = llc_cfm_prim.data;
struct llc_prim_if_block *prim = &llc_cfm_prim;
prim_data->res.sk = sk;
prim_data->res.link = llc_sk(sk)->link;
prim->data = prim_data;
prim->prim = LLC_RESET_PRIM;
prim->sap = llc_sk(sk)->sap;
ev->flag = 1;
ev->cfm_prim = prim;
return 0;
}
int llc_conn_ac_report_status(struct sock *sk, struct llc_conn_state_ev *ev)
{
return 0;
}
int llc_conn_ac_clear_remote_busy_if_f_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_RSP(rx_pdu) &&
!LLC_PDU_TYPE_IS_I(rx_pdu) &&
!LLC_I_PF_IS_1(rx_pdu) && llc_sk(sk)->ack_pf)
llc_conn_ac_clear_remote_busy(sk, ev);
return 0;
}
int llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2(struct sock *sk,
struct llc_conn_state_ev *ev)
{
if (llc_sk(sk)->data_flag == 2) {
del_timer(&llc_sk(sk)->rej_sent_timer.timer);
llc_sk(sk)->rej_sent_timer.running = 0;
}
return 0;
}
int llc_conn_ac_send_disc_cmd_p_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 p_bit = 1;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_disc_cmd(skb, p_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
llc_conn_ac_set_p_flag_1(sk, ev);
return rc;
}
int llc_conn_ac_send_dm_rsp_f_set_p(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
struct sk_buff *rx_skb = ev->data.pdu.skb;
u8 f_bit;
skb->dev = llc->dev;
llc_pdu_decode_pf_bit(rx_skb, &f_bit);
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_dm_rsp(skb, f_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_dm_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_dm_rsp(skb, f_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_dm_rsp_f_set_f_flag(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = llc->f_flag;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_dm_rsp(skb, f_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_frmr_rsp_f_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 f_bit;
int rc = 1;
struct sk_buff *skb, *ev_skb = ev->data.pdu.skb;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev_skb->nh.raw;
struct llc_opt *llc = llc_sk(sk);
llc->rx_pdu_hdr = (u32)*((u32 *)rx_pdu);
if (!LLC_PDU_IS_CMD(rx_pdu))
llc_pdu_decode_pf_bit(ev_skb, &f_bit);
else
f_bit = 0;
skb = llc_alloc_frame();
if (skb) {
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_frmr_rsp(skb, rx_pdu, f_bit, llc->vS,
llc->vR, INCORRECT);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_resend_frmr_rsp_f_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 f_bit = 0;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)&llc->rx_pdu_hdr;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_frmr_rsp(skb, rx_pdu, f_bit, llc->vS,
llc->vR, INCORRECT);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_resend_frmr_rsp_f_set_p(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 f_bit;
int rc = 1;
struct sk_buff *skb;
llc_pdu_decode_pf_bit(ev->data.pdu.skb, &f_bit);
skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_frmr_rsp(skb, rx_pdu, f_bit, llc->vS,
llc->vR, INCORRECT);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_i_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 p_bit = 1;
struct sk_buff *skb = ev->data.prim.data->data->data.skb;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
llc_pdu_header_init(skb, LLC_PDU_TYPE_I, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_i_cmd(skb, p_bit, llc->vS, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, ev);
return 0;
}
int llc_conn_ac_send_i_cmd_p_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 p_bit = 0;
struct sk_buff *skb = ev->data.prim.data->data->data.skb;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
llc_pdu_header_init(skb, LLC_PDU_TYPE_I, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_i_cmd(skb, p_bit, llc->vS, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, ev);
return 0;
}
int llc_conn_ac_resend_i_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 nr = LLC_I_GET_NR(rx_pdu);
llc_conn_resend_i_pdu_as_cmd(sk, nr, 1);
return 0;
}
int llc_conn_ac_resend_i_cmd_p_set_1_or_send_rr(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 nr = LLC_I_GET_NR(rx_pdu);
int rc = llc_conn_ac_send_rr_cmd_p_set_1(sk, ev);
if (!rc)
llc_conn_resend_i_pdu_as_cmd(sk, nr, 0);
return rc;
}
int llc_conn_ac_send_i_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 p_bit = 0;
struct sk_buff *skb = ev->data.prim.data->data->data.skb;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
llc_pdu_header_init(skb, LLC_PDU_TYPE_I, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_i_cmd(skb, p_bit, llc->vS, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, ev);
return 0;
}
int llc_conn_ac_resend_i_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 nr = LLC_I_GET_NR(rx_pdu);
llc_conn_resend_i_pdu_as_cmd(sk, nr, 0);
return 0;
}
int llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 nr;
u8 f_bit = 0;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
if (rc) {
nr = LLC_I_GET_NR(rx_pdu);
rc = 0;
llc_conn_resend_i_pdu_as_cmd(sk, nr, f_bit);
}
return rc;
}
int llc_conn_ac_resend_i_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 nr = LLC_I_GET_NR(rx_pdu);
llc_conn_resend_i_pdu_as_rsp(sk, nr, 1);
return 0;
}
int llc_conn_ac_send_rej_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 p_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_rej_cmd(skb, p_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rej_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 f_bit = 1;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rej_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rej_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 0;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rej_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rnr_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 p_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_rnr_cmd(skb, p_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rnr_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rnr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rnr_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 f_bit = 0;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rnr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_set_remote_busy(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (!llc->remote_busy_flag) {
llc->remote_busy_flag = 1;
llc->busy_state_timer.timer.expires = jiffies +
llc->busy_state_timer.expire * HZ;
llc->busy_state_timer.timer.data = (unsigned long)sk;
llc->busy_state_timer.timer.function = llc_conn_busy_tmr_cb;
add_timer(&llc->busy_state_timer.timer);
llc->busy_state_timer.running = 1;
}
return 0;
}
int llc_conn_ac_opt_send_rnr_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 0;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rnr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rr_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 p_bit = 1;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_rr_cmd(skb, p_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_ack_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 p_bit = 1;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_rr_cmd(skb, p_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rr_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_ack_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rr_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 0;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_ack_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 0;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_sabme_cmd_p_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
struct llc_opt *llc = llc_sk(sk);
u8 p_bit = 1;
if (skb) {
struct llc_sap *sap = llc->sap;
u8 *dmac = llc->daddr.mac;
if (llc->dev->flags & IFF_LOOPBACK)
dmac = llc->dev->dev_addr;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_sabme_cmd(skb, p_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, dmac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
llc->p_flag = p_bit;
return rc;
}
int llc_conn_ac_send_ua_rsp_f_set_f_flag(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = llc->f_flag;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_ua_rsp(skb, f_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_ua_rsp_f_set_p(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 f_bit;
int rc = 1;
struct sk_buff *rx_skb = ev->data.pdu.skb;
struct sk_buff *skb;
llc_pdu_decode_pf_bit(rx_skb, &f_bit);
skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_ua_rsp(skb, f_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_set_s_flag_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->s_flag = 0;
return 0;
}
int llc_conn_ac_set_s_flag_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->s_flag = 1;
return 0;
}
int llc_conn_ac_start_p_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
llc->p_flag = 1;
del_timer(&llc->pf_cycle_timer.timer);
llc->pf_cycle_timer.timer.expires = jiffies +
llc->pf_cycle_timer.expire * HZ;
llc->pf_cycle_timer.timer.data = (unsigned long)sk;
llc->pf_cycle_timer.timer.function = llc_conn_pf_cycle_tmr_cb;
add_timer(&llc->pf_cycle_timer.timer);
llc->pf_cycle_timer.running = 1;
return 0;
}
/**
* llc_conn_ac_send_ack_if_needed - check if ack is needed
* @sk: current connection structure
* @ev: current event
*
* Checks number of received PDUs which have not been acknowledged, yet,
* If number of them reaches to "npta"(Number of PDUs To Acknowledge) then
* sends an RR response as acknowledgement for them. Returns 0 for
* success, 1 otherwise.
*/
int llc_conn_ac_send_ack_if_needed(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 pf_bit;
struct sk_buff *skb = ev->data.pdu.skb;
struct llc_opt *llc = llc_sk(sk);
llc_pdu_decode_pf_bit(skb, &pf_bit);
llc->ack_pf |= pf_bit & 1;
if (!llc->ack_must_be_send) {
llc->first_pdu_Ns = llc->vR;
llc->ack_must_be_send = 1;
llc->ack_pf = pf_bit & 1;
}
if (((llc->vR - llc->first_pdu_Ns + 129) % 128) >= llc->npta) {
llc_conn_ac_send_rr_rsp_f_set_ackpf(sk, ev);
llc->ack_must_be_send = 0;
llc->ack_pf = 0;
llc_conn_ac_inc_npta_value(sk, ev);
}
return 0;
}
/**
* llc_conn_ac_rst_sendack_flag - resets ack_must_be_send flag
* @sk: current connection structure
* @ev: current event
*
* This action resets ack_must_be_send flag of given connection, this flag
* indicates if there is any PDU which has not been acknowledged yet.
* Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_rst_sendack_flag(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_sk(sk)->ack_must_be_send = llc_sk(sk)->ack_pf = 0;
return 0;
}
/**
* llc_conn_ac_send_i_rsp_f_set_ackpf - acknowledge received PDUs
* @sk: current connection structure
* @ev: current event
*
* Sends an I response PDU with f-bit set to ack_pf flag as acknowledge to
* all received PDUs which have not been acknowledged, yet. ack_pf flag is
* set to one if one PDU with p-bit set to one is received. Returns 0 for
* success, 1 otherwise.
*/
int llc_conn_ac_send_i_rsp_f_set_ackpf(struct sock *sk,
struct llc_conn_state_ev *ev)
{
struct sk_buff *skb = ev->data.prim.data->data->data.skb;
struct llc_opt *llc = llc_sk(sk);
u8 p_bit = llc->ack_pf;
struct llc_sap *sap = llc->sap;
llc_pdu_header_init(skb, LLC_PDU_TYPE_I, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_i_cmd(skb, p_bit, llc->vS, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, ev);
return 0;
}
/**
* llc_conn_ac_send_i_as_ack - sends an I-format PDU to acknowledge rx PDUs
* @sk: current connection structure.
* @ev: current event.
*
* This action sends an I-format PDU as acknowledge to received PDUs which
* have not been acknowledged, yet, if there is any. By using of this
* action number of acknowledgements decreases, this technic is called
* piggy backing. Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_send_i_as_ack(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (llc->ack_must_be_send) {
llc_conn_ac_send_i_rsp_f_set_ackpf(sk, ev);
llc->ack_must_be_send = 0 ;
llc->ack_pf = 0;
} else
llc_conn_ac_send_i_cmd_p_set_0(sk, ev);
return 0;
}
/**
* llc_conn_ac_send_rr_rsp_f_set_ackpf - ack all rx PDUs not yet acked
* @sk: current connection structure.
* @ev: current event.
*
* This action sends an RR response with f-bit set to ack_pf flag as
* acknowledge to all received PDUs which have not been acknowledged, yet,
* if there is any. ack_pf flag indicates if a PDU has been received with
* p-bit set to one. Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_send_rr_rsp_f_set_ackpf(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = llc->ack_pf;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
/**
* llc_conn_ac_inc_npta_value - tries to make value of npta greater
* @sk: current connection structure.
* @ev: current event.
*
* After "inc_cntr" times calling of this action, "npta" increase by one.
* this action tries to make vale of "npta" greater as possible; number of
* acknowledgements decreases by increasing of "npta". Returns 0 for
* success, 1 otherwise.
*/
int llc_conn_ac_inc_npta_value(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (!llc->inc_cntr) {
llc->dec_step = 0;
llc->dec_cntr = llc->inc_cntr = 2;
++llc->npta;
if (llc->npta > 127)
llc->npta = 127 ;
} else
--llc->inc_cntr;
return 0;
}
/**
* llc_conn_ac_adjust_npta_by_rr - decreases "npta" by one
* @sk: current connection structure.
* @ev: current event.
*
* After receiving "dec_cntr" times RR command, this action decreases
* "npta" by one. Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_adjust_npta_by_rr(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (!llc->connect_step && !llc->remote_busy_flag) {
if (!llc->dec_step) {
if (!llc->dec_cntr) {
llc->inc_cntr = llc->dec_cntr = 2;
if (llc->npta > 0)
llc->npta = llc->npta - 1;
} else
llc->dec_cntr -=1;
}
} else
llc->connect_step = 0 ;
return 0;
}
/**
* llc_conn_ac_adjust_npta_by_rnr - decreases "npta" by one
* @sk: current connection structure.
* @ev: current event.
*
* After receiving "dec_cntr" times RNR command, this action decreases
* "npta" by one. Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_adjust_npta_by_rnr(struct sock *sk,
struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (llc->remote_busy_flag)
if (!llc->dec_step) {
if (!llc->dec_cntr) {
llc->inc_cntr = llc->dec_cntr = 2;
if (llc->npta > 0)
--llc->npta;
} else
--llc->dec_cntr;
}
return 0;
}
/**
* llc_conn_ac_dec_tx_win_size - decreases tx window size
* @sk: current connection structure.
* @ev: current event.
*
* After receiving of a REJ command or response, transmit window size is
* decreased by number of PDUs which are outstanding yet. Returns 0 for
* success, 1 otherwise.
*/
int llc_conn_ac_dec_tx_win_size(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
u8 unacked_pdu = skb_queue_len(&llc->pdu_unack_q);
llc->k -= unacked_pdu;
if (llc->k < 2)
llc->k = 2;
return 0;
}
/**
* llc_conn_ac_inc_tx_win_size - tx window size is inc by 1
* @sk: current connection structure.
* @ev: current event.
*
* After receiving an RR response with f-bit set to one, transmit window
* size is increased by one. Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_inc_tx_win_size(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
llc->k += 1;
if (llc->k > 128)
llc->k = 128 ;
return 0;
}
int llc_conn_ac_stop_all_timers(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
del_timer(&llc->pf_cycle_timer.timer);
llc->pf_cycle_timer.running = 0;
del_timer(&llc->ack_timer.timer);
llc->ack_timer.running = 0;
del_timer(&llc->rej_sent_timer.timer);
llc->rej_sent_timer.running = 0;
del_timer(&llc->busy_state_timer.timer);
llc->busy_state_timer.running = 0;
llc->ack_must_be_send = 0;
llc->ack_pf = 0;
return 0;
}
int llc_conn_ac_stop_other_timers(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
del_timer(&llc->rej_sent_timer.timer);
llc->rej_sent_timer.running = 0;
del_timer(&llc->pf_cycle_timer.timer);
llc->pf_cycle_timer.running = 0;
del_timer(&llc->busy_state_timer.timer);
llc->busy_state_timer.running = 0;
llc->ack_must_be_send = 0;
llc->ack_pf = 0;
return 0;
}
int llc_conn_ac_start_ack_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
del_timer(&llc->ack_timer.timer);
llc->ack_timer.timer.expires = jiffies + llc->ack_timer.expire * HZ;
llc->ack_timer.timer.data = (unsigned long)sk;
llc->ack_timer.timer.function = llc_conn_ack_tmr_cb;
add_timer(&llc->ack_timer.timer);
llc->ack_timer.running = 1;
return 0;
}
int llc_conn_ac_start_rej_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
del_timer(&llc->rej_sent_timer.timer);
llc->rej_sent_timer.timer.expires = jiffies +
llc->rej_sent_timer.expire * HZ;
llc->rej_sent_timer.timer.data = (unsigned long)sk;
llc->rej_sent_timer.timer.function = llc_conn_rej_tmr_cb;
add_timer(&llc->rej_sent_timer.timer);
llc->rej_sent_timer.running = 1;
return 0;
}
int llc_conn_ac_start_ack_tmr_if_not_running(struct sock *sk,
struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (!llc->ack_timer.running) {
llc->ack_timer.timer.expires = jiffies +
llc->ack_timer.expire * HZ;
llc->ack_timer.timer.data = (unsigned long)sk;
llc->ack_timer.timer.function = llc_conn_ack_tmr_cb;
add_timer(&llc->ack_timer.timer);
llc->ack_timer.running = 1;
}
return 0;
}
int llc_conn_ac_stop_ack_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
del_timer(&llc_sk(sk)->ack_timer.timer);
llc_sk(sk)->ack_timer.running = 0;
return 0;
}
int llc_conn_ac_stop_p_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
del_timer(&llc->pf_cycle_timer.timer);
llc->pf_cycle_timer.running = 0;
llc->p_flag = 0;
return 0;
}
int llc_conn_ac_stop_rej_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
del_timer(&llc_sk(sk)->rej_sent_timer.timer);
llc_sk(sk)->rej_sent_timer.running = 0;
return 0;
}
int llc_conn_ac_upd_nr_received(struct sock *sk, struct llc_conn_state_ev *ev)
{
int acked;
u16 unacked = 0;
u8 fbit;
struct sk_buff *skb = ev->data.pdu.skb;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)skb->nh.raw;
struct llc_opt *llc = llc_sk(sk);
llc->last_nr = PDU_SUPV_GET_Nr(rx_pdu);
acked = llc_conn_remove_acked_pdus(sk, llc->last_nr, &unacked);
/* On loopback we don't queue I frames in unack_pdu_q queue. */
if (acked > 0 || (llc->dev->flags & IFF_LOOPBACK)) {
llc->retry_count = 0;
del_timer(&llc->ack_timer.timer);
llc->ack_timer.running = 0;
if (llc->failed_data_req) {
/* already, we did not accept data from upper layer
* (tx_window full or unacceptable state). Now, we
* can send data and must inform to upper layer.
*/
llc->failed_data_req = 0;
llc_conn_ac_data_confirm(sk, ev);
}
if (unacked) {
llc->ack_timer.timer.expires = jiffies +
llc->ack_timer.expire * HZ;
llc->ack_timer.timer.data = (unsigned long)sk;
llc->ack_timer.timer.function = llc_conn_ack_tmr_cb;
add_timer(&llc->ack_timer.timer);
llc->ack_timer.running = 1;
}
} else if (llc->failed_data_req) {
llc_pdu_decode_pf_bit(skb, &fbit);
if (fbit == 1) {
llc->failed_data_req = 0;
llc_conn_ac_data_confirm(sk, ev);
}
}
return 0;
}
int llc_conn_ac_upd_p_flag(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct sk_buff *skb = ev->data.pdu.skb;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)skb->nh.raw;
u8 f_bit;
if (!LLC_PDU_IS_RSP(rx_pdu) &&
!llc_pdu_decode_pf_bit(skb, &f_bit) && f_bit) {
llc_sk(sk)->p_flag = 0;
llc_conn_ac_stop_p_timer(sk, ev);
}
return 0;
}
int llc_conn_ac_set_data_flag_2(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->data_flag = 2;
return 0;
}
int llc_conn_ac_set_data_flag_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->data_flag = 0;
return 0;
}
int llc_conn_ac_set_data_flag_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->data_flag = 1;
return 0;
}
int llc_conn_ac_set_data_flag_1_if_data_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
if (!llc_sk(sk)->data_flag)
llc_sk(sk)->data_flag = 1;
return 0;
}
int llc_conn_ac_set_p_flag_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->p_flag = 0;
return 0;
}
int llc_conn_ac_set_p_flag_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->p_flag = 1;
return 0;
}
int llc_conn_ac_set_remote_busy_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->remote_busy_flag = 0;
return 0;
}
int llc_conn_ac_set_cause_flag_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->cause_flag = 0;
return 0;
}
int llc_conn_ac_set_cause_flag_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->cause_flag = 1;
return 0;
}
int llc_conn_ac_set_retry_cnt_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->retry_count = 0;
return 0;
}
int llc_conn_ac_inc_retry_cnt_by_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_sk(sk)->retry_count++;
return 0;
}
int llc_conn_ac_set_vr_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->vR = 0;
return 0;
}
int llc_conn_ac_inc_vr_by_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->vR = PDU_GET_NEXT_Vr(llc_sk(sk)->vR);
return 0;
}
int llc_conn_ac_set_vs_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->vS = 0;
return 0;
}
int llc_conn_ac_set_vs_nr(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->vS = llc_sk(sk)->last_nr;
return 0;
}
int llc_conn_ac_inc_vs_by_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->vS = (llc_sk(sk)->vS + 1) % 128;
return 0;
}
int llc_conn_ac_set_f_flag_p(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_pdu_decode_pf_bit(ev->data.pdu.skb, &llc_sk(sk)->f_flag);
return 0;
}
void llc_conn_pf_cycle_tmr_cb(unsigned long timeout_data)
{
struct sock *sk = (struct sock *)timeout_data;
struct llc_conn_state_ev *ev;
llc_sk(sk)->pf_cycle_timer.running = 0;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_P_TMR;
ev->data.tmr.timer_specific = NULL;
llc_process_tmr_ev(sk, ev);
}
}
static void llc_conn_busy_tmr_cb(unsigned long timeout_data)
{
struct sock *sk = (struct sock *)timeout_data;
struct llc_conn_state_ev *ev;
llc_sk(sk)->busy_state_timer.running = 0;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_BUSY_TMR;
ev->data.tmr.timer_specific = NULL;
llc_process_tmr_ev(sk, ev);
}
}
void llc_conn_ack_tmr_cb(unsigned long timeout_data)
{
struct sock* sk = (struct sock *)timeout_data;
struct llc_conn_state_ev *ev;
llc_sk(sk)->ack_timer.running = 0;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_ACK_TMR;
ev->data.tmr.timer_specific = NULL;
llc_process_tmr_ev(sk, ev);
}
}
static void llc_conn_rej_tmr_cb(unsigned long timeout_data)
{
struct sock *sk = (struct sock *)timeout_data;
struct llc_conn_state_ev *ev;
llc_sk(sk)->rej_sent_timer.running = 0;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_REJ_TMR;
ev->data.tmr.timer_specific = NULL;
llc_process_tmr_ev(sk, ev);
}
}
int llc_conn_ac_rst_vs(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->X = llc_sk(sk)->vS;
llc_conn_ac_set_vs_nr(sk, ev);
return 0;
}
int llc_conn_ac_upd_vs(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 nr = PDU_SUPV_GET_Nr(rx_pdu);
if (llc_circular_between(llc_sk(sk)->vS, nr, llc_sk(sk)->X))
llc_conn_ac_set_vs_nr(sk, ev);
return 0;
}
/*
* Non-standard actions; these not contained in IEEE specification; for
* our own usage
*/
/**
* llc_conn_disc - removes connection from SAP list and frees it
* @sk: closed connection
* @ev: occurred event
*
* Returns 2, to indicate the state machine that the connection was freed.
*/
int llc_conn_disc(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sap_unassign_sock(llc_sk(sk)->sap, sk);
llc_sock_free(sk);
return 2;
}
/**
* llc_conn_reset - resets connection
* @sk : reseting connection.
* @ev: occurred event.
*
* Stop all timers, empty all queues and reset all flags.
*/
int llc_conn_reset(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sock_reset(sk);
return 0;
}
/**
* llc_circular_between - designates that b is between a and c or not
* @a: lower bound
* @b: element to see if is between a and b
* @c: upper bound
*
* This function designates that b is between a and c or not (for example,
* 0 is between 127 and 1). Returns 1 if b is between a and c, 0
* otherwise.
*/
u8 llc_circular_between(u8 a, u8 b, u8 c)
{
b = b - a;
c = c - a;
return b <= c;
}
/**
* llc_process_tmr_ev - timer backend
* @sk: active connection
* @ev: occurred event
*
* This function is called from timer callback functions. When connection
* is busy (during sending a data frame) timer expiration event must be
* queued. Otherwise this event can be sent to connection state machine.
* Queued events will process by process_rxframes_events function after
* sending data frame. Returns 0 for success, 1 otherwise.
*/
static void llc_process_tmr_ev(struct sock *sk, struct llc_conn_state_ev *ev)
{
bh_lock_sock(sk);
if (llc_sk(sk)->state == LLC_CONN_OUT_OF_SVC) {
printk(KERN_WARNING "timer called on closed connection\n");
llc_conn_free_ev(ev);
goto out;
}
if (!sk->lock.users)
llc_conn_send_ev(sk, ev);
else {
struct sk_buff *skb = alloc_skb(1, GFP_ATOMIC);
if (skb) {
skb->cb[0] = LLC_EVENT;
skb->data = (void *)ev;
sk_add_backlog(sk, skb);
} else
llc_conn_free_ev(ev);
}
out:
bh_unlock_sock(sk);
}
/*
* 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 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <net/llc_conn.h>
#include <net/llc_sap.h>
#include <net/sock.h>
#include <net/llc_c_ev.h>
#include <net/llc_pdu.h>
#if 0
#define dprintk(args...) printk(KERN_DEBUG args)
#else
#define dprintk(args...)
#endif
extern u16 llc_circular_between(u8 a, u8 b, u8 c);
/**
* llc_util_ns_inside_rx_window - check if sequence number is in rx window
* @ns: sequence number of received pdu.
* @vr: sequence number which receiver expects to receive.
* @rw: receive window size of receiver.
*
* Checks if sequence number of received PDU is in range of receive
* window. Returns 0 for success, 1 otherwise
*/
static u16 llc_util_ns_inside_rx_window(u8 ns, u8 vr, u8 rw)
{
return !llc_circular_between(vr, ns,
(vr + rw - 1) % LLC_2_SEQ_NBR_MODULO);
}
/**
* llc_util_nr_inside_tx_window - check if sequence number is in tx window
* @sk: current connection.
* @nr: N(R) of received PDU.
*
* This routine checks if N(R) of received PDU is in range of transmit
* window; on the other hand checks if received PDU acknowledges some
* outstanding PDUs that are in transmit window. Returns 0 for success, 1
* otherwise.
*/
static u16 llc_util_nr_inside_tx_window(struct sock *sk, u8 nr)
{
u8 nr1, nr2;
struct sk_buff *skb;
llc_pdu_sn_t *pdu;
struct llc_opt *llc = llc_sk(sk);
int rc = 0;
if (llc->dev->flags & IFF_LOOPBACK)
goto out;
rc = 1;
if (!skb_queue_len(&llc->pdu_unack_q))
goto out;
skb = skb_peek(&llc->pdu_unack_q);
pdu = (llc_pdu_sn_t *)skb->nh.raw;
nr1 = LLC_I_GET_NS(pdu);
skb = skb_peek_tail(&llc->pdu_unack_q);
pdu = (llc_pdu_sn_t *)skb->nh.raw;
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 llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_CONN_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_conn_resp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_CONN_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_RESP ? 0 : 1;
}
int llc_conn_ev_data_req(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_DATA_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_disc_req(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_DISC_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_rst_req(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_RESET_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_rst_resp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_RESET_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_RESP ? 0 : 1;
}
int llc_conn_ev_local_busy_detected(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return ev->type == LLC_CONN_EV_TYPE_SIMPLE &&
ev->data.a.ev == LLC_CONN_EV_LOCAL_BUSY_DETECTED ? 0 : 1;
}
int llc_conn_ev_local_busy_cleared(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return ev->type == LLC_CONN_EV_TYPE_SIMPLE &&
ev->data.a.ev == LLC_CONN_EV_LOCAL_BUSY_CLEARED ? 0 : 1;
}
int llc_conn_ev_rx_bad_pdu(struct sock *sk, struct llc_conn_state_ev *ev)
{
return 1;
}
int llc_conn_ev_rx_disc_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_0(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_1(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_x_inval_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t * pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
u16 rc = !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_I(pdu) && ns != vr &&
llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
if (!rc)
dprintk(KERN_WARNING "rx_i_cmd_p_bit_set_x_inval_ns matched,"
"state = %d, ns = %d, vr = %d\n",
llc_sk(sk)->state, ns, vr);
return rc;
}
int llc_conn_ev_rx_i_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_0(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_1(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_x_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_I(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_x_inval_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
u16 rc = !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_I(pdu) && ns != vr &&
llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
if (!rc)
dprintk(KERN_WARNING "conn_ev_rx_i_rsp_fbit_set_x_inval_ns "
"matched : state = %d, ns = %d, vr = %d\n",
llc_sk(sk)->state, ns, vr);
return rc;
}
int llc_conn_ev_rx_rej_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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_RR ? 0 : 1;
}
int llc_conn_ev_rx_rr_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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_RR ? 0 : 1;
}
int llc_conn_ev_rx_sabme_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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 llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
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_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_CMD(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu)) {
if (!LLC_I_PF_IS_0(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:
if (!LLC_U_PF_IS_0(pdu))
rc = 0;
break;
}
}
return rc;
}
int llc_conn_ev_rx_xxx_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_CMD(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu))
rc = 0;
else if (!LLC_PDU_TYPE_IS_U(pdu))
switch (LLC_U_PDU_CMD(pdu)) {
case LLC_2_PDU_CMD_SABME:
case LLC_2_PDU_CMD_DISC:
rc = 0;
break;
}
}
return rc;
}
int llc_conn_ev_rx_xxx_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_RSP(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu)) {
if (!LLC_I_PF_IS_1(pdu))
rc = 0;
} else if (!LLC_PDU_TYPE_IS_U(pdu))
switch (LLC_U_PDU_RSP(pdu)) {
case LLC_2_PDU_RSP_UA:
case LLC_2_PDU_RSP_DM:
case LLC_2_PDU_RSP_FRMR:
if (!LLC_U_PF_IS_1(pdu))
rc = 0;
break;
}
}
return rc;
}
int llc_conn_ev_rx_xxx_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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_xxx_yyy(struct sock *sk, struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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:
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 llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vs = llc_sk(sk)->vS;
u8 nr = LLC_I_GET_NR(pdu);
if (!LLC_PDU_IS_CMD(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu)) {
if (nr != vs &&
llc_util_nr_inside_tx_window(sk, nr)) {
dprintk(KERN_ERR "conn_ev_rx_zzz_cmd_inv_nr "
"matched, state = %d, vs = %d, "
"nr = %d\n", 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 llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vs = llc_sk(sk)->vS;
u8 nr = LLC_I_GET_NR(pdu);
if (!LLC_PDU_IS_RSP(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu)) {
if (nr != vs &&
llc_util_nr_inside_tx_window(sk, nr)) {
rc = 0;
dprintk(KERN_ERR "conn_ev_rx_zzz_fbit_set"
"_x_inval_nr matched, state = %d, "
"vs = %d, nr = %d\n",
llc_sk(sk)->state, vs, nr);
}
}
}
return rc;
}
int llc_conn_ev_rx_any_frame(struct sock *sk, struct llc_conn_state_ev *ev)
{
return 0;
}
int llc_conn_ev_p_tmr_exp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type != LLC_CONN_EV_TYPE_P_TMR;
}
int llc_conn_ev_ack_tmr_exp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type != LLC_CONN_EV_TYPE_ACK_TMR;
}
int llc_conn_ev_rej_tmr_exp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type != LLC_CONN_EV_TYPE_REJ_TMR;
}
int llc_conn_ev_busy_tmr_exp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type != LLC_CONN_EV_TYPE_BUSY_TMR;
}
int llc_conn_ev_any_tmr_exp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type == LLC_CONN_EV_TYPE_P_TMR ||
ev->type == LLC_CONN_EV_TYPE_ACK_TMR ||
ev->type == LLC_CONN_EV_TYPE_REJ_TMR ||
ev->type == LLC_CONN_EV_TYPE_BUSY_TMR ? 0 : 1;
}
int llc_conn_ev_init_p_f_cycle(struct sock *sk, struct llc_conn_state_ev *ev)
{
return 1;
}
int llc_conn_ev_tx_buffer_full(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type == LLC_CONN_EV_TYPE_SIMPLE &&
ev->data.a.ev == 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 llc_conn_state_ev *ev)
{
return llc_sk(sk)->data_flag != 1;
}
int llc_conn_ev_qlfy_data_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->data_flag;
}
int llc_conn_ev_qlfy_data_flag_eq_2(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->data_flag != 2;
}
int llc_conn_ev_qlfy_p_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
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.
* @ev: 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 llc_conn_state_ev *ev)
{
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.
* @ev: 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 llc_conn_state_ev *ev)
{
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 llc_conn_state_ev *ev)
{
return llc_sk(sk)->p_flag;
}
int llc_conn_ev_qlfy_p_flag_eq_f(struct sock *sk, struct llc_conn_state_ev *ev)
{
u8 f_bit;
struct sk_buff *skb;
if (ev->type == LLC_CONN_EV_TYPE_PDU)
skb = ev->data.pdu.skb;
else
skb = ev->data.prim.data->data->conn.skb;
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 llc_conn_state_ev *ev)
{
return llc_sk(sk)->remote_busy_flag;
}
int llc_conn_ev_qlfy_remote_busy_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return !llc_sk(sk)->remote_busy_flag;
}
int llc_conn_ev_qlfy_retry_cnt_lt_n2(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return !(llc_sk(sk)->retry_count < llc_sk(sk)->n2);
}
int llc_conn_ev_qlfy_retry_cnt_gte_n2(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return !(llc_sk(sk)->retry_count >= llc_sk(sk)->n2);
}
int llc_conn_ev_qlfy_s_flag_eq_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
return !llc_sk(sk)->s_flag;
}
int llc_conn_ev_qlfy_s_flag_eq_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->s_flag;
}
int llc_conn_ev_qlfy_cause_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return !llc_sk(sk)->cause_flag;
}
int llc_conn_ev_qlfy_cause_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->cause_flag;
}
int llc_conn_ev_qlfy_init_p_f_cycle(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return 0;
}
int llc_conn_ev_qlfy_set_status_conn(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_CONN;
return 0;
}
int llc_conn_ev_qlfy_set_status_disc(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_DISC;
return 0;
}
int llc_conn_ev_qlfy_set_status_impossible(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_IMPOSSIBLE;
return 0;
}
int llc_conn_ev_qlfy_set_status_failed(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_FAILED;
return 0;
}
int llc_conn_ev_qlfy_set_status_remote_busy(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_REMOTE_BUSY;
return 0;
}
int llc_conn_ev_qlfy_set_status_received(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_RECEIVED;
return 0;
}
int llc_conn_ev_qlfy_set_status_refuse(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_REFUSE;
return 0;
}
int llc_conn_ev_qlfy_set_status_conflict(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_CONFLICT;
return 0;
}
int llc_conn_ev_qlfy_set_status_rst_done(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_RESET_DONE;
return 0;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* llc_conn.c - Driver routines for connection component.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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/interrupt.h>
#include <linux/init.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_conn.h>
#include <net/sock.h>
#include <net/llc_main.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_ac.h>
#include <net/llc_c_st.h>
#include <net/llc_mac.h>
#include <net/llc_pdu.h>
#include <net/llc_s_ev.h>
static int llc_find_offset(int state, int ev_type);
static void llc_conn_send_pdus(struct sock *sk);
static int llc_conn_service(struct sock *sk, struct llc_conn_state_ev *ev);
static int llc_exec_conn_trans_actions(struct sock *sk,
struct llc_conn_state_trans *trans,
struct llc_conn_state_ev *ev);
static struct llc_conn_state_trans *
llc_qualify_conn_ev(struct sock *sk, struct llc_conn_state_ev *ev);
/* Offset table on connection states transition diagram */
static int llc_offset_table[NBR_CONN_STATES][NBR_CONN_EV];
/**
* llc_conn_alloc_event: allocates an event
* @sk: socket that event is associated
*
* Returns pointer to allocated connection on success, %NULL on failure.
*/
struct llc_conn_state_ev *llc_conn_alloc_ev(struct sock *sk)
{
struct llc_conn_state_ev *ev = NULL;
/* verify connection is valid, active and open */
if (llc_sk(sk)->state != LLC_CONN_OUT_OF_SVC) {
/* get event structure to build a station event */
ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
if (ev)
memset(ev, 0, sizeof(*ev));
}
return ev;
}
/**
* llc_conn_send_event - sends event to connection state machine
* @sk: connection
* @ev: occurred event
*
* Sends an event to connection state machine. after processing event
* (executing it's actions and changing state), upper layer will be
* indicated or confirmed, if needed. Returns 0 for success, 1 for
* failure. The socket lock has to be held before calling this function.
*/
int llc_conn_send_ev(struct sock *sk, struct llc_conn_state_ev *ev)
{
/* sending event to state machine */
int rc = llc_conn_service(sk, ev);
struct llc_opt *llc = llc_sk(sk);
u8 flag = ev->flag;
struct llc_prim_if_block *ind_prim = ev->ind_prim;
struct llc_prim_if_block *cfm_prim = ev->cfm_prim;
llc_conn_free_ev(ev);
#ifdef THIS_BREAKS_DISCONNECT_NOTIFICATION_BADLY
/* check if the connection was freed by the state machine by
* means of llc_conn_disc */
if (rc == 2) {
printk(KERN_INFO __FUNCTION__ ": rc == 2\n");
rc = -ECONNABORTED;
goto out;
}
#endif /* THIS_BREAKS_DISCONNECT_NOTIFICATION_BADLY */
if (!flag) /* indicate or confirm not required */
goto out;
rc = 0;
if (ind_prim) /* indication required */
llc->sap->ind(ind_prim);
if (!cfm_prim) /* confirmation not required */
goto out;
/* data confirm has preconditions */
if (cfm_prim->prim != LLC_DATA_PRIM) {
llc->sap->conf(cfm_prim);
goto out;
}
if (!llc_data_accept_state(llc->state)) {
/* In this state, we can send I pdu */
/* FIXME: check if we don't need to see if sk->lock.users != 0
* is needed here
*/
rc = llc->sap->conf(cfm_prim);
if (rc) /* confirmation didn't accept by upper layer */
llc->failed_data_req = 1;
} else
llc->failed_data_req = 1;
out:
return rc;
}
void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb)
{
llc_sock_assert(sk);
/* queue PDU to send to MAC layer */
skb_queue_tail(&sk->write_queue, skb);
llc_conn_send_pdus(sk);
}
/**
* llc_conn_rtn_pdu - sends received data pdu to upper layer
* @sk: Active connection
* @skb: Received data frame
* @ev: Occurred event
*
* Sends received data pdu to upper layer (by using indicate function).
* Prepares service parameters (prim and prim_data). calling indication
* function will be done in llc_conn_send_ev.
*/
void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb,
struct llc_conn_state_ev *ev)
{
struct llc_prim_if_block *prim = &llc_ind_prim;
union llc_u_prim_data *prim_data = llc_ind_prim.data;
prim_data->data.sk = sk;
prim_data->data.pri = 0;
prim_data->data.skb = skb;
prim_data->data.link = llc_sk(sk)->link;
prim->data = prim_data;
prim->prim = LLC_DATA_PRIM;
prim->sap = llc_sk(sk)->sap;
ev->flag = 1;
/* saving prepd prim in event for future use in llc_conn_send_ev */
ev->ind_prim = prim;
}
/**
* llc_conn_resend_i_pdu_as_cmd - resend all all unacknowledged I PDUs
* @sk: active connection
* @nr: NR
* @first_p_bit: p_bit value of first pdu
*
* Resend all unacknowledged I PDUs, starting with the NR; send first as
* command PDU with P bit equal first_p_bit; if more than one send
* subsequent as command PDUs with P bit equal zero (0).
*/
void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit)
{
struct sk_buff *skb;
llc_pdu_sn_t *pdu;
u16 nbr_unack_pdus;
u8 howmany_resend = 0;
llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);
if (!nbr_unack_pdus)
goto out;
/* process unack PDUs only if unack queue is not empty; remove
* appropriate PDUs, fix them up, and put them on mac_pdu_q.
*/
while ((skb = skb_dequeue(&llc_sk(sk)->pdu_unack_q)) != NULL) {
pdu = (llc_pdu_sn_t *)skb->nh.raw;
llc_pdu_set_cmd_rsp(skb, LLC_PDU_CMD);
llc_pdu_set_pf_bit(skb, first_p_bit);
skb_queue_tail(&sk->write_queue, skb);
first_p_bit = 0;
llc_sk(sk)->vS = LLC_I_GET_NS(pdu);
howmany_resend++;
}
if (howmany_resend > 0)
llc_sk(sk)->vS = (llc_sk(sk)->vS + 1) % LLC_2_SEQ_NBR_MODULO;
/* any PDUs to re-send are queued up; start sending to MAC */
llc_conn_send_pdus(sk);
out:;
}
/**
* llc_conn_resend_i_pdu_as_rsp - Resend all unacknowledged I PDUs
* @sk: active connection.
* @nr: NR
* @first_f_bit: f_bit value of first pdu.
*
* Resend all unacknowledged I PDUs, starting with the NR; send first as
* response PDU with F bit equal first_f_bit; if more than one send
* subsequent as response PDUs with F bit equal zero (0).
*/
void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit)
{
struct sk_buff *skb;
llc_pdu_sn_t *pdu;
u16 nbr_unack_pdus;
u8 howmany_resend = 0;
llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);
if (!nbr_unack_pdus)
goto out;
/* process unack PDUs only if unack queue is not empty; remove
* appropriate PDUs, fix them up, and put them on mac_pdu_q
*/
while ((skb = skb_dequeue(&llc_sk(sk)->pdu_unack_q)) != NULL) {
pdu = (llc_pdu_sn_t *)skb->nh.raw;
llc_pdu_set_cmd_rsp(skb, LLC_PDU_RSP);
llc_pdu_set_pf_bit(skb, first_f_bit);
skb_queue_tail(&sk->write_queue, skb);
first_f_bit = 0;
llc_sk(sk)->vS = LLC_I_GET_NS(pdu);
howmany_resend++;
}
if (howmany_resend > 0)
llc_sk(sk)->vS = (llc_sk(sk)->vS + 1) % LLC_2_SEQ_NBR_MODULO;
/* any PDUs to re-send are queued up; start sending to MAC */
llc_conn_send_pdus(sk);
out:;
}
/**
* llc_conn_remove_acked_pdus - Removes acknowledged pdus from tx queue
* @sk: active connection
* nr: NR
* how_many_unacked: size of pdu_unack_q after removing acked pdus
*
* Removes acknowledged pdus from transmit queue (pdu_unack_q). Returns
* the number of pdus that removed from queue.
*/
int llc_conn_remove_acked_pdus(struct sock *sk, u8 nr, u16 *how_many_unacked)
{
int pdu_pos, i;
struct sk_buff *skb;
llc_pdu_sn_t *pdu;
int nbr_acked = 0;
int q_len = skb_queue_len(&llc_sk(sk)->pdu_unack_q);
if (!q_len)
goto out;
skb = skb_peek(&llc_sk(sk)->pdu_unack_q);
pdu = (llc_pdu_sn_t *)skb->nh.raw;
/* finding position of last acked pdu in queue */
pdu_pos = ((int)LLC_2_SEQ_NBR_MODULO + (int)nr -
(int)LLC_I_GET_NS(pdu)) % LLC_2_SEQ_NBR_MODULO;
for (i = 0; i < pdu_pos && i < q_len; i++) {
skb = skb_dequeue(&llc_sk(sk)->pdu_unack_q);
if (skb)
kfree_skb(skb);
nbr_acked++;
}
out:
*how_many_unacked = skb_queue_len(&llc_sk(sk)->pdu_unack_q);
return nbr_acked;
}
/**
* llc_conn_send_pdus - Sends queued PDUs
* @sk: active connection
*
* Sends queued pdus to MAC layer for transmition.
*/
static void llc_conn_send_pdus(struct sock *sk)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&sk->write_queue)) != NULL) {
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
if (!LLC_PDU_TYPE_IS_I(pdu) &&
!(skb->dev->flags & IFF_LOOPBACK))
skb_queue_tail(&llc_sk(sk)->pdu_unack_q, skb);
mac_send_pdu(skb);
if (LLC_PDU_TYPE_IS_I(pdu) ||
(skb->dev && skb->dev->flags & IFF_LOOPBACK))
kfree_skb(skb);
}
}
/**
* llc_conn_free_ev - free event
* @ev: event to free
*
* Free allocated event.
*/
void llc_conn_free_ev(struct llc_conn_state_ev *ev)
{
if (ev->type == LLC_CONN_EV_TYPE_PDU) {
/* free the frame that binded to this event */
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
if (LLC_PDU_TYPE_IS_I(pdu) || !ev->flag || !ev->ind_prim)
kfree_skb(ev->data.pdu.skb);
}
/* free event structure to free list of the same */
kfree(ev);
}
/**
* llc_conn_service - finds transition and changes state of connection
* @sk: connection
* @ev: happened event
*
* This function finds transition that matches with happened event, then
* executes related actions and finally changes state of connection.
* Returns 0 for success, 1 for failure.
*/
static int llc_conn_service(struct sock *sk, struct llc_conn_state_ev *ev)
{
int rc = 1;
struct llc_conn_state_trans *trans;
if (llc_sk(sk)->state > NBR_CONN_STATES)
goto out;
rc = 0;
trans = llc_qualify_conn_ev(sk, ev);
if (trans) {
rc = llc_exec_conn_trans_actions(sk, trans, ev);
if (!rc && trans->next_state != NO_STATE_CHANGE)
llc_sk(sk)->state = trans->next_state;
}
out:
return rc;
}
/**
* llc_qualify_conn_ev - finds transition for event
* @sk: connection
* @ev: happened event
*
* This function finds transition that matches with happened event.
* Returns pointer to found transition on success, %NULL otherwise.
*/
static struct llc_conn_state_trans *
llc_qualify_conn_ev(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_conn_state_trans **next_trans;
llc_conn_ev_qfyr_t *next_qualifier;
struct llc_conn_state *curr_state =
&llc_conn_state_table[llc_sk(sk)->state - 1];
/* search thru events for this state until
* list exhausted or until no more
*/
for (next_trans = curr_state->transitions +
llc_find_offset(llc_sk(sk)->state - 1, ev->type);
(*next_trans)->ev; next_trans++) {
if (!((*next_trans)->ev)(sk, ev)) {
/* got POSSIBLE event match; the event may require
* qualification based on the values of a number of
* state flags; if all qualifications are met (i.e.,
* if all qualifying functions return success, or 0,
* then this is THE event we're looking for
*/
for (next_qualifier = (*next_trans)->ev_qualifiers;
next_qualifier && *next_qualifier &&
!(*next_qualifier)(sk, ev); next_qualifier++)
/* nothing */;
if (!next_qualifier || !*next_qualifier)
/* all qualifiers executed successfully; this is
* our transition; return it so we can perform
* the associated actions & change the state
*/
return *next_trans;
}
}
return NULL;
}
/**
* llc_exec_conn_trans_actions - executes related actions
* @sk: connection
* @trans: transition that it's actions must be performed
* @ev: happened event
*
* Executes actions that is related to happened event. Returns 0 for
* success, 1 to indicate failure of at least one action or 2 if the
* connection was freed (llc_conn_disc was called)
*/
static int llc_exec_conn_trans_actions(struct sock *sk,
struct llc_conn_state_trans *trans,
struct llc_conn_state_ev *ev)
{
int rc = 0;
llc_conn_action_t *next_action;
for (next_action = trans->ev_actions;
next_action && *next_action; next_action++) {
int rc2 = (*next_action)(sk, ev);
if (rc2 == 2) {
rc = rc2;
break;
} else if (rc2)
rc = 1;
}
return rc;
}
/**
* llc_find_sock - Finds connection in sap for the remote/local sap/mac
* @sap: SAP
* @daddr: address of remote LLC (MAC + SAP)
* @laddr: address of local LLC (MAC + SAP)
*
* Search connection list of the SAP and finds connection using the remote
* mac, remote sap, local mac, and local sap. Returns pointer for
* connection found, %NULL otherwise.
*/
struct sock *llc_find_sock(struct llc_sap *sap, struct llc_addr *daddr,
struct llc_addr *laddr)
{
struct sock *rc = NULL;
struct list_head *entry;
spin_lock_bh(&sap->sk_list.lock);
if (list_empty(&sap->sk_list.list))
goto out;
list_for_each(entry, &sap->sk_list.list) {
struct llc_opt *llc = list_entry(entry, struct llc_opt, node);
if (llc->laddr.lsap == laddr->lsap &&
llc->daddr.lsap == daddr->lsap &&
!memcmp(llc->laddr.mac, laddr->mac, ETH_ALEN) &&
!memcmp(llc->daddr.mac, daddr->mac, ETH_ALEN)) {
rc = llc->sk;
break;
}
}
if (rc)
sock_hold(rc);
out:
spin_unlock_bh(&sap->sk_list.lock);
return rc;
}
/**
* llc_data_accept_state - designates if in this state data can be sent.
* @state: state of connection.
*
* Returns 0 if data can be sent, 1 otherwise.
*/
u8 llc_data_accept_state(u8 state)
{
if (state != LLC_CONN_STATE_NORMAL && state != LLC_CONN_STATE_BUSY &&
state != LLC_CONN_STATE_REJ)
return 1; /* data_conn_refuse */
return 0;
}
/**
* find_next_offset - finds offset for next category of transitions
* @state: state table.
* @offset: start offset.
*
* Finds offset of next category of transitions in transition table.
* Returns the start index of next category.
*/
u16 find_next_offset(struct llc_conn_state *state, u16 offset)
{
u16 cnt = 0;
struct llc_conn_state_trans **next_trans;
for (next_trans = state->transitions + offset;
(*next_trans)->ev; next_trans++)
++cnt;
return cnt;
}
/**
* llc_build_offset_table - builds offset table of connection
*
* Fills offset table of connection state transition table
* (llc_offset_table).
*/
void __init llc_build_offset_table(void)
{
struct llc_conn_state *curr_state;
int state, ev_type, next_offset;
memset(llc_offset_table, 0, sizeof(llc_offset_table));
for (state = 0; state < NBR_CONN_STATES; state++) {
curr_state = &llc_conn_state_table[state];
next_offset = 0;
for (ev_type = 0; ev_type < NBR_CONN_EV; ev_type++) {
llc_offset_table[state][ev_type] = next_offset;
next_offset += find_next_offset(curr_state,
next_offset) + 1;
}
}
}
/**
* llc_find_offset - finds start offset of category of transitions
* @state: state of connection
* @ev_type: type of happened event
*
* Finds start offset of desired category of transitions. Returns the
* desired start offset.
*/
static int llc_find_offset(int state, int ev_type)
{
int rc = 0;
/* at this stage, llc_offset_table[..][2] is not important. it is for
* init_pf_cycle and I don't know what is it.
*/
switch (ev_type) {
case LLC_CONN_EV_TYPE_PRIM:
rc = llc_offset_table[state][0]; break;
case LLC_CONN_EV_TYPE_PDU:
rc = llc_offset_table[state][4]; break;
case LLC_CONN_EV_TYPE_SIMPLE:
rc = llc_offset_table[state][1]; break;
case LLC_CONN_EV_TYPE_P_TMR:
case LLC_CONN_EV_TYPE_ACK_TMR:
case LLC_CONN_EV_TYPE_REJ_TMR:
case LLC_CONN_EV_TYPE_BUSY_TMR:
rc = llc_offset_table[state][3]; break;
}
return rc;
}
/*
* llc_evnt.c - LLC station component event match functions
* Description :
* Functions in this module are implementation of station component events.
* Details of events can be found in IEEE-802.2 standard document.
* All functions have one station and one event as input argument. All of
* them return 0 On success and 1 otherwise.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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_main.h>
#include <net/llc_evnt.h>
#include <net/llc_pdu.h>
int llc_stat_ev_enable_with_dup_addr_check(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return ev->type == LLC_STATION_EV_TYPE_SIMPLE &&
ev->data.a.ev ==
LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK ? 0 : 1;
}
int llc_stat_ev_enable_without_dup_addr_check(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return ev->type == LLC_STATION_EV_TYPE_SIMPLE &&
ev->data.a.ev ==
LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK ? 0 : 1;
}
int llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return ev->type == LLC_STATION_EV_TYPE_ACK_TMR &&
station->retry_count < station->maximum_retry ? 0 : 1;
}
int llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return ev->type == LLC_STATION_EV_TYPE_ACK_TMR &&
station->retry_count == station->maximum_retry ? 0 : 1;
}
int llc_stat_ev_rx_null_dsap_xid_c(struct llc_station *station,
struct llc_station_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_STATION_EV_TYPE_PDU &&
!LLC_PDU_IS_CMD(pdu) && /* command PDU */
!LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID &&
!pdu->dsap ? 0 : 1; /* NULL DSAP value */
}
int llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq(struct llc_station *station,
struct llc_station_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_STATION_EV_TYPE_PDU &&
!LLC_PDU_IS_RSP(pdu) && /* response PDU */
!LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID &&
!pdu->dsap && /* NULL DSAP value */
!station->xid_r_count ? 0 : 1;
}
int llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq(struct llc_station *station,
struct llc_station_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_STATION_EV_TYPE_PDU &&
!LLC_PDU_IS_RSP(pdu) && /* response PDU */
!LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID &&
!pdu->dsap && /* NULL DSAP value */
station->xid_r_count == 1 ? 0 : 1;
}
int llc_stat_ev_rx_null_dsap_test_c(struct llc_station *station,
struct llc_station_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_STATION_EV_TYPE_PDU &&
!LLC_PDU_IS_CMD(pdu) && /* command PDU */
!LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST &&
!pdu->dsap ? 0 : 1; /* NULL DSAP */
}
int llc_stat_ev_disable_req(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return ev->type == LLC_STATION_EV_TYPE_PRIM &&
ev->data.prim.prim == LLC_DISABLE_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
/*
* llc_if.c - Defines LLC interface to upper layer
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <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/llc_main.h>
#include <net/llc_mac.h>
static int llc_sap_req(struct llc_prim_if_block *prim);
static int llc_unitdata_req_handler(struct llc_prim_if_block *prim);
static int llc_test_req_handler(struct llc_prim_if_block *prim);
static int llc_xid_req_handler(struct llc_prim_if_block *prim);
static int llc_data_req_handler(struct llc_prim_if_block *prim);
static int llc_conn_req_handler(struct llc_prim_if_block *prim);
static int llc_disc_req_handler(struct llc_prim_if_block *prim);
static int llc_rst_req_handler(struct llc_prim_if_block *prim);
static int llc_flowcontrol_req_handler(struct llc_prim_if_block *prim);
static int llc_sap_resp(struct llc_prim_if_block *prim);
static int llc_conn_rsp_handler(struct llc_prim_if_block *prim);
static int llc_rst_rsp_handler(struct llc_prim_if_block *prim);
static int llc_no_rsp_handler(struct llc_prim_if_block *prim);
extern void llc_register_sap(unsigned char sap,
int (*rcvfunc)(struct sk_buff *skb,
struct net_device *dev,
struct packet_type *pt));
extern void llc_unregister_sap(unsigned char sap);
/* table of request handler functions */
static llc_prim_call_t llc_req_prim[LLC_NBR_PRIMITIVES] = {
[LLC_DATAUNIT_PRIM] = llc_unitdata_req_handler,
[LLC_CONN_PRIM] = llc_conn_req_handler,
[LLC_DATA_PRIM] = llc_data_req_handler,
[LLC_DISC_PRIM] = llc_disc_req_handler,
[LLC_RESET_PRIM] = llc_rst_req_handler,
[LLC_FLOWCONTROL_PRIM] = llc_flowcontrol_req_handler,
[LLC_XID_PRIM] = llc_xid_req_handler,
[LLC_TEST_PRIM] = llc_test_req_handler,
};
/* table of response handler functions */
static llc_prim_call_t llc_resp_prim[LLC_NBR_PRIMITIVES] = {
[LLC_DATAUNIT_PRIM] = llc_no_rsp_handler,
[LLC_CONN_PRIM] = llc_conn_rsp_handler,
[LLC_DATA_PRIM] = llc_no_rsp_handler,
[LLC_DISC_PRIM] = llc_no_rsp_handler,
[LLC_RESET_PRIM] = llc_rst_rsp_handler,
[LLC_FLOWCONTROL_PRIM] = llc_no_rsp_handler,
};
/**
* llc_sap_open - open interface to the upper layers.
* @nw_indicate: pointer to indicate function of upper layer.
* @nw_confirm: pointer to confirm function of upper layer.
* @lsap: SAP number.
* @sap: pointer to allocated SAP (output argument).
*
* Interface function to upper layer. each one who wants to get a SAP
* (for example NetBEUI) should call this function. Returns 0 for
* success, 1 for failure.
*/
struct llc_sap *llc_sap_open(llc_prim_call_t nw_indicate,
llc_prim_call_t nw_confirm, u8 lsap)
{
/* verify this SAP is not already open; if so, return error */
struct llc_sap *sap;
MOD_INC_USE_COUNT;
sap = llc_sap_find(lsap);
if (sap) { /* SAP already exists */
sap = NULL;
goto err;
}
/* sap requested does not yet exist */
sap = llc_sap_alloc();
if (!sap)
goto err;
/* allocated a SAP; initialize it and clear out its memory pool */
sap->laddr.lsap = lsap;
sap->req = llc_sap_req;
sap->resp = llc_sap_resp;
sap->ind = nw_indicate;
sap->conf = nw_confirm;
sap->parent_station = llc_station_get();
/* initialized SAP; add it to list of SAPs this station manages */
llc_sap_save(sap);
llc_register_sap(lsap, mac_indicate);
out:
return sap;
err:
MOD_DEC_USE_COUNT;
goto out;
}
/**
* 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.
*/
void llc_sap_close(struct llc_sap *sap)
{
llc_unregister_sap(sap->laddr.lsap);
llc_free_sap(sap);
MOD_DEC_USE_COUNT;
}
/**
* llc_sap_req - Request interface for upper layers
* @prim: pointer to structure that contains service parameters.
*
* Request interface function to upper layer. each one who wants to
* request a service from LLC, must call this function. details of
* requested service is defined in input argument(prim). Returns 0 for
* success, 1 otherwise.
*/
static int llc_sap_req(struct llc_prim_if_block *prim)
{
int rc = 1;
if (prim->prim > 8 || prim->prim == 6) {
printk(KERN_ERR __FUNCTION__ ": invalid primitive %d\n",
prim->prim);
goto out;
}
/* receive REQUEST primitive from network layer; call the appropriate
* primitive handler which then packages it up as an event and sends it
* to the SAP or CONNECTION event handler
*/
if (prim->prim < LLC_NBR_PRIMITIVES)
/* valid primitive; call the function to handle it */
rc = llc_req_prim[prim->prim](prim);
out:
return rc;
}
/**
* llc_unitdata_req_handler - unitdata request interface for upper layers
* @prim: pointer to structure that contains service parameters
*
* Upper layers calls this function when upper layer wants to send data
* using connection-less mode communication (UI pdu). Returns 0 for
* success, 1 otherwise.
*/
static int llc_unitdata_req_handler(struct llc_prim_if_block *prim)
{
int rc = 1;
struct llc_sap_state_ev *ev;
/* 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
*/
struct llc_sap *sap = llc_sap_find(prim->data->udata.saddr.lsap);
if (!sap)
goto out;
ev = llc_sap_alloc_ev(sap);
if (!ev)
goto out;
ev->type = LLC_SAP_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_DATAUNIT_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = 0;
llc_sap_send_ev(sap, ev);
out:
return rc;
}
/**
* llc_test_req_handler - TEST interface for upper layers.
* @prim: pointer to structure that contains service parameters.
*
* This function is called when upper layer wants to send a TEST pdu.
* Returns 0 for success, 1 otherwise.
*/
static int llc_test_req_handler(struct llc_prim_if_block *prim)
{
int rc = 1;
struct llc_sap_state_ev *ev;
/* package primitive as an event and send to SAP event handler */
struct llc_sap *sap = llc_sap_find(prim->data->udata.saddr.lsap);
if (!sap)
goto out;
ev = llc_sap_alloc_ev(sap);
if (!ev)
goto out;
ev->type = LLC_SAP_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_TEST_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = 0;
llc_sap_send_ev(sap, ev);
out:
return rc;
}
/**
* llc_xid_req_handler - XID interface for upper layers
* @prim: pointer to structure that contains service parameters.
*
* This function is called when upper layer wants to send a XID pdu.
* Returns 0 for success, 1 otherwise.
*/
static int llc_xid_req_handler(struct llc_prim_if_block *prim)
{
int rc = 1;
struct llc_sap_state_ev *ev;
/* package primitive as an event and send to SAP event handler */
struct llc_sap *sap = llc_sap_find(prim->data->udata.saddr.lsap);
if (!sap)
goto out;
ev = llc_sap_alloc_ev(sap);
if (!ev)
goto out;
ev->type = LLC_SAP_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_XID_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = 0;
llc_sap_send_ev(sap, ev);
out:
return rc;
}
/**
* llc_data_req_handler - Connection data sending for upper layers.
* @prim: pointer to structure that contains service parameters
*
* 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.
*/
static int llc_data_req_handler(struct llc_prim_if_block *prim)
{
struct llc_conn_state_ev *ev;
int rc = -ECONNABORTED;
/* accept data frame from network layer to be sent using connection
* mode communication; timeout/retries handled by this layer;
* package primitive as an event and send to connection event handler
*/
struct sock *sk = prim->data->data.sk;
struct llc_opt *llc = llc_sk(sk);
lock_sock(sk);
if (llc->state == LLC_CONN_STATE_ADM)
goto out;
rc = -EBUSY;
if (llc_data_accept_state(llc->state)) { /* data_conn_refuse */
llc->failed_data_req = 1;
goto out;
}
if (llc->p_flag) {
llc->failed_data_req = 1;
goto out;
}
rc = -ENOMEM;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_DATA_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
prim->data->data.skb->dev = llc->dev;
rc = llc_conn_send_ev(sk, ev);
}
out:
release_sock(sk);
return rc;
}
/**
* confirm_impossible - Informs upper layer about failed connection
* @prim: pointer to structure that contains confirmation data.
*
* Informs upper layer about failing in connection establishment. This
* function is called by llc_conn_req_handler.
*/
static void confirm_impossible(struct llc_prim_if_block *prim)
{
prim->data->conn.status = LLC_STATUS_IMPOSSIBLE;
prim->sap->conf(prim);
}
/**
* llc_conn_req_handler - Called by upper layer to establish a conn
* @prim: pointer to structure that contains service parameters.
*
* 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.
*/
static int llc_conn_req_handler(struct llc_prim_if_block *prim)
{
int rc = -EBUSY;
struct llc_opt *llc;
struct llc_sap *sap = prim->sap;
struct llc_conn_state_ev *ev;
struct net_device *ddev = mac_dev_peer(prim->data->conn.dev,
prim->data->conn.dev->type,
prim->data->conn.daddr.mac),
*sdev = (ddev->flags & IFF_LOOPBACK) ?
ddev : prim->data->conn.dev;
struct llc_addr laddr, daddr;
/* network layer supplies addressing required to establish connection;
* package as an event and send it to the connection event handler
*/
struct sock *sk;
memcpy(laddr.mac, sdev->dev_addr, sizeof(laddr.mac));
laddr.lsap = prim->data->conn.saddr.lsap;
memcpy(daddr.mac, ddev->dev_addr, sizeof(daddr.mac));
daddr.lsap = prim->data->conn.daddr.lsap;
sk = llc_find_sock(sap, &daddr, &laddr);
if (sk) {
confirm_impossible(prim);
goto out_put;
}
rc = -ENOMEM;
if (prim->data->conn.sk) {
sk = prim->data->conn.sk;
if (llc_sock_init(sk))
goto out;
} else {
sk = llc_sock_alloc();
if (!sk) {
confirm_impossible(prim);
goto out;
}
prim->data->conn.sk = sk;
}
sock_hold(sk);
lock_sock(sk);
/* assign new connection to it's SAP */
llc_sap_assign_sock(sap, sk);
llc = llc_sk(sk);
memcpy(&llc->daddr, &daddr, sizeof(llc->daddr));
memcpy(&llc->laddr, &laddr, sizeof(llc->laddr));
llc->dev = ddev;
llc->link = prim->data->conn.link;
llc->handler = prim->data->conn.handler;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_CONN_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = llc_conn_send_ev(sk, ev);
}
if (rc) {
llc_sap_unassign_sock(sap, sk);
llc_sock_free(sk);
confirm_impossible(prim);
}
release_sock(sk);
out_put:
sock_put(sk);
out:
return rc;
}
/**
* llc_disc_req_handler - Called by upper layer to close a connection
* @prim: pointer to structure that contains service parameters.
*
* 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.
*/
static int llc_disc_req_handler(struct llc_prim_if_block *prim)
{
u16 rc = 1;
struct llc_conn_state_ev *ev;
struct sock* sk = prim->data->disc.sk;
sock_hold(sk);
lock_sock(sk);
if (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
*/
ev = llc_conn_alloc_ev(sk);
if (!ev)
goto out;
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_DISC_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = llc_conn_send_ev(sk, ev);
out:
release_sock(sk);
sock_put(sk);
return rc;
}
/**
* llc_rst_req_handler - Resets an established LLC connection
* @prim: pointer to structure that contains service parameters.
*
* Called when upper layer wants to reset 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.
*/
static int llc_rst_req_handler(struct llc_prim_if_block *prim)
{
int rc = 1;
struct sock *sk = prim->data->res.sk;
struct llc_conn_state_ev *ev;
lock_sock(sk);
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_RESET_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = llc_conn_send_ev(sk, ev);
}
release_sock(sk);
return rc;
}
/* We don't support flow control. The original code from procom has
* some bits, but for now I'm cleaning this
*/
static int llc_flowcontrol_req_handler(struct llc_prim_if_block *prim)
{
return 1;
}
/**
* llc_sap_resp - Sends response to peer
* @prim: pointer to structure that contains service parameters
*
* This function is a interface function to upper layer. each one who
* wants to response to an indicate can call this function via calling
* sap_resp with proper service parameters. Returns 0 for success, 1
* otherwise.
*/
static int llc_sap_resp(struct llc_prim_if_block *prim)
{
u16 rc = 1;
/* network layer RESPONSE primitive received; package primitive
* as an event and send it to the connection event handler
*/
if (prim->prim < LLC_NBR_PRIMITIVES)
/* valid primitive; call the function to handle it */
rc = llc_resp_prim[prim->prim](prim);
return rc;
}
/**
* llc_conn_rsp_handler - Response to connect indication
* @prim: pointer to structure that contains response info.
*
* Response to connect indication.
*/
static int llc_conn_rsp_handler(struct llc_prim_if_block *prim)
{
struct sock *sk = prim->data->conn.sk;
llc_sk(sk)->link = prim->data->conn.link;
return 0;
}
/**
* llc_rst_rsp_handler - Response to RESET indication
* @prim: pointer to structure that contains response info
*
* Returns 0 for success, 1 otherwise
*/
static int llc_rst_rsp_handler(struct llc_prim_if_block *prim)
{
int rc = 1;
/* network layer supplies connection handle; map it to a connection;
* package as event and send it to connection event handler
*/
struct sock *sk = prim->data->res.sk;
struct llc_conn_state_ev *ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_RESET_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_RESP;
ev->data.prim.data = prim;
rc = llc_conn_send_ev(sk, ev);
}
return rc;
}
static int llc_no_rsp_handler(struct llc_prim_if_block *prim)
{
return 0;
}
EXPORT_SYMBOL(llc_sap_open);
EXPORT_SYMBOL(llc_sap_close);
/*
* llc_mac.c - Manages interface between LLC and MAC
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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/if_arp.h>
#include <linux/if_tr.h>
#include <linux/rtnetlink.h>
#include <net/llc_if.h>
#include <net/llc_mac.h>
#include <net/llc_pdu.h>
#include <net/llc_sap.h>
#include <net/llc_conn.h>
#include <net/sock.h>
#include <net/llc_main.h>
#include <net/llc_evnt.h>
#include <net/llc_c_ev.h>
#include <net/llc_s_ev.h>
#ifdef CONFIG_TR
extern void tr_source_route(struct sk_buff *skb, struct trh_hdr *trh,
struct net_device *dev);
#endif
/* function prototypes */
static void fix_up_incoming_skb(struct sk_buff *skb);
/**
* mac_send_pdu - Sends PDU to specific device.
* @skb: pdu which must be sent
*
* If module is not initialized then returns failure, else figures out
* where to direct this PDU. Sends PDU to specific device, at this point a
* device must has been assigned to the PDU; If not, can't transmit the
* PDU. PDU sent to MAC layer, is free to re-send at a later time. Returns
* 0 on success, 1 for failure.
*/
int mac_send_pdu(struct sk_buff *skb)
{
struct sk_buff *skb2;
int pri = GFP_ATOMIC, rc = -1;
if (!skb->dev) {
printk(KERN_ERR __FUNCTION__ ": skb->dev == NULL!");
goto out;
}
if (skb->sk)
pri = (int)skb->sk->priority;
skb2 = skb_clone(skb, pri);
if (!skb2)
goto out;
rc = 0;
dev_queue_xmit(skb2);
out:
return rc;
}
/**
* mac_indicate - 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_pdu_router for sending to proper state machine. If frame is
* related to a busy connection (a connection is sending data now),
* function queues this frame in connection's backlog.
*/
int mac_indicate(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt)
{
struct llc_sap *sap;
llc_pdu_sn_t *pdu;
u8 dest;
/* When the interface is in promisc. mode, drop all the crap that it
* receives, do not try to analyse it.
*/
if (skb->pkt_type == PACKET_OTHERHOST) {
printk(KERN_INFO __FUNCTION__ ": PACKET_OTHERHOST\n");
goto drop;
}
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
goto out;
fix_up_incoming_skb(skb);
pdu = (llc_pdu_sn_t *)skb->nh.raw;
if (!pdu->dsap) { /* NULL DSAP, refer to station */
llc_pdu_router(NULL, NULL, skb, 0);
goto out;
}
sap = llc_sap_find(pdu->dsap);
if (!sap) /* unknown SAP */
goto drop;
llc_decode_pdu_type(skb, &dest);
if (dest == LLC_DEST_SAP) /* type 1 services */
llc_pdu_router(sap, NULL, skb, LLC_TYPE_1);
else if (dest == LLC_DEST_CONN) {
struct llc_addr saddr, daddr;
struct sock *sk;
llc_pdu_decode_sa(skb, saddr.mac);
llc_pdu_decode_ssap(skb, &saddr.lsap);
llc_pdu_decode_da(skb, daddr.mac);
llc_pdu_decode_dsap(skb, &daddr.lsap);
sk = llc_find_sock(sap, &saddr, &daddr);
if (!sk) { /* didn't find an active connection; allocate a
* connection to use; associate it with this SAP
*/
sk = llc_sock_alloc();
if (!sk)
goto drop;
memcpy(&llc_sk(sk)->daddr, &saddr, sizeof(saddr));
llc_sap_assign_sock(sap, sk);
sock_hold(sk);
}
bh_lock_sock(sk);
if (!sk->lock.users) {
/* FIXME: Check this on SMP as it is now calling
* llc_pdu_router _with_ the lock held.
* Old comment:
* With the current code one can't call
* llc_pdu_router with the socket lock held, cause
* it'll route the pdu to the upper layers and it can
* reenter llc and in llc_req_prim will try to grab
* the same lock, maybe we should use spin_trylock_bh
* in the llc_req_prim (llc_data_req_handler, etc) and
* add the request to the backlog, well see...
*/
llc_pdu_router(llc_sk(sk)->sap, sk, skb, LLC_TYPE_2);
bh_unlock_sock(sk);
} else {
skb->cb[0] = LLC_PACKET;
sk_add_backlog(sk, skb);
bh_unlock_sock(sk);
}
sock_put(sk);
} else /* unknown or not supported pdu */
goto drop;
out:
return 0;
drop:
kfree_skb(skb);
goto out;
}
/**
* fix_up_incoming_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 void fix_up_incoming_skb(struct sk_buff *skb)
{
u8 llc_len = 2;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->data;
if ((pdu->ctrl_1 & LLC_PDU_TYPE_MASK) == LLC_PDU_TYPE_U)
llc_len = 1;
llc_len += 2;
skb_pull(skb, llc_len);
if (skb->protocol == htons(ETH_P_802_2)) {
u16 pdulen = ((struct ethhdr *)skb->mac.raw)->h_proto,
data_size = ntohs(pdulen) - llc_len;
skb_trim(skb, data_size);
}
}
/**
* llc_pdu_router - routes received pdus to the upper layers
* @sap: current sap component structure.
* @sk: current connection structure.
* @frame: received frame.
* @type: type of received frame, that is LLC_TYPE_1 or LLC_TYPE_2
*
* Queues received PDUs from LLC_MAC PDU receive queue until queue is
* empty; examines LLC header to determine the destination of PDU, if DSAP
* is NULL then data unit destined for station else frame destined for SAP
* or connection; finds a matching open SAP, if one, forwards the packet
* to it; if no matching SAP, drops the packet. Returns 0 or the return of
* llc_conn_send_ev (that may well result in the connection being
* destroyed)
*/
int llc_pdu_router(struct llc_sap *sap, struct sock* sk,
struct sk_buff *skb, u8 type)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
int rc = 0;
if (!pdu->dsap) {
struct llc_station *station = llc_station_get();
struct llc_station_state_ev *stat_ev =
llc_station_alloc_ev(station);
if (stat_ev) {
stat_ev->type = LLC_STATION_EV_TYPE_PDU;
stat_ev->data.pdu.skb = skb;
stat_ev->data.pdu.reason = 0;
llc_station_send_ev(station, stat_ev);
}
} else if (type == LLC_TYPE_1) {
struct llc_sap_state_ev *sap_ev = llc_sap_alloc_ev(sap);
if (sap_ev) {
sap_ev->type = LLC_SAP_EV_TYPE_PDU;
sap_ev->data.pdu.skb = skb;
sap_ev->data.pdu.reason = 0;
llc_sap_send_ev(sap, sap_ev);
}
} else if (type == LLC_TYPE_2) {
struct llc_conn_state_ev *conn_ev = llc_conn_alloc_ev(sk);
struct llc_opt *llc = llc_sk(sk);
if (!llc->dev)
llc->dev = skb->dev;
if (conn_ev) {
conn_ev->type = LLC_CONN_EV_TYPE_PDU;
conn_ev->data.pdu.skb = skb;
conn_ev->data.pdu.reason = 0;
rc = llc_conn_send_ev(sk, conn_ev);
}
}
return rc;
}
/**
* lan_hdrs_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.
*/
u16 lan_hdrs_init(struct sk_buff *skb, u8 *sa, u8 *da)
{
u8 *saddr;
u8 *daddr;
u16 rc = 0;
switch (skb->dev->type) {
#ifdef CONFIG_TR
case ARPHRD_IEEE802_TR: {
struct trh_hdr *trh = (struct trh_hdr *)
skb_push(skb, sizeof(*trh));
struct net_device *dev = skb->dev;
trh->ac = AC;
trh->fc = LLC_FRAME;
if (sa)
memcpy(trh->saddr, sa, dev->addr_len);
else
memset(trh->saddr, 0, dev->addr_len);
if (da) {
memcpy(trh->daddr, da, dev->addr_len);
tr_source_route(skb, trh, dev);
}
skb->mac.raw = skb->data;
break;
}
#endif
case ARPHRD_ETHER:
case ARPHRD_LOOPBACK: {
unsigned short len = skb->len;
skb->mac.raw = skb_push(skb, sizeof(struct ethhdr));
memset(skb->mac.raw, 0, sizeof(struct ethhdr));
((struct ethhdr *)skb->mac.raw)->h_proto = htons(len);
daddr = ((struct ethhdr *)skb->mac.raw)->h_dest;
saddr = ((struct ethhdr *)skb->mac.raw)->h_source;
memcpy(daddr, da, ETH_ALEN);
memcpy(saddr, sa, ETH_ALEN);
break;
}
default:
printk(KERN_WARNING "Unknown DEVICE type : %d\n",
skb->dev->type);
rc = 1;
}
return rc;
}
/**
* mac_dev_peer - search the appropriate dev to send packets to peer
* @current_dev - Current device suggested by upper layer
* @type - hardware type
* @mac - mac address
*
* Check if the we should use loopback to send packets, i.e., if the
* dmac belongs to one of the local interfaces, returning the pointer
* to the loopback &net_device struct or the current_dev if it is not
* local.
*/
struct net_device *mac_dev_peer(struct net_device *current_dev, int type,
u8 *mac)
{
struct net_device *dev;
rtnl_lock();
dev = dev_getbyhwaddr(type, mac);
if (dev)
dev = __dev_get_by_name("lo");
rtnl_unlock();
return dev ? : current_dev;
}
/*
* llc_main.c - This module contains main functions to manage station, saps
* and connections of the LLC.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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/kernel.h>
#include <linux/netdevice.h>
#include <net/sock.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_conn.h>
#include <net/llc_main.h>
#include <net/llc_evnt.h>
#include <net/llc_actn.h>
#include <net/llc_stat.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_mac.h>
#include <linux/llc.h>
/* static function prototypes */
static void llc_station_service_events(struct llc_station *station);
static void llc_station_free_ev(struct llc_station *station,
struct llc_station_state_ev *ev);
static void llc_station_send_pdus(struct llc_station *station);
static u16 llc_station_next_state(struct llc_station *station,
struct llc_station_state_ev *ev);
static u16 llc_exec_station_trans_actions(struct llc_station *station,
struct llc_station_state_trans *trans,
struct llc_station_state_ev *ev);
static struct llc_station_state_trans *
llc_find_station_trans(struct llc_station *station,
struct llc_station_state_ev *ev);
static int llc_rtn_all_conns(struct llc_sap *sap);
extern void llc_register_sap(unsigned char sap,
int (*rcvfunc)(struct sk_buff *skb,
struct net_device *dev,
struct packet_type *pt));
extern void llc_unregister_sap(unsigned char sap);
static struct llc_station llc_main_station; /* only one of its kind */
struct llc_prim_if_block llc_ind_prim, llc_cfm_prim;
static union llc_u_prim_data llc_ind_data_prim, llc_cfm_data_prim;
/**
* llc_sap_alloc - allocates and initializes sap.
*
* Allocates and initializes sap.
*/
struct llc_sap *llc_sap_alloc(void)
{
struct llc_sap *sap = kmalloc(sizeof(*sap), GFP_ATOMIC);
if (sap) {
memset(sap, 0, sizeof(*sap));
sap->state = LLC_SAP_STATE_ACTIVE;
memcpy(sap->laddr.mac, llc_main_station.mac_sa, ETH_ALEN);
spin_lock_init(&sap->sk_list.lock);
INIT_LIST_HEAD(&sap->sk_list.list);
skb_queue_head_init(&sap->mac_pdu_q);
}
return sap;
}
/**
* llc_free_sap - frees a sap
* @sap: Address of the sap
*
* Frees all associated connections (if any), removes this sap from
* the list of saps in te station and them frees the memory for this sap.
*/
void llc_free_sap(struct llc_sap *sap)
{
struct llc_station *station = sap->parent_station;
llc_rtn_all_conns(sap);
spin_lock_bh(&station->sap_list.lock);
list_del(&sap->node);
spin_unlock_bh(&station->sap_list.lock);
kfree(sap);
}
/**
* llc_sap_save - add sap to station list
* @sap: Address of the sap
*
* Adds a sap to the LLC's station sap list.
*/
void llc_sap_save(struct llc_sap *sap)
{
spin_lock_bh(&llc_main_station.sap_list.lock);
list_add_tail(&sap->node, &llc_main_station.sap_list.list);
spin_unlock_bh(&llc_main_station.sap_list.lock);
}
/**
* llc_sap_find - searchs a SAP in station
* @sap_value: sap to be found
*
* Searchs for a sap in the sap list of the LLC's station upon the sap ID.
* Returns the sap or %NULL if not found.
*/
struct llc_sap *llc_sap_find(u8 sap_value)
{
struct llc_sap* sap = NULL;
struct list_head *entry;
spin_lock_bh(&llc_main_station.sap_list.lock);
list_for_each(entry, &llc_main_station.sap_list.list) {
sap = list_entry(entry, struct llc_sap, node);
if (sap->laddr.lsap == sap_value)
break;
}
if (entry == &llc_main_station.sap_list.list) /* not found */
sap = NULL;
spin_unlock_bh(&llc_main_station.sap_list.lock);
return sap;
}
/**
* llc_backlog_rcv - Processes rx frames and expired timers.
* @sk: LLC sock (p8022 connection)
* @skb: queued rx frame or event
*
* This function processes frames that has received and timers that has
* expired during sending an I pdu (refer to data_req_handler). frames
* queue by mac_indicate function (llc_mac.c) and timers queue by timer
* callback functions(llc_c_ac.c).
*/
static int llc_backlog_rcv(struct sock *sk, struct sk_buff *skb)
{
int rc = 0;
struct llc_opt *llc = llc_sk(sk);
if (skb->cb[0] == LLC_PACKET) {
if (llc->state > 1) /* not closed */
rc = llc_pdu_router(llc->sap, sk, skb, LLC_TYPE_2);
else
kfree_skb(skb);
} else if (skb->cb[0] == LLC_EVENT) {
struct llc_conn_state_ev *ev =
(struct llc_conn_state_ev *)skb->data;
/* timer expiration event */
if (llc->state > 1) /* not closed */
rc = llc_conn_send_ev(sk, ev);
else
llc_conn_free_ev(ev);
kfree_skb(skb);
}
return rc;
}
/**
* llc_sock_init - Initialize a socket with default llc values.
* @sk: socket to intiailize.
*/
int llc_sock_init(struct sock* sk)
{
struct llc_opt *llc = kmalloc(sizeof(*llc), GFP_ATOMIC);
int rc = -ENOMEM;
if (!llc)
goto out;
memset(llc, 0, sizeof(*llc));
rc = 0;
llc->sk = sk;
llc->state = LLC_CONN_STATE_ADM;
llc->inc_cntr = llc->dec_cntr = 2;
llc->dec_step = llc->connect_step = 1;
llc->ack_timer.expire = LLC_ACK_TIME;
llc->pf_cycle_timer.expire = LLC_P_TIME;
llc->rej_sent_timer.expire = LLC_REJ_TIME;
llc->busy_state_timer.expire = LLC_BUSY_TIME;
llc->n2 = 2; /* max retransmit */
llc->k = 2; /* tx win size, will adjust dynam */
llc->rw = 128; /* rx win size (opt and equal to
* tx_win of remote LLC)
*/
skb_queue_head_init(&llc->pdu_unack_q);
sk->backlog_rcv = llc_backlog_rcv;
llc_sk(sk) = llc;
out:
return rc;
}
/**
* __llc_sock_alloc - Allocates LLC sock
*
* Allocates a LLC sock and initializes it. Returns the new LLC sock
* or %NULL if there's no memory available for one
*/
struct sock *__llc_sock_alloc(void)
{
struct sock *sk = sk_alloc(PF_LLC, GFP_ATOMIC, 1, NULL);
if (!sk)
goto out;
if (llc_sock_init(sk))
goto outsk;
sock_init_data(NULL, sk);
out:
return sk;
outsk:
sk_free(sk);
sk = NULL;
goto out;
}
/**
* __llc_sock_free - Frees a LLC socket
* @sk - socket to free
*
* Frees a LLC socket
*/
void __llc_sock_free(struct sock *sk, u8 free)
{
struct llc_opt *llc = llc_sk(sk);
llc->state = LLC_CONN_OUT_OF_SVC;
/* stop all (possibly) running timers */
llc_conn_ac_stop_all_timers(sk, NULL);
/* handle return of frames on lists */
printk(KERN_INFO __FUNCTION__ ": unackq=%d, txq=%d\n",
skb_queue_len(&llc->pdu_unack_q),
skb_queue_len(&sk->write_queue));
skb_queue_purge(&sk->write_queue);
skb_queue_purge(&llc->pdu_unack_q);
if (free)
sock_put(sk);
}
/**
* llc_sock_reset - resets a connection
* @sk: LLC socket to reset
*
* Resets a connection to the out of service state. Stops its timers
* and frees any frames in the queues of the connection.
*/
void llc_sock_reset(struct sock *sk)
{
struct llc_opt *llc = llc_sk(sk);
llc_conn_ac_stop_all_timers(sk, NULL);
skb_queue_purge(&sk->write_queue);
skb_queue_purge(&llc->pdu_unack_q);
llc->remote_busy_flag = 0;
llc->cause_flag = 0;
llc->retry_count = 0;
llc->p_flag = 0;
llc->f_flag = 0;
llc->s_flag = 0;
llc->ack_pf = 0;
llc->first_pdu_Ns = 0;
llc->ack_must_be_send = 0;
llc->dec_step = 1;
llc->inc_cntr = 2;
llc->dec_cntr = 2;
llc->X = 0;
llc->failed_data_req = 0 ;
llc->last_nr = 0;
}
/**
* llc_rtn_all_conns - Closes all connections of a sap
* @sap: sap to close its connections
*
* Closes all connections of a sap. Returns 0 if all actions complete
* successfully, nonzero otherwise
*/
static int llc_rtn_all_conns(struct llc_sap *sap)
{
int rc = 0;
union llc_u_prim_data prim_data;
struct llc_prim_if_block prim;
struct list_head *entry, *tmp;
spin_lock_bh(&sap->sk_list.lock);
if (list_empty(&sap->sk_list.list))
goto out;
list_for_each_safe(entry, tmp, &sap->sk_list.list) {
struct llc_opt *llc = list_entry(entry, struct llc_opt, node);
prim.sap = sap;
prim_data.disc.sk = llc->sk;
prim.prim = LLC_DISC_PRIM;
prim.data = &prim_data;
llc->state = LLC_CONN_STATE_TEMP;
if (sap->req(&prim))
rc = 1;
}
out:
spin_unlock_bh(&sap->sk_list.lock);
return rc;
}
/**
* llc_station_get - get addr of global station.
*
* Returns address of a place to copy the global station to it.
*/
struct llc_station *llc_station_get(void)
{
return &llc_main_station;
}
/**
* llc_station_alloc_ev - allocates an event
* @station: Address of the station
*
* Allocates an event in this station. Returns the allocated event on
* success, %NULL otherwise.
*/
struct llc_station_state_ev *llc_station_alloc_ev(struct llc_station *station)
{
struct llc_station_state_ev *ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
if (ev)
memset(ev, 0, sizeof(*ev));
return ev;
}
/**
* llc_station_send_ev: queue event and try to process queue.
* @station: Address of the station
* @ev: Address of the event
*
* Queues an event (on the station event queue) for handling by the
* station state machine and attempts to process any queued-up events.
*/
void llc_station_send_ev(struct llc_station *station,
struct llc_station_state_ev *ev)
{
spin_lock_bh(&station->ev_q.lock);
list_add_tail(&ev->node, &station->ev_q.list);
llc_station_service_events(station);
spin_unlock_bh(&station->ev_q.lock);
}
/**
* llc_station_send_pdu - queues PDU to send
* @station: Address of the station
* @skb: Address of the PDU
*
* Queues a PDU to send to the MAC layer.
*/
void llc_station_send_pdu(struct llc_station *station, struct sk_buff *skb)
{
skb_queue_tail(&station->mac_pdu_q, skb);
llc_station_send_pdus(station);
}
/**
* llc_station_send_pdus - tries to send queued PDUs
* @station: Address of the station
*
* Tries to send any PDUs queued in the station mac_pdu_q to the MAC
* layer.
*/
static void llc_station_send_pdus(struct llc_station *station)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&station->mac_pdu_q)) != NULL) {
int rc = mac_send_pdu(skb);
kfree_skb(skb);
if (rc)
break;
}
}
/**
* llc_station_free_ev - frees an event
* @station: Address of the station
* @event: Address of the event
*
* Frees an event.
*/
static void llc_station_free_ev(struct llc_station *station,
struct llc_station_state_ev *ev)
{
struct sk_buff *skb = ev->data.pdu.skb;
if (ev->type == LLC_STATION_EV_TYPE_PDU)
kfree_skb(skb);
kfree(ev);
}
/**
* llc_station_service_events - service events in the queue
* @station: Address of the station
*
* Get an event from the station event queue (if any); attempt to service
* the event; if event serviced, get the next event (if any) on the event
* queue; if event not service, re-queue the event on the event queue and
* attempt to service the next event; when serviced all events in queue,
* finished; if don't transition to different state, just service all
* events once; if transition to new state, service all events again.
* Caller must hold station->ev_q.lock.
*/
static void llc_station_service_events(struct llc_station *station)
{
struct llc_station_state_ev *ev;
struct list_head *entry, *tmp;
list_for_each_safe(entry, tmp, &station->ev_q.list) {
ev = list_entry(entry, struct llc_station_state_ev, node);
list_del(&ev->node);
llc_station_next_state(station, ev);
}
}
/**
* llc_station_next_state - processes event and goes to the next state
* @station: Address of the station
* @ev: Address of the event
*
* Processes an event, executes any transitions related to that event and
* updates the state of the station.
*/
static u16 llc_station_next_state(struct llc_station *station,
struct llc_station_state_ev *ev)
{
u16 rc = 1;
struct llc_station_state_trans *trans;
if (station->state > LLC_NBR_STATION_STATES)
goto out;
trans = llc_find_station_trans(station, ev);
if (trans) {
/* got the state to which we next transition; perform the
* actions associated with this transition before actually
* transitioning to the next state
*/
rc = llc_exec_station_trans_actions(station, trans, ev);
if (!rc)
/* transition station to next state if all actions
* execute successfully; done; wait for next event
*/
station->state = trans->next_state;
} else
/* event not recognized in current state; re-queue it for
* processing again at a later time; return failure
*/
rc = 0;
out:
llc_station_free_ev(station, ev);
return rc;
}
/**
* llc_find_station_trans - finds transition for this event
* @station: Address of the station
* @ev: Address of the event
*
* Search thru events of the current state of the station until list
* exhausted or it's obvious that the event is not valid for the current
* state. Returns the address of the transition if cound, %NULL otherwise.
*/
static struct llc_station_state_trans *
llc_find_station_trans(struct llc_station *station,
struct llc_station_state_ev *ev)
{
int i = 0;
struct llc_station_state_trans *rc = NULL;
struct llc_station_state_trans **next_trans;
struct llc_station_state *curr_state =
&llc_station_state_table[station->state - 1];
for (next_trans = curr_state->transitions; next_trans[i]->ev; i++)
if (!next_trans[i]->ev(station, ev)) {
rc = next_trans[i];
break;
}
return rc;
}
/**
* llc_exec_station_trans_actions - executes actions for transition
* @station: Address of the station
* @trans: Address of the transition
* @ev: Address of the event that caused the transition
*
* Executes actions of a transition of the station state machine. Returns
* 0 if all actions complete successfully, nonzero otherwise.
*/
static u16 llc_exec_station_trans_actions(struct llc_station *station,
struct llc_station_state_trans *trans,
struct llc_station_state_ev *ev)
{
u16 rc = 0;
llc_station_action_t *next_action;
for (next_action = trans->ev_actions;
next_action && *next_action; next_action++)
if ((*next_action)(station, ev))
rc = 1;
return rc;
}
/**
* llc_alloc_frame - allocates sk_buff for frame
*
* Allocates an sk_buff for frame and initializes sk_buff fields.
* Returns allocated skb or %NULL when out of memory.
*/
struct sk_buff *llc_alloc_frame(void)
{
struct sk_buff *skb = alloc_skb(128, GFP_ATOMIC);
if (skb) {
skb_reserve(skb, 50);
skb->nh.raw = skb->h.raw = skb->data;
skb->protocol = htons(ETH_P_802_2);
skb->dev = dev_base->next;
skb->mac.raw = skb->head;
}
return skb;
}
static int llc_proc_get_info(char *bf, char **start, off_t offset, int length)
{
struct llc_opt *llc;
struct list_head *sap_entry, *llc_entry;
off_t begin = 0, pos = 0;
int len = 0;
spin_lock_bh(&llc_main_station.sap_list.lock);
list_for_each(sap_entry, &llc_main_station.sap_list.list) {
struct llc_sap *sap = list_entry(sap_entry, struct llc_sap,
node);
len += sprintf(bf + len, "lsap=%d\n", sap->laddr.lsap);
spin_lock_bh(&sap->sk_list.lock);
if (list_empty(&sap->sk_list.list)) {
len += sprintf(bf + len, "no connections\n");
goto unlock;
}
len += sprintf(bf + len,
"connection list:\nstate retr txwin rxwin\n");
list_for_each(llc_entry, &sap->sk_list.list) {
llc = list_entry(llc_entry, struct llc_opt, node);
len += sprintf(bf + len, " %-5d%-5d%-6d%-5d\n",
llc->state, llc->retry_count, llc->k,
llc->rw);
}
unlock:
spin_unlock_bh(&sap->sk_list.lock);
pos = begin + len;
if (pos < offset) {
len = 0; /* Keep dumping into the buffer start */
begin = pos;
}
if (pos > offset + length) /* We have dumped enough */
break;
}
spin_unlock_bh(&llc_main_station.sap_list.lock);
/* The data in question runs from begin to begin + len */
*start = bf + (offset - begin); /* Start of wanted data */
len -= (offset - begin); /* Remove unwanted header data from length */
return len;
}
static char llc_banner[] __initdata =
KERN_INFO "LLC 2.0 by Procom, 1997, Arnaldo C. Melo, 2001\n"
KERN_INFO "NET4.0 IEEE 802.2 extended support\n";
static char llc_error_msg[] __initdata =
KERN_ERR "LLC install NOT successful.\n";
static int __init llc_init(void)
{
u16 rc = 0;
struct llc_station_state_ev *ev;
printk(llc_banner);
INIT_LIST_HEAD(&llc_main_station.ev_q.list);
spin_lock_init(&llc_main_station.ev_q.lock);
INIT_LIST_HEAD(&llc_main_station.sap_list.list);
spin_lock_init(&llc_main_station.sap_list.lock);
skb_queue_head_init(&llc_main_station.mac_pdu_q);
ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
if (!ev)
goto err;
memset(ev, 0, sizeof(*ev));
if(dev_base->next)
memcpy(llc_main_station.mac_sa, dev_base->next->dev_addr, ETH_ALEN);
else
memset(llc_main_station.mac_sa, 0, ETH_ALEN);
llc_main_station.ack_timer.expires = jiffies + 3 * HZ;
/* initialize the station component */
llc_register_sap(0, mac_indicate);
llc_main_station.maximum_retry = 1;
llc_main_station.state = LLC_STATION_STATE_DOWN;
ev->type = LLC_STATION_EV_TYPE_SIMPLE;
ev->data.a.ev = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK;
rc = llc_station_next_state(&llc_main_station, ev);
llc_build_offset_table();
llc_ind_prim.data = &llc_ind_data_prim;
llc_cfm_prim.data = &llc_cfm_data_prim;
proc_net_create("802.2", 0, llc_proc_get_info);
llc_ui_init();
out:
return rc;
err:
printk(llc_error_msg);
rc = 1;
goto out;
}
static void __exit llc_exit(void)
{
llc_ui_exit();
llc_unregister_sap(0);
proc_net_remove("802.2");
}
module_init(llc_init);
module_exit(llc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Procom, 1997, Arnaldo C. Melo, Jay Schullist, 2001");
MODULE_DESCRIPTION("LLC 2.0, NET4.0 IEEE 802.2 extended support");
/*
* llc_pdu.c - access to PDU internals
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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/if_tr.h>
#include <net/llc_pdu.h>
#include <net/llc_if.h>
#include <net/llc_mac.h>
#include <net/llc_main.h>
static int llc_pdu_decode_pdu_type(struct sk_buff *skb, u8 *type);
static int llc_get_llc_hdr_length(u8 pdu_type);
static u8 llc_pdu_get_pf_bit(llc_pdu_sn_t *pdu);
/**
* llc_pdu_header_init - initializes pdu header
* @skb: input skb that header must be set into it.
* @pdu_type: type of PDU (U, I or S).
* @ssap: source sap.
* @dsap: destination sap.
* @cr: command/response bit (0 or 1).
*
* This function sets DSAP, SSAP and command/Response bit in LLC header.
*/
void llc_pdu_header_init(struct sk_buff *skb, u8 pdu_type, u8 ssap,
u8 dsap, u8 cr)
{
llc_pdu_un_t *p;
skb->nh.raw = skb_push(skb, llc_get_llc_hdr_length(pdu_type));
p = (llc_pdu_un_t *)skb->nh.raw;
p->dsap = dsap;
p->ssap = ssap;
p->ssap |= cr;
}
void llc_pdu_set_cmd_rsp(struct sk_buff *skb, u8 pdu_type)
{
((llc_pdu_un_t *)skb->nh.raw)->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;
if (llc_pdu_decode_pdu_type(skb, &pdu_type))
goto out;
switch (pdu_type) {
case LLC_PDU_TYPE_I:
case LLC_PDU_TYPE_S:
((llc_pdu_sn_t *)skb->nh.raw)->ctrl_2 =
(((llc_pdu_sn_t *)skb->nh.raw)->ctrl_2 & 0xFE) |
bit_value;
break;
case LLC_PDU_TYPE_U:
((llc_pdu_un_t *)skb->nh.raw)->ctrl_1 |=
(((llc_pdu_un_t *)skb->nh.raw)->ctrl_1 & 0xEF) |
(bit_value << 4);
break;
}
out:;
}
/**
* 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.
*/
int llc_pdu_decode_pf_bit(struct sk_buff *skb, u8 *pf_bit)
{
u8 pdu_type;
int rc = llc_pdu_decode_pdu_type(skb, &pdu_type);
if (rc)
goto out;
switch (pdu_type) {
case LLC_PDU_TYPE_I:
case LLC_PDU_TYPE_S:
*pf_bit = ((llc_pdu_sn_t *)skb->nh.raw)->ctrl_2 &
LLC_S_PF_BIT_MASK;
break;
case LLC_PDU_TYPE_U:
*pf_bit = (((llc_pdu_un_t *)skb->nh.raw)->ctrl_1 &
LLC_U_PF_BIT_MASK) >> 4;
break;
}
out:
return 0;
}
/**
* llc_pdu_decode_cr_bit - extracs command response bit from LLC header
* @skb: input skb that c/r bit must be extracted from it.
* @cr_bit: command/response bit (0 or 1).
*
* This function extracts command/response bit from LLC header. this bit
* is right bit of source SAP.
*/
int llc_pdu_decode_cr_bit(struct sk_buff *skb, u8 *cr_bit)
{
*cr_bit = ((llc_pdu_un_t *)skb->nh.raw)->ssap & LLC_PDU_CMD_RSP_MASK;
return 0;
}
/**
* llc_pdu_decode_sa - extracs source address (MAC) of input frame
* @skb: input skb that source address must be extracted from it.
* @sa: pointer to source address (6 byte array).
*
* This function extracts source address(MAC) of input frame.
*/
int llc_pdu_decode_sa(struct sk_buff *skb, u8 *sa)
{
if (skb->protocol == ntohs(ETH_P_802_2))
memcpy(sa, ((struct ethhdr *)skb->mac.raw)->h_source, ETH_ALEN);
else if (skb->protocol == ntohs(ETH_P_TR_802_2))
memcpy(sa, ((struct trh_hdr *)skb->mac.raw)->saddr, ETH_ALEN);
return 0;
}
/**
* llc_pdu_decode_da - extracts dest address of input frame
* @skb: input skb that destination address must be extracted from it
* @sa: pointer to destination address (6 byte array).
*
* This function extracts destination address(MAC) of input frame.
*/
int llc_pdu_decode_da(struct sk_buff *skb, u8 *da)
{
if (skb->protocol == ntohs(ETH_P_802_2))
memcpy(da, ((struct ethhdr *)skb->mac.raw)->h_dest, ETH_ALEN);
else if (skb->protocol == ntohs(ETH_P_TR_802_2))
memcpy(da, ((struct trh_hdr *)skb->mac.raw)->daddr, ETH_ALEN);
return 0;
}
/**
* llc_pdu_decode_dsap - extracts dest SAP of input frame
* @skb: input skb that destination SAP must be extracted from it.
* @dsap: destination SAP (output argument).
*
* This function extracts destination SAP of input frame. right bit of
* DSAP designates individual/group SAP.
*/
int llc_pdu_decode_dsap(struct sk_buff *skb, u8 *dsap)
{
*dsap = ((llc_pdu_un_t *)skb->nh.raw)->dsap & 0xFE;
return 0;
}
/**
* llc_pdu_decode_ssap - extracts source SAP of input frame
* @skb: input skb that source SAP must be extracted from it.
* @ssap: source SAP (output argument).
*
* This function extracts source SAP of input frame. right bit of SSAP is
* command/response bit.
*/
int llc_pdu_decode_ssap(struct sk_buff *skb, u8 *ssap)
{
*ssap = ((llc_pdu_un_t *)skb->nh.raw)->ssap & 0xFE;
return 0;
}
/**
* llc_pdu_init_as_ui_cmd - sets LLC header as UI PDU
* @skb: input skb that header must be set into it.
*
* This function sets third byte of LLC header as a UI PDU.
*/
int llc_pdu_init_as_ui_cmd(struct sk_buff *skb)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_1_PDU_CMD_UI;
return 0;
}
/**
* llc_pdu_init_as_xid_cmd - sets bytes 3, 4 & 5 of LLC header as XID
* @skb: input skb that header must be set into it.
*
* This function sets third,fourth,fifth and sixth bytes of LLC header as
* a XID PDU.
*/
int llc_pdu_init_as_xid_cmd(struct sk_buff *skb, u8 svcs_supported,
u8 rx_window)
{
llc_xid_info_t *xid_info;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_1_PDU_CMD_XID;
pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;
xid_info = (llc_xid_info_t *)(((u8 *)&pdu->ctrl_1) + 1);
xid_info->fmt_id = LLC_XID_FMT_ID; /* 0x81 */
xid_info->type = svcs_supported;
xid_info->rw = rx_window << 1; /* size of recieve window */
skb_put(skb, 3);
return 0;
}
/**
* llc_pdu_init_as_test_cmd - sets PDU as TEST
* @skb - Address of the skb to build
*
* Sets a PDU as TEST
*/
int llc_pdu_init_as_test_cmd(struct sk_buff *skb)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_1_PDU_CMD_TEST;
pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;
return 0;
}
/**
* 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.
*/
int llc_pdu_init_as_disc_cmd(struct sk_buff *skb, u8 p_bit)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
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;
return 0;
}
/**
* 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.
*/
int llc_pdu_init_as_i_cmd(struct sk_buff *skb, u8 p_bit, u8 ns, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
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 */
return 0;
}
/**
* 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.
*/
int llc_pdu_init_as_rej_cmd(struct sk_buff *skb, u8 p_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
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 */
return 0;
}
/**
* 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.
*/
int llc_pdu_init_as_rnr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
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 */
return 0;
}
/**
* 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.
*/
int llc_pdu_init_as_rr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
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 */
return 0;
}
/**
* 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.
*/
int llc_pdu_init_as_sabme_cmd(struct sk_buff *skb, u8 p_bit)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
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;
return 0;
}
/**
* 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.
*/
int llc_pdu_init_as_dm_rsp(struct sk_buff *skb, u8 f_bit)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
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;
return 0;
}
/**
* pdu_init_as_xid_rsp - builds XID response PDU
* @skb: Address of the skb to build
* @svcs_supported: The class of the LLC (I or II)
* @rx_window: The size of the receive window of the LLC
*
* Builds a pdu frame as an XID response.
*/
int llc_pdu_init_as_xid_rsp(struct sk_buff *skb, u8 svcs_supported,
u8 rx_window)
{
llc_xid_info_t *xid_info;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_1_PDU_CMD_XID;
pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;
xid_info = (llc_xid_info_t *)(((u8 *)&pdu->ctrl_1) + 1);
xid_info->fmt_id = LLC_XID_FMT_ID;
xid_info->type = svcs_supported;
xid_info->rw = rx_window << 1;
skb_put(skb, 3);
return 0;
}
/**
* pdu_init_as_test_rsp - build TEST response PDU
* @skb: Address of the skb to build
* @ev_skb: The received TEST command PDU frame
*
* Builds a pdu frame as a TEST response.
*/
int llc_pdu_init_as_test_rsp(struct sk_buff *skb, struct sk_buff *ev_skb)
{
int dsize;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_1_PDU_CMD_TEST;
pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;
if (ev_skb->protocol == ntohs(ETH_P_802_2)) {
dsize = ntohs(((struct ethhdr *)ev_skb->mac.raw)->h_proto) - 3;
memcpy(((u8 *)skb->nh.raw) + 3,
((u8 *)ev_skb->nh.raw) + 3, dsize);
skb_put(skb, dsize);
}
return 0;
}
/**
* pdu_init_as_frmr_rsp - builds FRMR response PDU
* @pdu_frame: 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.
*/
int llc_pdu_init_as_frmr_rsp(struct sk_buff *skb, llc_pdu_sn_t *prev_pdu,
u8 f_bit, u8 vs, u8 vr, u8 vzyxw)
{
llc_frmr_info_t *frmr_info;
u8 prev_pf = 0;
u8 *ctrl;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
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 = (llc_frmr_info_t *)&pdu->ctrl_2;
ctrl = (u8 *)&prev_pdu->ctrl_1;
FRMR_INFO_SET_REJ_CNTRL(frmr_info,ctrl);
FRMR_INFO_SET_Vs(frmr_info, vs);
FRMR_INFO_SET_Vr(frmr_info, vr);
prev_pf = llc_pdu_get_pf_bit(prev_pdu);
FRMR_INFO_SET_C_R_BIT(frmr_info, prev_pf);
FRMR_INFO_SET_INVALID_PDU_CTRL_IND(frmr_info, vzyxw);
FRMR_INFO_SET_INVALID_PDU_INFO_IND(frmr_info, vzyxw);
FRMR_INFO_SET_PDU_INFO_2LONG_IND(frmr_info, vzyxw);
FRMR_INFO_SET_PDU_INVALID_Nr_IND(frmr_info, vzyxw);
FRMR_INFO_SET_PDU_INVALID_Ns_IND(frmr_info, vzyxw);
skb_put(skb, 5);
return 0;
}
/**
* 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.
*/
int llc_pdu_init_as_rr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
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 */
return 0;
}
/**
* 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.
*/
int llc_pdu_init_as_rej_rsp(struct sk_buff *skb, u8 f_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
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 */
return 0;
}
/**
* pdu_init_as_rnr_rsp - builds RNR response pdu
* @pdu_frame: 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.
*/
int llc_pdu_init_as_rnr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
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 */
return 0;
}
/**
* 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.
*/
int llc_pdu_init_as_ua_rsp(struct sk_buff *skb, u8 f_bit)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
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;
return 0;
}
/**
* 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 int llc_pdu_decode_pdu_type(struct sk_buff *skb, u8 *type)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
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;
return 0;
}
/**
* llc_decode_pdu_type - designates component LLC must handle for PDU
* @skb: input skb
* @dest: destination component
*
* This function designates which component of LLC must handle this PDU.
*/
int llc_decode_pdu_type(struct sk_buff *skb, u8 *dest)
{
u8 type = LLC_DEST_CONN; /* I-PDU or S-PDU type */
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
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:
*dest = type;
return 0;
}
/**
* get_llc_hdr_len - designates LLC header length
* @pdu_type: type of PDU.
*
* This function designates LLC header length of PDU. header length for I
* and S PDU is 4 and for U is 3 bytes. Returns the length of header.
*/
static int llc_get_llc_hdr_length(u8 pdu_type)
{
int rtn_val = 0;
switch (pdu_type) {
case LLC_PDU_TYPE_I:
case LLC_PDU_TYPE_S:
rtn_val = 4;
break;
case LLC_PDU_TYPE_U:
rtn_val = 3;
break;
}
return rtn_val;
}
/**
* 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(llc_pdu_sn_t *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;
}
/*
* 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 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_if.h>
#include <net/llc_sap.h>
#include <net/llc_main.h>
#include <net/llc_s_ev.h>
#include <net/llc_pdu.h>
#include <net/llc_mac.h>
/**
* llc_sap_action_unit_data_ind - forward UI PDU to network layer
* @sap: SAP
* @ev: 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 llc_sap_state_ev *ev)
{
llc_sap_rtn_pdu(sap, ev->data.pdu.skb, ev);
return 0;
}
/**
* llc_sap_action_send_ui - sends UI PDU resp to UNITDATA REQ to MAC layer
* @sap: SAP
* @ev: 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 llc_sap_state_ev *ev)
{
struct llc_prim_if_block *prim = ev->data.prim.data;
struct llc_prim_unit_data *prim_data = &prim->data->udata;
struct sk_buff *skb = prim->data->udata.skb;
int rc;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, prim_data->saddr.lsap,
prim_data->daddr.lsap, LLC_PDU_CMD);
rc = llc_pdu_init_as_ui_cmd(skb);
if (rc)
goto out;
rc = lan_hdrs_init(skb, prim_data->saddr.mac, prim_data->daddr.mac);
if (!rc)
llc_sap_send_pdu(sap, skb);
out:
return rc;
}
/**
* llc_sap_action_send_xid_c - send XID PDU as response to XID REQ
* @sap: SAP
* @ev: 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 llc_sap_state_ev *ev)
{
struct llc_prim_if_block *prim = ev->data.prim.data;
struct llc_prim_xid *prim_data = &prim->data->xid;
struct sk_buff *skb = prim_data->skb;
int rc;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, prim_data->saddr.lsap,
prim_data->daddr.lsap, LLC_PDU_CMD);
rc = llc_pdu_init_as_xid_cmd(skb, LLC_XID_NULL_CLASS_2, 0);
if (rc)
goto out;
rc = lan_hdrs_init(skb, prim_data->saddr.mac, prim_data->daddr.mac);
if (!rc)
llc_sap_send_pdu(sap, skb);
out:
return rc;
}
/**
* llc_sap_action_send_xid_r - send XID PDU resp to MAC for received XID
* @sap: SAP
* @ev: 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 llc_sap_state_ev *ev)
{
u8 mac_da[ETH_ALEN], mac_sa[ETH_ALEN], dsap;
int rc = 1;
struct sk_buff *ev_skb = ev->data.pdu.skb;
struct sk_buff *skb;
llc_pdu_decode_sa(ev_skb, mac_da);
llc_pdu_decode_da(ev_skb, mac_sa);
llc_pdu_decode_ssap(ev_skb, &dsap);
skb = llc_alloc_frame();
if (!skb)
goto out;
skb->dev = ev_skb->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap, dsap,
LLC_PDU_RSP);
rc = llc_pdu_init_as_xid_rsp(skb, LLC_XID_NULL_CLASS_2, 0);
if (rc)
goto out;
rc = lan_hdrs_init(skb, mac_sa, mac_da);
if (!rc)
llc_sap_send_pdu(sap, skb);
out:
return rc;
}
/**
* llc_sap_action_send_test_c - send TEST PDU to MAC in resp to TEST REQ
* @sap: SAP
* @ev: 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 llc_sap_state_ev *ev)
{
struct llc_prim_if_block *prim = ev->data.prim.data;
struct llc_prim_test *prim_data = &prim->data->test;
struct sk_buff *skb = prim_data->skb;
int rc;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, prim_data->saddr.lsap,
prim_data->daddr.lsap, LLC_PDU_CMD);
rc = llc_pdu_init_as_test_cmd(skb);
if (rc)
goto out;
rc = lan_hdrs_init(skb, prim_data->saddr.mac, prim_data->daddr.mac);
if (!rc)
llc_sap_send_pdu(sap, skb);
out:
return rc;
}
int llc_sap_action_send_test_r(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
u8 mac_da[ETH_ALEN], mac_sa[ETH_ALEN], dsap;
int rc = 1;
struct sk_buff *ev_skb = ev->data.pdu.skb;
struct sk_buff *skb;
llc_pdu_decode_sa(ev_skb, mac_da);
llc_pdu_decode_da(ev_skb, mac_sa);
llc_pdu_decode_ssap(ev_skb, &dsap);
skb = llc_alloc_frame();
if (!skb)
goto out;
skb->dev = ev_skb->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap, dsap,
LLC_PDU_RSP);
rc = llc_pdu_init_as_test_rsp(skb, ev_skb);
if (rc)
goto out;
rc = lan_hdrs_init(skb, mac_sa, mac_da);
if (!rc)
llc_sap_send_pdu(sap, skb);
out:
return rc;
}
/**
* llc_sap_action_report_status - report data link status to layer mgmt
* @sap: SAP
* @ev: 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 llc_sap_state_ev *ev)
{
return 0;
}
/**
* llc_sap_action_xid_ind - send XID PDU resp to net layer via XID IND
* @sap: SAP
* @ev: 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 llc_sap_state_ev *ev)
{
llc_sap_rtn_pdu(sap, ev->data.pdu.skb, ev);
return 0;
}
/**
* llc_sap_action_test_ind - send TEST PDU to net layer via TEST IND
* @sap: SAP
* @ev: 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 llc_sap_state_ev *ev)
{
llc_sap_rtn_pdu(sap, ev->data.pdu.skb, ev);
return 0;
}
/*
* 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 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 llc_sap_state_ev *ev)
{
return ev->type == LLC_SAP_EV_TYPE_SIMPLE &&
ev->data.a.ev == LLC_SAP_EV_ACTIVATION_REQ ? 0 : 1;
}
int llc_sap_ev_rx_ui(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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 llc_sap_state_ev *ev)
{
return ev->type == LLC_SAP_EV_TYPE_PRIM &&
ev->data.prim.prim == LLC_DATAUNIT_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_sap_ev_xid_req(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
return ev->type == LLC_SAP_EV_TYPE_PRIM &&
ev->data.prim.prim == LLC_XID_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_sap_ev_rx_xid_c(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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 llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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 llc_sap_state_ev *ev)
{
return ev->type == LLC_SAP_EV_TYPE_PRIM &&
ev->data.prim.prim == LLC_TEST_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_sap_ev_rx_test_c(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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 llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
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 llc_sap_state_ev *ev)
{
return ev->type == LLC_SAP_EV_TYPE_SIMPLE &&
ev->data.a.ev == LLC_SAP_EV_DEACTIVATION_REQ ? 0 : 1;
}
/*
* 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 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_n;
/* 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_n,
};
/* 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_n,
};
/* SAP state transition table */
struct llc_sap_state llc_sap_state_table[] = {
{
curr_state: LLC_SAP_STATE_INACTIVE,
transitions: llc_sap_inactive_state_transitions,
},
{
curr_state: LLC_SAP_STATE_ACTIVE,
transitions: llc_sap_active_state_transitions,
},
};
/*
* llc_sap.c - driver routines for SAP component.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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/skbuff.h>
#include <net/llc_conn.h>
#include <net/llc_sap.h>
#include <net/llc_s_ev.h>
#include <net/llc_s_ac.h>
#include <net/llc_s_st.h>
#include <net/sock.h>
#include <net/llc_main.h>
#include <net/llc_mac.h>
#include <net/llc_pdu.h>
#include <linux/if_tr.h>
static void llc_sap_free_ev(struct llc_sap *sap, struct llc_sap_state_ev *ev);
static int llc_sap_next_state(struct llc_sap *sap, struct llc_sap_state_ev *ev);
static int llc_exec_sap_trans_actions(struct llc_sap *sap,
struct llc_sap_state_trans *trans,
struct llc_sap_state_ev *ev);
static struct llc_sap_state_trans *llc_find_sap_trans(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
/**
* llc_sap_assign_sock - adds a connection to a SAP
* @sap: pointer to SAP.
* @conn: pointer to connection.
*
* This function adds a connection to connection_list of a SAP.
*/
void llc_sap_assign_sock(struct llc_sap *sap, struct sock *sk)
{
spin_lock_bh(&sap->sk_list.lock);
llc_sk(sk)->sap = sap;
list_add_tail(&llc_sk(sk)->node, &sap->sk_list.list);
sock_hold(sk);
spin_unlock_bh(&sap->sk_list.lock);
}
/**
* llc_sap_unassign_sock - removes a connection from SAP
* @sap: SAP
* @sk: pointer to connection
*
* This function removes a connection from connection_list of a SAP.
* List locking is performed by caller (rtn_all_conns).
*/
void llc_sap_unassign_sock(struct llc_sap *sap, struct sock *sk)
{
spin_lock_bh(&sap->sk_list.lock);
list_del(&llc_sk(sk)->node);
sock_put(sk);
spin_unlock_bh(&sap->sk_list.lock);
}
/**
* llc_sap_alloc_ev - allocates sap event
* @sap: pointer to SAP
* @ev: allocated event (output argument)
*
* Returns the allocated sap event or %NULL when out of memory.
*/
struct llc_sap_state_ev *llc_sap_alloc_ev(struct llc_sap *sap)
{
struct llc_sap_state_ev *ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
if (ev)
memset(ev, 0, sizeof(*ev));
return ev;
}
/**
* llc_sap_send_ev - sends event to SAP state machine
* @sap: pointer to SAP
* @ev: pointer to occurred event
*
* After executing actions of the event, upper layer will be indicated
* if needed(on receiving an UI frame).
*/
void llc_sap_send_ev(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
struct llc_prim_if_block *prim;
u8 flag;
llc_sap_next_state(sap, ev);
flag = ev->ind_cfm_flag;
prim = ev->prim;
if (flag == LLC_IND) {
skb_get(ev->data.pdu.skb);
sap->ind(prim);
}
llc_sap_free_ev(sap, ev);
}
/**
* llc_sap_rtn_pdu - Informs upper layer on rx of an UI, XID or TEST pdu.
* @sap: pointer to SAP
* @skb: received pdu
* @ev: pointer to occurred event
*/
void llc_sap_rtn_pdu(struct llc_sap *sap, struct sk_buff *skb,
struct llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu;
struct llc_prim_if_block *prim = &llc_ind_prim;
union llc_u_prim_data *prim_data = llc_ind_prim.data;
u8 lfb;
llc_pdu_decode_sa(skb, prim_data->udata.saddr.mac);
llc_pdu_decode_da(skb, prim_data->udata.daddr.mac);
llc_pdu_decode_dsap(skb, &prim_data->udata.daddr.lsap);
llc_pdu_decode_ssap(skb, &prim_data->udata.saddr.lsap);
prim_data->udata.pri = 0;
prim_data->udata.skb = skb;
pdu = (llc_pdu_un_t *)skb->nh.raw;
switch (LLC_U_PDU_RSP(pdu)) {
case LLC_1_PDU_CMD_TEST:
prim->prim = LLC_TEST_PRIM;
break;
case LLC_1_PDU_CMD_XID:
prim->prim = LLC_XID_PRIM;
break;
case LLC_1_PDU_CMD_UI:
if (skb->protocol == ntohs(ETH_P_TR_802_2)) {
if (((struct trh_hdr *)skb->mac.raw)->rcf) {
lfb = ntohs(((struct trh_hdr *)
skb->mac.raw)->rcf) &
0x0070;
prim_data->udata.lfb = lfb >> 4;
} else {
lfb = 0xFF;
prim_data->udata.lfb = 0xFF;
}
}
prim->prim = LLC_DATAUNIT_PRIM;
break;
}
prim->data = prim_data;
prim->sap = sap;
ev->ind_cfm_flag = LLC_IND;
ev->prim = prim;
}
/**
* llc_sap_send_pdu - Sends a frame to MAC layer for transmition
* @sap: pointer to SAP
* @skb: pdu that must be sent
*/
void llc_sap_send_pdu(struct llc_sap *sap, struct sk_buff *skb)
{
mac_send_pdu(skb);
kfree_skb(skb);
}
/**
* llc_sap_free_ev - frees an sap event
* @sap: pointer to SAP
* @ev: released event
*/
static void llc_sap_free_ev(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
if (ev->type == LLC_SAP_EV_TYPE_PDU) {
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
if (LLC_U_PDU_CMD(pdu) != LLC_1_PDU_CMD_UI)
kfree_skb(ev->data.pdu.skb);
}
kfree(ev);
}
/**
* llc_sap_next_state - finds transition, execs actions & change SAP state
* @sap: pointer to SAP
* @ev: 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 llc_sap_state_ev *ev)
{
int rc = 1;
struct llc_sap_state_trans *trans;
if (sap->state <= LLC_NBR_SAP_STATES) {
trans = llc_find_sap_trans(sap, ev);
if (trans) {
/* got the state to which we next transition; perform
* the actions associated with this transition before
* actually transitioning to the next state
*/
rc = llc_exec_sap_trans_actions(sap, trans, ev);
if (!rc)
/* transition SAP to next state if all actions
* execute successfully
*/
sap->state = trans->next_state;
}
}
return rc;
}
/**
* llc_find_sap_trans - finds transition for event
* @sap: pointer to SAP
* @ev: 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 llc_sap_state_ev* ev)
{
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, ev)) {
/* got event match; return it */
rc = next_trans[i];
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
* @ev: 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 llc_sap_state_ev *ev)
{
int rc = 0;
llc_sap_action_t *next_action;
for (next_action = trans->ev_actions;
next_action && *next_action; next_action++)
if ((*next_action)(sap, ev))
rc = 1;
return rc;
}
/*
* llc_sock.c - LLC User Interface SAPs
* Description:
* Functions in this module are implementation of socket based llc
* communications for the Linux operating system. Support of llc class
* one and class two is provided via SOCK_DGRAM and SOCK_STREAM
* respectively.
*
* An llc2 connection is (mac + sap), only one llc2 sap connection
* is allowed per mac. Though one sap may have multiple mac + sap
* connections.
*
* Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
#include <linux/proc_fs.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/errno.h>
#include <net/sock.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_pdu.h>
#include <net/llc_conn.h>
#include <linux/llc.h>
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/init.h>
#define dprintk(format, a...) printk(KERN_INFO __FUNCTION__ ": " format, ##a)
/* remember: uninitialized global data is zeroed because its in .bss */
static u16 llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
static u16 llc_ui_sap_link_no_max[256];
static u8 llc_ui_addrany[IFHWADDRLEN];
static struct sockaddr_llc llc_ui_addrnull;
static struct proto_ops llc_ui_ops;
static struct sock *llc_ui_sockets;
static rwlock_t llc_ui_sockets_lock = RW_LOCK_UNLOCKED;
static int llc_ui_indicate(struct llc_prim_if_block *prim);
static int llc_ui_confirm(struct llc_prim_if_block *prim);
static int llc_ui_wait_for_conn(struct sock *sk, int seconds);
static int llc_ui_wait_for_disc(struct sock *sk, int seconds);
/**
* llc_ui_next_link_no - return the next unused link number for a sap
* @sap: Address of sap to get link number from.
*
* Return the next unused link number for a given sap.
*/
static inline u16 llc_ui_next_link_no(int sap)
{
return llc_ui_sap_link_no_max[sap]++;
}
/**
* llc_ui_mac_match - determines if two mac addresses are the same
* @mac1: First mac address to compare.
* @mac2: Second mac address to compare.
*
* Determines if two given mac address are the same. Returns 0 if there
* is not a complete match up to len, 1 if a complete match up to len is
* found.
*/
static inline u8 llc_ui_mac_match(u8 *mac1, u8 *mac2)
{
return !memcmp(mac1, mac2, IFHWADDRLEN);
}
/**
* llc_ui_mac_null - determines if a address is a null mac address
* @mac: Mac address to test if null.
*
* Determines if a given address is a null mac address. Returns 0 if the
* address is not a null mac, 1 if the address is a null mac.
*/
static inline u8 llc_ui_mac_null(u8 *mac)
{
return !memcmp(mac, llc_ui_addrany, IFHWADDRLEN);
}
/**
* llc_ui_addr_null - determines if a address structure is null
* @addr: Address to test if null.
*/
static inline u8 llc_ui_addr_null(struct sockaddr_llc *addr)
{
return !memcmp(addr, &llc_ui_addrnull, sizeof(*addr));
}
/**
* llc_ui_protocol_type - return eth protocol for ARP header type
* @arphrd: ARP header type.
*
* Given an ARP header type return the corresponding ethernet protocol.
* Returns 0 if ARP header type not supported or the corresponding
* ethernet protocol type.
*/
static inline u16 llc_ui_protocol_type(u16 arphrd)
{
u16 rc = htons(ETH_P_802_2);
if (arphrd == ARPHRD_IEEE802_TR)
rc = htons(ETH_P_TR_802_2);
return rc;
}
/**
* llc_ui_header_len - return length of llc header based on operation
* @sk: Socket which contains a valid llc socket type.
* @addr: Complete sockaddr_llc structure received from the user.
*
* Provide the length of the llc header depending on what kind of
* operation the user would like to perform and the type of socket.
* Returns the correct llc header length.
*/
static inline u8 llc_ui_header_len(struct sock *sk, struct sockaddr_llc *addr)
{
u8 rc = LLC_PDU_LEN_U;
if (addr->sllc_test || addr->sllc_xid)
rc = LLC_PDU_LEN_U;
else if (sk->type == SOCK_STREAM)
rc = LLC_PDU_LEN_I;
return rc;
}
/**
* llc_ui_send_conn - send connect command for new llc2 connection
* @sap : Sap the socket is bound to.
* @addr: Source and destination fields provided by the user.
* @dev : Device which this connection should use.
* @link: Link number to assign to this connection.
*
* Send a connect command to the llc layer for a new llc2 connection.
* Returns 0 upon success, non-zero if action didn't succeed.
*/
static int llc_ui_send_conn(struct sock *sk, struct llc_sap *sap,
struct sockaddr_llc *addr,
struct net_device *dev, int link)
{
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
union llc_u_prim_data prim_data;
struct llc_prim_if_block prim;
prim.data = &prim_data;
prim.sap = sap;
prim.prim = LLC_CONN_PRIM;
prim_data.conn.dev = dev;
prim_data.conn.link = link;
prim_data.conn.sk = NULL;
prim_data.conn.handler = sk;
prim_data.conn.pri = 0;
prim_data.conn.saddr.lsap = llc_ui->addr.sllc_ssap;
prim_data.conn.daddr.lsap = addr->sllc_dsap;
memcpy(prim_data.conn.saddr.mac, dev->dev_addr, IFHWADDRLEN);
memcpy(prim_data.conn.daddr.mac, addr->sllc_dmac, IFHWADDRLEN);
return sap->req(&prim);
}
/**
* llc_ui_send_disc - send disc command to llc layer
* @sk: Socket with valid llc information.
*
* Send a disconnect command to the llc layer for an established
* llc2 connection.
* Returns 0 upon success, non-zero if action did not succeed.
*/
static int llc_ui_send_disc(struct sock *sk)
{
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
union llc_u_prim_data prim_data;
struct llc_prim_if_block prim;
int rc = 0;
if (sk->type != SOCK_STREAM || sk->state != TCP_ESTABLISHED)
goto out;
sk->state = TCP_CLOSING;
prim.data = &prim_data;
prim.sap = llc_ui->sap;
prim.prim = LLC_DISC_PRIM;
prim_data.disc.sk = llc_ui->core_sk;
prim_data.disc.link = llc_ui->link;
rc = llc_ui->sap->req(&prim);
out:
return rc;
}
/**
* llc_ui_send_data - send data via reliable llc2 connection
* @sap: Sap the socket is bound to.
* @sk: Connection the socket is using.
* @skb: Data the user wishes to send.
* @addr: Source and destination fields provided by the user.
*
* Send data via reliable llc2 connection.
* Returns 0 upon success, non-zero if action did not succeed.
*/
static int llc_ui_send_data(struct llc_sap *sap, struct sock* sk,
struct sk_buff *skb, struct sockaddr_llc *addr)
{
union llc_u_prim_data prim_data;
struct llc_prim_if_block prim;
struct llc_ui_opt* llc_ui = llc_ui_sk(sk);
struct llc_opt* llc_core = llc_sk(llc_ui->core_sk);
int rc;
prim.data = &prim_data;
prim.sap = sap;
prim.prim = LLC_DATA_PRIM;
prim_data.data.skb = skb;
prim_data.data.pri = 0;
prim_data.data.sk = llc_ui->core_sk;
skb->protocol = llc_ui_protocol_type(addr->sllc_arphrd);
sock_hold(sk);
try:
rc = sap->req(&prim);
if (rc != -EBUSY)
goto out;
rc = wait_event_interruptible(sk->socket->wait, !llc_ui->core_sk ||
!llc_core->failed_data_req);
if (!rc)
goto try;
if (!llc_ui->core_sk)
rc = -ENOTCONN;
out:
sock_put(sk);
return rc;
}
/**
* llc_ui_send_llc1 - send llc1 prim data block to llc layer.
* @sap : Sap the socket is bound to.
* @skb : Data the user wishes to send.
* @addr : Source and destination fields provided by the user.
* @primitive: Action the llc layer should perform.
*
* Send an llc1 primitive data block to the llc layer for processing.
* This function is used for test, xid and unit_data messages.
* Returns 0 upon success, non-zero if action did not succeed.
*/
static int llc_ui_send_llc1(struct llc_sap *sap, struct sk_buff *skb,
struct sockaddr_llc *addr, int primitive)
{
union llc_u_prim_data prim_data;
struct llc_prim_if_block prim;
prim.data = &prim_data;
prim.sap = sap;
prim.prim = primitive;
prim_data.test.skb = skb;
prim_data.test.saddr.lsap = sap->laddr.lsap;
prim_data.test.daddr.lsap = addr->sllc_dsap;
skb->protocol = llc_ui_protocol_type(addr->sllc_arphrd);
memcpy(prim_data.test.saddr.mac, skb->dev->dev_addr, IFHWADDRLEN);
memcpy(prim_data.test.daddr.mac, addr->sllc_dmac, IFHWADDRLEN);
return sap->req(&prim);
}
/**
* llc_ui_find_sap - returns sap struct that matches sap number specified
* @sap: Sap number to search for.
*
* Search the local socket list and return the first instance of the sap
* structure which matches the sap number the user specified.
* Returns llc_sap upon match, %NULL otherwise.
*/
static inline struct llc_sap *llc_ui_find_sap(u8 sap)
{
struct sock *sk;
struct llc_sap *s = NULL;
read_lock_bh(&llc_ui_sockets_lock);
for (sk = llc_ui_sockets; sk; sk = sk->next) {
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
if (!llc_ui->sap)
continue;
if (llc_ui->sap->laddr.lsap == sap) {
s = llc_ui->sap;
break;
}
}
read_unlock_bh(&llc_ui_sockets_lock);
return s;
}
static struct sock *__llc_ui_find_sk_by_exact(struct llc_addr *laddr,
struct llc_addr *daddr)
{
struct sock *sk;
for (sk = llc_ui_sockets; sk; sk = sk->next) {
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
if (llc_ui->addr.sllc_ssap == laddr->lsap &&
llc_ui->addr.sllc_dsap == daddr->lsap &&
llc_ui_mac_null(llc_ui->addr.sllc_mmac) &&
llc_ui_mac_match(llc_ui->addr.sllc_smac, laddr->mac) &&
llc_ui_mac_match(llc_ui->addr.sllc_dmac, daddr->mac))
break;
}
return sk;
}
/**
* __llc_ui_find_sk_by_addr - return socket matching local mac + sap.
* @addr: Local address to match.
*
* Search the local socket list and return the socket which has a matching
* local (mac + sap) address (allows null mac). This search will work on
* unconnected and connected sockets, though find_by_link_no is recommend
* for connected sockets.
* Returns sock upon match, %NULL otherwise.
*/
static struct sock *__llc_ui_find_sk_by_addr(struct llc_addr *laddr,
struct llc_addr *daddr,
struct net_device *dev)
{
struct sock *sk, *tmp_sk;
for (sk = llc_ui_sockets; sk; sk = sk->next) {
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
if (llc_ui->addr.sllc_ssap != laddr->lsap)
continue;
if (llc_ui_mac_null(llc_ui->addr.sllc_smac)) {
if (!llc_ui_mac_null(llc_ui->addr.sllc_mmac) &&
!llc_ui_mac_match(llc_ui->addr.sllc_mmac,
laddr->mac))
continue;
break;
}
if (dev && !llc_ui_mac_null(llc_ui->addr.sllc_mmac) &&
llc_ui_mac_match(llc_ui->addr.sllc_mmac, laddr->mac) &&
llc_ui_mac_match(llc_ui->addr.sllc_smac, dev->dev_addr))
break;
if (dev->flags & IFF_LOOPBACK)
break;
if (!llc_ui_mac_match(llc_ui->addr.sllc_smac, laddr->mac))
continue;
tmp_sk = __llc_ui_find_sk_by_exact(laddr, daddr);
if (tmp_sk) {
sk = tmp_sk;
break;
}
if (llc_ui_mac_null(llc_ui->addr.sllc_dmac))
break;
}
return sk;
}
static struct sock *llc_ui_find_sk_by_addr(struct llc_addr *addr,
struct llc_addr *daddr,
struct net_device *dev)
{
struct sock *sk;
read_lock(&llc_ui_sockets_lock);
sk = __llc_ui_find_sk_by_addr(addr, daddr, dev);
if (sk)
sock_hold(sk);
read_unlock(&llc_ui_sockets_lock);
return sk;
}
static struct sock *llc_ui_bh_find_sk_by_addr(struct llc_addr *addr,
struct llc_addr *daddr,
struct net_device *dev)
{
struct sock *sk;
read_lock_bh(&llc_ui_sockets_lock);
sk = __llc_ui_find_sk_by_addr(addr, daddr, dev);
if (sk)
sock_hold(sk);
read_unlock_bh(&llc_ui_sockets_lock);
return sk;
}
/**
* llc_ui_insert_socket - insert socket into list
* @sk: Socket to insert.
*
* Insert a socket into the local llc socket list.
*/
static inline void llc_ui_insert_socket(struct sock *sk)
{
write_lock_bh(&llc_ui_sockets_lock);
sk->next = llc_ui_sockets;
if (sk->next)
llc_ui_sockets->pprev = &sk->next;
llc_ui_sockets = sk;
sk->pprev = &llc_ui_sockets;
sock_hold(sk);
write_unlock_bh(&llc_ui_sockets_lock);
}
/**
* llc_ui_remove_socket - remove socket from list
* @sk: Socket to remove.
*
* Remove a socket from the local llc socket list.
*/
static inline void llc_ui_remove_socket(struct sock *sk)
{
write_lock_bh(&llc_ui_sockets_lock);
if (sk->pprev) {
if (sk->next)
sk->next->pprev = sk->pprev;
*sk->pprev = sk->next;
sk->pprev = NULL;
/* this only makes sense if the socket was inserted on the
* list, if sk->pprev is NULL it wasn't
*/
sock_put(sk);
}
write_unlock_bh(&llc_ui_sockets_lock);
}
/**
* llc_ui_destroy_sk - destroy socket
* @data: Socket which is to be destroyed.
*
* Really destroy the socket.
*/
static void llc_ui_destroy_sk(struct sock *sk)
{
skb_queue_purge(&sk->receive_queue);
skb_queue_purge(&sk->write_queue);
sock_put(sk);
MOD_DEC_USE_COUNT;
}
/**
* llc_ui_destroy_timer - try to destroy socket again
* @data: Socket which is to be destroyed.
*
* Attempt to destroy a socket which was previously destroyed but
* was still in use at the time.
*/
static void llc_ui_destroy_timer(unsigned long data)
{
struct sock *sk = (struct sock *)data;
if (!atomic_read(&sk->wmem_alloc) &&
!atomic_read(&sk->rmem_alloc) && sk->dead)
llc_ui_destroy_sk(sk);
else {
sk->timer.expires = jiffies + SOCK_DESTROY_TIME;
add_timer(&sk->timer);
}
}
/**
* llc_ui_create - alloc and init a new llc_ui socket
* @sock: Socket to initialize and attach allocated sk to.
* @protocol: Unused.
*
* Allocate and initialize a new llc_ui socket, validate the user wants a
* socket type we have available.
* Returns 0 upon success, negative upon failure.
*/
static int llc_ui_create(struct socket *sock, int protocol)
{
struct sock *sk;
struct llc_ui_opt *llc_ui;
int rc = -ESOCKTNOSUPPORT;
MOD_INC_USE_COUNT;
if (sock->type != SOCK_DGRAM && sock->type != SOCK_STREAM)
goto decmod;
rc = -ENOMEM;
sk = sk_alloc(PF_LLC, GFP_KERNEL, 1, NULL);
if (!sk)
goto decmod;
llc_ui = kmalloc(sizeof(*llc_ui), GFP_KERNEL);
if (!llc_ui)
goto outsk;
memset(llc_ui, 0, sizeof(*llc_ui));
rc = 0;
sock_init_data(sock, sk);
llc_ui_sk(sk) = llc_ui;
sock->ops = &llc_ui_ops;
out:
return rc;
outsk:
sk_free(sk);
decmod:
MOD_DEC_USE_COUNT;
goto out;
}
/**
* llc_ui_release - shutdown socket
* @sock: Socket to release.
*
* Shutdown and deallocate an existing socket.
*/
static int llc_ui_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui;
if (!sk)
goto out;
llc_ui = llc_ui_sk(sk);
if (llc_ui->core_sk && !llc_ui_send_disc(sk))
llc_ui_wait_for_disc(sk, 255);
llc_ui_remove_socket(sk);
if (llc_ui->sap && !llc_ui_find_sap(llc_ui->sap->laddr.lsap))
llc_sap_close(llc_ui->sap);
dprintk("rxq=%d, txq=%d\n", skb_queue_len(&sk->receive_queue),
skb_queue_len(&sk->write_queue));
sock_orphan(sk);
sock->sk = NULL;
if (!atomic_read(&sk->wmem_alloc) &&
!atomic_read(&sk->rmem_alloc) && sk->dead)
llc_ui_destroy_sk(sk);
else {
init_timer(&sk->timer);
sk->timer.expires = jiffies + SOCK_DESTROY_TIME;
sk->timer.function = llc_ui_destroy_timer;
sk->timer.data = (unsigned long)sk;
add_timer(&sk->timer);
}
out:
return 0;
}
/**
* llc_ui_autoport - provide dynamicly allocate SAP number
*
* Provide the caller with a dynamicly allocated SAP number according
* to the rules that are set in this function. Returns: 0, upon failure,
* SAP number otherwise.
*/
static int llc_ui_autoport(void)
{
struct llc_sap *sap;
int i, tries = 0;
while (tries < LLC_SAP_DYN_TRIES) {
for (i = llc_ui_sap_last_autoport;
i < LLC_SAP_DYN_STOP; i += 2) {
sap = llc_ui_find_sap(i);
if (!sap) {
llc_ui_sap_last_autoport = i + 2;
goto out;
}
}
llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
tries++;
}
i = 0;
out:
return i;
}
/**
* llc_ui_autobind - Bind a socket to a specific address.
* @sk: Socket to bind an address to.
* @addr: Address the user wants the socket bound to.
*
* Bind a socket to a specific address. For llc a user is able to bind to
* a specific sap only or mac + sap. If the user only specifies a sap and
* a null dmac (all zeros) the user is attempting to bind to an entire
* sap. This will stop anyone else on the local system from using that
* sap. If someone else has a mac + sap open the bind to null + sap will
* fail.
* If the user desires to bind to a specific mac + sap, it is possible to
* have multiple sap connections via multiple macs.
* Bind and autobind for that matter must enforce the correct sap usage
* otherwise all hell will break loose.
* Returns: 0 upon success, negative otherwise.
*/
static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
struct llc_sap *sap;
struct net_device *dev = NULL;
int rc = -EINVAL;
if (!sk->zapped)
goto out;
/* bind to a specific mac, optional. */
if (!llc_ui_mac_null(addr->sllc_smac)) {
rtnl_lock();
dev = dev_getbyhwaddr(addr->sllc_arphrd, addr->sllc_smac);
rtnl_unlock();
rc = -ENETUNREACH;
if (!dev)
goto out;
llc_ui->dev = dev;
}
/* bind to a specific sap, optional. */
if (!addr->sllc_ssap) {
rc = -EUSERS;
addr->sllc_ssap = llc_ui_autoport();
if (!addr->sllc_ssap)
goto out;
}
sap = llc_ui_find_sap(addr->sllc_ssap);
if (!sap) {
sap = llc_sap_open(llc_ui_indicate, llc_ui_confirm,
addr->sllc_ssap);
rc = -EBUSY; /* some other network layer is using the sap */
if (!sap)
goto out;
} else {
struct llc_addr laddr, daddr;
struct sock *ask;
rc = -EUSERS; /* can't get exclusive use of sap */
if (!dev && llc_ui_mac_null(addr->sllc_mmac))
goto out;
memset(&laddr, 0, sizeof(laddr));
memset(&daddr, 0, sizeof(daddr));
if (!llc_ui_mac_null(addr->sllc_mmac)) {
if (sk->type != SOCK_DGRAM) {
rc = -EOPNOTSUPP;
goto out;
}
memcpy(laddr.mac, addr->sllc_mmac, IFHWADDRLEN);
} else
memcpy(laddr.mac, addr->sllc_smac, IFHWADDRLEN);
laddr.lsap = addr->sllc_ssap;
rc = -EADDRINUSE; /* mac + sap clash. */
ask = llc_ui_bh_find_sk_by_addr(&laddr, &daddr, dev);
if (ask) {
sock_put(ask);
goto out;
}
}
memcpy(&llc_ui->addr, addr, sizeof(*addr));
llc_ui->sap = sap;
rc = sk->zapped = 0;
llc_ui_insert_socket(sk);
out:
return rc;
}
/**
* llc_ui_bind - bind a socket to a specific address.
* @sock: Socket to bind an address to.
* @uaddr: Address the user wants the socket bound to.
* @addrlen: Length of the uaddr structure.
*
* Bind a socket to a specific address. For llc a user is able to bind to
* a specific sap only or mac + sap. If the user only specifies a sap and
* a null dmac (all zeros) the user is attempting to bind to an entire
* sap. This will stop anyone else on the local system from using that
* sap. If someone else has a mac + sap open the bind to null + sap will
* fail.
* If the user desires to bind to a specific mac + sap, it is possible to
* have multiple sap connections via multiple macs.
* Bind and autobind for that matter must enforce the correct sap usage
* otherwise all hell will break loose.
* Returns: 0 upon success, negative otherwise.
*/
static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
{
struct sockaddr_llc *addr = (struct sockaddr_llc *)uaddr;
struct sock *sk = sock->sk;
int rc = -EINVAL;
if (!sk->zapped || addrlen != sizeof(*addr))
goto out;
rc = -EAFNOSUPPORT;
if (addr->sllc_family != AF_LLC)
goto out;
/* use autobind, to avoid code replication. */
rc = llc_ui_autobind(sock, addr);
out:
return rc;
}
/**
* llc_ui_shutdown - shutdown a connect llc2 socket.
* @sock: Socket to shutdown.
* @how: What part of the socket to shutdown.
*
* Shutdown a connected llc2 socket. Currently this function only supports
* shutting down both sends and receives (2), we could probably make this
* function such that a user can shutdown only half the connection but not
* right now.
* Returns: 0 upon success, negative otherwise.
*/
static int llc_ui_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
int rc = -ENOTCONN;
lock_sock(sk);
if (sk->state != TCP_ESTABLISHED)
goto out;
rc = -EINVAL;
if (how != 2)
goto out;
rc = llc_ui_send_disc(sk);
if (!rc)
llc_ui_wait_for_disc(sk, 255);
/* Wake up anyone sleeping in poll */
sk->state_change(sk);
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_connect - Connect to a remote llc2 mac + sap.
* @sock: Socket which will be connected to the remote destination.
* @uaddr: Remote and possibly the local address of the new connection.
* @addrlen: Size of uaddr structure.
* @flags: Operational flags specified by the user.
*
* Connect to a remote llc2 mac + sap. The caller must specify the
* destination mac and address to connect to. If the user previously
* called bind(2) with a smac the user does not need to specify the source
* address and mac.
* This function will autobind if user did not previously call bind.
* Returns: 0 upon success, negative otherwise.
*/
static int llc_ui_connect(struct socket *sock, struct sockaddr *uaddr,
int addrlen, int flags)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
struct sockaddr_llc *addr = (struct sockaddr_llc *)uaddr;
struct net_device *dev;
int rc = -EINVAL;
lock_sock(sk);
if (addrlen != sizeof(*addr))
goto out;
rc = -EAFNOSUPPORT;
if (addr->sllc_family != AF_LLC)
goto out;
/* bind connection to sap if user hasn't done it. */
if (sk->zapped) {
/* bind to sap with null dev, exclusive */
rc = llc_ui_autobind(sock, addr);
if (rc)
goto out;
}
if (!llc_ui->dev) {
rtnl_lock();
dev = dev_getbyhwaddr(addr->sllc_arphrd, addr->sllc_smac);
rtnl_unlock();
if (!dev)
goto out;
} else
dev = llc_ui->dev;
if (sk->type != SOCK_STREAM)
goto out;
rc = -EALREADY;
if (sock->state == SS_CONNECTING)
goto out;
sock->state = SS_CONNECTING;
sk->state = TCP_SYN_SENT;
llc_ui->link = llc_ui_next_link_no(llc_ui->sap->laddr.lsap);
rc = llc_ui_send_conn(sk, llc_ui->sap, addr, dev, llc_ui->link);
if (rc) {
sock->state = SS_UNCONNECTED;
sk->state = TCP_CLOSE;
goto out;
}
rc = llc_ui_wait_for_conn(sk, 255);
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_listen - allow a normal socket to accept incoming connections
* @sock: Socket to allow incoming connections on.
* @backlog: Number of connections to queue.
*
* Allow a normal socket to accept incoming connections.
* Returns 0 upon success, negative otherwise.
*/
static int llc_ui_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
int rc = -EINVAL;
lock_sock(sk);
if (sock->state != SS_UNCONNECTED)
goto out;
rc = -EOPNOTSUPP;
if (sk->type != SOCK_STREAM && sk->type != SOCK_SEQPACKET)
goto out;
rc = -EAGAIN;
if (sk->zapped)
goto out;
rc = 0;
if (!(unsigned)backlog) /* BSDism */
backlog = 1;
if ((unsigned)backlog > SOMAXCONN)
backlog = SOMAXCONN;
sk->max_ack_backlog = backlog;
if (sk->state != TCP_LISTEN) {
sk->ack_backlog = 0;
sk->state = TCP_LISTEN;
}
sk->socket->flags |= __SO_ACCEPTCON;
out:
release_sock(sk);
return rc;
}
static int llc_ui_wait_for_disc(struct sock *sk, int seconds)
{
DECLARE_WAITQUEUE(wait, current);
int rc, timeout = seconds * HZ;
add_wait_queue_exclusive(sk->sleep, &wait);
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
rc = 0;
if (sk->state != TCP_CLOSE)
timeout = schedule_timeout(timeout);
else
break;
rc = -ERESTARTSYS;
if (signal_pending(current))
break;
rc = -EAGAIN;
if (!timeout)
break;
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk->sleep, &wait);
return rc;
}
static int llc_ui_wait_for_conn(struct sock *sk, int seconds)
{
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
DECLARE_WAITQUEUE(wait, current);
int rc, timeout = seconds * HZ;
add_wait_queue_exclusive(sk->sleep, &wait);
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
rc = 0;
if (sk->state != TCP_ESTABLISHED)
timeout = schedule_timeout(timeout);
if (sk->state == TCP_ESTABLISHED) {
if (!llc_ui->core_sk)
rc = -EAGAIN;
break;
}
rc = -EAGAIN;
if (sk->state == TCP_CLOSE)
break;
rc = -ERESTARTSYS;
if (signal_pending(current))
break;
rc = -EAGAIN;
if (!timeout)
break;
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk->sleep, &wait);
return rc;
}
/**
* llc_ui_accept - accept a new incoming connection.
* @sock: Socket which connections arrive on.
* @newsock: Socket to move incoming connection to.
* @flags: User specified operational flags.
*
* Accept a new incoming connection.
* Returns 0 upon success, negative otherwise.
*/
static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags)
{
struct sock *sk = sock->sk, *newsk;
struct llc_ui_opt *llc_ui, *newllc_ui;
struct llc_opt *newllc_core;
struct sk_buff *skb;
int rc = -EOPNOTSUPP;
lock_sock(sk);
if (sk->type != SOCK_SEQPACKET && sk->type != SOCK_STREAM)
goto out;
rc = -EINVAL;
if (sock->state != SS_UNCONNECTED || sk->state != TCP_LISTEN)
goto out;
/* wait for a connection to arrive. */
do {
skb = skb_dequeue(&sk->receive_queue);
if (!skb) {
rc = -EWOULDBLOCK;
if (flags & O_NONBLOCK)
goto out;
interruptible_sleep_on(sk->sleep);
rc = -ERESTARTSYS;
if (signal_pending(current))
goto out;
}
} while (!skb);
rc = -EINVAL;
if(!skb->sk)
goto frees;
/* attach connection to a new socket. */
rc = llc_ui_create(newsock, sk->protocol);
if (rc)
goto frees;
rc = 0;
newsk = newsock->sk;
newsk->pair = NULL;
newsk->socket = newsock;
newsk->sleep = &newsock->wait;
newsk->zapped = 0;
newsk->state = TCP_ESTABLISHED;
newsock->state = SS_CONNECTED;
llc_ui = llc_ui_sk(sk);
newllc_ui = llc_ui_sk(newsk);
newllc_ui->sap = llc_ui->sap;
newllc_ui->dev = llc_ui->dev;
newllc_ui->core_sk = skb->sk;
newllc_core = llc_sk(newllc_ui->core_sk);
newllc_ui->link = newllc_core->link;
newllc_core->handler = newsk;
memcpy(&newllc_ui->addr, &llc_ui->addr, sizeof(newllc_ui->addr));
memcpy(newllc_ui->addr.sllc_dmac, newllc_core->daddr.mac, IFHWADDRLEN);
newllc_ui->addr.sllc_dsap = newllc_core->daddr.lsap;
/* put original socket back into a clean listen state. */
sk->state = TCP_LISTEN;
sk->ack_backlog--;
llc_ui_insert_socket(newsk);
skb->sk = NULL;
frees:
kfree_skb(skb);
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_recvmsg - copy received data to the socket user.
* @sock: Socket to copy data from.
* @msg: Various user space related information.
* @size: Size of user buffer.
* @flags: User specified flags.
* @scm: Unknown.
*
* Copy received data to the socket user.
* Returns non-negative upon success, negative otherwise.
*/
static int llc_ui_recvmsg(struct socket *sock, struct msghdr *msg, int size,
int flags, struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
struct sockaddr_llc *uaddr = (struct sockaddr_llc *)msg->msg_name;
struct sk_buff *skb;
int rc = -ENOMEM, copied = 0;
int noblock = flags & MSG_DONTWAIT;
lock_sock(sk);
skb = skb_recv_datagram(sk, flags, noblock, &rc);
if (!skb)
goto out;
copied = skb->len;
if (copied > size) {
copied = size;
msg->msg_flags |= MSG_TRUNC;
}
rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (rc)
goto dgram_free;
if (uaddr)
memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));
msg->msg_namelen = sizeof(*uaddr);
dgram_free:
skb_free_datagram(sk, skb); /* Free the datagram. */
out:
release_sock(sk);
return rc ? : copied;
}
/**
* llc_ui_sendmsg - Transmit data provided by the socket user.
* @sock: Socket to transmit data from.
* @msg: Various user related information.
* @len: Length of data to transmit.
* @scm: Unknown.
*
* Transmit data provided by the socket user.
* Returns non-negative upon success, negative otherwise.
*/
static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, int len,
struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
struct sockaddr_llc *addr = (struct sockaddr_llc *)msg->msg_name;
int flags = msg->msg_flags;
struct net_device *dev;
struct sk_buff *skb;
int rc = -EOPNOTSUPP, size = 0;
lock_sock(sk);
if (flags & ~MSG_DONTWAIT)
goto release;
rc = -EINVAL;
if (addr) {
if (msg->msg_namelen < sizeof(*addr))
goto release;
} else {
if (llc_ui_addr_null(&llc_ui->addr))
goto release;
addr = &llc_ui->addr;
}
/* must bind connection to sap if user hasn't done it. */
if (sk->zapped) {
/* bind to sap with null dev, exclusive. */
rc = llc_ui_autobind(sock, addr);
if (rc)
goto release;
}
if (!llc_ui->dev) {
rtnl_lock();
dev = dev_getbyhwaddr(addr->sllc_arphrd, addr->sllc_smac);
rtnl_unlock();
rc = -ENETUNREACH;
if (!dev)
goto release;
} else
dev = llc_ui->dev;
size = dev->hard_header_len + len + llc_ui_header_len(sk, addr);
rc = -EMSGSIZE;
if (size > dev->mtu)
goto release;
skb = sock_alloc_send_skb(sk, size, flags & MSG_DONTWAIT, &rc);
if (!skb)
goto release;
skb->sk = sk;
skb->dev = dev;
skb_reserve(skb, dev->hard_header_len + llc_ui_header_len(sk, addr));
rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
if (rc)
goto release;
if (addr->sllc_test) {
rc = llc_ui_send_llc1(llc_ui->sap, skb, addr, LLC_TEST_PRIM);
goto out;
}
if (addr->sllc_xid) {
rc = llc_ui_send_llc1(llc_ui->sap, skb, addr, LLC_XID_PRIM);
goto out;
}
if (sk->type == SOCK_DGRAM || addr->sllc_ua) {
rc = llc_ui_send_llc1(llc_ui->sap, skb, addr, LLC_DATAUNIT_PRIM);
goto out;
}
rc = -ENOPROTOOPT;
if (!(sk->type == SOCK_STREAM && !addr->sllc_ua))
goto out;
rc = -ENOTCONN;
if (!llc_ui->core_sk)
goto out;
rc = llc_ui_send_data(llc_ui->sap, sk, skb, addr);
out:
if (rc)
skb_free_datagram(sk, skb);
release:
release_sock(sk);
return rc ? : len;
}
/**
* llc_ui_getname - return the address info of a socket
* @sock: Socket to get address of.
* @uaddr: Address structure to return information.
* @uaddrlen: Length of address structure.
* @peer: Does user want local or remote address information.
*
* Return the address information of a socket.
*/
static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddrlen, int peer)
{
struct sockaddr_llc sllc;
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
int rc = 0;
lock_sock(sk);
if (sk->zapped)
goto out;
*uaddrlen = sizeof(sllc);
memset(uaddr, 0, *uaddrlen);
if (peer) {
rc = -ENOTCONN;
if (sk->state != TCP_ESTABLISHED)
goto out;
if(llc_ui->dev)
sllc.sllc_arphrd = llc_ui->dev->type;
sllc.sllc_dsap = llc_sk(llc_ui->core_sk)->daddr.lsap;
memcpy(&sllc.sllc_dmac, &llc_sk(llc_ui->core_sk)->daddr.mac,
IFHWADDRLEN);
} else {
rc = -EINVAL;
if (!llc_ui->sap)
goto out;
sllc.sllc_ssap = llc_ui->sap->laddr.lsap;
if (llc_ui->dev) {
sllc.sllc_arphrd = llc_ui->dev->type;
memcpy(&sllc.sllc_smac, &llc_ui->dev->dev_addr,
IFHWADDRLEN);
}
}
rc = 0;
sllc.sllc_family = AF_LLC;
memcpy(uaddr, &sllc, sizeof(sllc));
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_ioctl - io controls for PF_LLC
* @sock: Socket to get/set info
* @cmd: command
* @arg: optional argument for cmd
*
* get/set info on llc sockets
*/
static int llc_ui_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
return dev_ioctl(cmd, (void *)arg);
}
/**
* llc_ui_setsockopt - set various connection specific parameters.
* @sock: Socket to set options on.
* @level: Socket level user is requesting operations on.
* @optname: Operation name.
* @optval User provided operation data.
* @optlen: Length of optval.
*
* Set various connection specific parameters.
*/
static int llc_ui_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
struct llc_opt *llc_core;
int rc = -EINVAL, opt;
lock_sock(sk);
if (level != SOL_LLC || optlen != sizeof(int))
goto out;
rc = -ENOTCONN;
if (!llc_ui->core_sk)
goto out;
rc = get_user(opt, (int *)optval);
if (rc)
goto out;
rc = -EINVAL;
llc_core = llc_sk(llc_ui->core_sk);
switch (optname) {
case LLC_OPT_RETRY:
if (opt > LLC_OPT_MAX_RETRY)
goto out;
llc_core->n2 = opt;
break;
case LLC_OPT_SIZE:
if (opt > LLC_OPT_MAX_SIZE)
goto out;
llc_core->n1 = opt;
break;
case LLC_OPT_ACK_TMR_EXP:
if (opt > LLC_OPT_MAX_ACK_TMR_EXP)
goto out;
llc_core->ack_timer.expire = opt;
break;
case LLC_OPT_P_TMR_EXP:
if (opt > LLC_OPT_MAX_P_TMR_EXP)
goto out;
llc_core->pf_cycle_timer.expire = opt;
break;
case LLC_OPT_REJ_TMR_EXP:
if (opt > LLC_OPT_MAX_REJ_TMR_EXP)
goto out;
llc_core->rej_sent_timer.expire = opt;
break;
case LLC_OPT_BUSY_TMR_EXP:
if (opt > LLC_OPT_MAX_BUSY_TMR_EXP)
goto out;
llc_core->busy_state_timer.expire = opt;
break;
case LLC_OPT_TX_WIN:
if (opt > LLC_OPT_MAX_WIN)
goto out;
llc_core->k = opt;
break;
case LLC_OPT_RX_WIN:
if (opt > LLC_OPT_MAX_WIN)
goto out;
llc_core->rw = opt;
break;
default:
rc = -ENOPROTOOPT;
goto out;
}
rc = 0;
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_getsockopt - get connection specific socket info
* @sock: Socket to get information from.
* @level: Socket level user is requesting operations on.
* @optname: Operation name.
* @optval: Variable to return operation data in.
* @optlen: Length of optval.
*
* Get connection specific socket information.
*/
static int llc_ui_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
struct llc_opt *llc_core;
int val = 0, len = 0, rc = -EINVAL;
lock_sock(sk);
if (level != SOL_LLC)
goto out;
rc = -ENOTCONN;
if (!llc_ui->core_sk)
goto out;
rc = get_user(len, optlen);
if (rc)
goto out;
rc = -EINVAL;
if (len != sizeof(int))
goto out;
llc_core = llc_sk(llc_ui->core_sk);
switch (optname) {
case LLC_OPT_RETRY:
val = llc_core->n2; break;
case LLC_OPT_SIZE:
val = llc_core->n1; break;
case LLC_OPT_ACK_TMR_EXP:
val = llc_core->ack_timer.expire; break;
case LLC_OPT_P_TMR_EXP:
val = llc_core->pf_cycle_timer.expire; break;
case LLC_OPT_REJ_TMR_EXP:
val = llc_core->rej_sent_timer.expire; break;
case LLC_OPT_BUSY_TMR_EXP:
val = llc_core->busy_state_timer.expire; break;
case LLC_OPT_TX_WIN:
val = llc_core->k; break;
case LLC_OPT_RX_WIN:
val = llc_core->rw; break;
default:
rc = -ENOPROTOOPT;
goto out;
}
rc = 0;
if (put_user(len, optlen) || copy_to_user(optval, &val, len))
rc = -EFAULT;
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_ind_test - handle TEST indication
* @prim: Primitive block provided by the llc layer.
*
* handle TEST indication.
*/
static void llc_ui_ind_test(struct llc_prim_if_block *prim)
{
struct llc_prim_test *prim_data = &prim->data->test;
struct sk_buff *skb = prim_data->skb;
struct sockaddr_llc *llc_ui = llc_ui_skb_cb(skb);
struct sock *sk = llc_ui_find_sk_by_addr(&prim_data->daddr,
&prim_data->saddr, skb->dev);
if (!sk)
goto out;
if (sk->state == TCP_LISTEN)
goto out_put;
/* save primitive for use by the user. */
llc_ui->sllc_family = AF_LLC;
llc_ui->sllc_arphrd = skb->dev->type;
llc_ui->sllc_test = 1;
llc_ui->sllc_xid = 0;
llc_ui->sllc_ua = 0;
llc_ui->sllc_dsap = prim_data->daddr.lsap;
memcpy(llc_ui->sllc_dmac, prim_data->daddr.mac, IFHWADDRLEN);
llc_ui->sllc_ssap = prim_data->saddr.lsap;
memcpy(llc_ui->sllc_smac, prim_data->saddr.mac, IFHWADDRLEN);
/* queue skb to the user. */
if (sock_queue_rcv_skb(sk, skb))
kfree_skb(skb);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_ind_xid - handle XID indication
* @prim: Primitive block provided by the llc layer.
*
* handle XID indication.
*/
static void llc_ui_ind_xid(struct llc_prim_if_block *prim)
{
struct llc_prim_xid *prim_data = &prim->data->xid;
struct sk_buff *skb = prim_data->skb;
struct sockaddr_llc *llc_ui = llc_ui_skb_cb(skb);
struct sock *sk = llc_ui_find_sk_by_addr(&prim_data->daddr,
&prim_data->saddr, skb->dev);
if (!sk)
goto out;
if (sk->state == TCP_LISTEN)
goto out_put;
/* save primitive for use by the user. */
llc_ui->sllc_family = AF_LLC;
llc_ui->sllc_arphrd = 0;
llc_ui->sllc_test = 0;
llc_ui->sllc_xid = 1;
llc_ui->sllc_ua = 0;
llc_ui->sllc_dsap = prim_data->daddr.lsap;
memcpy(llc_ui->sllc_dmac, prim_data->daddr.mac, IFHWADDRLEN);
llc_ui->sllc_ssap = prim_data->saddr.lsap;
memcpy(llc_ui->sllc_smac, prim_data->saddr.mac, IFHWADDRLEN);
/* queue skb to the user. */
if (sock_queue_rcv_skb(sk, skb))
kfree_skb(skb);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_ind_dataunit - handle DATAUNIT indication
* @prim: Primitive block provided by the llc layer.
*
* handle DATAUNIT indication.
*/
static void llc_ui_ind_dataunit(struct llc_prim_if_block *prim)
{
struct llc_prim_unit_data *prim_data = &prim->data->udata;
struct sk_buff *skb = prim_data->skb;
struct sockaddr_llc *llc_ui = llc_ui_skb_cb(skb);
struct sock *sk = llc_ui_find_sk_by_addr(&prim_data->daddr,
&prim_data->saddr, skb->dev);
if (!sk)
goto out;
if (sk->state == TCP_LISTEN)
goto out_put;
/* save primitive for use by the user. */
llc_ui->sllc_family = AF_LLC;
llc_ui->sllc_arphrd = skb->dev->type;
llc_ui->sllc_test = 0;
llc_ui->sllc_xid = 0;
llc_ui->sllc_ua = 1;
llc_ui->sllc_dsap = prim_data->daddr.lsap;
memcpy(llc_ui->sllc_dmac, prim_data->daddr.mac, IFHWADDRLEN);
llc_ui->sllc_ssap = prim_data->saddr.lsap;
memcpy(llc_ui->sllc_smac, prim_data->saddr.mac, IFHWADDRLEN);
/* queue skb to the user. */
if (sock_queue_rcv_skb(sk, skb))
kfree_skb(skb);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_ind_conn - handle CONNECT indication
* @prim: Primitive block provided by the llc layer.
*
* handle CONNECT indication.
*/
static void llc_ui_ind_conn(struct llc_prim_if_block *prim)
{
struct llc_prim_conn *prim_data = &prim->data->conn;
struct sock* sk;
struct sk_buff *skb2;
llc_sk(prim_data->sk)->laddr.lsap = prim->sap->laddr.lsap;
sk = llc_ui_find_sk_by_addr(&llc_sk(prim_data->sk)->laddr,
&prim_data->saddr, prim_data->dev);
if (!sk) {
dprintk("llc_ui_find_sk_by_addr failed\n");
goto out;
}
if (sk->type != SOCK_STREAM || sk->state != TCP_LISTEN)
goto out_put;
if (prim->data->conn.status)
goto out_put; /* bad status. */
/* give this connection a link number. */
llc_sk(prim_data->sk)->link =
llc_ui_next_link_no(llc_sk(prim_data->sk)->laddr.lsap);
skb2 = alloc_skb(0, GFP_ATOMIC);
if (!skb2)
goto out_put;
skb2->sk = prim_data->sk;
skb_queue_tail(&sk->receive_queue, skb2);
sk->state_change(sk);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_ind_data - handle DATA indication
* @prim: Primitive block provided by the llc layer.
*
* handle CONNECT indication.
*/
static void llc_ui_ind_data(struct llc_prim_if_block *prim)
{
struct llc_prim_data *prim_data = &prim->data->data;
struct sk_buff *skb = prim_data->skb;
struct sockaddr_llc *llc_ui = llc_ui_skb_cb(skb);
struct sock* sk = llc_sk(prim_data->sk)->handler;
if (!sk)
goto out;
sock_hold(sk);
if (sk->type != SOCK_STREAM || sk->state != TCP_ESTABLISHED)
goto out_put;
/* save primitive for use by the user. */
llc_ui->sllc_family = AF_LLC;
llc_ui->sllc_arphrd = skb->dev->type;
llc_ui->sllc_test = 0;
llc_ui->sllc_xid = 0;
llc_ui->sllc_ua = 0;
llc_ui->sllc_dsap = llc_ui_sk(sk)->sap->laddr.lsap;
memcpy(llc_ui->sllc_dmac, llc_sk(prim_data->sk)->laddr.mac,
IFHWADDRLEN);
llc_ui->sllc_ssap = llc_sk(prim_data->sk)->daddr.lsap;
memcpy(llc_ui->sllc_smac, llc_sk(prim_data->sk)->daddr.mac,
IFHWADDRLEN);
/* queue skb to the user. */
if (sock_queue_rcv_skb(sk, skb)) {
dprintk("sock_queue_rcv_skb failed!\n");
kfree_skb(skb);
}
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_ind_disc - handle DISC indication
* @prim: Primitive block provided by the llc layer.
*
* handle DISC indication.
*/
static void llc_ui_ind_disc(struct llc_prim_if_block *prim)
{
struct llc_prim_disc *prim_data = &prim->data->disc;
struct sock* sk = llc_sk(prim_data->sk)->handler;
if (!sk)
goto out;
sock_hold(sk);
if (sk->type != SOCK_STREAM || sk->state != TCP_ESTABLISHED)
goto out_put;
llc_ui_sk(sk)->core_sk = NULL;
sk->shutdown = SHUTDOWN_MASK;
sk->socket->state = SS_UNCONNECTED;
sk->state = TCP_CLOSE;
if (!sk->dead) {
sk->state_change(sk);
sk->dead = 1;
}
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_indicate - LLC user interface hook into the LLC layer.
* @prim: Primitive block provided by the llc layer.
*
* LLC user interface hook into the LLC layer, every llc_ui sap references
* this function as its indicate handler.
* Always returns 0 to indicate reception of primitive.
*/
static int llc_ui_indicate(struct llc_prim_if_block *prim)
{
switch (prim->prim) {
case LLC_TEST_PRIM:
llc_ui_ind_test(prim); break;
case LLC_XID_PRIM:
llc_ui_ind_xid(prim); break;
case LLC_DATAUNIT_PRIM:
llc_ui_ind_dataunit(prim); break;
case LLC_CONN_PRIM:
llc_ui_ind_conn(prim); break;
case LLC_DATA_PRIM:
llc_ui_ind_data(prim); break;
case LLC_DISC_PRIM:
llc_ui_ind_disc(prim); break;
case LLC_RESET_PRIM:
case LLC_FLOWCONTROL_PRIM:
default: break;
}
return 0;
}
/**
* llc_ui_conf_conn - handle CONN confirm.
* @prim: Primitive block provided by the llc layer.
*
* handle CONN confirm.
*/
static void llc_ui_conf_conn(struct llc_prim_if_block *prim)
{
struct llc_prim_conn *prim_data = &prim->data->conn;
struct llc_opt *llc_core = llc_sk(prim_data->sk);
struct llc_ui_opt *llc_ui = llc_ui_sk(prim_data->sk);
struct sock* sk = llc_core->handler;
if (!sk) {
dprintk("llc_core->handler == NULL!\n");
goto out;
}
sock_hold(sk);
if (sk->type != SOCK_STREAM || sk->state != TCP_SYN_SENT)
goto out_put;
if (!prim->data->conn.status) {
sk->socket->state = SS_CONNECTED;
sk->state = TCP_ESTABLISHED;
llc_ui->core_sk = prim_data->sk;
} else {
dprintk("prim->data->conn.status = %d\n",
prim->data->conn.status);
sk->socket->state = SS_UNCONNECTED;
sk->state = TCP_CLOSE;
llc_ui->core_sk = NULL;
}
sk->state_change(sk);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_conf_data - handle DATA confirm.
* @prim: Primitive block provided by the llc layer.
*
* handle DATA confirm.
*/
static void llc_ui_conf_data(struct llc_prim_if_block *prim)
{
struct llc_prim_data *prim_data = &prim->data->data;
struct sock* sk = llc_sk(prim_data->sk)->handler;
if (sk)
wake_up(sk->sleep);
}
/**
* llc_ui_conf_disc - handle DISC confirm.
* @prim: Primitive block provided by the llc layer.
*
* handle DISC confirm.
*/
static void llc_ui_conf_disc(struct llc_prim_if_block *prim)
{
struct llc_prim_disc *prim_data = &prim->data->disc;
struct sock* sk = llc_sk(prim_data->sk)->handler;
if (!sk)
goto out;
sock_hold(sk);
if (sk->type != SOCK_STREAM || sk->state != TCP_CLOSING)
goto out_put;
llc_ui_sk(sk)->core_sk = NULL;
sk->socket->state = SS_UNCONNECTED;
sk->state = TCP_CLOSE;
sk->state_change(sk);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_confirm - LLC user interface hook into the LLC layer
* @prim: Primitive block provided by the llc layer.
*
* LLC user interface hook into the LLC layer, every llc_ui sap references
* this function as its confirm handler.
* Always returns 0 to indicate reception of primitive.
*/
static int llc_ui_confirm(struct llc_prim_if_block *prim)
{
switch (prim->prim) {
case LLC_CONN_PRIM:
llc_ui_conf_conn(prim); break;
case LLC_DATA_PRIM:
llc_ui_conf_data(prim); break;
case LLC_DISC_PRIM:
llc_ui_conf_disc(prim); break;
case LLC_RESET_PRIM: break;
default:
printk(KERN_ERR __FUNCTION__ ": unknown prim %d\n",
prim->prim);
break;
}
return 0;
}
#ifdef CONFIG_PROC_FS
/**
* llc_ui_get_info - return info to procfs
* @buffer: where to put the formatted output
* @start: starting from
* @offset: offset into buffer.
* @length: size of the buffer
*
* Get the output of the local llc ui socket list to the caller.
* Returns the length of data wrote to buffer.
*/
static int llc_ui_get_info(char *buffer, char **start, off_t offset, int length)
{
off_t pos = 0;
off_t begin = 0;
struct sock *s;
int len = sprintf(buffer, "SocketID SKt Mc local_mac_sap\t "
"remote_mac_sap\t tx_queue rx_queue st uid "
"link_no\n");
/* Output the LLC socket data for the /proc filesystem */
read_lock_bh(&llc_ui_sockets_lock);
for (s = llc_ui_sockets; s; s = s->next) {
struct llc_ui_opt *llc_ui = llc_ui_sk(s);
len += sprintf(buffer + len, "%p %02X %02X ", s, s->type,
!llc_ui_mac_null(llc_ui->addr.sllc_mmac));
if (llc_ui->sap) {
if (llc_ui->dev &&
llc_ui_mac_null(llc_ui->addr.sllc_mmac))
len += sprintf(buffer + len,
"%02X:%02X:%02X:%02X:%02X:%02X",
llc_ui->dev->dev_addr[0],
llc_ui->dev->dev_addr[1],
llc_ui->dev->dev_addr[2],
llc_ui->dev->dev_addr[3],
llc_ui->dev->dev_addr[4],
llc_ui->dev->dev_addr[5]);
else {
if (!llc_ui_mac_null(llc_ui->addr.sllc_mmac))
len += sprintf(buffer + len,
"%02X:%02X:%02X:%02X:%02X:%02X",
llc_ui->addr.sllc_mmac[0],
llc_ui->addr.sllc_mmac[1],
llc_ui->addr.sllc_mmac[2],
llc_ui->addr.sllc_mmac[3],
llc_ui->addr.sllc_mmac[4],
llc_ui->addr.sllc_mmac[5]);
else
len += sprintf(buffer + len,
"00:00:00:00:00:00");
}
len += sprintf(buffer + len, "@%02X ",
llc_ui->sap->laddr.lsap);
} else
len += sprintf(buffer + len, "00:00:00:00:00:00@00 ");
len += sprintf(buffer + len,
"%02X:%02X:%02X:%02X:%02X:%02X@%02X "
"%08X:%08X %02X %-3d ",
llc_ui->addr.sllc_dmac[0],
llc_ui->addr.sllc_dmac[1],
llc_ui->addr.sllc_dmac[2],
llc_ui->addr.sllc_dmac[3],
llc_ui->addr.sllc_dmac[4],
llc_ui->addr.sllc_dmac[5],
llc_ui->addr.sllc_dsap,
atomic_read(&s->wmem_alloc),
atomic_read(&s->rmem_alloc), s->state,
SOCK_INODE(s->socket)->i_uid);
if (llc_ui->core_sk)
len += sprintf(buffer + len, "%-7d\n",
llc_sk(llc_ui->core_sk)->link);
else
len += sprintf(buffer + len, "no_link\n");
/* Are we still dumping unwanted data then discard the record */
pos = begin + len;
if (pos < offset) {
len = 0; /* Keep dumping into the buffer start */
begin = pos;
}
if (pos > offset + length) /* We have dumped enough */
break;
}
read_unlock_bh(&llc_ui_sockets_lock);
/* The data in question runs from begin to begin + len */
*start = buffer + offset - begin; /* Start of wanted data */
len -= offset - begin; /* Remove unwanted header data from length */
if (len > length)
len = length; /* Remove unwanted tail data from length */
return len;
}
#endif /* CONFIG_PROC_FS */
static struct net_proto_family llc_ui_family_ops = {
.family = PF_LLC,
.create = llc_ui_create,
};
static struct proto_ops SOCKOPS_WRAPPED(llc_ui_ops) = {
.family = PF_LLC,
.release = llc_ui_release,
.bind = llc_ui_bind,
.connect = llc_ui_connect,
.socketpair = sock_no_socketpair,
.accept = llc_ui_accept,
.getname = llc_ui_getname,
.poll = datagram_poll,
.ioctl = llc_ui_ioctl,
.listen = llc_ui_listen,
.shutdown = llc_ui_shutdown,
.setsockopt = llc_ui_setsockopt,
.getsockopt = llc_ui_getsockopt,
.sendmsg = llc_ui_sendmsg,
.recvmsg = llc_ui_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
#include <linux/smp_lock.h>
SOCKOPS_WRAP(llc_ui, PF_LLC);
static char llc_ui_banner[] __initdata =
KERN_INFO "NET4.0 IEEE 802.2 User Interface SAPs, Jay Schulist, 2001\n";
int __init llc_ui_init(void)
{
llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
sock_register(&llc_ui_family_ops);
proc_net_create("llc", 0, llc_ui_get_info);
printk(llc_ui_banner);
return 0;
}
void __exit llc_ui_exit(void)
{
proc_net_remove("llc");
sock_unregister(PF_LLC);
}
/*
* llc_stat.c - Implementation of LLC station component state machine
* transitions
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 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_sap.h>
#include <net/llc_evnt.h>
#include <net/llc_actn.h>
#include <net/llc_stat.h>
/* COMMON STATION STATE transitions */
/* dummy last-transition indicator; common to all state transition groups
* last entry for this state
* all members are zeros, .bss zeroes it
*/
static struct llc_station_state_trans llc_stat_state_trans_n;
/* DOWN STATE transitions */
/* state transition for LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK event */
static llc_station_action_t llc_stat_down_state_actions_1[] = {
[0] = llc_station_ac_start_ack_timer,
[1] = llc_station_ac_set_retry_cnt_0,
[2] = llc_station_ac_set_xid_r_cnt_0,
[3] = llc_station_ac_send_null_dsap_xid_c,
[4] = NULL,
};
static struct llc_station_state_trans llc_stat_down_state_trans_1 = {
.ev = llc_stat_ev_enable_with_dup_addr_check,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_down_state_actions_1,
};
/* state transition for LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK event */
static llc_station_action_t llc_stat_down_state_actions_2[] = {
[0] = llc_station_ac_report_status, /* STATION UP */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_down_state_trans_2 = {
.ev = llc_stat_ev_enable_without_dup_addr_check,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_down_state_actions_2,
};
/* array of pointers; one to each transition */
static struct llc_station_state_trans *llc_stat_dwn_state_trans[] = {
[0] = &llc_stat_down_state_trans_1,
[1] = &llc_stat_down_state_trans_2,
[2] = &llc_stat_state_trans_n,
};
/* UP STATE transitions */
/* state transition for LLC_STATION_EV_DISABLE_REQ event */
static llc_station_action_t llc_stat_up_state_actions_1[] = {
[0] = llc_station_ac_report_status, /* STATION DOWN */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_up_state_trans_1 = {
.ev = llc_stat_ev_disable_req,
.next_state = LLC_STATION_STATE_DOWN,
.ev_actions = llc_stat_up_state_actions_1,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */
static llc_station_action_t llc_stat_up_state_actions_2[] = {
[0] = llc_station_ac_send_xid_r,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_up_state_trans_2 = {
.ev = llc_stat_ev_rx_null_dsap_xid_c,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_up_state_actions_2,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */
static llc_station_action_t llc_stat_up_state_actions_3[] = {
[0] = llc_station_ac_send_test_r,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_up_state_trans_3 = {
.ev = llc_stat_ev_rx_null_dsap_test_c,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_up_state_actions_3,
};
/* array of pointers; one to each transition */
static struct llc_station_state_trans *llc_stat_up_state_trans [] = {
[0] = &llc_stat_up_state_trans_1,
[1] = &llc_stat_up_state_trans_2,
[2] = &llc_stat_up_state_trans_3,
[3] = &llc_stat_state_trans_n,
};
/* DUP ADDR CHK STATE transitions */
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_1[] = {
[0] = llc_station_ac_inc_xid_r_cnt_by_1,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_1 = {
.ev = llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_dupaddr_state_actions_1,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_2[] = {
[0] = llc_station_ac_report_status, /* DUPLICATE ADDRESS FOUND */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_2 = {
.ev = llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq,
.next_state = LLC_STATION_STATE_DOWN,
.ev_actions = llc_stat_dupaddr_state_actions_2,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */
static llc_station_action_t llc_stat_dupaddr_state_actions_3[] = {
[0] = llc_station_ac_send_xid_r,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_3 = {
.ev = llc_stat_ev_rx_null_dsap_xid_c,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_dupaddr_state_actions_3,
};
/* state transition for LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_4[] = {
[0] = llc_station_ac_start_ack_timer,
[1] = llc_station_ac_inc_retry_cnt_by_1,
[2] = llc_station_ac_set_xid_r_cnt_0,
[3] = llc_station_ac_send_null_dsap_xid_c,
[4] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_4 = {
.ev = llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_dupaddr_state_actions_4
};
/* state transition for LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_5[] = {
[0] = llc_station_ac_report_status, /* STATION UP */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_5 = {
.ev = llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_dupaddr_state_actions_5,
};
/* state transition for LLC_STATION_EV_DISABLE_REQ event */
static llc_station_action_t llc_stat_dupaddr_state_actions_6[] = {
[0] = llc_station_ac_report_status, /* STATION DOWN */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_6 = {
.ev = llc_stat_ev_disable_req,
.next_state = LLC_STATION_STATE_DOWN,
.ev_actions = llc_stat_dupaddr_state_actions_6,
};
/* array of pointers; one to each transition */
static struct llc_station_state_trans *llc_stat_dupaddr_state_trans[] = {
[0] = &llc_stat_dupaddr_state_trans_6, /* Request */
[1] = &llc_stat_dupaddr_state_trans_4, /* Timer */
[2] = &llc_stat_dupaddr_state_trans_5,
[3] = &llc_stat_dupaddr_state_trans_1, /* Receive frame */
[4] = &llc_stat_dupaddr_state_trans_2,
[5] = &llc_stat_dupaddr_state_trans_3,
[6] = &llc_stat_state_trans_n
};
struct llc_station_state llc_station_state_table[LLC_NBR_STATION_STATES] = {
{
.curr_state = LLC_STATION_STATE_DOWN,
.transitions = llc_stat_dwn_state_trans,
},
{
.curr_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.transitions = llc_stat_dupaddr_state_trans,
},
{
.curr_state = LLC_STATION_STATE_UP,
.transitions = llc_stat_up_state_trans,
}
};
...@@ -444,6 +444,7 @@ EXPORT_SYMBOL(arp_find); ...@@ -444,6 +444,7 @@ EXPORT_SYMBOL(arp_find);
#endif /* CONFIG_INET */ #endif /* CONFIG_INET */
#ifdef CONFIG_TR #ifdef CONFIG_TR
EXPORT_SYMBOL(tr_source_route);
EXPORT_SYMBOL(tr_type_trans); EXPORT_SYMBOL(tr_type_trans);
#endif #endif
...@@ -462,6 +463,7 @@ EXPORT_SYMBOL(dev_get_by_index); ...@@ -462,6 +463,7 @@ EXPORT_SYMBOL(dev_get_by_index);
EXPORT_SYMBOL(__dev_get_by_index); EXPORT_SYMBOL(__dev_get_by_index);
EXPORT_SYMBOL(dev_get_by_name); EXPORT_SYMBOL(dev_get_by_name);
EXPORT_SYMBOL(__dev_get_by_name); EXPORT_SYMBOL(__dev_get_by_name);
EXPORT_SYMBOL(dev_getbyhwaddr);
EXPORT_SYMBOL(netdev_finish_unregister); EXPORT_SYMBOL(netdev_finish_unregister);
EXPORT_SYMBOL(netdev_set_master); EXPORT_SYMBOL(netdev_set_master);
EXPORT_SYMBOL(eth_type_trans); EXPORT_SYMBOL(eth_type_trans);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment