2 * ap_search.c from Access Point SNMP Utils for Linux
4 * Copyright (c) 2002 Roman Festchook <roma at polesye dot net>
5 * Copyright (c) 2003 Jan Rafaj <jr-aputils at cedric dot unob dot cz>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License Version 2 from
9 * June 1991 as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <sys/types.h>
27 #include <sys/ioctl.h>
30 #include <sys/sockio.h>
34 #include <sys/socket.h>
37 #include <sys/socket.h>
44 #if defined (__GLIBC__)
48 #define SEARCH_COMMUNITY _("Community name: ")
49 #define SEARCH_HEADER _(" NUM IP ADDRESS MIB TYPE FW TYPE (VERSION) AP NAME")
50 #define MAX_APS LAST_ROW-7
52 extern int atmel410_filter;
53 extern char *community;
62 void scan_local_segment (struct sockaddr_in *from, struct sockaddr_in *to,
65 extern WINDOW *main_sub;
66 extern char *ap_types[];
67 unsigned char message[1024], *start;
68 /* unsigned char name_fw[256], *name_p, *fwver_p; */
69 extern int atmel410_filter; /* if called from ap-gl utility */
73 * operAccessPointName OIDs used to detect AP MIB type [in order
74 * of appearance in the 'for' loop below: ATMEL410, NWN, ATMEL12350]
77 char operAccessPointName[3][12] = {
78 {0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x01, 0x0A, 0x00},
79 {0x2B, 0x06, 0x01, 0x02, 0x01, 0x01, 0x05, 0x00},
80 {0x2B, 0x06, 0x01, 0x04, 0x01, 0xE0, 0x3E, 0x01, 0x02, 0x01, 0x0A, 0x00}
84 * note: we'll rather use sysDescr OID for probing, becouse the devices
85 * wont rely on correct community for this query
87 char sysDescr[3][12] = {
88 {0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x01, 0x01, 0x00},
89 {0x2B, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00},
90 {0x2B, 0x06, 0x01, 0x04, 0x01, 0xE0, 0x3E, 0x01, 0x01, 0x01, 0x01, 0x00}
93 int scd_ap_type, last_searched_type = ATMEL12350;
94 int c, s2, errno, len, client_len = SIZE;
95 struct in_addr to_addr_reserv;
99 struct ip_mreq mult = { {0}, {INADDR_ANY} };
102 struct timeval timeout, starttime, curtime;
106 print_help(_("Please wait while scanning, or press 'Q' to quit."));
108 if ((s2 = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
109 print_helperr(ERR_SOCKET);
114 if (bind(s2, (struct sockaddr *) from, SIZE) == -1) {
115 print_helperr(ERR_SOCKET);
120 if (setsockopt (s2, SOL_SOCKET, SO_BROADCAST, &opts,
121 sizeof(struct sopts)) == -1) {
122 print_helperr(_("Can't set broadcast option on socket. "
128 inet_aton("224.0.1.43", &mult.imr_multiaddr);
129 if (setsockopt(s2, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mult,
130 sizeof(struct ip_mreq)) == -1) {
131 print_helperr(_("Can't set multicast membership on socket. "
137 mvwaddstr(main_sub, LAST_ROW - 5, 1,
138 _("Scanning via network interface:"));
139 sprintf(message, _(" Index: %i"), f_ifctr);
140 mvwaddstr(main_sub, LAST_ROW - 4, 1, message);
141 sprintf(message, _(" Name: %s"), ifname);
142 mvwaddstr(main_sub, LAST_ROW - 3, 1, message);
143 sprintf(message, _(" IP: %s"), inet_ntoa(from->sin_addr));
144 mvwaddstr(main_sub, LAST_ROW - 2, 1, message);
146 if(atmel410_filter) {
147 last_searched_type = ATMEL410;
150 for (scd_ap_type = ATMEL410; scd_ap_type <= last_searched_type; scd_ap_type++) {
151 clear_main_new(LAST_ROW - 1, LAST_ROW);
152 sprintf(message, _("Scanning for AP with MIB type: %s"),
153 ap_types[scd_ap_type]);
154 mvwaddstr(main_sub, LAST_ROW - 1, 1, message);
157 /* varbinds[0].oid = operAccessPointName[scd_ap_type];*/
158 varbinds[0].oid = sysDescr[scd_ap_type];
159 varbinds[0].len_oid = (scd_ap_type == NWN ?
160 /* 8 : sizeof(operAccessPointName[scd_ap_type]));*/
161 8 : sizeof(sysDescr[scd_ap_type]));
162 varbinds[0].len_val = 0;
163 varbinds[0].type = NULL_VALUE;
164 len = ber(message, varbinds, 1, GET);
166 if (scd_ap_type == NWN) {
167 to_addr_reserv = to->sin_addr;
168 to->sin_addr = mult.imr_multiaddr;
172 if (sendto(s2, message, len, 0, (struct sockaddr *) to, SIZE) == -1) {
173 sprintf(message, _("Failure in sendto(): %s. Press any key."),
175 print_helperr(message);
182 gettimeofday(&starttime, &tz);
191 * Compute time difference. Note that for portability reasons,
192 * we may not rely on select() below setting up timeout to
193 * remaining time upon its return, although this works on Linux.
195 gettimeofday(&curtime, &tz);
196 c = (curtime.tv_sec * 1000000 + curtime.tv_usec) -
197 (starttime.tv_sec * 1000000 + starttime.tv_usec);
198 if (c < (2 * 1000000 + 0)) {
199 c = (2 * 1000000 + 0) - c;
200 timeout.tv_sec = c / 1000000;
201 timeout.tv_usec = c - timeout.tv_sec * 1000000;
203 /* Return if nothing has been received after timeout secs. */
206 c = select(s2 + 1, &rds, NULL, NULL, &timeout);
216 /* Key pressed. If it is 'Q', return. */
217 if (FD_ISSET(0, &rds)) {
219 if (c == 'q' || c == 'Q') {
225 /* If data are available for reading on s2, try to read them now. */
226 if (FD_ISSET(s2, &rds))
227 if ((len = recvfrom(s2, message, 512, 0,
228 (struct sockaddr *) from, &client_len)) == -1)
232 if (*start != ASN_HEADER)
235 start += (start[1] & 0x80) ? (start[1] & 0x7F) + 2 : 2;
236 start += *(start + 4) + 5;
238 if (*start != RESPONSE)
241 start += (start[1] & 0x80) ? (start[1] & 0x7F) + 2 : 2;
243 if (*(start + 5) || *(start + 9) != ASN_HEADER)
246 start += (start[10] & 0x80) ? (start[10] & 0x7F) + 11 : 11;
248 if (*(start) != ASN_HEADER)
251 start += (start[1] & 0x80) ? (start[1] & 0x7F) + 2 : 2;
252 start += *(start + 1) + 2;
254 if (start[1] & 0x80) {
255 varbinds[0].len_val = start[2];
256 start += (start[1] & 0x7F) + 2;
258 varbinds[0].len_val = start[1];
263 * Dupe check. There are 2 reasons why to do this:
264 * 1. If interface-based IP aliases are used (f.e. on Linux),
265 * it may well happen that their broadcast addresses may
266 * inadvertedly overlap each other, so the same AP IPs would
267 * answer twice (for a specific interface and its alias
269 * 2. ATMEL410 devices are capable to answer both queries with
270 * 410 as well as 12350 IDs, while ATMEL12350 device only
271 * answers queries with 12350 ID. Hence, we need to check
272 * for duplicate responses from ATMEL410 devices, when
273 * ATMEL410 check has already performed and ATMEL12350 one
275 * Note, that ATMEL410 devices may, under certain circumstances,
276 * fail to pass the ATMEL410 check, and can be accidentally
277 * marked as an ATMEL12350 device. This is a known bug and
278 * should be eventually solved in any of upcomming releases (TODO).
281 int dupcnt = 0, j = i - 1;
284 if (from->sin_addr.s_addr == fapsa[j].ip.s_addr)
292 /* new AP (unique IP/APtype pair) found */
294 fapsa = realloc(fapsa, (i + 1) * sizeof(struct faps));
296 fapsa[i].ip = from->sin_addr;
297 fapsa[i].type = scd_ap_type;
299 for (len = 0; len < varbinds[0].len_val && start[len]; len++);
300 start[len + 1] = '\0';
303 * Returned sysDescr string is (hopefully!) always of format:
304 * string1 (Ver. string2) string3
306 * string1 = "802.11 X "
307 * where: "X" identifies firmware type (AP, APP, WB, WA, etc)
308 * unfortunately this is not deterministic across
309 * manufacturers (each one uses different naming scheme)
310 * string2 = firmware version
311 * string3 = device name, as defined with operAccessPointName
313 name_p = strchr(start, ')');
316 fwver_p = strstr(start, "(Ver. ");
317 fwver_p += strlen("(Ver. ");
318 sprintf(name_fw, "%s (%s)", name_p, fwver_p);
320 if (strlen(name_fw) > (size_t)(COLS - MCOLS - 37)) {
321 name_fw[COLS - MCOLS - 38] = '>';
322 name_fw[COLS - MCOLS - 37] = '\0';
326 if (strlen(start) > (size_t)(COLS - MCOLS - 37)) {
327 /* truncate (limit to scr. size) */
328 start[COLS - MCOLS - 38] = '>';
329 start[COLS - MCOLS - 37] = '\0';
332 sprintf(message, " %3i %-15s %-10s %s", i,
333 inet_ntoa(fapsa[i].ip), ap_types[fapsa[i].type], start);
336 mvwaddstr(main_sub, i, 0, message);
339 /* Bail out if the number of found devices exceeds sane limit. */
344 if (scd_ap_type == NWN)
345 to->sin_addr = to_addr_reserv;
356 extern WINDOW *main_sub;
358 unsigned int ilen = 256;
359 struct sockaddr_in from, to;
362 char *ifbuf_ptr = NULL, *ifrec_ptr;
367 print_help(_("Please enter SNMP community name that will be used for AP "
369 mvwaddstr(main_sub, 0, 2, SEARCH_COMMUNITY);
371 get_pass(buf, 0, 2 + strlen(SEARCH_COMMUNITY), sizeof(buf));
372 old_community = community;
373 community = malloc(strlen(buf) + 1);
374 strncpy(community, buf, strlen(buf) + 1);
378 print_top(NULL, _("Access Points Search"));
379 mvwaddstr(main_sub, 0, 0, SEARCH_HEADER);
382 if ((s1 = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
383 print_helperr(ERR_SOCKET);
391 * Find all IPs of locally available IPv4 interfaces and corresponding
392 * broadcast and/or point-to-point addresses.
395 if (!(ifbuf_ptr = realloc(ifbuf_ptr, ilen))) {
396 print_helperr(_("realloc() error."));
400 ifc.ifc_buf = ifbuf_ptr;
402 if (ioctl(s1, SIOCGIFCONF, &ifc) >= 0)
403 /* Stupid condition thx to BSD and 2 SIOCGIFCONF implementations */
404 if (ifc.ifc_len + sizeof(struct ifreq) + 64 < ilen)
408 print_helperr(_("Network interface discovery error."));
411 ilen += 100 + (ilen >> 2);
413 ifrec_ptr = ifbuf_ptr;
414 while (ifrec_ptr < ifbuf_ptr + ifc.ifc_len) {
415 memset(&from.sin_addr, 0, sizeof(struct in_addr));
416 memset(&to.sin_addr, 0, sizeof(struct in_addr));
417 ifr = (struct ifreq *) ifrec_ptr;
419 ilen = sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len;
420 if (ilen < sizeof(*ifr))
423 if (ifr->ifr_addr.sa_family == AF_INET)
424 if (ioctl(s1, SIOCGIFFLAGS, ifrec_ptr) == 0)
425 if (ifr->ifr_flags & IFF_UP)
426 if (!(ifr->ifr_flags & IFF_LOOPBACK)) {
428 ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
429 if (ifr->ifr_flags & IFF_POINTOPOINT) {
430 ioctl(s1, SIOCGIFDSTADDR, ifrec_ptr);
432 ((struct sockaddr_in *)&ifr->ifr_dstaddr)->sin_addr;
434 ioctl(s1, SIOCGIFBRDADDR, ifrec_ptr);
436 ((struct sockaddr_in *)&ifr->ifr_broadaddr)->sin_addr;
442 if (ioctl(s1, SIOCGIFFLAGS, ifrec_ptr) == 0)
443 if (ifr->ifr_flags & IFF_UP)
444 if (!(ifr->ifr_flags & IFF_LOOPBACK)) {
445 if (ifr->ifr_flags & IFF_POINTOPOINT) {
446 ioctl(s1, SIOCGIFDSTADDR, ifrec_ptr);
448 ((struct sockaddr_in *)&ifr->ifr_dstaddr)->sin_addr;
450 ioctl(s1, SIOCGIFBRDADDR, ifrec_ptr);
452 ((struct sockaddr_in *)&ifr->ifr_broadaddr)->sin_addr;
454 if (ioctl(s1, SIOCGIFADDR, ifrec_ptr) == 0)
455 if (ifr->ifr_addr.sa_family == AF_INET)
457 ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
460 if (from.sin_addr.s_addr && to.sin_addr.s_addr) {
462 from.sin_family = to.sin_family = AF_INET;
463 from.sin_port = INADDR_ANY;
464 to.sin_port = htons(161);
465 scan_local_segment(&from, &to, ifr->ifr_name);
466 clear_main_new(LAST_ROW - 5, LAST_ROW);
469 /* No need to process next interfaces if the table is already full, */
473 /* or if someone pressed 'Q'. */
483 print_helperr(_("No local network interfaces found. Press any key."));
485 print_help(_("No directly reachable Access Points found. "
489 mvwaddstr(main_sub, LAST_ROW - 1, 1,
490 _("Single-screen maximum number of APs found."));
493 print_help(_("# - connect to AP; Q - quit"));
495 switch (ac = getch()) {
509 if (ac - '0' > (i - 1))
512 print_top(NULL, NULL);
514 /* free(old_community); */
515 connect_options(fapsa[ac - '0'].ip.s_addr,
516 fapsa[ac - '0'].type + 1);
532 community = old_community;
535 print_top(NULL, NULL);