161e12104SWon Kang /*
261e12104SWon Kang  * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
361e12104SWon Kang  *
461e12104SWon Kang  * This software is licensed under the terms of the GNU General Public
561e12104SWon Kang  * License version 2, as published by the Free Software Foundation, and
661e12104SWon Kang  * may be copied, distributed, and modified under those terms.
761e12104SWon Kang  *
861e12104SWon Kang  * This program is distributed in the hope that it will be useful,
961e12104SWon Kang  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1061e12104SWon Kang  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1161e12104SWon Kang  * GNU General Public License for more details.
1261e12104SWon Kang  */
1361e12104SWon Kang 
1461e12104SWon Kang #include <linux/version.h>
1561e12104SWon Kang #include <linux/export.h>
1661e12104SWon Kang #include <linux/etherdevice.h>
1761e12104SWon Kang #include <linux/netlink.h>
1861e12104SWon Kang #include <asm/byteorder.h>
1961e12104SWon Kang #include <net/sock.h>
2061e12104SWon Kang 
2161e12104SWon Kang #include "netlink_k.h"
2261e12104SWon Kang 
2361e12104SWon Kang #if defined(DEFINE_MUTEX)
2461e12104SWon Kang static DEFINE_MUTEX(netlink_mutex);
2561e12104SWon Kang #else
2661e12104SWon Kang static struct semaphore netlink_mutex;
2761e12104SWon Kang #define mutex_lock(x)		down(x)
2861e12104SWon Kang #define mutex_unlock(x)		up(x)
2961e12104SWon Kang #endif
3061e12104SWon Kang 
3161e12104SWon Kang #define ND_MAX_GROUP		30
3261e12104SWon Kang #define ND_IFINDEX_LEN		sizeof(int)
3361e12104SWon Kang #define ND_NLMSG_SPACE(len)	(NLMSG_SPACE(len) + ND_IFINDEX_LEN)
3461e12104SWon Kang #define ND_NLMSG_DATA(nlh)	((void *)((char *)NLMSG_DATA(nlh) + ND_IFINDEX_LEN))
3561e12104SWon Kang #define ND_NLMSG_S_LEN(len)	(len+ND_IFINDEX_LEN)
3661e12104SWon Kang #define ND_NLMSG_R_LEN(nlh)	(nlh->nlmsg_len-ND_IFINDEX_LEN)
3761e12104SWon Kang #define ND_NLMSG_IFIDX(nlh)	NLMSG_DATA(nlh)
3861e12104SWon Kang #define ND_MAX_MSG_LEN		(1024 * 32)
3961e12104SWon Kang 
4061e12104SWon Kang static void (*rcv_cb)(struct net_device *dev, u16 type, void *msg, int len);
4161e12104SWon Kang 
4261e12104SWon Kang static void netlink_rcv_cb(struct sk_buff *skb)
4361e12104SWon Kang {
4461e12104SWon Kang 	struct nlmsghdr	*nlh;
4561e12104SWon Kang 	struct net_device *dev;
4661e12104SWon Kang 	u32 mlen;
4761e12104SWon Kang 	void *msg;
4861e12104SWon Kang 	int ifindex;
4961e12104SWon Kang 
5061e12104SWon Kang 	if (!rcv_cb) {
5161e12104SWon Kang 		printk(KERN_ERR "glte: nl cb - unregistered\n");
5261e12104SWon Kang 		return;
5361e12104SWon Kang 	}
5461e12104SWon Kang 
5561e12104SWon Kang 	if (skb->len < NLMSG_SPACE(0)) {
5661e12104SWon Kang 		printk(KERN_ERR "glte: nl cb - invalid skb length\n");
5761e12104SWon Kang 		return;
5861e12104SWon Kang 	}
5961e12104SWon Kang 
6061e12104SWon Kang 	nlh = (struct nlmsghdr *)skb->data;
6161e12104SWon Kang 
6261e12104SWon Kang 	if (skb->len < nlh->nlmsg_len || nlh->nlmsg_len > ND_MAX_MSG_LEN) {
6361e12104SWon Kang 		printk(KERN_ERR "glte: nl cb - invalid length (%d,%d)\n",
6461e12104SWon Kang 		       skb->len, nlh->nlmsg_len);
6561e12104SWon Kang 		return;
6661e12104SWon Kang 	}
6761e12104SWon Kang 
6861e12104SWon Kang 	memcpy(&ifindex, ND_NLMSG_IFIDX(nlh), ND_IFINDEX_LEN);
6961e12104SWon Kang 	msg = ND_NLMSG_DATA(nlh);
7061e12104SWon Kang 	mlen = ND_NLMSG_R_LEN(nlh);
7161e12104SWon Kang 
7261e12104SWon Kang 	dev = dev_get_by_index(&init_net, ifindex);
7361e12104SWon Kang 	if (dev) {
7461e12104SWon Kang 		rcv_cb(dev, nlh->nlmsg_type, msg, mlen);
7561e12104SWon Kang 		dev_put(dev);
7661e12104SWon Kang 	} else {
7761e12104SWon Kang 		printk(KERN_ERR "glte: nl cb - dev (%d) not found\n", ifindex);
7861e12104SWon Kang 	}
7961e12104SWon Kang }
8061e12104SWon Kang 
8161e12104SWon Kang static void netlink_rcv(struct sk_buff *skb)
8261e12104SWon Kang {
8361e12104SWon Kang 	mutex_lock(&netlink_mutex);
8461e12104SWon Kang 	netlink_rcv_cb(skb);
8561e12104SWon Kang 	mutex_unlock(&netlink_mutex);
8661e12104SWon Kang }
8761e12104SWon Kang 
8861e12104SWon Kang struct sock *netlink_init(int unit,
8961e12104SWon Kang 	void (*cb)(struct net_device *dev, u16 type, void *msg, int len))
9061e12104SWon Kang {
9161e12104SWon Kang 	struct sock *sock;
9261e12104SWon Kang #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
9361e12104SWon Kang 	struct netlink_kernel_cfg cfg = {
9461e12104SWon Kang 		.input  = netlink_rcv,
9561e12104SWon Kang 	};
9661e12104SWon Kang #endif
9761e12104SWon Kang 
9861e12104SWon Kang #if !defined(DEFINE_MUTEX)
9961e12104SWon Kang 	init_MUTEX(&netlink_mutex);
10061e12104SWon Kang #endif
10161e12104SWon Kang 
10261e12104SWon Kang #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
10361e12104SWon Kang 	sock = netlink_kernel_create(&init_net, unit, 0, netlink_rcv, NULL,
10461e12104SWon Kang 		THIS_MODULE);
10561e12104SWon Kang #elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
10661e12104SWon Kang 	sock = netlink_kernel_create(&init_net, unit, THIS_MODULE, &cfg);
10761e12104SWon Kang #else
10861e12104SWon Kang 	sock = netlink_kernel_create(&init_net, unit, &cfg);
10961e12104SWon Kang #endif
11061e12104SWon Kang 
11161e12104SWon Kang 	if (sock)
11261e12104SWon Kang 		rcv_cb = cb;
11361e12104SWon Kang 
11461e12104SWon Kang 	return sock;
11561e12104SWon Kang }
11661e12104SWon Kang 
11761e12104SWon Kang void netlink_exit(struct sock *sock)
11861e12104SWon Kang {
11961e12104SWon Kang 	sock_release(sock->sk_socket);
12061e12104SWon Kang }
12161e12104SWon Kang 
12261e12104SWon Kang int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len)
12361e12104SWon Kang {
12461e12104SWon Kang 	static u32 seq;
12561e12104SWon Kang 	struct sk_buff *skb = NULL;
12661e12104SWon Kang 	struct nlmsghdr *nlh;
12761e12104SWon Kang 	int ret = 0;
12861e12104SWon Kang 
12961e12104SWon Kang 	if (group > ND_MAX_GROUP)
13061e12104SWon Kang 		return -EINVAL;
13161e12104SWon Kang 
13261e12104SWon Kang 	if (!netlink_has_listeners(sock, group+1))
13361e12104SWon Kang 		return -ESRCH;
13461e12104SWon Kang 
13561e12104SWon Kang 	skb = alloc_skb(NLMSG_SPACE(len), GFP_ATOMIC);
13661e12104SWon Kang 	if (!skb)
13761e12104SWon Kang 		return -ENOMEM;
13861e12104SWon Kang 
13961e12104SWon Kang 	seq++;
14061e12104SWon Kang 
14161e12104SWon Kang 	nlh = nlmsg_put(skb, 0, seq, type, len, 0);
14261e12104SWon Kang 	memcpy(NLMSG_DATA(nlh), msg, len);
14361e12104SWon Kang #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
14461e12104SWon Kang 	NETLINK_CB(skb).pid = 0;
14561e12104SWon Kang #else
14661e12104SWon Kang 	NETLINK_CB(skb).portid = 0;
14761e12104SWon Kang #endif
14861e12104SWon Kang 	NETLINK_CB(skb).dst_group = 0;
14961e12104SWon Kang 
15061e12104SWon Kang 	ret = netlink_broadcast(sock, skb, 0, group+1, GFP_ATOMIC);
15161e12104SWon Kang 	if (!ret)
15261e12104SWon Kang 		return len;
15361e12104SWon Kang 
15461e12104SWon Kang 	if (ret != -ESRCH)
15561e12104SWon Kang 		printk(KERN_ERR "glte: nl broadcast g=%d, t=%d, l=%d, r=%d\n",
15661e12104SWon Kang 		       group, type, len, ret);
15761e12104SWon Kang 	else if (netlink_has_listeners(sock, group+1))
15861e12104SWon Kang 		return -EAGAIN;
15961e12104SWon Kang 
16061e12104SWon Kang 	return ret;
16161e12104SWon Kang }
162