xref: /openbmc/linux/net/llc/af_llc.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * af_llc.c - LLC User Interface SAPs
31da177e4SLinus Torvalds  * Description:
41da177e4SLinus Torvalds  *   Functions in this module are implementation of socket based llc
51da177e4SLinus Torvalds  *   communications for the Linux operating system. Support of llc class
61da177e4SLinus Torvalds  *   one and class two is provided via SOCK_DGRAM and SOCK_STREAM
71da177e4SLinus Torvalds  *   respectively.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *   An llc2 connection is (mac + sap), only one llc2 sap connection
101da177e4SLinus Torvalds  *   is allowed per mac. Though one sap may have multiple mac + sap
111da177e4SLinus Torvalds  *   connections.
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  * Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
141da177e4SLinus Torvalds  *		 2002-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  * This program can be redistributed or modified under the terms of the
171da177e4SLinus Torvalds  * GNU General Public License as published by the Free Software Foundation.
181da177e4SLinus Torvalds  * This program is distributed without any warranty or implied warranty
191da177e4SLinus Torvalds  * of merchantability or fitness for a particular purpose.
201da177e4SLinus Torvalds  *
211da177e4SLinus Torvalds  * See the GNU General Public License for more details.
221da177e4SLinus Torvalds  */
236e2144b7SArnaldo Carvalho de Melo #include <linux/compiler.h>
241da177e4SLinus Torvalds #include <linux/kernel.h>
251da177e4SLinus Torvalds #include <linux/module.h>
261da177e4SLinus Torvalds #include <linux/rtnetlink.h>
271da177e4SLinus Torvalds #include <linux/init.h>
285a0e3ad6STejun Heo #include <linux/slab.h>
29174cd4b1SIngo Molnar #include <linux/sched/signal.h>
30174cd4b1SIngo Molnar 
311da177e4SLinus Torvalds #include <net/llc.h>
321da177e4SLinus Torvalds #include <net/llc_sap.h>
331da177e4SLinus Torvalds #include <net/llc_pdu.h>
341da177e4SLinus Torvalds #include <net/llc_conn.h>
35c752f073SArnaldo Carvalho de Melo #include <net/tcp_states.h>
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds /* remember: uninitialized global data is zeroed because its in .bss */
381da177e4SLinus Torvalds static u16 llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
391da177e4SLinus Torvalds static u16 llc_ui_sap_link_no_max[256];
401da177e4SLinus Torvalds static struct sockaddr_llc llc_ui_addrnull;
4190ddc4f0SEric Dumazet static const struct proto_ops llc_ui_ops;
421da177e4SLinus Torvalds 
435ff904d5SAlan Cox static bool llc_ui_wait_for_conn(struct sock *sk, long timeout);
4454fb7f25SArnaldo Carvalho de Melo static int llc_ui_wait_for_disc(struct sock *sk, long timeout);
4554fb7f25SArnaldo Carvalho de Melo static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout);
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds #if 0
481da177e4SLinus Torvalds #define dprintk(args...) printk(KERN_DEBUG args)
491da177e4SLinus Torvalds #else
50c535f920SRandy Dunlap #define dprintk(args...) do {} while (0)
511da177e4SLinus Torvalds #endif
521da177e4SLinus Torvalds 
53e5cd6fe3SOctavian Purdila /* Maybe we'll add some more in the future. */
54e5cd6fe3SOctavian Purdila #define LLC_CMSG_PKTINFO	1
55e5cd6fe3SOctavian Purdila 
56e5cd6fe3SOctavian Purdila 
571da177e4SLinus Torvalds /**
581da177e4SLinus Torvalds  *	llc_ui_next_link_no - return the next unused link number for a sap
591da177e4SLinus Torvalds  *	@sap: Address of sap to get link number from.
601da177e4SLinus Torvalds  *
611da177e4SLinus Torvalds  *	Return the next unused link number for a given sap.
621da177e4SLinus Torvalds  */
llc_ui_next_link_no(int sap)632342c990SArnaldo Carvalho de Melo static inline u16 llc_ui_next_link_no(int sap)
641da177e4SLinus Torvalds {
651da177e4SLinus Torvalds 	return llc_ui_sap_link_no_max[sap]++;
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds /**
691da177e4SLinus Torvalds  *	llc_proto_type - return eth protocol for ARP header type
701da177e4SLinus Torvalds  *	@arphrd: ARP header type.
711da177e4SLinus Torvalds  *
721da177e4SLinus Torvalds  *	Given an ARP header type return the corresponding ethernet protocol.
731da177e4SLinus Torvalds  */
llc_proto_type(u16 arphrd)743fbd418aSAl Viro static inline __be16 llc_proto_type(u16 arphrd)
751da177e4SLinus Torvalds {
76211ed865SPaul Gortmaker 	return htons(ETH_P_802_2);
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds /**
801da177e4SLinus Torvalds  *	llc_ui_addr_null - determines if a address structure is null
811da177e4SLinus Torvalds  *	@addr: Address to test if null.
821da177e4SLinus Torvalds  */
llc_ui_addr_null(struct sockaddr_llc * addr)832342c990SArnaldo Carvalho de Melo static inline u8 llc_ui_addr_null(struct sockaddr_llc *addr)
841da177e4SLinus Torvalds {
851da177e4SLinus Torvalds 	return !memcmp(addr, &llc_ui_addrnull, sizeof(*addr));
861da177e4SLinus Torvalds }
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds /**
891da177e4SLinus Torvalds  *	llc_ui_header_len - return length of llc header based on operation
901da177e4SLinus Torvalds  *	@sk: Socket which contains a valid llc socket type.
911da177e4SLinus Torvalds  *	@addr: Complete sockaddr_llc structure received from the user.
921da177e4SLinus Torvalds  *
931da177e4SLinus Torvalds  *	Provide the length of the llc header depending on what kind of
941da177e4SLinus Torvalds  *	operation the user would like to perform and the type of socket.
951da177e4SLinus Torvalds  *	Returns the correct llc header length.
961da177e4SLinus Torvalds  */
llc_ui_header_len(struct sock * sk,struct sockaddr_llc * addr)972342c990SArnaldo Carvalho de Melo static inline u8 llc_ui_header_len(struct sock *sk, struct sockaddr_llc *addr)
981da177e4SLinus Torvalds {
991da177e4SLinus Torvalds 	u8 rc = LLC_PDU_LEN_U;
1001da177e4SLinus Torvalds 
101c7c9d210SPavel Skripkin 	if (addr->sllc_test)
1021da177e4SLinus Torvalds 		rc = LLC_PDU_LEN_U;
103c7c9d210SPavel Skripkin 	else if (addr->sllc_xid)
104c7c9d210SPavel Skripkin 		/* We need to expand header to sizeof(struct llc_xid_info)
105c7c9d210SPavel Skripkin 		 * since llc_pdu_init_as_xid_cmd() sets 4,5,6 bytes of LLC header
106c7c9d210SPavel Skripkin 		 * as XID PDU. In llc_ui_sendmsg() we reserved header size and then
107c7c9d210SPavel Skripkin 		 * filled all other space with user data. If we won't reserve this
108c7c9d210SPavel Skripkin 		 * bytes, llc_pdu_init_as_xid_cmd() will overwrite user data
109c7c9d210SPavel Skripkin 		 */
110c7c9d210SPavel Skripkin 		rc = LLC_PDU_LEN_U_XID;
1111da177e4SLinus Torvalds 	else if (sk->sk_type == SOCK_STREAM)
1121da177e4SLinus Torvalds 		rc = LLC_PDU_LEN_I;
1131da177e4SLinus Torvalds 	return rc;
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds /**
1171da177e4SLinus Torvalds  *	llc_ui_send_data - send data via reliable llc2 connection
1181da177e4SLinus Torvalds  *	@sk: Connection the socket is using.
1191da177e4SLinus Torvalds  *	@skb: Data the user wishes to send.
1201da177e4SLinus Torvalds  *	@noblock: can we block waiting for data?
1211da177e4SLinus Torvalds  *
1221da177e4SLinus Torvalds  *	Send data via reliable llc2 connection.
1231da177e4SLinus Torvalds  *	Returns 0 upon success, non-zero if action did not succeed.
124fc8d5db1SEric Biggers  *
125fc8d5db1SEric Biggers  *	This function always consumes a reference to the skb.
1261da177e4SLinus Torvalds  */
llc_ui_send_data(struct sock * sk,struct sk_buff * skb,int noblock)1271da177e4SLinus Torvalds static int llc_ui_send_data(struct sock* sk, struct sk_buff *skb, int noblock)
1281da177e4SLinus Torvalds {
1291da177e4SLinus Torvalds 	struct llc_sock* llc = llc_sk(sk);
1301da177e4SLinus Torvalds 
131451677c4SJochen Friedrich 	if (unlikely(llc_data_accept_state(llc->state) ||
132451677c4SJochen Friedrich 		     llc->remote_busy_flag ||
133451677c4SJochen Friedrich 		     llc->p_flag)) {
13454fb7f25SArnaldo Carvalho de Melo 		long timeout = sock_sndtimeo(sk, noblock);
135fc8d5db1SEric Biggers 		int rc;
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 		rc = llc_ui_wait_for_busy_core(sk, timeout);
138fc8d5db1SEric Biggers 		if (rc) {
139fc8d5db1SEric Biggers 			kfree_skb(skb);
1401da177e4SLinus Torvalds 			return rc;
1411da177e4SLinus Torvalds 		}
142fc8d5db1SEric Biggers 	}
143fc8d5db1SEric Biggers 	return llc_build_and_send_pkt(sk, skb);
144fc8d5db1SEric Biggers }
1451da177e4SLinus Torvalds 
llc_ui_sk_init(struct socket * sock,struct sock * sk)1461da177e4SLinus Torvalds static void llc_ui_sk_init(struct socket *sock, struct sock *sk)
1471da177e4SLinus Torvalds {
148ee5850deSDavid S. Miller 	sock_graft(sk, sock);
1491da177e4SLinus Torvalds 	sk->sk_type	= sock->type;
1501da177e4SLinus Torvalds 	sock->ops	= &llc_ui_ops;
1511da177e4SLinus Torvalds }
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds static struct proto llc_proto = {
1549c005e01SArnaldo Carvalho de Melo 	.name	  = "LLC",
1551da177e4SLinus Torvalds 	.owner	  = THIS_MODULE,
1561da177e4SLinus Torvalds 	.obj_size = sizeof(struct llc_sock),
1575f0d5a3aSPaul E. McKenney 	.slab_flags = SLAB_TYPESAFE_BY_RCU,
1581da177e4SLinus Torvalds };
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds /**
1611da177e4SLinus Torvalds  *	llc_ui_create - alloc and init a new llc_ui socket
1623f378b68SEric Paris  *	@net: network namespace (must be default network)
1631da177e4SLinus Torvalds  *	@sock: Socket to initialize and attach allocated sk to.
1641da177e4SLinus Torvalds  *	@protocol: Unused.
1653f378b68SEric Paris  *	@kern: on behalf of kernel or userspace
1661da177e4SLinus Torvalds  *
1671da177e4SLinus Torvalds  *	Allocate and initialize a new llc_ui socket, validate the user wants a
1681da177e4SLinus Torvalds  *	socket type we have available.
1691da177e4SLinus Torvalds  *	Returns 0 upon success, negative upon failure.
1701da177e4SLinus Torvalds  */
llc_ui_create(struct net * net,struct socket * sock,int protocol,int kern)1713f378b68SEric Paris static int llc_ui_create(struct net *net, struct socket *sock, int protocol,
1723f378b68SEric Paris 			 int kern)
1731da177e4SLinus Torvalds {
1741da177e4SLinus Torvalds 	struct sock *sk;
1751da177e4SLinus Torvalds 	int rc = -ESOCKTNOSUPPORT;
1761da177e4SLinus Torvalds 
177df008c91SEric W. Biederman 	if (!ns_capable(net->user_ns, CAP_NET_RAW))
1783480c63bSPatrick McHardy 		return -EPERM;
1793480c63bSPatrick McHardy 
18009ad9bc7SOctavian Purdila 	if (!net_eq(net, &init_net))
1811b8d7ae4SEric W. Biederman 		return -EAFNOSUPPORT;
1821b8d7ae4SEric W. Biederman 
183af426d32SArnaldo Carvalho de Melo 	if (likely(sock->type == SOCK_DGRAM || sock->type == SOCK_STREAM)) {
1841da177e4SLinus Torvalds 		rc = -ENOMEM;
18511aa9c28SEric W. Biederman 		sk = llc_sk_alloc(net, PF_LLC, GFP_KERNEL, &llc_proto, kern);
1861da177e4SLinus Torvalds 		if (sk) {
1871da177e4SLinus Torvalds 			rc = 0;
1881da177e4SLinus Torvalds 			llc_ui_sk_init(sock, sk);
1891da177e4SLinus Torvalds 		}
1901da177e4SLinus Torvalds 	}
1911da177e4SLinus Torvalds 	return rc;
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds /**
1951da177e4SLinus Torvalds  *	llc_ui_release - shutdown socket
1961da177e4SLinus Torvalds  *	@sock: Socket to release.
1971da177e4SLinus Torvalds  *
1981da177e4SLinus Torvalds  *	Shutdown and deallocate an existing socket.
1991da177e4SLinus Torvalds  */
llc_ui_release(struct socket * sock)2001da177e4SLinus Torvalds static int llc_ui_release(struct socket *sock)
2011da177e4SLinus Torvalds {
2021da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
2031da177e4SLinus Torvalds 	struct llc_sock *llc;
2041da177e4SLinus Torvalds 
205af426d32SArnaldo Carvalho de Melo 	if (unlikely(sk == NULL))
2061da177e4SLinus Torvalds 		goto out;
2071da177e4SLinus Torvalds 	sock_hold(sk);
2081da177e4SLinus Torvalds 	lock_sock(sk);
2091da177e4SLinus Torvalds 	llc = llc_sk(sk);
2100dc47877SHarvey Harrison 	dprintk("%s: closing local(%02X) remote(%02X)\n", __func__,
2111da177e4SLinus Torvalds 		llc->laddr.lsap, llc->daddr.lsap);
2121da177e4SLinus Torvalds 	if (!llc_send_disc(sk))
2131da177e4SLinus Torvalds 		llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo);
2143a04ce71SCong Wang 	if (!sock_flag(sk, SOCK_ZAPPED)) {
2153a04ce71SCong Wang 		struct llc_sap *sap = llc->sap;
2163a04ce71SCong Wang 
2173a04ce71SCong Wang 		/* Hold this for release_sock(), so that llc_backlog_rcv()
2183a04ce71SCong Wang 		 * could still use it.
219f7e43672SCong Wang 		 */
220f7e43672SCong Wang 		llc_sap_hold(sap);
2211da177e4SLinus Torvalds 		llc_sap_remove_socket(llc->sap, sk);
2221da177e4SLinus Torvalds 		release_sock(sk);
223f7e43672SCong Wang 		llc_sap_put(sap);
2243a04ce71SCong Wang 	} else {
2253a04ce71SCong Wang 		release_sock(sk);
2263a04ce71SCong Wang 	}
227d62607c3SJakub Kicinski 	netdev_put(llc->dev, &llc->dev_tracker);
2281da177e4SLinus Torvalds 	sock_put(sk);
2293151051bSEric Dumazet 	sock_orphan(sk);
2303151051bSEric Dumazet 	sock->sk = NULL;
2311da177e4SLinus Torvalds 	llc_sk_free(sk);
2321da177e4SLinus Torvalds out:
2331da177e4SLinus Torvalds 	return 0;
2341da177e4SLinus Torvalds }
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds /**
2371da177e4SLinus Torvalds  *	llc_ui_autoport - provide dynamically allocate SAP number
2381da177e4SLinus Torvalds  *
2391da177e4SLinus Torvalds  *	Provide the caller with a dynamically allocated SAP number according
2401da177e4SLinus Torvalds  *	to the rules that are set in this function. Returns: 0, upon failure,
2411da177e4SLinus Torvalds  *	SAP number otherwise.
2421da177e4SLinus Torvalds  */
llc_ui_autoport(void)2431da177e4SLinus Torvalds static int llc_ui_autoport(void)
2441da177e4SLinus Torvalds {
2451da177e4SLinus Torvalds 	struct llc_sap *sap;
2461da177e4SLinus Torvalds 	int i, tries = 0;
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	while (tries < LLC_SAP_DYN_TRIES) {
2491da177e4SLinus Torvalds 		for (i = llc_ui_sap_last_autoport;
2501da177e4SLinus Torvalds 		     i < LLC_SAP_DYN_STOP; i += 2) {
2511da177e4SLinus Torvalds 			sap = llc_sap_find(i);
2521da177e4SLinus Torvalds 			if (!sap) {
2531da177e4SLinus Torvalds 				llc_ui_sap_last_autoport = i + 2;
2541da177e4SLinus Torvalds 				goto out;
2551da177e4SLinus Torvalds 			}
2566e2144b7SArnaldo Carvalho de Melo 			llc_sap_put(sap);
2571da177e4SLinus Torvalds 		}
2581da177e4SLinus Torvalds 		llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
2591da177e4SLinus Torvalds 		tries++;
2601da177e4SLinus Torvalds 	}
2611da177e4SLinus Torvalds 	i = 0;
2621da177e4SLinus Torvalds out:
2631da177e4SLinus Torvalds 	return i;
2641da177e4SLinus Torvalds }
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds /**
2675a770c02SArnaldo Carvalho de Melo  *	llc_ui_autobind - automatically bind a socket to a sap
2685a770c02SArnaldo Carvalho de Melo  *	@sock: socket to bind
2695a770c02SArnaldo Carvalho de Melo  *	@addr: address to connect to
2701da177e4SLinus Torvalds  *
2715a770c02SArnaldo Carvalho de Melo  * 	Used by llc_ui_connect and llc_ui_sendmsg when the user hasn't
2725a770c02SArnaldo Carvalho de Melo  * 	specifically used llc_ui_bind to bind to an specific address/sap
2735a770c02SArnaldo Carvalho de Melo  *
2741da177e4SLinus Torvalds  *	Returns: 0 upon success, negative otherwise.
2751da177e4SLinus Torvalds  */
llc_ui_autobind(struct socket * sock,struct sockaddr_llc * addr)2761da177e4SLinus Torvalds static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr)
2771da177e4SLinus Torvalds {
2781da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
2791da177e4SLinus Torvalds 	struct llc_sock *llc = llc_sk(sk);
2802d327a79SEric Dumazet 	struct net_device *dev = NULL;
2811da177e4SLinus Torvalds 	struct llc_sap *sap;
2821da177e4SLinus Torvalds 	int rc = -EINVAL;
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	if (!sock_flag(sk, SOCK_ZAPPED))
2851da177e4SLinus Torvalds 		goto out;
286a9b11101SEric Dumazet 	if (!addr->sllc_arphrd)
287a9b11101SEric Dumazet 		addr->sllc_arphrd = ARPHRD_ETHER;
288a9b11101SEric Dumazet 	if (addr->sllc_arphrd != ARPHRD_ETHER)
289a9b11101SEric Dumazet 		goto out;
2901da177e4SLinus Torvalds 	rc = -ENODEV;
291abf9d537SOctavian Purdila 	if (sk->sk_bound_dev_if) {
2922d327a79SEric Dumazet 		dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if);
2932d327a79SEric Dumazet 		if (dev && addr->sllc_arphrd != dev->type) {
2942d327a79SEric Dumazet 			dev_put(dev);
2952d327a79SEric Dumazet 			dev = NULL;
296abf9d537SOctavian Purdila 		}
297abf9d537SOctavian Purdila 	} else
2982d327a79SEric Dumazet 		dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd);
2992d327a79SEric Dumazet 	if (!dev)
3001da177e4SLinus Torvalds 		goto out;
3011da177e4SLinus Torvalds 	rc = -EUSERS;
3021da177e4SLinus Torvalds 	llc->laddr.lsap = llc_ui_autoport();
3031da177e4SLinus Torvalds 	if (!llc->laddr.lsap)
3041da177e4SLinus Torvalds 		goto out;
3051da177e4SLinus Torvalds 	rc = -EBUSY; /* some other network layer is using the sap */
3061da177e4SLinus Torvalds 	sap = llc_sap_open(llc->laddr.lsap, NULL);
3071da177e4SLinus Torvalds 	if (!sap)
3081da177e4SLinus Torvalds 		goto out;
3092d327a79SEric Dumazet 
3102d327a79SEric Dumazet 	/* Note: We do not expect errors from this point. */
3112d327a79SEric Dumazet 	llc->dev = dev;
3122d327a79SEric Dumazet 	netdev_tracker_alloc(llc->dev, &llc->dev_tracker, GFP_KERNEL);
3132d327a79SEric Dumazet 	dev = NULL;
3142d327a79SEric Dumazet 
3151da177e4SLinus Torvalds 	memcpy(llc->laddr.mac, llc->dev->dev_addr, IFHWADDRLEN);
3161da177e4SLinus Torvalds 	memcpy(&llc->addr, addr, sizeof(llc->addr));
3171da177e4SLinus Torvalds 	/* assign new connection to its SAP */
3181da177e4SLinus Torvalds 	llc_sap_add_socket(sap, sk);
3191da177e4SLinus Torvalds 	sock_reset_flag(sk, SOCK_ZAPPED);
3201da177e4SLinus Torvalds 	rc = 0;
3211da177e4SLinus Torvalds out:
3222d327a79SEric Dumazet 	dev_put(dev);
3231da177e4SLinus Torvalds 	return rc;
3241da177e4SLinus Torvalds }
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds /**
3271da177e4SLinus Torvalds  *	llc_ui_bind - bind a socket to a specific address.
3281da177e4SLinus Torvalds  *	@sock: Socket to bind an address to.
3291da177e4SLinus Torvalds  *	@uaddr: Address the user wants the socket bound to.
3301da177e4SLinus Torvalds  *	@addrlen: Length of the uaddr structure.
3311da177e4SLinus Torvalds  *
3321da177e4SLinus Torvalds  *	Bind a socket to a specific address. For llc a user is able to bind to
3335a770c02SArnaldo Carvalho de Melo  *	a specific sap only or mac + sap.
3341da177e4SLinus Torvalds  *	If the user desires to bind to a specific mac + sap, it is possible to
3351da177e4SLinus Torvalds  *	have multiple sap connections via multiple macs.
3361da177e4SLinus Torvalds  *	Bind and autobind for that matter must enforce the correct sap usage
3371da177e4SLinus Torvalds  *	otherwise all hell will break loose.
3381da177e4SLinus Torvalds  *	Returns: 0 upon success, negative otherwise.
3391da177e4SLinus Torvalds  */
llc_ui_bind(struct socket * sock,struct sockaddr * uaddr,int addrlen)3401da177e4SLinus Torvalds static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
3411da177e4SLinus Torvalds {
3421da177e4SLinus Torvalds 	struct sockaddr_llc *addr = (struct sockaddr_llc *)uaddr;
3431da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
3441da177e4SLinus Torvalds 	struct llc_sock *llc = llc_sk(sk);
3452d327a79SEric Dumazet 	struct net_device *dev = NULL;
3461da177e4SLinus Torvalds 	struct llc_sap *sap;
3471da177e4SLinus Torvalds 	int rc = -EINVAL;
3481da177e4SLinus Torvalds 
3490908cf4dSlinzhang 	lock_sock(sk);
350af426d32SArnaldo Carvalho de Melo 	if (unlikely(!sock_flag(sk, SOCK_ZAPPED) || addrlen != sizeof(*addr)))
3511da177e4SLinus Torvalds 		goto out;
3521da177e4SLinus Torvalds 	rc = -EAFNOSUPPORT;
353a9b11101SEric Dumazet 	if (!addr->sllc_arphrd)
354a9b11101SEric Dumazet 		addr->sllc_arphrd = ARPHRD_ETHER;
355a9b11101SEric Dumazet 	if (unlikely(addr->sllc_family != AF_LLC || addr->sllc_arphrd != ARPHRD_ETHER))
3561da177e4SLinus Torvalds 		goto out;
357c68e747dSTetsuo Handa 	dprintk("%s: binding %02X\n", __func__, addr->sllc_sap);
358cf309e3fSJochen Friedrich 	rc = -ENODEV;
359941666c2SEric Dumazet 	rcu_read_lock();
360abf9d537SOctavian Purdila 	if (sk->sk_bound_dev_if) {
3612d327a79SEric Dumazet 		dev = dev_get_by_index_rcu(&init_net, sk->sk_bound_dev_if);
3622d327a79SEric Dumazet 		if (dev) {
363951fd874SJoe Perches 			if (is_zero_ether_addr(addr->sllc_mac))
3642d327a79SEric Dumazet 				memcpy(addr->sllc_mac, dev->dev_addr,
365abf9d537SOctavian Purdila 				       IFHWADDRLEN);
3662d327a79SEric Dumazet 			if (addr->sllc_arphrd != dev->type ||
367951fd874SJoe Perches 			    !ether_addr_equal(addr->sllc_mac,
3682d327a79SEric Dumazet 					      dev->dev_addr)) {
369abf9d537SOctavian Purdila 				rc = -EINVAL;
3702d327a79SEric Dumazet 				dev = NULL;
371abf9d537SOctavian Purdila 			}
372abf9d537SOctavian Purdila 		}
3732d327a79SEric Dumazet 	} else {
3742d327a79SEric Dumazet 		dev = dev_getbyhwaddr_rcu(&init_net, addr->sllc_arphrd,
375abf9d537SOctavian Purdila 					   addr->sllc_mac);
3762d327a79SEric Dumazet 	}
3772d327a79SEric Dumazet 	dev_hold(dev);
378941666c2SEric Dumazet 	rcu_read_unlock();
3792d327a79SEric Dumazet 	if (!dev)
380cf309e3fSJochen Friedrich 		goto out;
3812d327a79SEric Dumazet 
3821da177e4SLinus Torvalds 	if (!addr->sllc_sap) {
3831da177e4SLinus Torvalds 		rc = -EUSERS;
3841da177e4SLinus Torvalds 		addr->sllc_sap = llc_ui_autoport();
3851da177e4SLinus Torvalds 		if (!addr->sllc_sap)
3861da177e4SLinus Torvalds 			goto out;
3871da177e4SLinus Torvalds 	}
3881da177e4SLinus Torvalds 	sap = llc_sap_find(addr->sllc_sap);
3891da177e4SLinus Torvalds 	if (!sap) {
3901da177e4SLinus Torvalds 		sap = llc_sap_open(addr->sllc_sap, NULL);
3911da177e4SLinus Torvalds 		rc = -EBUSY; /* some other network layer is using the sap */
3921da177e4SLinus Torvalds 		if (!sap)
3931da177e4SLinus Torvalds 			goto out;
3941da177e4SLinus Torvalds 	} else {
3951da177e4SLinus Torvalds 		struct llc_addr laddr, daddr;
3961da177e4SLinus Torvalds 		struct sock *ask;
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds 		memset(&laddr, 0, sizeof(laddr));
3991da177e4SLinus Torvalds 		memset(&daddr, 0, sizeof(daddr));
4001da177e4SLinus Torvalds 		/*
40159c51591SMichael Opdenacker 		 * FIXME: check if the address is multicast,
4021da177e4SLinus Torvalds 		 * 	  only SOCK_DGRAM can do this.
4031da177e4SLinus Torvalds 		 */
4041da177e4SLinus Torvalds 		memcpy(laddr.mac, addr->sllc_mac, IFHWADDRLEN);
4051da177e4SLinus Torvalds 		laddr.lsap = addr->sllc_sap;
4061da177e4SLinus Torvalds 		rc = -EADDRINUSE; /* mac + sap clash. */
40797b1d320SKuniyuki Iwashima 		ask = llc_lookup_established(sap, &daddr, &laddr, &init_net);
4081da177e4SLinus Torvalds 		if (ask) {
4091da177e4SLinus Torvalds 			sock_put(ask);
4106e2144b7SArnaldo Carvalho de Melo 			goto out_put;
4111da177e4SLinus Torvalds 		}
4121da177e4SLinus Torvalds 	}
4132d327a79SEric Dumazet 
4142d327a79SEric Dumazet 	/* Note: We do not expect errors from this point. */
4152d327a79SEric Dumazet 	llc->dev = dev;
4162d327a79SEric Dumazet 	netdev_tracker_alloc(llc->dev, &llc->dev_tracker, GFP_KERNEL);
4172d327a79SEric Dumazet 	dev = NULL;
4182d327a79SEric Dumazet 
4191da177e4SLinus Torvalds 	llc->laddr.lsap = addr->sllc_sap;
4201da177e4SLinus Torvalds 	memcpy(llc->laddr.mac, addr->sllc_mac, IFHWADDRLEN);
4211da177e4SLinus Torvalds 	memcpy(&llc->addr, addr, sizeof(llc->addr));
4221da177e4SLinus Torvalds 	/* assign new connection to its SAP */
4231da177e4SLinus Torvalds 	llc_sap_add_socket(sap, sk);
4241da177e4SLinus Torvalds 	sock_reset_flag(sk, SOCK_ZAPPED);
4251da177e4SLinus Torvalds 	rc = 0;
4266e2144b7SArnaldo Carvalho de Melo out_put:
4276e2144b7SArnaldo Carvalho de Melo 	llc_sap_put(sap);
4281da177e4SLinus Torvalds out:
4292d327a79SEric Dumazet 	dev_put(dev);
4300908cf4dSlinzhang 	release_sock(sk);
4311da177e4SLinus Torvalds 	return rc;
4321da177e4SLinus Torvalds }
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds /**
4351da177e4SLinus Torvalds  *	llc_ui_shutdown - shutdown a connect llc2 socket.
4361da177e4SLinus Torvalds  *	@sock: Socket to shutdown.
4371da177e4SLinus Torvalds  *	@how: What part of the socket to shutdown.
4381da177e4SLinus Torvalds  *
4391da177e4SLinus Torvalds  *	Shutdown a connected llc2 socket. Currently this function only supports
4401da177e4SLinus Torvalds  *	shutting down both sends and receives (2), we could probably make this
4411da177e4SLinus Torvalds  *	function such that a user can shutdown only half the connection but not
4421da177e4SLinus Torvalds  *	right now.
4431da177e4SLinus Torvalds  *	Returns: 0 upon success, negative otherwise.
4441da177e4SLinus Torvalds  */
llc_ui_shutdown(struct socket * sock,int how)4451da177e4SLinus Torvalds static int llc_ui_shutdown(struct socket *sock, int how)
4461da177e4SLinus Torvalds {
4471da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
4481da177e4SLinus Torvalds 	int rc = -ENOTCONN;
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds 	lock_sock(sk);
451af426d32SArnaldo Carvalho de Melo 	if (unlikely(sk->sk_state != TCP_ESTABLISHED))
4521da177e4SLinus Torvalds 		goto out;
4531da177e4SLinus Torvalds 	rc = -EINVAL;
4541da177e4SLinus Torvalds 	if (how != 2)
4551da177e4SLinus Torvalds 		goto out;
4561da177e4SLinus Torvalds 	rc = llc_send_disc(sk);
4571da177e4SLinus Torvalds 	if (!rc)
4581da177e4SLinus Torvalds 		rc = llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo);
4591da177e4SLinus Torvalds 	/* Wake up anyone sleeping in poll */
4601da177e4SLinus Torvalds 	sk->sk_state_change(sk);
4611da177e4SLinus Torvalds out:
4621da177e4SLinus Torvalds 	release_sock(sk);
4631da177e4SLinus Torvalds 	return rc;
4641da177e4SLinus Torvalds }
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds /**
4671da177e4SLinus Torvalds  *	llc_ui_connect - Connect to a remote llc2 mac + sap.
4681da177e4SLinus Torvalds  *	@sock: Socket which will be connected to the remote destination.
4691da177e4SLinus Torvalds  *	@uaddr: Remote and possibly the local address of the new connection.
4701da177e4SLinus Torvalds  *	@addrlen: Size of uaddr structure.
4711da177e4SLinus Torvalds  *	@flags: Operational flags specified by the user.
4721da177e4SLinus Torvalds  *
4731da177e4SLinus Torvalds  *	Connect to a remote llc2 mac + sap. The caller must specify the
4741da177e4SLinus Torvalds  *	destination mac and address to connect to. If the user hasn't previously
4751da177e4SLinus Torvalds  *	called bind(2) with a smac the address of the first interface of the
4761da177e4SLinus Torvalds  *	specified arp type will be used.
4771da177e4SLinus Torvalds  *	This function will autobind if user did not previously call bind.
4781da177e4SLinus Torvalds  *	Returns: 0 upon success, negative otherwise.
4791da177e4SLinus Torvalds  */
llc_ui_connect(struct socket * sock,struct sockaddr * uaddr,int addrlen,int flags)4801da177e4SLinus Torvalds static int llc_ui_connect(struct socket *sock, struct sockaddr *uaddr,
4811da177e4SLinus Torvalds 			  int addrlen, int flags)
4821da177e4SLinus Torvalds {
4831da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
4841da177e4SLinus Torvalds 	struct llc_sock *llc = llc_sk(sk);
4851da177e4SLinus Torvalds 	struct sockaddr_llc *addr = (struct sockaddr_llc *)uaddr;
4861da177e4SLinus Torvalds 	int rc = -EINVAL;
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds 	lock_sock(sk);
489af426d32SArnaldo Carvalho de Melo 	if (unlikely(addrlen != sizeof(*addr)))
4901da177e4SLinus Torvalds 		goto out;
4911da177e4SLinus Torvalds 	rc = -EAFNOSUPPORT;
492af426d32SArnaldo Carvalho de Melo 	if (unlikely(addr->sllc_family != AF_LLC))
493af426d32SArnaldo Carvalho de Melo 		goto out;
494af426d32SArnaldo Carvalho de Melo 	if (unlikely(sk->sk_type != SOCK_STREAM))
495af426d32SArnaldo Carvalho de Melo 		goto out;
496af426d32SArnaldo Carvalho de Melo 	rc = -EALREADY;
497af426d32SArnaldo Carvalho de Melo 	if (unlikely(sock->state == SS_CONNECTING))
4981da177e4SLinus Torvalds 		goto out;
4991da177e4SLinus Torvalds 	/* bind connection to sap if user hasn't done it. */
5001da177e4SLinus Torvalds 	if (sock_flag(sk, SOCK_ZAPPED)) {
5011da177e4SLinus Torvalds 		/* bind to sap with null dev, exclusive */
5021da177e4SLinus Torvalds 		rc = llc_ui_autobind(sock, addr);
5031da177e4SLinus Torvalds 		if (rc)
5041da177e4SLinus Torvalds 			goto out;
5055564af21SJochen Friedrich 	}
5061da177e4SLinus Torvalds 	llc->daddr.lsap = addr->sllc_sap;
5071da177e4SLinus Torvalds 	memcpy(llc->daddr.mac, addr->sllc_mac, IFHWADDRLEN);
5081da177e4SLinus Torvalds 	sock->state = SS_CONNECTING;
5091da177e4SLinus Torvalds 	sk->sk_state   = TCP_SYN_SENT;
5101da177e4SLinus Torvalds 	llc->link   = llc_ui_next_link_no(llc->sap->laddr.lsap);
511774ccb4fSArnaldo Carvalho de Melo 	rc = llc_establish_connection(sk, llc->dev->dev_addr,
5121da177e4SLinus Torvalds 				      addr->sllc_mac, addr->sllc_sap);
5131da177e4SLinus Torvalds 	if (rc) {
5140dc47877SHarvey Harrison 		dprintk("%s: llc_ui_send_conn failed :-(\n", __func__);
5151da177e4SLinus Torvalds 		sock->state  = SS_UNCONNECTED;
5161da177e4SLinus Torvalds 		sk->sk_state = TCP_CLOSE;
5171da177e4SLinus Torvalds 		goto out;
5181da177e4SLinus Torvalds 	}
519b35bd110SArnaldo Carvalho de Melo 
520b35bd110SArnaldo Carvalho de Melo 	if (sk->sk_state == TCP_SYN_SENT) {
52154fb7f25SArnaldo Carvalho de Melo 		const long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
522b35bd110SArnaldo Carvalho de Melo 
523b35bd110SArnaldo Carvalho de Melo 		if (!timeo || !llc_ui_wait_for_conn(sk, timeo))
524b35bd110SArnaldo Carvalho de Melo 			goto out;
525b35bd110SArnaldo Carvalho de Melo 
526b35bd110SArnaldo Carvalho de Melo 		rc = sock_intr_errno(timeo);
527b35bd110SArnaldo Carvalho de Melo 		if (signal_pending(current))
528b35bd110SArnaldo Carvalho de Melo 			goto out;
529b35bd110SArnaldo Carvalho de Melo 	}
530b35bd110SArnaldo Carvalho de Melo 
531b35bd110SArnaldo Carvalho de Melo 	if (sk->sk_state == TCP_CLOSE)
532b35bd110SArnaldo Carvalho de Melo 		goto sock_error;
533b35bd110SArnaldo Carvalho de Melo 
534b35bd110SArnaldo Carvalho de Melo 	sock->state = SS_CONNECTED;
535b35bd110SArnaldo Carvalho de Melo 	rc = 0;
5361da177e4SLinus Torvalds out:
5371da177e4SLinus Torvalds 	release_sock(sk);
5381da177e4SLinus Torvalds 	return rc;
539b35bd110SArnaldo Carvalho de Melo sock_error:
540b35bd110SArnaldo Carvalho de Melo 	rc = sock_error(sk) ? : -ECONNABORTED;
541b35bd110SArnaldo Carvalho de Melo 	sock->state = SS_UNCONNECTED;
542b35bd110SArnaldo Carvalho de Melo 	goto out;
5431da177e4SLinus Torvalds }
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds /**
5461da177e4SLinus Torvalds  *	llc_ui_listen - allow a normal socket to accept incoming connections
5471da177e4SLinus Torvalds  *	@sock: Socket to allow incoming connections on.
5481da177e4SLinus Torvalds  *	@backlog: Number of connections to queue.
5491da177e4SLinus Torvalds  *
5501da177e4SLinus Torvalds  *	Allow a normal socket to accept incoming connections.
5511da177e4SLinus Torvalds  *	Returns 0 upon success, negative otherwise.
5521da177e4SLinus Torvalds  */
llc_ui_listen(struct socket * sock,int backlog)5531da177e4SLinus Torvalds static int llc_ui_listen(struct socket *sock, int backlog)
5541da177e4SLinus Torvalds {
5551da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
5561da177e4SLinus Torvalds 	int rc = -EINVAL;
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	lock_sock(sk);
559af426d32SArnaldo Carvalho de Melo 	if (unlikely(sock->state != SS_UNCONNECTED))
5601da177e4SLinus Torvalds 		goto out;
5611da177e4SLinus Torvalds 	rc = -EOPNOTSUPP;
562af426d32SArnaldo Carvalho de Melo 	if (unlikely(sk->sk_type != SOCK_STREAM))
5631da177e4SLinus Torvalds 		goto out;
5641da177e4SLinus Torvalds 	rc = -EAGAIN;
5651da177e4SLinus Torvalds 	if (sock_flag(sk, SOCK_ZAPPED))
5661da177e4SLinus Torvalds 		goto out;
5671da177e4SLinus Torvalds 	rc = 0;
56895c96174SEric Dumazet 	if (!(unsigned int)backlog)	/* BSDism */
5691da177e4SLinus Torvalds 		backlog = 1;
5701da177e4SLinus Torvalds 	sk->sk_max_ack_backlog = backlog;
5711da177e4SLinus Torvalds 	if (sk->sk_state != TCP_LISTEN) {
5721da177e4SLinus Torvalds 		sk->sk_ack_backlog = 0;
5731da177e4SLinus Torvalds 		sk->sk_state	   = TCP_LISTEN;
5741da177e4SLinus Torvalds 	}
5751da177e4SLinus Torvalds 	sk->sk_socket->flags |= __SO_ACCEPTCON;
5761da177e4SLinus Torvalds out:
5771da177e4SLinus Torvalds 	release_sock(sk);
5781da177e4SLinus Torvalds 	return rc;
5791da177e4SLinus Torvalds }
5801da177e4SLinus Torvalds 
llc_ui_wait_for_disc(struct sock * sk,long timeout)58154fb7f25SArnaldo Carvalho de Melo static int llc_ui_wait_for_disc(struct sock *sk, long timeout)
5821da177e4SLinus Torvalds {
583d9dc8b0fSWANG Cong 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
584b35bd110SArnaldo Carvalho de Melo 	int rc = 0;
5851da177e4SLinus Torvalds 
586d9dc8b0fSWANG Cong 	add_wait_queue(sk_sleep(sk), &wait);
58754fb7f25SArnaldo Carvalho de Melo 	while (1) {
588d0ac89f6SEric Dumazet 		if (sk_wait_event(sk, &timeout,
589d0ac89f6SEric Dumazet 				  READ_ONCE(sk->sk_state) == TCP_CLOSE, &wait))
59054fb7f25SArnaldo Carvalho de Melo 			break;
5911da177e4SLinus Torvalds 		rc = -ERESTARTSYS;
5921da177e4SLinus Torvalds 		if (signal_pending(current))
5931da177e4SLinus Torvalds 			break;
5941da177e4SLinus Torvalds 		rc = -EAGAIN;
5951da177e4SLinus Torvalds 		if (!timeout)
5961da177e4SLinus Torvalds 			break;
597b35bd110SArnaldo Carvalho de Melo 		rc = 0;
5981da177e4SLinus Torvalds 	}
599d9dc8b0fSWANG Cong 	remove_wait_queue(sk_sleep(sk), &wait);
6001da177e4SLinus Torvalds 	return rc;
6011da177e4SLinus Torvalds }
6021da177e4SLinus Torvalds 
llc_ui_wait_for_conn(struct sock * sk,long timeout)6035ff904d5SAlan Cox static bool llc_ui_wait_for_conn(struct sock *sk, long timeout)
6041da177e4SLinus Torvalds {
605d9dc8b0fSWANG Cong 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
6061da177e4SLinus Torvalds 
607d9dc8b0fSWANG Cong 	add_wait_queue(sk_sleep(sk), &wait);
60854fb7f25SArnaldo Carvalho de Melo 	while (1) {
609d0ac89f6SEric Dumazet 		if (sk_wait_event(sk, &timeout,
610d0ac89f6SEric Dumazet 				  READ_ONCE(sk->sk_state) != TCP_SYN_SENT, &wait))
61154fb7f25SArnaldo Carvalho de Melo 			break;
612b35bd110SArnaldo Carvalho de Melo 		if (signal_pending(current) || !timeout)
6131da177e4SLinus Torvalds 			break;
6141da177e4SLinus Torvalds 	}
615d9dc8b0fSWANG Cong 	remove_wait_queue(sk_sleep(sk), &wait);
616b35bd110SArnaldo Carvalho de Melo 	return timeout;
6171da177e4SLinus Torvalds }
6181da177e4SLinus Torvalds 
llc_ui_wait_for_busy_core(struct sock * sk,long timeout)61954fb7f25SArnaldo Carvalho de Melo static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout)
6201da177e4SLinus Torvalds {
621d9dc8b0fSWANG Cong 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
6221da177e4SLinus Torvalds 	struct llc_sock *llc = llc_sk(sk);
6231da177e4SLinus Torvalds 	int rc;
6241da177e4SLinus Torvalds 
625d9dc8b0fSWANG Cong 	add_wait_queue(sk_sleep(sk), &wait);
62654fb7f25SArnaldo Carvalho de Melo 	while (1) {
6271da177e4SLinus Torvalds 		rc = 0;
62854fb7f25SArnaldo Carvalho de Melo 		if (sk_wait_event(sk, &timeout,
629d0ac89f6SEric Dumazet 				  (READ_ONCE(sk->sk_shutdown) & RCV_SHUTDOWN) ||
63054fb7f25SArnaldo Carvalho de Melo 				  (!llc_data_accept_state(llc->state) &&
631451677c4SJochen Friedrich 				   !llc->remote_busy_flag &&
632d9dc8b0fSWANG Cong 				   !llc->p_flag), &wait))
633b35bd110SArnaldo Carvalho de Melo 			break;
6341da177e4SLinus Torvalds 		rc = -ERESTARTSYS;
6351da177e4SLinus Torvalds 		if (signal_pending(current))
6361da177e4SLinus Torvalds 			break;
6371da177e4SLinus Torvalds 		rc = -EAGAIN;
6381da177e4SLinus Torvalds 		if (!timeout)
6391da177e4SLinus Torvalds 			break;
6401da177e4SLinus Torvalds 	}
641d9dc8b0fSWANG Cong 	remove_wait_queue(sk_sleep(sk), &wait);
6421da177e4SLinus Torvalds 	return rc;
6431da177e4SLinus Torvalds }
6441da177e4SLinus Torvalds 
llc_wait_data(struct sock * sk,long timeo)6452928c19eSArnaldo Carvalho de Melo static int llc_wait_data(struct sock *sk, long timeo)
646afdbe357SArnaldo Carvalho de Melo {
647afdbe357SArnaldo Carvalho de Melo 	int rc;
648afdbe357SArnaldo Carvalho de Melo 
649afdbe357SArnaldo Carvalho de Melo 	while (1) {
650afdbe357SArnaldo Carvalho de Melo 		/*
651afdbe357SArnaldo Carvalho de Melo 		 * POSIX 1003.1g mandates this order.
652afdbe357SArnaldo Carvalho de Melo 		 */
653afdbe357SArnaldo Carvalho de Melo 		rc = sock_error(sk);
654c1cbe4b7SBenjamin LaHaise 		if (rc)
655afdbe357SArnaldo Carvalho de Melo 			break;
656afdbe357SArnaldo Carvalho de Melo 		rc = 0;
657afdbe357SArnaldo Carvalho de Melo 		if (sk->sk_shutdown & RCV_SHUTDOWN)
658afdbe357SArnaldo Carvalho de Melo 			break;
659afdbe357SArnaldo Carvalho de Melo 		rc = -EAGAIN;
660afdbe357SArnaldo Carvalho de Melo 		if (!timeo)
661afdbe357SArnaldo Carvalho de Melo 			break;
662afdbe357SArnaldo Carvalho de Melo 		rc = sock_intr_errno(timeo);
663afdbe357SArnaldo Carvalho de Melo 		if (signal_pending(current))
664afdbe357SArnaldo Carvalho de Melo 			break;
665afdbe357SArnaldo Carvalho de Melo 		rc = 0;
666dfbafc99SSabrina Dubroca 		if (sk_wait_data(sk, &timeo, NULL))
667afdbe357SArnaldo Carvalho de Melo 			break;
668afdbe357SArnaldo Carvalho de Melo 	}
669afdbe357SArnaldo Carvalho de Melo 	return rc;
670afdbe357SArnaldo Carvalho de Melo }
671afdbe357SArnaldo Carvalho de Melo 
llc_cmsg_rcv(struct msghdr * msg,struct sk_buff * skb)672e5cd6fe3SOctavian Purdila static void llc_cmsg_rcv(struct msghdr *msg, struct sk_buff *skb)
673e5cd6fe3SOctavian Purdila {
674e5cd6fe3SOctavian Purdila 	struct llc_sock *llc = llc_sk(skb->sk);
675e5cd6fe3SOctavian Purdila 
676e5cd6fe3SOctavian Purdila 	if (llc->cmsg_flags & LLC_CMSG_PKTINFO) {
677e5cd6fe3SOctavian Purdila 		struct llc_pktinfo info;
678e5cd6fe3SOctavian Purdila 
679b8670c09SKangjie Lu 		memset(&info, 0, sizeof(info));
680e5cd6fe3SOctavian Purdila 		info.lpi_ifindex = llc_sk(skb->sk)->dev->ifindex;
681e5cd6fe3SOctavian Purdila 		llc_pdu_decode_dsap(skb, &info.lpi_sap);
682e5cd6fe3SOctavian Purdila 		llc_pdu_decode_da(skb, info.lpi_mac);
683e5cd6fe3SOctavian Purdila 		put_cmsg(msg, SOL_LLC, LLC_OPT_PKTINFO, sizeof(info), &info);
684e5cd6fe3SOctavian Purdila 	}
685e5cd6fe3SOctavian Purdila }
686e5cd6fe3SOctavian Purdila 
6871da177e4SLinus Torvalds /**
6881da177e4SLinus Torvalds  *	llc_ui_accept - accept a new incoming connection.
6891da177e4SLinus Torvalds  *	@sock: Socket which connections arrive on.
6901da177e4SLinus Torvalds  *	@newsock: Socket to move incoming connection to.
6911da177e4SLinus Torvalds  *	@flags: User specified operational flags.
692cdfbabfbSDavid Howells  *	@kern: If the socket is kernel internal
6931da177e4SLinus Torvalds  *
6941da177e4SLinus Torvalds  *	Accept a new incoming connection.
6951da177e4SLinus Torvalds  *	Returns 0 upon success, negative otherwise.
6961da177e4SLinus Torvalds  */
llc_ui_accept(struct socket * sock,struct socket * newsock,int flags,bool kern)697cdfbabfbSDavid Howells static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags,
698cdfbabfbSDavid Howells 			 bool kern)
6991da177e4SLinus Torvalds {
7001da177e4SLinus Torvalds 	struct sock *sk = sock->sk, *newsk;
7011da177e4SLinus Torvalds 	struct llc_sock *llc, *newllc;
7021da177e4SLinus Torvalds 	struct sk_buff *skb;
7031da177e4SLinus Torvalds 	int rc = -EOPNOTSUPP;
7041da177e4SLinus Torvalds 
7050dc47877SHarvey Harrison 	dprintk("%s: accepting on %02X\n", __func__,
7061da177e4SLinus Torvalds 		llc_sk(sk)->laddr.lsap);
7071da177e4SLinus Torvalds 	lock_sock(sk);
708af426d32SArnaldo Carvalho de Melo 	if (unlikely(sk->sk_type != SOCK_STREAM))
7091da177e4SLinus Torvalds 		goto out;
7101da177e4SLinus Torvalds 	rc = -EINVAL;
711af426d32SArnaldo Carvalho de Melo 	if (unlikely(sock->state != SS_UNCONNECTED ||
712af426d32SArnaldo Carvalho de Melo 		     sk->sk_state != TCP_LISTEN))
7131da177e4SLinus Torvalds 		goto out;
7141da177e4SLinus Torvalds 	/* wait for a connection to arrive. */
715b35bd110SArnaldo Carvalho de Melo 	if (skb_queue_empty(&sk->sk_receive_queue)) {
716afdbe357SArnaldo Carvalho de Melo 		rc = llc_wait_data(sk, sk->sk_rcvtimeo);
7171da177e4SLinus Torvalds 		if (rc)
7181da177e4SLinus Torvalds 			goto out;
719b35bd110SArnaldo Carvalho de Melo 	}
7200dc47877SHarvey Harrison 	dprintk("%s: got a new connection on %02X\n", __func__,
7211da177e4SLinus Torvalds 		llc_sk(sk)->laddr.lsap);
7221da177e4SLinus Torvalds 	skb = skb_dequeue(&sk->sk_receive_queue);
7231da177e4SLinus Torvalds 	rc = -EINVAL;
7241da177e4SLinus Torvalds 	if (!skb->sk)
7251da177e4SLinus Torvalds 		goto frees;
7261da177e4SLinus Torvalds 	rc = 0;
7271da177e4SLinus Torvalds 	newsk = skb->sk;
7281da177e4SLinus Torvalds 	/* attach connection to a new socket. */
7291da177e4SLinus Torvalds 	llc_ui_sk_init(newsock, newsk);
7301da177e4SLinus Torvalds 	sock_reset_flag(newsk, SOCK_ZAPPED);
7311da177e4SLinus Torvalds 	newsk->sk_state		= TCP_ESTABLISHED;
7321da177e4SLinus Torvalds 	newsock->state		= SS_CONNECTED;
7331da177e4SLinus Torvalds 	llc			= llc_sk(sk);
7341da177e4SLinus Torvalds 	newllc			= llc_sk(newsk);
7351da177e4SLinus Torvalds 	memcpy(&newllc->addr, &llc->addr, sizeof(newllc->addr));
7361da177e4SLinus Torvalds 	newllc->link = llc_ui_next_link_no(newllc->laddr.lsap);
7371da177e4SLinus Torvalds 
7381da177e4SLinus Torvalds 	/* put original socket back into a clean listen state. */
7391da177e4SLinus Torvalds 	sk->sk_state = TCP_LISTEN;
7407976a11bSEric Dumazet 	sk_acceptq_removed(sk);
7410dc47877SHarvey Harrison 	dprintk("%s: ok success on %02X, client on %02X\n", __func__,
7421da177e4SLinus Torvalds 		llc_sk(sk)->addr.sllc_sap, newllc->daddr.lsap);
7431da177e4SLinus Torvalds frees:
7441da177e4SLinus Torvalds 	kfree_skb(skb);
7451da177e4SLinus Torvalds out:
7461da177e4SLinus Torvalds 	release_sock(sk);
7471da177e4SLinus Torvalds 	return rc;
7481da177e4SLinus Torvalds }
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds /**
7511da177e4SLinus Torvalds  *	llc_ui_recvmsg - copy received data to the socket user.
7521da177e4SLinus Torvalds  *	@sock: Socket to copy data from.
7531da177e4SLinus Torvalds  *	@msg: Various user space related information.
7548420e1b5SArnaldo Carvalho de Melo  *	@len: Size of user buffer.
7551da177e4SLinus Torvalds  *	@flags: User specified flags.
7561da177e4SLinus Torvalds  *
7571da177e4SLinus Torvalds  *	Copy received data to the socket user.
7581da177e4SLinus Torvalds  *	Returns non-negative upon success, negative otherwise.
7591da177e4SLinus Torvalds  */
llc_ui_recvmsg(struct socket * sock,struct msghdr * msg,size_t len,int flags)7601b784140SYing Xue static int llc_ui_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
7611b784140SYing Xue 			  int flags)
7621da177e4SLinus Torvalds {
763342dfc30SSteffen Hurrle 	DECLARE_SOCKADDR(struct sockaddr_llc *, uaddr, msg->msg_name);
7648420e1b5SArnaldo Carvalho de Melo 	const int nonblock = flags & MSG_DONTWAIT;
7658420e1b5SArnaldo Carvalho de Melo 	struct sk_buff *skb = NULL;
7668420e1b5SArnaldo Carvalho de Melo 	struct sock *sk = sock->sk;
7678420e1b5SArnaldo Carvalho de Melo 	struct llc_sock *llc = llc_sk(sk);
7681da177e4SLinus Torvalds 	size_t copied = 0;
7698420e1b5SArnaldo Carvalho de Melo 	u32 peek_seq = 0;
7704d231b76SDaniel Borkmann 	u32 *seq, skb_len;
7718420e1b5SArnaldo Carvalho de Melo 	unsigned long used;
7728420e1b5SArnaldo Carvalho de Melo 	int target;	/* Read at least this many bytes */
7738420e1b5SArnaldo Carvalho de Melo 	long timeo;
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds 	lock_sock(sk);
7768420e1b5SArnaldo Carvalho de Melo 	copied = -ENOTCONN;
77729efcd26SStephen Hemminger 	if (unlikely(sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN))
7781da177e4SLinus Torvalds 		goto out;
7798420e1b5SArnaldo Carvalho de Melo 
7808420e1b5SArnaldo Carvalho de Melo 	timeo = sock_rcvtimeo(sk, nonblock);
7818420e1b5SArnaldo Carvalho de Melo 
7828420e1b5SArnaldo Carvalho de Melo 	seq = &llc->copied_seq;
7838420e1b5SArnaldo Carvalho de Melo 	if (flags & MSG_PEEK) {
7848420e1b5SArnaldo Carvalho de Melo 		peek_seq = llc->copied_seq;
7858420e1b5SArnaldo Carvalho de Melo 		seq = &peek_seq;
7861da177e4SLinus Torvalds 	}
7878420e1b5SArnaldo Carvalho de Melo 
7888420e1b5SArnaldo Carvalho de Melo 	target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
7898420e1b5SArnaldo Carvalho de Melo 	copied = 0;
7908420e1b5SArnaldo Carvalho de Melo 
7918420e1b5SArnaldo Carvalho de Melo 	do {
7928420e1b5SArnaldo Carvalho de Melo 		u32 offset;
7938420e1b5SArnaldo Carvalho de Melo 
7948420e1b5SArnaldo Carvalho de Melo 		/*
7958420e1b5SArnaldo Carvalho de Melo 		 * We need to check signals first, to get correct SIGURG
7968420e1b5SArnaldo Carvalho de Melo 		 * handling. FIXME: Need to check this doesn't impact 1003.1g
7978420e1b5SArnaldo Carvalho de Melo 		 * and move it down to the bottom of the loop
7988420e1b5SArnaldo Carvalho de Melo 		 */
7998420e1b5SArnaldo Carvalho de Melo 		if (signal_pending(current)) {
8008420e1b5SArnaldo Carvalho de Melo 			if (copied)
8018420e1b5SArnaldo Carvalho de Melo 				break;
8028420e1b5SArnaldo Carvalho de Melo 			copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
8038420e1b5SArnaldo Carvalho de Melo 			break;
8041da177e4SLinus Torvalds 		}
8058420e1b5SArnaldo Carvalho de Melo 
8068420e1b5SArnaldo Carvalho de Melo 		/* Next get a buffer. */
8078420e1b5SArnaldo Carvalho de Melo 
8088420e1b5SArnaldo Carvalho de Melo 		skb = skb_peek(&sk->sk_receive_queue);
8098420e1b5SArnaldo Carvalho de Melo 		if (skb) {
8108420e1b5SArnaldo Carvalho de Melo 			offset = *seq;
8118420e1b5SArnaldo Carvalho de Melo 			goto found_ok_skb;
8121da177e4SLinus Torvalds 		}
8138420e1b5SArnaldo Carvalho de Melo 		/* Well, if we have backlog, try to process it now yet. */
8148420e1b5SArnaldo Carvalho de Melo 
8159ed498c6SEric Dumazet 		if (copied >= target && !READ_ONCE(sk->sk_backlog.tail))
8168420e1b5SArnaldo Carvalho de Melo 			break;
8178420e1b5SArnaldo Carvalho de Melo 
8188420e1b5SArnaldo Carvalho de Melo 		if (copied) {
8198420e1b5SArnaldo Carvalho de Melo 			if (sk->sk_err ||
8208420e1b5SArnaldo Carvalho de Melo 			    sk->sk_state == TCP_CLOSE ||
8218420e1b5SArnaldo Carvalho de Melo 			    (sk->sk_shutdown & RCV_SHUTDOWN) ||
8228420e1b5SArnaldo Carvalho de Melo 			    !timeo ||
8238420e1b5SArnaldo Carvalho de Melo 			    (flags & MSG_PEEK))
8248420e1b5SArnaldo Carvalho de Melo 				break;
8258420e1b5SArnaldo Carvalho de Melo 		} else {
8268420e1b5SArnaldo Carvalho de Melo 			if (sock_flag(sk, SOCK_DONE))
8278420e1b5SArnaldo Carvalho de Melo 				break;
8288420e1b5SArnaldo Carvalho de Melo 
8298420e1b5SArnaldo Carvalho de Melo 			if (sk->sk_err) {
8308420e1b5SArnaldo Carvalho de Melo 				copied = sock_error(sk);
8318420e1b5SArnaldo Carvalho de Melo 				break;
8328420e1b5SArnaldo Carvalho de Melo 			}
8338420e1b5SArnaldo Carvalho de Melo 			if (sk->sk_shutdown & RCV_SHUTDOWN)
8348420e1b5SArnaldo Carvalho de Melo 				break;
8358420e1b5SArnaldo Carvalho de Melo 
83629efcd26SStephen Hemminger 			if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSE) {
8378420e1b5SArnaldo Carvalho de Melo 				if (!sock_flag(sk, SOCK_DONE)) {
8388420e1b5SArnaldo Carvalho de Melo 					/*
8398420e1b5SArnaldo Carvalho de Melo 					 * This occurs when user tries to read
8408420e1b5SArnaldo Carvalho de Melo 					 * from never connected socket.
8418420e1b5SArnaldo Carvalho de Melo 					 */
8428420e1b5SArnaldo Carvalho de Melo 					copied = -ENOTCONN;
8438420e1b5SArnaldo Carvalho de Melo 					break;
8448420e1b5SArnaldo Carvalho de Melo 				}
8458420e1b5SArnaldo Carvalho de Melo 				break;
8468420e1b5SArnaldo Carvalho de Melo 			}
8478420e1b5SArnaldo Carvalho de Melo 			if (!timeo) {
8488420e1b5SArnaldo Carvalho de Melo 				copied = -EAGAIN;
8498420e1b5SArnaldo Carvalho de Melo 				break;
8508420e1b5SArnaldo Carvalho de Melo 			}
8518420e1b5SArnaldo Carvalho de Melo 		}
8528420e1b5SArnaldo Carvalho de Melo 
8538420e1b5SArnaldo Carvalho de Melo 		if (copied >= target) { /* Do not sleep, just process backlog. */
8548420e1b5SArnaldo Carvalho de Melo 			release_sock(sk);
8558420e1b5SArnaldo Carvalho de Melo 			lock_sock(sk);
8568420e1b5SArnaldo Carvalho de Melo 		} else
857dfbafc99SSabrina Dubroca 			sk_wait_data(sk, &timeo, NULL);
8588420e1b5SArnaldo Carvalho de Melo 
8598420e1b5SArnaldo Carvalho de Melo 		if ((flags & MSG_PEEK) && peek_seq != llc->copied_seq) {
860e87cc472SJoe Perches 			net_dbg_ratelimited("LLC(%s:%d): Application bug, race in MSG_PEEK\n",
861e87cc472SJoe Perches 					    current->comm,
862e87cc472SJoe Perches 					    task_pid_nr(current));
8638420e1b5SArnaldo Carvalho de Melo 			peek_seq = llc->copied_seq;
8648420e1b5SArnaldo Carvalho de Melo 		}
8658420e1b5SArnaldo Carvalho de Melo 		continue;
8668420e1b5SArnaldo Carvalho de Melo 	found_ok_skb:
8674d231b76SDaniel Borkmann 		skb_len = skb->len;
8688420e1b5SArnaldo Carvalho de Melo 		/* Ok so how much can we use? */
8698420e1b5SArnaldo Carvalho de Melo 		used = skb->len - offset;
8708420e1b5SArnaldo Carvalho de Melo 		if (len < used)
8718420e1b5SArnaldo Carvalho de Melo 			used = len;
8728420e1b5SArnaldo Carvalho de Melo 
8738420e1b5SArnaldo Carvalho de Melo 		if (!(flags & MSG_TRUNC)) {
87451f3d02bSDavid S. Miller 			int rc = skb_copy_datagram_msg(skb, offset, msg, used);
8758420e1b5SArnaldo Carvalho de Melo 			if (rc) {
8768420e1b5SArnaldo Carvalho de Melo 				/* Exception. Bailout! */
8778420e1b5SArnaldo Carvalho de Melo 				if (!copied)
8788420e1b5SArnaldo Carvalho de Melo 					copied = -EFAULT;
8798420e1b5SArnaldo Carvalho de Melo 				break;
8808420e1b5SArnaldo Carvalho de Melo 			}
8818420e1b5SArnaldo Carvalho de Melo 		}
8828420e1b5SArnaldo Carvalho de Melo 
8838420e1b5SArnaldo Carvalho de Melo 		*seq += used;
8848420e1b5SArnaldo Carvalho de Melo 		copied += used;
8858420e1b5SArnaldo Carvalho de Melo 		len -= used;
8868420e1b5SArnaldo Carvalho de Melo 
8879cef310fSAlex Juncu 		/* For non stream protcols we get one packet per recvmsg call */
8889cef310fSAlex Juncu 		if (sk->sk_type != SOCK_STREAM)
8899cef310fSAlex Juncu 			goto copy_uaddr;
8909cef310fSAlex Juncu 
8918420e1b5SArnaldo Carvalho de Melo 		if (!(flags & MSG_PEEK)) {
892604d415eSEric Dumazet 			skb_unlink(skb, &sk->sk_receive_queue);
893604d415eSEric Dumazet 			kfree_skb(skb);
8948420e1b5SArnaldo Carvalho de Melo 			*seq = 0;
8958420e1b5SArnaldo Carvalho de Melo 		}
89630a584d9SStephen Hemminger 
89730a584d9SStephen Hemminger 		/* Partial read */
8984d231b76SDaniel Borkmann 		if (used + offset < skb_len)
89930a584d9SStephen Hemminger 			continue;
9008420e1b5SArnaldo Carvalho de Melo 	} while (len > 0);
9018420e1b5SArnaldo Carvalho de Melo 
9021da177e4SLinus Torvalds out:
9031da177e4SLinus Torvalds 	release_sock(sk);
9048420e1b5SArnaldo Carvalho de Melo 	return copied;
9058420e1b5SArnaldo Carvalho de Melo copy_uaddr:
9068420e1b5SArnaldo Carvalho de Melo 	if (uaddr != NULL && skb != NULL) {
9078420e1b5SArnaldo Carvalho de Melo 		memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));
9088420e1b5SArnaldo Carvalho de Melo 		msg->msg_namelen = sizeof(*uaddr);
9098420e1b5SArnaldo Carvalho de Melo 	}
910e5cd6fe3SOctavian Purdila 	if (llc_sk(sk)->cmsg_flags)
911e5cd6fe3SOctavian Purdila 		llc_cmsg_rcv(msg, skb);
9129cef310fSAlex Juncu 
9139cef310fSAlex Juncu 	if (!(flags & MSG_PEEK)) {
914604d415eSEric Dumazet 		skb_unlink(skb, &sk->sk_receive_queue);
915604d415eSEric Dumazet 		kfree_skb(skb);
9169cef310fSAlex Juncu 		*seq = 0;
9179cef310fSAlex Juncu 	}
9189cef310fSAlex Juncu 
9198420e1b5SArnaldo Carvalho de Melo 	goto out;
9201da177e4SLinus Torvalds }
9211da177e4SLinus Torvalds 
9221da177e4SLinus Torvalds /**
9231da177e4SLinus Torvalds  *	llc_ui_sendmsg - Transmit data provided by the socket user.
9241da177e4SLinus Torvalds  *	@sock: Socket to transmit data from.
9251da177e4SLinus Torvalds  *	@msg: Various user related information.
9261da177e4SLinus Torvalds  *	@len: Length of data to transmit.
9271da177e4SLinus Torvalds  *
9281da177e4SLinus Torvalds  *	Transmit data provided by the socket user.
9291da177e4SLinus Torvalds  *	Returns non-negative upon success, negative otherwise.
9301da177e4SLinus Torvalds  */
llc_ui_sendmsg(struct socket * sock,struct msghdr * msg,size_t len)9311b784140SYing Xue static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
9321da177e4SLinus Torvalds {
933cafd3ad3SEric Dumazet 	DECLARE_SOCKADDR(struct sockaddr_llc *, addr, msg->msg_name);
9341da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
9351da177e4SLinus Torvalds 	struct llc_sock *llc = llc_sk(sk);
9361da177e4SLinus Torvalds 	int flags = msg->msg_flags;
9371da177e4SLinus Torvalds 	int noblock = flags & MSG_DONTWAIT;
938cafd3ad3SEric Dumazet 	int rc = -EINVAL, copied = 0, hdrlen, hh_len;
939fc8d5db1SEric Biggers 	struct sk_buff *skb = NULL;
940cafd3ad3SEric Dumazet 	struct net_device *dev;
9411da177e4SLinus Torvalds 	size_t size = 0;
9421da177e4SLinus Torvalds 
9430dc47877SHarvey Harrison 	dprintk("%s: sending from %02X to %02X\n", __func__,
9441da177e4SLinus Torvalds 		llc->laddr.lsap, llc->daddr.lsap);
9451da177e4SLinus Torvalds 	lock_sock(sk);
9461da177e4SLinus Torvalds 	if (addr) {
9471da177e4SLinus Torvalds 		if (msg->msg_namelen < sizeof(*addr))
948fc8d5db1SEric Biggers 			goto out;
9491da177e4SLinus Torvalds 	} else {
9501da177e4SLinus Torvalds 		if (llc_ui_addr_null(&llc->addr))
951fc8d5db1SEric Biggers 			goto out;
9521da177e4SLinus Torvalds 		addr = &llc->addr;
9531da177e4SLinus Torvalds 	}
9541da177e4SLinus Torvalds 	/* must bind connection to sap if user hasn't done it. */
9551da177e4SLinus Torvalds 	if (sock_flag(sk, SOCK_ZAPPED)) {
9561da177e4SLinus Torvalds 		/* bind to sap with null dev, exclusive. */
9571da177e4SLinus Torvalds 		rc = llc_ui_autobind(sock, addr);
9581da177e4SLinus Torvalds 		if (rc)
959fc8d5db1SEric Biggers 			goto out;
9601da177e4SLinus Torvalds 	}
961cafd3ad3SEric Dumazet 	dev = llc->dev;
962cafd3ad3SEric Dumazet 	hh_len = LL_RESERVED_SPACE(dev);
963cafd3ad3SEric Dumazet 	hdrlen = llc_ui_header_len(sk, addr);
9641da177e4SLinus Torvalds 	size = hdrlen + len;
965cafd3ad3SEric Dumazet 	size = min_t(size_t, size, READ_ONCE(dev->mtu));
9661da177e4SLinus Torvalds 	copied = size - hdrlen;
9672c5d5b13SEric Dumazet 	rc = -EINVAL;
9682c5d5b13SEric Dumazet 	if (copied < 0)
969fc8d5db1SEric Biggers 		goto out;
9701da177e4SLinus Torvalds 	release_sock(sk);
971cafd3ad3SEric Dumazet 	skb = sock_alloc_send_skb(sk, hh_len + size, noblock, &rc);
9721da177e4SLinus Torvalds 	lock_sock(sk);
9731da177e4SLinus Torvalds 	if (!skb)
974fc8d5db1SEric Biggers 		goto out;
975cafd3ad3SEric Dumazet 	if (sock_flag(sk, SOCK_ZAPPED) ||
976cafd3ad3SEric Dumazet 	    llc->dev != dev ||
977cafd3ad3SEric Dumazet 	    hdrlen != llc_ui_header_len(sk, addr) ||
978cafd3ad3SEric Dumazet 	    hh_len != LL_RESERVED_SPACE(dev) ||
979cafd3ad3SEric Dumazet 	    size > READ_ONCE(dev->mtu))
980cafd3ad3SEric Dumazet 		goto out;
981cafd3ad3SEric Dumazet 	skb->dev      = dev;
9821da177e4SLinus Torvalds 	skb->protocol = llc_proto_type(addr->sllc_arphrd);
983cafd3ad3SEric Dumazet 	skb_reserve(skb, hh_len + hdrlen);
9846ce8e9ceSAl Viro 	rc = memcpy_from_msg(skb_put(skb, copied), msg, copied);
9851da177e4SLinus Torvalds 	if (rc)
9861da177e4SLinus Torvalds 		goto out;
9871da177e4SLinus Torvalds 	if (sk->sk_type == SOCK_DGRAM || addr->sllc_ua) {
9881da177e4SLinus Torvalds 		llc_build_and_send_ui_pkt(llc->sap, skb, addr->sllc_mac,
9891da177e4SLinus Torvalds 					  addr->sllc_sap);
990fc8d5db1SEric Biggers 		skb = NULL;
9911da177e4SLinus Torvalds 		goto out;
9921da177e4SLinus Torvalds 	}
9931da177e4SLinus Torvalds 	if (addr->sllc_test) {
9941da177e4SLinus Torvalds 		llc_build_and_send_test_pkt(llc->sap, skb, addr->sllc_mac,
9951da177e4SLinus Torvalds 					    addr->sllc_sap);
996fc8d5db1SEric Biggers 		skb = NULL;
9971da177e4SLinus Torvalds 		goto out;
9981da177e4SLinus Torvalds 	}
9991da177e4SLinus Torvalds 	if (addr->sllc_xid) {
10001da177e4SLinus Torvalds 		llc_build_and_send_xid_pkt(llc->sap, skb, addr->sllc_mac,
10011da177e4SLinus Torvalds 					   addr->sllc_sap);
1002fc8d5db1SEric Biggers 		skb = NULL;
10031da177e4SLinus Torvalds 		goto out;
10041da177e4SLinus Torvalds 	}
10051da177e4SLinus Torvalds 	rc = -ENOPROTOOPT;
10061da177e4SLinus Torvalds 	if (!(sk->sk_type == SOCK_STREAM && !addr->sllc_ua))
10071da177e4SLinus Torvalds 		goto out;
10081da177e4SLinus Torvalds 	rc = llc_ui_send_data(sk, skb, noblock);
1009fc8d5db1SEric Biggers 	skb = NULL;
10101da177e4SLinus Torvalds out:
10111da177e4SLinus Torvalds 	kfree_skb(skb);
1012fc8d5db1SEric Biggers 	if (rc)
10131da177e4SLinus Torvalds 		dprintk("%s: failed sending from %02X to %02X: %d\n",
10140dc47877SHarvey Harrison 			__func__, llc->laddr.lsap, llc->daddr.lsap, rc);
10151da177e4SLinus Torvalds 	release_sock(sk);
10161da177e4SLinus Torvalds 	return rc ? : copied;
10171da177e4SLinus Torvalds }
10181da177e4SLinus Torvalds 
10191da177e4SLinus Torvalds /**
10201da177e4SLinus Torvalds  *	llc_ui_getname - return the address info of a socket
10211da177e4SLinus Torvalds  *	@sock: Socket to get address of.
10221da177e4SLinus Torvalds  *	@uaddr: Address structure to return information.
10231da177e4SLinus Torvalds  *	@peer: Does user want local or remote address information.
10241da177e4SLinus Torvalds  *
10251da177e4SLinus Torvalds  *	Return the address information of a socket.
10261da177e4SLinus Torvalds  */
llc_ui_getname(struct socket * sock,struct sockaddr * uaddr,int peer)10271da177e4SLinus Torvalds static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr,
10289b2c45d4SDenys Vlasenko 			  int peer)
10291da177e4SLinus Torvalds {
10301da177e4SLinus Torvalds 	struct sockaddr_llc sllc;
10311da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
10321da177e4SLinus Torvalds 	struct llc_sock *llc = llc_sk(sk);
10333592aaebSMathias Krause 	int rc = -EBADF;
10341da177e4SLinus Torvalds 
103528e9fc59SJiri Slaby 	memset(&sllc, 0, sizeof(sllc));
10361da177e4SLinus Torvalds 	lock_sock(sk);
10371da177e4SLinus Torvalds 	if (sock_flag(sk, SOCK_ZAPPED))
10381da177e4SLinus Torvalds 		goto out;
10391da177e4SLinus Torvalds 	if (peer) {
10401da177e4SLinus Torvalds 		rc = -ENOTCONN;
10411da177e4SLinus Torvalds 		if (sk->sk_state != TCP_ESTABLISHED)
10421da177e4SLinus Torvalds 			goto out;
10431da177e4SLinus Torvalds 		if(llc->dev)
10441da177e4SLinus Torvalds 			sllc.sllc_arphrd = llc->dev->type;
10451da177e4SLinus Torvalds 		sllc.sllc_sap = llc->daddr.lsap;
10461da177e4SLinus Torvalds 		memcpy(&sllc.sllc_mac, &llc->daddr.mac, IFHWADDRLEN);
10471da177e4SLinus Torvalds 	} else {
10481da177e4SLinus Torvalds 		rc = -EINVAL;
10491da177e4SLinus Torvalds 		if (!llc->sap)
10501da177e4SLinus Torvalds 			goto out;
10511da177e4SLinus Torvalds 		sllc.sllc_sap = llc->sap->laddr.lsap;
10521da177e4SLinus Torvalds 
10531da177e4SLinus Torvalds 		if (llc->dev) {
10541da177e4SLinus Torvalds 			sllc.sllc_arphrd = llc->dev->type;
1055a186d2aeSJiri Pirko 			memcpy(&sllc.sllc_mac, llc->dev->dev_addr,
10561da177e4SLinus Torvalds 			       IFHWADDRLEN);
10571da177e4SLinus Torvalds 		}
10581da177e4SLinus Torvalds 	}
10591da177e4SLinus Torvalds 	sllc.sllc_family = AF_LLC;
10601da177e4SLinus Torvalds 	memcpy(uaddr, &sllc, sizeof(sllc));
10619b2c45d4SDenys Vlasenko 	rc = sizeof(sllc);
10621da177e4SLinus Torvalds out:
10631da177e4SLinus Torvalds 	release_sock(sk);
10641da177e4SLinus Torvalds 	return rc;
10651da177e4SLinus Torvalds }
10661da177e4SLinus Torvalds 
10671da177e4SLinus Torvalds /**
10681da177e4SLinus Torvalds  *	llc_ui_ioctl - io controls for PF_LLC
10691da177e4SLinus Torvalds  *	@sock: Socket to get/set info
10701da177e4SLinus Torvalds  *	@cmd: command
10711da177e4SLinus Torvalds  *	@arg: optional argument for cmd
10721da177e4SLinus Torvalds  *
10731da177e4SLinus Torvalds  *	get/set info on llc sockets
10741da177e4SLinus Torvalds  */
llc_ui_ioctl(struct socket * sock,unsigned int cmd,unsigned long arg)10751da177e4SLinus Torvalds static int llc_ui_ioctl(struct socket *sock, unsigned int cmd,
10761da177e4SLinus Torvalds 			unsigned long arg)
10771da177e4SLinus Torvalds {
1078b5e5fa5eSChristoph Hellwig 	return -ENOIOCTLCMD;
10791da177e4SLinus Torvalds }
10801da177e4SLinus Torvalds 
10811da177e4SLinus Torvalds /**
10821da177e4SLinus Torvalds  *	llc_ui_setsockopt - set various connection specific parameters.
10831da177e4SLinus Torvalds  *	@sock: Socket to set options on.
10841da177e4SLinus Torvalds  *	@level: Socket level user is requesting operations on.
10851da177e4SLinus Torvalds  *	@optname: Operation name.
10862c53040fSBen Hutchings  *	@optval: User provided operation data.
10871da177e4SLinus Torvalds  *	@optlen: Length of optval.
10881da177e4SLinus Torvalds  *
10891da177e4SLinus Torvalds  *	Set various connection specific parameters.
10901da177e4SLinus Torvalds  */
llc_ui_setsockopt(struct socket * sock,int level,int optname,sockptr_t optval,unsigned int optlen)10911da177e4SLinus Torvalds static int llc_ui_setsockopt(struct socket *sock, int level, int optname,
1092a7b75c5aSChristoph Hellwig 			     sockptr_t optval, unsigned int optlen)
10931da177e4SLinus Torvalds {
10941da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
10951da177e4SLinus Torvalds 	struct llc_sock *llc = llc_sk(sk);
1096339db11bSDan Carpenter 	unsigned int opt;
1097339db11bSDan Carpenter 	int rc = -EINVAL;
10981da177e4SLinus Torvalds 
10991da177e4SLinus Torvalds 	lock_sock(sk);
1100af426d32SArnaldo Carvalho de Melo 	if (unlikely(level != SOL_LLC || optlen != sizeof(int)))
11011da177e4SLinus Torvalds 		goto out;
1102*9a62ca28SMichal Luczaj 	rc = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen);
11031da177e4SLinus Torvalds 	if (rc)
11041da177e4SLinus Torvalds 		goto out;
11051da177e4SLinus Torvalds 	rc = -EINVAL;
11061da177e4SLinus Torvalds 	switch (optname) {
11071da177e4SLinus Torvalds 	case LLC_OPT_RETRY:
11081da177e4SLinus Torvalds 		if (opt > LLC_OPT_MAX_RETRY)
11091da177e4SLinus Torvalds 			goto out;
11101da177e4SLinus Torvalds 		llc->n2 = opt;
11111da177e4SLinus Torvalds 		break;
11121da177e4SLinus Torvalds 	case LLC_OPT_SIZE:
11131da177e4SLinus Torvalds 		if (opt > LLC_OPT_MAX_SIZE)
11141da177e4SLinus Torvalds 			goto out;
11151da177e4SLinus Torvalds 		llc->n1 = opt;
11161da177e4SLinus Torvalds 		break;
11171da177e4SLinus Torvalds 	case LLC_OPT_ACK_TMR_EXP:
11181da177e4SLinus Torvalds 		if (opt > LLC_OPT_MAX_ACK_TMR_EXP)
11191da177e4SLinus Torvalds 			goto out;
1120590232a7SArnaldo Carvalho de Melo 		llc->ack_timer.expire = opt * HZ;
11211da177e4SLinus Torvalds 		break;
11221da177e4SLinus Torvalds 	case LLC_OPT_P_TMR_EXP:
11231da177e4SLinus Torvalds 		if (opt > LLC_OPT_MAX_P_TMR_EXP)
11241da177e4SLinus Torvalds 			goto out;
1125590232a7SArnaldo Carvalho de Melo 		llc->pf_cycle_timer.expire = opt * HZ;
11261da177e4SLinus Torvalds 		break;
11271da177e4SLinus Torvalds 	case LLC_OPT_REJ_TMR_EXP:
11281da177e4SLinus Torvalds 		if (opt > LLC_OPT_MAX_REJ_TMR_EXP)
11291da177e4SLinus Torvalds 			goto out;
1130590232a7SArnaldo Carvalho de Melo 		llc->rej_sent_timer.expire = opt * HZ;
11311da177e4SLinus Torvalds 		break;
11321da177e4SLinus Torvalds 	case LLC_OPT_BUSY_TMR_EXP:
11331da177e4SLinus Torvalds 		if (opt > LLC_OPT_MAX_BUSY_TMR_EXP)
11341da177e4SLinus Torvalds 			goto out;
1135590232a7SArnaldo Carvalho de Melo 		llc->busy_state_timer.expire = opt * HZ;
11361da177e4SLinus Torvalds 		break;
11371da177e4SLinus Torvalds 	case LLC_OPT_TX_WIN:
11381da177e4SLinus Torvalds 		if (opt > LLC_OPT_MAX_WIN)
11391da177e4SLinus Torvalds 			goto out;
11401da177e4SLinus Torvalds 		llc->k = opt;
11411da177e4SLinus Torvalds 		break;
11421da177e4SLinus Torvalds 	case LLC_OPT_RX_WIN:
11431da177e4SLinus Torvalds 		if (opt > LLC_OPT_MAX_WIN)
11441da177e4SLinus Torvalds 			goto out;
11451da177e4SLinus Torvalds 		llc->rw = opt;
11461da177e4SLinus Torvalds 		break;
1147e5cd6fe3SOctavian Purdila 	case LLC_OPT_PKTINFO:
1148e5cd6fe3SOctavian Purdila 		if (opt)
1149e5cd6fe3SOctavian Purdila 			llc->cmsg_flags |= LLC_CMSG_PKTINFO;
1150e5cd6fe3SOctavian Purdila 		else
1151e5cd6fe3SOctavian Purdila 			llc->cmsg_flags &= ~LLC_CMSG_PKTINFO;
1152e5cd6fe3SOctavian Purdila 		break;
11531da177e4SLinus Torvalds 	default:
11541da177e4SLinus Torvalds 		rc = -ENOPROTOOPT;
11551da177e4SLinus Torvalds 		goto out;
11561da177e4SLinus Torvalds 	}
11571da177e4SLinus Torvalds 	rc = 0;
11581da177e4SLinus Torvalds out:
11591da177e4SLinus Torvalds 	release_sock(sk);
11601da177e4SLinus Torvalds 	return rc;
11611da177e4SLinus Torvalds }
11621da177e4SLinus Torvalds 
11631da177e4SLinus Torvalds /**
11641da177e4SLinus Torvalds  *	llc_ui_getsockopt - get connection specific socket info
11651da177e4SLinus Torvalds  *	@sock: Socket to get information from.
11661da177e4SLinus Torvalds  *	@level: Socket level user is requesting operations on.
11671da177e4SLinus Torvalds  *	@optname: Operation name.
11681da177e4SLinus Torvalds  *	@optval: Variable to return operation data in.
11691da177e4SLinus Torvalds  *	@optlen: Length of optval.
11701da177e4SLinus Torvalds  *
11711da177e4SLinus Torvalds  *	Get connection specific socket information.
11721da177e4SLinus Torvalds  */
llc_ui_getsockopt(struct socket * sock,int level,int optname,char __user * optval,int __user * optlen)11731da177e4SLinus Torvalds static int llc_ui_getsockopt(struct socket *sock, int level, int optname,
11741da177e4SLinus Torvalds 			     char __user *optval, int __user *optlen)
11751da177e4SLinus Torvalds {
11761da177e4SLinus Torvalds 	struct sock *sk = sock->sk;
11771da177e4SLinus Torvalds 	struct llc_sock *llc = llc_sk(sk);
11781da177e4SLinus Torvalds 	int val = 0, len = 0, rc = -EINVAL;
11791da177e4SLinus Torvalds 
11801da177e4SLinus Torvalds 	lock_sock(sk);
1181af426d32SArnaldo Carvalho de Melo 	if (unlikely(level != SOL_LLC))
11821da177e4SLinus Torvalds 		goto out;
11831da177e4SLinus Torvalds 	rc = get_user(len, optlen);
11841da177e4SLinus Torvalds 	if (rc)
11851da177e4SLinus Torvalds 		goto out;
11861da177e4SLinus Torvalds 	rc = -EINVAL;
11871da177e4SLinus Torvalds 	if (len != sizeof(int))
11881da177e4SLinus Torvalds 		goto out;
11891da177e4SLinus Torvalds 	switch (optname) {
11901da177e4SLinus Torvalds 	case LLC_OPT_RETRY:
11911da177e4SLinus Torvalds 		val = llc->n2;					break;
11921da177e4SLinus Torvalds 	case LLC_OPT_SIZE:
11931da177e4SLinus Torvalds 		val = llc->n1;					break;
11941da177e4SLinus Torvalds 	case LLC_OPT_ACK_TMR_EXP:
1195590232a7SArnaldo Carvalho de Melo 		val = llc->ack_timer.expire / HZ;		break;
11961da177e4SLinus Torvalds 	case LLC_OPT_P_TMR_EXP:
1197590232a7SArnaldo Carvalho de Melo 		val = llc->pf_cycle_timer.expire / HZ;		break;
11981da177e4SLinus Torvalds 	case LLC_OPT_REJ_TMR_EXP:
1199590232a7SArnaldo Carvalho de Melo 		val = llc->rej_sent_timer.expire / HZ;		break;
12001da177e4SLinus Torvalds 	case LLC_OPT_BUSY_TMR_EXP:
1201590232a7SArnaldo Carvalho de Melo 		val = llc->busy_state_timer.expire / HZ;	break;
12021da177e4SLinus Torvalds 	case LLC_OPT_TX_WIN:
12031da177e4SLinus Torvalds 		val = llc->k;				break;
12041da177e4SLinus Torvalds 	case LLC_OPT_RX_WIN:
12051da177e4SLinus Torvalds 		val = llc->rw;				break;
1206e5cd6fe3SOctavian Purdila 	case LLC_OPT_PKTINFO:
1207e5cd6fe3SOctavian Purdila 		val = (llc->cmsg_flags & LLC_CMSG_PKTINFO) != 0;
1208e5cd6fe3SOctavian Purdila 		break;
12091da177e4SLinus Torvalds 	default:
12101da177e4SLinus Torvalds 		rc = -ENOPROTOOPT;
12111da177e4SLinus Torvalds 		goto out;
12121da177e4SLinus Torvalds 	}
12131da177e4SLinus Torvalds 	rc = 0;
12141da177e4SLinus Torvalds 	if (put_user(len, optlen) || copy_to_user(optval, &val, len))
12151da177e4SLinus Torvalds 		rc = -EFAULT;
12161da177e4SLinus Torvalds out:
12171da177e4SLinus Torvalds 	release_sock(sk);
12181da177e4SLinus Torvalds 	return rc;
12191da177e4SLinus Torvalds }
12201da177e4SLinus Torvalds 
1221ec1b4cf7SStephen Hemminger static const struct net_proto_family llc_ui_family_ops = {
12221da177e4SLinus Torvalds 	.family = PF_LLC,
12231da177e4SLinus Torvalds 	.create = llc_ui_create,
12241da177e4SLinus Torvalds 	.owner	= THIS_MODULE,
12251da177e4SLinus Torvalds };
12261da177e4SLinus Torvalds 
122790ddc4f0SEric Dumazet static const struct proto_ops llc_ui_ops = {
12281da177e4SLinus Torvalds 	.family	     = PF_LLC,
12291da177e4SLinus Torvalds 	.owner       = THIS_MODULE,
12301da177e4SLinus Torvalds 	.release     = llc_ui_release,
12311da177e4SLinus Torvalds 	.bind	     = llc_ui_bind,
12321da177e4SLinus Torvalds 	.connect     = llc_ui_connect,
12331da177e4SLinus Torvalds 	.socketpair  = sock_no_socketpair,
12341da177e4SLinus Torvalds 	.accept      = llc_ui_accept,
12351da177e4SLinus Torvalds 	.getname     = llc_ui_getname,
1236a11e1d43SLinus Torvalds 	.poll	     = datagram_poll,
12371da177e4SLinus Torvalds 	.ioctl       = llc_ui_ioctl,
12381da177e4SLinus Torvalds 	.listen      = llc_ui_listen,
12391da177e4SLinus Torvalds 	.shutdown    = llc_ui_shutdown,
12401da177e4SLinus Torvalds 	.setsockopt  = llc_ui_setsockopt,
12411da177e4SLinus Torvalds 	.getsockopt  = llc_ui_getsockopt,
12421da177e4SLinus Torvalds 	.sendmsg     = llc_ui_sendmsg,
12431da177e4SLinus Torvalds 	.recvmsg     = llc_ui_recvmsg,
12441da177e4SLinus Torvalds 	.mmap	     = sock_no_mmap,
12451da177e4SLinus Torvalds };
12461da177e4SLinus Torvalds 
124701af4a0eSStephen Hemminger static const char llc_proc_err_msg[] __initconst =
1248590232a7SArnaldo Carvalho de Melo 	KERN_CRIT "LLC: Unable to register the proc_fs entries\n";
124901af4a0eSStephen Hemminger static const char llc_sysctl_err_msg[] __initconst =
1250590232a7SArnaldo Carvalho de Melo 	KERN_CRIT "LLC: Unable to register the sysctl entries\n";
125101af4a0eSStephen Hemminger static const char llc_sock_err_msg[] __initconst =
1252590232a7SArnaldo Carvalho de Melo 	KERN_CRIT "LLC: Unable to register the network family\n";
1253590232a7SArnaldo Carvalho de Melo 
llc2_init(void)12541da177e4SLinus Torvalds static int __init llc2_init(void)
12551da177e4SLinus Torvalds {
12561da177e4SLinus Torvalds 	int rc = proto_register(&llc_proto, 0);
12571da177e4SLinus Torvalds 
12581da177e4SLinus Torvalds 	if (rc != 0)
12591da177e4SLinus Torvalds 		goto out;
12601da177e4SLinus Torvalds 
12611da177e4SLinus Torvalds 	llc_build_offset_table();
12621da177e4SLinus Torvalds 	llc_station_init();
12631da177e4SLinus Torvalds 	llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
12641da177e4SLinus Torvalds 	rc = llc_proc_init();
1265590232a7SArnaldo Carvalho de Melo 	if (rc != 0) {
1266590232a7SArnaldo Carvalho de Melo 		printk(llc_proc_err_msg);
1267f4f8720fSBen Hutchings 		goto out_station;
1268590232a7SArnaldo Carvalho de Melo 	}
1269590232a7SArnaldo Carvalho de Melo 	rc = llc_sysctl_init();
1270590232a7SArnaldo Carvalho de Melo 	if (rc) {
1271590232a7SArnaldo Carvalho de Melo 		printk(llc_sysctl_err_msg);
1272590232a7SArnaldo Carvalho de Melo 		goto out_proc;
1273590232a7SArnaldo Carvalho de Melo 	}
1274590232a7SArnaldo Carvalho de Melo 	rc = sock_register(&llc_ui_family_ops);
1275590232a7SArnaldo Carvalho de Melo 	if (rc) {
1276590232a7SArnaldo Carvalho de Melo 		printk(llc_sock_err_msg);
1277590232a7SArnaldo Carvalho de Melo 		goto out_sysctl;
1278590232a7SArnaldo Carvalho de Melo 	}
12791da177e4SLinus Torvalds 	llc_add_pack(LLC_DEST_SAP, llc_sap_handler);
12801da177e4SLinus Torvalds 	llc_add_pack(LLC_DEST_CONN, llc_conn_handler);
12811da177e4SLinus Torvalds out:
12821da177e4SLinus Torvalds 	return rc;
1283590232a7SArnaldo Carvalho de Melo out_sysctl:
1284590232a7SArnaldo Carvalho de Melo 	llc_sysctl_exit();
1285590232a7SArnaldo Carvalho de Melo out_proc:
1286590232a7SArnaldo Carvalho de Melo 	llc_proc_exit();
1287f4f8720fSBen Hutchings out_station:
1288f4f8720fSBen Hutchings 	llc_station_exit();
12891da177e4SLinus Torvalds 	proto_unregister(&llc_proto);
12901da177e4SLinus Torvalds 	goto out;
12911da177e4SLinus Torvalds }
12921da177e4SLinus Torvalds 
llc2_exit(void)12931da177e4SLinus Torvalds static void __exit llc2_exit(void)
12941da177e4SLinus Torvalds {
12951da177e4SLinus Torvalds 	llc_station_exit();
12961da177e4SLinus Torvalds 	llc_remove_pack(LLC_DEST_SAP);
12971da177e4SLinus Torvalds 	llc_remove_pack(LLC_DEST_CONN);
12981da177e4SLinus Torvalds 	sock_unregister(PF_LLC);
12991da177e4SLinus Torvalds 	llc_proc_exit();
1300590232a7SArnaldo Carvalho de Melo 	llc_sysctl_exit();
13011da177e4SLinus Torvalds 	proto_unregister(&llc_proto);
13021da177e4SLinus Torvalds }
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds module_init(llc2_init);
13051da177e4SLinus Torvalds module_exit(llc2_exit);
13061da177e4SLinus Torvalds 
13071da177e4SLinus Torvalds MODULE_LICENSE("GPL");
13081da177e4SLinus Torvalds MODULE_AUTHOR("Procom 1997, Jay Schullist 2001, Arnaldo C. Melo 2001-2003");
13091da177e4SLinus Torvalds MODULE_DESCRIPTION("IEEE 802.2 PF_LLC support");
13101da177e4SLinus Torvalds MODULE_ALIAS_NETPROTO(PF_LLC);
1311