/*
* af_llc.c - LLC User Interface SAPs
* Description:
* Functions in this module are implementation of socket based llc
* communications for the Linux operating system. Support of llc class
* one and class two is provided via SOCK_DGRAM and SOCK_STREAM
* respectively.
*
* An llc2 connection is (mac + sap), only one llc2 sap connection
* is allowed per mac. Though one sap may have multiple mac + sap
* connections.
*
* Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
* 2002-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rtnetlink.h>
#include <linux/init.h>
#include <net/llc.h>
#include <net/llc_sap.h>
#include <net/llc_pdu.h>
#include <net/llc_conn.h>
#include <net/tcp_states.h>
/* remember: uninitialized global data is zeroed because its in .bss */
static u16 llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
static u16 llc_ui_sap_link_no_max[256];
static struct sockaddr_llc llc_ui_addrnull;
static struct proto_ops llc_ui_ops;
static int llc_ui_wait_for_conn(struct sock *sk, int timeout);
static int llc_ui_wait_for_disc(struct sock *sk, int timeout);
static int llc_ui_wait_for_data(struct sock *sk, int timeout);
static int llc_ui_wait_for_busy_core(struct sock *sk, int timeout);
#if 0
#define dprintk(args...) printk(KERN_DEBUG args)
#else
#define dprintk(args...)
#endif
/**
* llc_ui_next_link_no - return the next unused link number for a sap
* @sap: Address of sap to get link number from.
*
* Return the next unused link number for a given sap.
*/
static __inline__ u16 llc_ui_next_link_no(int sap)
{
return llc_ui_sap_link_no_max[sap]++;
}
/**
* llc_proto_type - return eth protocol for ARP header type
* @arphrd: ARP header type.
*
* Given an ARP header type return the corresponding ethernet protocol.
*/
static __inline__ u16 llc_proto_type(u16 arphrd)
{
return arphrd == ARPHRD_IEEE802_TR ?
htons(ETH_P_TR_802_2) : htons(ETH_P_802_2);
}
/**
* llc_ui_addr_null - determines if a address structure is null
* @addr: Address to test if null.
*/
static __inline__ u8 llc_ui_addr_null(struct sockaddr_llc *addr)
{
return !memcmp(addr, &llc_ui_addrnull, sizeof(*addr));
}
/**
* llc_ui_header_len - return length of llc header based on operation
* @sk: Socket which contains a valid llc socket type.
* @addr: Complete sockaddr_llc structure received from the user.
*
* Provide the length of the llc header depending on what kind of
* operation the user would like to perform and the type of socket.
* Returns the correct llc header length.
*/
static __inline__ u8 llc_ui_header_len(struct sock *sk,
struct sockaddr_llc *addr)
{
u8 rc = LLC_PDU_LEN_U;
if (addr->sllc_test || addr->sllc_xid)
rc = LLC_PDU_LEN_U;
else if (sk->sk_type == SOCK_STREAM)
rc = LLC_PDU_LEN_I;
return rc;
}
/**
* llc_ui_send_data - send data via reliable llc2 connection
* @sk: Connection the socket is using.
* @skb: Data the user wishes to send.
* @addr: Source and destination fields provided by the user.
* @noblock: can we block waiting for data?
*
* Send data via reliable llc2 connection.
* Returns 0 upon success, non-zero if action did not succeed.
*/
static int llc_ui_send_data(struct sock* sk, struct sk_buff *skb, int noblock)
{
struct llc_sock* llc = llc_sk(sk);
int rc = 0;
if (llc_data_accept_state(llc->state) || llc->p_flag) {
int timeout = sock_sndtimeo(sk, noblock);
rc = llc_ui_wait_for_busy_core(sk, timeout);
}
if (!rc)
rc = llc_build_and_send_pkt(sk, skb);
return rc;
}
static void llc_ui_sk_init(struct socket *sock, struct sock *sk)
{
sk->sk_type = sock->type;
sk->sk_sleep = &sock->wait;
sk->sk_socket = sock;
sock->sk = sk;
sock->ops = &llc_ui_ops;
}
static struct proto llc_proto = {
.name = "DDP",
.owner = THIS_MODULE,
.obj_size = sizeof(struct llc_sock),
};
/**
* llc_ui_create - alloc and init a new llc_ui socket
* @sock: Socket to initialize and attach allocated sk to.
* @protocol: Unused.
*
* Allocate and initialize a new llc_ui socket, validate the user wants a
* socket type we have available.
* Returns 0 upon success, negative upon failure.
*/
static int llc_ui_create(struct socket *sock, int protocol)
{
struct sock *sk;
int rc = -ESOCKTNOSUPPORT;
if (likely(sock->type == SOCK_DGRAM || sock->type == SOCK_STREAM)) {
rc = -ENOMEM;
sk = llc_sk_alloc(PF_LLC, GFP_KERNEL,