Separate event state gathering from action dispatch in main epoll loop.
This is the first step towards using coroutines.
This commit is contained in:
parent
658d2954b8
commit
61387408d0
88
src/arp.c
88
src/arp.c
@ -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.
|
||||
|
61
src/arp.h
61
src/arp.h
@ -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
181
src/coroutine.h
Normal 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 */
|
20
src/dhcp.c
20
src/dhcp.c
@ -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
|
||||
|
@ -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]);
|
||||
|
115
src/ndhc.c
115
src/ndhc.c
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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_ */
|
||||
|
82
src/rfkill.c
82
src/rfkill.c
@ -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);
|
||||
}
|
||||
|
||||
|
11
src/rfkill.h
11
src/rfkill.h
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user