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 
140ec473b5SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
150ec473b5SJoe Perches 
1661e12104SWon Kang #include <linux/export.h>
1761e12104SWon Kang #include <linux/etherdevice.h>
1861e12104SWon Kang #include <linux/netlink.h>
1961e12104SWon Kang #include <asm/byteorder.h>
2061e12104SWon Kang #include <net/sock.h>
2161e12104SWon Kang 
2261e12104SWon Kang #include "netlink_k.h"
2361e12104SWon Kang 
2461e12104SWon Kang #if defined(DEFINE_MUTEX)
2561e12104SWon Kang static DEFINE_MUTEX(netlink_mutex);
2661e12104SWon Kang #else
2761e12104SWon Kang static struct semaphore netlink_mutex;
2861e12104SWon Kang #define mutex_lock(x)		down(x)
2961e12104SWon Kang #define mutex_unlock(x)		up(x)
3061e12104SWon Kang #endif
3161e12104SWon Kang 
3261e12104SWon Kang #define ND_MAX_GROUP		30
3361e12104SWon Kang #define ND_IFINDEX_LEN		sizeof(int)
3461e12104SWon Kang #define ND_NLMSG_SPACE(len)	(NLMSG_SPACE(len) + ND_IFINDEX_LEN)
35a6708372SDaniel Ngu #define ND_NLMSG_DATA(nlh)	((void *)((char *)NLMSG_DATA(nlh) + \
36a6708372SDaniel Ngu 						  ND_IFINDEX_LEN))
3761e12104SWon Kang #define ND_NLMSG_S_LEN(len)	(len+ND_IFINDEX_LEN)
3861e12104SWon Kang #define ND_NLMSG_R_LEN(nlh)	(nlh->nlmsg_len-ND_IFINDEX_LEN)
3961e12104SWon Kang #define ND_NLMSG_IFIDX(nlh)	NLMSG_DATA(nlh)
4061e12104SWon Kang #define ND_MAX_MSG_LEN		(1024 * 32)
4161e12104SWon Kang 
4261e12104SWon Kang static void (*rcv_cb)(struct net_device *dev, u16 type, void *msg, int len);
4361e12104SWon Kang 
4461e12104SWon Kang static void netlink_rcv_cb(struct sk_buff *skb)
4561e12104SWon Kang {
4661e12104SWon Kang 	struct nlmsghdr	*nlh;
4761e12104SWon Kang 	struct net_device *dev;
4861e12104SWon Kang 	u32 mlen;
4961e12104SWon Kang 	void *msg;
5061e12104SWon Kang 	int ifindex;
5161e12104SWon Kang 
5261e12104SWon Kang 	if (!rcv_cb) {
530ec473b5SJoe Perches 		pr_err("nl cb - unregistered\n");
5461e12104SWon Kang 		return;
5561e12104SWon Kang 	}
5661e12104SWon Kang 
5758419939SHimangi Saraogi 	if (skb->len < NLMSG_HDRLEN) {
580ec473b5SJoe Perches 		pr_err("nl cb - invalid skb length\n");
5961e12104SWon Kang 		return;
6061e12104SWon Kang 	}
6161e12104SWon Kang 
6261e12104SWon Kang 	nlh = (struct nlmsghdr *)skb->data;
6361e12104SWon Kang 
6461e12104SWon Kang 	if (skb->len < nlh->nlmsg_len || nlh->nlmsg_len > ND_MAX_MSG_LEN) {
650ec473b5SJoe Perches 		pr_err("nl cb - invalid length (%d,%d)\n",
6661e12104SWon Kang 		       skb->len, nlh->nlmsg_len);
6761e12104SWon Kang 		return;
6861e12104SWon Kang 	}
6961e12104SWon Kang 
7061e12104SWon Kang 	memcpy(&ifindex, ND_NLMSG_IFIDX(nlh), ND_IFINDEX_LEN);
7161e12104SWon Kang 	msg = ND_NLMSG_DATA(nlh);
7261e12104SWon Kang 	mlen = ND_NLMSG_R_LEN(nlh);
7361e12104SWon Kang 
7461e12104SWon Kang 	dev = dev_get_by_index(&init_net, ifindex);
7561e12104SWon Kang 	if (dev) {
7661e12104SWon Kang 		rcv_cb(dev, nlh->nlmsg_type, msg, mlen);
7761e12104SWon Kang 		dev_put(dev);
7861e12104SWon Kang 	} else {
790ec473b5SJoe Perches 		pr_err("nl cb - dev (%d) not found\n", ifindex);
8061e12104SWon Kang 	}
8161e12104SWon Kang }
8261e12104SWon Kang 
8361e12104SWon Kang static void netlink_rcv(struct sk_buff *skb)
8461e12104SWon Kang {
8561e12104SWon Kang 	mutex_lock(&netlink_mutex);
8661e12104SWon Kang 	netlink_rcv_cb(skb);
8761e12104SWon Kang 	mutex_unlock(&netlink_mutex);
8861e12104SWon Kang }
8961e12104SWon Kang 
9061e12104SWon Kang struct sock *netlink_init(int unit,
9161e12104SWon Kang 	void (*cb)(struct net_device *dev, u16 type, void *msg, int len))
9261e12104SWon Kang {
9361e12104SWon Kang 	struct sock *sock;
9461e12104SWon Kang 	struct netlink_kernel_cfg cfg = {
9561e12104SWon Kang 		.input  = netlink_rcv,
9661e12104SWon Kang 	};
9761e12104SWon Kang 
9861e12104SWon Kang #if !defined(DEFINE_MUTEX)
9961e12104SWon Kang 	init_MUTEX(&netlink_mutex);
10061e12104SWon Kang #endif
10161e12104SWon Kang 
10261e12104SWon Kang 	sock = netlink_kernel_create(&init_net, unit, &cfg);
10361e12104SWon Kang 
10461e12104SWon Kang 	if (sock)
10561e12104SWon Kang 		rcv_cb = cb;
10661e12104SWon Kang 
10761e12104SWon Kang 	return sock;
10861e12104SWon Kang }
10961e12104SWon Kang 
11061e12104SWon Kang void netlink_exit(struct sock *sock)
11161e12104SWon Kang {
11261e12104SWon Kang 	sock_release(sock->sk_socket);
11361e12104SWon Kang }
11461e12104SWon Kang 
11561e12104SWon Kang int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len)
11661e12104SWon Kang {
11761e12104SWon Kang 	static u32 seq;
11861e12104SWon Kang 	struct sk_buff *skb = NULL;
11961e12104SWon Kang 	struct nlmsghdr *nlh;
12061e12104SWon Kang 	int ret = 0;
12161e12104SWon Kang 
12261e12104SWon Kang 	if (group > ND_MAX_GROUP)
12361e12104SWon Kang 		return -EINVAL;
12461e12104SWon Kang 
12561e12104SWon Kang 	if (!netlink_has_listeners(sock, group+1))
12661e12104SWon Kang 		return -ESRCH;
12761e12104SWon Kang 
12861e12104SWon Kang 	skb = alloc_skb(NLMSG_SPACE(len), GFP_ATOMIC);
12961e12104SWon Kang 	if (!skb)
13061e12104SWon Kang 		return -ENOMEM;
13161e12104SWon Kang 
13261e12104SWon Kang 	seq++;
13361e12104SWon Kang 
13461e12104SWon Kang 	nlh = nlmsg_put(skb, 0, seq, type, len, 0);
13561e12104SWon Kang 	memcpy(NLMSG_DATA(nlh), msg, len);
13661e12104SWon Kang 	NETLINK_CB(skb).portid = 0;
13761e12104SWon Kang 	NETLINK_CB(skb).dst_group = 0;
13861e12104SWon Kang 
13961e12104SWon Kang 	ret = netlink_broadcast(sock, skb, 0, group+1, GFP_ATOMIC);
14061e12104SWon Kang 	if (!ret)
14161e12104SWon Kang 		return len;
14261e12104SWon Kang 
14361e12104SWon Kang 	if (ret != -ESRCH)
1440ec473b5SJoe Perches 		pr_err("nl broadcast g=%d, t=%d, l=%d, r=%d\n",
14561e12104SWon Kang 		       group, type, len, ret);
14661e12104SWon Kang 	else if (netlink_has_listeners(sock, group+1))
14761e12104SWon Kang 		return -EAGAIN;
14861e12104SWon Kang 
14961e12104SWon Kang 	return ret;
15061e12104SWon Kang }
151