This repository was archived by the owner on Sep 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathwsupp_rfkill.c
142 lines (113 loc) · 3.57 KB
/
wsupp_rfkill.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include "common.h"
#include "rfkill.h"
#include "wsupp.h"
/* When a card gets rf-killed, the link loses IFF_UP and RTNL gets notification
of a state change. But when rfkill gets undone, the reverse does not happen.
The interface remains in "down" state and must be commanded back "up".
RTNL layer also gets no notifications of any kind that rf-unkill happened
(and this tool doesn't even listen to RTNL notifications).
The only somewhat reliable way to be notified is by listening to /dev/rfkill.
But rfkill device is provided by a standalone module that may not be loadeded
at any given time, and may get un-/re-loaded. Normally this does not happens,
so wimon keeps the fd open. However if open attempt fails, wsupp will try to
re-open it on any suitable occasion. This may lead to redundant open calls
in case rfkill is in fact missing, but there's probably no other way around
this. Hopefully events that trigger reopen attempts are rare.
Another problem is that /dev/rfkill reports events for rfkill devices (idx
in the struct below) which do *not* match netdev ifi-s. The trick used here
is to check for /sys/class/net/$ifname/phy80211/rfkill$idx. The $idx-$ifname
association seems to be stable for at least as long as the fd remains open,
but there are no guarantees beyond that.
The end result of this all is effectively `ifconfig (iface) up` done each
time some managed link gets un-killed, followed by attempt to re-establish
connection. */
int rfkill;
int rfkilled;
static int rfkidx;
/* The interface gets brought up with simple ioctls. It could have been done
with RTNL as well, but setting up RTNL for this mere reason hardly makes
sense.
Current code works on interface named $ifname, in contrast with GENL code
that uses $ifindex instead. Renaming the interface while wsupp runs *will*
confuse it. */
//#define IFF_UP (1<<0)
static void bring_iface_up(void)
{
int fd = netlink;
char* name = ifname;
uint nlen = strlen(name);
struct ifreq ifr;
int ret;
if(nlen > sizeof(ifr.ifr_name))
quit("iface name too long: %s\n", name);
memzero(&ifr, sizeof(ifr));
memcpy(ifr.ifr_name, name, nlen);
if((ret = ioctl(fd, SIOCGIFFLAGS, &ifr)) < 0)
quit("ioctl SIOCGIFFLAGS %s: %m\n", name);
if(ifr.ifr_flags & IFF_UP)
return;
ifr.ifr_flags |= IFF_UP;
if((ret = ioctl(fd, SIOCSIFFLAGS, &ifr)) < 0)
quit("ioctl SIOCSIFFLAGS %s: %m\n", name);
}
static int match_rfkill(int idx)
{
struct stat st;
int plen = 100;
char path[plen];
snprintf(path, plen, "/sys/class/net/%s/phy80211/rfkill%i",
ifname, idx);
return (stat(path, &st) >= 0);
}
static void handle_event(struct rfkill_event* re)
{
if(rfkidx < 0) {
if(match_rfkill(re->idx))
rfkidx = re->idx;
else
return;
} else if(re->idx != rfkidx) {
return;
}
if(re->soft || re->hard) {
rfkilled = 1;
clr_timer();
} else {
rfkilled = 0;
bring_iface_up();
handle_rfrestored();
}
}
void retry_rfkill(void)
{
if(rfkill > 0)
return;
rfkill = open("/dev/rfkill", O_RDONLY | O_NONBLOCK);
rfkidx = -1;
pollset = 0;
}
/* One event per read() here, even if more are queued. */
void handle_rfkill(void)
{
char buf[128];
struct rfkill_event* re;
int fd = rfkill;
int rd;
while((rd = read(fd, buf, sizeof(buf))) > 0) {
re = (struct rfkill_event*) buf;
if((ulong)rd < sizeof(*re))
continue;
if(re->type != RFKILL_TYPE_WLAN)
continue;
handle_event(re);
}
}