A userspace application that filters DHCP floods to protect a DHCP server. It uses the Netfilter userspace packet queuing API.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

dhcp_protect.c 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. // dhcp_protect.c
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <stdint.h>
  7. #include <errno.h>
  8. #include <time.h>
  9. #include <linux/netfilter.h>
  10. #include <libnetfilter_queue/libnetfilter_queue.h>
  11. #include <uthash.h>
  12. #include "dhcp_protect.h"
  13. // main function
  14. int main(int argc, char **argv) {
  15. char *configfile;
  16. dp_conf conf;
  17. struct nfq_q_handle *qh;
  18. if ( argc == 2 ) {
  19. configfile = argv[1];
  20. }
  21. else {
  22. usage(argv[0]);
  23. return EXIT_FAILURE;
  24. }
  25. if ( load_config(&conf, configfile) == NULL )
  26. return EXIT_FAILURE;
  27. nfq_start(&conf);
  28. return 0;
  29. }
  30. // start netfilter queue
  31. void nfq_start(dp_conf *conf) {
  32. struct nfq_handle *h;
  33. struct nfq_q_handle *qh;
  34. int fd;
  35. if ( ( h = nfq_open() ) == NULL ) {
  36. fprintf(stderr,"error during nfq_open() %s\n", strerror(errno));
  37. return;
  38. }
  39. if ( ( qh = nfq_create_queue(h, conf->queue, &dp_callback, conf) ) == NULL ) {
  40. fprintf(stderr, "error during nfq_create_queue() %s\n", strerror(errno));
  41. return;
  42. }
  43. if ( nfq_set_mode(qh, NFQNL_COPY_PACKET, 1500) < 0 ) {
  44. fprintf(stderr,"error during nfq_set_mode() %s\n", strerror(errno));
  45. return;
  46. }
  47. if ( nfq_set_queue_flags(qh, NFQA_CFG_F_FAIL_OPEN, NFQA_CFG_F_FAIL_OPEN) < 0 ) {
  48. fprintf(stderr,"error during nfq_set_queue_flags() %s\n", strerror(errno));
  49. return;
  50. }
  51. fd = nfq_fd(h);
  52. while(1) {
  53. static char buf[65536];
  54. int rv;
  55. if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) {
  56. nfq_handle_packet(h, buf, rv); /* send packet to callback */
  57. }
  58. }
  59. }
  60. // display usage
  61. void usage(char *prog) {
  62. fprintf(stderr,"Usage: %s <configuration file>\n",prog);
  63. }
  64. // load configuration file
  65. dp_conf *load_config(dp_conf *conf, char *file) {
  66. FILE *fh;
  67. char *line = NULL;
  68. size_t len = 0;
  69. int error = 0;
  70. printf("Loading configuration %s\n",file);
  71. if ( ( fh = fopen(file,"r") ) == NULL ) {
  72. fprintf(stderr,"Failed to open configuration file '%s': %s\n", file, strerror(errno));
  73. return NULL;
  74. }
  75. while (getline(&line, &len, fh) != -1) {
  76. char *name, *value;
  77. name = strtok(line, "=\r\n ");
  78. value = strtok(NULL,"=\r\n ");
  79. if ( name == NULL || value == NULL || name[0] == '#' )
  80. continue;
  81. if ( strcmp(name,"max_pkt_per_interval")==0 )
  82. conf->pktint = atoi(value);
  83. else if ( strcmp(name,"interval")==0 )
  84. conf->interval = atoi(value);
  85. else if ( strcmp(name, "debug")==0 )
  86. conf->debug = atoi(value) ? 1 : 0;
  87. else if ( strcmp(name, "blacklist_time")==0 )
  88. conf->bltime = atoi(value);
  89. else if ( strcmp(name, "queue")==0 )
  90. conf->queue = atoi(value);
  91. else
  92. fprintf(stderr,"unknown directive '%s', ignored\n", name);
  93. free(line);
  94. }
  95. fclose(fh);
  96. if ( conf->pktint < 1 || conf->pktint > 1000 ) {
  97. fprintf(stderr,"max_pkt_per_interval value invalid (min 1, max 1000)\n");
  98. error=1;
  99. }
  100. if ( conf->interval < 10 || conf->interval > 60 ) {
  101. fprintf(stderr,"interval value invalid (min 10, max 60)\n");
  102. error=1;
  103. }
  104. if ( conf->debug < 0 || conf->debug > 1 ) {
  105. fprintf(stderr,"debug value invalid (0 or 1)\n");
  106. error=1;
  107. }
  108. if ( conf->bltime < 10 || conf->bltime > 900 ) {
  109. fprintf(stderr,"blacklist_time value invalid (min 10, max 900)\n");
  110. error=1;
  111. }
  112. if ( conf->queue < 0 ) {
  113. fprintf(stderr,"queue must be a positive integer\n");
  114. error=1;
  115. }
  116. if ( error )
  117. return NULL;
  118. printf("Configuration:\n");
  119. printf("\t%-20s = %4i\n", "max_pkt_per_interval", conf->pktint);
  120. printf("\t%-20s = %4i\n", "interval", conf->interval);
  121. printf("\t%-20s = %4i\n", "debug", conf->debug);
  122. printf("\t%-20s = %4i\n", "blacklist_time", conf->bltime);
  123. printf("\t%-20s = %4i\n", "queue", conf->queue);
  124. return conf;
  125. }
  126. // decode dhcp packet
  127. u_int32_t dhcp_check(struct nfq_data *nfa, int *verdict, dp_conf *conf) {
  128. unsigned char *pkt;
  129. int pktlen;
  130. int offset = 0;
  131. unsigned char *remoteid;
  132. int remoteidlen = 0;
  133. int found = 0;
  134. uint8_t ipver = 0;
  135. uint8_t ihl = 0;
  136. int i;
  137. pktlen = nfq_get_payload(nfa, &pkt);
  138. for(i=0; i<pktlen; i++) {
  139. if ( !(i%16) ) printf("\n");
  140. printf("%02x ", pkt[i]);
  141. }
  142. // can we read the IP proto and IP header length ?
  143. if ( pktlen > 0 ) {
  144. ipver = pkt[offset];
  145. ipver >>= 4;
  146. ihl = pkt[offset];
  147. ihl &= 0x0f;
  148. if ( ipver != 4 ) {
  149. if ( conf->debug ) printf("not an IPv4 packet\n");
  150. return NF_ACCEPT;
  151. }
  152. // jump over the IPv4 header
  153. offset += ihl * 4;
  154. }
  155. // jump over UDP + DHCP header
  156. offset += 8 + 28 + 16 + 64 + 128;
  157. // minimum packet size, fixed header + magic cookie (4 octets)
  158. if ( pktlen < offset + 4 ) {
  159. if ( conf->debug ) printf("packet too small\n");
  160. return NF_ACCEPT;
  161. }
  162. // check magic cookie
  163. if ( pkt[offset] != 99 || pkt[offset+1] != 130 || pkt[offset+2] != 83 || pkt[offset+3] != 99 ) {
  164. if ( conf->debug ) printf("invalid magic cookie %02x%02x%02x%02x\n", pkt[offset], pkt[offset+1], pkt[offset+2], pkt[offset+3]);
  165. return NF_ACCEPT;
  166. }
  167. offset+=4;
  168. // parse TLV options
  169. while(offset<pktlen && !found) {
  170. uint8_t type = pkt[offset];
  171. uint8_t len;
  172. offset++;
  173. printf("type : %i\n",type);
  174. // padding of 1 octet
  175. if ( type == 0 ) {
  176. continue;
  177. }
  178. // end of options
  179. if ( type == 255 )
  180. break;
  181. // make sure we can read len
  182. if ( offset>=pktlen )
  183. break;
  184. len = pkt[offset];
  185. offset++;
  186. printf("len : %i\n", len);
  187. // can the value be read
  188. if ( offset+len>=pktlen )
  189. break;
  190. // option 82 parser
  191. if ( type == 82 ) {
  192. unsigned char *o82 = pkt+offset;
  193. int o82off = 0;
  194. // loop until the end, +2 to ensure we can read type and length
  195. while(o82off+2<len && !found) {
  196. uint8_t otype = o82[o82off];
  197. uint8_t olen = o82[o82off+1];
  198. o82off+=2;
  199. printf("o82 type=%i len=%i\n", otype, olen);
  200. // remoteid
  201. if ( otype == 2 ) {
  202. // make sure we don't overflow and can read all data
  203. if ( o82off + olen > len ) {
  204. if ( conf->debug) printf("option 82.2 data too long\n");
  205. return NF_ACCEPT;
  206. }
  207. else {
  208. remoteid = o82 + o82off;
  209. remoteidlen = olen;
  210. found=1;
  211. }
  212. }
  213. o82off+=olen;
  214. }
  215. }
  216. offset+=len;
  217. }
  218. if ( found ) {
  219. dp_blacklist_count(conf, remoteid, remoteidlen);
  220. if ( dp_blacklist_check(conf, remoteid, remoteidlen) )
  221. return NF_ACCEPT;
  222. else
  223. return NF_DROP;
  224. }
  225. printf("got a packet, len = %i\n", pktlen);
  226. return NF_ACCEPT;
  227. }
  228. // netfilter queue callback
  229. static int dp_callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) {
  230. dp_conf *conf = (dp_conf*)data;
  231. int verdict;
  232. u_int32_t id = dhcp_check(nfa, &verdict, conf); /* Treat packet */
  233. return nfq_set_verdict(qh, id, verdict, 0, NULL); /* Verdict packet */
  234. }
  235. void dp_blacklist_count(dp_conf *conf, unsigned char *remoteid, int len) {
  236. int i;
  237. dp_blacklist *bl, *tmp;
  238. // does the element already exist
  239. HASH_FIND(hh, blacklists, remoteid, len, bl);
  240. // found it, increment the counter
  241. if ( bl ) {
  242. if ( conf->debug ) printf("BL: item found, incrementing\n");
  243. bl->count++;
  244. }
  245. // not found, create a new one
  246. else {
  247. bl = malloc(sizeof(dp_blacklist));
  248. memcpy(bl->remoteid, remoteid, len);
  249. bl->len = len;
  250. bl->count = 1;
  251. if ( conf->debug ) printf("BL: item not found, creating\n");
  252. HASH_ADD(hh, blacklists, remoteid, len, bl);
  253. }
  254. printf("count this remoteid ");
  255. for(i=0; i<len; i++) printf("%02x",*(uint8_t*)(remoteid+i));
  256. printf("\n");
  257. // is it time to cleanup the list?
  258. if ( dp_timestamp + conf->interval < time(NULL) ) {
  259. if ( conf->debug ) printf("cleanup interval\n");
  260. dp_timestamp = time(NULL);
  261. HASH_ITER(hh, blacklists, bl, tmp) {
  262. HASH_DEL(blacklists, bl);
  263. free(bl);
  264. }
  265. }
  266. }
  267. int dp_blacklist_check(dp_conf *conf, unsigned char *remoteid, int len) {
  268. dp_blacklist *bl;
  269. HASH_FIND(hh, blacklists, remoteid, len, bl);
  270. if ( bl ) {
  271. if(conf->debug) printf("found item\n");
  272. if ( bl->count > conf->pktint ) {
  273. if(conf->debug) printf("flood detected!\n");
  274. return NF_DROP;
  275. }
  276. }
  277. }