]>
git.ipfire.org Git - ipfire-2.x.git/blob - src/misc-progs/wioscan.c
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * version 2 as published by the Free Software Foundation.
10 #include <sys/types.h>
18 #include <sys/ioctl.h>
19 #include <sys/socket.h>
20 #include <netpacket/packet.h>
21 #include <net/ethernet.h>
23 #include <net/if_arp.h>
24 #include <netinet/ether.h>
25 #include <arpa/inet.h>
32 #define STR(S) _STR(S)
34 #define ARP htons(ETHERTYPE_ARP)
35 #define IP htons(ETHERTYPE_IP)
42 #define elemof(T) (sizeof T/sizeof*T)
43 #define endof(T) (T+elemof(T))
45 #define offsetof(T,M) ((int)(long)&((T*)0)->M)
52 struct sockaddr_in in
;
53 struct sockaddr_ll ll
;
56 int sock
; /* packet socket */
68 } opts
= {nsend
:8, wait
:250};
70 void print_oui(int sp
, u8 a
[6]);
73 void print_he(struct he
*he
);
79 static inline hw_eq(struct hwaddr
*h
, int hl
, u8
*ha
)
81 return h
->len
== hl
&& memcmp(h
->addr
, ha
, hl
) == 0;
84 static inline void hw_set(struct hwaddr
*h
, int hl
, u8
*ha
)
86 memcpy(h
->addr
, ha
, (h
->len
= hl
));
92 u32 ip
, net
, mask
, bcast
;
97 static inline u32
ip_from_sa(struct sockaddr
*sa
)
99 return ntohl(((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
);
104 struct list hashtbl
[128];
112 static void init_hash() __attribute__((constructor
));
113 static void init_hash()
116 for(i
=0;i
<elemof(hashtbl
);i
++) list_init(&hashtbl
[i
]);
119 int he_for(u32 ip
, struct he
**ret
, int alloc
)
124 h
= &hashtbl
[ip
& elemof(hashtbl
)-1];
125 for(l
=h
->next
; l
!=h
; l
=l
->next
) {
126 he
= list_entry(l
, struct he
, hash
);
134 he
= (struct he
*)malloc(sizeof *he
);
136 list_add(l
->prev
, &he
->hash
);
147 static void my__ioctl(int i
, struct ifreq
*r
, char *t
)
149 if(ioctl(net
, i
, r
) < 0)
150 err(1, "ioctl(%s,%s)", t
, r
->ifr_name
);
152 #define my_ioctl(I,R) my__ioctl(I,R,#I)
154 void fill_ifinfo(char *name
)
159 ifinfo
.index
= if_nametoindex(name
);
160 if(!ifinfo
.index
) errx(1, "No such interface: %s", name
);
163 net
= socket(PF_INET
, SOCK_DGRAM
, 0);
164 if(net
<0) err(1, "socket(PF_INET)");
165 strcpy(ir
.ifr_name
, ifinfo
.name
);
166 my_ioctl(SIOCGIFFLAGS
, &ir
);
167 flags
= ir
.ifr_flags
;
168 if(flags
& IFF_NOARP
) errx(1, "%s: ARP not supported.", name
);
169 my_ioctl(SIOCGIFADDR
, &ir
);
170 ifinfo
.ip
= ip_from_sa(&ir
.ifr_addr
);
171 if(flags
& IFF_POINTOPOINT
) {
172 my_ioctl(SIOCGIFDSTADDR
, &ir
);
173 ifinfo
.net
= ip_from_sa(&ir
.ifr_dstaddr
);
174 ifinfo
.mask
= (u32
)~0;
175 ifinfo
.bcast
= 0; /* none */
177 my_ioctl(SIOCGIFNETMASK
, &ir
);
178 ifinfo
.mask
= ip_from_sa(&ir
.ifr_netmask
);
179 my_ioctl(SIOCGIFBRDADDR
, &ir
);
180 ifinfo
.bcast
= ip_from_sa(&ir
.ifr_broadaddr
);
181 ifinfo
.net
= ifinfo
.ip
& ifinfo
.mask
;
186 static inline char *str_ip(u32 ip
)
189 n
.s_addr
= htonl(ip
);
193 char *str_hw(u8
*a
, int l
)
195 static char buf
[3*HWMAX
];
200 d
+= sprintf(d
, "%02X", *a
++);
208 static char *str_addr(union addr
*addr
)
210 switch(addr
->sa
.sa_family
) {
211 case AF_INET
: return inet_ntoa(addr
->in
.sin_addr
);
212 case AF_PACKET
: return str_hw(addr
->ll
.sll_addr
, addr
->ll
.sll_halen
);
213 default: return "???";
217 static inline void setup_socket()
222 sock
= socket(PF_PACKET
, SOCK_DGRAM
, 0);
223 if(sock
< 0) err(1, "socket(PF_PACKET)");
225 memset(&addr
.ll
, 0, sizeof addr
.ll
);
226 addr
.sa
.sa_family
= AF_PACKET
;
227 addr
.ll
.sll_protocol
= ARP
;
228 addr
.ll
.sll_ifindex
= ifinfo
.index
;
230 if(bind(sock
, &addr
.sa
, sizeof addr
.ll
)<0)
233 if(getsockname(sock
, &addr
.sa
, &l
)<0)
234 err(1, "getsockname");
236 if(addr
.ll
.sll_halen
> HWMAX
)
237 errx(1, "hardware address too long (%d)", addr
.ll
.sll_halen
);
238 ifinfo
.hw
.len
= addr
.ll
.sll_halen
;
239 memcpy(ifinfo
.hw
.addr
, addr
.ll
.sll_addr
, sizeof ifinfo
.hw
.addr
);
240 ifinfo
.hw_type
= addr
.ll
.sll_hatype
;
256 static inline u8
*get_sha(struct arppkt
*pkt
) {return pkt
->a
;}
257 static inline u8
*get_tha(struct arppkt
*pkt
) {return pkt
->a
+pkt
->hln
+4;}
258 static inline u32
get_sip(struct arppkt
*pkt
) {return ntohl(*(u32
*)(pkt
->a
+pkt
->hln
));}
259 static inline u32
get_tip(struct arppkt
*pkt
) {return ntohl(*(u32
*)(pkt
->a
+2*pkt
->hln
+4));}
262 void print_arp(struct arppkt
*arp
)
265 printf("hrd:%04X pro:%04X ", ntohs(arp
->hrd
), ntohs(arp
->pro
));
266 printf("hln:%d pln:%d op:%d ", arp
->hln
, arp
->pln
, ntohs(arp
->op
));
267 printf("sha:%s ", str_hw(p
, arp
->hln
)); p
+=arp
->hln
;
268 printf("sip:%s ", str_ip(ntohl(*(u32
*)p
))); p
+=arp
->pln
;
269 printf("tha:%s ", str_hw(p
, arp
->hln
)); p
+=arp
->hln
;
270 printf("tip:%s\n", str_ip(ntohl(*(u32
*)p
)));
278 #define IN_RANGE(I) ((I) >= scan.start && (I) <= (u32)(scan.end-1))
286 arp
.hrd
= htons(ifinfo
.hw_type
);
288 arp
.hln
= ifinfo
.hw
.len
;
292 memcpy(p
, ifinfo
.hw
.addr
, ifinfo
.hw
.len
); p
+= ifinfo
.hw
.len
;
293 *(u32
*)p
= htonl(ifinfo
.ip
); p
+= 4;
294 memset(p
, 0, ifinfo
.hw
.len
); p
+= ifinfo
.hw
.len
;
297 while(scan
.ip
!= scan
.end
) {
299 if(scan
.ip
== ifinfo
.bcast
|| he_for(scan
.ip
, 0, 0)) {
303 *(u32
*)p
= htonl(scan
.ip
);
304 v
= sendto(sock
, &arp
, p
+4-(u8
*)&arp
, 0, &bcast
.sa
, sizeof bcast
.ll
);
306 if(errno
!= ENOBUFS
|| opts
.nsend
<= 1)
307 err(1, "send(%s)", str_addr(&bcast
));
312 if(++ns
>= opts
.nsend
) break;
317 void compare_resp(struct he
*he
, union addr
*src
, int hln
, u8
*sha
)
319 if(hw_eq(&he
->hw
, hln
, sha
)
320 && hw_eq(&he
->from
, src
->ll
.sll_halen
, src
->ll
.sll_addr
))
323 fprintf(stderr
, "%s: ", str_ip(he
->ip
));
324 fprintf(stderr
, "inconsistency: %s", str_hw(sha
, hln
));
325 if(src
->ll
.sll_halen
!= hln
|| memcmp(src
->ll
.sll_addr
, sha
, hln
))
326 fprintf(stderr
, " from %s",
327 str_hw(src
->ll
.sll_addr
, src
->ll
.sll_halen
));
328 fprintf(stderr
, ", was %s\n", str_hw(he
->hw
.addr
, he
->hw
.len
));
329 if(!hw_eq(&he
->hw
, he
->from
.len
, he
->from
.addr
))
330 fprintf(stderr
, " from %s",
331 str_hw(he
->from
.addr
, he
->from
.len
));
334 int arp_recv(struct arppkt
*pkt
, union addr
*src
)
336 socklen_t l
= sizeof *src
;
337 int v
= recvfrom(sock
, pkt
, sizeof *pkt
, 0, &src
->sa
, &l
);
338 if(v
< 0) err(1, "recvfrom");
339 if(v
< offsetof(struct arppkt
, a
))
343 if(pkt
->hrd
!= htons(ifinfo
.hw_type
) || pkt
->hln
!= ifinfo
.hw
.len
)
345 if(v
< offsetof(struct arppkt
, a
) + 2*pkt
->hln
+ 2*4)
357 if(!arp_recv(&arp
, &addr
))
359 if(arp
.op
!= htons(2)) /* only responses */
364 if(!he_for(ip
, &he
, 1)) {
365 hw_set(&he
->hw
, arp
.hln
, get_sha(&arp
));
366 hw_set(&he
->from
, addr
.ll
.sll_halen
, addr
.ll
.sll_addr
);
367 if(opts
.sort
) return;
368 if(opts
.isrange
&& !IN_RANGE(ip
)) return;
371 compare_resp(he
, &addr
, arp
.hln
, get_sha(&arp
));
381 if(!arp_recv(&arp
, &src
))
383 printf("%s: ", str_addr(&src
));
384 printf("%s %-15s ", str_hw(get_sha(&arp
),arp
.hln
),
385 str_ip(get_sip(&arp
)));
386 switch(htons(arp
.op
)) {
388 printf("Q %s", str_ip(get_tip(&arp
)));
391 printf("A %s %s", str_hw(get_tha(&arp
),arp
.hln
),
392 str_ip(get_tip(&arp
)));
395 printf("%X", htons(arp
.op
));
406 struct pollfd pollfd
;
408 pollfd
.events
= POLLIN
;
409 v
= poll(&pollfd
, 1, n
);
418 void print_he(struct he
*he
)
421 if(opts
.noown
&& he
->ip
== ifinfo
.ip
)
423 printf("%s,", str_hw(he
->hw
.addr
, he
->hw
.len
));
424 l
= 15 - printf("%s", str_ip(he
->ip
));
426 if(!opts
.proui
&& !hw_eq(&he
->from
, he
->hw
.len
, he
->hw
.addr
))
430 print_oui(l
, he
->hw
.addr
);
431 else if(!opts
.noethn
) {
432 #if !defined __dietlibc_ && !defined __UCLIBC__
434 if(!ether_ntohost(nm
, (struct ether_addr
*)he
->hw
.addr
))
435 printf("%*s%s", l
, "", nm
);
439 printf(" from %s", str_hw(he
->from
.addr
, he
->from
.len
));
443 static int parse_iprange(char *p
)
449 for(sh
= 24;; sh
-= 8) {
452 v
= strtoul(p
, &e
, 10);
453 if(p
== e
|| v
> 255)
460 v
= strtoul(p
, &e
, 10);
461 if(p
== e
|| *e
|| v
> 32)
471 scan
.end
= scan
.start
- v
;
484 if(!*p
|| *p
== '*' && !p
[1])
495 unsigned long v
= strtoul(p
, &e
, 10);
496 if(p
== e
|| v
> 255)
516 int main(int argc
, char **argv
)
518 for(;;) switch(getopt(argc
, argv
, "fsaepwlh")) {
519 case 'f': opts
.sort
=0; break;
520 case 's': opts
.sort
=1; break;
521 case 'a': opts
.noown
=1; break;
522 case 'e': opts
.noethn
=1; break;
523 case 'p': opts
.proui
=1; break;
524 case 'w': opts
.nsend
=2; opts
.wait
=1000; break;
525 case 'l': opts
.passive
=1; break;
528 "wioscan [-faep] [interface] [ip-range]\n"
529 "\t-s sort responses\n"
530 "\t-a do not list interface's own address\n"
531 #if !defined __dietlibc_ && !defined __UCLIBC__
532 "\t-e do not include info from /etc/ethers\n"
534 "\t-p print vendor names\n"
535 "\t-w slow operation\n"
536 "\t-l listen only (not promiscuous)\n"
537 "ip-range: ip ip/bits ip-ip\n"
547 if(optind
<argc
&& (*argv
[optind
] < '0' || *argv
[optind
] > '9'))
548 dev
= argv
[optind
++];
554 scan
.start
= ifinfo
.net
;
555 scan
.end
= (ifinfo
.net
| ~ifinfo
.mask
) + 1;
557 if(!parse_iprange(argv
[optind
]))
558 errx(1, "%s: bad IP range", argv
[optind
]);
562 if(ifinfo
.hw_type
!= ARPHRD_ETHER
)
563 opts
.proui
= 0, opts
.noethn
= 1;
568 /* hw broadcast address is Linux's secret, this works with Ethernet */
569 bcast
.sa
.sa_family
= AF_PACKET
;
570 bcast
.ll
.sll_protocol
= ARP
;
571 bcast
.ll
.sll_ifindex
= ifinfo
.index
;
572 bcast
.ll
.sll_hatype
= ifinfo
.hw_type
;
573 bcast
.ll
.sll_pkttype
= PACKET_BROADCAST
; /* unused :-( */
574 bcast
.ll
.sll_halen
= ifinfo
.hw
.len
;
575 memset(bcast
.ll
.sll_addr
, 0xFF, ifinfo
.hw
.len
);
577 if(IN_RANGE(ifinfo
.ip
)) {
578 /* XXX we should add all our arpable addresses on the interface */
580 he_for(ifinfo
.ip
, &he
, 1);
581 hw_set(&he
->hw
, ifinfo
.hw
.len
, ifinfo
.hw
.addr
);
582 hw_set(&he
->from
, ifinfo
.hw
.len
, ifinfo
.hw
.addr
);
588 scan
.ip
= scan
.start
;
594 scan
.ip
= scan
.start
;
599 while(waitsock(opts
.wait
))
602 if(opts
.sort
) for(scan
.ip
= ifinfo
.net
; scan
.ip
!= scan
.end
; scan
.ip
++) {
604 if(he_for(scan
.ip
, &he
, 0))
613 static char *ouiptr
, *ouiend
;
615 static void open_oui()
618 fd
= open("oui", O_RDONLY
);
620 fd
= open(STR(OUI
), O_RDONLY
);
623 if(fstat(fd
, &st
) < 0 || st
.st_size
== 0) goto err_cl
;
624 ouiptr
= mmap(0, st
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
625 ouiend
= ouiptr
+ st
.st_size
;
626 if(ouiptr
== MAP_FAILED
) {
630 warnx("Can't open OUI database");
633 #ifdef MADV_SEQUENTIAL
634 madvise(ouiptr
, st
.st_size
, MADV_SEQUENTIAL
);
638 void print_oui(int sp
, u8 a
[6])
640 char addr
[7], *p
, *q
;
647 sprintf(addr
, "%02X%02X%02X", a
[0], a
[1], a
[2]);
649 for(p
=ouiptr
; p
<ouiend
; p
=q
+1) {
650 q
= memchr(p
, '\n', ouiend
-p
);
652 if(q
-p
< 8 || memcmp(p
, addr
, 6))
657 printf("%*s%.*s", sp
, "", (int)(q
-p
), p
);
660 if(a
[0]==0 && a
[1]==0xFF) {