blob: 8a922066e15afd55e1e69054d079502e7f0cf517 [file] [log] [blame]
/*
*
* Copyright (C) 2007 Mindspeed Technologies, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "cmm.h"
/*********************************************************************************************/
/*
The following routines were derived from the libnetlink.c file in the iproute2 package.
Copyright notice from that file follows...
*/
/*
* libnetlink.c RTnetlink service routines.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
*/
void cmm_rtnl_close(struct rtnl_handle *rth)
{
close(rth->fd);
}
int cmm_rtnl_fd(struct rtnl_handle *rth)
{
return rth->fd;
}
unsigned int cmm_rtnl_rcvbufsiz(struct rtnl_handle *rth, unsigned int size)
{
socklen_t socklen = sizeof(size);
unsigned int read_size = 0;
if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUFFORCE, &size, socklen) < 0) {
/* if this didn't work, we try at least to get the system
* wide maximum (or whatever the user requested) */
setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, &size, socklen);
}
getsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, &read_size, &socklen);
return read_size;
}
int cmm_rtnl_set_nonblocking_mode(struct rtnl_handle *rth)
{
if (fcntl(rth->fd, F_SETFL, O_NONBLOCK) < 0)
{
cmm_print(DEBUG_ERROR, "%s: fcntl(%d) failed %s\n", __func__, rth->fd, strerror(errno));
goto err0;
}
rth->flags = RTNL_NON_BLOCKING_MODE;
return 0;
err0:
return -1;
}
int cmm_nl_open(struct rtnl_handle *rth, unsigned subscriptions, unsigned int group)
{
struct sockaddr_nl nladdr;
memset(rth, 0, sizeof(*rth));
rth->fd = socket(PF_NETLINK, SOCK_RAW, group);
if (rth->fd < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: socket() %s\n", __func__, __LINE__, strerror(errno));
goto err0;
}
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_groups = subscriptions;
nladdr.nl_pid = 0;
if (bind(rth->fd, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: bind() %s\n", __func__, __LINE__, strerror(errno));
goto err1;
}
return 0;
err1:
close(rth->fd);
err0:
return -1;
}
int inline cmm_rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
{
return cmm_nl_open(rth, subscriptions, NETLINK_ROUTE);
}
int cmm_rtnl_neigh_dump_request(struct rtnl_handle *rth, int family)
{
struct ndmsg ndm = {
.ndm_family = family,
.ndm_ifindex = 0,
.ndm_state = 0,
.ndm_flags = 0,
.ndm_type = 0,
};
return cmm_rtnl_dump_request(rth, RTM_GETNEIGH, &ndm, sizeof(struct ndmsg));
}
void cmm_nlh_init(struct nlmsghdr *nlh, unsigned int len, unsigned short type, unsigned short flags)
{
memset(nlh, 0, sizeof(struct nlmsghdr));
nlh->nlmsg_len = NLMSG_SPACE(len);
nlh->nlmsg_type = type;
nlh->nlmsg_flags = flags;
nlh->nlmsg_pid = 0;
nlh->nlmsg_seq = 0;
}
int cmm_rtnl_send(struct rtnl_handle *rth, struct nlmsghdr *nlh)
{
struct sockaddr_nl nladdr;
struct iovec iov = {
.iov_base = nlh, .iov_len = nlh->nlmsg_len,
};
struct msghdr msg= {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
int rc;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_groups = 0;
nladdr.nl_pid = 0;
if ((rc = sendmsg(rth->fd, &msg, 0)) < 0) {
cmm_print(DEBUG_ERROR, "%s::%d: sendmsg() %s\n", __func__, __LINE__, strerror(errno));
}
return rc;
}
int cmm_rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
{
char buf[NLMSG_SPACE(len)] __attribute__ ((aligned (4)));
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
cmm_nlh_init(nlh, len, type, NLM_F_REQUEST | NLM_F_DUMP);
memcpy(NLMSG_DATA(nlh), req, len);
return cmm_rtnl_send(rth, nlh);
}
int cmm_rtnl_listen(struct rtnl_handle *rth, rtnl_filter_t handler, void *jarg)
{
int len;
struct nlmsghdr *nlh;
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char buf[64 * 1024] __attribute__ ((aligned (4)));
int rc;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = 0;
nladdr.nl_groups = 0;
iov.iov_base = buf;
do {
iov.iov_len = sizeof(buf);
len = recvmsg(rth->fd, &msg, 0);
if (len < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN)
goto err;
cmm_print(DEBUG_ERROR, "%s::%d: recvmsg() %s\n", __func__, __LINE__, strerror(errno));
goto err;
}
if (len == 0) {
cmm_print(DEBUG_ERROR, "%s::%d: EOF on netlink\n", __func__, __LINE__);
goto err;
}
if (msg.msg_namelen != sizeof(nladdr)) {
cmm_print(DEBUG_ERROR, "%s::%d: wrong sender address len(%d)\n", __func__, __LINE__, msg.msg_namelen);
goto err;
}
nlh = (struct nlmsghdr *)buf;
while (NLMSG_OK(nlh, len)) {
if (nlh->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = NLMSG_DATA(nlh);
cmm_print(DEBUG_ERROR, "%s::%d: netlink error %s\n", __func__, __LINE__, strerror(-err->error));
goto err;
}
if (nlh->nlmsg_type == NLMSG_DONE)
goto out;
rc = handler(&nladdr, nlh, jarg);
if (rc <= RTNL_CB_STOP) {
if (rc == RTNL_CB_STOP)
goto out;
goto err;
}
nlh = NLMSG_NEXT(nlh, len);
}
if (msg.msg_flags & MSG_TRUNC) {
cmm_print(DEBUG_ERROR, "%s::%d: truncated message\n", __func__, __LINE__);
goto err;
}
} while (!(rth->flags & RTNL_NON_BLOCKING_MODE));
out:
return 0;
err:
return -1;
}
int cmm_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
while (RTA_OK(rta, len)) {
if (rta->rta_type <= max)
tb[rta->rta_type] = rta;
rta = RTA_NEXT(rta,len);
}
if (len)
cmm_print(DEBUG_ERROR, "%s::%d: payload too long %d %d\n", __func__, __LINE__, len, rta->rta_len);
return 0;
}
struct rtattr *cmm_get_rtattr(struct rtattr *rta, int len, int type)
{
while (RTA_OK(rta, len)) {
if (rta->rta_type == type)
goto found;
rta = RTA_NEXT(rta, len);
}
return NULL;
found:
return rta;
}
void cmm_addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen)
{
int len = RTA_LENGTH(alen);
struct rtattr *rta;
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), data, alen);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
}
int cmm_addattr16(struct nlmsghdr *n, int maxlen, int type, __u16 data)
{
int len = RTA_LENGTH(2);
struct rtattr *rta;
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), &data, 2);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
return 0;
}
int cmm_addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
{
int len = RTA_LENGTH(4);
struct rtattr *rta;
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), &data, 4);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
return 0;
}