背景
家里的win10 电脑, 家人外出时 偶尔远程连接上来用, 为了降低卧室的风扇噪音, 15分钟不用电脑, 设置了睡眠功能.
一旦睡眠了, 远程 RDP 就连接不上了.那下次怎么访问 RDP 呢?
所以!
在 VPN 路由器上, 监听 TCP连接的3389端口握手报文的到达, 然后使用 wake_on_lan 唤醒 win10 电脑.
完美.
环境
节点1
VPN:wireguard
OS: Debian 12
IP: 192.168.1.8
节点1
OS: win10
IP: 192.168.1.9
节点3
外出的电脑 使用VPN连接到家里
实测
外出的电脑 VPN 拨号成功后, 双击RDP, 直接连接成功!!!
代码
ether-wake.api.c1
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335/* ether-wake.c: Send a magic packet to wake up sleeping machines. */
/*
This program generates and transmits a Wake-On-LAN (WOL) "Magic Packet",
used for restarting machines that have been soft-powered-down
(ACPI D3-warm state). It currently generates the standard AMD Magic Packet
format, with an optional password appended.
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
Contact the author for use under other terms.
This source file was originally part of the network tricks package, and
is now distributed to support the Scyld Beowulf system.
Copyright 1999-2003 Donald Becker and Scyld Computing Corporation.
The author may be reached as becker@scyld, or C/O
Scyld Computing Corporation
914 Bay Ridge Road, Suite 220
Annapolis MD 21403
Notes:
On some systems dropping root capability allows the process to be
dumped, traced or debugged.
If someone traces this program, they get control of a raw socket.
Linux handles this safely, but beware when porting this program.
An alternative to needing 'root' is using a UDP broadcast socket, however
doing so only works with adapters configured for unicast+broadcast Rx
filter. That configuration consumes more power.
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#if 0 /* Only exists on some versions. */
#include <ioctls.h>
#endif
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <features.h>
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#endif
#include <netdb.h>
#include <netinet/ether.h>
/* Grrr, no consistency between include versions.
Enable this if setsockopt() isn't declared with your library. */
#if 0
extern int setsockopt __P ((int __fd, int __level, int __optname,
__ptr_t __optval, int __optlen));
#else /* New, correct head files. */
#include <sys/socket.h>
#endif
u_char outpack[1000];
int outpack_sz = 0;
int debug = 0;
u_char wol_passwd[6];
int wol_passwd_sz = 0;
static int opt_no_src_addr = 0, opt_broadcast = 0;
static int get_dest_addr(const char *arg, struct ether_addr *eaddr);
static int get_fill(unsigned char *pkt, struct ether_addr *eaddr);
int wake_main(const char *dev, char *wake_mac)
{
const char *ifname = dev;
int one = 1; /* True, for socket options. */
int s; /* Raw socket */
int verbose = 0;
int perm_failure = 0;
int i,pktsize;
#if defined(PF_PACKET)
struct sockaddr_ll whereto;
#else
struct sockaddr whereto; /* who to wake up */
#endif
struct ether_addr eaddr;
/* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to
work as non-root, but we need SOCK_PACKET to specify the Ethernet
destination address. */
#if defined(PF_PACKET)
s = socket(PF_PACKET, SOCK_RAW, 0);
#else
s = socket(AF_INET, SOCK_PACKET, SOCK_PACKET);
#endif
if (s < 0) {
if (errno == EPERM)
fprintf(stderr, "ether-wake: This program must be run as root.\n");
else
perror("ether-wake: socket");
perm_failure++;
}
/* Don't revert if debugging allows a normal user to get the raw socket. */
setuid(getuid());
/* We look up the station address before reporting failure so that
errors may be reported even when run as a normal user.
*/
if (get_dest_addr(wake_mac, &eaddr) != 0)
return 3;
if (perm_failure && ! debug)
return 2;
pktsize = get_fill(outpack, &eaddr);
/* Fill in the source address, if possible.
The code to retrieve the local station address is Linux specific. */
if (! opt_no_src_addr) {
struct ifreq if_hwaddr;
unsigned char *hwaddr = (unsigned char *)if_hwaddr.ifr_hwaddr.sa_data;
strcpy(if_hwaddr.ifr_name, ifname);
if (ioctl(s, SIOCGIFHWADDR, &if_hwaddr) < 0) {
fprintf(stderr, "SIOCGIFHWADDR on %s failed: %s\n", ifname,
strerror(errno));
/* Magic packets still work if our source address is bogus, but
we fail just to be anal. */
return 1;
}
memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6);
if (verbose) {
printf("The hardware address (SIOCGIFHWADDR) of %s is type %d "
"%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", ifname,
if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1],
hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
}
}
if (wol_passwd_sz > 0) {
memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz);
pktsize += wol_passwd_sz;
}
if (verbose > 1) {
printf("The final packet is: ");
for (i = 0; i < pktsize; i++)
printf(" %2.2x", outpack[i]);
printf(".\n");
}
/* This is necessary for broadcasts to work */
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&one, sizeof(one)) < 0)
perror("setsockopt: SO_BROADCAST");
#if defined(PF_PACKET)
{
struct ifreq ifr;
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) {
fprintf(stderr, "SIOCGIFINDEX on %s failed: %s\n", ifname,
strerror(errno));
return 1;
}
memset(&whereto, 0, sizeof(whereto));
whereto.sll_family = AF_PACKET;
whereto.sll_ifindex = ifr.ifr_ifindex;
/* The manual page incorrectly claims the address must be filled.
We do so because the code may change to match the docs. */
whereto.sll_halen = ETH_ALEN;
memcpy(whereto.sll_addr, outpack, ETH_ALEN);
}
#else
whereto.sa_family = 0;
strcpy(whereto.sa_data, ifname);
#endif
if ((i = sendto(s, outpack, pktsize, 0, (struct sockaddr *)&whereto,
sizeof(whereto))) < 0)
perror("sendto");
else
printf("Sendto worked ! %d.\n", i);
#ifdef USE_SEND
if (bind(s, (struct sockaddr *)&whereto, sizeof(whereto)) < 0)
perror("bind");
else if (send(s, outpack, 100, 0) < 0)
perror("send");
#endif
#ifdef USE_SENDMSG
{
struct msghdr msghdr = { 0,};
struct iovec iovector[1];
msghdr.msg_name = &whereto;
msghdr.msg_namelen = sizeof(whereto);
msghdr.msg_iov = iovector;
msghdr.msg_iovlen = 1;
iovector[0].iov_base = outpack;
iovector[0].iov_len = pktsize;
if ((i = sendmsg(s, &msghdr, 0)) < 0)
perror("sendmsg");
else if (debug)
printf("sendmsg worked, %d (%d).\n", i, errno);
}
#endif
close(s);
return 0;
}
/* Convert the host ID string to a MAC address.
The string may be a
Host name
IP address string
MAC address string
*/
static int get_dest_addr(const char *hostid, struct ether_addr *eaddr)
{
struct ether_addr *eap;
eap = ether_aton(hostid);
if (eap) {
*eaddr = *eap;
if (debug)
fprintf(stderr, "The target station address is %s.\n",
ether_ntoa(eaddr));
} else if (ether_hostton(hostid, eaddr) == 0) {
if (debug)
fprintf(stderr, "Station address for hostname %s is %s.\n",
hostid, ether_ntoa(eaddr));
} else {
(void)fprintf(stderr,
"ether-wake: The Magic Packet host address must be "
"specified as\n"
" - a station address, 00:11:22:33:44:55, or\n"
" - a hostname with a known 'ethers' entry.\n");
return -1;
}
return 0;
}
static int get_fill(unsigned char *pkt, struct ether_addr *eaddr)
{
int offset, i;
unsigned char *station_addr = eaddr->ether_addr_octet;
if (opt_broadcast)
memset(pkt+0, 0xff, 6);
else
memcpy(pkt, station_addr, 6);
memcpy(pkt+6, station_addr, 6);
pkt[12] = 0x08; /* Or 0x0806 for ARP, 0x8035 for RARP */
pkt[13] = 0x42;
offset = 14;
memset(pkt+offset, 0xff, 6);
offset += 6;
for (i = 0; i < 16; i++) {
memcpy(pkt+offset, station_addr, 6);
offset += 6;
}
if (debug) {
fprintf(stderr, "Packet is ");
for (i = 0; i < offset; i++)
fprintf(stderr, " %2.2x", pkt[i]);
fprintf(stderr, ".\n");
}
return offset;
}
static int get_wol_pw(const char *optarg)
{
int passwd[6];
int byte_cnt;
int i;
byte_cnt = sscanf(optarg, "%2x:%2x:%2x:%2x:%2x:%2x",
&passwd[0], &passwd[1], &passwd[2],
&passwd[3], &passwd[4], &passwd[5]);
if (byte_cnt < 4)
byte_cnt = sscanf(optarg, "%d.%d.%d.%d",
&passwd[0], &passwd[1], &passwd[2], &passwd[3]);
if (byte_cnt < 4) {
fprintf(stderr, "Unable to read the Wake-On-LAN password.\n");
return 0;
}
printf(" The Magic packet password is %2.2x %2.2x %2.2x %2.2x (%d).\n",
passwd[0], passwd[1], passwd[2], passwd[3], byte_cnt);
for (i = 0; i < byte_cnt; i++)
wol_passwd[i] = passwd[i];
return wol_passwd_sz = byte_cnt;
}
#if 0
{
to = (struct sockaddr_in *)&whereto;
to->sin_family = AF_INET;
if (inet_aton(target, &to->sin_addr)) {
hostname = target;
}
memset (&sa, 0, sizeof sa);
sa.sa_family = AF_INET;
strncpy (sa.sa_data, interface, sizeof sa.sa_data);
sendto (sock, buf, bufix + len, 0, &sa, sizeof sa);
strncpy (sa.sa_data, interface, sizeof sa.sa_data);
#if 1
sendto (sock, buf, bufix + len, 0, &sa, sizeof sa);
#else
bind (sock, &sa, sizeof sa);
connect();
send (sock, buf, bufix + len, 0);
#endif
}
#endif
/*
* Local variables:
* compile-command: "gcc -O -Wall -o ether-wake ether-wake.c"
* c-indent-level: 4
* c-basic-offset: 4
* c-indent-level: 4
* tab-width: 4
* End:
*/
tcp_syn_wol.c1
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212// https://www.ibm.com/docs/en/aix/7.2?topic=pcl-sample-2-capturing-packet-data-saving-it-file-processing-later
/*
* Use pcap_open_live() to open a packet capture device.
* Use pcap_dump() to output the packet capture data in
* binary format to a file for processing later.
*/
#include <unistd.h>
#include <stdlib.h>
#include <libgen.h>
#include <stdio.h>
#include <string.h>
#include <pcap.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define IFSZ 16
#define FLTRSZ 120
#define MAXHOSTSZ 256
int wake_main(const char *dev, char *wake_mac);
struct wake_on_lan_t
{
uint32_t ipv4;
char mac_str[64];
};
enum {
CB_FLD_ADDR,
CB_FLD_MAC,
CB_FLD_NUM,
};
int rule_index = 0;
const char *pcap_dev = NULL;
const char *wake_dev = NULL;
struct wake_on_lan_t *g_rules ;
int g_rule_cnt ;
int offset_ip = 0; //有些网卡没有ETH头,如隧道
int ipv4_offset_to_dst = 16;
int usage(char *progname)
{
printf("Usage: %s <pcap_dev> <offset_to_ip> <wake_dev>\n", basename(progname));
printf("普通网卡有 ETH头, offset_to_ip=14\n");
printf("虚拟网卡没有ETH头,直接是IPV4, offset_to_ip=0\n");
exit(11);
}
void pkt_arrived(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{
char buff[64];
uint32_t dst = *(uint32_t*)(bytes + offset_ip+ipv4_offset_to_dst);
printf("目标IP %s ", inet_ntop(AF_INET, &dst, buff, sizeof(buff)));
int i = 0;
for(i = 0; i < g_rule_cnt; i++)
{
if(g_rules[i].ipv4 == dst)
{
printf("唤醒 %s ", g_rules[i].mac_str);
wake_main(wake_dev, g_rules[i].mac_str);
return;
}
}
printf("忽略\n");
}
int cb_rule(const char*line, void *user)
{
int i, rc;
char *s, *sp, *in[CB_FLD_NUM];
static const char *dlm = " \t\n";
int dim = 2;
char buff[1024];
strncpy(buff, line, sizeof(buff));
s = buff;
for (i = 0; i != dim; i++, s = NULL)
{
in[i] = strtok_r(s, dlm, &sp);
if (in[i] == NULL)
{
return -1;
}
}
struct wake_on_lan_t *rule = (struct wake_on_lan_t*)user + rule_index;
inet_pton(AF_INET, in[CB_FLD_ADDR], &rule->ipv4);
memcpy(rule->mac_str, in[CB_FLD_MAC], sizeof(rule->mac_str));
rule_index++;
printf("ipv4:%u mac:%s\n", rule->ipv4, in[CB_FLD_MAC]);
return 0;
}
int cb_cnt(const char*line, void *user)
{
(*(int32_t*)user)++;
return 0;
}
int fgets_callback(const char*rulefile, int (*cb)(const char *line, void *user), void *user)
{
unsigned int i = 0;
char buff[128];
FILE *fh = fopen(rulefile, "rb");
if (fh == NULL)
{
perror(rulefile);
return -1;
}
while ((fgets(buff, sizeof(buff), fh) != NULL))
{
cb(buff, user);
}
fclose(fh);
return 0;
}
int conf_get_rules(const char *filename, struct wake_on_lan_t**rule, int *nums)
{
int cnt = 0;
int rc = 0;
rc = fgets_callback(filename, cb_cnt, &cnt);
if(rc < 0)
{
return -1;
}
struct wake_on_lan_t *p = calloc(cnt, sizeof(struct wake_on_lan_t));
rc = fgets_callback(filename, cb_rule, p);
if(rc < 0)
{
return -1;
}
*rule = p;
*nums = cnt;
return 0;
}
int
main(int argc, char **argv)
{
pcap_t *p; /* packet capture descriptor */
struct pcap_stat ps; /* packet statistics */
char errbuf[PCAP_ERRBUF_SIZE]; /* buffer to hold error text */
char prestr[80]; /* prefix string for errors from pcap_perror */
struct bpf_program prog; /* compiled bpf filter program */
int optimize = 1; /* passed to pcap_compile to do optimization */
int snaplen = 80; /* amount of data per packet */
int promisc = 1; /* do not change mode; if in promiscuous mode, stay in it, otherwise, do not */
int to_ms = 1000; /* timeout, in milliseconds */
uint32_t mask = 0; /* network address mask */
int pcount = 0; /* number of packets actually read */
//app pcapdev offset_to_ip wake_dev
if (argc < 4)
{
usage(argv[0]);
return -1;
}
const char *conf = "/etc/tcp_syn_wol.conf";
int rc = conf_get_rules(conf, &g_rules, &g_rule_cnt);
if(rc < 0)
{
printf("load Error %s\n", conf);
return 0;
}
pcap_dev = argv[1];
offset_ip = atoi(argv[2]);
wake_dev = argv[3];
if (!(p = pcap_open_live(pcap_dev, snaplen, promisc, to_ms, errbuf))) {
fprintf(stderr, "Error opening interface %s: %s\n", pcap_dev, errbuf);
exit(2);
}
if (pcap_compile(p,&prog, "tcp[tcpflags] == tcp-syn", optimize,mask) < 0) {
fprintf(stderr, "Error compiling bpf filter on %s: %s\n", pcap_dev, pcap_geterr(p));
exit(5);
}
if (pcap_setfilter(p, &prog) < 0) {
sprintf(prestr, "Error installing bpf filter on interface %s", pcap_dev);
pcap_perror(p,prestr);
exit(6);
}
if ((pcount = pcap_loop(p, -1, pkt_arrived, NULL)) < 0)
{
sprintf(prestr,"Error reading packets from interface %s", pcap_dev);
pcap_perror(p,prestr);
exit(8);
}
if (pcap_stats(p, &ps) != 0) {
fprintf(stderr, "Error getting Packet Capture stats: %s\n",
pcap_geterr(p));
exit(10);
}
printf("Packet Capture Statistics:\n");
printf("%d packets received by filter\n", ps.ps_recv);
printf("%d packets dropped by kernel\n", ps.ps_drop);
pcap_close(p);
}
配置文件
tcp_syn_wol.conf1
2192.168.XXX.XXX XX:XX:XX:XX:5A:64
192.168.XXX.XXX XX:XX:XX:XX:5A:C4
编译运行
compile.sh1
2gcc -g ether-wake.api.c tcp_syn_wol.c -l pcap -o tcp_syn_wol
./tcp_syn_wol enp1s0 14 enp1s0
服务文件
1 |
|