/**
 *	llc_ui_send_data - send data via reliable llc2 connection
 *	@sk: Connection the socket is using.
 *	@skb: Data the user wishes to send.
 *	@addr: Source and destination fields provided by the user.
 *	@noblock: can we block waiting for data?
 *
 *	Send data via reliable llc2 connection.
 *	Returns 0 upon success, non-zero if action did not succeed.
 */
static int llc_ui_send_data(struct sock* sk, struct sk_buff *skb, int noblock)
{
	struct llc_opt* llc = llc_sk(sk);
	int rc = 0;

	if (llc_data_accept_state(llc->state) || llc->p_flag) {
		int timeout = sock_sndtimeo(sk, noblock);

		rc = llc_ui_wait_for_busy_core(sk, timeout);
	}
	if (!rc)
		rc = llc_build_and_send_pkt(sk, skb);
	return rc;
}
Exemple #2
0
/**
 *	llc_conn_ac_inc_npta_value - tries to make value of npta greater
 *	@sk: current connection structure.
 *	@skb: 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.
 */
static int llc_conn_ac_inc_npta_value(struct sock *sk, struct sk_buff *skb)
{
	struct llc_sock *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;
}
Exemple #3
0
/**
 *	llc_process_tmr_ev - timer backend
 *	@sk: active connection
 *	@skb: 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 llc_backlog_rcv function after sending
 *	data frame.
 */
static void llc_process_tmr_ev(struct sock *sk, struct sk_buff *skb)
{
	if (llc_sk(sk)->state == LLC_CONN_OUT_OF_SVC) {
		printk(KERN_WARNING "%s: timer called on closed connection\n",
		       __func__);
		kfree_skb(skb);
	} else {
		if (!sock_owned_by_user(sk))
			llc_conn_state_process(sk, skb);
		else {
			llc_set_backlog_type(skb, LLC_EVENT);
			sk_add_backlog(sk, skb);
		}
	}
}
Exemple #4
0
/**
 *	llc_conn_ac_adjust_npta_by_rnr - decreases "npta" by one
 *	@sk: current connection structure.
 *	@skb: 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 sk_buff *skb)
{
	struct llc_sock *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;
}
Exemple #5
0
int llc_conn_ac_clear_remote_busy(struct sock *sk, struct sk_buff *skb)
{
	struct llc_sock *llc = llc_sk(sk);

	if (llc->remote_busy_flag) {
		u8 nr;
		struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);

		llc->remote_busy_flag = 0;
		del_timer(&llc->busy_state_timer.timer);
		nr = LLC_I_GET_NR(pdu);
		llc_conn_resend_i_pdu_as_cmd(sk, nr, 0);
	}
	return 0;
}
Exemple #6
0
/**
 *	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 hasn't previously
 *	called bind(2) with a smac the address of the first interface of the
 *	specified arp type will be used.
 *	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_sock *llc = llc_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 (sock_flag(sk, SOCK_ZAPPED)) {
		/* bind to sap with null dev, exclusive */
		rc = llc_ui_autobind(sock, addr);
		if (rc)
			goto out;
		llc->daddr.lsap = addr->sllc_sap;
		memcpy(llc->daddr.mac, addr->sllc_mac, IFHWADDRLEN);
	}
	dev = llc->dev;
	if (sk->sk_type != SOCK_STREAM)
		goto out;
	rc = -EALREADY;
	if (sock->state == SS_CONNECTING)
		goto out;
	sock->state = SS_CONNECTING;
	sk->sk_state   = TCP_SYN_SENT;
	llc->link   = llc_ui_next_link_no(llc->sap->laddr.lsap);
	rc = llc_establish_connection(sk, dev->dev_addr,
				      addr->sllc_mac, addr->sllc_sap);
	if (rc) {
		dprintk("%s: llc_ui_send_conn failed :-(\n", __FUNCTION__);
		sock->state  = SS_UNCONNECTED;
		sk->sk_state = TCP_CLOSE;
		goto out;
	}
	rc = llc_ui_wait_for_conn(sk, sk->sk_rcvtimeo);
	if (rc)
		dprintk("%s: llc_ui_wait_for_conn failed=%d\n", __FUNCTION__, rc);
out:
	release_sock(sk);
	return rc;
}
Exemple #7
0
int llc_conn_ac_send_i_xxx_x_set_0(struct sock *sk, struct sk_buff *skb)
{
	int rc;
	struct llc_sock *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, 0, llc->vS, llc->vR);
	rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac);
	if (likely(!rc)) {
		llc_conn_send_pdu(sk, skb);
		llc_conn_ac_inc_vs_by_1(sk, skb);
	}
	return 0;
}
/**
 *	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 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;
	struct llc_sock *llc = llc_sk(sk);
	struct llc_sap *sap;
	int rc = -EINVAL;

	dprintk("%s: binding %02X\n", __func__, addr->sllc_sap);
	if (unlikely(!sock_flag(sk, SOCK_ZAPPED) || addrlen != sizeof(*addr)))
		goto out;
	rc = -EAFNOSUPPORT;
	if (unlikely(addr->sllc_family != AF_LLC))
		goto out;
	rc = -ENODEV;
<<<<<<< HEAD
/**
 *	llc_conn_ac_send_i_rsp_f_set_ackpf - acknowledge received PDUs
 *	@sk: current connection structure
 *	@skb: 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 sk_buff *skb)
{
	int rc;
	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_RSP);
	llc_pdu_init_as_i_cmd(skb, llc->ack_pf, llc->vS, llc->vR);
	rc = llc_mac_hdr_init(skb, llc->dev->dev_addr, llc->daddr.mac);
	if (!rc) {
		llc_conn_send_pdu(sk, skb);
		llc_conn_ac_inc_vs_by_1(sk, skb);
	}
	return rc;
}
Exemple #10
0
/**
 *	llc_conn_ac_adjust_npta_by_rr - decreases "npta" by one
 *	@sk: current connection structure.
 *	@skb: 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 sk_buff *skb)
{
	struct llc_sock *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;
}
Exemple #11
0
/**
 *	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 __user *optval, int __user *optlen)
{
	struct sock *sk = sock->sk;
	struct llc_sock *llc = llc_sk(sk);
	int val = 0, len = 0, rc = -EINVAL;

	lock_sock(sk);
	if (level != SOL_LLC)
		goto out;
	rc = get_user(len, optlen);
	if (rc)
		goto out;
	rc = -EINVAL;
	if (len != sizeof(int))
		goto out;
	switch (optname) {
	case LLC_OPT_RETRY:
		val = llc->n2;				break;
	case LLC_OPT_SIZE:
		val = llc->n1;				break;
	case LLC_OPT_ACK_TMR_EXP:
		val = llc->ack_timer.expire;		break;
	case LLC_OPT_P_TMR_EXP:
		val = llc->pf_cycle_timer.expire;	break;
	case LLC_OPT_REJ_TMR_EXP:
		val = llc->rej_sent_timer.expire;	break;
	case LLC_OPT_BUSY_TMR_EXP:
		val = llc->busy_state_timer.expire;	break;
	case LLC_OPT_TX_WIN:
		val = llc->k;				break;
	case LLC_OPT_RX_WIN:
		val = llc->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;
}
Exemple #12
0
/**
 *	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);
	}
}
int llc_conn_ac_rst_ind(struct sock *sk, struct sk_buff *skb)
{
	u8 reason = 0;
	int rc = 1;
	struct llc_conn_state_ev *ev = llc_conn_ev(skb);
	struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
	struct llc_opt *llc = llc_sk(sk);

	switch (ev->type) {
	case LLC_CONN_EV_TYPE_PDU:
		if (LLC_PDU_IS_RSP(pdu) &&
		    LLC_PDU_TYPE_IS_U(pdu) &&
		    LLC_U_PDU_RSP(pdu) == LLC_2_PDU_RSP_FRMR) {
			reason = LLC_RESET_REASON_LOCAL;
			rc = 0;
		} else if (LLC_PDU_IS_CMD(pdu) &&
			   LLC_PDU_TYPE_IS_U(pdu) &&
			   LLC_U_PDU_CMD(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) {
		ev->reason   = reason;
		ev->ind_prim = LLC_RESET_PRIM;
	}
	return rc;
}
/**
 *	llc_conn_ac_send_ack_if_needed - check if ack is needed
 *	@sk: current connection structure
 *	@skb: 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 sk_buff *skb)
{
	u8 pf_bit;
	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, skb);
		llc->ack_must_be_send	= 0;
		llc->ack_pf		= 0;
		llc_conn_ac_inc_npta_value(sk, skb);
	}
	return 0;
}
int llc_conn_ac_conn_ind(struct sock *sk, struct sk_buff *skb)
{
	int rc = -ENOTCONN;
	u8 dsap;
	struct llc_sap *sap;

	llc_pdu_decode_dsap(skb, &dsap);
	sap = llc_sap_find(dsap);
	if (sap) {
		struct llc_conn_state_ev *ev = llc_conn_ev(skb);
		struct llc_opt *llc = llc_sk(sk);

		llc_pdu_decode_sa(skb, llc->daddr.mac);
		llc_pdu_decode_da(skb, llc->laddr.mac);
		llc->dev = skb->dev;
		ev->ind_prim = LLC_CONN_PRIM;
		rc = 0;
	}
	return rc;
}
Exemple #16
0
/**
 *	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;
}
Exemple #17
0
/**
 *	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_sock *llc = llc_sk(sk);
	int rc = 0;

	lock_sock(sk);
	if (sock_flag(sk, SOCK_ZAPPED))
		goto out;
	*uaddrlen = sizeof(sllc);
	memset(uaddr, 0, *uaddrlen);
	if (peer) {
		rc = -ENOTCONN;
		if (sk->sk_state != TCP_ESTABLISHED)
			goto out;
		if(llc->dev)
			sllc.sllc_arphrd = llc->dev->type;
		sllc.sllc_sap = llc->daddr.lsap;
		memcpy(&sllc.sllc_mac, &llc->daddr.mac, IFHWADDRLEN);
	} else {
		rc = -EINVAL;
		if (!llc->sap)
			goto out;
		sllc.sllc_sap = llc->sap->laddr.lsap;

		if (llc->dev) {
			sllc.sllc_arphrd = llc->dev->type;
			memcpy(&sllc.sllc_mac, &llc->dev->dev_addr,
			       IFHWADDRLEN);
		}
	}
	rc = 0;
	sllc.sllc_family = AF_LLC;
	memcpy(uaddr, &sllc, sizeof(sllc));
out:
	release_sock(sk);
	return rc;
}
Exemple #18
0
/**
 *	llc_sk_reset - resets a connection
 *	@sk: LLC socket to reset
 *
 *	Resets a connection to the out of service state. Stops its timers
 *	and frees any frames in the queues of the connection.
 */
void llc_sk_reset(struct sock *sk)
{
	struct llc_sock *llc = llc_sk(sk);

	llc_conn_ac_stop_all_timers(sk, NULL);
	skb_queue_purge(&sk->sk_write_queue);
	skb_queue_purge(&llc->pdu_unack_q);
	llc->remote_busy_flag	= 0;
	llc->cause_flag		= 0;
	llc->retry_count	= 0;
	llc_conn_set_p_flag(sk, 0);
	llc->f_flag		= 0;
	llc->s_flag		= 0;
	llc->ack_pf		= 0;
	llc->first_pdu_Ns	= 0;
	llc->ack_must_be_send	= 0;
	llc->dec_step		= 1;
	llc->inc_cntr		= 2;
	llc->dec_cntr		= 2;
	llc->X			= 0;
	llc->failed_data_req	= 0 ;
	llc->last_nr		= 0;
}
/**
 *	llc_util_nr_inside_tx_window - check if sequence number is in tx window
 *	@sk: current connection.
 *	@nr: N(R) of received PDU.
 *
 *	This routine checks if N(R) of received PDU is in range of transmit
 *	window; on the other hand checks if received PDU acknowledges some
 *	outstanding PDUs that are in transmit window. Returns 0 for success, 1
 *	otherwise.
 */
static u16 llc_util_nr_inside_tx_window(struct sock *sk, u8 nr)
{
	u8 nr1, nr2;
	struct sk_buff *skb;
	struct llc_pdu_sn *pdu;
	struct llc_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_hdr(skb);
	nr1 = LLC_I_GET_NS(pdu);
	skb = skb_peek_tail(&llc->pdu_unack_q);
	pdu = llc_pdu_sn_hdr(skb);
	nr2 = LLC_I_GET_NS(pdu);
	rc = !llc_circular_between(nr1, nr, (nr2 + 1) % LLC_2_SEQ_NBR_MODULO);
out:
	return rc;
}
Exemple #20
0
int llc_conn_ac_send_ack_xxx_x_set_0(struct sock *sk, struct sk_buff *skb)
{
	int rc = -ENOBUFS;
	struct llc_sock *llc = llc_sk(sk);
	struct sk_buff *nskb = llc_alloc_frame(sk, llc->dev, LLC_PDU_TYPE_S, 0);

	if (nskb) {
		struct llc_sap *sap = llc->sap;

		llc_pdu_header_init(nskb, LLC_PDU_TYPE_S, sap->laddr.lsap,
				    llc->daddr.lsap, LLC_PDU_RSP);
		llc_pdu_init_as_rr_rsp(nskb, 0, llc->vR);
		rc = llc_mac_hdr_init(nskb, llc->dev->dev_addr, llc->daddr.mac);
		if (unlikely(rc))
			goto free;
		llc_conn_send_pdu(sk, nskb);
	}
out:
	return rc;
free:
	kfree_skb(nskb);
	goto out;
}
Exemple #21
0
/**
 *    llc_build_and_send_pkt - Connection data sending for upper layers.
 *    @sk: connection
 *    @skb: packet to send
 *
 *    This function is called when upper layer wants to send data using
 *    connection oriented communication mode. During sending data, connection
 *    will be locked and received frames and expired timers will be queued.
 *    Returns 0 for success, -ECONNABORTED when the connection already
 *    closed and -EBUSY when sending data is not permitted in this state or
 *    LLC has send an I pdu with p bit set to 1 and is waiting for it's
 *    response.
 */
int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb)
{
    struct llc_conn_state_ev *ev;
    int rc = -ECONNABORTED;
    struct llc_sock *llc = llc_sk(sk);

    if (unlikely(llc->state == LLC_CONN_STATE_ADM))
        goto out;
    rc = -EBUSY;
    if (unlikely(llc_data_accept_state(llc->state) || /* data_conn_refuse */
             llc->p_flag)) {
        llc->failed_data_req = 1;
        goto out;
    }
    ev = llc_conn_ev(skb);
    ev->type      = LLC_CONN_EV_TYPE_PRIM;
    ev->prim      = LLC_DATA_PRIM;
    ev->prim_type = LLC_PRIM_TYPE_REQ;
    skb->dev      = llc->dev;
    rc = llc_conn_state_process(sk, skb);
out:
    return rc;
}
Exemple #22
0
/**
 *	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_ui_autobind - automatically bind a socket to a sap
 *	@sock: socket to bind
 *	@addr: address to connect to
 *
 * 	Used by llc_ui_connect and llc_ui_sendmsg when the user hasn't
 * 	specifically used llc_ui_bind to bind to an specific address/sap
 *
 *	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_sock *llc = llc_sk(sk);
	struct llc_sap *sap;
	int rc = -EINVAL;

	if (!sock_flag(sk, SOCK_ZAPPED))
		goto out;
	rc = -ENODEV;
	if (sk->sk_bound_dev_if) {
		llc->dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if);
		if (llc->dev && addr->sllc_arphrd != llc->dev->type) {
			dev_put(llc->dev);
			llc->dev = NULL;
		}
	} else
		llc->dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd);
	if (!llc->dev)
		goto out;
	rc = -EUSERS;
	llc->laddr.lsap = llc_ui_autoport();
	if (!llc->laddr.lsap)
		goto out;
	rc = -EBUSY; /* some other network layer is using the sap */
	sap = llc_sap_open(llc->laddr.lsap, NULL);
	if (!sap)
		goto out;
	memcpy(llc->laddr.mac, llc->dev->dev_addr, IFHWADDRLEN);
	memcpy(&llc->addr, addr, sizeof(llc->addr));
	/* assign new connection to its SAP */
	llc_sap_add_socket(sap, sk);
	sock_reset_flag(sk, SOCK_ZAPPED);
	rc = 0;
out:
	return rc;
}
Exemple #24
0
/**
 *	__llc_lookup_established - Finds connection for the remote/local sap/mac
 *	@sap: SAP
 *	@daddr: address of remote LLC (MAC + SAP)
 *	@laddr: address of local LLC (MAC + SAP)
 *
 *	Search connection list of the SAP and finds connection using the remote
 *	mac, remote sap, local mac, and local sap. Returns pointer for
 *	connection found, %NULL otherwise.
 *	Caller has to make sure local_bh is disabled.
 */
static struct sock *__llc_lookup_established(struct llc_sap *sap,
					     struct llc_addr *daddr,
					     struct llc_addr *laddr)
{
	struct sock *rc;
	struct hlist_nulls_node *node;
	int slot = llc_sk_laddr_hashfn(sap, laddr);
	struct hlist_nulls_head *laddr_hb = &sap->sk_laddr_hash[slot];

	rcu_read_lock();
again:
	sk_nulls_for_each_rcu(rc, node, laddr_hb) {
		if (llc_estab_match(sap, daddr, laddr, rc)) {
			/* Extra checks required by SLAB_DESTROY_BY_RCU */
			if (unlikely(!atomic_inc_not_zero(&rc->sk_refcnt)))
				goto again;
			if (unlikely(llc_sk(rc)->sap != sap ||
				     !llc_estab_match(sap, daddr, laddr, rc))) {
				sock_put(rc);
				continue;
			}
			goto found;
		}
	}
	rc = NULL;
	/*
	 * if the nulls value we got at the end of this lookup is
	 * not the expected one, we must restart lookup.
	 * We probably met an item that was moved to another chain.
	 */
	if (unlikely(get_nulls_value(node) != slot))
		goto again;
found:
	rcu_read_unlock();
	return rc;
}
int llc_conn_ac_send_ack_cmd_p_set_1(struct sock *sk, struct sk_buff *skb)
{
	int rc = -ENOBUFS;
	struct sk_buff *nskb = llc_alloc_frame();

	if (nskb) {
		struct llc_opt *llc = llc_sk(sk);
		struct llc_sap *sap = llc->sap;

		nskb->dev = llc->dev;
		llc_pdu_header_init(nskb, LLC_PDU_TYPE_S, sap->laddr.lsap,
				    llc->daddr.lsap, LLC_PDU_CMD);
		llc_pdu_init_as_rr_cmd(nskb, 1, llc->vR);
		rc = llc_mac_hdr_init(nskb, llc->dev->dev_addr, llc->daddr.mac);
		if (rc)
			goto free;
		llc_conn_send_pdu(sk, nskb);
	}
out:
	return rc;
free:
	kfree_skb(nskb);
	goto out;
}
int llc_conn_ac_send_ua_rsp_f_set_f_flag(struct sock *sk, struct sk_buff *skb)
{
	int rc = -ENOBUFS;
	struct sk_buff *nskb = llc_alloc_frame();

	if (nskb) {
		struct llc_opt *llc = llc_sk(sk);
		struct llc_sap *sap = llc->sap;

		nskb->dev = llc->dev;
		llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, sap->laddr.lsap,
				    llc->daddr.lsap, LLC_PDU_RSP);
		llc_pdu_init_as_ua_rsp(nskb, llc->f_flag);
		rc = llc_mac_hdr_init(nskb, llc->dev->dev_addr, llc->daddr.mac);
		if (rc)
			goto free;
		llc_conn_send_pdu(sk, nskb);
	}
out:
	return rc;
free:
	kfree_skb(nskb);
	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_sock *llc;

	if (unlikely(sk == NULL))
		goto out;
	sock_hold(sk);
	lock_sock(sk);
	llc = llc_sk(sk);
	dprintk("%s: closing local(%02X) remote(%02X)\n", __func__,
		llc->laddr.lsap, llc->daddr.lsap);
	if (!llc_send_disc(sk))
		llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo);
	if (!sock_flag(sk, SOCK_ZAPPED))
		llc_sap_remove_socket(llc->sap, sk);
	release_sock(sk);
	if (llc->dev)
		dev_put(llc->dev);
	sock_put(sk);
	llc_sk_free(sk);
out:
	return 0;
}
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];

	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)) {
			for (next_qualifier = (*next_trans)->ev_qualifiers;
			     next_qualifier && *next_qualifier &&
			     !(*next_qualifier)(sk, skb); next_qualifier++)
				;
			if (!next_qualifier || !*next_qualifier)
				return *next_trans;
		}
	}
	return NULL;
}
Exemple #29
0
/**
 *    llc_establish_connection - Called by upper layer to establish a conn
 *    @sk: connection
 *    @lmac: local mac address
 *    @dmac: destination mac address
 *    @dsap: destination sap
 *
 *    Upper layer calls this to establish an LLC connection with a remote
 *    machine. This function packages a proper event and sends it connection
 *    component state machine. Success or failure of connection
 *    establishment will inform to upper layer via calling it's confirm
 *    function and passing proper information.
 */
int llc_establish_connection(struct sock *sk, u8 *lmac, u8 *dmac, u8 dsap)
{
    int rc = -EISCONN;
    struct llc_addr laddr, daddr;
    struct sk_buff *skb;
    struct llc_sock *llc = llc_sk(sk);
    struct sock *existing;

    laddr.lsap = llc->sap->laddr.lsap;
    daddr.lsap = dsap;
    memcpy(daddr.mac, dmac, sizeof(daddr.mac));
    memcpy(laddr.mac, lmac, sizeof(laddr.mac));
    existing = llc_lookup_established(llc->sap, &daddr, &laddr);
    if (existing) {
        if (existing->sk_state == TCP_ESTABLISHED) {
            sk = existing;
            goto out_put;
        } else
            sock_put(existing);
    }
    sock_hold(sk);
    rc = -ENOMEM;
    skb = alloc_skb(0, GFP_ATOMIC);
    if (skb) {
        struct llc_conn_state_ev *ev = llc_conn_ev(skb);

        ev->type      = LLC_CONN_EV_TYPE_PRIM;
        ev->prim      = LLC_CONN_PRIM;
        ev->prim_type = LLC_PRIM_TYPE_REQ;
        skb_set_owner_w(skb, sk);
        rc = llc_conn_state_process(sk, skb);
    }
out_put:
    sock_put(sk);
    return rc;
}
Exemple #30
0
int llc_conn_ac_send_disc_cmd_p_set_x(struct sock *sk, struct sk_buff *skb)
{
	int rc = -ENOBUFS;
	struct llc_sock *llc = llc_sk(sk);
	struct sk_buff *nskb = llc_alloc_frame(sk, llc->dev, LLC_PDU_TYPE_U, 0);

	if (nskb) {
		struct llc_sap *sap = llc->sap;

		llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, sap->laddr.lsap,
				    llc->daddr.lsap, LLC_PDU_CMD);
		llc_pdu_init_as_disc_cmd(nskb, 1);
		rc = llc_mac_hdr_init(nskb, llc->dev->dev_addr, llc->daddr.mac);
		if (unlikely(rc))
			goto free;
		llc_conn_send_pdu(sk, nskb);
		llc_conn_ac_set_p_flag_1(sk, skb);
	}
out:
	return rc;
free:
	kfree_skb(nskb);
	goto out;
}