⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 llc_conn.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * llc_conn.c - Driver routines for connection component. * * Copyright (c) 1997 by Procom Technology, Inc. *		 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br> * * This program can be redistributed or modified under the terms of the * GNU General Public License as published by the Free Software Foundation. * This program is distributed without any warranty or implied warranty * of merchantability or fitness for a particular purpose. * * See the GNU General Public License for more details. */#include <linux/init.h>#include <net/llc_sap.h>#include <net/llc_conn.h>#include <net/sock.h>#include <net/tcp_states.h>#include <net/llc_c_ev.h>#include <net/llc_c_ac.h>#include <net/llc_c_st.h>#include <net/llc_pdu.h>#if 0#define dprintk(args...) printk(KERN_DEBUG args)#else#define dprintk(args...)#endifstatic int llc_find_offset(int state, int ev_type);static void llc_conn_send_pdus(struct sock *sk);static int llc_conn_service(struct sock *sk, struct sk_buff *skb);static int llc_exec_conn_trans_actions(struct sock *sk,				       struct llc_conn_state_trans *trans,				       struct sk_buff *ev);static struct llc_conn_state_trans *llc_qualify_conn_ev(struct sock *sk,							struct sk_buff *skb);/* Offset table on connection states transition diagram */static int llc_offset_table[NBR_CONN_STATES][NBR_CONN_EV];int sysctl_llc2_ack_timeout = LLC2_ACK_TIME * HZ;int sysctl_llc2_p_timeout = LLC2_P_TIME * HZ;int sysctl_llc2_rej_timeout = LLC2_REJ_TIME * HZ;int sysctl_llc2_busy_timeout = LLC2_BUSY_TIME * HZ;/** *	llc_conn_state_process - sends event to connection state machine *	@sk: connection *	@skb: occurred event * *	Sends an event to connection state machine. After processing event *	(executing it's actions and changing state), upper layer will be *	indicated or confirmed, if needed. Returns 0 for success, 1 for *	failure. The socket lock has to be held before calling this function. */int llc_conn_state_process(struct sock *sk, struct sk_buff *skb){	int rc;	struct llc_sock *llc = llc_sk(skb->sk);	struct llc_conn_state_ev *ev = llc_conn_ev(skb);	/*	 * We have to hold the skb, because llc_conn_service will kfree it in	 * the sending path and we need to look at the skb->cb, where we encode	 * llc_conn_state_ev.	 */	skb_get(skb);	ev->ind_prim = ev->cfm_prim = 0;	/*	 * Send event to state machine	 */	rc = llc_conn_service(skb->sk, skb);	if (unlikely(rc != 0)) {		printk(KERN_ERR "%s: llc_conn_service failed\n", __FUNCTION__);		goto out_kfree_skb;	}	if (unlikely(!ev->ind_prim && !ev->cfm_prim)) {		/* indicate or confirm not required */		/* XXX this is not very pretty, perhaps we should store		 * XXX indicate/confirm-needed state in the llc_conn_state_ev		 * XXX control block of the SKB instead? -DaveM		 */		if (!skb->next)			goto out_kfree_skb;		goto out_skb_put;	}	if (unlikely(ev->ind_prim && ev->cfm_prim)) /* Paranoia */		skb_get(skb);	switch (ev->ind_prim) {	case LLC_DATA_PRIM:		llc_save_primitive(sk, skb, LLC_DATA_PRIM);		if (unlikely(sock_queue_rcv_skb(sk, skb))) {			/*			 * shouldn't happen			 */			printk(KERN_ERR "%s: sock_queue_rcv_skb failed!\n",			       __FUNCTION__);			kfree_skb(skb);		}		break;	case LLC_CONN_PRIM:		/*		 * Can't be sock_queue_rcv_skb, because we have to leave the		 * skb->sk pointing to the newly created struct sock in		 * llc_conn_handler. -acme		 */		skb_queue_tail(&sk->sk_receive_queue, skb);		sk->sk_state_change(sk);		break;	case LLC_DISC_PRIM:		sock_hold(sk);		if (sk->sk_type == SOCK_STREAM &&		    sk->sk_state == TCP_ESTABLISHED) {			sk->sk_shutdown       = SHUTDOWN_MASK;			sk->sk_socket->state  = SS_UNCONNECTED;			sk->sk_state          = TCP_CLOSE;			if (!sock_flag(sk, SOCK_DEAD)) {				sock_set_flag(sk, SOCK_DEAD);				sk->sk_state_change(sk);			}		}		kfree_skb(skb);		sock_put(sk);		break;	case LLC_RESET_PRIM:		/*		 * FIXME:		 * RESET is not being notified to upper layers for now		 */		printk(KERN_INFO "%s: received a reset ind!\n", __FUNCTION__);		kfree_skb(skb);		break;	default:		if (ev->ind_prim) {			printk(KERN_INFO "%s: received unknown %d prim!\n",				__FUNCTION__, ev->ind_prim);			kfree_skb(skb);		}		/* No indication */		break;	}	switch (ev->cfm_prim) {	case LLC_DATA_PRIM:		if (!llc_data_accept_state(llc->state))			sk->sk_write_space(sk);		else			rc = llc->failed_data_req = 1;		break;	case LLC_CONN_PRIM:		if (sk->sk_type == SOCK_STREAM &&		    sk->sk_state == TCP_SYN_SENT) {			if (ev->status) {				sk->sk_socket->state = SS_UNCONNECTED;				sk->sk_state         = TCP_CLOSE;			} else {				sk->sk_socket->state = SS_CONNECTED;				sk->sk_state         = TCP_ESTABLISHED;			}			sk->sk_state_change(sk);		}		break;	case LLC_DISC_PRIM:		sock_hold(sk);		if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSING) {			sk->sk_socket->state = SS_UNCONNECTED;			sk->sk_state         = TCP_CLOSE;			sk->sk_state_change(sk);		}		sock_put(sk);		break;	case LLC_RESET_PRIM:		/*		 * FIXME:		 * RESET is not being notified to upper layers for now		 */		printk(KERN_INFO "%s: received a reset conf!\n", __FUNCTION__);		break;	default:		if (ev->cfm_prim) {			printk(KERN_INFO "%s: received unknown %d prim!\n",					__FUNCTION__, ev->cfm_prim);			break;		}		goto out_skb_put; /* No confirmation */	}out_kfree_skb:	kfree_skb(skb);out_skb_put:	kfree_skb(skb);	return rc;}void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb){	/* queue PDU to send to MAC layer */	skb_queue_tail(&sk->sk_write_queue, skb);	llc_conn_send_pdus(sk);}/** *	llc_conn_rtn_pdu - sends received data pdu to upper layer *	@sk: Active connection *	@skb: Received data frame * *	Sends received data pdu to upper layer (by using indicate function). *	Prepares service parameters (prim and prim_data). calling indication *	function will be done in llc_conn_state_process. */void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb){	struct llc_conn_state_ev *ev = llc_conn_ev(skb);	ev->ind_prim = LLC_DATA_PRIM;}/** *	llc_conn_resend_i_pdu_as_cmd - resend all all unacknowledged I PDUs *	@sk: active connection *	@nr: NR *	@first_p_bit: p_bit value of first pdu * *	Resend all unacknowledged I PDUs, starting with the NR; send first as *	command PDU with P bit equal first_p_bit; if more than one send *	subsequent as command PDUs with P bit equal zero (0). */void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit){	struct sk_buff *skb;	struct llc_pdu_sn *pdu;	u16 nbr_unack_pdus;	struct llc_sock *llc;	u8 howmany_resend = 0;	llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);	if (!nbr_unack_pdus)		goto out;	/*	 * Process unack PDUs only if unack queue is not empty; remove	 * appropriate PDUs, fix them up, and put them on mac_pdu_q.	 */	llc = llc_sk(sk);	while ((skb = skb_dequeue(&llc->pdu_unack_q)) != NULL) {		pdu = llc_pdu_sn_hdr(skb);		llc_pdu_set_cmd_rsp(skb, LLC_PDU_CMD);		llc_pdu_set_pf_bit(skb, first_p_bit);		skb_queue_tail(&sk->sk_write_queue, skb);		first_p_bit = 0;		llc->vS = LLC_I_GET_NS(pdu);		howmany_resend++;	}	if (howmany_resend > 0)		llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;	/* any PDUs to re-send are queued up; start sending to MAC */	llc_conn_send_pdus(sk);out:;}/** *	llc_conn_resend_i_pdu_as_rsp - Resend all unacknowledged I PDUs *	@sk: active connection. *	@nr: NR *	@first_f_bit: f_bit value of first pdu. * *	Resend all unacknowledged I PDUs, starting with the NR; send first as *	response PDU with F bit equal first_f_bit; if more than one send *	subsequent as response PDUs with F bit equal zero (0). */void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit){	struct sk_buff *skb;	u16 nbr_unack_pdus;	struct llc_sock *llc = llc_sk(sk);	u8 howmany_resend = 0;	llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);	if (!nbr_unack_pdus)		goto out;	/*	 * Process unack PDUs only if unack queue is not empty; remove	 * appropriate PDUs, fix them up, and put them on mac_pdu_q	 */	while ((skb = skb_dequeue(&llc->pdu_unack_q)) != NULL) {		struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);		llc_pdu_set_cmd_rsp(skb, LLC_PDU_RSP);		llc_pdu_set_pf_bit(skb, first_f_bit);		skb_queue_tail(&sk->sk_write_queue, skb);		first_f_bit = 0;		llc->vS = LLC_I_GET_NS(pdu);		howmany_resend++;	}	if (howmany_resend > 0)		llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;	/* any PDUs to re-send are queued up; start sending to MAC */	llc_conn_send_pdus(sk);out:;}/** *	llc_conn_remove_acked_pdus - Removes acknowledged pdus from tx queue *	@sk: active connection *	nr: NR *	how_many_unacked: size of pdu_unack_q after removing acked pdus * *	Removes acknowledged pdus from transmit queue (pdu_unack_q). Returns *	the number of pdus that removed from queue. */int llc_conn_remove_acked_pdus(struct sock *sk, u8 nr, u16 *how_many_unacked){	int pdu_pos, i;	struct sk_buff *skb;	struct llc_pdu_sn *pdu;	int nbr_acked = 0;	struct llc_sock *llc = llc_sk(sk);	int q_len = skb_queue_len(&llc->pdu_unack_q);	if (!q_len)		goto out;	skb = skb_peek(&llc->pdu_unack_q);	pdu = llc_pdu_sn_hdr(skb);	/* finding position of last acked pdu in queue */	pdu_pos = ((int)LLC_2_SEQ_NBR_MODULO + (int)nr -			(int)LLC_I_GET_NS(pdu)) % LLC_2_SEQ_NBR_MODULO;	for (i = 0; i < pdu_pos && i < q_len; i++) {		skb = skb_dequeue(&llc->pdu_unack_q);		if (skb)			kfree_skb(skb);		nbr_acked++;	}out:	*how_many_unacked = skb_queue_len(&llc->pdu_unack_q);	return nbr_acked;}/** *	llc_conn_send_pdus - Sends queued PDUs *	@sk: active connection * *	Sends queued pdus to MAC layer for transmission. */static void llc_conn_send_pdus(struct sock *sk){	struct sk_buff *skb;	while ((skb = skb_dequeue(&sk->sk_write_queue)) != NULL) {		struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);		if (LLC_PDU_TYPE_IS_I(pdu) &&		    !(skb->dev->flags & IFF_LOOPBACK)) {			struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);			skb_queue_tail(&llc_sk(sk)->pdu_unack_q, skb);			if (!skb2)				break;			skb = skb2;		}		dev_queue_xmit(skb);	}}/** *	llc_conn_service - finds transition and changes state of connection *	@sk: connection *	@skb: happened event * *	This function finds transition that matches with happened event, then *	executes related actions and finally changes state of connection. *	Returns 0 for success, 1 for failure. */static int llc_conn_service(struct sock *sk, struct sk_buff *skb){	int rc = 1;	struct llc_sock *llc = llc_sk(sk);	struct llc_conn_state_trans *trans;	if (llc->state > NBR_CONN_STATES)		goto out;	rc = 0;	trans = llc_qualify_conn_ev(sk, skb);	if (trans) {		rc = llc_exec_conn_trans_actions(sk, trans, skb);		if (!rc && trans->next_state != NO_STATE_CHANGE) {			llc->state = trans->next_state;			if (!llc_data_accept_state(llc->state))				sk->sk_state_change(sk);		}	}out:	return rc;}/** *	llc_qualify_conn_ev - finds transition for event *	@sk: connection *	@skb: happened event * *	This function finds transition that matches with happened event. *	Returns pointer to found transition on success, %NULL otherwise. */static struct llc_conn_state_trans *llc_qualify_conn_ev(struct sock *sk,							struct sk_buff *skb){	struct llc_conn_state_trans **next_trans;	llc_conn_ev_qfyr_t *next_qualifier;	struct llc_conn_state_ev *ev = llc_conn_ev(skb);	struct llc_sock *llc = llc_sk(sk);	struct llc_conn_state *curr_state =					&llc_conn_state_table[llc->state - 1];	/* search thru events for this state until	 * list exhausted or until no more	 */	for (next_trans = curr_state->transitions +		llc_find_offset(llc->state - 1, ev->type);	     (*next_trans)->ev; next_trans++) {		if (!((*next_trans)->ev)(sk, skb)) {			/* got POSSIBLE event match; the event may require			 * qualification based on the values of a number of			 * state flags; if all qualifications are met (i.e.,			 * if all qualifying functions return success, or 0,			 * then this is THE event we're looking for			 */			for (next_qualifier = (*next_trans)->ev_qualifiers;			     next_qualifier && *next_qualifier &&			     !(*next_qualifier)(sk, skb); next_qualifier++)				/* nothing */;			if (!next_qualifier || !*next_qualifier)				/* all qualifiers executed successfully; this is				 * our transition; return it so we can perform				 * the associated actions & change the state				 */				return *next_trans;		}	}	return NULL;}/** *	llc_exec_conn_trans_actions - executes related actions *	@sk: connection *	@trans: transition that it's actions must be performed *	@skb: event * *	Executes actions that is related to happened event. Returns 0 for *	success, 1 to indicate failure of at least one action. */static int llc_exec_conn_trans_actions(struct sock *sk,				       struct llc_conn_state_trans *trans,				       struct sk_buff *skb){	int rc = 0;	llc_conn_action_t *next_action;	for (next_action = trans->ev_actions;	     next_action && *next_action; next_action++) {		int rc2 = (*next_action)(sk, skb);		if (rc2 == 2) {			rc = rc2;			break;		} else if (rc2)			rc = 1;	}	return rc;}/** *	__llc_lookup_established - Finds connection for the remote/local sap/mac

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -