174 lines
4.3 KiB
C
174 lines
4.3 KiB
C
#include "capwap.h"
|
|
#include "network.h"
|
|
#include <linux/socket.h>
|
|
#include "wifi_drivers.h"
|
|
#include "netlink_link.h"
|
|
|
|
static void netlink_event_receive_cb(EV_P_ ev_io *w, int revents);
|
|
|
|
/* */
|
|
struct netlink_request {
|
|
struct nlmsghdr hdr;
|
|
struct ifinfomsg ifinfo;
|
|
char opts[16];
|
|
};
|
|
|
|
/* */
|
|
struct netlink *netlink_init(wifi_global_handle handle)
|
|
{
|
|
int sock;
|
|
struct sockaddr_nl local;
|
|
struct netlink* netlinkhandle;
|
|
|
|
/* Create netlink socket */
|
|
sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
|
if (sock < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Bind to kernel */
|
|
memset(&local, 0, sizeof(struct sockaddr_nl));
|
|
local.nl_family = AF_NETLINK;
|
|
local.nl_groups = RTMGRP_LINK;
|
|
if (bind(sock, (struct sockaddr*)&local, sizeof(struct sockaddr_nl)) < 0) {
|
|
close(sock);
|
|
return NULL;
|
|
}
|
|
|
|
/* Netlink reference */
|
|
netlinkhandle = (struct netlink*)capwap_alloc(sizeof(struct netlink));
|
|
netlinkhandle->handle = handle;
|
|
netlinkhandle->sock = sock;
|
|
netlinkhandle->nl_sequence = 1;
|
|
|
|
ev_io_init(&netlinkhandle->io_ev, netlink_event_receive_cb, sock, EV_READ);
|
|
ev_io_start(EV_DEFAULT_UC_ &netlinkhandle->io_ev);
|
|
|
|
return netlinkhandle;
|
|
}
|
|
|
|
/* */
|
|
void netlink_free(struct netlink* netlinkhandle)
|
|
{
|
|
ASSERT(netlinkhandle != NULL);
|
|
ASSERT(netlinkhandle->sock >= 0);
|
|
|
|
if (ev_is_active(&netlinkhandle->io_ev))
|
|
ev_io_stop(EV_DEFAULT_UC_ &netlinkhandle->io_ev);
|
|
|
|
/* */
|
|
close(netlinkhandle->sock);
|
|
capwap_free(netlinkhandle);
|
|
}
|
|
|
|
static void invoke_event_fn(netlink_event_fn event_fn, struct netlink *netlinkhandle, struct nlmsghdr* message)
|
|
{
|
|
if (!event_fn)
|
|
return;
|
|
|
|
if (NLMSG_PAYLOAD(message, 0) < sizeof(struct ifinfomsg))
|
|
return;
|
|
|
|
event_fn(netlinkhandle->handle,
|
|
NLMSG_DATA(message),
|
|
(uint8_t*)(NLMSG_DATA(message) + NLMSG_ALIGN(sizeof(struct ifinfomsg))),
|
|
NLMSG_PAYLOAD(message, sizeof(struct ifinfomsg)));
|
|
}
|
|
|
|
/* */
|
|
static void netlink_event_receive_cb(EV_P_ ev_io *w, int revents)
|
|
{
|
|
struct netlink *netlinkhandle = (struct netlink *)
|
|
(((char *)w) - offsetof(struct netlink, io_ev));
|
|
int result;
|
|
struct sockaddr_nl from;
|
|
socklen_t fromlen;
|
|
char buffer[8192];
|
|
struct nlmsghdr* message;
|
|
|
|
/* Retrieve all netlink message */
|
|
for (;;) {
|
|
/* Get message */
|
|
fromlen = sizeof(struct sockaddr_nl);
|
|
result = recvfrom(w->fd, buffer, sizeof(buffer), MSG_DONTWAIT,
|
|
(struct sockaddr *)&from, &fromlen);
|
|
if (result <= 0) {
|
|
if (errno == EINTR) {
|
|
continue;
|
|
}
|
|
|
|
/* */
|
|
break;
|
|
}
|
|
|
|
/* Parsing message */
|
|
message = (struct nlmsghdr*)buffer;
|
|
while (NLMSG_OK(message, result)) {
|
|
switch (message->nlmsg_type) {
|
|
case RTM_NEWLINK:
|
|
invoke_event_fn(netlinkhandle->newlink_event,
|
|
netlinkhandle, message);
|
|
break;
|
|
|
|
case RTM_DELLINK:
|
|
invoke_event_fn(netlinkhandle->dellink_event,
|
|
netlinkhandle, message);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* */
|
|
message = NLMSG_NEXT(message, result);
|
|
}
|
|
}
|
|
}
|
|
|
|
int netlink_set_link_status(struct netlink* netlinkhandle, int ifindex, int linkmode, int operstate) {
|
|
char* data;
|
|
struct rtattr* rta;
|
|
struct netlink_request request;
|
|
|
|
ASSERT(netlinkhandle != NULL);
|
|
ASSERT(ifindex >= 0);
|
|
|
|
/* */
|
|
memset(&request, 0, sizeof(struct netlink_request));
|
|
request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
|
request.hdr.nlmsg_type = RTM_SETLINK;
|
|
request.hdr.nlmsg_flags = NLM_F_REQUEST;
|
|
request.hdr.nlmsg_seq = netlinkhandle->nl_sequence++;
|
|
request.hdr.nlmsg_pid = 0;
|
|
request.ifinfo.ifi_family = AF_UNSPEC;
|
|
request.ifinfo.ifi_type = 0;
|
|
request.ifinfo.ifi_index = ifindex;
|
|
request.ifinfo.ifi_flags = 0;
|
|
request.ifinfo.ifi_change = 0;
|
|
|
|
if (linkmode != -1) {
|
|
rta = (struct rtattr*)((char*)&request + NLMSG_ALIGN(request.hdr.nlmsg_len));
|
|
rta->rta_type = IFLA_LINKMODE;
|
|
rta->rta_len = RTA_LENGTH(sizeof(char));
|
|
data = (char*)RTA_DATA(rta);
|
|
*data = (char)linkmode;
|
|
request.hdr.nlmsg_len = NLMSG_ALIGN(request.hdr.nlmsg_len) + RTA_LENGTH(sizeof(char));
|
|
}
|
|
|
|
if (operstate != -1) {
|
|
rta = (struct rtattr*)((char*)&request + NLMSG_ALIGN(request.hdr.nlmsg_len));
|
|
rta->rta_type = IFLA_OPERSTATE;
|
|
rta->rta_len = RTA_LENGTH(sizeof(char));
|
|
data = (char*)RTA_DATA(rta);
|
|
*data = (char)operstate;
|
|
request.hdr.nlmsg_len = NLMSG_ALIGN(request.hdr.nlmsg_len) + RTA_LENGTH(sizeof(char));
|
|
}
|
|
|
|
/* Send new interface operation state */
|
|
if (send(netlinkhandle->sock, &request, request.hdr.nlmsg_len, 0) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|