123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- /*
- * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/export.h>
- #include <linux/etherdevice.h>
- #include <linux/netlink.h>
- #include <asm/byteorder.h>
- #include <net/sock.h>
- #include "netlink_k.h"
- #if defined(DEFINE_MUTEX)
- static DEFINE_MUTEX(netlink_mutex);
- #else
- static struct semaphore netlink_mutex;
- #define mutex_lock(x) down(x)
- #define mutex_unlock(x) up(x)
- #endif
- #define ND_MAX_GROUP 30
- #define ND_IFINDEX_LEN sizeof(int)
- #define ND_NLMSG_SPACE(len) (NLMSG_SPACE(len) + ND_IFINDEX_LEN)
- #define ND_NLMSG_DATA(nlh) ((void *)((char *)NLMSG_DATA(nlh) + \
- ND_IFINDEX_LEN))
- #define ND_NLMSG_S_LEN(len) (len + ND_IFINDEX_LEN)
- #define ND_NLMSG_R_LEN(nlh) (nlh->nlmsg_len - ND_IFINDEX_LEN)
- #define ND_NLMSG_IFIDX(nlh) NLMSG_DATA(nlh)
- #define ND_MAX_MSG_LEN (1024 * 32)
- static void (*rcv_cb)(struct net_device *dev, u16 type, void *msg, int len);
- static void netlink_rcv_cb(struct sk_buff *skb)
- {
- struct nlmsghdr *nlh;
- struct net_device *dev;
- u32 mlen;
- void *msg;
- int ifindex;
- if (!rcv_cb) {
- pr_err("nl cb - unregistered\n");
- return;
- }
- if (skb->len < NLMSG_HDRLEN) {
- pr_err("nl cb - invalid skb length\n");
- return;
- }
- nlh = (struct nlmsghdr *)skb->data;
- if (skb->len < nlh->nlmsg_len || nlh->nlmsg_len > ND_MAX_MSG_LEN) {
- pr_err("nl cb - invalid length (%d,%d)\n",
- skb->len, nlh->nlmsg_len);
- return;
- }
- memcpy(&ifindex, ND_NLMSG_IFIDX(nlh), ND_IFINDEX_LEN);
- msg = ND_NLMSG_DATA(nlh);
- mlen = ND_NLMSG_R_LEN(nlh);
- dev = dev_get_by_index(&init_net, ifindex);
- if (dev) {
- rcv_cb(dev, nlh->nlmsg_type, msg, mlen);
- dev_put(dev);
- } else {
- pr_err("nl cb - dev (%d) not found\n", ifindex);
- }
- }
- static void netlink_rcv(struct sk_buff *skb)
- {
- mutex_lock(&netlink_mutex);
- netlink_rcv_cb(skb);
- mutex_unlock(&netlink_mutex);
- }
- struct sock *netlink_init(int unit,
- void (*cb)(struct net_device *dev, u16 type, void *msg, int len))
- {
- struct sock *sock;
- struct netlink_kernel_cfg cfg = {
- .input = netlink_rcv,
- };
- #if !defined(DEFINE_MUTEX)
- init_MUTEX(&netlink_mutex);
- #endif
- sock = netlink_kernel_create(&init_net, unit, &cfg);
- if (sock)
- rcv_cb = cb;
- return sock;
- }
- void netlink_exit(struct sock *sock)
- {
- sock_release(sock->sk_socket);
- }
- int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len)
- {
- static u32 seq;
- struct sk_buff *skb = NULL;
- struct nlmsghdr *nlh;
- int ret = 0;
- if (group > ND_MAX_GROUP)
- return -EINVAL;
- if (!netlink_has_listeners(sock, group + 1))
- return -ESRCH;
- skb = alloc_skb(NLMSG_SPACE(len), GFP_ATOMIC);
- if (!skb)
- return -ENOMEM;
- seq++;
- nlh = nlmsg_put(skb, 0, seq, type, len, 0);
- memcpy(NLMSG_DATA(nlh), msg, len);
- NETLINK_CB(skb).portid = 0;
- NETLINK_CB(skb).dst_group = 0;
- ret = netlink_broadcast(sock, skb, 0, group + 1, GFP_ATOMIC);
- if (!ret)
- return len;
- if (ret != -ESRCH)
- pr_err("nl broadcast g=%d, t=%d, l=%d, r=%d\n",
- group, type, len, ret);
- else if (netlink_has_listeners(sock, group + 1))
- return -EAGAIN;
- return ret;
- }
|