Separate event state gathering from action dispatch in main epoll loop.
This is the first step towards using coroutines.
This commit is contained in:
		
							
								
								
									
										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 | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user