xref: /linux/net/llc/llc_if.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * llc_if.c - Defines LLC interface to upper layer
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (c) 1997 by Procom Technology, Inc.
51da177e4SLinus Torvalds  * 		 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * This program can be redistributed or modified under the terms of the
81da177e4SLinus Torvalds  * GNU General Public License as published by the Free Software Foundation.
91da177e4SLinus Torvalds  * This program is distributed without any warranty or implied warranty
101da177e4SLinus Torvalds  * of merchantability or fitness for a particular purpose.
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  * See the GNU General Public License for more details.
131da177e4SLinus Torvalds  */
145a0e3ad6STejun Heo #include <linux/gfp.h>
151da177e4SLinus Torvalds #include <linux/module.h>
161da177e4SLinus Torvalds #include <linux/kernel.h>
171da177e4SLinus Torvalds #include <linux/netdevice.h>
1874bca138SFabian Frederick #include <linux/errno.h>
191da177e4SLinus Torvalds #include <net/llc_if.h>
201da177e4SLinus Torvalds #include <net/llc_sap.h>
211da177e4SLinus Torvalds #include <net/llc_s_ev.h>
221da177e4SLinus Torvalds #include <net/llc_conn.h>
231da177e4SLinus Torvalds #include <net/sock.h>
241da177e4SLinus Torvalds #include <net/llc_c_ev.h>
251da177e4SLinus Torvalds #include <net/llc_c_ac.h>
261da177e4SLinus Torvalds #include <net/llc_c_st.h>
27c752f073SArnaldo Carvalho de Melo #include <net/tcp_states.h>
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds /**
301da177e4SLinus Torvalds  *	llc_build_and_send_pkt - Connection data sending for upper layers.
311da177e4SLinus Torvalds  *	@sk: connection
321da177e4SLinus Torvalds  *	@skb: packet to send
331da177e4SLinus Torvalds  *
341da177e4SLinus Torvalds  *	This function is called when upper layer wants to send data using
351da177e4SLinus Torvalds  *	connection oriented communication mode. During sending data, connection
361da177e4SLinus Torvalds  *	will be locked and received frames and expired timers will be queued.
371da177e4SLinus Torvalds  *	Returns 0 for success, -ECONNABORTED when the connection already
381da177e4SLinus Torvalds  *	closed and -EBUSY when sending data is not permitted in this state or
391da177e4SLinus Torvalds  *	LLC has send an I pdu with p bit set to 1 and is waiting for it's
401da177e4SLinus Torvalds  *	response.
41fc8d5db1SEric Biggers  *
42fc8d5db1SEric Biggers  *	This function always consumes a reference to the skb.
431da177e4SLinus Torvalds  */
llc_build_and_send_pkt(struct sock * sk,struct sk_buff * skb)441da177e4SLinus Torvalds int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb)
451da177e4SLinus Torvalds {
461da177e4SLinus Torvalds 	struct llc_conn_state_ev *ev;
471da177e4SLinus Torvalds 	int rc = -ECONNABORTED;
481da177e4SLinus Torvalds 	struct llc_sock *llc = llc_sk(sk);
491da177e4SLinus Torvalds 
50249ff1c6SArnaldo Carvalho de Melo 	if (unlikely(llc->state == LLC_CONN_STATE_ADM))
51fc8d5db1SEric Biggers 		goto out_free;
521da177e4SLinus Torvalds 	rc = -EBUSY;
53249ff1c6SArnaldo Carvalho de Melo 	if (unlikely(llc_data_accept_state(llc->state) || /* data_conn_refuse */
54249ff1c6SArnaldo Carvalho de Melo 		     llc->p_flag)) {
551da177e4SLinus Torvalds 		llc->failed_data_req = 1;
56fc8d5db1SEric Biggers 		goto out_free;
571da177e4SLinus Torvalds 	}
581da177e4SLinus Torvalds 	ev = llc_conn_ev(skb);
591da177e4SLinus Torvalds 	ev->type      = LLC_CONN_EV_TYPE_PRIM;
601da177e4SLinus Torvalds 	ev->prim      = LLC_DATA_PRIM;
611da177e4SLinus Torvalds 	ev->prim_type = LLC_PRIM_TYPE_REQ;
621da177e4SLinus Torvalds 	skb->dev      = llc->dev;
63fc8d5db1SEric Biggers 	return llc_conn_state_process(sk, skb);
64fc8d5db1SEric Biggers 
65fc8d5db1SEric Biggers out_free:
66fc8d5db1SEric Biggers 	kfree_skb(skb);
671da177e4SLinus Torvalds 	return rc;
681da177e4SLinus Torvalds }
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds /**
711da177e4SLinus Torvalds  *	llc_establish_connection - Called by upper layer to establish a conn
721da177e4SLinus Torvalds  *	@sk: connection
731da177e4SLinus Torvalds  *	@lmac: local mac address
741da177e4SLinus Torvalds  *	@dmac: destination mac address
751da177e4SLinus Torvalds  *	@dsap: destination sap
761da177e4SLinus Torvalds  *
771da177e4SLinus Torvalds  *	Upper layer calls this to establish an LLC connection with a remote
781da177e4SLinus Torvalds  *	machine. This function packages a proper event and sends it connection
791da177e4SLinus Torvalds  *	component state machine. Success or failure of connection
801da177e4SLinus Torvalds  *	establishment will inform to upper layer via calling it's confirm
811da177e4SLinus Torvalds  *	function and passing proper information.
821da177e4SLinus Torvalds  */
llc_establish_connection(struct sock * sk,const u8 * lmac,u8 * dmac,u8 dsap)832ef6db76SJakub Kicinski int llc_establish_connection(struct sock *sk, const u8 *lmac, u8 *dmac, u8 dsap)
841da177e4SLinus Torvalds {
851da177e4SLinus Torvalds 	int rc = -EISCONN;
861da177e4SLinus Torvalds 	struct llc_addr laddr, daddr;
871da177e4SLinus Torvalds 	struct sk_buff *skb;
881da177e4SLinus Torvalds 	struct llc_sock *llc = llc_sk(sk);
891da177e4SLinus Torvalds 	struct sock *existing;
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 	laddr.lsap = llc->sap->laddr.lsap;
921da177e4SLinus Torvalds 	daddr.lsap = dsap;
931da177e4SLinus Torvalds 	memcpy(daddr.mac, dmac, sizeof(daddr.mac));
941da177e4SLinus Torvalds 	memcpy(laddr.mac, lmac, sizeof(laddr.mac));
95*97b1d320SKuniyuki Iwashima 	existing = llc_lookup_established(llc->sap, &daddr, &laddr, sock_net(sk));
961da177e4SLinus Torvalds 	if (existing) {
971da177e4SLinus Torvalds 		if (existing->sk_state == TCP_ESTABLISHED) {
981da177e4SLinus Torvalds 			sk = existing;
991da177e4SLinus Torvalds 			goto out_put;
1001da177e4SLinus Torvalds 		} else
1011da177e4SLinus Torvalds 			sock_put(existing);
1021da177e4SLinus Torvalds 	}
1031da177e4SLinus Torvalds 	sock_hold(sk);
1041da177e4SLinus Torvalds 	rc = -ENOMEM;
1051da177e4SLinus Torvalds 	skb = alloc_skb(0, GFP_ATOMIC);
1061da177e4SLinus Torvalds 	if (skb) {
1071da177e4SLinus Torvalds 		struct llc_conn_state_ev *ev = llc_conn_ev(skb);
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 		ev->type      = LLC_CONN_EV_TYPE_PRIM;
1101da177e4SLinus Torvalds 		ev->prim      = LLC_CONN_PRIM;
1111da177e4SLinus Torvalds 		ev->prim_type = LLC_PRIM_TYPE_REQ;
112d389424eSArnaldo Carvalho de Melo 		skb_set_owner_w(skb, sk);
1131da177e4SLinus Torvalds 		rc = llc_conn_state_process(sk, skb);
1141da177e4SLinus Torvalds 	}
1151da177e4SLinus Torvalds out_put:
1161da177e4SLinus Torvalds 	sock_put(sk);
1171da177e4SLinus Torvalds 	return rc;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds /**
1211da177e4SLinus Torvalds  *	llc_send_disc - Called by upper layer to close a connection
1221da177e4SLinus Torvalds  *	@sk: connection to be closed
1231da177e4SLinus Torvalds  *
1241da177e4SLinus Torvalds  *	Upper layer calls this when it wants to close an established LLC
1251da177e4SLinus Torvalds  *	connection with a remote machine. This function packages a proper event
1261da177e4SLinus Torvalds  *	and sends it to connection component state machine. Returns 0 for
1271da177e4SLinus Torvalds  *	success, 1 otherwise.
1281da177e4SLinus Torvalds  */
llc_send_disc(struct sock * sk)1291da177e4SLinus Torvalds int llc_send_disc(struct sock *sk)
1301da177e4SLinus Torvalds {
1311da177e4SLinus Torvalds 	u16 rc = 1;
1321da177e4SLinus Torvalds 	struct llc_conn_state_ev *ev;
1331da177e4SLinus Torvalds 	struct sk_buff *skb;
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	sock_hold(sk);
1361da177e4SLinus Torvalds 	if (sk->sk_type != SOCK_STREAM || sk->sk_state != TCP_ESTABLISHED ||
1371da177e4SLinus Torvalds 	    llc_sk(sk)->state == LLC_CONN_STATE_ADM ||
1381da177e4SLinus Torvalds 	    llc_sk(sk)->state == LLC_CONN_OUT_OF_SVC)
1391da177e4SLinus Torvalds 		goto out;
1401da177e4SLinus Torvalds 	/*
1411da177e4SLinus Torvalds 	 * Postpone unassigning the connection from its SAP and returning the
1421da177e4SLinus Torvalds 	 * connection until all ACTIONs have been completely executed
1431da177e4SLinus Torvalds 	 */
1441da177e4SLinus Torvalds 	skb = alloc_skb(0, GFP_ATOMIC);
1451da177e4SLinus Torvalds 	if (!skb)
1461da177e4SLinus Torvalds 		goto out;
147d389424eSArnaldo Carvalho de Melo 	skb_set_owner_w(skb, sk);
1481da177e4SLinus Torvalds 	sk->sk_state  = TCP_CLOSING;
1491da177e4SLinus Torvalds 	ev	      = llc_conn_ev(skb);
1501da177e4SLinus Torvalds 	ev->type      = LLC_CONN_EV_TYPE_PRIM;
1511da177e4SLinus Torvalds 	ev->prim      = LLC_DISC_PRIM;
1521da177e4SLinus Torvalds 	ev->prim_type = LLC_PRIM_TYPE_REQ;
1531da177e4SLinus Torvalds 	rc = llc_conn_state_process(sk, skb);
1541da177e4SLinus Torvalds out:
1551da177e4SLinus Torvalds 	sock_put(sk);
1561da177e4SLinus Torvalds 	return rc;
1571da177e4SLinus Torvalds }
158