rfkill: Add support for reacting to radio kill switch events.
In order for this to work, the correct rfkill index must be specified with the rfkill-idx option. It might be possible to auto-detect the corresponding rfkill-idx option, but I'm not sure if there's a guaranteed mapping between rfkill name and interface name, as it seems that rfkills should represent phy devices and not wlan devices. The rfkill indexes can be found by checking /sys/class/rfkill/rfkill<IDX>.
This commit is contained in:
		
							
								
								
									
										12
									
								
								src/cfg.rl
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/cfg.rl
									
									
									
									
									
								
							| @@ -163,6 +163,11 @@ struct cfgparse { | |||||||
|         case -1: allow_hostname = 0; default: break; |         case -1: allow_hostname = 0; default: break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     action rfkill_idx { | ||||||
|  |         uint32_t t = atoi(ccfg.buf); | ||||||
|  |         client_config.rfkillIdx = t; | ||||||
|  |         client_config.enable_rfkill = 1; | ||||||
|  |     } | ||||||
|     action version { print_version(); exit(EXIT_SUCCESS); } |     action version { print_version(); exit(EXIT_SUCCESS); } | ||||||
|     action help { show_usage(); exit(EXIT_SUCCESS); } |     action help { show_usage(); exit(EXIT_SUCCESS); } | ||||||
| }%% | }%% | ||||||
| @@ -206,13 +211,14 @@ struct cfgparse { | |||||||
|     gw_metric = 'gw-metric' value @gw_metric; |     gw_metric = 'gw-metric' value @gw_metric; | ||||||
|     resolv_conf = 'resolv-conf' value @resolv_conf; |     resolv_conf = 'resolv-conf' value @resolv_conf; | ||||||
|     dhcp_set_hostname = 'dhcp-set-hostname' boolval @dhcp_set_hostname; |     dhcp_set_hostname = 'dhcp-set-hostname' boolval @dhcp_set_hostname; | ||||||
|  |     rfkill_idx = 'rfkill-idx' value @rfkill_idx; | ||||||
|  |  | ||||||
|     main := blankline | |     main := blankline | | ||||||
|         clientid | background | pidfile | hostname | interface | now | quit | |         clientid | background | pidfile | hostname | interface | now | quit | | ||||||
|         request | vendorid | user | ifch_user | sockd_user | chroot | |         request | vendorid | user | ifch_user | sockd_user | chroot | | ||||||
|         state_dir | seccomp_enforce | relentless_defense | arp_probe_wait | |         state_dir | seccomp_enforce | relentless_defense | arp_probe_wait | | ||||||
|         arp_probe_num | arp_probe_min | arp_probe_max | gw_metric | |         arp_probe_num | arp_probe_min | arp_probe_max | gw_metric | | ||||||
|         resolv_conf | dhcp_set_hostname |         resolv_conf | dhcp_set_hostname | rfkill_idx | ||||||
|     ; |     ; | ||||||
| }%% | }%% | ||||||
|  |  | ||||||
| @@ -278,6 +284,7 @@ static void parse_cfgfile(const char *fname) | |||||||
|     gw_metric = ('-t'|'--gw-metric') argval @gw_metric; |     gw_metric = ('-t'|'--gw-metric') argval @gw_metric; | ||||||
|     resolv_conf = ('-R'|'--resolv-conf') argval @resolv_conf; |     resolv_conf = ('-R'|'--resolv-conf') argval @resolv_conf; | ||||||
|     dhcp_set_hostname = ('-H'|'--dhcp-set-hostname') tbv @dhcp_set_hostname; |     dhcp_set_hostname = ('-H'|'--dhcp-set-hostname') tbv @dhcp_set_hostname; | ||||||
|  |     rfkill_idx = ('-K'|'--rfkill-idx') argval @rfkill_idx; | ||||||
|     version = ('-v'|'--version') 0 @version; |     version = ('-v'|'--version') 0 @version; | ||||||
|     help = ('-?'|'--help') 0 @help; |     help = ('-?'|'--help') 0 @help; | ||||||
|  |  | ||||||
| @@ -286,7 +293,8 @@ static void parse_cfgfile(const char *fname) | |||||||
|         now | quit | request | vendorid | user | ifch_user | sockd_user | |         now | quit | request | vendorid | user | ifch_user | sockd_user | | ||||||
|         chroot | state_dir | seccomp_enforce | relentless_defense | |         chroot | state_dir | seccomp_enforce | relentless_defense | | ||||||
|         arp_probe_wait | arp_probe_num | arp_probe_min | arp_probe_max | |         arp_probe_wait | arp_probe_num | arp_probe_min | arp_probe_max | | ||||||
|         gw_metric | resolv_conf | dhcp_set_hostname | version | help |         gw_metric | resolv_conf | dhcp_set_hostname | rfkill_idx | | ||||||
|  |         version | help | ||||||
|     )*; |     )*; | ||||||
| }%% | }%% | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								src/ndhc.8
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/ndhc.8
									
									
									
									
									
								
							| @@ -133,6 +133,17 @@ default is 2000ms.  The precise inter-probe wait time is randomized. | |||||||
| Specifies the routing metric for the default gateway entry.  Defaults to | Specifies the routing metric for the default gateway entry.  Defaults to | ||||||
| 0 if not specified.  Higher values will de-prioritize the route entry. | 0 if not specified.  Higher values will de-prioritize the route entry. | ||||||
| .TP | .TP | ||||||
|  | .BI \-K\  RFKILLIDX ,\  \-\-rfkill\-idx= RFKILLIDX | ||||||
|  | If set, specifies the rfkill device index that corresponds to this interface. | ||||||
|  | ndhc will then listen for matching radio frequency kill switch events | ||||||
|  | and will bring the interface up and down in reaction to the events. | ||||||
|  | The rfkill devices can be found in /sys/class/rfkill/rfkill<RFKILLIDX>. | ||||||
|  | It may be useful to check the contents of the 'name' file within this | ||||||
|  | directory to determine the correct device index.  In any event, if | ||||||
|  | an rfkill-idx parameter is specified, ndhc will print messages for any | ||||||
|  | rfkill events that it sees, so it should not be too difficult to locate | ||||||
|  | the proper rfkill device by checking the logs after hitting the switch. | ||||||
|  | .TP | ||||||
| .BI \-v ,\  \-\-version | .BI \-v ,\  \-\-version | ||||||
| Display the ndhc version number. | Display the ndhc version number. | ||||||
| .SH SIGNALS | .SH SIGNALS | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								src/ndhc.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/ndhc.c
									
									
									
									
									
								
							| @@ -71,6 +71,7 @@ | |||||||
| #include "ifchd.h" | #include "ifchd.h" | ||||||
| #include "duiaid.h" | #include "duiaid.h" | ||||||
| #include "sockd.h" | #include "sockd.h" | ||||||
|  | #include "rfkill.h" | ||||||
|  |  | ||||||
| struct client_state_t cs = { | struct client_state_t cs = { | ||||||
|     .ifchWorking = 0, |     .ifchWorking = 0, | ||||||
| @@ -82,6 +83,7 @@ struct client_state_t cs = { | |||||||
|     .arpFd = -1, |     .arpFd = -1, | ||||||
|     .nlFd = -1, |     .nlFd = -1, | ||||||
|     .nlPortId = -1, |     .nlPortId = -1, | ||||||
|  |     .rfkillFd = -1, | ||||||
|     .routerArp = "\0\0\0\0\0\0", |     .routerArp = "\0\0\0\0\0\0", | ||||||
|     .serverArp = "\0\0\0\0\0\0", |     .serverArp = "\0\0\0\0\0\0", | ||||||
| }; | }; | ||||||
| @@ -275,7 +277,7 @@ static void handle_ifch_message(void) | |||||||
|         cs.ifchWorking = 0; |         cs.ifchWorking = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| #define NDHC_NUM_EP_FDS 7 | #define NDHC_NUM_EP_FDS 8 | ||||||
| static void do_ndhc_work(void) | static void do_ndhc_work(void) | ||||||
| { | { | ||||||
|     struct epoll_event events[NDHC_NUM_EP_FDS]; |     struct epoll_event events[NDHC_NUM_EP_FDS]; | ||||||
| @@ -295,6 +297,8 @@ static void do_ndhc_work(void) | |||||||
|     epoll_add(cs.epollFd, ifchSock[0]); |     epoll_add(cs.epollFd, ifchSock[0]); | ||||||
|     epoll_add(cs.epollFd, ifchStream[0]); |     epoll_add(cs.epollFd, ifchStream[0]); | ||||||
|     epoll_add(cs.epollFd, sockdStream[0]); |     epoll_add(cs.epollFd, sockdStream[0]); | ||||||
|  |     if (client_config.enable_rfkill && cs.rfkillFd != -1) | ||||||
|  |         epoll_add(cs.epollFd, cs.nlFd); | ||||||
|     start_dhcp_listen(&cs); |     start_dhcp_listen(&cs); | ||||||
|     nowts = curms(); |     nowts = curms(); | ||||||
|     goto jumpstart; |     goto jumpstart; | ||||||
| @@ -330,6 +334,9 @@ static void do_ndhc_work(void) | |||||||
|             } else if (fd == sockdStream[0]) { |             } else if (fd == sockdStream[0]) { | ||||||
|                 if (events[i].events & (EPOLLHUP|EPOLLERR|EPOLLRDHUP)) |                 if (events[i].events & (EPOLLHUP|EPOLLERR|EPOLLRDHUP)) | ||||||
|                     exit(EXIT_FAILURE); |                     exit(EXIT_FAILURE); | ||||||
|  |             } else if (fd == cs.rfkillFd && client_config.enable_rfkill) { | ||||||
|  |                 if (events[i].events & EPOLLIN) | ||||||
|  |                     handle_rfkill_notice(&cs, client_config.rfkillIdx); | ||||||
|             } else |             } else | ||||||
|                 suicide("epoll_wait: unknown fd"); |                 suicide("epoll_wait: unknown fd"); | ||||||
|         } |         } | ||||||
| @@ -429,6 +436,8 @@ static void ndhc_main(void) { | |||||||
|     if ((cs.nlFd = nl_open(NETLINK_ROUTE, RTMGRP_LINK, &cs.nlPortId)) < 0) |     if ((cs.nlFd = nl_open(NETLINK_ROUTE, RTMGRP_LINK, &cs.nlPortId)) < 0) | ||||||
|         suicide("%s: failed to open netlink socket", __func__); |         suicide("%s: failed to open netlink socket", __func__); | ||||||
|  |  | ||||||
|  |     cs.rfkillFd = rfkill_open(&client_config.enable_rfkill); | ||||||
|  |  | ||||||
|     if (client_config.foreground && !client_config.background_if_no_lease) { |     if (client_config.foreground && !client_config.background_if_no_lease) { | ||||||
|         if (file_exists(pidfile, "w") < 0) |         if (file_exists(pidfile, "w") < 0) | ||||||
|             suicide("%s: can't open pidfile '%s' for write!", |             suicide("%s: can't open pidfile '%s' for write!", | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ struct client_state_t { | |||||||
|     int ifsPrevState; |     int ifsPrevState; | ||||||
|     int ifchWorking; // ifch is performing interface changes. |     int ifchWorking; // ifch is performing interface changes. | ||||||
|     int ifDeconfig; // Set if the interface has already been deconfigured. |     int ifDeconfig; // Set if the interface has already been deconfigured. | ||||||
|     int epollFd, signalFd, listenFd, arpFd, nlFd; |     int epollFd, signalFd, listenFd, arpFd, nlFd, rfkillFd; | ||||||
|     int nlPortId; |     int nlPortId; | ||||||
|     uint32_t clientAddr, serverAddr, srcAddr, routerAddr; |     uint32_t clientAddr, serverAddr, srcAddr, routerAddr; | ||||||
|     uint32_t lease, renewTime, rebindTime, xid; |     uint32_t lease, renewTime, rebindTime, xid; | ||||||
| @@ -54,6 +54,7 @@ struct client_config_t { | |||||||
|     char quit_after_lease;       // Quit after obtaining lease |     char quit_after_lease;       // Quit after obtaining lease | ||||||
|     char abort_if_no_lease;      // Abort if no lease |     char abort_if_no_lease;      // Abort if no lease | ||||||
|     char background_if_no_lease; // Fork to background if no lease |     char background_if_no_lease; // Fork to background if no lease | ||||||
|  |     char enable_rfkill;          // Listen for rfkill events | ||||||
|     char interface[IFNAMSIZ];    // The name of the interface to use |     char interface[IFNAMSIZ];    // The name of the interface to use | ||||||
|     char clientid[64];           // Optional client id to use |     char clientid[64];           // Optional client id to use | ||||||
|     uint8_t clientid_len;        // Length of the clientid |     uint8_t clientid_len;        // Length of the clientid | ||||||
| @@ -61,6 +62,7 @@ struct client_config_t { | |||||||
|     char vendor[64];             // Vendor identification that will be sent |     char vendor[64];             // Vendor identification that will be sent | ||||||
|     int metric;                  // Metric for the default route |     int metric;                  // Metric for the default route | ||||||
|     int ifindex;                 // Index number of the interface to use |     int ifindex;                 // Index number of the interface to use | ||||||
|  |     uint32_t rfkillIdx;          // Index of the corresponding rfkill device | ||||||
|     uint8_t arp[6];              // Our arp address |     uint8_t arp[6];              // Our arp address | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										92
									
								
								src/rfkill.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/rfkill.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | /* rfkill.c - rfkill interface and handling | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2015 Nicholas J. Kain <njkain at gmail dot com> | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * - Redistributions of source code must retain the above copyright notice, | ||||||
|  |  *   this list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * - Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *   this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *   and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
|  |  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||||||
|  |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||||
|  |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||||
|  |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||||
|  |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||||
|  |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||||
|  |  * POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <string.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <linux/rfkill.h> | ||||||
|  | #include "nk/log.h" | ||||||
|  | #include "nk/io.h" | ||||||
|  | #include "ndhc.h" | ||||||
|  | #include "netlink.h" | ||||||
|  | #include "rfkill.h" | ||||||
|  |  | ||||||
|  | int rfkill_open(char enable_rfkill[static 1]) | ||||||
|  | { | ||||||
|  |     if (!*enable_rfkill) | ||||||
|  |         return -1; | ||||||
|  |     int r = open("/dev/rfkill", O_RDONLY|O_CLOEXEC|O_NONBLOCK); | ||||||
|  |     if (r < 0) { | ||||||
|  |         *enable_rfkill = 0; | ||||||
|  |         log_line("rfkill disabled: could not open /dev/rfkill: %s", | ||||||
|  |                  strerror(errno)); | ||||||
|  |     } | ||||||
|  |     return r; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void handle_rfkill_notice(struct client_state_t cs[static 1], 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; | ||||||
|  |     } | ||||||
|  |     if (len != RFKILL_EVENT_SIZE_V1) { | ||||||
|  |         log_error("rfkill: event has unexpected size: %d", len); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     log_line("rfkill: idx[%u] type[%u] op[%u] soft[%u] hard[%u]", | ||||||
|  |              event.idx, event.type, event.op, event.soft, event.hard); | ||||||
|  |     if (event.idx != rfkidx) | ||||||
|  |         return; | ||||||
|  |     if (event.op != RFKILL_OP_CHANGE && event.op != RFKILL_OP_CHANGE_ALL) | ||||||
|  |         return; | ||||||
|  |     if (event.soft || event.hard) { | ||||||
|  |         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 (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"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										35
									
								
								src/rfkill.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/rfkill.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | #ifndef NDHC_RFKILL_H_ | ||||||
|  | #define NDHC_RFKILL_H_ | ||||||
|  | /* rfkill.h - rfkill interface and handling | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2015 Nicholas J. Kain <njkain at gmail dot com> | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * - Redistributions of source code must retain the above copyright notice, | ||||||
|  |  *   this list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * - Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *   this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *   and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
|  |  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||||||
|  |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||||
|  |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||||
|  |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||||
|  |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||||
|  |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||||
|  |  * POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | int rfkill_open(char enable_rfkill[static 1]); | ||||||
|  | void handle_rfkill_notice(struct client_state_t cs[static 1], uint32_t rfkidx); | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
		Reference in New Issue
	
	Block a user