]>
Commit | Line | Data |
---|---|---|
0d6cc79d SF |
1 | /* |
2 | * wioscan | |
3 | * | |
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. | |
7 | */ | |
8 | ||
9 | #define _GNU_SOURCE | |
10 | #include <sys/types.h> | |
11 | #include <unistd.h> | |
12 | #include <string.h> | |
13 | #include <stdlib.h> | |
14 | #include <stdio.h> | |
15 | #include <poll.h> | |
16 | #include <errno.h> | |
17 | #include <err.h> | |
18 | #include <sys/ioctl.h> | |
19 | #include <sys/socket.h> | |
20 | #include <netpacket/packet.h> | |
21 | #include <net/ethernet.h> | |
22 | #include <net/if.h> | |
23 | #include <net/if_arp.h> | |
24 | #include <netinet/ether.h> | |
25 | #include <arpa/inet.h> | |
26 | #include <stdint.h> | |
27 | #include <fcntl.h> | |
28 | #include <sys/stat.h> | |
29 | #include <sys/mman.h> | |
30 | ||
31 | #define _STR(S) #S | |
32 | #define STR(S) _STR(S) | |
33 | ||
34 | #define ARP htons(ETHERTYPE_ARP) | |
35 | #define IP htons(ETHERTYPE_IP) | |
36 | ||
37 | typedef uint8_t u8; | |
38 | typedef uint16_t u16; | |
39 | typedef uint32_t u32; | |
40 | ||
41 | #include "list.h" | |
42 | #define elemof(T) (sizeof T/sizeof*T) | |
43 | #define endof(T) (T+elemof(T)) | |
44 | #ifndef offsetof | |
45 | #define offsetof(T,M) ((int)(long)&((T*)0)->M) | |
46 | #endif | |
47 | ||
48 | #define HWMAX 8 | |
49 | ||
50 | union addr { | |
51 | struct sockaddr sa; | |
52 | struct sockaddr_in in; | |
53 | struct sockaddr_ll ll; | |
54 | }; | |
55 | ||
56 | int sock; /* packet socket */ | |
57 | union addr bcast; | |
58 | ||
59 | struct opts { | |
60 | unsigned sort:1; | |
61 | unsigned noown:1; | |
62 | unsigned noethn:1; | |
63 | unsigned proui:1; | |
64 | unsigned isrange:1; | |
65 | unsigned passive:1; | |
66 | unsigned nsend; | |
67 | unsigned wait; | |
68 | } opts = {nsend:8, wait:250}; | |
69 | ||
70 | void print_oui(int sp, u8 a[6]); | |
71 | ||
72 | struct he; | |
73 | void print_he(struct he *he); | |
74 | ||
75 | struct hwaddr { | |
76 | u8 len, addr[HWMAX]; | |
77 | }; | |
78 | ||
79 | static inline hw_eq(struct hwaddr *h, int hl, u8 *ha) | |
80 | { | |
81 | return h->len == hl && memcmp(h->addr, ha, hl) == 0; | |
82 | } | |
83 | ||
84 | static inline void hw_set(struct hwaddr *h, int hl, u8 *ha) | |
85 | { | |
86 | memcpy(h->addr, ha, (h->len = hl)); | |
87 | } | |
88 | ||
89 | struct ifinfo { | |
90 | int index; | |
91 | char *name; | |
92 | u32 ip, net, mask, bcast; | |
93 | u16 hw_type; | |
94 | struct hwaddr hw; | |
95 | } ifinfo; | |
96 | ||
97 | static inline u32 ip_from_sa(struct sockaddr *sa) | |
98 | { | |
99 | return ntohl(((struct sockaddr_in*)sa)->sin_addr.s_addr); | |
100 | } | |
101 | ||
102 | /* TABLE */ | |
103 | ||
104 | struct list hashtbl[128]; | |
105 | struct he { | |
106 | struct list hash; | |
107 | u32 ip; | |
108 | struct hwaddr hw; | |
109 | struct hwaddr from; | |
110 | }; | |
111 | ||
112 | static void init_hash() __attribute__((constructor)); | |
113 | static void init_hash() | |
114 | { | |
115 | int i; | |
116 | for(i=0;i<elemof(hashtbl);i++) list_init(&hashtbl[i]); | |
117 | } | |
118 | ||
119 | int he_for(u32 ip, struct he **ret, int alloc) | |
120 | { | |
121 | struct list *h, *l; | |
122 | struct he *he; | |
123 | int v = 1; | |
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); | |
127 | if(he->ip == ip) | |
128 | goto ret; | |
129 | if(he->ip > ip) | |
130 | break; | |
131 | } | |
132 | v = 0; | |
133 | if(alloc) { | |
134 | he = (struct he*)malloc(sizeof *he); | |
135 | he->ip = ip; | |
136 | list_add(l->prev, &he->hash); | |
137 | ret: | |
138 | if(ret) *ret = he; | |
139 | } | |
140 | return v; | |
141 | } | |
142 | ||
143 | /* INTERFACE */ | |
144 | ||
145 | static int net; | |
146 | ||
147 | static void my__ioctl(int i, struct ifreq *r, char *t) | |
148 | { | |
149 | if(ioctl(net, i, r) < 0) | |
150 | err(1, "ioctl(%s,%s)", t, r->ifr_name); | |
151 | } | |
152 | #define my_ioctl(I,R) my__ioctl(I,R,#I) | |
153 | ||
154 | void fill_ifinfo(char *name) | |
155 | { | |
156 | struct ifreq ir; | |
157 | int flags; | |
158 | ||
159 | ifinfo.index = if_nametoindex(name); | |
160 | if(!ifinfo.index) errx(1, "No such interface: %s", name); | |
161 | ifinfo.name = name; | |
162 | ||
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 */ | |
176 | } else { | |
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; | |
182 | } | |
183 | close(net); | |
184 | } | |
185 | ||
186 | static inline char *str_ip(u32 ip) | |
187 | { | |
188 | struct in_addr n; | |
189 | n.s_addr = htonl(ip); | |
190 | return inet_ntoa(n); | |
191 | } | |
192 | ||
193 | char *str_hw(u8 *a, int l) | |
194 | { | |
195 | static char buf[3*HWMAX]; | |
196 | char *d = buf; | |
197 | if(!l) return "*"; | |
198 | if(l>HWMAX) l=HWMAX; | |
199 | for(;;) { | |
200 | d += sprintf(d, "%02X", *a++); | |
201 | if(--l <= 0) break; | |
202 | *d++ = ':'; | |
203 | } | |
204 | *d = 0; | |
205 | return buf; | |
206 | } | |
207 | ||
208 | static char *str_addr(union addr *addr) | |
209 | { | |
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 "???"; | |
214 | } | |
215 | } | |
216 | ||
217 | static inline void setup_socket() | |
218 | { | |
219 | union addr addr; | |
220 | socklen_t l; | |
221 | ||
222 | sock = socket(PF_PACKET, SOCK_DGRAM, 0); | |
223 | if(sock < 0) err(1, "socket(PF_PACKET)"); | |
224 | ||
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; | |
229 | ||
230 | if(bind(sock, &addr.sa, sizeof addr.ll)<0) | |
231 | err(1, "bind"); | |
232 | l = sizeof addr.ll; | |
233 | if(getsockname(sock, &addr.sa, &l)<0) | |
234 | err(1, "getsockname"); | |
235 | ||
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; | |
241 | } | |
242 | ||
243 | /* SCAN */ | |
244 | ||
245 | struct arppkt { | |
246 | u16 hrd, pro; | |
247 | u8 hln, pln; | |
248 | u16 op; | |
249 | u8 a[2*HWMAX+2*4]; | |
250 | /* u8 sha[6]; | |
251 | u8 sip[4]; | |
252 | u8 tha[6]; | |
253 | u8 tip[4];*/ | |
254 | }; | |
255 | ||
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));} | |
260 | ||
261 | #if 0 | |
262 | void print_arp(struct arppkt *arp) | |
263 | { | |
264 | u8 *p = arp->a; | |
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))); | |
271 | } | |
272 | #endif | |
273 | ||
274 | static struct scan { | |
275 | u32 ip, start, end; | |
276 | } scan; | |
277 | ||
278 | #define IN_RANGE(I) ((I) >= scan.start && (I) <= (u32)(scan.end-1)) | |
279 | ||
280 | int sendscan() | |
281 | { | |
282 | struct arppkt arp; | |
283 | int ns; | |
284 | u8 *p; | |
285 | ||
286 | arp.hrd = htons(ifinfo.hw_type); | |
287 | arp.pro = IP; | |
288 | arp.hln = ifinfo.hw.len; | |
289 | arp.pln = 4; | |
290 | arp.op = htons(1); | |
291 | p = arp.a; | |
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; | |
295 | ||
296 | ns = 0; | |
297 | while(scan.ip != scan.end) { | |
298 | int v; | |
299 | if(scan.ip == ifinfo.bcast || he_for(scan.ip, 0, 0)) { | |
300 | scan.ip++; | |
301 | continue; | |
302 | } | |
303 | *(u32*)p = htonl(scan.ip); | |
304 | v = sendto(sock, &arp, p+4-(u8*)&arp, 0, &bcast.sa, sizeof bcast.ll); | |
305 | if(v<0) { | |
306 | if(errno != ENOBUFS || opts.nsend <= 1) | |
307 | err(1, "send(%s)", str_addr(&bcast)); | |
308 | opts.nsend--; | |
309 | return -1; | |
310 | } | |
311 | scan.ip++; | |
312 | if(++ns >= opts.nsend) break; | |
313 | } | |
314 | return ns; | |
315 | } | |
316 | ||
317 | void compare_resp(struct he *he, union addr *src, int hln, u8 *sha) | |
318 | { | |
319 | if(hw_eq(&he->hw, hln, sha) | |
320 | && hw_eq(&he->from, src->ll.sll_halen, src->ll.sll_addr)) | |
321 | return; | |
322 | ||
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)); | |
332 | } | |
333 | ||
334 | int arp_recv(struct arppkt *pkt, union addr *src) | |
335 | { | |
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)) | |
340 | return 0; | |
341 | if(pkt->pro != IP) | |
342 | return 0; | |
343 | if(pkt->hrd != htons(ifinfo.hw_type) || pkt->hln != ifinfo.hw.len) | |
344 | return 0; | |
345 | if(v < offsetof(struct arppkt, a) + 2*pkt->hln + 2*4) | |
346 | return 0; | |
347 | return 1; | |
348 | } | |
349 | ||
350 | void receive() | |
351 | { | |
352 | union addr addr; | |
353 | struct arppkt arp; | |
354 | struct he *he; | |
355 | u32 ip; | |
356 | ||
357 | if(!arp_recv(&arp, &addr)) | |
358 | return; | |
359 | if(arp.op != htons(2)) /* only responses */ | |
360 | return; | |
361 | ||
362 | ip = get_sip(&arp); | |
363 | ||
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; | |
369 | print_he(he); | |
370 | } else | |
371 | compare_resp(he, &addr, arp.hln, get_sha(&arp)); | |
372 | } | |
373 | ||
374 | /**/ | |
375 | ||
376 | void passive() | |
377 | { | |
378 | for(;;) { | |
379 | struct arppkt arp; | |
380 | union addr src; | |
381 | if(!arp_recv(&arp, &src)) | |
382 | continue; | |
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)) { | |
387 | case 1: | |
388 | printf("Q %s", str_ip(get_tip(&arp))); | |
389 | break; | |
390 | case 2: | |
391 | printf("A %s %s", str_hw(get_tha(&arp),arp.hln), | |
392 | str_ip(get_tip(&arp))); | |
393 | break; | |
394 | default: | |
395 | printf("%X", htons(arp.op)); | |
396 | } | |
397 | putchar('\n'); | |
398 | } | |
399 | } | |
400 | ||
401 | /**/ | |
402 | ||
403 | int waitsock(int n) | |
404 | { | |
405 | int v; | |
406 | struct pollfd pollfd; | |
407 | pollfd.fd = sock; | |
408 | pollfd.events = POLLIN; | |
409 | v = poll(&pollfd, 1, n); | |
410 | if(v < 0) { | |
411 | if(errno != EINTR) | |
412 | err(1, "poll"); | |
413 | v = 0; | |
414 | } | |
415 | return v; | |
416 | } | |
417 | ||
418 | void print_he(struct he *he) | |
419 | { | |
420 | int l, w; | |
421 | if(opts.noown && he->ip == ifinfo.ip) | |
422 | return; | |
423 | printf("%s,", str_hw(he->hw.addr, he->hw.len)); | |
424 | l = 15 - printf("%s", str_ip(he->ip)); | |
425 | w = 0; | |
426 | if(!opts.proui && !hw_eq(&he->from, he->hw.len, he->hw.addr)) | |
427 | w = 1, l = 1; | |
428 | ||
429 | if(opts.proui) | |
430 | print_oui(l, he->hw.addr); | |
431 | else if(!opts.noethn) { | |
432 | #if !defined __dietlibc_ && !defined __UCLIBC__ | |
433 | char nm[1024]; | |
434 | if(!ether_ntohost(nm, (struct ether_addr*)he->hw.addr)) | |
435 | printf("%*s%s", l, "", nm); | |
436 | #endif | |
437 | } | |
438 | if(w) | |
439 | printf(" from %s", str_hw(he->from.addr, he->from.len)); | |
440 | putchar('\n'); | |
441 | } | |
442 | ||
443 | static int parse_iprange(char *p) | |
444 | { | |
445 | char *e; | |
446 | u32 ip=0; | |
447 | int sh; | |
448 | ||
449 | for(sh = 24;; sh -= 8) { | |
450 | unsigned long v; | |
451 | ||
452 | v = strtoul(p, &e, 10); | |
453 | if(p == e || v > 255) | |
454 | return 0; | |
455 | ||
456 | ip |= v << sh; | |
457 | ||
458 | p = e + 1; | |
459 | if(*e == '/') { | |
460 | v = strtoul(p, &e, 10); | |
461 | if(p == e || *e || v > 32) | |
462 | return 0; | |
463 | if(v) { | |
464 | v = 32 - v; | |
465 | if(sh > v) | |
466 | return 0; | |
467 | mask: | |
468 | v = ~0 << v; | |
469 | } | |
470 | scan.start = ip & v; | |
471 | scan.end = scan.start - v; | |
472 | return 1; | |
473 | } | |
474 | ||
475 | if(!sh) break; | |
476 | ||
477 | v = sh; | |
478 | if(!*e) | |
479 | goto mask; | |
480 | ||
481 | if(*e != '.') | |
482 | return 0; | |
483 | ||
484 | if(!*p || *p == '*' && !p[1]) | |
485 | goto mask; | |
486 | } | |
487 | ||
488 | scan.start = ip; | |
489 | scan.end = ip + 1; | |
490 | ||
491 | if(*e == '-') { | |
492 | u32 end = 0, m = ~0; | |
493 | ||
494 | do { | |
495 | unsigned long v = strtoul(p, &e, 10); | |
496 | if(p == e || v > 255) | |
497 | return 0; | |
498 | p = e + 1; | |
499 | end = end<<8 | v; | |
500 | m <<= 8; | |
501 | } while(m && *e); | |
502 | ||
503 | if(*e) | |
504 | return 0; | |
505 | ||
506 | end |= ip & m; | |
507 | if(end < ip) | |
508 | return 0; | |
509 | ||
510 | scan.end = end + 1; | |
511 | return 1; | |
512 | } | |
513 | return *e == 0; | |
514 | } | |
515 | ||
516 | int main(int argc, char **argv) | |
517 | { | |
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; | |
526 | case 'h': | |
527 | printf( | |
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" | |
533 | #endif | |
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" | |
538 | ); | |
539 | return 0; | |
540 | case EOF: | |
541 | goto endopt; | |
542 | } | |
543 | endopt: | |
544 | ||
545 | { | |
546 | char *dev = "eth0"; | |
547 | if(optind<argc && (*argv[optind] < '0' || *argv[optind] > '9')) | |
548 | dev = argv[optind++]; | |
549 | fill_ifinfo(dev); | |
550 | setup_socket(); | |
551 | } | |
552 | ||
553 | if(optind>=argc) { | |
554 | scan.start = ifinfo.net; | |
555 | scan.end = (ifinfo.net | ~ifinfo.mask) + 1; | |
556 | } else { | |
557 | if(!parse_iprange(argv[optind])) | |
558 | errx(1, "%s: bad IP range", argv[optind]); | |
559 | opts.isrange = 1; | |
560 | } | |
561 | ||
562 | if(ifinfo.hw_type != ARPHRD_ETHER) | |
563 | opts.proui = 0, opts.noethn = 1; | |
564 | ||
565 | if(opts.passive) | |
566 | passive(); | |
567 | ||
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); | |
576 | ||
577 | if(IN_RANGE(ifinfo.ip)) { | |
578 | /* XXX we should add all our arpable addresses on the interface */ | |
579 | struct he *he; | |
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); | |
583 | if(!opts.sort) | |
584 | print_he(he); | |
585 | } | |
586 | ||
587 | /* 1st scan */ | |
588 | scan.ip = scan.start; | |
589 | while(sendscan()) { | |
590 | while(waitsock(10)) | |
591 | receive(); | |
592 | } | |
593 | /* 2nd scan */ | |
594 | scan.ip = scan.start; | |
595 | while(sendscan()) { | |
596 | while(waitsock(10)) | |
597 | receive(); | |
598 | } | |
599 | while(waitsock(opts.wait)) | |
600 | receive(); | |
601 | ||
602 | if(opts.sort) for(scan.ip = ifinfo.net; scan.ip != scan.end; scan.ip++) { | |
603 | struct he *he; | |
604 | if(he_for(scan.ip, &he, 0)) | |
605 | print_he(he); | |
606 | } | |
607 | return 0; | |
608 | } | |
609 | ||
610 | ||
611 | typedef uint8_t u8; | |
612 | static int fd = -2; | |
613 | static char *ouiptr, *ouiend; | |
614 | ||
615 | static void open_oui() | |
616 | { | |
617 | struct stat st; | |
618 | fd = open("oui", O_RDONLY); | |
619 | if(fd < 0) { | |
620 | fd = open(STR(OUI), O_RDONLY); | |
621 | if(fd < 0) goto err; | |
622 | } | |
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) { | |
627 | err_cl: | |
628 | close(fd); fd=-1; | |
629 | err: | |
630 | warnx("Can't open OUI database"); | |
631 | return; | |
632 | } | |
633 | #ifdef MADV_SEQUENTIAL | |
634 | madvise(ouiptr, st.st_size, MADV_SEQUENTIAL); | |
635 | #endif | |
636 | } | |
637 | ||
638 | void print_oui(int sp, u8 a[6]) | |
639 | { | |
640 | char addr[7], *p, *q; | |
641 | if(fd < 0) { | |
642 | if(fd == -2) | |
643 | open_oui(); | |
644 | if(fd < 0) | |
645 | return; | |
646 | } | |
647 | sprintf(addr, "%02X%02X%02X", a[0], a[1], a[2]); | |
648 | ||
649 | for(p=ouiptr; p<ouiend; p=q+1) { | |
650 | q = memchr(p, '\n', ouiend-p); | |
651 | if(!q) q=ouiend; | |
652 | if(q-p < 8 || memcmp(p, addr, 6)) | |
653 | continue; | |
654 | ||
655 | p += 7; | |
656 | print: | |
657 | printf("%*s%.*s", sp, "", (int)(q-p), p); | |
658 | return; | |
659 | } | |
660 | if(a[0]==0 && a[1]==0xFF) { | |
661 | p = "(generated)"; | |
662 | q = p + 11; | |
663 | goto print; | |
664 | } | |
665 | } |