1/*
2 *
3 * Copyright (C) 1998 by Christopher Chan-Nui
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 *
19 */
20#include <ctype.h>
21#include <unistd.h>
22#include <stdio.h>
23#include <netdb.h>
24#include <netinet/in.h>
25#include <sys/socket.h>
26#ifdef HAVE_GETOPT_H
27#include <getopt.h>
28#endif
29#include <errno.h>
30
31#include <arpa/inet.h>
32#include <net/if.h>
33#include <net/if_arp.h>
34#include <netinet/ether.h>
35#include <netpacket/packet.h>
36#include <string.h>
37#include <stdlib.h>
38#include <sys/ioctl.h>
39
40#define _PATH_PROCNET_ARP "/proc/net/arp"
41
42#define ARP_OPT_A (0x1)
43#define ARP_OPT_p (0x2)
44#define ARP_OPT_H (0x4)
45#define ARP_OPT_t (0x8)
46#define ARP_OPT_i (0x10)
47#define ARP_OPT_a (0x20)
48#define ARP_OPT_d (0x40)
49#define ARP_OPT_n (0x80)        /* do not resolve addresses     */
50#define ARP_OPT_D (0x100)       /* HW-address is devicename     */
51#define ARP_OPT_s (0x200)
52#define ARP_OPT_v (0x400 * DEBUG)       /* debugging output flag        */
53
54#define DEV_NAME "br0"
55
56#if 0
57#ifndef DEFAULTMAC
58#define DEFAULTMAC "00a0c9852a5f"
59#endif
60#ifndef DEFAULTTARGET
61#define DEFAULTTARGET "255.255.255.255"
62#endif
63
64static char *rcsid="@(#) $Id: wakelan.c,v 1.8 1998/08/30 05:04:28 channui Exp $";
65static void *use_rcsid = (void *)((char *)&use_rcsid || (void *)&rcsid);
66char *versionid = "1.0";
67
68void usage(char *name) {
69    printf ("Usage: %s [options] [mac] [broadcast] [port]\n"
70	    "    -b addr    broadcast address\n"
71	    "    -m mac     mac address of host\n"
72	    "    -p port    UDP port to broadcast to\n"
73	    "    -v[v]      version\n"
74	    , name);
75    exit (0);
76}
77#endif
78
79int parse_mac(unsigned char *mac, char *str) {
80    int i;
81    int count;
82    char c;
83    unsigned char val;
84    int colon_ok = 1;
85    for (i = 0; i < 6; i++) {
86	mac[i] = 0;
87    }
88    for (i = 0; i < 6; i++) {
89	count = 0;
90	val   = 0;
91	do {
92	    c = toupper(*str++);
93	    if (c >= '0' && c <= '9') {
94		val = (val * 16) + (c - '0');
95	    } else if (c >= 'A' && c <= 'F') {
96		val = (val * 16) + (c - 'A') + 10;
97	    } else if (c == ':') {
98		if (colon_ok || count-- != 0)
99		    break;
100	    } else if (c == '\0') {
101		str--;
102		break;
103	    } else {
104		return 0;
105	    }
106	    colon_ok=1;
107	} while (++count < 2);
108	colon_ok=(count<2);
109	*mac++ = val;
110    }
111    if (*str)
112	return 0;
113    return 1;
114}
115
116#if 0
117int main (int argc, char *argv[]) {
118    int sock;
119    int optval = 1;
120    int version =0;
121    int i, j, c, rc;
122    char msg[1024];
123    int  msglen = 0;
124    struct sockaddr_in bcast;
125    struct hostent *he;
126    struct in_addr inaddr;
127    unsigned char macaddr[6];
128    char *mac    = DEFAULTMAC;
129    char *target = DEFAULTTARGET;
130    short bport = htons(32767);
131
132    while ((c = getopt(argc, argv, "hvp:m:b:")) != EOF) {
133	switch (c) {
134	    case 'b': target = optarg;             break;
135	    case 'm': mac    = optarg;             break;
136	    case 'p': bport = htons(atoi(optarg)); break;
137	    case 'v': version++;                   break;
138	    case 'h':
139	    case '?':
140		usage(argv[0]);
141	}
142    }
143
144    if (version) {
145        printf ("Version: %s\n", versionid);
146        if (version > 1) {
147            printf ("  RCSID: %s\n", rcsid);
148        }
149        exit (0);
150    }
151
152    if (argv[optind] != NULL) {
153	mac = argv[optind++];
154    }
155    if (argv[optind] != NULL) {
156	target = argv[optind++];
157    }
158    if (argv[optind] != NULL) {
159	bport = htons(atoi(argv[optind++]));
160    }
161    if (argv[optind] != NULL) {
162	usage(argv[0]);
163    }
164
165    if (!parse_mac(macaddr, mac)) {
166	printf ("Illegal MAC address '%s'\n", mac);
167	exit (1);
168    }
169
170    if (!inet_aton(target, &inaddr)) {
171	he = gethostbyname(target);
172	inaddr = *(struct in_addr *)he->h_addr_list[0];
173    }
174
175    for (i = 0; i < 6; i++) {
176	msg[msglen++] = 0xff;
177    }
178    for (i = 0; i < 16; i++) {
179	for (j = 0; j < sizeof(macaddr); j++) {
180	    msg[msglen++] = macaddr[j];
181	}
182    }
183
184    memset(&bcast, 0, sizeof(bcast));
185    bcast.sin_family      = AF_INET;
186    bcast.sin_addr.s_addr = inaddr.s_addr;
187    bcast.sin_port        = bport;
188
189    sock = socket(AF_INET, SOCK_DGRAM, 0);
190    if (sock < 0) {
191	printf ("Can't allocate socket\n");
192	exit (1);
193    }
194    if ((rc=setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval))) < 0) {
195	printf ("Can't socket option SO_BROADCAST: rc = %d, errno=%s(%d)\n",
196		rc, strerror(errno), errno);
197	exit (1);
198    }
199    sendto(sock, &msg, msglen, 0, (struct sockaddr *)&bcast, sizeof(bcast));
200    return 0;
201}
202#else
203int send_wol (char *target, char *mac)
204{
205    int sock;
206    int optval = 1;
207    int i, j, rc;
208    char msg[1024];
209    int  msglen = 0;
210    struct sockaddr_in bcast;
211    struct hostent *he;
212    struct in_addr inaddr;
213    unsigned char macaddr[6];
214    short bport = htons(32767);
215
216    if (!parse_mac(macaddr, mac)) {
217	printf ("Illegal MAC address '%s'\n", mac);
218	exit (1);
219    }
220
221    if (!inet_aton(target, &inaddr)) {
222	he = gethostbyname(target);
223	inaddr = *(struct in_addr *)he->h_addr_list[0];
224    }
225
226    for (i = 0; i < 6; i++) {
227	msg[msglen++] = 0xff;
228    }
229    for (i = 0; i < 16; i++) {
230	for (j = 0; j < sizeof(macaddr); j++) {
231	    msg[msglen++] = macaddr[j];
232	}
233    }
234
235    memset(&bcast, 0, sizeof(bcast));
236    bcast.sin_family      = AF_INET;
237    bcast.sin_addr.s_addr = inaddr.s_addr;
238    bcast.sin_port        = bport;
239
240    sock = socket(AF_INET, SOCK_DGRAM, 0);
241    if (sock < 0) {
242	printf ("Can't allocate socket\n");
243	return -1;
244    }
245    if ((rc=setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval))) < 0) {
246	printf ("Can't socket option SO_BROADCAST: rc = %d, errno=%s(%d)\n",
247		rc, strerror(errno), errno);
248	return -1;
249    }
250    sendto(sock, &msg, msglen, 0, (struct sockaddr *)&bcast, sizeof(bcast));
251    return 0;
252}
253#endif
254
255int get_bcast_addr(char *ifname, char *bcast_addr)
256{
257        struct ifreq ifr;
258        int fd;
259
260        fd = socket(AF_INET, SOCK_DGRAM, 0);
261        if (fd < 0) {
262                printf("Unable to open the socket\n");
263                return -1;
264        }
265        strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
266        memset(bcast_addr, 0, 16);
267        if (ioctl(fd, SIOCGIFBRDADDR, &ifr) >= 0)
268                sprintf(bcast_addr, "%s", inet_ntoa(((struct sockaddr_in *)&ifr.ifr_broadaddr)->sin_addr));
269
270        close(fd);
271
272        return 0;
273}
274
275int main()
276{
277        char host[100];
278        char ip[100];
279        char hwa[100];
280        char mask[100];
281        char line[200];
282        char dev[100];
283        int type, flags;
284        FILE *fp;
285        int num;
286        char mac[16];
287        char bcast_addr[16];
288        int hwa_idx;
289        int mac_idx;
290
291        host[0] = '\0';
292
293        /* Open the PROCps kernel table. */
294        fp = fopen(_PATH_PROCNET_ARP, "r");
295        if (fp == NULL) {
296                printf("Unable to open the file\n");
297                return 0;
298        }
299
300        /* get Bcast addr for 'br0' */
301        get_bcast_addr(DEV_NAME, (char *)bcast_addr);
302
303        /* Bypass header -- read until newline */
304        if (fgets(line, sizeof(line), fp) != (char *) NULL) {
305                mask[0] = '-'; mask[1] = '\0';
306                dev[0] = '-'; dev[1] = '\0';
307
308                /* Read the ARP cache entries. */
309                for (; fgets(line, sizeof(line), fp);) {
310                        num = sscanf(line, "%s 0x%x 0x%x %100s %100s %100s\n",
311                                                 ip, &type, &flags, hwa, mask, dev);
312                        if (num < 4)
313                                break;
314
315//                      printf("ip: %s, hwa: %s, dev: %s\n", ip, hwa, dev);
316
317                        if (!strcmp(dev, DEV_NAME)) {
318                                memset(mac, 0, 16);
319                                mac_idx = 0;
320                                /* exclude ':' from the 'hwa' */
321                                for (hwa_idx = 0; hwa_idx <= 17; hwa_idx += 3, mac_idx += 2) {
322                                        memcpy(mac + mac_idx, hwa + hwa_idx, 2);
323                                }
324//                              printf("mac: %s\n", mac);
325                                /* Trigger WOL for the mac */
326				send_wol(bcast_addr, mac);
327//                              sprintf(cmd, "/usr/sbin/wol -b %s -m %s", bcast_addr, mac);
328//                              printf("cmd: %s\n", cmd);
329//                                system(cmd);
330                        }
331                }
332        }
333
334        fclose(fp);
335
336        return 0;
337}
338