ebt_ulog based arpwatch
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

305 lines
7.7KB

  1. /***************************************************************************
  2. * Copyright (C) 07/2007 by Olaf Rempel *
  3. * razzor@kopf-tisch.de *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; version 2 of the License *
  8. * *
  9. * This program is distributed in the hope that it will be useful, *
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  12. * GNU General Public License for more details. *
  13. * *
  14. * You should have received a copy of the GNU General Public License *
  15. * along with this program; if not, write to the *
  16. * Free Software Foundation, Inc., *
  17. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  18. ***************************************************************************/
  19. #include <stdlib.h>
  20. #include <stdint.h>
  21. #include <string.h>
  22. #include <sys/types.h>
  23. #include <sys/socket.h>
  24. #include <arpa/inet.h>
  25. #include <net/if.h>
  26. #include <time.h>
  27. #include "configfile.h"
  28. #include "datastore.h"
  29. #include "event.h"
  30. #include "list.h"
  31. #include "logging.h"
  32. struct subnet_entry {
  33. struct list_head list;
  34. uint32_t ip;
  35. uint32_t mask;
  36. char dev[IFNAMSIZ];
  37. };
  38. struct hash_table {
  39. struct list_head bucket[0];
  40. };
  41. struct ip_entry {
  42. struct list_head list;
  43. uint32_t ip;
  44. struct subnet_entry *subnet;
  45. uint8_t mac[6];
  46. uint32_t timestamp;
  47. };
  48. LIST_HEAD(subnet_list);
  49. static int hashsize;
  50. static struct hash_table *hashtable;
  51. static struct event_timeout *gc_event;
  52. static int ds_add_subnet(const char *dev, uint32_t ip, uint32_t mask)
  53. {
  54. struct subnet_entry *entry = malloc(sizeof(struct subnet_entry));
  55. if (entry == NULL) {
  56. log_print(LOG_ERROR, "ds_add_subnet(): out of memory");
  57. return -1;
  58. }
  59. entry->ip = ip;
  60. entry->mask = mask;
  61. strncpy(entry->dev, dev, sizeof(entry->dev));
  62. list_add_tail(&entry->list, &subnet_list);
  63. return 0;
  64. }
  65. static struct subnet_entry * ds_find_subnet(uint32_t ip)
  66. {
  67. struct subnet_entry *entry;
  68. list_for_each_entry(entry, &subnet_list, list)
  69. if ((ip & entry->mask) == entry->ip)
  70. return entry;
  71. return NULL;
  72. }
  73. static int ds_calc_hash(uint32_t ip)
  74. {
  75. /* TODO: real hash */
  76. return (ip * 16777219) % hashsize;
  77. }
  78. static struct ip_entry * ds_find_ip(uint32_t ip)
  79. {
  80. struct ip_entry *entry;
  81. list_for_each_entry(entry, &(hashtable->bucket[ds_calc_hash(ip)]), list)
  82. if (entry->ip == ip)
  83. return entry;
  84. return NULL;
  85. }
  86. static int is_same_mac(uint8_t *a, uint8_t *b)
  87. {
  88. return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2]) |
  89. (a[3] ^ b[3]) | (a[4] ^ b[4]) | (a[5] ^ b[5])) == 0x00;
  90. }
  91. static void ds_error(struct ip_entry *entry, const char *msg, const char *dev)
  92. {
  93. char *subnetdev = '\0';
  94. char ip[16], subnet[16] = {'\0'}, mask[16] = {'\0'};
  95. inet_ntop(AF_INET, &entry->ip, ip, sizeof(ip));
  96. if (entry->subnet != NULL) {
  97. subnetdev = entry->subnet->dev;
  98. inet_ntop(AF_INET, &entry->subnet->ip, subnet, sizeof(subnet));
  99. inet_ntop(AF_INET, &entry->subnet->mask, mask, sizeof(mask));
  100. }
  101. log_print(LOG_DEBUG, "%s: paket %s: %s[%02x:%02x:%02x:%02x:%02x:%02x] subnet %s: %s/%s",
  102. msg, dev, ip,
  103. entry->mac[0], entry->mac[1], entry->mac[2],
  104. entry->mac[3], entry->mac[4], entry->mac[5],
  105. subnetdev, subnet, mask);
  106. }
  107. int ds_check_update_ip(const char *dev, uint32_t ip, uint8_t *mac, uint32_t timestamp)
  108. {
  109. struct ip_entry *entry = ds_find_ip(ip);
  110. if (entry == NULL) {
  111. entry = malloc(sizeof(struct ip_entry));
  112. if (entry == NULL) {
  113. log_print(LOG_ERROR, "ds_check_update_ip(): out of memory");
  114. return -1;
  115. }
  116. entry->ip = ip;
  117. memcpy(entry->mac, mac, 6);
  118. entry->timestamp = timestamp;
  119. entry->subnet = ds_find_subnet(ip);
  120. if (entry->subnet == NULL) {
  121. ds_error(entry, "NEW: subnet not found", dev);
  122. free(entry);
  123. return -1;
  124. }
  125. if (strncmp(entry->subnet->dev, dev, sizeof(entry->subnet->dev)) != 0) {
  126. ds_error(entry, "NEW: invalid device", dev);
  127. free(entry);
  128. return -1;
  129. }
  130. list_add_tail(&entry->list, &(hashtable->bucket[ds_calc_hash(ip)]));
  131. return 0;
  132. } else if (strncmp(entry->subnet->dev, dev, sizeof(entry->subnet->dev)) != 0) {
  133. ds_error(entry, "UPDATE: invalid device", dev);
  134. return -1;
  135. }
  136. if (is_same_mac(entry->mac, mac)) {
  137. entry->timestamp = timestamp;
  138. } else {
  139. char ip[16];
  140. inet_ntop(AF_INET, &entry->ip, ip, sizeof(ip));
  141. log_print(LOG_DEBUG, "UPDATE: %s: %s [%02x:%02x:%02x:%02x:%02x:%02x] -> [%02x:%02x:%02x:%02x:%02x:%02x]",
  142. dev, ip,
  143. entry->mac[0], entry->mac[1], entry->mac[2],
  144. entry->mac[3], entry->mac[4], entry->mac[5],
  145. mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  146. memcpy(entry->mac, mac, 6);
  147. entry->timestamp = timestamp;
  148. }
  149. return 0;
  150. }
  151. static int parse_netmask(char *buf, uint32_t *ip, uint32_t *mask)
  152. {
  153. char *maskstr = strchr(buf, '/');
  154. if (maskstr == NULL)
  155. return -1;
  156. *maskstr++ = '\0';
  157. if (inet_pton(AF_INET, buf, ip) <= 0)
  158. return -1;
  159. if (inet_pton(AF_INET, maskstr, mask) <= 0) {
  160. *mask = atoi(maskstr);
  161. if (*mask < 0 || *mask > 32)
  162. return -1;
  163. *mask = htonl(0xFFFFFFFF << (32 - *mask));
  164. }
  165. return 0;
  166. }
  167. static int add_subnet_cb(const char *parameter, void *privdata)
  168. {
  169. char *dev = strdup(parameter);
  170. char *ipmask = strchr(dev, ':');
  171. if (ipmask == NULL) {
  172. log_print(LOG_WARN, "add_subnet_cb(): no ip found");
  173. free(dev);
  174. return -1;
  175. }
  176. *ipmask++ = '\0';
  177. uint32_t ip, mask;
  178. if (parse_netmask(ipmask, &ip, &mask) != 0) {
  179. log_print(LOG_WARN, "add_subnet_cb(): invalid ip/mask");
  180. free(dev);
  181. return -1;
  182. }
  183. char ipstr[16], maskstr[16];
  184. inet_ntop(AF_INET, &ip, ipstr, sizeof(ipstr));
  185. inet_ntop(AF_INET, &mask, maskstr, sizeof(maskstr));
  186. log_print(LOG_INFO, "datastore: adding device %s with subnet %s/%s", dev, ipstr, maskstr);
  187. ds_add_subnet(dev, ip, mask);
  188. free(dev);
  189. return 0;
  190. }
  191. static int hash_gc(void *privdata)
  192. {
  193. uint32_t check = time(NULL) - (int)privdata;
  194. uint32_t i, total = 0, removed = 0;
  195. for (i = 0; i < hashsize; i++) {
  196. struct ip_entry *entry, *tmp;
  197. list_for_each_entry_safe(entry, tmp, &(hashtable->bucket[i]), list) {
  198. total++;
  199. if (entry->timestamp < check) {
  200. list_del(&entry->list);
  201. free(entry);
  202. removed++;
  203. }
  204. }
  205. }
  206. log_print(LOG_DEBUG, "garbage collector: %d/%d", removed, total);
  207. return 0;
  208. }
  209. int datastore_init(void)
  210. {
  211. hashsize = config_get_int("global", "hashsize", 1021);
  212. hashtable = malloc(sizeof(struct hash_table) + sizeof(struct list_head) * hashsize);
  213. if (hashtable == NULL) {
  214. log_print(LOG_ERROR, "ds_init(): out of memory");
  215. return -1;
  216. }
  217. int i;
  218. for (i = 0; i < hashsize; i++)
  219. INIT_LIST_HEAD(&(hashtable->bucket[i]));
  220. int cnt = config_get_strings("global", "subnet", add_subnet_cb, NULL);
  221. if (cnt <= 0) {
  222. log_print(LOG_ERROR, "ds_init(): no subnets defined");
  223. free(hashtable);
  224. return -1;
  225. }
  226. int timeout = config_get_int("global", "hashgc", 300);
  227. struct timeval tv = { .tv_sec = timeout, .tv_usec = 0 };
  228. gc_event = event_add_timeout(&tv, hash_gc, (void *)timeout);
  229. return 0;
  230. }
  231. void datastore_close(void)
  232. {
  233. struct subnet_entry *entry, *tmp;
  234. list_for_each_entry_safe(entry, tmp, &subnet_list, list) {
  235. list_del(&entry->list);
  236. free(entry);
  237. }
  238. int i;
  239. for (i = 0; i < hashsize; i++) {
  240. struct ip_entry *entry, *tmp;
  241. list_for_each_entry_safe(entry, tmp, &(hashtable->bucket[i]), list) {
  242. list_del(&entry->list);
  243. free(entry);
  244. }
  245. }
  246. event_remove_timeout(gc_event);
  247. free(hashtable);
  248. }