xref: /OK3568_Linux_fs/kernel/net/llc/af_llc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * af_llc.c - LLC User Interface SAPs
3*4882a593Smuzhiyun  * Description:
4*4882a593Smuzhiyun  *   Functions in this module are implementation of socket based llc
5*4882a593Smuzhiyun  *   communications for the Linux operating system. Support of llc class
6*4882a593Smuzhiyun  *   one and class two is provided via SOCK_DGRAM and SOCK_STREAM
7*4882a593Smuzhiyun  *   respectively.
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  *   An llc2 connection is (mac + sap), only one llc2 sap connection
10*4882a593Smuzhiyun  *   is allowed per mac. Though one sap may have multiple mac + sap
11*4882a593Smuzhiyun  *   connections.
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
14*4882a593Smuzhiyun  *		 2002-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  * This program can be redistributed or modified under the terms of the
17*4882a593Smuzhiyun  * GNU General Public License as published by the Free Software Foundation.
18*4882a593Smuzhiyun  * This program is distributed without any warranty or implied warranty
19*4882a593Smuzhiyun  * of merchantability or fitness for a particular purpose.
20*4882a593Smuzhiyun  *
21*4882a593Smuzhiyun  * See the GNU General Public License for more details.
22*4882a593Smuzhiyun  */
23*4882a593Smuzhiyun #include <linux/compiler.h>
24*4882a593Smuzhiyun #include <linux/kernel.h>
25*4882a593Smuzhiyun #include <linux/module.h>
26*4882a593Smuzhiyun #include <linux/rtnetlink.h>
27*4882a593Smuzhiyun #include <linux/init.h>
28*4882a593Smuzhiyun #include <linux/slab.h>
29*4882a593Smuzhiyun #include <linux/sched/signal.h>
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #include <net/llc.h>
32*4882a593Smuzhiyun #include <net/llc_sap.h>
33*4882a593Smuzhiyun #include <net/llc_pdu.h>
34*4882a593Smuzhiyun #include <net/llc_conn.h>
35*4882a593Smuzhiyun #include <net/tcp_states.h>
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /* remember: uninitialized global data is zeroed because its in .bss */
38*4882a593Smuzhiyun static u16 llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
39*4882a593Smuzhiyun static u16 llc_ui_sap_link_no_max[256];
40*4882a593Smuzhiyun static struct sockaddr_llc llc_ui_addrnull;
41*4882a593Smuzhiyun static const struct proto_ops llc_ui_ops;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun static bool llc_ui_wait_for_conn(struct sock *sk, long timeout);
44*4882a593Smuzhiyun static int llc_ui_wait_for_disc(struct sock *sk, long timeout);
45*4882a593Smuzhiyun static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout);
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun #if 0
48*4882a593Smuzhiyun #define dprintk(args...) printk(KERN_DEBUG args)
49*4882a593Smuzhiyun #else
50*4882a593Smuzhiyun #define dprintk(args...) do {} while (0)
51*4882a593Smuzhiyun #endif
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun /* Maybe we'll add some more in the future. */
54*4882a593Smuzhiyun #define LLC_CMSG_PKTINFO	1
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun /**
58*4882a593Smuzhiyun  *	llc_ui_next_link_no - return the next unused link number for a sap
59*4882a593Smuzhiyun  *	@sap: Address of sap to get link number from.
60*4882a593Smuzhiyun  *
61*4882a593Smuzhiyun  *	Return the next unused link number for a given sap.
62*4882a593Smuzhiyun  */
llc_ui_next_link_no(int sap)63*4882a593Smuzhiyun static inline u16 llc_ui_next_link_no(int sap)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun 	return llc_ui_sap_link_no_max[sap]++;
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun /**
69*4882a593Smuzhiyun  *	llc_proto_type - return eth protocol for ARP header type
70*4882a593Smuzhiyun  *	@arphrd: ARP header type.
71*4882a593Smuzhiyun  *
72*4882a593Smuzhiyun  *	Given an ARP header type return the corresponding ethernet protocol.
73*4882a593Smuzhiyun  */
llc_proto_type(u16 arphrd)74*4882a593Smuzhiyun static inline __be16 llc_proto_type(u16 arphrd)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	return htons(ETH_P_802_2);
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /**
80*4882a593Smuzhiyun  *	llc_ui_addr_null - determines if a address structure is null
81*4882a593Smuzhiyun  *	@addr: Address to test if null.
82*4882a593Smuzhiyun  */
llc_ui_addr_null(struct sockaddr_llc * addr)83*4882a593Smuzhiyun static inline u8 llc_ui_addr_null(struct sockaddr_llc *addr)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	return !memcmp(addr, &llc_ui_addrnull, sizeof(*addr));
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun /**
89*4882a593Smuzhiyun  *	llc_ui_header_len - return length of llc header based on operation
90*4882a593Smuzhiyun  *	@sk: Socket which contains a valid llc socket type.
91*4882a593Smuzhiyun  *	@addr: Complete sockaddr_llc structure received from the user.
92*4882a593Smuzhiyun  *
93*4882a593Smuzhiyun  *	Provide the length of the llc header depending on what kind of
94*4882a593Smuzhiyun  *	operation the user would like to perform and the type of socket.
95*4882a593Smuzhiyun  *	Returns the correct llc header length.
96*4882a593Smuzhiyun  */
llc_ui_header_len(struct sock * sk,struct sockaddr_llc * addr)97*4882a593Smuzhiyun static inline u8 llc_ui_header_len(struct sock *sk, struct sockaddr_llc *addr)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	u8 rc = LLC_PDU_LEN_U;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	if (addr->sllc_test)
102*4882a593Smuzhiyun 		rc = LLC_PDU_LEN_U;
103*4882a593Smuzhiyun 	else if (addr->sllc_xid)
104*4882a593Smuzhiyun 		/* We need to expand header to sizeof(struct llc_xid_info)
105*4882a593Smuzhiyun 		 * since llc_pdu_init_as_xid_cmd() sets 4,5,6 bytes of LLC header
106*4882a593Smuzhiyun 		 * as XID PDU. In llc_ui_sendmsg() we reserved header size and then
107*4882a593Smuzhiyun 		 * filled all other space with user data. If we won't reserve this
108*4882a593Smuzhiyun 		 * bytes, llc_pdu_init_as_xid_cmd() will overwrite user data
109*4882a593Smuzhiyun 		 */
110*4882a593Smuzhiyun 		rc = LLC_PDU_LEN_U_XID;
111*4882a593Smuzhiyun 	else if (sk->sk_type == SOCK_STREAM)
112*4882a593Smuzhiyun 		rc = LLC_PDU_LEN_I;
113*4882a593Smuzhiyun 	return rc;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun /**
117*4882a593Smuzhiyun  *	llc_ui_send_data - send data via reliable llc2 connection
118*4882a593Smuzhiyun  *	@sk: Connection the socket is using.
119*4882a593Smuzhiyun  *	@skb: Data the user wishes to send.
120*4882a593Smuzhiyun  *	@noblock: can we block waiting for data?
121*4882a593Smuzhiyun  *
122*4882a593Smuzhiyun  *	Send data via reliable llc2 connection.
123*4882a593Smuzhiyun  *	Returns 0 upon success, non-zero if action did not succeed.
124*4882a593Smuzhiyun  *
125*4882a593Smuzhiyun  *	This function always consumes a reference to the skb.
126*4882a593Smuzhiyun  */
llc_ui_send_data(struct sock * sk,struct sk_buff * skb,int noblock)127*4882a593Smuzhiyun static int llc_ui_send_data(struct sock* sk, struct sk_buff *skb, int noblock)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun 	struct llc_sock* llc = llc_sk(sk);
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	if (unlikely(llc_data_accept_state(llc->state) ||
132*4882a593Smuzhiyun 		     llc->remote_busy_flag ||
133*4882a593Smuzhiyun 		     llc->p_flag)) {
134*4882a593Smuzhiyun 		long timeout = sock_sndtimeo(sk, noblock);
135*4882a593Smuzhiyun 		int rc;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 		rc = llc_ui_wait_for_busy_core(sk, timeout);
138*4882a593Smuzhiyun 		if (rc) {
139*4882a593Smuzhiyun 			kfree_skb(skb);
140*4882a593Smuzhiyun 			return rc;
141*4882a593Smuzhiyun 		}
142*4882a593Smuzhiyun 	}
143*4882a593Smuzhiyun 	return llc_build_and_send_pkt(sk, skb);
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
llc_ui_sk_init(struct socket * sock,struct sock * sk)146*4882a593Smuzhiyun static void llc_ui_sk_init(struct socket *sock, struct sock *sk)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	sock_graft(sk, sock);
149*4882a593Smuzhiyun 	sk->sk_type	= sock->type;
150*4882a593Smuzhiyun 	sock->ops	= &llc_ui_ops;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun static struct proto llc_proto = {
154*4882a593Smuzhiyun 	.name	  = "LLC",
155*4882a593Smuzhiyun 	.owner	  = THIS_MODULE,
156*4882a593Smuzhiyun 	.obj_size = sizeof(struct llc_sock),
157*4882a593Smuzhiyun 	.slab_flags = SLAB_TYPESAFE_BY_RCU,
158*4882a593Smuzhiyun };
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun /**
161*4882a593Smuzhiyun  *	llc_ui_create - alloc and init a new llc_ui socket
162*4882a593Smuzhiyun  *	@net: network namespace (must be default network)
163*4882a593Smuzhiyun  *	@sock: Socket to initialize and attach allocated sk to.
164*4882a593Smuzhiyun  *	@protocol: Unused.
165*4882a593Smuzhiyun  *	@kern: on behalf of kernel or userspace
166*4882a593Smuzhiyun  *
167*4882a593Smuzhiyun  *	Allocate and initialize a new llc_ui socket, validate the user wants a
168*4882a593Smuzhiyun  *	socket type we have available.
169*4882a593Smuzhiyun  *	Returns 0 upon success, negative upon failure.
170*4882a593Smuzhiyun  */
llc_ui_create(struct net * net,struct socket * sock,int protocol,int kern)171*4882a593Smuzhiyun static int llc_ui_create(struct net *net, struct socket *sock, int protocol,
172*4882a593Smuzhiyun 			 int kern)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	struct sock *sk;
175*4882a593Smuzhiyun 	int rc = -ESOCKTNOSUPPORT;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	if (!ns_capable(net->user_ns, CAP_NET_RAW))
178*4882a593Smuzhiyun 		return -EPERM;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	if (!net_eq(net, &init_net))
181*4882a593Smuzhiyun 		return -EAFNOSUPPORT;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	if (likely(sock->type == SOCK_DGRAM || sock->type == SOCK_STREAM)) {
184*4882a593Smuzhiyun 		rc = -ENOMEM;
185*4882a593Smuzhiyun 		sk = llc_sk_alloc(net, PF_LLC, GFP_KERNEL, &llc_proto, kern);
186*4882a593Smuzhiyun 		if (sk) {
187*4882a593Smuzhiyun 			rc = 0;
188*4882a593Smuzhiyun 			llc_ui_sk_init(sock, sk);
189*4882a593Smuzhiyun 		}
190*4882a593Smuzhiyun 	}
191*4882a593Smuzhiyun 	return rc;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun /**
195*4882a593Smuzhiyun  *	llc_ui_release - shutdown socket
196*4882a593Smuzhiyun  *	@sock: Socket to release.
197*4882a593Smuzhiyun  *
198*4882a593Smuzhiyun  *	Shutdown and deallocate an existing socket.
199*4882a593Smuzhiyun  */
llc_ui_release(struct socket * sock)200*4882a593Smuzhiyun static int llc_ui_release(struct socket *sock)
201*4882a593Smuzhiyun {
202*4882a593Smuzhiyun 	struct sock *sk = sock->sk;
203*4882a593Smuzhiyun 	struct llc_sock *llc;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	if (unlikely(sk == NULL))
206*4882a593Smuzhiyun 		goto out;
207*4882a593Smuzhiyun 	sock_hold(sk);
208*4882a593Smuzhiyun 	lock_sock(sk);
209*4882a593Smuzhiyun 	llc = llc_sk(sk);
210*4882a593Smuzhiyun 	dprintk("%s: closing local(%02X) remote(%02X)\n", __func__,
211*4882a593Smuzhiyun 		llc->laddr.lsap, llc->daddr.lsap);
212*4882a593Smuzhiyun 	if (!llc_send_disc(sk))
213*4882a593Smuzhiyun 		llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo);
214*4882a593Smuzhiyun 	if (!sock_flag(sk, SOCK_ZAPPED)) {
215*4882a593Smuzhiyun 		struct llc_sap *sap = llc->sap;
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 		/* Hold this for release_sock(), so that llc_backlog_rcv()
218*4882a593Smuzhiyun 		 * could still use it.
219*4882a593Smuzhiyun 		 */
220*4882a593Smuzhiyun 		llc_sap_hold(sap);
221*4882a593Smuzhiyun 		llc_sap_remove_socket(llc->sap, sk);
222*4882a593Smuzhiyun 		release_sock(sk);
223*4882a593Smuzhiyun 		llc_sap_put(sap);
224*4882a593Smuzhiyun 	} else {
225*4882a593Smuzhiyun 		release_sock(sk);
226*4882a593Smuzhiyun 	}
227*4882a593Smuzhiyun 	if (llc->dev)
228*4882a593Smuzhiyun 		dev_put(llc->dev);
229*4882a593Smuzhiyun 	sock_put(sk);
230*4882a593Smuzhiyun 	llc_sk_free(sk);
231*4882a593Smuzhiyun out:
232*4882a593Smuzhiyun 	return 0;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun /**
236*4882a593Smuzhiyun  *	llc_ui_autoport - provide dynamically allocate SAP number
237*4882a593Smuzhiyun  *
238*4882a593Smuzhiyun  *	Provide the caller with a dynamically allocated SAP number according
239*4882a593Smuzhiyun  *	to the rules that are set in this function. Returns: 0, upon failure,
240*4882a593Smuzhiyun  *	SAP number otherwise.
241*4882a593Smuzhiyun  */
llc_ui_autoport(void)242*4882a593Smuzhiyun static int llc_ui_autoport(void)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun 	struct llc_sap *sap;
245*4882a593Smuzhiyun 	int i, tries = 0;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	while (tries < LLC_SAP_DYN_TRIES) {
248*4882a593Smuzhiyun 		for (i = llc_ui_sap_last_autoport;
249*4882a593Smuzhiyun 		     i < LLC_SAP_DYN_STOP; i += 2) {
250*4882a593Smuzhiyun 			sap = llc_sap_find(i);
251*4882a593Smuzhiyun 			if (!sap) {
252*4882a593Smuzhiyun 				llc_ui_sap_last_autoport = i + 2;
253*4882a593Smuzhiyun 				goto out;
254*4882a593Smuzhiyun 			}
255*4882a593Smuzhiyun 			llc_sap_put(sap);
256*4882a593Smuzhiyun 		}
257*4882a593Smuzhiyun 		llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
258*4882a593Smuzhiyun 		tries++;
259*4882a593Smuzhiyun 	}
260*4882a593Smuzhiyun 	i = 0;
261*4882a593Smuzhiyun out:
262*4882a593Smuzhiyun 	return i;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun /**
266*4882a593Smuzhiyun  *	llc_ui_autobind - automatically bind a socket to a sap
267*4882a593Smuzhiyun  *	@sock: socket to bind
268*4882a593Smuzhiyun  *	@addr: address to connect to
269*4882a593Smuzhiyun  *
270*4882a593Smuzhiyun  * 	Used by llc_ui_connect and llc_ui_sendmsg when the user hasn't
271*4882a593Smuzhiyun  * 	specifically used llc_ui_bind to bind to an specific address/sap
272*4882a593Smuzhiyun  *
273*4882a593Smuzhiyun  *	Returns: 0 upon success, negative otherwise.
274*4882a593Smuzhiyun  */
llc_ui_autobind(struct socket * sock,struct sockaddr_llc * addr)275*4882a593Smuzhiyun static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	struct sock *sk = sock->sk;
278*4882a593Smuzhiyun 	struct llc_sock *llc = llc_sk(sk);
279*4882a593Smuzhiyun 	struct net_device *dev = NULL;
280*4882a593Smuzhiyun 	struct llc_sap *sap;
281*4882a593Smuzhiyun 	int rc = -EINVAL;
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	if (!sock_flag(sk, SOCK_ZAPPED))
284*4882a593Smuzhiyun 		goto out;
285*4882a593Smuzhiyun 	if (!addr->sllc_arphrd)
286*4882a593Smuzhiyun 		addr->sllc_arphrd = ARPHRD_ETHER;
287*4882a593Smuzhiyun 	if (addr->sllc_arphrd != ARPHRD_ETHER)
288*4882a593Smuzhiyun 		goto out;
289*4882a593Smuzhiyun 	rc = -ENODEV;
290*4882a593Smuzhiyun 	if (sk->sk_bound_dev_if) {
291*4882a593Smuzhiyun 		dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if);
292*4882a593Smuzhiyun 		if (dev && addr->sllc_arphrd != dev->type) {
293*4882a593Smuzhiyun 			dev_put(dev);
294*4882a593Smuzhiyun 			dev = NULL;
295*4882a593Smuzhiyun 		}
296*4882a593Smuzhiyun 	} else
297*4882a593Smuzhiyun 		dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd);
298*4882a593Smuzhiyun 	if (!dev)
299*4882a593Smuzhiyun 		goto out;
300*4882a593Smuzhiyun 	rc = -EUSERS;
301*4882a593Smuzhiyun 	llc->laddr.lsap = llc_ui_autoport();
302*4882a593Smuzhiyun 	if (!llc->laddr.lsap)
303*4882a593Smuzhiyun 		goto out;
304*4882a593Smuzhiyun 	rc = -EBUSY; /* some other network layer is using the sap */
305*4882a593Smuzhiyun 	sap = llc_sap_open(llc->laddr.lsap, NULL);
306*4882a593Smuzhiyun 	if (!sap)
307*4882a593Smuzhiyun 		goto out;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	/* Note: We do not expect errors from this point. */
310*4882a593Smuzhiyun 	llc->dev = dev;
311*4882a593Smuzhiyun 	dev = NULL;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	memcpy(llc->laddr.mac, llc->dev->dev_addr, IFHWADDRLEN);
314*4882a593Smuzhiyun 	memcpy(&llc->addr, addr, sizeof(llc->addr));
315*4882a593Smuzhiyun 	/* assign new connection to its SAP */
316*4882a593Smuzhiyun 	llc_sap_add_socket(sap, sk);
317*4882a593Smuzhiyun 	sock_reset_flag(sk, SOCK_ZAPPED);
318*4882a593Smuzhiyun 	rc = 0;
319*4882a593Smuzhiyun out:
320*4882a593Smuzhiyun 	dev_put(dev);
321*4882a593Smuzhiyun 	return rc;
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun /**
325*4882a593Smuzhiyun  *	llc_ui_bind - bind a socket to a specific address.
326*4882a593Smuzhiyun  *	@sock: Socket to bind an address to.
327*4882a593Smuzhiyun  *	@uaddr: Address the user wants the socket bound to.
328*4882a593Smuzhiyun  *	@addrlen: Length of the uaddr structure.
329*4882a593Smuzhiyun  *
330*4882a593Smuzhiyun  *	Bind a socket to a specific address. For llc a user is able to bind to
331*4882a593Smuzhiyun  *	a specific sap only or mac + sap.
332*4882a593Smuzhiyun  *	If the user desires to bind to a specific mac + sap, it is possible to
333*4882a593Smuzhiyun  *	have multiple sap connections via multiple macs.
334*4882a593Smuzhiyun  *	Bind and autobind for that matter must enforce the correct sap usage
335*4882a593Smuzhiyun  *	otherwise all hell will break loose.
336*4882a593Smuzhiyun  *	Returns: 0 upon success, negative otherwise.
337*4882a593Smuzhiyun  */
llc_ui_bind(struct socket * sock,struct sockaddr * uaddr,int addrlen)338*4882a593Smuzhiyun static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
339*4882a593Smuzhiyun {
340*4882a593Smuzhiyun 	struct sockaddr_llc *addr = (struct sockaddr_llc *)uaddr;
341*4882a593Smuzhiyun 	struct sock *sk = sock->sk;
342*4882a593Smuzhiyun 	struct llc_sock *llc = llc_sk(sk);
343*4882a593Smuzhiyun 	struct net_device *dev = NULL;
344*4882a593Smuzhiyun 	struct llc_sap *sap;
345*4882a593Smuzhiyun 	int rc = -EINVAL;
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	lock_sock(sk);
348*4882a593Smuzhiyun 	if (unlikely(!sock_flag(sk, SOCK_ZAPPED) || addrlen != sizeof(*addr)))
349*4882a593Smuzhiyun 		goto out;
350*4882a593Smuzhiyun 	rc = -EAFNOSUPPORT;
351*4882a593Smuzhiyun 	if (!addr->sllc_arphrd)
352*4882a593Smuzhiyun 		addr->sllc_arphrd = ARPHRD_ETHER;
353*4882a593Smuzhiyun 	if (unlikely(addr->sllc_family != AF_LLC || addr->sllc_arphrd != ARPHRD_ETHER))
354*4882a593Smuzhiyun 		goto out;
355*4882a593Smuzhiyun 	dprintk("%s: binding %02X\n", __func__, addr->sllc_sap);
356*4882a593Smuzhiyun 	rc = -ENODEV;
357*4882a593Smuzhiyun 	rcu_read_lock();
358*4882a593Smuzhiyun 	if (sk->sk_bound_dev_if) {
359*4882a593Smuzhiyun 		dev = dev_get_by_index_rcu(&init_net, sk->sk_bound_dev_if);
360*4882a593Smuzhiyun 		if (dev) {
361*4882a593Smuzhiyun 			if (is_zero_ether_addr(addr->sllc_mac))
362*4882a593Smuzhiyun 				memcpy(addr->sllc_mac, dev->dev_addr,
363*4882a593Smuzhiyun 				       IFHWADDRLEN);
364*4882a593Smuzhiyun 			if (addr->sllc_arphrd != dev->type ||
365*4882a593Smuzhiyun 			    !ether_addr_equal(addr->sllc_mac,
366*4882a593Smuzhiyun 					      dev->dev_addr)) {
367*4882a593Smuzhiyun 				rc = -EINVAL;
368*4882a593Smuzhiyun 				dev = NULL;
369*4882a593Smuzhiyun 			}
370*4882a593Smuzhiyun 		}
371*4882a593Smuzhiyun 	} else {
372*4882a593Smuzhiyun 		dev = dev_getbyhwaddr_rcu(&init_net, addr->sllc_arphrd,
373*4882a593Smuzhiyun 					   addr->sllc_mac);
374*4882a593Smuzhiyun 	}
375*4882a593Smuzhiyun 	if (dev)
376*4882a593Smuzhiyun 		dev_hold(dev);
377*4882a593Smuzhiyun 	rcu_read_unlock();
378*4882a593Smuzhiyun 	if (!dev)
379*4882a593Smuzhiyun 		goto out;
380*4882a593Smuzhiyun 	if (!addr->sllc_sap) {
381*4882a593Smuzhiyun 		rc = -EUSERS;
382*4882a593Smuzhiyun 		addr->sllc_sap = llc_ui_autoport();
383*4882a593Smuzhiyun 		if (!addr->sllc_sap)
384*4882a593Smuzhiyun 			goto out;
385*4882a593Smuzhiyun 	}
386*4882a593Smuzhiyun 	sap = llc_sap_find(addr->sllc_sap);
387*4882a593Smuzhiyun 	if (!sap) {
388*4882a593Smuzhiyun 		sap = llc_sap_open(addr->sllc_sap, NULL);
389*4882a593Smuzhiyun 		rc = -EBUSY; /* some other network layer is using the sap */
390*4882a593Smuzhiyun 		if (!sap)
391*4882a593Smuzhiyun 			goto out;
392*4882a593Smuzhiyun 	} else {
393*4882a593Smuzhiyun 		struct llc_addr laddr, daddr;
394*4882a593Smuzhiyun 		struct sock *ask;
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 		memset(&laddr, 0, sizeof(laddr));
397*4882a593Smuzhiyun 		memset(&daddr, 0, sizeof(daddr));
398*4882a593Smuzhiyun 		/*
399*4882a593Smuzhiyun 		 * FIXME: check if the address is multicast,
400*4882a593Smuzhiyun 		 * 	  only SOCK_DGRAM can do this.
401*4882a593Smuzhiyun 		 */
402*4882a593Smuzhiyun 		memcpy(laddr.mac, addr->sllc_mac, IFHWADDRLEN);
403*4882a593Smuzhiyun 		laddr.lsap = addr->sllc_sap;
404*4882a593Smuzhiyun 		rc = -EADDRINUSE; /* mac + sap clash. */
405*4882a593Smuzhiyun 		ask = llc_lookup_established(sap, &daddr, &laddr);
406*4882a593Smuzhiyun 		if (ask) {
407*4882a593Smuzhiyun 			sock_put(ask);
408*4882a593Smuzhiyun 			goto out_put;
409*4882a593Smuzhiyun 		}
410*4882a593Smuzhiyun 	}
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	/* Note: We do not expect errors from this point. */
413*4882a593Smuzhiyun 	llc->dev = dev;
414*4882a593Smuzhiyun 	dev = NULL;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	llc->laddr.lsap = addr->sllc_sap;
417*4882a593Smuzhiyun 	memcpy(llc->laddr.mac, addr->sllc_mac, IFHWADDRLEN);
418*4882a593Smuzhiyun 	memcpy(&llc->addr, addr, sizeof(llc->addr));
419*4882a593Smuzhiyun 	/* assign new connection to its SAP */
420*4882a593Smuzhiyun 	llc_sap_add_socket(sap, sk);
421*4882a593Smuzhiyun 	sock_reset_flag(sk, SOCK_ZAPPED);
422*4882a593Smuzhiyun 	rc = 0;
423*4882a593Smuzhiyun out_put:
424*4882a593Smuzhiyun 	llc_sap_put(sap);
425*4882a593Smuzhiyun out:
426*4882a593Smuzhiyun 	dev_put(dev);
427*4882a593Smuzhiyun 	release_sock(sk);
428*4882a593Smuzhiyun 	return rc;
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun /**
432*4882a593Smuzhiyun  *	llc_ui_shutdown - shutdown a connect llc2 socket.
433*4882a593Smuzhiyun  *	@sock: Socket to shutdown.
434*4882a593Smuzhiyun  *	@how: What part of the socket to shutdown.
435*4882a593Smuzhiyun  *
436*4882a593Smuzhiyun  *	Shutdown a connected llc2 socket. Currently this function only supports
437*4882a593Smuzhiyun  *	shutting down both sends and receives (2), we could probably make this
438*4882a593Smuzhiyun  *	function such that a user can shutdown only half the connection but not
439*4882a593Smuzhiyun  *	right now.
440*4882a593Smuzhiyun  *	Returns: 0 upon success, negative otherwise.
441*4882a593Smuzhiyun  */
llc_ui_shutdown(struct socket * sock,int how)442*4882a593Smuzhiyun static int llc_ui_shutdown(struct socket *sock, int how)
443*4882a593Smuzhiyun {
444*4882a593Smuzhiyun 	struct sock *sk = sock->sk;
445*4882a593Smuzhiyun 	int rc = -ENOTCONN;
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 	lock_sock(sk);
448*4882a593Smuzhiyun 	if (unlikely(sk->sk_state != TCP_ESTABLISHED))
449*4882a593Smuzhiyun 		goto out;
450*4882a593Smuzhiyun 	rc = -EINVAL;
451*4882a593Smuzhiyun 	if (how != 2)
452*4882a593Smuzhiyun 		goto out;
453*4882a593Smuzhiyun 	rc = llc_send_disc(sk);
454*4882a593Smuzhiyun 	if (!rc)
455*4882a593Smuzhiyun 		rc = llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo);
456*4882a593Smuzhiyun 	/* Wake up anyone sleeping in poll */
457*4882a593Smuzhiyun 	sk->sk_state_change(sk);
458*4882a593Smuzhiyun out:
459*4882a593Smuzhiyun 	release_sock(sk);
460*4882a593Smuzhiyun 	return rc;
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun /**
464*4882a593Smuzhiyun  *	llc_ui_connect - Connect to a remote llc2 mac + sap.
465*4882a593Smuzhiyun  *	@sock: Socket which will be connected to the remote destination.
466*4882a593Smuzhiyun  *	@uaddr: Remote and possibly the local address of the new connection.
467*4882a593Smuzhiyun  *	@addrlen: Size of uaddr structure.
468*4882a593Smuzhiyun  *	@flags: Operational flags specified by the user.
469*4882a593Smuzhiyun  *
470*4882a593Smuzhiyun  *	Connect to a remote llc2 mac + sap. The caller must specify the
471*4882a593Smuzhiyun  *	destination mac and address to connect to. If the user hasn't previously
472*4882a593Smuzhiyun  *	called bind(2) with a smac the address of the first interface of the
473*4882a593Smuzhiyun  *	specified arp type will be used.
474*4882a593Smuzhiyun  *	This function will autobind if user did not previously call bind.
475*4882a593Smuzhiyun  *	Returns: 0 upon success, negative otherwise.
476*4882a593Smuzhiyun  */
llc_ui_connect(struct socket * sock,struct sockaddr * uaddr,int addrlen,int flags)477*4882a593Smuzhiyun static int llc_ui_connect(struct socket *sock, struct sockaddr *uaddr,
478*4882a593Smuzhiyun 			  int addrlen, int flags)
479*4882a593Smuzhiyun {
480*4882a593Smuzhiyun 	struct sock *sk = sock->sk;
481*4882a593Smuzhiyun 	struct llc_sock *llc = llc_sk(sk);
482*4882a593Smuzhiyun 	struct sockaddr_llc *addr = (struct sockaddr_llc *)uaddr;
483*4882a593Smuzhiyun 	int rc = -EINVAL;
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	lock_sock(sk);
486*4882a593Smuzhiyun 	if (unlikely(addrlen != sizeof(*addr)))
487*4882a593Smuzhiyun 		goto out;
488*4882a593Smuzhiyun 	rc = -EAFNOSUPPORT;
489*4882a593Smuzhiyun 	if (unlikely(addr->sllc_family != AF_LLC))
490*4882a593Smuzhiyun 		goto out;
491*4882a593Smuzhiyun 	if (unlikely(sk->sk_type != SOCK_STREAM))
492*4882a593Smuzhiyun 		goto out;
493*4882a593Smuzhiyun 	rc = -EALREADY;
494*4882a593Smuzhiyun 	if (unlikely(sock->state == SS_CONNECTING))
495*4882a593Smuzhiyun 		goto out;
496*4882a593Smuzhiyun 	/* bind connection to sap if user hasn't done it. */
497*4882a593Smuzhiyun 	if (sock_flag(sk, SOCK_ZAPPED)) {
498*4882a593Smuzhiyun 		/* bind to sap with null dev, exclusive */
499*4882a593Smuzhiyun 		rc = llc_ui_autobind(sock, addr);
500*4882a593Smuzhiyun 		if (rc)
501*4882a593Smuzhiyun 			goto out;
502*4882a593Smuzhiyun 	}
503*4882a593Smuzhiyun 	llc->daddr.lsap = addr->sllc_sap;
504*4882a593Smuzhiyun 	memcpy(llc->daddr.mac, addr->sllc_mac, IFHWADDRLEN);
505*4882a593Smuzhiyun 	sock->state = SS_CONNECTING;
506*4882a593Smuzhiyun 	sk->sk_state   = TCP_SYN_SENT;
507*4882a593Smuzhiyun 	llc->link   = llc_ui_next_link_no(llc->sap->laddr.lsap);
508*4882a593Smuzhiyun 	rc = llc_establish_connection(sk, llc->dev->dev_addr,
509*4882a593Smuzhiyun 				      addr->sllc_mac, addr->sllc_sap);
510*4882a593Smuzhiyun 	if (rc) {
511*4882a593Smuzhiyun 		dprintk("%s: llc_ui_send_conn failed :-(\n", __func__);
512*4882a593Smuzhiyun 		sock->state  = SS_UNCONNECTED;
513*4882a593Smuzhiyun 		sk->sk_state = TCP_CLOSE;
514*4882a593Smuzhiyun 		goto out;
515*4882a593Smuzhiyun 	}
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 	if (sk->sk_state == TCP_SYN_SENT) {
518*4882a593Smuzhiyun 		const long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 		if (!timeo || !llc_ui_wait_for_conn(sk, timeo))
521*4882a593Smuzhiyun 			goto out;
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 		rc = sock_intr_errno(timeo);
524*4882a593Smuzhiyun 		if (signal_pending(current))
525*4882a593Smuzhiyun 			goto out;
526*4882a593Smuzhiyun 	}
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	if (sk->sk_state == TCP_CLOSE)
529*4882a593Smuzhiyun 		goto sock_error;
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun 	sock->state = SS_CONNECTED;
532*4882a593Smuzhiyun 	rc = 0;
533*4882a593Smuzhiyun out:
534*4882a593Smuzhiyun 	release_sock(sk);
535*4882a593Smuzhiyun 	return rc;
536*4882a593Smuzhiyun sock_error:
537*4882a593Smuzhiyun 	rc = sock_error(sk) ? : -ECONNABORTED;
538*4882a593Smuzhiyun 	sock->state = SS_UNCONNECTED;
539*4882a593Smuzhiyun 	goto out;
540*4882a593Smuzhiyun }
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun /**
543*4882a593Smuzhiyun  *	llc_ui_listen - allow a normal socket to accept incoming connections
544*4882a593Smuzhiyun  *	@sock: Socket to allow incoming connections on.
545*4882a593Smuzhiyun  *	@backlog: Number of connections to queue.
546*4882a593Smuzhiyun  *
547*4882a593Smuzhiyun  *	Allow a normal socket to accept incoming connections.
548*4882a593Smuzhiyun  *	Returns 0 upon success, negative otherwise.
549*4882a593Smuzhiyun  */
llc_ui_listen(struct socket * sock,int backlog)550*4882a593Smuzhiyun static int llc_ui_listen(struct socket *sock, int backlog)
551*4882a593Smuzhiyun {
552*4882a593Smuzhiyun 	struct sock *sk = sock->sk;
553*4882a593Smuzhiyun 	int rc = -EINVAL;
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	lock_sock(sk);
556*4882a593Smuzhiyun 	if (unlikely(sock->state != SS_UNCONNECTED))
557*4882a593Smuzhiyun 		goto out;
558*4882a593Smuzhiyun 	rc = -EOPNOTSUPP;
559*4882a593Smuzhiyun 	if (unlikely(sk->sk_type != SOCK_STREAM))
560*4882a593Smuzhiyun 		goto out;
561*4882a593Smuzhiyun 	rc = -EAGAIN;
562*4882a593Smuzhiyun 	if (sock_flag(sk, SOCK_ZAPPED))
563*4882a593Smuzhiyun 		goto out;
564*4882a593Smuzhiyun 	rc = 0;
565*4882a593Smuzhiyun 	if (!(unsigned int)backlog)	/* BSDism */
566*4882a593Smuzhiyun 		backlog = 1;
567*4882a593Smuzhiyun 	sk->sk_max_ack_backlog = backlog;
568*4882a593Smuzhiyun 	if (sk->sk_state != TCP_LISTEN) {
569*4882a593Smuzhiyun 		sk->sk_ack_backlog = 0;
570*4882a593Smuzhiyun 		sk->sk_state	   = TCP_LISTEN;
571*4882a593Smuzhiyun 	}
572*4882a593Smuzhiyun 	sk->sk_socket->flags |= __SO_ACCEPTCON;
573*4882a593Smuzhiyun out:
574*4882a593Smuzhiyun 	release_sock(sk);
575*4882a593Smuzhiyun 	return rc;
576*4882a593Smuzhiyun }
577*4882a593Smuzhiyun 
llc_ui_wait_for_disc(struct sock * sk,long timeout)578*4882a593Smuzhiyun static int llc_ui_wait_for_disc(struct sock *sk, long timeout)
579*4882a593Smuzhiyun {
580*4882a593Smuzhiyun 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
581*4882a593Smuzhiyun 	int rc = 0;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	add_wait_queue(sk_sleep(sk), &wait);
584*4882a593Smuzhiyun 	while (1) {
585*4882a593Smuzhiyun 		if (sk_wait_event(sk, &timeout, sk->sk_state == TCP_CLOSE, &wait))
586*4882a593Smuzhiyun 			break;
587*4882a593Smuzhiyun 		rc = -ERESTARTSYS;
588*4882a593Smuzhiyun 		if (signal_pending(current))
589*4882a593Smuzhiyun 			break;
590*4882a593Smuzhiyun 		rc = -EAGAIN;
591*4882a593Smuzhiyun 		if (!timeout)
592*4882a593Smuzhiyun 			break;
593*4882a593Smuzhiyun 		rc = 0;
594*4882a593Smuzhiyun 	}
595*4882a593Smuzhiyun 	remove_wait_queue(sk_sleep(sk), &wait);
596*4882a593Smuzhiyun 	return rc;
597*4882a593Smuzhiyun }
598*4882a593Smuzhiyun 
llc_ui_wait_for_conn(struct sock * sk,long timeout)599*4882a593Smuzhiyun static bool llc_ui_wait_for_conn(struct sock *sk, long timeout)
600*4882a593Smuzhiyun {
601*4882a593Smuzhiyun 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	add_wait_queue(sk_sleep(sk), &wait);
604*4882a593Smuzhiyun 	while (1) {
605*4882a593Smuzhiyun 		if (sk_wait_event(sk, &timeout, sk->sk_state != TCP_SYN_SENT, &wait))
606*4882a593Smuzhiyun 			break;
607*4882a593Smuzhiyun 		if (signal_pending(current) || !timeout)
608*4882a593Smuzhiyun 			break;
609*4882a593Smuzhiyun 	}
610*4882a593Smuzhiyun 	remove_wait_queue(sk_sleep(sk), &wait);
611*4882a593Smuzhiyun 	return timeout;
612*4882a593Smuzhiyun }
613*4882a593Smuzhiyun 
llc_ui_wait_for_busy_core(struct sock * sk,long timeout)614*4882a593Smuzhiyun static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout)
615*4882a593Smuzhiyun {
616*4882a593Smuzhiyun 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
617*4882a593Smuzhiyun 	struct llc_sock *llc = llc_sk(sk);
618*4882a593Smuzhiyun 	int rc;
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 	add_wait_queue(sk_sleep(sk), &wait);
621*4882a593Smuzhiyun 	while (1) {
622*4882a593Smuzhiyun 		rc = 0;
623*4882a593Smuzhiyun 		if (sk_wait_event(sk, &timeout,
624*4882a593Smuzhiyun 				  (sk->sk_shutdown & RCV_SHUTDOWN) ||
625*4882a593Smuzhiyun 				  (!llc_data_accept_state(llc->state) &&
626*4882a593Smuzhiyun 				   !llc->remote_busy_flag &&
627*4882a593Smuzhiyun 				   !llc->p_flag), &wait))
628*4882a593Smuzhiyun 			break;
629*4882a593Smuzhiyun 		rc = -ERESTARTSYS;
630*4882a593Smuzhiyun 		if (signal_pending(current))
631*4882a593Smuzhiyun 			break;
632*4882a593Smuzhiyun 		rc = -EAGAIN;
633*4882a593Smuzhiyun 		if (!timeout)
634*4882a593Smuzhiyun 			break;
635*4882a593Smuzhiyun 	}
636*4882a593Smuzhiyun 	remove_wait_queue(sk_sleep(sk), &wait);
637*4882a593Smuzhiyun 	return rc;
638*4882a593Smuzhiyun }
639*4882a593Smuzhiyun 
llc_wait_data(struct sock * sk,long timeo)640*4882a593Smuzhiyun static int llc_wait_data(struct sock *sk, long timeo)
641*4882a593Smuzhiyun {
642*4882a593Smuzhiyun 	int rc;
643*4882a593Smuzhiyun 
644*4882a593Smuzhiyun 	while (1) {
645*4882a593Smuzhiyun 		/*
646*4882a593Smuzhiyun 		 * POSIX 1003.1g mandates this order.
647*4882a593Smuzhiyun 		 */
648*4882a593Smuzhiyun 		rc = sock_error(sk);
649*4882a593Smuzhiyun 		if (rc)
650*4882a593Smuzhiyun 			break;
651*4882a593Smuzhiyun 		rc = 0;
652*4882a593Smuzhiyun 		if (sk->sk_shutdown & RCV_SHUTDOWN)
653*4882a593Smuzhiyun 			break;
654*4882a593Smuzhiyun 		rc = -EAGAIN;
655*4882a593Smuzhiyun 		if (!timeo)
656*4882a593Smuzhiyun 			break;
657*4882a593Smuzhiyun 		rc = sock_intr_errno(timeo);
658*4882a593Smuzhiyun 		if (signal_pending(current))
659*4882a593Smuzhiyun 			break;
660*4882a593Smuzhiyun 		rc = 0;
661*4882a593Smuzhiyun 		if (sk_wait_data(sk, &timeo, NULL))
662*4882a593Smuzhiyun 			break;
663*4882a593Smuzhiyun 	}
664*4882a593Smuzhiyun 	return rc;
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun 
llc_cmsg_rcv(struct msghdr * msg,struct sk_buff * skb)667*4882a593Smuzhiyun static void llc_cmsg_rcv(struct msghdr *msg, struct sk_buff *skb)
668*4882a593Smuzhiyun {
669*4882a593Smuzhiyun 	struct llc_sock *llc = llc_sk(skb->sk);
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun 	if (llc->cmsg_flags & LLC_CMSG_PKTINFO) {
672*4882a593Smuzhiyun 		struct llc_pktinfo info;
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 		memset(&info, 0, sizeof(info));
675*4882a593Smuzhiyun 		info.lpi_ifindex = llc_sk(skb->sk)->dev->ifindex;
676*4882a593Smuzhiyun 		llc_pdu_decode_dsap(skb, &info.lpi_sap);
677*4882a593Smuzhiyun 		llc_pdu_decode_da(skb, info.lpi_mac);
678*4882a593Smuzhiyun 		put_cmsg(msg, SOL_LLC, LLC_OPT_PKTINFO, sizeof(info), &info);
679*4882a593Smuzhiyun 	}
680*4882a593Smuzhiyun }
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun /**
683*4882a593Smuzhiyun  *	llc_ui_accept - accept a new incoming connection.
684*4882a593Smuzhiyun  *	@sock: Socket which connections arrive on.
685*4882a593Smuzhiyun  *	@newsock: Socket to move incoming connection to.
686*4882a593Smuzhiyun  *	@flags: User specified operational flags.
687*4882a593Smuzhiyun  *	@kern: If the socket is kernel internal
688*4882a593Smuzhiyun  *
689*4882a593Smuzhiyun  *	Accept a new incoming connection.
690*4882a593Smuzhiyun  *	Returns 0 upon success, negative otherwise.
691*4882a593Smuzhiyun  */
llc_ui_accept(struct socket * sock,struct socket * newsock,int flags,bool kern)692*4882a593Smuzhiyun static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags,
693*4882a593Smuzhiyun 			 bool kern)
694*4882a593Smuzhiyun {
695*4882a593Smuzhiyun 	struct sock *sk = sock->sk, *newsk;
696*4882a593Smuzhiyun 	struct llc_sock *llc, *newllc;
697*4882a593Smuzhiyun 	struct sk_buff *skb;
698*4882a593Smuzhiyun 	int rc = -EOPNOTSUPP;
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun 	dprintk("%s: accepting on %02X\n", __func__,
701*4882a593Smuzhiyun 		llc_sk(sk)->laddr.lsap);
702*4882a593Smuzhiyun 	lock_sock(sk);
703*4882a593Smuzhiyun 	if (unlikely(sk->sk_type != SOCK_STREAM))
704*4882a593Smuzhiyun 		goto out;
705*4882a593Smuzhiyun 	rc = -EINVAL;
706*4882a593Smuzhiyun 	if (unlikely(sock->state != SS_UNCONNECTED ||
707*4882a593Smuzhiyun 		     sk->sk_state != TCP_LISTEN))
708*4882a593Smuzhiyun 		goto out;
709*4882a593Smuzhiyun 	/* wait for a connection to arrive. */
710*4882a593Smuzhiyun 	if (skb_queue_empty(&sk->sk_receive_queue)) {
711*4882a593Smuzhiyun 		rc = llc_wait_data(sk, sk->sk_rcvtimeo);
712*4882a593Smuzhiyun 		if (rc)
713*4882a593Smuzhiyun 			goto out;
714*4882a593Smuzhiyun 	}
715*4882a593Smuzhiyun 	dprintk("%s: got a new connection on %02X\n", __func__,
716*4882a593Smuzhiyun 		llc_sk(sk)->laddr.lsap);
717*4882a593Smuzhiyun 	skb = skb_dequeue(&sk->sk_receive_queue);
718*4882a593Smuzhiyun 	rc = -EINVAL;
719*4882a593Smuzhiyun 	if (!skb->sk)
720*4882a593Smuzhiyun 		goto frees;
721*4882a593Smuzhiyun 	rc = 0;
722*4882a593Smuzhiyun 	newsk = skb->sk;
723*4882a593Smuzhiyun 	/* attach connection to a new socket. */
724*4882a593Smuzhiyun 	llc_ui_sk_init(newsock, newsk);
725*4882a593Smuzhiyun 	sock_reset_flag(newsk, SOCK_ZAPPED);
726*4882a593Smuzhiyun 	newsk->sk_state		= TCP_ESTABLISHED;
727*4882a593Smuzhiyun 	newsock->state		= SS_CONNECTED;
728*4882a593Smuzhiyun 	llc			= llc_sk(sk);
729*4882a593Smuzhiyun 	newllc			= llc_sk(newsk);
730*4882a593Smuzhiyun 	memcpy(&newllc->addr, &llc->addr, sizeof(newllc->addr));
731*4882a593Smuzhiyun 	newllc->link = llc_ui_next_link_no(newllc->laddr.lsap);
732*4882a593Smuzhiyun 
733*4882a593Smuzhiyun 	/* put original socket back into a clean listen state. */
734*4882a593Smuzhiyun 	sk->sk_state = TCP_LISTEN;
735*4882a593Smuzhiyun 	sk_acceptq_removed(sk);
736*4882a593Smuzhiyun 	dprintk("%s: ok success on %02X, client on %02X\n", __func__,
737*4882a593Smuzhiyun 		llc_sk(sk)->addr.sllc_sap, newllc->daddr.lsap);
738*4882a593Smuzhiyun frees:
739*4882a593Smuzhiyun 	kfree_skb(skb);
740*4882a593Smuzhiyun out:
741*4882a593Smuzhiyun 	release_sock(sk);
742*4882a593Smuzhiyun 	return rc;
743*4882a593Smuzhiyun }
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun /**
746*4882a593Smuzhiyun  *	llc_ui_recvmsg - copy received data to the socket user.
747*4882a593Smuzhiyun  *	@sock: Socket to copy data from.
748*4882a593Smuzhiyun  *	@msg: Various user space related information.
749*4882a593Smuzhiyun  *	@len: Size of user buffer.
750*4882a593Smuzhiyun  *	@flags: User specified flags.
751*4882a593Smuzhiyun  *
752*4882a593Smuzhiyun  *	Copy received data to the socket user.
753*4882a593Smuzhiyun  *	Returns non-negative upon success, negative otherwise.
754*4882a593Smuzhiyun  */
llc_ui_recvmsg(struct socket * sock,struct msghdr * msg,size_t len,int flags)755*4882a593Smuzhiyun static int llc_ui_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
756*4882a593Smuzhiyun 			  int flags)
757*4882a593Smuzhiyun {
758*4882a593Smuzhiyun 	DECLARE_SOCKADDR(struct sockaddr_llc *, uaddr, msg->msg_name);
759*4882a593Smuzhiyun 	const int nonblock = flags & MSG_DONTWAIT;
760*4882a593Smuzhiyun 	struct sk_buff *skb = NULL;
761*4882a593Smuzhiyun 	struct sock *sk = sock->sk;
762*4882a593Smuzhiyun 	struct llc_sock *llc = llc_sk(sk);
763*4882a593Smuzhiyun 	size_t copied = 0;
764*4882a593Smuzhiyun 	u32 peek_seq = 0;
765*4882a593Smuzhiyun 	u32 *seq, skb_len;
766*4882a593Smuzhiyun 	unsigned long used;
767*4882a593Smuzhiyun 	int target;	/* Read at least this many bytes */
768*4882a593Smuzhiyun 	long timeo;
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun 	lock_sock(sk);
771*4882a593Smuzhiyun 	copied = -ENOTCONN;
772*4882a593Smuzhiyun 	if (unlikely(sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN))
773*4882a593Smuzhiyun 		goto out;
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	timeo = sock_rcvtimeo(sk, nonblock);
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 	seq = &llc->copied_seq;
778*4882a593Smuzhiyun 	if (flags & MSG_PEEK) {
779*4882a593Smuzhiyun 		peek_seq = llc->copied_seq;
780*4882a593Smuzhiyun 		seq = &peek_seq;
781*4882a593Smuzhiyun 	}
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
784*4882a593Smuzhiyun 	copied = 0;
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	do {
787*4882a593Smuzhiyun 		u32 offset;
788*4882a593Smuzhiyun 
789*4882a593Smuzhiyun 		/*
790*4882a593Smuzhiyun 		 * We need to check signals first, to get correct SIGURG
791*4882a593Smuzhiyun 		 * handling. FIXME: Need to check this doesn't impact 1003.1g
792*4882a593Smuzhiyun 		 * and move it down to the bottom of the loop
793*4882a593Smuzhiyun 		 */
794*4882a593Smuzhiyun 		if (signal_pending(current)) {
795*4882a593Smuzhiyun 			if (copied)
796*4882a593Smuzhiyun 				break;
797*4882a593Smuzhiyun 			copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
798*4882a593Smuzhiyun 			break;
799*4882a593Smuzhiyun 		}
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun 		/* Next get a buffer. */
802*4882a593Smuzhiyun 
803*4882a593Smuzhiyun 		skb = skb_peek(&sk->sk_receive_queue);
804*4882a593Smuzhiyun 		if (skb) {
805*4882a593Smuzhiyun 			offset = *seq;
806*4882a593Smuzhiyun 			goto found_ok_skb;
807*4882a593Smuzhiyun 		}
808*4882a593Smuzhiyun 		/* Well, if we have backlog, try to process it now yet. */
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 		if (copied >= target && !READ_ONCE(sk->sk_backlog.tail))
811*4882a593Smuzhiyun 			break;
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 		if (copied) {
814*4882a593Smuzhiyun 			if (sk->sk_err ||
815*4882a593Smuzhiyun 			    sk->sk_state == TCP_CLOSE ||
816*4882a593Smuzhiyun 			    (sk->sk_shutdown & RCV_SHUTDOWN) ||
817*4882a593Smuzhiyun 			    !timeo ||
818*4882a593Smuzhiyun 			    (flags & MSG_PEEK))
819*4882a593Smuzhiyun 				break;
820*4882a593Smuzhiyun 		} else {
821*4882a593Smuzhiyun 			if (sock_flag(sk, SOCK_DONE))
822*4882a593Smuzhiyun 				break;
823*4882a593Smuzhiyun 
824*4882a593Smuzhiyun 			if (sk->sk_err) {
825*4882a593Smuzhiyun 				copied = sock_error(sk);
826*4882a593Smuzhiyun 				break;
827*4882a593Smuzhiyun 			}
828*4882a593Smuzhiyun 			if (sk->sk_shutdown & RCV_SHUTDOWN)
829*4882a593Smuzhiyun 				break;
830*4882a593Smuzhiyun 
831*4882a593Smuzhiyun 			if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSE) {
832*4882a593Smuzhiyun 				if (!sock_flag(sk, SOCK_DONE)) {
833*4882a593Smuzhiyun 					/*
834*4882a593Smuzhiyun 					 * This occurs when user tries to read
835*4882a593Smuzhiyun 					 * from never connected socket.
836*4882a593Smuzhiyun 					 */
837*4882a593Smuzhiyun 					copied = -ENOTCONN;
838*4882a593Smuzhiyun 					break;
839*4882a593Smuzhiyun 				}
840*4882a593Smuzhiyun 				break;
841*4882a593Smuzhiyun 			}
842*4882a593Smuzhiyun 			if (!timeo) {
843*4882a593Smuzhiyun 				copied = -EAGAIN;
844*4882a593Smuzhiyun 				break;
845*4882a593Smuzhiyun 			}
846*4882a593Smuzhiyun 		}
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun 		if (copied >= target) { /* Do not sleep, just process backlog. */
849*4882a593Smuzhiyun 			release_sock(sk);
850*4882a593Smuzhiyun 			lock_sock(sk);
851*4882a593Smuzhiyun 		} else
852*4882a593Smuzhiyun 			sk_wait_data(sk, &timeo, NULL);
853*4882a593Smuzhiyun 
854*4882a593Smuzhiyun 		if ((flags & MSG_PEEK) && peek_seq != llc->copied_seq) {
855*4882a593Smuzhiyun 			net_dbg_ratelimited("LLC(%s:%d): Application bug, race in MSG_PEEK\n",
856*4882a593Smuzhiyun 					    current->comm,
857*4882a593Smuzhiyun 					    task_pid_nr(current));
858*4882a593Smuzhiyun 			peek_seq = llc->copied_seq;
859*4882a593Smuzhiyun 		}
860*4882a593Smuzhiyun 		continue;
861*4882a593Smuzhiyun 	found_ok_skb:
862*4882a593Smuzhiyun 		skb_len = skb->len;
863*4882a593Smuzhiyun 		/* Ok so how much can we use? */
864*4882a593Smuzhiyun 		used = skb->len - offset;
865*4882a593Smuzhiyun 		if (len < used)
866*4882a593Smuzhiyun 			used = len;
867*4882a593Smuzhiyun 
868*4882a593Smuzhiyun 		if (!(flags & MSG_TRUNC)) {
869*4882a593Smuzhiyun 			int rc = skb_copy_datagram_msg(skb, offset, msg, used);
870*4882a593Smuzhiyun 			if (rc) {
871*4882a593Smuzhiyun 				/* Exception. Bailout! */
872*4882a593Smuzhiyun 				if (!copied)
873*4882a593Smuzhiyun 					copied = -EFAULT;
874*4882a593Smuzhiyun 				break;
875*4882a593Smuzhiyun 			}
876*4882a593Smuzhiyun 		}
877*4882a593Smuzhiyun 
878*4882a593Smuzhiyun 		*seq += used;
879*4882a593Smuzhiyun 		copied += used;
880*4882a593Smuzhiyun 		len -= used;
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun 		/* For non stream protcols we get one packet per recvmsg call */
883*4882a593Smuzhiyun 		if (sk->sk_type != SOCK_STREAM)
884*4882a593Smuzhiyun 			goto copy_uaddr;
885*4882a593Smuzhiyun 
886*4882a593Smuzhiyun 		if (!(flags & MSG_PEEK)) {
887*4882a593Smuzhiyun 			skb_unlink(skb, &sk->sk_receive_queue);
888*4882a593Smuzhiyun 			kfree_skb(skb);
889*4882a593Smuzhiyun 			*seq = 0;
890*4882a593Smuzhiyun 		}
891*4882a593Smuzhiyun 
892*4882a593Smuzhiyun 		/* Partial read */
893*4882a593Smuzhiyun 		if (used + offset < skb_len)
894*4882a593Smuzhiyun 			continue;
895*4882a593Smuzhiyun 	} while (len > 0);
896*4882a593Smuzhiyun 
897*4882a593Smuzhiyun out:
898*4882a593Smuzhiyun 	release_sock(sk);
899*4882a593Smuzhiyun 	return copied;
900*4882a593Smuzhiyun copy_uaddr:
901*4882a593Smuzhiyun 	if (uaddr != NULL && skb != NULL) {
902*4882a593Smuzhiyun 		memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));
903*4882a593Smuzhiyun 		msg->msg_namelen = sizeof(*uaddr);
904*4882a593Smuzhiyun 	}
905*4882a593Smuzhiyun 	if (llc_sk(sk)->cmsg_flags)
906*4882a593Smuzhiyun 		llc_cmsg_rcv(msg, skb);
907*4882a593Smuzhiyun 
908*4882a593Smuzhiyun 	if (!(flags & MSG_PEEK)) {
909*4882a593Smuzhiyun 		skb_unlink(skb, &sk->sk_receive_queue);
910*4882a593Smuzhiyun 		kfree_skb(skb);
911*4882a593Smuzhiyun 		*seq = 0;
912*4882a593Smuzhiyun 	}
913*4882a593Smuzhiyun 
914*4882a593Smuzhiyun 	goto out;
915*4882a593Smuzhiyun }
916*4882a593Smuzhiyun 
917*4882a593Smuzhiyun /**
918*4882a593Smuzhiyun  *	llc_ui_sendmsg - Transmit data provided by the socket user.
919*4882a593Smuzhiyun  *	@sock: Socket to transmit data from.
920*4882a593Smuzhiyun  *	@msg: Various user related information.
921*4882a593Smuzhiyun  *	@len: Length of data to transmit.
922*4882a593Smuzhiyun  *
923*4882a593Smuzhiyun  *	Transmit data provided by the socket user.
924*4882a593Smuzhiyun  *	Returns non-negative upon success, negative otherwise.
925*4882a593Smuzhiyun  */
llc_ui_sendmsg(struct socket * sock,struct msghdr * msg,size_t len)926*4882a593Smuzhiyun static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
927*4882a593Smuzhiyun {
928*4882a593Smuzhiyun 	struct sock *sk = sock->sk;
929*4882a593Smuzhiyun 	struct llc_sock *llc = llc_sk(sk);
930*4882a593Smuzhiyun 	DECLARE_SOCKADDR(struct sockaddr_llc *, addr, msg->msg_name);
931*4882a593Smuzhiyun 	int flags = msg->msg_flags;
932*4882a593Smuzhiyun 	int noblock = flags & MSG_DONTWAIT;
933*4882a593Smuzhiyun 	struct sk_buff *skb = NULL;
934*4882a593Smuzhiyun 	size_t size = 0;
935*4882a593Smuzhiyun 	int rc = -EINVAL, copied = 0, hdrlen;
936*4882a593Smuzhiyun 
937*4882a593Smuzhiyun 	dprintk("%s: sending from %02X to %02X\n", __func__,
938*4882a593Smuzhiyun 		llc->laddr.lsap, llc->daddr.lsap);
939*4882a593Smuzhiyun 	lock_sock(sk);
940*4882a593Smuzhiyun 	if (addr) {
941*4882a593Smuzhiyun 		if (msg->msg_namelen < sizeof(*addr))
942*4882a593Smuzhiyun 			goto out;
943*4882a593Smuzhiyun 	} else {
944*4882a593Smuzhiyun 		if (llc_ui_addr_null(&llc->addr))
945*4882a593Smuzhiyun 			goto out;
946*4882a593Smuzhiyun 		addr = &llc->addr;
947*4882a593Smuzhiyun 	}
948*4882a593Smuzhiyun 	/* must bind connection to sap if user hasn't done it. */
949*4882a593Smuzhiyun 	if (sock_flag(sk, SOCK_ZAPPED)) {
950*4882a593Smuzhiyun 		/* bind to sap with null dev, exclusive. */
951*4882a593Smuzhiyun 		rc = llc_ui_autobind(sock, addr);
952*4882a593Smuzhiyun 		if (rc)
953*4882a593Smuzhiyun 			goto out;
954*4882a593Smuzhiyun 	}
955*4882a593Smuzhiyun 	hdrlen = llc->dev->hard_header_len + llc_ui_header_len(sk, addr);
956*4882a593Smuzhiyun 	size = hdrlen + len;
957*4882a593Smuzhiyun 	if (size > llc->dev->mtu)
958*4882a593Smuzhiyun 		size = llc->dev->mtu;
959*4882a593Smuzhiyun 	copied = size - hdrlen;
960*4882a593Smuzhiyun 	rc = -EINVAL;
961*4882a593Smuzhiyun 	if (copied < 0)
962*4882a593Smuzhiyun 		goto out;
963*4882a593Smuzhiyun 	release_sock(sk);
964*4882a593Smuzhiyun 	skb = sock_alloc_send_skb(sk, size, noblock, &rc);
965*4882a593Smuzhiyun 	lock_sock(sk);
966*4882a593Smuzhiyun 	if (!skb)
967*4882a593Smuzhiyun 		goto out;
968*4882a593Smuzhiyun 	skb->dev      = llc->dev;
969*4882a593Smuzhiyun 	skb->protocol = llc_proto_type(addr->sllc_arphrd);
970*4882a593Smuzhiyun 	skb_reserve(skb, hdrlen);
971*4882a593Smuzhiyun 	rc = memcpy_from_msg(skb_put(skb, copied), msg, copied);
972*4882a593Smuzhiyun 	if (rc)
973*4882a593Smuzhiyun 		goto out;
974*4882a593Smuzhiyun 	if (sk->sk_type == SOCK_DGRAM || addr->sllc_ua) {
975*4882a593Smuzhiyun 		llc_build_and_send_ui_pkt(llc->sap, skb, addr->sllc_mac,
976*4882a593Smuzhiyun 					  addr->sllc_sap);
977*4882a593Smuzhiyun 		skb = NULL;
978*4882a593Smuzhiyun 		goto out;
979*4882a593Smuzhiyun 	}
980*4882a593Smuzhiyun 	if (addr->sllc_test) {
981*4882a593Smuzhiyun 		llc_build_and_send_test_pkt(llc->sap, skb, addr->sllc_mac,
982*4882a593Smuzhiyun 					    addr->sllc_sap);
983*4882a593Smuzhiyun 		skb = NULL;
984*4882a593Smuzhiyun 		goto out;
985*4882a593Smuzhiyun 	}
986*4882a593Smuzhiyun 	if (addr->sllc_xid) {
987*4882a593Smuzhiyun 		llc_build_and_send_xid_pkt(llc->sap, skb, addr->sllc_mac,
988*4882a593Smuzhiyun 					   addr->sllc_sap);
989*4882a593Smuzhiyun 		skb = NULL;
990*4882a593Smuzhiyun 		goto out;
991*4882a593Smuzhiyun 	}
992*4882a593Smuzhiyun 	rc = -ENOPROTOOPT;
993*4882a593Smuzhiyun 	if (!(sk->sk_type == SOCK_STREAM && !addr->sllc_ua))
994*4882a593Smuzhiyun 		goto out;
995*4882a593Smuzhiyun 	rc = llc_ui_send_data(sk, skb, noblock);
996*4882a593Smuzhiyun 	skb = NULL;
997*4882a593Smuzhiyun out:
998*4882a593Smuzhiyun 	kfree_skb(skb);
999*4882a593Smuzhiyun 	if (rc)
1000*4882a593Smuzhiyun 		dprintk("%s: failed sending from %02X to %02X: %d\n",
1001*4882a593Smuzhiyun 			__func__, llc->laddr.lsap, llc->daddr.lsap, rc);
1002*4882a593Smuzhiyun 	release_sock(sk);
1003*4882a593Smuzhiyun 	return rc ? : copied;
1004*4882a593Smuzhiyun }
1005*4882a593Smuzhiyun 
1006*4882a593Smuzhiyun /**
1007*4882a593Smuzhiyun  *	llc_ui_getname - return the address info of a socket
1008*4882a593Smuzhiyun  *	@sock: Socket to get address of.
1009*4882a593Smuzhiyun  *	@uaddr: Address structure to return information.
1010*4882a593Smuzhiyun  *	@peer: Does user want local or remote address information.
1011*4882a593Smuzhiyun  *
1012*4882a593Smuzhiyun  *	Return the address information of a socket.
1013*4882a593Smuzhiyun  */
llc_ui_getname(struct socket * sock,struct sockaddr * uaddr,int peer)1014*4882a593Smuzhiyun static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr,
1015*4882a593Smuzhiyun 			  int peer)
1016*4882a593Smuzhiyun {
1017*4882a593Smuzhiyun 	struct sockaddr_llc sllc;
1018*4882a593Smuzhiyun 	struct sock *sk = sock->sk;
1019*4882a593Smuzhiyun 	struct llc_sock *llc = llc_sk(sk);
1020*4882a593Smuzhiyun 	int rc = -EBADF;
1021*4882a593Smuzhiyun 
1022*4882a593Smuzhiyun 	memset(&sllc, 0, sizeof(sllc));
1023*4882a593Smuzhiyun 	lock_sock(sk);
1024*4882a593Smuzhiyun 	if (sock_flag(sk, SOCK_ZAPPED))
1025*4882a593Smuzhiyun 		goto out;
1026*4882a593Smuzhiyun 	if (peer) {
1027*4882a593Smuzhiyun 		rc = -ENOTCONN;
1028*4882a593Smuzhiyun 		if (sk->sk_state != TCP_ESTABLISHED)
1029*4882a593Smuzhiyun 			goto out;
1030*4882a593Smuzhiyun 		if(llc->dev)
1031*4882a593Smuzhiyun 			sllc.sllc_arphrd = llc->dev->type;
1032*4882a593Smuzhiyun 		sllc.sllc_sap = llc->daddr.lsap;
1033*4882a593Smuzhiyun 		memcpy(&sllc.sllc_mac, &llc->daddr.mac, IFHWADDRLEN);
1034*4882a593Smuzhiyun 	} else {
1035*4882a593Smuzhiyun 		rc = -EINVAL;
1036*4882a593Smuzhiyun 		if (!llc->sap)
1037*4882a593Smuzhiyun 			goto out;
1038*4882a593Smuzhiyun 		sllc.sllc_sap = llc->sap->laddr.lsap;
1039*4882a593Smuzhiyun 
1040*4882a593Smuzhiyun 		if (llc->dev) {
1041*4882a593Smuzhiyun 			sllc.sllc_arphrd = llc->dev->type;
1042*4882a593Smuzhiyun 			memcpy(&sllc.sllc_mac, llc->dev->dev_addr,
1043*4882a593Smuzhiyun 			       IFHWADDRLEN);
1044*4882a593Smuzhiyun 		}
1045*4882a593Smuzhiyun 	}
1046*4882a593Smuzhiyun 	sllc.sllc_family = AF_LLC;
1047*4882a593Smuzhiyun 	memcpy(uaddr, &sllc, sizeof(sllc));
1048*4882a593Smuzhiyun 	rc = sizeof(sllc);
1049*4882a593Smuzhiyun out:
1050*4882a593Smuzhiyun 	release_sock(sk);
1051*4882a593Smuzhiyun 	return rc;
1052*4882a593Smuzhiyun }
1053*4882a593Smuzhiyun 
1054*4882a593Smuzhiyun /**
1055*4882a593Smuzhiyun  *	llc_ui_ioctl - io controls for PF_LLC
1056*4882a593Smuzhiyun  *	@sock: Socket to get/set info
1057*4882a593Smuzhiyun  *	@cmd: command
1058*4882a593Smuzhiyun  *	@arg: optional argument for cmd
1059*4882a593Smuzhiyun  *
1060*4882a593Smuzhiyun  *	get/set info on llc sockets
1061*4882a593Smuzhiyun  */
llc_ui_ioctl(struct socket * sock,unsigned int cmd,unsigned long arg)1062*4882a593Smuzhiyun static int llc_ui_ioctl(struct socket *sock, unsigned int cmd,
1063*4882a593Smuzhiyun 			unsigned long arg)
1064*4882a593Smuzhiyun {
1065*4882a593Smuzhiyun 	return -ENOIOCTLCMD;
1066*4882a593Smuzhiyun }
1067*4882a593Smuzhiyun 
1068*4882a593Smuzhiyun /**
1069*4882a593Smuzhiyun  *	llc_ui_setsockopt - set various connection specific parameters.
1070*4882a593Smuzhiyun  *	@sock: Socket to set options on.
1071*4882a593Smuzhiyun  *	@level: Socket level user is requesting operations on.
1072*4882a593Smuzhiyun  *	@optname: Operation name.
1073*4882a593Smuzhiyun  *	@optval: User provided operation data.
1074*4882a593Smuzhiyun  *	@optlen: Length of optval.
1075*4882a593Smuzhiyun  *
1076*4882a593Smuzhiyun  *	Set various connection specific parameters.
1077*4882a593Smuzhiyun  */
llc_ui_setsockopt(struct socket * sock,int level,int optname,sockptr_t optval,unsigned int optlen)1078*4882a593Smuzhiyun static int llc_ui_setsockopt(struct socket *sock, int level, int optname,
1079*4882a593Smuzhiyun 			     sockptr_t optval, unsigned int optlen)
1080*4882a593Smuzhiyun {
1081*4882a593Smuzhiyun 	struct sock *sk = sock->sk;
1082*4882a593Smuzhiyun 	struct llc_sock *llc = llc_sk(sk);
1083*4882a593Smuzhiyun 	unsigned int opt;
1084*4882a593Smuzhiyun 	int rc = -EINVAL;
1085*4882a593Smuzhiyun 
1086*4882a593Smuzhiyun 	lock_sock(sk);
1087*4882a593Smuzhiyun 	if (unlikely(level != SOL_LLC || optlen != sizeof(int)))
1088*4882a593Smuzhiyun 		goto out;
1089*4882a593Smuzhiyun 	rc = copy_from_sockptr(&opt, optval, sizeof(opt));
1090*4882a593Smuzhiyun 	if (rc)
1091*4882a593Smuzhiyun 		goto out;
1092*4882a593Smuzhiyun 	rc = -EINVAL;
1093*4882a593Smuzhiyun 	switch (optname) {
1094*4882a593Smuzhiyun 	case LLC_OPT_RETRY:
1095*4882a593Smuzhiyun 		if (opt > LLC_OPT_MAX_RETRY)
1096*4882a593Smuzhiyun 			goto out;
1097*4882a593Smuzhiyun 		llc->n2 = opt;
1098*4882a593Smuzhiyun 		break;
1099*4882a593Smuzhiyun 	case LLC_OPT_SIZE:
1100*4882a593Smuzhiyun 		if (opt > LLC_OPT_MAX_SIZE)
1101*4882a593Smuzhiyun 			goto out;
1102*4882a593Smuzhiyun 		llc->n1 = opt;
1103*4882a593Smuzhiyun 		break;
1104*4882a593Smuzhiyun 	case LLC_OPT_ACK_TMR_EXP:
1105*4882a593Smuzhiyun 		if (opt > LLC_OPT_MAX_ACK_TMR_EXP)
1106*4882a593Smuzhiyun 			goto out;
1107*4882a593Smuzhiyun 		llc->ack_timer.expire = opt * HZ;
1108*4882a593Smuzhiyun 		break;
1109*4882a593Smuzhiyun 	case LLC_OPT_P_TMR_EXP:
1110*4882a593Smuzhiyun 		if (opt > LLC_OPT_MAX_P_TMR_EXP)
1111*4882a593Smuzhiyun 			goto out;
1112*4882a593Smuzhiyun 		llc->pf_cycle_timer.expire = opt * HZ;
1113*4882a593Smuzhiyun 		break;
1114*4882a593Smuzhiyun 	case LLC_OPT_REJ_TMR_EXP:
1115*4882a593Smuzhiyun 		if (opt > LLC_OPT_MAX_REJ_TMR_EXP)
1116*4882a593Smuzhiyun 			goto out;
1117*4882a593Smuzhiyun 		llc->rej_sent_timer.expire = opt * HZ;
1118*4882a593Smuzhiyun 		break;
1119*4882a593Smuzhiyun 	case LLC_OPT_BUSY_TMR_EXP:
1120*4882a593Smuzhiyun 		if (opt > LLC_OPT_MAX_BUSY_TMR_EXP)
1121*4882a593Smuzhiyun 			goto out;
1122*4882a593Smuzhiyun 		llc->busy_state_timer.expire = opt * HZ;
1123*4882a593Smuzhiyun 		break;
1124*4882a593Smuzhiyun 	case LLC_OPT_TX_WIN:
1125*4882a593Smuzhiyun 		if (opt > LLC_OPT_MAX_WIN)
1126*4882a593Smuzhiyun 			goto out;
1127*4882a593Smuzhiyun 		llc->k = opt;
1128*4882a593Smuzhiyun 		break;
1129*4882a593Smuzhiyun 	case LLC_OPT_RX_WIN:
1130*4882a593Smuzhiyun 		if (opt > LLC_OPT_MAX_WIN)
1131*4882a593Smuzhiyun 			goto out;
1132*4882a593Smuzhiyun 		llc->rw = opt;
1133*4882a593Smuzhiyun 		break;
1134*4882a593Smuzhiyun 	case LLC_OPT_PKTINFO:
1135*4882a593Smuzhiyun 		if (opt)
1136*4882a593Smuzhiyun 			llc->cmsg_flags |= LLC_CMSG_PKTINFO;
1137*4882a593Smuzhiyun 		else
1138*4882a593Smuzhiyun 			llc->cmsg_flags &= ~LLC_CMSG_PKTINFO;
1139*4882a593Smuzhiyun 		break;
1140*4882a593Smuzhiyun 	default:
1141*4882a593Smuzhiyun 		rc = -ENOPROTOOPT;
1142*4882a593Smuzhiyun 		goto out;
1143*4882a593Smuzhiyun 	}
1144*4882a593Smuzhiyun 	rc = 0;
1145*4882a593Smuzhiyun out:
1146*4882a593Smuzhiyun 	release_sock(sk);
1147*4882a593Smuzhiyun 	return rc;
1148*4882a593Smuzhiyun }
1149*4882a593Smuzhiyun 
1150*4882a593Smuzhiyun /**
1151*4882a593Smuzhiyun  *	llc_ui_getsockopt - get connection specific socket info
1152*4882a593Smuzhiyun  *	@sock: Socket to get information from.
1153*4882a593Smuzhiyun  *	@level: Socket level user is requesting operations on.
1154*4882a593Smuzhiyun  *	@optname: Operation name.
1155*4882a593Smuzhiyun  *	@optval: Variable to return operation data in.
1156*4882a593Smuzhiyun  *	@optlen: Length of optval.
1157*4882a593Smuzhiyun  *
1158*4882a593Smuzhiyun  *	Get connection specific socket information.
1159*4882a593Smuzhiyun  */
llc_ui_getsockopt(struct socket * sock,int level,int optname,char __user * optval,int __user * optlen)1160*4882a593Smuzhiyun static int llc_ui_getsockopt(struct socket *sock, int level, int optname,
1161*4882a593Smuzhiyun 			     char __user *optval, int __user *optlen)
1162*4882a593Smuzhiyun {
1163*4882a593Smuzhiyun 	struct sock *sk = sock->sk;
1164*4882a593Smuzhiyun 	struct llc_sock *llc = llc_sk(sk);
1165*4882a593Smuzhiyun 	int val = 0, len = 0, rc = -EINVAL;
1166*4882a593Smuzhiyun 
1167*4882a593Smuzhiyun 	lock_sock(sk);
1168*4882a593Smuzhiyun 	if (unlikely(level != SOL_LLC))
1169*4882a593Smuzhiyun 		goto out;
1170*4882a593Smuzhiyun 	rc = get_user(len, optlen);
1171*4882a593Smuzhiyun 	if (rc)
1172*4882a593Smuzhiyun 		goto out;
1173*4882a593Smuzhiyun 	rc = -EINVAL;
1174*4882a593Smuzhiyun 	if (len != sizeof(int))
1175*4882a593Smuzhiyun 		goto out;
1176*4882a593Smuzhiyun 	switch (optname) {
1177*4882a593Smuzhiyun 	case LLC_OPT_RETRY:
1178*4882a593Smuzhiyun 		val = llc->n2;					break;
1179*4882a593Smuzhiyun 	case LLC_OPT_SIZE:
1180*4882a593Smuzhiyun 		val = llc->n1;					break;
1181*4882a593Smuzhiyun 	case LLC_OPT_ACK_TMR_EXP:
1182*4882a593Smuzhiyun 		val = llc->ack_timer.expire / HZ;		break;
1183*4882a593Smuzhiyun 	case LLC_OPT_P_TMR_EXP:
1184*4882a593Smuzhiyun 		val = llc->pf_cycle_timer.expire / HZ;		break;
1185*4882a593Smuzhiyun 	case LLC_OPT_REJ_TMR_EXP:
1186*4882a593Smuzhiyun 		val = llc->rej_sent_timer.expire / HZ;		break;
1187*4882a593Smuzhiyun 	case LLC_OPT_BUSY_TMR_EXP:
1188*4882a593Smuzhiyun 		val = llc->busy_state_timer.expire / HZ;	break;
1189*4882a593Smuzhiyun 	case LLC_OPT_TX_WIN:
1190*4882a593Smuzhiyun 		val = llc->k;				break;
1191*4882a593Smuzhiyun 	case LLC_OPT_RX_WIN:
1192*4882a593Smuzhiyun 		val = llc->rw;				break;
1193*4882a593Smuzhiyun 	case LLC_OPT_PKTINFO:
1194*4882a593Smuzhiyun 		val = (llc->cmsg_flags & LLC_CMSG_PKTINFO) != 0;
1195*4882a593Smuzhiyun 		break;
1196*4882a593Smuzhiyun 	default:
1197*4882a593Smuzhiyun 		rc = -ENOPROTOOPT;
1198*4882a593Smuzhiyun 		goto out;
1199*4882a593Smuzhiyun 	}
1200*4882a593Smuzhiyun 	rc = 0;
1201*4882a593Smuzhiyun 	if (put_user(len, optlen) || copy_to_user(optval, &val, len))
1202*4882a593Smuzhiyun 		rc = -EFAULT;
1203*4882a593Smuzhiyun out:
1204*4882a593Smuzhiyun 	release_sock(sk);
1205*4882a593Smuzhiyun 	return rc;
1206*4882a593Smuzhiyun }
1207*4882a593Smuzhiyun 
1208*4882a593Smuzhiyun static const struct net_proto_family llc_ui_family_ops = {
1209*4882a593Smuzhiyun 	.family = PF_LLC,
1210*4882a593Smuzhiyun 	.create = llc_ui_create,
1211*4882a593Smuzhiyun 	.owner	= THIS_MODULE,
1212*4882a593Smuzhiyun };
1213*4882a593Smuzhiyun 
1214*4882a593Smuzhiyun static const struct proto_ops llc_ui_ops = {
1215*4882a593Smuzhiyun 	.family	     = PF_LLC,
1216*4882a593Smuzhiyun 	.owner       = THIS_MODULE,
1217*4882a593Smuzhiyun 	.release     = llc_ui_release,
1218*4882a593Smuzhiyun 	.bind	     = llc_ui_bind,
1219*4882a593Smuzhiyun 	.connect     = llc_ui_connect,
1220*4882a593Smuzhiyun 	.socketpair  = sock_no_socketpair,
1221*4882a593Smuzhiyun 	.accept      = llc_ui_accept,
1222*4882a593Smuzhiyun 	.getname     = llc_ui_getname,
1223*4882a593Smuzhiyun 	.poll	     = datagram_poll,
1224*4882a593Smuzhiyun 	.ioctl       = llc_ui_ioctl,
1225*4882a593Smuzhiyun 	.listen      = llc_ui_listen,
1226*4882a593Smuzhiyun 	.shutdown    = llc_ui_shutdown,
1227*4882a593Smuzhiyun 	.setsockopt  = llc_ui_setsockopt,
1228*4882a593Smuzhiyun 	.getsockopt  = llc_ui_getsockopt,
1229*4882a593Smuzhiyun 	.sendmsg     = llc_ui_sendmsg,
1230*4882a593Smuzhiyun 	.recvmsg     = llc_ui_recvmsg,
1231*4882a593Smuzhiyun 	.mmap	     = sock_no_mmap,
1232*4882a593Smuzhiyun 	.sendpage    = sock_no_sendpage,
1233*4882a593Smuzhiyun };
1234*4882a593Smuzhiyun 
1235*4882a593Smuzhiyun static const char llc_proc_err_msg[] __initconst =
1236*4882a593Smuzhiyun 	KERN_CRIT "LLC: Unable to register the proc_fs entries\n";
1237*4882a593Smuzhiyun static const char llc_sysctl_err_msg[] __initconst =
1238*4882a593Smuzhiyun 	KERN_CRIT "LLC: Unable to register the sysctl entries\n";
1239*4882a593Smuzhiyun static const char llc_sock_err_msg[] __initconst =
1240*4882a593Smuzhiyun 	KERN_CRIT "LLC: Unable to register the network family\n";
1241*4882a593Smuzhiyun 
llc2_init(void)1242*4882a593Smuzhiyun static int __init llc2_init(void)
1243*4882a593Smuzhiyun {
1244*4882a593Smuzhiyun 	int rc = proto_register(&llc_proto, 0);
1245*4882a593Smuzhiyun 
1246*4882a593Smuzhiyun 	if (rc != 0)
1247*4882a593Smuzhiyun 		goto out;
1248*4882a593Smuzhiyun 
1249*4882a593Smuzhiyun 	llc_build_offset_table();
1250*4882a593Smuzhiyun 	llc_station_init();
1251*4882a593Smuzhiyun 	llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
1252*4882a593Smuzhiyun 	rc = llc_proc_init();
1253*4882a593Smuzhiyun 	if (rc != 0) {
1254*4882a593Smuzhiyun 		printk(llc_proc_err_msg);
1255*4882a593Smuzhiyun 		goto out_station;
1256*4882a593Smuzhiyun 	}
1257*4882a593Smuzhiyun 	rc = llc_sysctl_init();
1258*4882a593Smuzhiyun 	if (rc) {
1259*4882a593Smuzhiyun 		printk(llc_sysctl_err_msg);
1260*4882a593Smuzhiyun 		goto out_proc;
1261*4882a593Smuzhiyun 	}
1262*4882a593Smuzhiyun 	rc = sock_register(&llc_ui_family_ops);
1263*4882a593Smuzhiyun 	if (rc) {
1264*4882a593Smuzhiyun 		printk(llc_sock_err_msg);
1265*4882a593Smuzhiyun 		goto out_sysctl;
1266*4882a593Smuzhiyun 	}
1267*4882a593Smuzhiyun 	llc_add_pack(LLC_DEST_SAP, llc_sap_handler);
1268*4882a593Smuzhiyun 	llc_add_pack(LLC_DEST_CONN, llc_conn_handler);
1269*4882a593Smuzhiyun out:
1270*4882a593Smuzhiyun 	return rc;
1271*4882a593Smuzhiyun out_sysctl:
1272*4882a593Smuzhiyun 	llc_sysctl_exit();
1273*4882a593Smuzhiyun out_proc:
1274*4882a593Smuzhiyun 	llc_proc_exit();
1275*4882a593Smuzhiyun out_station:
1276*4882a593Smuzhiyun 	llc_station_exit();
1277*4882a593Smuzhiyun 	proto_unregister(&llc_proto);
1278*4882a593Smuzhiyun 	goto out;
1279*4882a593Smuzhiyun }
1280*4882a593Smuzhiyun 
llc2_exit(void)1281*4882a593Smuzhiyun static void __exit llc2_exit(void)
1282*4882a593Smuzhiyun {
1283*4882a593Smuzhiyun 	llc_station_exit();
1284*4882a593Smuzhiyun 	llc_remove_pack(LLC_DEST_SAP);
1285*4882a593Smuzhiyun 	llc_remove_pack(LLC_DEST_CONN);
1286*4882a593Smuzhiyun 	sock_unregister(PF_LLC);
1287*4882a593Smuzhiyun 	llc_proc_exit();
1288*4882a593Smuzhiyun 	llc_sysctl_exit();
1289*4882a593Smuzhiyun 	proto_unregister(&llc_proto);
1290*4882a593Smuzhiyun }
1291*4882a593Smuzhiyun 
1292*4882a593Smuzhiyun module_init(llc2_init);
1293*4882a593Smuzhiyun module_exit(llc2_exit);
1294*4882a593Smuzhiyun 
1295*4882a593Smuzhiyun MODULE_LICENSE("GPL");
1296*4882a593Smuzhiyun MODULE_AUTHOR("Procom 1997, Jay Schullist 2001, Arnaldo C. Melo 2001-2003");
1297*4882a593Smuzhiyun MODULE_DESCRIPTION("IEEE 802.2 PF_LLC support");
1298*4882a593Smuzhiyun MODULE_ALIAS_NETPROTO(PF_LLC);
1299