Separate event state gathering from action dispatch in main epoll loop.

This is the first step towards using coroutines.
This commit is contained in:
Nicholas J. Kain 2015-02-15 06:38:03 -05:00
parent 658d2954b8
commit 61387408d0
10 changed files with 437 additions and 223 deletions

View File

@ -62,52 +62,7 @@ int arp_probe_max = 2000; // maximum delay until repeated probe (ms)
#define RATE_LIMIT_INTERVAL 60000 // delay between successive attempts
#define DEFEND_INTERVAL 10000 // minimum interval between defensive ARPs
typedef enum {
AS_NONE = 0, // Nothing to react to wrt ARP
AS_COLLISION_CHECK, // Checking to see if another host has our IP before
// accepting a new lease.
AS_GW_CHECK, // Seeing if the default GW still exists on the local
// segment after the hardware link was lost.
AS_GW_QUERY, // Finding the default GW MAC address.
AS_DEFENSE, // Defending our IP address (RFC5227)
AS_MAX,
} arp_state_t;
typedef enum {
ASEND_COLLISION_CHECK,
ASEND_GW_PING,
ASEND_ANNOUNCE,
ASEND_MAX,
} arp_send_t;
struct arp_stats {
long long ts;
int count;
};
struct arp_data {
struct dhcpmsg dhcp_packet; // Used only for AS_COLLISION_CHECK
struct arpMsg reply;
struct arp_stats send_stats[ASEND_MAX];
long long wake_ts[AS_MAX];
long long last_conflict_ts; // TS of the last conflicting ARP seen.
long long arp_check_start_ts; // TS of when we started the
// AS_COLLISION_CHECK state.
size_t reply_offset;
arp_state_t state;
unsigned int total_conflicts; // Total number of address conflicts on
// the interface. Never decreases.
int gw_check_initpings; // Initial count of ASEND_GW_PING when
// AS_GW_CHECK was entered.
uint16_t probe_wait_time; // Time to wait for a COLLISION_CHECK reply
// (in ms?).
bool using_bpf:1; // Is a BPF installed on the ARP socket?
bool relentless_def:1; // Don't give up defense no matter what.
bool router_replied:1;
bool server_replied:1;
};
static struct arp_data garp = {
struct arp_data garp = {
.state = AS_NONE,
.wake_ts = { -1, -1, -1, -1, -1 },
.send_stats = {{0,0},{0,0},{0,0}},
@ -237,7 +192,7 @@ void arp_close_fd(struct client_state_t cs[static 1])
garp.wake_ts[i] = -1;
}
static void arp_reopen_fd(struct client_state_t cs[static 1])
void arp_reopen_fd(struct client_state_t cs[static 1])
{
arp_state_t prev_state = garp.state;
arp_min_close_fd(cs);
@ -406,7 +361,7 @@ static int arp_get_gw_hwaddr(struct client_state_t cs[static 1])
return 0;
}
static void arp_failed(struct client_state_t cs[static 1])
void arp_failed(struct client_state_t cs[static 1])
{
log_line("%s: arp: Offered address is in use. Declining.",
client_config.interface);
@ -427,7 +382,7 @@ static void arp_failed(struct client_state_t cs[static 1])
0 : RATE_LIMIT_INTERVAL);
}
static void arp_gw_failed(struct client_state_t cs[static 1])
void arp_gw_failed(struct client_state_t cs[static 1])
{
garp.wake_ts[AS_GW_CHECK] = -1;
reinit_selecting(cs, 0);
@ -794,31 +749,31 @@ static const arp_state_fn_t arp_states[] = {
{ arp_do_invalid, 0 }, // AS_MAX
};
void handle_arp_response(struct client_state_t cs[static 1])
void arp_packet_action(struct client_state_t cs[static 1])
{
if (arp_states[garp.state].packet_fn)
arp_states[garp.state].packet_fn(cs);
arp_reply_clear();
}
int arp_packet_get(struct client_state_t cs[static 1])
{
ssize_t r = 0;
if (garp.reply_offset < sizeof garp.reply) {
r = safe_read(cs->arpFd, (char *)&garp.reply + garp.reply_offset,
sizeof garp.reply - garp.reply_offset);
if (r == 0)
return ARPR_CLOSED;
if (r < 0) {
log_error("%s: (%s) ARP response read failed: %s",
client_config.interface, __func__, strerror(errno));
switch (garp.state) {
case AS_COLLISION_CHECK: arp_failed(cs); break;
case AS_GW_CHECK: arp_gw_failed(cs); break;
default: arp_reopen_fd(cs); break;
}
} else
garp.reply_offset += (size_t)r;
}
if (r <= 0) {
handle_arp_timeout(cs, curms());
return;
return ARPR_ERROR;
}
garp.reply_offset += (size_t)r;
}
if (garp.reply_offset < ARP_MSG_SIZE)
return;
return ARPR_NONE;
// Emulate the BPF filters if they are not in use.
if (!garp.using_bpf &&
@ -826,12 +781,9 @@ void handle_arp_response(struct client_state_t cs[static 1])
(garp.state == AS_DEFENSE &&
!arp_validate_bpf_defense(cs, &garp.reply)))) {
arp_reply_clear();
return;
return ARPR_NONE;
}
if (arp_states[garp.state].packet_fn)
arp_states[garp.state].packet_fn(cs);
arp_reply_clear();
return ARPR_PENDING;
}
// Perform retransmission if necessary.

View File

@ -58,6 +58,53 @@ extern int arp_probe_num;
extern int arp_probe_min;
extern int arp_probe_max;
typedef enum {
AS_NONE = 0, // Nothing to react to wrt ARP
AS_COLLISION_CHECK, // Checking to see if another host has our IP before
// accepting a new lease.
AS_GW_CHECK, // Seeing if the default GW still exists on the local
// segment after the hardware link was lost.
AS_GW_QUERY, // Finding the default GW MAC address.
AS_DEFENSE, // Defending our IP address (RFC5227)
AS_MAX,
} arp_state_t;
typedef enum {
ASEND_COLLISION_CHECK,
ASEND_GW_PING,
ASEND_ANNOUNCE,
ASEND_MAX,
} arp_send_t;
struct arp_stats {
long long ts;
int count;
};
struct arp_data {
struct dhcpmsg dhcp_packet; // Used only for AS_COLLISION_CHECK
struct arpMsg reply;
struct arp_stats send_stats[ASEND_MAX];
long long wake_ts[AS_MAX];
long long last_conflict_ts; // TS of the last conflicting ARP seen.
long long arp_check_start_ts; // TS of when we started the
// AS_COLLISION_CHECK state.
size_t reply_offset;
arp_state_t state;
unsigned int total_conflicts; // Total number of address conflicts on
// the interface. Never decreases.
int gw_check_initpings; // Initial count of ASEND_GW_PING when
// AS_GW_CHECK was entered.
uint16_t probe_wait_time; // Time to wait for a COLLISION_CHECK reply
// (in ms?).
bool using_bpf:1; // Is a BPF installed on the ARP socket?
bool relentless_def:1; // Don't give up defense no matter what.
bool router_replied:1;
bool server_replied:1;
};
extern struct arp_data garp;
void set_arp_relentless_def(bool v);
void arp_reset_send_stats(void);
void arp_close_fd(struct client_state_t cs[static 1]);
@ -66,7 +113,19 @@ int arp_check(struct client_state_t cs[static 1],
int arp_gw_check(struct client_state_t cs[static 1]);
void arp_set_defense_mode(struct client_state_t cs[static 1]);
void arp_success(struct client_state_t cs[static 1]);
void handle_arp_response(struct client_state_t cs[static 1]);
void arp_failed(struct client_state_t cs[static 1]);
void arp_gw_failed(struct client_state_t cs[static 1]);
void arp_reopen_fd(struct client_state_t cs[static 1]);
enum {
ARPR_NONE = 0,
ARPR_ERROR,
ARPR_PENDING,
ARPR_CLOSED,
};
void arp_packet_action(struct client_state_t cs[static 1]);
int arp_packet_get(struct client_state_t cs[static 1]);
void handle_arp_timeout(struct client_state_t cs[static 1], long long nowts);
long long arp_get_wake_ts(void);

181
src/coroutine.h Normal file
View File

@ -0,0 +1,181 @@
/* coroutine.h
*
* Coroutine mechanics, implemented on top of standard ANSI C. See
* http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html for
* a full discussion of the theory behind this.
*
* To use these macros to define a coroutine, you need to write a
* function that looks something like this.
*
* [Simple version using static variables (scr macros)]
* int ascending (void) {
* static int i;
*
* scrBegin;
* for (i=0; i<10; i++) {
* scrReturn(i);
* }
* scrFinish(-1);
* }
*
* [Re-entrant version using an explicit context structure (ccr macros)]
* int ascending (ccrContParam) {
* ccrBeginContext;
* int i;
* ccrEndContext(foo);
*
* ccrBegin(foo);
* for (foo->i=0; foo->i<10; foo->i++) {
* ccrReturn(foo->i);
* }
* ccrFinish(-1);
* }
*
* In the static version, you need only surround the function body
* with `scrBegin' and `scrFinish', and then you can do `scrReturn'
* within the function and on the next call control will resume
* just after the scrReturn statement. Any local variables you need
* to be persistent across an `scrReturn' must be declared static.
*
* In the re-entrant version, you need to declare your persistent
* variables between `ccrBeginContext' and `ccrEndContext'. These
* will be members of a structure whose name you specify in the
* parameter to `ccrEndContext'.
*
* The re-entrant macros will malloc() the state structure on first
* call, and free() it when `ccrFinish' is reached. If you want to
* abort in the middle, you can use `ccrStop' to free the state
* structure immediately (equivalent to an explicit return() in a
* caller-type routine).
*
* A coroutine returning void type may call `ccrReturnV',
* `ccrFinishV' and `ccrStopV', or `scrReturnV', to avoid having to
* specify an empty parameter to the ordinary return macros.
*
* Ground rules:
* - never put `ccrReturn' or `scrReturn' within an explicit `switch'.
* - never put two `ccrReturn' or `scrReturn' statements on the same
* source line.
*
* The caller of a static coroutine calls it just as if it were an
* ordinary function:
*
* void main(void) {
* int i;
* do {
* i = ascending();
* printf("got number %d\n", i);
* } while (i != -1);
* }
*
* The caller of a re-entrant coroutine must provide a context
* variable:
*
* void main(void) {
* ccrContext z = 0;
* do {
* printf("got number %d\n", ascending (&z));
* } while (z);
* }
*
* Note that the context variable is set back to zero when the
* coroutine terminates (by crStop, or by control reaching
* crFinish). This can make the re-entrant coroutines more useful
* than the static ones, because you can tell when they have
* finished.
*
* If you need to dispose of a crContext when it is non-zero (that
* is, if you want to stop calling a coroutine without suffering a
* memory leak), the caller should call `ccrAbort(ctx)' where `ctx'
* is the context variable.
*
* This mechanism could have been better implemented using GNU C
* and its ability to store pointers to labels, but sadly this is
* not part of the ANSI C standard and so the mechanism is done by
* case statements instead. That's why you can't put a crReturn()
* inside a switch() statement.
*/
/*
* coroutine.h is copyright 1995,2000 Simon Tatham.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* $Id$
*/
#ifndef COROUTINE_H
#define COROUTINE_H
#include <stdlib.h>
/*
* `scr' macros for static coroutines.
*/
#define scrBegin static int scrLine = 0; switch(scrLine) { case 0:;
#define scrFinish(z) } return (z)
#define scrFinishV } return
#define scrReturn(z) \
do {\
scrLine=__LINE__;\
return (z); case __LINE__:;\
} while (0)
#define scrReturnV \
do {\
scrLine=__LINE__;\
return; case __LINE__:;\
} while (0)
/*
* `ccr' macros for re-entrant coroutines.
*/
#define ccrContParam void **ccrParam
#define ccrBeginContext struct ccrContextTag { int ccrLine
#define ccrEndContext(x) } *x = (struct ccrContextTag *)*ccrParam
#define ccrBegin(x) if(!x) {x= *ccrParam=malloc(sizeof(*x)); x->ccrLine=0;}\
if (x) switch(x->ccrLine) { case 0:;
#define ccrFinish(z) } free(*ccrParam); *ccrParam=0; return (z)
#define ccrFinishV } free(*ccrParam); *ccrParam=0; return
#define ccrReturn(z) \
do {\
((struct ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\
return (z); case __LINE__:;\
} while (0)
#define ccrReturnV \
do {\
((struct ccrContextTag *)*ccrParam)->ccrLine=__LINE__;\
return; case __LINE__:;\
} while (0)
#define ccrStop(z) do{ free(*ccrParam); *ccrParam=0; return (z); }while(0)
#define ccrStopV do{ free(*ccrParam); *ccrParam=0; return; }while(0)
#define ccrContext void *
#define ccrAbort(ctx) do { free (ctx); ctx = 0; } while (0)
#endif /* COROUTINE_H */

View File

@ -380,13 +380,14 @@ static int validate_dhcp_packet(struct client_state_t cs[static 1],
return 1;
}
void handle_packet(struct client_state_t cs[static 1])
int dhcp_packet_get(struct client_state_t cs[static 1],
struct dhcpmsg packet[static 1],
uint8_t msgtype[static 1],
uint32_t srcaddr[static 1])
{
if (cs->listenFd < 0)
return;
struct dhcpmsg packet;
uint32_t srcaddr;
ssize_t r = get_raw_packet(cs, &packet, &srcaddr);
return -1;
ssize_t r = get_raw_packet(cs, packet, srcaddr);
if (r < 0) {
// Not a transient issue handled by packet collection functions.
if (r != -2) {
@ -395,12 +396,11 @@ void handle_packet(struct client_state_t cs[static 1])
stop_dhcp_listen(cs);
start_dhcp_listen(cs);
}
return;
return -1;
}
uint8_t msgtype;
if (!validate_dhcp_packet(cs, (size_t)r, &packet, &msgtype))
return;
packet_action(cs, &packet, msgtype, srcaddr);
if (!validate_dhcp_packet(cs, (size_t)r, packet, msgtype))
return -1;
return 0;
}
// Initialize a DHCP client packet that will be sent to a server

View File

@ -83,7 +83,10 @@ struct udp_dhcp_packet {
void start_dhcp_listen(struct client_state_t cs[static 1]);
void stop_dhcp_listen(struct client_state_t cs[static 1]);
void handle_packet(struct client_state_t cs[static 1]);
int dhcp_packet_get(struct client_state_t cs[static 1],
struct dhcpmsg packet[static 1],
uint8_t msgtype[static 1],
uint32_t srcaddr[static 1]);
ssize_t send_discover(struct client_state_t cs[static 1]);
ssize_t send_selecting(struct client_state_t cs[static 1]);
ssize_t send_renew(struct client_state_t cs[static 1]);

View File

@ -182,7 +182,13 @@ static void setup_signals_ndhc(void)
epoll_add(cs.epollFd, cs.signalFd);
}
static void signal_dispatch(void)
enum {
SIGNAL_NONE = 0,
SIGNAL_RENEW,
SIGNAL_RELEASE
};
static int signal_dispatch(void)
{
struct signalfd_siginfo si;
memset(&si, 0, sizeof si);
@ -190,24 +196,22 @@ static void signal_dispatch(void)
if (r < 0) {
log_error("%s: ndhc: error reading from signalfd: %s",
client_config.interface, strerror(errno));
return;
return SIGNAL_NONE;
}
if ((size_t)r < sizeof si) {
log_error("%s: ndhc: short read from signalfd: %zd < %zu",
client_config.interface, r, sizeof si);
return;
return SIGNAL_NONE;
}
switch (si.ssi_signo) {
case SIGUSR1: force_renew_action(&cs); break;
case SIGUSR2: force_release_action(&cs); break;
case SIGUSR1: return SIGNAL_RENEW;
case SIGUSR2: return SIGNAL_RELEASE;
case SIGCHLD:
suicide("ndhc-master: Subprocess terminated unexpectedly. Exiting.");
break;
case SIGTERM:
log_line("Received SIGTERM. Exiting gracefully.");
exit(EXIT_SUCCESS);
break;
default: break;
default: return SIGNAL_NONE;
}
}
@ -261,6 +265,7 @@ static void fail_if_state_dir_dne(void)
#define NDHC_NUM_EP_FDS 7
static void do_ndhc_work(void)
{
struct dhcpmsg dhcp_packet;
struct epoll_event events[NDHC_NUM_EP_FDS];
long long nowts;
int timeout;
@ -284,27 +289,53 @@ static void do_ndhc_work(void)
goto jumpstart;
for (;;) {
int r = epoll_wait(cs.epollFd, events, NDHC_NUM_EP_FDS, timeout);
if (r < 0) {
int maxi = epoll_wait(cs.epollFd, events, NDHC_NUM_EP_FDS, timeout);
if (maxi < 0) {
if (errno == EINTR)
continue;
else
suicide("epoll_wait failed");
}
for (int i = 0; i < r; ++i) {
for (int i = 0; i < maxi; ++i) {
int fd = events[i].data.fd;
if (fd == cs.signalFd) {
if (events[i].events & EPOLLIN)
signal_dispatch();
if (!(events[i].events & EPOLLIN))
return;
int sigv = signal_dispatch();
if (sigv == SIGNAL_RENEW)
force_renew_action(&cs);
else if (sigv == SIGNAL_RELEASE)
force_release_action(&cs);
} else if (fd == cs.listenFd) {
if (events[i].events & EPOLLIN)
handle_packet(&cs);
if (!(events[i].events & EPOLLIN))
return;
uint32_t srcaddr;
uint8_t msgtype;
int r = dhcp_packet_get(&cs, &dhcp_packet, &msgtype, &srcaddr);
if (!r)
packet_action(&cs, &dhcp_packet, msgtype, srcaddr);
} else if (fd == cs.arpFd) {
if (events[i].events & EPOLLIN)
handle_arp_response(&cs);
if (!(events[i].events & EPOLLIN))
return;
int r = arp_packet_get(&cs);
if (r == ARPR_PENDING) {
arp_packet_action(&cs);
} else if (r == ARPR_ERROR) {
if (garp.state == AS_COLLISION_CHECK)
arp_failed(&cs);
else if (garp.state == AS_GW_CHECK)
arp_gw_failed(&cs);
else
arp_reopen_fd(&cs);
handle_arp_timeout(&cs, curms());
} else if (r == ARPR_CLOSED)
handle_arp_timeout(&cs, curms());
} else if (fd == cs.nlFd) {
if (events[i].events & EPOLLIN)
handle_nl_message(&cs);
if (!(events[i].events & EPOLLIN))
return;
int nl_event = nl_event_get(&cs);
if (nl_event != IFS_NONE)
nl_event_react(&cs, nl_event);
} else if (fd == ifchStream[0]) {
if (events[i].events & (EPOLLHUP|EPOLLERR|EPOLLRDHUP))
exit(EXIT_FAILURE);
@ -312,8 +343,30 @@ static void do_ndhc_work(void)
if (events[i].events & (EPOLLHUP|EPOLLERR|EPOLLRDHUP))
exit(EXIT_FAILURE);
} else if (fd == cs.rfkillFd && client_config.enable_rfkill) {
if (events[i].events & EPOLLIN)
handle_rfkill_notice(&cs, client_config.rfkillIdx);
if (!(events[i].events & EPOLLIN))
return;
int rfk = rfkill_get(&cs, 1, client_config.rfkillIdx);
if (rfk == RFK_ENABLED) {
cs.rfkill_set = 1;
if (cs.ifsPrevState == IFS_UP) {
log_line("rfkill: radio now blocked; bringing interface down");
cs.ifsPrevState = IFS_DOWN;
ifnocarrier_action(&cs);
} else
log_line("rfkill: radio now blocked, but interface isn't up");
} else if (rfk == RFK_DISABLED) {
cs.rfkill_set = 0;
if (cs.ifsPrevState == IFS_DOWN) {
log_line("rfkill: radio now unblocked; bringing interface up");
cs.ifsPrevState = IFS_UP;
ifup_action(&cs);
} else {
if (cs.ifsPrevState == IFS_SHUT)
log_line("rfkill: radio now unblocked, but interface was shut down by user");
else
log_line("rfkill: radio now unblocked, but interface is removed");
}
}
} else
suicide("epoll_wait: unknown fd");
}
@ -461,7 +514,6 @@ void background(void)
static void wait_for_rfkill()
{
cs.rfkill_set = 1;
struct epoll_event events[2];
cs.rfkillFd = rfkill_open(&client_config.enable_rfkill);
if (cs.rfkillFd < 0)
@ -480,13 +532,20 @@ static void wait_for_rfkill()
}
for (int i = 0; i < r; ++i) {
int fd = events[i].data.fd;
if (fd == cs.rfkillFd) {
if (events[i].events & EPOLLIN) {
if (!rfkill_wait_for_end(&cs))
goto rfkill_gone;
}
} else
if (fd != cs.rfkillFd)
suicide("epoll_wait: unknown fd");
if (events[i].events & EPOLLIN) {
int rfk = rfkill_get(&cs, 0, 0);
if (rfk == RFK_DISABLED) {
switch (perform_ifup()) {
case 1: case 0: goto rfkill_gone;
case -3:
log_line("rfkill: radio immediately blocked again; spurious?");
break;
default: suicide("failed to set the interface to up state");
}
}
}
}
}
rfkill_gone:

View File

@ -43,67 +43,81 @@
#include "nl.h"
#include "state.h"
static void nl_process_msgs(const struct nlmsghdr *nlh, void *data)
int nl_event_react(struct client_state_t cs[static 1], int state)
{
struct ifinfomsg *ifm = NLMSG_DATA(nlh);
struct client_state_t *cs = data;
if (state == cs->ifsPrevState)
return -1;
// If the rfkill switch is set, a lot of netlink state change
// commands will fail outright, so just ignore events until
// it is gone.
if (cs->rfkill_set)
return;
return -1;
switch(nlh->nlmsg_type) {
case RTM_NEWLINK:
if (ifm->ifi_index != client_config.ifindex)
break;
// IFF_UP corresponds to ifconfig down or ifconfig up.
if (ifm->ifi_flags & IFF_UP) {
// IFF_RUNNING is the hardware carrier.
if (ifm->ifi_flags & IFF_RUNNING) {
if (cs->ifsPrevState != IFS_UP) {
cs->ifsPrevState = IFS_UP;
ifup_action(cs);
}
} else if (cs->ifsPrevState != IFS_DOWN) {
// Interface configured, but no hardware carrier.
cs->ifsPrevState = IFS_DOWN;
ifnocarrier_action(cs);
}
} else if (cs->ifsPrevState != IFS_SHUT) {
// User shut down the interface.
cs->ifsPrevState = IFS_SHUT;
ifdown_action(cs);
}
break;
case RTM_DELLINK:
if (ifm->ifi_index != client_config.ifindex)
break;
if (cs->ifsPrevState != IFS_REMOVED) {
cs->ifsPrevState = IFS_REMOVED;
log_line("Interface removed. Exiting.");
exit(EXIT_SUCCESS);
}
break;
default:
break;
switch (state) {
case IFS_UP:
cs->ifsPrevState = IFS_UP;
ifup_action(cs);
break;
case IFS_DOWN:
// Interface configured, but no hardware carrier.
cs->ifsPrevState = IFS_DOWN;
ifnocarrier_action(cs);
break;
case IFS_SHUT:
// User shut down the interface.
cs->ifsPrevState = IFS_SHUT;
ifdown_action(cs);
break;
case IFS_REMOVED:
cs->ifsPrevState = IFS_REMOVED;
log_line("Interface removed. Exiting.");
exit(EXIT_SUCCESS);
break;
default: break;
}
return 0;
}
void handle_nl_message(struct client_state_t cs[static 1])
static int nl_process_msgs_return;
static void nl_process_msgs(const struct nlmsghdr *nlh, void *data)
{
(void)data;
struct ifinfomsg *ifm = NLMSG_DATA(nlh);
if (ifm->ifi_index != client_config.ifindex)
return;
if (nlh->nlmsg_type == RTM_NEWLINK) {
// IFF_UP corresponds to ifconfig down or ifconfig up.
// IFF_RUNNING is the hardware carrier.
if (ifm->ifi_flags & IFF_UP) {
if (ifm->ifi_flags & IFF_RUNNING)
nl_process_msgs_return = IFS_UP;
else
nl_process_msgs_return = IFS_DOWN;
} else {
nl_process_msgs_return = IFS_SHUT;
}
} else if (nlh->nlmsg_type == RTM_DELLINK)
nl_process_msgs_return = IFS_REMOVED;
}
int nl_event_get(struct client_state_t cs[static 1])
{
char nlbuf[8192];
ssize_t ret;
assert(cs->nlFd != -1);
nl_process_msgs_return = IFS_NONE;
do {
ret = nl_recv_buf(cs->nlFd, nlbuf, sizeof nlbuf);
if (ret < 0)
break;
if (nl_foreach_nlmsg(nlbuf, ret, 0, cs->nlPortId, nl_process_msgs, cs)
if (nl_foreach_nlmsg(nlbuf, ret, 0, cs->nlPortId, nl_process_msgs, 0)
< 0)
break;
} while (ret > 0);
return nl_process_msgs_return;
}
static int get_if_index_and_mac(const struct nlmsghdr *nlh,

View File

@ -40,7 +40,8 @@ enum {
IFS_REMOVED
};
void handle_nl_message(struct client_state_t cs[static 1]);
int nl_event_react(struct client_state_t cs[static 1], int state);
int nl_event_get(struct client_state_t cs[static 1]);
int nl_getifdata(void);
#endif /* NK_NETLINK_H_ */

View File

@ -26,7 +26,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
@ -36,8 +35,6 @@
#include "nk/log.h"
#include "nk/io.h"
#include "ndhc.h"
#include "netlink.h"
#include "ifset.h"
#include "rfkill.h"
int rfkill_open(char enable_rfkill[static 1])
@ -53,90 +50,31 @@ int rfkill_open(char enable_rfkill[static 1])
return r;
}
static int rfkill_check(struct client_state_t cs[static 1],
int (*rfenable)(struct client_state_t[static 1]),
int (*rfdisable)(struct client_state_t[static 1]),
bool check_idx, uint32_t rfkidx)
// check_idx: Does rfkidx have any meaning?
// rfkidx: Pay attention only to this radio kill switch number.
int rfkill_get(struct client_state_t cs[static 1],
int check_idx, uint32_t rfkidx)
{
struct rfkill_event event;
ssize_t len = safe_read(cs->rfkillFd, (char *)&event, sizeof event);
if (len < 0) {
log_error("rfkill: safe_read failed: %s", strerror(errno));
return -1;
return RFK_FAIL;
}
if (len != RFKILL_EVENT_SIZE_V1) {
log_error("rfkill: event has unexpected size: %d", len);
return -1;
return RFK_FAIL;
}
log_line("rfkill: idx[%u] type[%u] op[%u] soft[%u] hard[%u]",
event.idx, event.type, event.op, event.soft, event.hard);
if (check_idx && event.idx != rfkidx)
return 0;
return RFK_NONE;
if (event.op != RFKILL_OP_CHANGE && event.op != RFKILL_OP_CHANGE_ALL)
return 0;
return RFK_NONE;
if (event.soft || event.hard) {
return rfenable(cs);
return RFK_ENABLED;
} else {
return rfdisable(cs);
return RFK_DISABLED;
}
}
static int handle_rfkill_notice_enable(struct client_state_t cs[static 1])
{
cs->rfkill_set = 1;
if (cs->ifsPrevState == IFS_UP) {
log_line("rfkill: radio now blocked; bringing interface down");
cs->ifsPrevState = IFS_DOWN;
ifnocarrier_action(cs);
} else
log_line("rfkill: radio now blocked, but interface isn't up");
return 0;
}
static int handle_rfkill_notice_disable(struct client_state_t cs[static 1])
{
cs->rfkill_set = 0;
if (cs->ifsPrevState == IFS_DOWN) {
log_line("rfkill: radio now unblocked; bringing interface up");
cs->ifsPrevState = IFS_UP;
ifup_action(cs);
} else {
if (cs->ifsPrevState == IFS_SHUT)
log_line("rfkill: radio now unblocked, but interface was shut down by user");
else
log_line("rfkill: radio now unblocked, but interface is removed");
}
return 0;
}
static int rfkill_wait_for_end_enable(struct client_state_t cs[static 1])
{
(void)cs;
return -1;
}
static int rfkill_wait_for_end_disable(struct client_state_t cs[static 1])
{
switch (perform_ifup()) {
case 1: case 0:
cs->rfkill_set = 0;
return 0;
case -3:
log_line("rfkill: radio immediately blocked again; spurious?");
return -1;
default: suicide("failed to set the interface to up state");
}
}
int handle_rfkill_notice(struct client_state_t cs[static 1], uint32_t rfkidx)
{
return rfkill_check(cs, handle_rfkill_notice_enable,
handle_rfkill_notice_disable, true, rfkidx);
}
int rfkill_wait_for_end(struct client_state_t cs[static 1])
{
return rfkill_check(cs, rfkill_wait_for_end_enable,
rfkill_wait_for_end_disable, false, 0);
}

View File

@ -28,9 +28,16 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
enum {
RFK_NONE = 0,
RFK_FAIL,
RFK_ENABLED,
RFK_DISABLED,
};
int rfkill_open(char enable_rfkill[static 1]);
int handle_rfkill_notice(struct client_state_t cs[static 1], uint32_t rfkidx);
int rfkill_wait_for_end(struct client_state_t cs[static 1]);
int rfkill_get(struct client_state_t cs[static 1],
int check_idx, uint32_t rfkidx);
#endif