2 ** Copyright (C) 2010 Kacper Wysocki <kacper.wysocki@redpill-linpro.com>
4 ** This program is free software; you can redistribute it and/or modify
5 ** it under the terms of the GNU General Public License Version 2 as
6 ** published by the Free Software Foundation. You may not use, modify or
7 ** distribute this program under any other version of the GNU General
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.
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.
30 * This file eats a MAC-address and returns a MAC-vendor match.
33 * primary file format in use is Wireshark format:
34 * MA:CC:AD:RE:SS[/36] manufacturer # optional comment
40 * Returns a fingerprint match and the fingerprint it matched
46 /* alloc_mac return a newly allocated copy of *e */
47 static mac_entry
*alloc_mac(mac_entry
*e
)
49 mac_entry
*n
= calloc(1, sizeof(mac_entry
));
54 void print_mac(const uint8_t *mac
){
56 for(i
= 0; i
< 6; i
++){
57 printf("%02hhx:", mac
[i
]);
61 void print_mac_entry(mac_entry
*e
){
67 printf("/%d", e
->mask
);
69 printf(" %s ", e
->vendor
);
71 printf("# %s", e
->comment
);
78 void print_mac_entries(mac_entry
*e
) {
82 print_mac_entries(e
->next
);
85 void dump_macs(mac_entry
**sig
, int len
)
88 for (i
= 0; i
< len
; i
++) {
91 print_mac_entries(sig
[i
]);
96 /* match mac with vendor list
97 * most specific match first.
99 * how does this grab ya:
100 * aa:bb:cc:dd:ee:ff matches
102 * .. by searching upward from
105 mac_entry
*match_mac(mac_entry
**db
, const uint8_t mac
[], uint8_t mask
)
107 uint32_t index
= hash_mac(mac
, mask
/ 8);
108 uint8_t r
,i
,ditch
= 0;
112 printf("/%d match\n", mask);
114 if(db
== NULL
) return NULL
; // database not loaded!
118 return NULL
; // oopsy we run out of matches.
120 // hash will only get us so far, we must search the rest of the way
121 mac_entry
*match
= db
[index
];
126 //print_mac_entry(match);
128 /* XXX: we could definitely take advantage of
129 * 64bit or SIMD instructions to speed this up, here and in the hash func
131 for(i
= 0; i
< match
->mask
/ 8; i
++){
132 if (mac
[i
] != match
->o
[i
])
133 i
= 6; // 7!? flag and break
136 continue; // we flagged and broke
138 // do we have a winner?
141 // there is more to match, maybe less than a nibble
142 //dlog("nibble match! /%d :%d, %02x=?%02x\n", match->mask, r, mac[i] >> match->mask, match->o[i] >> match->mask);
143 if(! ((mac
[i
] ^ match
->o
[i
]) & (0xFF << (8 - r
))) )
144 return match
; // if the bits match, expression is 0
146 // easy case: we have a winner..
149 } while (NULL
!= (match
= match
->next
));
151 // tail recurse, defer to the wisdom of our elders
153 match
= match_mac(db
, mac
, mask
- 8);
156 // real tough case.. we didn't find anything down the road
157 // and must search upwards :-P. This is the DITCH, we scan
158 // 255 hashpoints per octet to find out if there is anything around..
161 while(ditch
++ < 0xFF) {
162 match
= db
[index
+ditch
%MAC_HASHSIZE
];
171 /* load_mac: fill **macp with mac_entry
173 * sigp is a pointer to either
174 ** a pointer to a preallocated buffer of size max_sigs * fp_entry OR
175 ** a NULL pointer indicating that we should allocate max_sigs for you
176 * max_sigs is the maximal size of the buffer, or 0 in which case we decide
178 * Theory: snarf sigs in serially, easypeasy
182 int load_mac(const char *file
, mac_entry
**sigp
[], int hashsize
)
184 mac_entry
**sig
; // output
187 //debug("opening %s\n", file);
188 FILE *f
= fopen(file
, "r");
192 perror("failed to open file");
196 perror("need a pointer to fill");
200 hashsize
= MAC_HASHSIZE
;
202 *sigp
= calloc(hashsize
, sizeof(mac_entry
*));
206 // read a line at a time and load it into the hash.
207 while ((p
= fgets(buf
, sizeof(buf
), f
))) {
208 mac_entry entry
= {{0}}; //guarantee it's empty
211 char vendor
[128] = {0};
218 /* Remove leading and trailing blanks */
221 while (l
&& isspace(*(p
+ l
- 1)))
222 *(p
+ (l
--) - 1) = 0;
224 /* Skip empty lines and comments */
230 /* first part: mac address */
231 while (*p
&& !isspace(*p
))
233 if (isxdigit(*p
) && isxdigit(*(p
+1))) {
234 //mac.o[octet++] = strtol(p,&(p+1), 16);
235 sscanf(p
, "%2hhx", &entry
.o
[octet
]);
239 } else if (*p
== '-' || *p
== ':') {
242 } else if (*p
== '/') {
244 entry
.mask
= strtol(p
+1, NULL
, 10);
248 p
++; // skip unknown chars...
251 /* second part: vendor */
252 sscanf(p
, "%127s", vendor
);
254 while (*p
&& !isspace(*p
))
258 /* third part, #comment (optional) */
263 lp
= strnlen(p
, MAXLINE
- (p
- buf
) - 1);
265 while (lp
&& isspace(*(p
+ lp
- 1)))
266 *(p
+ (lp
--) - 1) = 0;
268 /* then copy the comment */
269 comment
= calloc(1, lp
+ 1);
270 strncpy(comment
, p
, lp
);
274 entry
.vendor
= strdup(vendor
);
275 entry
.comment
= comment
;
277 // if there is no mask, all octets count
279 entry
.mask
= octet
* 8; //48 - octet * 8;
281 int index
= hash_mac(entry
.o
, octet
);
285 sig
[index
] = alloc_mac(&entry
);
294 printf("hash collision %d: at index %d\n", cc, index);
297 e
->next
= alloc_mac(&entry
);
307 printf("Hash table layout: ");
309 for (i
= 0; i
< MAC_HASHSIZE
; i
++) {
316 max
= (max
> z
)? max
: z
;
321 printf ("max : %d\n", max
);
323 #endif /* DEBUG_HASH */
326 debug("[!] WARNING: no signatures loaded from config file.\n");
328 dlog("%d sigs from %d lines\n", sigcnt
, ln
);
333 /* eats an ARP packet and adds/enters the asset */
334 void arp_check(eth_hdr
, tstamp
)