From e3d4d4c1aa9f3f572aaefdc8bd2de76ee30d451a Mon Sep 17 00:00:00 2001 From: "Nicholas J. Kain" Date: Fri, 13 Feb 2015 16:25:36 -0500 Subject: [PATCH] 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. --- src/cfg.rl | 12 +++++-- src/ndhc.8 | 11 +++++++ src/ndhc.c | 11 ++++++- src/ndhc.h | 4 ++- src/rfkill.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/rfkill.h | 35 ++++++++++++++++++++ 6 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 src/rfkill.c create mode 100644 src/rfkill.h diff --git a/src/cfg.rl b/src/cfg.rl index b8f56e7..d83b495 100644 --- a/src/cfg.rl +++ b/src/cfg.rl @@ -163,6 +163,11 @@ struct cfgparse { 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 help { show_usage(); exit(EXIT_SUCCESS); } }%% @@ -206,13 +211,14 @@ struct cfgparse { gw_metric = 'gw-metric' value @gw_metric; resolv_conf = 'resolv-conf' value @resolv_conf; dhcp_set_hostname = 'dhcp-set-hostname' boolval @dhcp_set_hostname; + rfkill_idx = 'rfkill-idx' value @rfkill_idx; main := blankline | clientid | background | pidfile | hostname | interface | now | quit | request | vendorid | user | ifch_user | sockd_user | chroot | state_dir | seccomp_enforce | relentless_defense | arp_probe_wait | 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; resolv_conf = ('-R'|'--resolv-conf') argval @resolv_conf; dhcp_set_hostname = ('-H'|'--dhcp-set-hostname') tbv @dhcp_set_hostname; + rfkill_idx = ('-K'|'--rfkill-idx') argval @rfkill_idx; version = ('-v'|'--version') 0 @version; help = ('-?'|'--help') 0 @help; @@ -286,7 +293,8 @@ static void parse_cfgfile(const char *fname) now | quit | request | vendorid | user | ifch_user | sockd_user | chroot | state_dir | seccomp_enforce | relentless_defense | 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 )*; }%% diff --git a/src/ndhc.8 b/src/ndhc.8 index a096316..724dfc8 100644 --- a/src/ndhc.8 +++ b/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 0 if not specified. Higher values will de-prioritize the route entry. .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. +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 Display the ndhc version number. .SH SIGNALS diff --git a/src/ndhc.c b/src/ndhc.c index 1bec61a..c35b6a6 100644 --- a/src/ndhc.c +++ b/src/ndhc.c @@ -71,6 +71,7 @@ #include "ifchd.h" #include "duiaid.h" #include "sockd.h" +#include "rfkill.h" struct client_state_t cs = { .ifchWorking = 0, @@ -82,6 +83,7 @@ struct client_state_t cs = { .arpFd = -1, .nlFd = -1, .nlPortId = -1, + .rfkillFd = -1, .routerArp = "\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; } -#define NDHC_NUM_EP_FDS 7 +#define NDHC_NUM_EP_FDS 8 static void do_ndhc_work(void) { 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, ifchStream[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); nowts = curms(); goto jumpstart; @@ -330,6 +334,9 @@ static void do_ndhc_work(void) } else if (fd == sockdStream[0]) { 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); } else 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) 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 (file_exists(pidfile, "w") < 0) suicide("%s: can't open pidfile '%s' for write!", diff --git a/src/ndhc.h b/src/ndhc.h index 017c0f7..d231c55 100644 --- a/src/ndhc.h +++ b/src/ndhc.h @@ -40,7 +40,7 @@ struct client_state_t { int ifsPrevState; int ifchWorking; // ifch is performing interface changes. 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; uint32_t clientAddr, serverAddr, srcAddr, routerAddr; uint32_t lease, renewTime, rebindTime, xid; @@ -54,6 +54,7 @@ struct client_config_t { char quit_after_lease; // Quit after obtaining lease char abort_if_no_lease; // Abort 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 clientid[64]; // Optional client id to use 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 int metric; // Metric for the default route 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 }; diff --git a/src/rfkill.c b/src/rfkill.c new file mode 100644 index 0000000..4b2f703 --- /dev/null +++ b/src/rfkill.c @@ -0,0 +1,92 @@ +/* rfkill.c - rfkill interface and handling + * + * Copyright (c) 2015 Nicholas J. Kain + * 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 +#include +#include +#include +#include +#include +#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"); + } + } +} + diff --git a/src/rfkill.h b/src/rfkill.h new file mode 100644 index 0000000..ac2ae18 --- /dev/null +++ b/src/rfkill.h @@ -0,0 +1,35 @@ +#ifndef NDHC_RFKILL_H_ +#define NDHC_RFKILL_H_ +/* rfkill.h - rfkill interface and handling + * + * Copyright (c) 2015 Nicholas J. Kain + * 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 +