|
|
|
@ -1,9 +1,6 @@
|
|
|
|
|
/* netlink.c - netlink physical link notification handling and info retrieval
|
|
|
|
|
*
|
|
|
|
|
* (c) 2011 Nicholas J. Kain <njkain at gmail dot com>
|
|
|
|
|
* (c) 2006-2007 Stefan Rompf <sux@loplof.de>
|
|
|
|
|
*
|
|
|
|
|
* This code was largely taken from Stefan Rompf's dhcpclient.
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
@ -34,14 +31,15 @@
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/select.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
#include <libmnl/libmnl.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
#include "netlink.h"
|
|
|
|
|
#include "ifchange.h"
|
|
|
|
|
#include "arp.h"
|
|
|
|
|
#include "log.h"
|
|
|
|
|
|
|
|
|
|
#define NLMSG_RECVSIZE 8192
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
IFS_NONE = 0,
|
|
|
|
|
IFS_UP,
|
|
|
|
@ -50,76 +48,34 @@ enum {
|
|
|
|
|
IFS_REMOVED
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static unsigned int nl_seq;
|
|
|
|
|
static struct mnl_socket *mls;
|
|
|
|
|
|
|
|
|
|
/* internal callback handling */
|
|
|
|
|
static void (*nlcb_function)(struct nlmsghdr *msg, void **args);
|
|
|
|
|
static void *nlcb_args[3];
|
|
|
|
|
static __u32 nlcb_pid;
|
|
|
|
|
static unsigned int nlcb_seq;
|
|
|
|
|
static char nlcb_run;
|
|
|
|
|
static void nl_close(struct client_state_t *cs)
|
|
|
|
|
{
|
|
|
|
|
mnl_socket_close(mls);
|
|
|
|
|
cs->nlFd = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int nl_open(struct client_state_t *cs)
|
|
|
|
|
{
|
|
|
|
|
struct sockaddr_nl nlsock = {
|
|
|
|
|
.nl_family = AF_NETLINK,
|
|
|
|
|
.nl_pad = 0,
|
|
|
|
|
.nl_pid = getpid(),
|
|
|
|
|
.nl_groups = RTMGRP_LINK
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
nlcb_pid = nlsock.nl_pid;
|
|
|
|
|
|
|
|
|
|
assert(cs->nlFd == -1);
|
|
|
|
|
|
|
|
|
|
cs->nlFd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
|
|
|
|
|
|
|
|
|
if (cs->nlFd == -1)
|
|
|
|
|
if ((mls = mnl_socket_open(NETLINK_ROUTE)) == (struct mnl_socket *)-1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (bind(cs->nlFd, (const struct sockaddr *)&nlsock, sizeof(nlsock)))
|
|
|
|
|
goto err_close;
|
|
|
|
|
|
|
|
|
|
cs->nlFd = mnl_socket_get_fd(mls);
|
|
|
|
|
if (fcntl(cs->nlFd, F_SETFD, FD_CLOEXEC))
|
|
|
|
|
goto err_close;
|
|
|
|
|
|
|
|
|
|
if (mnl_socket_bind(mls, RTMGRP_LINK, 0))
|
|
|
|
|
goto err_close;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err_close:
|
|
|
|
|
nl_close(cs);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nl_close(struct client_state_t *cs)
|
|
|
|
|
{
|
|
|
|
|
close(cs->nlFd);
|
|
|
|
|
cs->nlFd = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nl_queryifstatus(int ifidx, struct client_state_t *cs)
|
|
|
|
|
{
|
|
|
|
|
struct {
|
|
|
|
|
struct nlmsghdr hdr;
|
|
|
|
|
struct ifinfomsg ifinfo;
|
|
|
|
|
} req;
|
|
|
|
|
|
|
|
|
|
req.hdr.nlmsg_len = sizeof req;
|
|
|
|
|
req.hdr.nlmsg_type = RTM_GETLINK;
|
|
|
|
|
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
|
|
|
|
|
req.hdr.nlmsg_seq = ++nl_seq;
|
|
|
|
|
req.hdr.nlmsg_pid = nlcb_pid;
|
|
|
|
|
req.ifinfo.ifi_family = AF_UNSPEC;
|
|
|
|
|
req.ifinfo.ifi_index = ifidx; /* Doesn't work... */
|
|
|
|
|
req.ifinfo.ifi_flags = IFF_UP;
|
|
|
|
|
req.ifinfo.ifi_change = 0xffffffff;
|
|
|
|
|
|
|
|
|
|
send(cs->nlFd, &req, sizeof req, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void takedown_if(struct client_state_t *cs)
|
|
|
|
|
{
|
|
|
|
|
log_line("nl: taking down interface");
|
|
|
|
|
// Same as packet.c: line 258
|
|
|
|
|
// XXX: Same as packet.c - merge somehow?
|
|
|
|
|
ifchange(NULL, IFCHANGE_DECONFIG);
|
|
|
|
|
cs->dhcpState = DS_SELECTING;
|
|
|
|
|
cs->timeout = 0;
|
|
|
|
@ -128,53 +84,99 @@ static void takedown_if(struct client_state_t *cs)
|
|
|
|
|
set_listen_raw(cs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Decode netlink messages and process them
|
|
|
|
|
static void nl_handlemsg(struct nlmsghdr *msg, unsigned int len,
|
|
|
|
|
struct client_state_t *cs)
|
|
|
|
|
static int data_attr_cb(const struct nlattr *attr, void *data)
|
|
|
|
|
{
|
|
|
|
|
if (len < sizeof(*msg)) return;
|
|
|
|
|
const struct nlattr **tb = data;
|
|
|
|
|
int type = mnl_attr_get_type(attr);
|
|
|
|
|
|
|
|
|
|
while(NLMSG_OK(msg,len)) {
|
|
|
|
|
if (nlcb_run &&
|
|
|
|
|
nlcb_pid == msg->nlmsg_pid &&
|
|
|
|
|
nlcb_seq == msg->nlmsg_seq) {
|
|
|
|
|
nlcb_function(msg, nlcb_args);
|
|
|
|
|
|
|
|
|
|
if (msg->nlmsg_type == NLMSG_DONE ||
|
|
|
|
|
msg->nlmsg_type == NLMSG_ERROR) nlcb_run = 0;
|
|
|
|
|
/* skip unsupported attribute in user-space */
|
|
|
|
|
if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
|
|
|
|
|
return MNL_CB_OK;
|
|
|
|
|
switch (type) {
|
|
|
|
|
case IFLA_IFNAME:
|
|
|
|
|
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
|
|
|
|
|
log_warning("nl: IFLA_IFNAME failed validation.");
|
|
|
|
|
return MNL_CB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case IFLA_ADDRESS:
|
|
|
|
|
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
|
|
|
|
|
log_warning("nl: IFLA_ADDRESS failed validation.");
|
|
|
|
|
return MNL_CB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
tb[type] = attr;
|
|
|
|
|
return MNL_CB_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NLMSG_PAYLOAD(msg, msg->nlmsg_len) >= sizeof(struct ifinfomsg)) {
|
|
|
|
|
struct ifinfomsg *ifinfo = NLMSG_DATA(msg);
|
|
|
|
|
static void get_if_index_and_mac(const struct nlmsghdr *nlh,
|
|
|
|
|
struct ifinfomsg *ifm,
|
|
|
|
|
const struct nlattr **tb)
|
|
|
|
|
{
|
|
|
|
|
mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
|
|
|
|
|
if (!tb[IFLA_IFNAME])
|
|
|
|
|
return;
|
|
|
|
|
if (!strcmp(client_config.interface, mnl_attr_get_str(tb[IFLA_IFNAME]))) {
|
|
|
|
|
client_config.ifindex = ifm->ifi_index;
|
|
|
|
|
if (!tb[IFLA_ADDRESS])
|
|
|
|
|
suicide("FATAL: adapter %s lacks a hardware address");
|
|
|
|
|
int maclen = mnl_attr_get_len(tb[IFLA_ADDRESS]) - 4;
|
|
|
|
|
if (maclen != 6)
|
|
|
|
|
suicide("FATAL: adapter hardware address length should be 6, but is %u",
|
|
|
|
|
maclen);
|
|
|
|
|
|
|
|
|
|
switch(msg->nlmsg_type) {
|
|
|
|
|
const unsigned char *mac =
|
|
|
|
|
(unsigned char *)mnl_attr_get_str(tb[IFLA_ADDRESS]);
|
|
|
|
|
log_line("%s hardware address %x:%x:%x:%x:%x:%x",
|
|
|
|
|
client_config.interface,
|
|
|
|
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
|
|
|
memcpy(client_config.arp, mac, 6);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// XXX: Rather than exit, go into RELEASE state until a new hardware event
|
|
|
|
|
// forces wakeup.
|
|
|
|
|
static int data_cb(const struct nlmsghdr *nlh, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct nlattr *tb[IFLA_MAX+1] = {0};
|
|
|
|
|
struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
|
|
|
|
|
struct client_state_t *cs = data;
|
|
|
|
|
|
|
|
|
|
switch(nlh->nlmsg_type) {
|
|
|
|
|
case RTM_NEWLINK:
|
|
|
|
|
if (ifinfo->ifi_index != client_config.ifindex)
|
|
|
|
|
if (!client_config.ifindex)
|
|
|
|
|
get_if_index_and_mac(nlh, ifm, (const struct nlattr **)tb);
|
|
|
|
|
if (ifm->ifi_index != client_config.ifindex)
|
|
|
|
|
break;
|
|
|
|
|
if (ifinfo->ifi_flags & IFF_UP) {
|
|
|
|
|
if (ifinfo->ifi_flags & IFF_RUNNING) {
|
|
|
|
|
if (ifm->ifi_flags & IFF_UP) {
|
|
|
|
|
if (ifm->ifi_flags & IFF_RUNNING) {
|
|
|
|
|
if (cs->ifsPrevState != IFS_UP) {
|
|
|
|
|
cs->ifsPrevState = IFS_UP;
|
|
|
|
|
/*
|
|
|
|
|
* If we have a lease, then check to see
|
|
|
|
|
* if our gateway is still valid (via ARP).
|
|
|
|
|
* If it fails, state -> INIT.
|
|
|
|
|
*
|
|
|
|
|
* If we don't have a lease, state -> INIT.
|
|
|
|
|
*/
|
|
|
|
|
// If we have a lease, then check to see
|
|
|
|
|
// if our gateway is still valid (via ARP).
|
|
|
|
|
// If it fails, state -> SELECTING.
|
|
|
|
|
if (cs->dhcpState == DS_BOUND) {
|
|
|
|
|
if (arp_gw_check(cs) == -1)
|
|
|
|
|
log_warning("arp_gw_check could not make arp socket, assuming lease is still OK");
|
|
|
|
|
} else if (cs->dhcpState != DS_SELECTING)
|
|
|
|
|
log_warning("nl: arp_gw_check could not make arp socket, assuming lease is still OK");
|
|
|
|
|
else
|
|
|
|
|
log_line("nl: interface back, revalidating lease");
|
|
|
|
|
// If we don't have a lease, state -> SELECTING.
|
|
|
|
|
} else if (cs->dhcpState != DS_SELECTING) {
|
|
|
|
|
log_line("nl: interface back, querying for new lease");
|
|
|
|
|
takedown_if(cs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (cs->ifsPrevState != IFS_DOWN) {
|
|
|
|
|
// Interface was marked up but not running.
|
|
|
|
|
// Get a new lease from scratch.
|
|
|
|
|
cs->ifsPrevState = IFS_DOWN;
|
|
|
|
|
takedown_if(cs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// No hardware carrier.
|
|
|
|
|
if (cs->ifsPrevState != IFS_SHUT) {
|
|
|
|
|
cs->ifsPrevState = IFS_SHUT;
|
|
|
|
|
log_line("Interface shut down; exiting.");
|
|
|
|
@ -183,7 +185,7 @@ static void nl_handlemsg(struct nlmsghdr *msg, unsigned int len,
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case RTM_DELLINK:
|
|
|
|
|
if (ifinfo->ifi_index != client_config.ifindex)
|
|
|
|
|
if (ifm->ifi_index != client_config.ifindex)
|
|
|
|
|
break;
|
|
|
|
|
if (cs->ifsPrevState != IFS_REMOVED) {
|
|
|
|
|
cs->ifsPrevState = IFS_REMOVED;
|
|
|
|
@ -194,95 +196,41 @@ static void nl_handlemsg(struct nlmsghdr *msg, unsigned int len,
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
msg = NLMSG_NEXT(msg,len);
|
|
|
|
|
}
|
|
|
|
|
return MNL_CB_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void handle_nl_message(struct client_state_t *cs)
|
|
|
|
|
{
|
|
|
|
|
char c[NLMSG_RECVSIZE];
|
|
|
|
|
struct nlmsghdr *msg = (struct nlmsghdr *)c;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
|
|
|
int ret;
|
|
|
|
|
assert(cs->nlFd != -1);
|
|
|
|
|
n = recv(cs->nlFd, c, NLMSG_RECVSIZE, 0);
|
|
|
|
|
nl_handlemsg(msg, n, cs);
|
|
|
|
|
do {
|
|
|
|
|
ret = mnl_socket_recvfrom(mls, buf, sizeof buf);
|
|
|
|
|
ret = mnl_cb_run(buf, ret, 0, 0, data_cb, cs);
|
|
|
|
|
} while (ret > 0);
|
|
|
|
|
if (ret == -1)
|
|
|
|
|
log_line("nl callback function returned error: %s", strerror(errno));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wait for and synchronously process netlink replies until a callback completes
|
|
|
|
|
static void nl_sync_dump(struct client_state_t *cs)
|
|
|
|
|
{
|
|
|
|
|
char c[NLMSG_RECVSIZE];
|
|
|
|
|
struct nlmsghdr *msg = (struct nlmsghdr *)c;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
nlcb_seq = nl_seq;
|
|
|
|
|
for(nlcb_run = 1; nlcb_run;) {
|
|
|
|
|
n = recv(cs->nlFd, c, NLMSG_RECVSIZE, 0);
|
|
|
|
|
assert(n >= 0);
|
|
|
|
|
nl_handlemsg(msg, n, cs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Callback function for getting interface mac address and index.
|
|
|
|
|
static void copy_ifdata(struct nlmsghdr *msg, void **args)
|
|
|
|
|
{
|
|
|
|
|
struct ifinfomsg *ifinfo = NLMSG_DATA(msg);
|
|
|
|
|
struct rtattr *rta = IFLA_RTA(ifinfo);
|
|
|
|
|
int len = NLMSG_PAYLOAD(msg, sizeof(*ifinfo));
|
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
|
|
if (msg->nlmsg_type != RTM_NEWLINK)
|
|
|
|
|
return;
|
|
|
|
|
if (client_config.ifindex)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for(; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
|
|
|
|
|
switch(rta->rta_type) {
|
|
|
|
|
case IFLA_IFNAME:
|
|
|
|
|
if (!strncmp(client_config.interface,
|
|
|
|
|
(char *)RTA_DATA(rta), RTA_PAYLOAD(rta))) {
|
|
|
|
|
client_config.ifindex = ifinfo->ifi_index;
|
|
|
|
|
found |= 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case IFLA_ADDRESS:
|
|
|
|
|
if (found != 1)
|
|
|
|
|
break;
|
|
|
|
|
/* We can only handle ethernet like devices with 6 octet MAC */
|
|
|
|
|
if (RTA_PAYLOAD(rta) == 6) {
|
|
|
|
|
memcpy(client_config.arp, RTA_DATA(rta), 6);
|
|
|
|
|
found |= 2;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (found == 3)
|
|
|
|
|
nlcb_args[0] = (void *)1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Gets interface mac address and index (synchronous).
|
|
|
|
|
int nl_getifdata(const char *ifname, struct client_state_t *cs)
|
|
|
|
|
{
|
|
|
|
|
struct {
|
|
|
|
|
struct nlmsghdr hdr;
|
|
|
|
|
struct ifinfomsg ifinfo;
|
|
|
|
|
} req;
|
|
|
|
|
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
|
|
|
struct nlmsghdr *nlh;
|
|
|
|
|
struct ifinfomsg *ifinfo;
|
|
|
|
|
unsigned int seq;
|
|
|
|
|
|
|
|
|
|
req.hdr.nlmsg_len = sizeof(req);
|
|
|
|
|
req.hdr.nlmsg_type = RTM_GETLINK;
|
|
|
|
|
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
|
|
|
|
|
req.hdr.nlmsg_seq = ++nl_seq;
|
|
|
|
|
req.hdr.nlmsg_pid = nlcb_pid;
|
|
|
|
|
req.ifinfo.ifi_family = AF_UNSPEC;
|
|
|
|
|
nlh = mnl_nlmsg_put_header(buf);
|
|
|
|
|
nlh->nlmsg_type = RTM_GETLINK;
|
|
|
|
|
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
|
|
|
|
|
nlh->nlmsg_seq = seq = time(NULL);
|
|
|
|
|
|
|
|
|
|
if (send(cs->nlFd, &req, sizeof(req), 0) != sizeof(req)) return -1;
|
|
|
|
|
ifinfo = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg));
|
|
|
|
|
ifinfo->ifi_family = AF_UNSPEC;
|
|
|
|
|
|
|
|
|
|
nlcb_function = copy_ifdata;
|
|
|
|
|
nlcb_args[0] = NULL;
|
|
|
|
|
if (mnl_socket_sendto(mls, nlh, nlh->nlmsg_len) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
nl_sync_dump(cs);
|
|
|
|
|
|
|
|
|
|
return nlcb_args[0]?0:-1;
|
|
|
|
|
handle_nl_message(cs);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|