aboutsummaryrefslogtreecommitdiff
path: root/net/llc/af_llc.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/llc/af_llc.c')
-rw-r--r--net/llc/af_llc.c222
1 files changed, 145 insertions, 77 deletions
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index 8171c53bc0e..0080d2b0a8a 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -20,12 +20,12 @@
*
* See the GNU General Public License for more details.
*/
-#include <linux/config.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rtnetlink.h>
#include <linux/init.h>
+#include <linux/slab.h>
#include <net/llc.h>
#include <net/llc_sap.h>
#include <net/llc_pdu.h>
@@ -48,13 +48,17 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout);
#define dprintk(args...)
#endif
+/* Maybe we'll add some more in the future. */
+#define LLC_CMSG_PKTINFO 1
+
+
/**
* 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)
+static inline u16 llc_ui_next_link_no(int sap)
{
return llc_ui_sap_link_no_max[sap]++;
}
@@ -65,17 +69,16 @@ static __inline__ u16 llc_ui_next_link_no(int sap)
*
* Given an ARP header type return the corresponding ethernet protocol.
*/
-static __inline__ u16 llc_proto_type(u16 arphrd)
+static inline __be16 llc_proto_type(u16 arphrd)
{
- return arphrd == ARPHRD_IEEE802_TR ?
- htons(ETH_P_TR_802_2) : htons(ETH_P_802_2);
+ return 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)
+static inline u8 llc_ui_addr_null(struct sockaddr_llc *addr)
{
return !memcmp(addr, &llc_ui_addrnull, sizeof(*addr));
}
@@ -89,8 +92,7 @@ static __inline__ u8 llc_ui_addr_null(struct sockaddr_llc *addr)
* 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)
+static inline u8 llc_ui_header_len(struct sock *sk, struct sockaddr_llc *addr)
{
u8 rc = LLC_PDU_LEN_U;
@@ -105,7 +107,6 @@ static __inline__ u8 llc_ui_header_len(struct sock *sk,
* 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.
@@ -116,7 +117,7 @@ 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 (unlikely(llc_data_accept_state(llc->state) ||
+ if (unlikely(llc_data_accept_state(llc->state) ||
llc->remote_busy_flag ||
llc->p_flag)) {
long timeout = sock_sndtimeo(sk, noblock);
@@ -130,36 +131,44 @@ static int llc_ui_send_data(struct sock* sk, struct sk_buff *skb, int noblock)
static void llc_ui_sk_init(struct socket *sock, struct sock *sk)
{
+ sock_graft(sk, sock);
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",
+ .name = "LLC",
.owner = THIS_MODULE,
.obj_size = sizeof(struct llc_sock),
+ .slab_flags = SLAB_DESTROY_BY_RCU,
};
/**
* llc_ui_create - alloc and init a new llc_ui socket
+ * @net: network namespace (must be default network)
* @sock: Socket to initialize and attach allocated sk to.
* @protocol: Unused.
+ * @kern: on behalf of kernel or userspace
*
* 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)
+static int llc_ui_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
{
struct sock *sk;
int rc = -ESOCKTNOSUPPORT;
+ if (!ns_capable(net->user_ns, CAP_NET_RAW))
+ return -EPERM;
+
+ if (!net_eq(net, &init_net))
+ return -EAFNOSUPPORT;
+
if (likely(sock->type == SOCK_DGRAM || sock->type == SOCK_STREAM)) {
rc = -ENOMEM;
- sk = llc_sk_alloc(PF_LLC, GFP_KERNEL, &llc_proto);
+ sk = llc_sk_alloc(net, PF_LLC, GFP_KERNEL, &llc_proto);
if (sk) {
rc = 0;
llc_ui_sk_init(sock, sk);
@@ -184,7 +193,7 @@ static int llc_ui_release(struct socket *sock)
sock_hold(sk);
lock_sock(sk);
llc = llc_sk(sk);
- dprintk("%s: closing local(%02X) remote(%02X)\n", __FUNCTION__,
+ dprintk("%s: closing local(%02X) remote(%02X)\n", __func__,
llc->laddr.lsap, llc->daddr.lsap);
if (!llc_send_disc(sk))
llc_ui_wait_for_disc(sk, sk->sk_rcvtimeo);
@@ -249,7 +258,14 @@ static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr)
if (!sock_flag(sk, SOCK_ZAPPED))
goto out;
rc = -ENODEV;
- llc->dev = dev_getfirstbyhwtype(addr->sllc_arphrd);
+ if (sk->sk_bound_dev_if) {
+ llc->dev = dev_get_by_index(&init_net, sk->sk_bound_dev_if);
+ if (llc->dev && addr->sllc_arphrd != llc->dev->type) {
+ dev_put(llc->dev);
+ llc->dev = NULL;
+ }
+ } else
+ llc->dev = dev_getfirstbyhwtype(&init_net, addr->sllc_arphrd);
if (!llc->dev)
goto out;
rc = -EUSERS;
@@ -292,16 +308,35 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
struct llc_sap *sap;
int rc = -EINVAL;
- dprintk("%s: binding %02X\n", __FUNCTION__, addr->sllc_sap);
+ dprintk("%s: binding %02X\n", __func__, addr->sllc_sap);
if (unlikely(!sock_flag(sk, SOCK_ZAPPED) || addrlen != sizeof(*addr)))
goto out;
rc = -EAFNOSUPPORT;
if (unlikely(addr->sllc_family != AF_LLC))
goto out;
rc = -ENODEV;
- rtnl_lock();
- llc->dev = dev_getbyhwaddr(addr->sllc_arphrd, addr->sllc_mac);
- rtnl_unlock();
+ rcu_read_lock();
+ if (sk->sk_bound_dev_if) {
+ llc->dev = dev_get_by_index_rcu(&init_net, sk->sk_bound_dev_if);
+ if (llc->dev) {
+ if (!addr->sllc_arphrd)
+ addr->sllc_arphrd = llc->dev->type;
+ if (is_zero_ether_addr(addr->sllc_mac))
+ memcpy(addr->sllc_mac, llc->dev->dev_addr,
+ IFHWADDRLEN);
+ if (addr->sllc_arphrd != llc->dev->type ||
+ !ether_addr_equal(addr->sllc_mac,
+ llc->dev->dev_addr)) {
+ rc = -EINVAL;
+ llc->dev = NULL;
+ }
+ }
+ } else
+ llc->dev = dev_getbyhwaddr_rcu(&init_net, addr->sllc_arphrd,
+ addr->sllc_mac);
+ if (llc->dev)
+ dev_hold(llc->dev);
+ rcu_read_unlock();
if (!llc->dev)
goto out;
if (!addr->sllc_sap) {
@@ -316,7 +351,6 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
rc = -EBUSY; /* some other network layer is using the sap */
if (!sap)
goto out;
- llc_sap_hold(sap);
} else {
struct llc_addr laddr, daddr;
struct sock *ask;
@@ -324,7 +358,7 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
memset(&laddr, 0, sizeof(laddr));
memset(&daddr, 0, sizeof(daddr));
/*
- * FIXME: check if the the address is multicast,
+ * FIXME: check if the address is multicast,
* only SOCK_DGRAM can do this.
*/
memcpy(laddr.mac, addr->sllc_mac, IFHWADDRLEN);
@@ -429,7 +463,7 @@ static int llc_ui_connect(struct socket *sock, struct sockaddr *uaddr,
rc = llc_establish_connection(sk, llc->dev->dev_addr,
addr->sllc_mac, addr->sllc_sap);
if (rc) {
- dprintk("%s: llc_ui_send_conn failed :-(\n", __FUNCTION__);
+ dprintk("%s: llc_ui_send_conn failed :-(\n", __func__);
sock->state = SS_UNCONNECTED;
sk->sk_state = TCP_CLOSE;
goto out;
@@ -483,7 +517,7 @@ static int llc_ui_listen(struct socket *sock, int backlog)
if (sock_flag(sk, SOCK_ZAPPED))
goto out;
rc = 0;
- if (!(unsigned)backlog) /* BSDism */
+ if (!(unsigned int)backlog) /* BSDism */
backlog = 1;
sk->sk_max_ack_backlog = backlog;
if (sk->sk_state != TCP_LISTEN) {
@@ -502,7 +536,7 @@ static int llc_ui_wait_for_disc(struct sock *sk, long timeout)
int rc = 0;
while (1) {
- prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
if (sk_wait_event(sk, &timeout, sk->sk_state == TCP_CLOSE))
break;
rc = -ERESTARTSYS;
@@ -513,7 +547,7 @@ static int llc_ui_wait_for_disc(struct sock *sk, long timeout)
break;
rc = 0;
}
- finish_wait(sk->sk_sleep, &wait);
+ finish_wait(sk_sleep(sk), &wait);
return rc;
}
@@ -522,13 +556,13 @@ static int llc_ui_wait_for_conn(struct sock *sk, long timeout)
DEFINE_WAIT(wait);
while (1) {
- prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
if (sk_wait_event(sk, &timeout, sk->sk_state != TCP_SYN_SENT))
break;
if (signal_pending(current) || !timeout)
break;
}
- finish_wait(sk->sk_sleep, &wait);
+ finish_wait(sk_sleep(sk), &wait);
return timeout;
}
@@ -539,7 +573,7 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout)
int rc;
while (1) {
- prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
rc = 0;
if (sk_wait_event(sk, &timeout,
(sk->sk_shutdown & RCV_SHUTDOWN) ||
@@ -554,7 +588,7 @@ static int llc_ui_wait_for_busy_core(struct sock *sk, long timeout)
if (!timeout)
break;
}
- finish_wait(sk->sk_sleep, &wait);
+ finish_wait(sk_sleep(sk), &wait);
return rc;
}
@@ -585,6 +619,20 @@ static int llc_wait_data(struct sock *sk, long timeo)
return rc;
}
+static void llc_cmsg_rcv(struct msghdr *msg, struct sk_buff *skb)
+{
+ struct llc_sock *llc = llc_sk(skb->sk);
+
+ if (llc->cmsg_flags & LLC_CMSG_PKTINFO) {
+ struct llc_pktinfo info;
+
+ info.lpi_ifindex = llc_sk(skb->sk)->dev->ifindex;
+ llc_pdu_decode_dsap(skb, &info.lpi_sap);
+ llc_pdu_decode_da(skb, info.lpi_mac);
+ put_cmsg(msg, SOL_LLC, LLC_OPT_PKTINFO, sizeof(info), &info);
+ }
+}
+
/**
* llc_ui_accept - accept a new incoming connection.
* @sock: Socket which connections arrive on.
@@ -601,8 +649,8 @@ static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags)
struct sk_buff *skb;
int rc = -EOPNOTSUPP;
- dprintk("%s: accepting on %02X\n", __FUNCTION__,
- llc_sk(sk)->laddr.lsap);
+ dprintk("%s: accepting on %02X\n", __func__,
+ llc_sk(sk)->laddr.lsap);
lock_sock(sk);
if (unlikely(sk->sk_type != SOCK_STREAM))
goto out;
@@ -616,8 +664,8 @@ static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags)
if (rc)
goto out;
}
- dprintk("%s: got a new connection on %02X\n", __FUNCTION__,
- llc_sk(sk)->laddr.lsap);
+ dprintk("%s: got a new connection on %02X\n", __func__,
+ llc_sk(sk)->laddr.lsap);
skb = skb_dequeue(&sk->sk_receive_queue);
rc = -EINVAL;
if (!skb->sk)
@@ -637,7 +685,7 @@ static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags)
/* put original socket back into a clean listen state. */
sk->sk_state = TCP_LISTEN;
sk->sk_ack_backlog--;
- dprintk("%s: ok success on %02X, client on %02X\n", __FUNCTION__,
+ dprintk("%s: ok success on %02X, client on %02X\n", __func__,
llc_sk(sk)->addr.sllc_sap, newllc->daddr.lsap);
frees:
kfree_skb(skb);
@@ -659,21 +707,22 @@ out:
static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len, int flags)
{
- struct sockaddr_llc *uaddr = (struct sockaddr_llc *)msg->msg_name;
+ DECLARE_SOCKADDR(struct sockaddr_llc *, uaddr, msg->msg_name);
const int nonblock = flags & MSG_DONTWAIT;
struct sk_buff *skb = NULL;
struct sock *sk = sock->sk;
struct llc_sock *llc = llc_sk(sk);
+ unsigned long cpu_flags;
size_t copied = 0;
u32 peek_seq = 0;
- u32 *seq;
+ u32 *seq, skb_len;
unsigned long used;
int target; /* Read at least this many bytes */
long timeo;
lock_sock(sk);
copied = -ENOTCONN;
- if (sk->sk_state == TCP_LISTEN)
+ if (unlikely(sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN))
goto out;
timeo = sock_rcvtimeo(sk, nonblock);
@@ -682,7 +731,7 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
if (flags & MSG_PEEK) {
peek_seq = llc->copied_seq;
seq = &peek_seq;
- }
+ }
target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
copied = 0;
@@ -732,7 +781,7 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
if (sk->sk_shutdown & RCV_SHUTDOWN)
break;
- if (sk->sk_state == TCP_CLOSE) {
+ if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSE) {
if (!sock_flag(sk, SOCK_DONE)) {
/*
* This occurs when user tries to read
@@ -756,14 +805,14 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
sk_wait_data(sk, &timeo);
if ((flags & MSG_PEEK) && peek_seq != llc->copied_seq) {
- if (net_ratelimit())
- printk(KERN_DEBUG "LLC(%s:%d): Application "
- "bug, race in MSG_PEEK.\n",
- current->comm, current->pid);
+ net_dbg_ratelimited("LLC(%s:%d): Application bug, race in MSG_PEEK\n",
+ current->comm,
+ task_pid_nr(current));
peek_seq = llc->copied_seq;
}
continue;
found_ok_skb:
+ skb_len = skb->len;
/* Ok so how much can we use? */
used = skb->len - offset;
if (len < used)
@@ -784,24 +833,22 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
copied += used;
len -= used;
- if (used + offset < skb->len)
- continue;
+ /* For non stream protcols we get one packet per recvmsg call */
+ if (sk->sk_type != SOCK_STREAM)
+ goto copy_uaddr;
if (!(flags & MSG_PEEK)) {
- sk_eat_skb(sk, skb);
+ spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
+ sk_eat_skb(sk, skb, false);
+ spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
*seq = 0;
}
+
+ /* Partial read */
+ if (used + offset < skb_len)
+ continue;
} while (len > 0);
- /*
- * According to UNIX98, msg_name/msg_namelen are ignored
- * on connected socket. -ANK
- * But... af_llc still doesn't have separate sets of methods for
- * SOCK_DGRAM and SOCK_STREAM :-( So we have to do this test, will
- * eventually fix this tho :-) -acme
- */
- if (sk->sk_type == SOCK_DGRAM)
- goto copy_uaddr;
out:
release_sock(sk);
return copied;
@@ -810,6 +857,16 @@ copy_uaddr:
memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));
msg->msg_namelen = sizeof(*uaddr);
}
+ if (llc_sk(sk)->cmsg_flags)
+ llc_cmsg_rcv(msg, skb);
+
+ if (!(flags & MSG_PEEK)) {
+ spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags);
+ sk_eat_skb(sk, skb, false);
+ spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags);
+ *seq = 0;
+ }
+
goto out;
}
@@ -827,14 +884,14 @@ static int llc_ui_sendmsg(struct kiocb *iocb, struct socket *sock,
{
struct sock *sk = sock->sk;
struct llc_sock *llc = llc_sk(sk);
- struct sockaddr_llc *addr = (struct sockaddr_llc *)msg->msg_name;
+ DECLARE_SOCKADDR(struct sockaddr_llc *, addr, msg->msg_name);
int flags = msg->msg_flags;
int noblock = flags & MSG_DONTWAIT;
struct sk_buff *skb;
size_t size = 0;
int rc = -EINVAL, copied = 0, hdrlen;
- dprintk("%s: sending from %02X to %02X\n", __FUNCTION__,
+ dprintk("%s: sending from %02X to %02X\n", __func__,
llc->laddr.lsap, llc->daddr.lsap);
lock_sock(sk);
if (addr) {
@@ -864,7 +921,7 @@ static int llc_ui_sendmsg(struct kiocb *iocb, struct socket *sock,
goto release;
skb->dev = llc->dev;
skb->protocol = llc_proto_type(addr->sllc_arphrd);
- skb_reserve(skb, hdrlen);
+ skb_reserve(skb, hdrlen);
rc = memcpy_fromiovec(skb_put(skb, copied), msg->msg_iov, copied);
if (rc)
goto out;
@@ -892,7 +949,7 @@ out:
kfree_skb(skb);
release:
dprintk("%s: failed sending from %02X to %02X: %d\n",
- __FUNCTION__, llc->laddr.lsap, llc->daddr.lsap, rc);
+ __func__, llc->laddr.lsap, llc->daddr.lsap, rc);
}
release_sock(sk);
return rc ? : copied;
@@ -913,13 +970,13 @@ static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr,
struct sockaddr_llc sllc;
struct sock *sk = sock->sk;
struct llc_sock *llc = llc_sk(sk);
- int rc = 0;
+ int rc = -EBADF;
+ memset(&sllc, 0, sizeof(sllc));
lock_sock(sk);
if (sock_flag(sk, SOCK_ZAPPED))
goto out;
*uaddrlen = sizeof(sllc);
- memset(uaddr, 0, *uaddrlen);
if (peer) {
rc = -ENOTCONN;
if (sk->sk_state != TCP_ESTABLISHED)
@@ -936,7 +993,7 @@ static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr,
if (llc->dev) {
sllc.sllc_arphrd = llc->dev->type;
- memcpy(&sllc.sllc_mac, &llc->dev->dev_addr,
+ memcpy(&sllc.sllc_mac, llc->dev->dev_addr,
IFHWADDRLEN);
}
}
@@ -967,17 +1024,18 @@ static int llc_ui_ioctl(struct socket *sock, unsigned int cmd,
* @sock: Socket to set options on.
* @level: Socket level user is requesting operations on.
* @optname: Operation name.
- * @optval User provided operation data.
+ * @optval: User provided operation data.
* @optlen: Length of optval.
*
* Set various connection specific parameters.
*/
static int llc_ui_setsockopt(struct socket *sock, int level, int optname,
- char __user *optval, int optlen)
+ char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
struct llc_sock *llc = llc_sk(sk);
- int rc = -EINVAL, opt;
+ unsigned int opt;
+ int rc = -EINVAL;
lock_sock(sk);
if (unlikely(level != SOL_LLC || optlen != sizeof(int)))
@@ -1027,6 +1085,12 @@ static int llc_ui_setsockopt(struct socket *sock, int level, int optname,
goto out;
llc->rw = opt;
break;
+ case LLC_OPT_PKTINFO:
+ if (opt)
+ llc->cmsg_flags |= LLC_CMSG_PKTINFO;
+ else
+ llc->cmsg_flags &= ~LLC_CMSG_PKTINFO;
+ break;
default:
rc = -ENOPROTOOPT;
goto out;
@@ -1080,6 +1144,9 @@ static int llc_ui_getsockopt(struct socket *sock, int level, int optname,
val = llc->k; break;
case LLC_OPT_RX_WIN:
val = llc->rw; break;
+ case LLC_OPT_PKTINFO:
+ val = (llc->cmsg_flags & LLC_CMSG_PKTINFO) != 0;
+ break;
default:
rc = -ENOPROTOOPT;
goto out;
@@ -1092,7 +1159,7 @@ out:
return rc;
}
-static struct net_proto_family llc_ui_family_ops = {
+static const struct net_proto_family llc_ui_family_ops = {
.family = PF_LLC,
.create = llc_ui_create,
.owner = THIS_MODULE,
@@ -1119,12 +1186,12 @@ static const struct proto_ops llc_ui_ops = {
.sendpage = sock_no_sendpage,
};
-static char llc_proc_err_msg[] __initdata =
- KERN_CRIT "LLC: Unable to register the proc_fs entries\n";
-static char llc_sysctl_err_msg[] __initdata =
- KERN_CRIT "LLC: Unable to register the sysctl entries\n";
-static char llc_sock_err_msg[] __initdata =
- KERN_CRIT "LLC: Unable to register the network family\n";
+static const char llc_proc_err_msg[] __initconst =
+ KERN_CRIT "LLC: Unable to register the proc_fs entries\n";
+static const char llc_sysctl_err_msg[] __initconst =
+ KERN_CRIT "LLC: Unable to register the sysctl entries\n";
+static const char llc_sock_err_msg[] __initconst =
+ KERN_CRIT "LLC: Unable to register the network family\n";
static int __init llc2_init(void)
{
@@ -1139,7 +1206,7 @@ static int __init llc2_init(void)
rc = llc_proc_init();
if (rc != 0) {
printk(llc_proc_err_msg);
- goto out_unregister_llc_proto;
+ goto out_station;
}
rc = llc_sysctl_init();
if (rc) {
@@ -1159,7 +1226,8 @@ out_sysctl:
llc_sysctl_exit();
out_proc:
llc_proc_exit();
-out_unregister_llc_proto:
+out_station:
+ llc_station_exit();
proto_unregister(&llc_proto);
goto out;
}