bugfix: avoid crashing in the ditch mac
[prads.git] / src / mac.c
blobe42ba0bd790d0ce904ac8e8018b5130742b2de19
1 /*
2 ** Copyright (C) 2010 Kacper Wysocki <kacper.wysocki@redpill-linpro.com>
3 **
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
8 ** Public License.
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.
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.
20 /* $Id$ */
22 #include "common.h"
23 #include "prads.h"
24 #include "sys_func.h"
25 #include "mac.h"
26 /* macfp
28 * Purpose:
30 * This file eats a MAC-address and returns a MAC-vendor match.
32 * File format:
33 * primary file format in use is Wireshark format:
34 * MA:CC:AD:RE:SS[/36] manufacturer # optional comment
36 * MAC-address
38 * Effect:
40 * Returns a fingerprint match and the fingerprint it matched
42 * Comments:
44 * Old school...
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));
50 *n = *e; // copy
51 return n;
54 void print_mac(const uint8_t *mac){
55 int i;
56 for(i = 0; i < 6; i++){
57 printf("%02hhx:", mac[i]);
61 void print_mac_entry(mac_entry *e){
62 if(!e) return;
64 print_mac(e->o);
66 if(e->mask)
67 printf("/%d", e->mask);
69 printf(" %s ", e->vendor);
70 if(e->comment)
71 printf("# %s", e->comment);
73 printf("\n");
75 return;
78 void print_mac_entries(mac_entry *e) {
79 print_mac_entry(e);
81 if(e->next)
82 print_mac_entries(e->next);
85 void dump_macs(mac_entry **sig, int len)
87 int i;
88 for (i = 0; i < len; i++) {
89 if(sig[i]){
90 printf("%d: ", 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
101 * aa:bb:cc:d0/26
102 * .. by searching upward from
103 * aa:bb:cc:00
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;
109 /* debug
110 if(mask == 48) {
111 print_mac(mac);
112 printf("/%d match\n", mask);
113 } */
114 if(db == NULL) return NULL; // database not loaded!
117 if(mask == 0)
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];
122 if(match) {
124 check_match:
125 do {
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
135 if(i == 7){
136 continue; // we flagged and broke
138 // do we have a winner?
139 r = match->mask % 8;
140 if(r) {
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
145 } else {
146 // easy case: we have a winner..
147 return match;
149 } while (NULL != (match = match->next));
151 // tail recurse, defer to the wisdom of our elders
152 if(! ditch ) {
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..
160 if(!match) {
161 while(ditch++ < 0xFF) {
162 match = db[index +ditch %MAC_HASHSIZE];
163 if(match)
164 goto check_match;
167 return match;
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
180 * returns errno
182 int load_mac(const char *file, mac_entry **sigp[], int hashsize)
184 mac_entry **sig; // output
185 uint32_t ln = 0;
186 uint32_t sigcnt = 0;
187 //debug("opening %s\n", file);
188 FILE *f = fopen(file, "r");
189 char buf[MAXLINE];
190 char *p;
191 if (!f) {
192 perror("failed to open file");
193 return errno;
195 if(!sigp){
196 perror("need a pointer to fill");
197 return -1;
199 if(!hashsize)
200 hashsize = MAC_HASHSIZE;
201 if(*sigp == NULL){
202 *sigp = calloc(hashsize, sizeof(mac_entry *));
203 sig = *sigp;
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
209 uint32_t l, lp;
210 uint8_t octet = 0;
211 char vendor[128] = {0};
212 char *comment = 0;
214 mac_entry *e;
216 ln++;
218 /* Remove leading and trailing blanks */
219 SKIP_SPACES(p);
220 l = strlen(p);
221 while (l && isspace(*(p + l - 1)))
222 *(p + (l--) - 1) = 0;
224 /* Skip empty lines and comments */
225 if (!l)
226 continue;
227 if (*p == '#')
228 continue;
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]);
236 octet++;
237 p += 2;
238 continue;
239 } else if (*p == '-' || *p == ':') {
240 p++;
241 continue;
242 } else if (*p == '/') {
243 // mac mask
244 entry.mask = strtol(p+1, NULL, 10);
245 p += 2;
246 continue;
247 } // else
248 p++; // skip unknown chars...
250 SKIP_SPACES(p);
251 /* second part: vendor */
252 sscanf(p, "%127s", vendor);
253 // scan forward..
254 while (*p && !isspace(*p))
255 p++;
256 SKIP_SPACES(p);
258 /* third part, #comment (optional) */
259 if(*p == '#') {
260 /* chomp it first */
261 p++;
262 SKIP_SPACES(p);
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);
273 /* roll hash */
274 entry.vendor = strdup(vendor);
275 entry.comment = comment;
277 // if there is no mask, all octets count
278 if(!entry.mask)
279 entry.mask = octet * 8; //48 - octet * 8;
281 int index = hash_mac(entry.o, octet);
282 e = sig[index];
284 if (!e) {
285 sig[index] = alloc_mac(&entry);
286 } else {
287 int cc = 0;
288 // collision!
289 while (e->next){
290 e = e->next;
291 cc++;
294 printf("hash collision %d: at index %d\n", cc, index);
295 print_mac(&entry);
297 e->next = alloc_mac(&entry);
299 sigcnt++;
302 fclose(f);
303 #ifdef DEBUG_HASH
305 int i,max;
306 mac_entry *p;
307 printf("Hash table layout: ");
308 max = 0;
309 for (i = 0; i < MAC_HASHSIZE; i++) {
310 int z = 0;
311 p = sig[i];
312 while (p) {
313 p = p->next;
314 z++;
316 max = (max > z)? max : z;
317 printf("%d ", z);
320 putchar('\n');
321 printf ("max : %d\n", max);
323 #endif /* DEBUG_HASH */
325 if (!sigcnt)
326 debug("[!] WARNING: no signatures loaded from config file.\n");
327 else
328 dlog("%d sigs from %d lines\n", sigcnt, ln);
330 return 0;
333 /* eats an ARP packet and adds/enters the asset */
334 void arp_check(eth_hdr, tstamp)
336 return;